summaryrefslogtreecommitdiffstats
path: root/vcl/source/app/weldutils.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /vcl/source/app/weldutils.cxx
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/source/app/weldutils.cxx')
-rw-r--r--vcl/source/app/weldutils.cxx662
1 files changed, 662 insertions, 0 deletions
diff --git a/vcl/source/app/weldutils.cxx b/vcl/source/app/weldutils.cxx
new file mode 100644
index 0000000000..235e3140ad
--- /dev/null
+++ b/vcl/source/app/weldutils.cxx
@@ -0,0 +1,662 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <comphelper/processfactory.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <vcl/builderpage.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/event.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weldutils.hxx>
+
+BuilderPage::BuilderPage(weld::Widget* pParent, weld::DialogController* pController,
+ const OUString& rUIXMLDescription, const OUString& rID, bool bIsMobile)
+ : m_pDialogController(pController)
+ , m_xBuilder(Application::CreateBuilder(pParent, rUIXMLDescription, bIsMobile))
+ , m_xContainer(m_xBuilder->weld_container(rID))
+{
+}
+
+void BuilderPage::Activate() {}
+
+void BuilderPage::Deactivate() {}
+
+BuilderPage::~BuilderPage() COVERITY_NOEXCEPT_FALSE {}
+
+namespace weld
+{
+bool DialogController::runAsync(const std::shared_ptr<DialogController>& rController,
+ const std::function<void(sal_Int32)>& func)
+{
+ return rController->getDialog()->runAsync(rController, func);
+}
+
+DialogController::~DialogController() COVERITY_NOEXCEPT_FALSE {}
+
+Dialog* GenericDialogController::getDialog() { return m_xDialog.get(); }
+
+GenericDialogController::GenericDialogController(weld::Widget* pParent, const OUString& rUIFile,
+ const OUString& rDialogId, bool bMobile)
+ : m_xBuilder(Application::CreateBuilder(pParent, rUIFile, bMobile))
+ , m_xDialog(m_xBuilder->weld_dialog(rDialogId))
+{
+}
+
+GenericDialogController::~GenericDialogController() COVERITY_NOEXCEPT_FALSE {}
+
+Dialog* MessageDialogController::getDialog() { return m_xDialog.get(); }
+
+MessageDialogController::MessageDialogController(weld::Widget* pParent, const OUString& rUIFile,
+ const OUString& rDialogId,
+ const OUString& rRelocateId)
+ : m_xBuilder(Application::CreateBuilder(pParent, rUIFile))
+ , m_xDialog(m_xBuilder->weld_message_dialog(rDialogId))
+ , m_xContentArea(m_xDialog->weld_message_area())
+{
+ if (!rRelocateId.isEmpty())
+ {
+ m_xRelocate = m_xBuilder->weld_container(rRelocateId);
+ m_xOrigParent = m_xRelocate->weld_parent();
+ //fdo#75121, a bit tricky because the widgets we want to align with
+ //don't actually exist in the ui description, they're implied
+ m_xOrigParent->move(m_xRelocate.get(), m_xContentArea.get());
+ }
+}
+
+MessageDialogController::~MessageDialogController()
+{
+ if (m_xRelocate)
+ {
+ m_xContentArea->move(m_xRelocate.get(), m_xOrigParent.get());
+ }
+}
+
+AssistantController::AssistantController(weld::Widget* pParent, const OUString& rUIFile,
+ const OUString& rDialogId)
+ : m_xBuilder(Application::CreateBuilder(pParent, rUIFile))
+ , m_xAssistant(m_xBuilder->weld_assistant(rDialogId))
+{
+}
+
+Dialog* AssistantController::getDialog() { return m_xAssistant.get(); }
+
+AssistantController::~AssistantController() {}
+
+void TriStateEnabled::ButtonToggled(weld::Toggleable& rToggle)
+{
+ if (bTriStateEnabled)
+ {
+ switch (eState)
+ {
+ case TRISTATE_INDET:
+ rToggle.set_state(TRISTATE_FALSE);
+ break;
+ case TRISTATE_TRUE:
+ rToggle.set_state(TRISTATE_INDET);
+ break;
+ case TRISTATE_FALSE:
+ rToggle.set_state(TRISTATE_TRUE);
+ break;
+ }
+ }
+ eState = rToggle.get_state();
+}
+
+void RemoveParentKeepChildren(weld::TreeView& rTreeView, const weld::TreeIter& rParent)
+{
+ if (rTreeView.iter_has_child(rParent))
+ {
+ std::unique_ptr<weld::TreeIter> xNewParent(rTreeView.make_iterator(&rParent));
+ if (!rTreeView.iter_parent(*xNewParent))
+ xNewParent.reset();
+
+ while (true)
+ {
+ std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator(&rParent));
+ if (!rTreeView.iter_children(*xChild))
+ break;
+ rTreeView.move_subtree(*xChild, xNewParent.get(), -1);
+ }
+ }
+ rTreeView.remove(rParent);
+}
+
+EntryFormatter::EntryFormatter(weld::FormattedSpinButton& rSpinButton)
+ : m_rEntry(rSpinButton)
+ , m_pSpinButton(&rSpinButton)
+ , m_eOptions(Application::GetSettings().GetStyleSettings().GetSelectionOptions())
+{
+ Init();
+}
+
+EntryFormatter::EntryFormatter(weld::Entry& rEntry)
+ : m_rEntry(rEntry)
+ , m_pSpinButton(nullptr)
+ , m_eOptions(Application::GetSettings().GetStyleSettings().GetSelectionOptions())
+{
+ Init();
+}
+
+EntryFormatter::~EntryFormatter()
+{
+ m_rEntry.connect_changed(Link<weld::Entry&, void>());
+ m_rEntry.connect_focus_out(Link<weld::Widget&, void>());
+ if (m_pSpinButton)
+ m_pSpinButton->SetFormatter(nullptr);
+}
+
+void EntryFormatter::Init()
+{
+ m_rEntry.connect_changed(LINK(this, EntryFormatter, ModifyHdl));
+ m_rEntry.connect_focus_out(LINK(this, EntryFormatter, FocusOutHdl));
+ if (m_pSpinButton)
+ m_pSpinButton->SetFormatter(this);
+}
+
+Selection EntryFormatter::GetEntrySelection() const
+{
+ int nStartPos, nEndPos;
+ m_rEntry.get_selection_bounds(nStartPos, nEndPos);
+ return Selection(nStartPos, nEndPos);
+}
+
+OUString EntryFormatter::GetEntryText() const { return m_rEntry.get_text(); }
+
+void EntryFormatter::SetEntryText(const OUString& rText, const Selection& rSel)
+{
+ m_rEntry.set_text(rText);
+ auto nMin = rSel.Min();
+ auto nMax = rSel.Max();
+ m_rEntry.select_region(nMin < 0 ? 0 : nMin, nMax == SELECTION_MAX ? -1 : nMax);
+}
+
+void EntryFormatter::SetEntryTextColor(const Color* pColor)
+{
+ m_rEntry.set_font_color(pColor ? *pColor : COL_AUTO);
+}
+
+void EntryFormatter::UpdateCurrentValue(double dCurrentValue)
+{
+ Formatter::UpdateCurrentValue(dCurrentValue);
+ if (m_pSpinButton)
+ m_pSpinButton->sync_value_from_formatter();
+}
+
+void EntryFormatter::ClearMinValue()
+{
+ Formatter::ClearMinValue();
+ if (m_pSpinButton)
+ m_pSpinButton->sync_range_from_formatter();
+}
+
+void EntryFormatter::SetMinValue(double dMin)
+{
+ Formatter::SetMinValue(dMin);
+ if (m_pSpinButton)
+ m_pSpinButton->sync_range_from_formatter();
+}
+
+void EntryFormatter::ClearMaxValue()
+{
+ Formatter::ClearMaxValue();
+ if (m_pSpinButton)
+ m_pSpinButton->sync_range_from_formatter();
+}
+
+void EntryFormatter::SetMaxValue(double dMin)
+{
+ Formatter::SetMaxValue(dMin);
+ if (m_pSpinButton)
+ m_pSpinButton->sync_range_from_formatter();
+}
+
+void EntryFormatter::SetSpinSize(double dStep)
+{
+ Formatter::SetSpinSize(dStep);
+ if (m_pSpinButton)
+ m_pSpinButton->sync_increments_from_formatter();
+}
+
+SelectionOptions EntryFormatter::GetEntrySelectionOptions() const { return m_eOptions; }
+
+void EntryFormatter::FieldModified() { m_aModifyHdl.Call(m_rEntry); }
+
+IMPL_LINK_NOARG(EntryFormatter, ModifyHdl, weld::Entry&, void)
+{
+ // This leads to FieldModified getting called at the end of Modify() and
+ // FieldModified then calls any modification callback
+ Modify();
+}
+
+IMPL_LINK_NOARG(EntryFormatter, FocusOutHdl, weld::Widget&, void)
+{
+ EntryLostFocus();
+ if (m_pSpinButton)
+ m_pSpinButton->signal_value_changed();
+ m_aFocusOutHdl.Call(m_rEntry);
+}
+
+DoubleNumericFormatter::DoubleNumericFormatter(weld::Entry& rEntry)
+ : EntryFormatter(rEntry)
+{
+ ResetConformanceTester();
+}
+
+DoubleNumericFormatter::DoubleNumericFormatter(weld::FormattedSpinButton& rSpinButton)
+ : EntryFormatter(rSpinButton)
+{
+ ResetConformanceTester();
+}
+
+DoubleNumericFormatter::~DoubleNumericFormatter() = default;
+
+void DoubleNumericFormatter::FormatChanged(FORMAT_CHANGE_TYPE nWhat)
+{
+ ResetConformanceTester();
+ EntryFormatter::FormatChanged(nWhat);
+}
+
+bool DoubleNumericFormatter::CheckText(const OUString& sText) const
+{
+ // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
+ // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
+ // Thus, the roundabout way via a regular expression
+ return m_pNumberValidator->isValidNumericFragment(sText);
+}
+
+void DoubleNumericFormatter::ResetConformanceTester()
+{
+ // the thousands and the decimal separator are language dependent
+ const SvNumberformat* pFormatEntry = GetOrCreateFormatter()->GetEntry(m_nFormatKey);
+
+ sal_Unicode cSeparatorThousand = ',';
+ sal_Unicode cSeparatorDecimal = '.';
+ if (pFormatEntry)
+ {
+ LocaleDataWrapper aLocaleInfo(LanguageTag(pFormatEntry->GetLanguage()));
+
+ OUString sSeparator = aLocaleInfo.getNumThousandSep();
+ if (!sSeparator.isEmpty())
+ cSeparatorThousand = sSeparator[0];
+
+ sSeparator = aLocaleInfo.getNumDecimalSep();
+ if (!sSeparator.isEmpty())
+ cSeparatorDecimal = sSeparator[0];
+ }
+
+ m_pNumberValidator.reset(
+ new validation::NumberValidator(cSeparatorThousand, cSeparatorDecimal));
+}
+
+LongCurrencyFormatter::LongCurrencyFormatter(weld::Entry& rEntry)
+ : EntryFormatter(rEntry)
+ , m_bThousandSep(true)
+{
+ Init();
+}
+
+LongCurrencyFormatter::LongCurrencyFormatter(weld::FormattedSpinButton& rSpinButton)
+ : EntryFormatter(rSpinButton)
+ , m_bThousandSep(true)
+{
+ Init();
+}
+
+void LongCurrencyFormatter::Init()
+{
+ SetOutputHdl(LINK(this, LongCurrencyFormatter, FormatOutputHdl));
+ SetInputHdl(LINK(this, LongCurrencyFormatter, ParseInputHdl));
+}
+
+void LongCurrencyFormatter::SetUseThousandSep(bool b)
+{
+ m_bThousandSep = b;
+ ReFormat();
+}
+
+void LongCurrencyFormatter::SetCurrencySymbol(const OUString& rStr)
+{
+ m_aCurrencySymbol = rStr;
+ ReFormat();
+}
+
+LongCurrencyFormatter::~LongCurrencyFormatter() = default;
+
+TimeFormatter::TimeFormatter(weld::Entry& rEntry)
+ : EntryFormatter(rEntry)
+ , m_eFormat(TimeFieldFormat::F_NONE)
+ , m_eTimeFormat(TimeFormat::Hour24)
+ , m_bDuration(false)
+{
+ Init();
+}
+
+TimeFormatter::TimeFormatter(weld::FormattedSpinButton& rSpinButton)
+ : EntryFormatter(rSpinButton)
+ , m_eFormat(TimeFieldFormat::F_NONE)
+ , m_eTimeFormat(TimeFormat::Hour24)
+ , m_bDuration(false)
+{
+ Init();
+}
+
+void TimeFormatter::Init()
+{
+ DisableRemainderFactor(); //so with hh::mm::ss, incrementing mm will not reset ss
+
+ SetOutputHdl(LINK(this, TimeFormatter, FormatOutputHdl));
+ SetInputHdl(LINK(this, TimeFormatter, ParseInputHdl));
+
+ SetMin(tools::Time(0, 0));
+ SetMax(tools::Time(23, 59, 59, 999999999));
+
+ // so the spin size can depend on which zone the cursor is in
+ get_widget().connect_cursor_position(LINK(this, TimeFormatter, CursorChangedHdl));
+ // and set the initial spin size
+ CursorChangedHdl(get_widget());
+}
+
+void TimeFormatter::SetExtFormat(ExtTimeFieldFormat eFormat)
+{
+ switch (eFormat)
+ {
+ case ExtTimeFieldFormat::Short24H:
+ {
+ m_eTimeFormat = TimeFormat::Hour24;
+ m_bDuration = false;
+ m_eFormat = TimeFieldFormat::F_NONE;
+ }
+ break;
+ case ExtTimeFieldFormat::Long24H:
+ {
+ m_eTimeFormat = TimeFormat::Hour24;
+ m_bDuration = false;
+ m_eFormat = TimeFieldFormat::F_SEC;
+ }
+ break;
+ case ExtTimeFieldFormat::Short12H:
+ {
+ m_eTimeFormat = TimeFormat::Hour12;
+ m_bDuration = false;
+ m_eFormat = TimeFieldFormat::F_NONE;
+ }
+ break;
+ case ExtTimeFieldFormat::Long12H:
+ {
+ m_eTimeFormat = TimeFormat::Hour12;
+ m_bDuration = false;
+ m_eFormat = TimeFieldFormat::F_SEC;
+ }
+ break;
+ case ExtTimeFieldFormat::ShortDuration:
+ {
+ m_bDuration = true;
+ m_eFormat = TimeFieldFormat::F_NONE;
+ }
+ break;
+ case ExtTimeFieldFormat::LongDuration:
+ {
+ m_bDuration = true;
+ m_eFormat = TimeFieldFormat::F_SEC;
+ }
+ break;
+ }
+
+ ReFormat();
+}
+
+void TimeFormatter::SetDuration(bool bDuration)
+{
+ m_bDuration = bDuration;
+ ReFormat();
+}
+
+void TimeFormatter::SetTimeFormat(TimeFieldFormat eTimeFormat)
+{
+ m_eFormat = eTimeFormat;
+ ReFormat();
+}
+
+TimeFormatter::~TimeFormatter() = default;
+
+DateFormatter::DateFormatter(weld::Entry& rEntry)
+ : EntryFormatter(rEntry)
+ , m_eFormat(ExtDateFieldFormat::SystemShort)
+{
+ Init();
+}
+
+void DateFormatter::Init()
+{
+ SetOutputHdl(LINK(this, DateFormatter, FormatOutputHdl));
+ SetInputHdl(LINK(this, DateFormatter, ParseInputHdl));
+
+ SetMin(Date(1, 1, 1900));
+ SetMax(Date(31, 12, 2200));
+}
+
+void DateFormatter::SetExtDateFormat(ExtDateFieldFormat eFormat)
+{
+ m_eFormat = eFormat;
+ ReFormat();
+}
+
+DateFormatter::~DateFormatter() = default;
+
+PatternFormatter::PatternFormatter(weld::Entry& rEntry)
+ : m_rEntry(rEntry)
+ , m_bStrictFormat(false)
+ , m_bSameMask(true)
+ , m_bReformat(false)
+ , m_bInPattKeyInput(false)
+{
+ m_rEntry.connect_changed(LINK(this, PatternFormatter, ModifyHdl));
+ m_rEntry.connect_focus_in(LINK(this, PatternFormatter, FocusInHdl));
+ m_rEntry.connect_focus_out(LINK(this, PatternFormatter, FocusOutHdl));
+ m_rEntry.connect_key_press(LINK(this, PatternFormatter, KeyInputHdl));
+}
+
+IMPL_LINK_NOARG(PatternFormatter, ModifyHdl, weld::Entry&, void) { Modify(); }
+
+IMPL_LINK_NOARG(PatternFormatter, FocusOutHdl, weld::Widget&, void)
+{
+ EntryLostFocus();
+ m_aFocusOutHdl.Call(m_rEntry);
+}
+
+IMPL_LINK_NOARG(PatternFormatter, FocusInHdl, weld::Widget&, void)
+{
+ EntryGainFocus();
+ m_aFocusInHdl.Call(m_rEntry);
+}
+
+PatternFormatter::~PatternFormatter()
+{
+ m_rEntry.connect_changed(Link<weld::Entry&, void>());
+ m_rEntry.connect_focus_out(Link<weld::Widget&, void>());
+}
+
+int GetMinimumEditHeight()
+{
+ // load this little .ui just to measure the height of an Entry
+ std::unique_ptr<weld::Builder> xBuilder(
+ Application::CreateBuilder(nullptr, "cui/ui/namedialog.ui"));
+ std::unique_ptr<weld::Entry> xEntry(xBuilder->weld_entry("name_entry"));
+ return xEntry->get_preferred_size().Height();
+}
+
+WidgetStatusListener::WidgetStatusListener(weld::Widget* widget, const OUString& aCommand)
+ : mWidget(widget)
+{
+ css::uno::Reference<css::uno::XComponentContext> xContext
+ = ::comphelper::getProcessComponentContext();
+ css::uno::Reference<css::frame::XDesktop2> xDesktop = css::frame::Desktop::create(xContext);
+
+ css::uno::Reference<css::frame::XFrame> xFrame(xDesktop->getActiveFrame());
+ if (!xFrame.is())
+ xFrame = xDesktop;
+
+ mxFrame = xFrame;
+
+ maCommandURL.Complete = aCommand;
+ css::uno::Reference<css::util::XURLTransformer> xParser
+ = css::util::URLTransformer::create(xContext);
+ xParser->parseStrict(maCommandURL);
+}
+
+void WidgetStatusListener::startListening()
+{
+ if (mxDispatch.is())
+ mxDispatch->removeStatusListener(this, maCommandURL);
+
+ css::uno::Reference<css::frame::XDispatchProvider> xDispatchProvider(mxFrame,
+ css::uno::UNO_QUERY);
+ if (!xDispatchProvider.is())
+ return;
+
+ mxDispatch = xDispatchProvider->queryDispatch(maCommandURL, "", 0);
+ if (mxDispatch.is())
+ mxDispatch->addStatusListener(this, maCommandURL);
+}
+
+void WidgetStatusListener::statusChanged(const css::frame::FeatureStateEvent& rEvent)
+{
+ mWidget->set_sensitive(rEvent.IsEnabled);
+}
+
+void WidgetStatusListener::disposing(const css::lang::EventObject& /*Source*/)
+{
+ mxDispatch.clear();
+}
+
+void WidgetStatusListener::dispose()
+{
+ if (mxDispatch.is())
+ {
+ mxDispatch->removeStatusListener(this, maCommandURL);
+ mxDispatch.clear();
+ }
+ mxFrame.clear();
+ mWidget = nullptr;
+}
+
+ButtonPressRepeater::ButtonPressRepeater(weld::Button& rButton, const Link<Button&, void>& rLink,
+ const Link<const CommandEvent&, void>& rContextLink)
+ : m_rButton(rButton)
+ , m_aRepeat("vcl ButtonPressRepeater m_aRepeat")
+ , m_aLink(rLink)
+ , m_aContextLink(rContextLink)
+ , m_bModKey(false)
+{
+ // instead of connect_clicked because we want a button held down to
+ // repeat the next/prev
+ m_rButton.connect_mouse_press(LINK(this, ButtonPressRepeater, MousePressHdl));
+ m_rButton.connect_mouse_release(LINK(this, ButtonPressRepeater, MouseReleaseHdl));
+
+ m_aRepeat.SetInvokeHandler(LINK(this, ButtonPressRepeater, RepeatTimerHdl));
+}
+
+IMPL_LINK(ButtonPressRepeater, MousePressHdl, const MouseEvent&, rMouseEvent, bool)
+{
+ if (rMouseEvent.IsRight())
+ {
+ m_aContextLink.Call(
+ CommandEvent(rMouseEvent.GetPosPixel(), CommandEventId::ContextMenu, true));
+ return false;
+ }
+ m_bModKey = rMouseEvent.IsMod1();
+ if (!m_rButton.get_sensitive())
+ return false;
+ auto self = weak_from_this();
+ RepeatTimerHdl(nullptr);
+ if (!self.lock())
+ return false;
+ if (!m_rButton.get_sensitive())
+ return false;
+ m_aRepeat.SetTimeout(MouseSettings::GetButtonStartRepeat());
+ m_aRepeat.Start();
+ return true;
+}
+
+IMPL_LINK_NOARG(ButtonPressRepeater, MouseReleaseHdl, const MouseEvent&, bool)
+{
+ m_bModKey = false;
+ m_aRepeat.Stop();
+ return true;
+}
+
+IMPL_LINK_NOARG(ButtonPressRepeater, RepeatTimerHdl, Timer*, void)
+{
+ m_aRepeat.SetTimeout(Application::GetSettings().GetMouseSettings().GetButtonRepeat());
+ m_aLink.Call(m_rButton);
+}
+
+weld::Window* GetPopupParent(vcl::Window& rOutWin, tools::Rectangle& rRect)
+{
+ rRect.SetPos(rOutWin.OutputToScreenPixel(rRect.TopLeft()));
+ AbsoluteScreenPixelRectangle aRectAbs = FloatingWindow::ImplConvertToAbsPos(&rOutWin, rRect);
+
+ vcl::Window* pWin = rOutWin.GetFrameWindow();
+
+ rRect = FloatingWindow::ImplConvertToRelPos(pWin, aRectAbs);
+ rRect.SetPos(pWin->ScreenToOutputPixel(rRect.TopLeft()));
+
+ return rOutWin.GetFrameWeld();
+}
+
+void SetPointFont(OutputDevice& rDevice, const vcl::Font& rFont, bool bUseDeviceDPI)
+{
+ auto pDefaultDevice = Application::GetDefaultDevice();
+ if (pDefaultDevice)
+ if (vcl::Window* pDefaultWindow = pDefaultDevice->GetOwnerWindow())
+ pDefaultWindow->SetPointFont(rDevice, rFont, bUseDeviceDPI);
+}
+
+ReorderingDropTarget::ReorderingDropTarget(weld::TreeView& rTreeView)
+ : DropTargetHelper(rTreeView.get_drop_target())
+ , m_rTreeView(rTreeView)
+{
+}
+
+sal_Int8 ReorderingDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
+{
+ // to enable the autoscroll when we're close to the edges
+ m_rTreeView.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
+ return DND_ACTION_MOVE;
+}
+
+sal_Int8 ReorderingDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
+{
+ weld::TreeView* pSource = m_rTreeView.get_drag_source();
+ // only dragging within the same widget allowed
+ if (!pSource || pSource != &m_rTreeView)
+ return DND_ACTION_NONE;
+
+ std::unique_ptr<weld::TreeIter> xSource(m_rTreeView.make_iterator());
+ if (!m_rTreeView.get_selected(xSource.get()))
+ return DND_ACTION_NONE;
+
+ std::unique_ptr<weld::TreeIter> xTarget(m_rTreeView.make_iterator());
+ int nTargetPos = -1;
+ if (m_rTreeView.get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true))
+ nTargetPos = m_rTreeView.get_iter_index_in_parent(*xTarget);
+ m_rTreeView.move_subtree(*xSource, nullptr, nTargetPos);
+
+ return DND_ACTION_NONE;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */