1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <com/sun/star/util/URLTransformer.hpp>
11 #include <com/sun/star/frame/Desktop.hpp>
12 #include <comphelper/processfactory.hxx>
13 #include <svl/numformat.hxx>
14 #include <svl/zforlist.hxx>
15 #include <svl/zformat.hxx>
16 #include <vcl/builderpage.hxx>
17 #include <vcl/commandevent.hxx>
18 #include <vcl/commandinfoprovider.hxx>
19 #include <vcl/event.hxx>
20 #include <vcl/toolkit/floatwin.hxx>
21 #include <vcl/settings.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/weldutils.hxx>
25 BuilderPage::BuilderPage(weld::Widget
* pParent
, weld::DialogController
* pController
,
26 const OUString
& rUIXMLDescription
, const OUString
& rID
, bool bIsMobile
)
27 : m_pDialogController(pController
)
28 , m_xBuilder(Application::CreateBuilder(pParent
, rUIXMLDescription
, bIsMobile
))
29 , m_xContainer(m_xBuilder
->weld_container(rID
))
33 void BuilderPage::Activate() {}
35 void BuilderPage::Deactivate() {}
37 BuilderPage::~BuilderPage() COVERITY_NOEXCEPT_FALSE
{}
41 bool DialogController::runAsync(const std::shared_ptr
<DialogController
>& rController
,
42 const std::function
<void(sal_Int32
)>& func
)
44 return rController
->getDialog()->runAsync(rController
, func
);
47 DialogController::~DialogController() COVERITY_NOEXCEPT_FALSE
{}
49 Dialog
* GenericDialogController::getDialog() { return m_xDialog
.get(); }
51 GenericDialogController::GenericDialogController(weld::Widget
* pParent
, const OUString
& rUIFile
,
52 const OUString
& rDialogId
, bool bMobile
)
53 : m_xBuilder(Application::CreateBuilder(pParent
, rUIFile
, bMobile
))
54 , m_xDialog(m_xBuilder
->weld_dialog(rDialogId
))
58 GenericDialogController::~GenericDialogController() COVERITY_NOEXCEPT_FALSE
{}
60 Dialog
* MessageDialogController::getDialog() { return m_xDialog
.get(); }
62 MessageDialogController::MessageDialogController(weld::Widget
* pParent
, const OUString
& rUIFile
,
63 const OUString
& rDialogId
,
64 const OUString
& rRelocateId
)
65 : m_xBuilder(Application::CreateBuilder(pParent
, rUIFile
))
66 , m_xDialog(m_xBuilder
->weld_message_dialog(rDialogId
))
67 , m_xContentArea(m_xDialog
->weld_message_area())
69 if (!rRelocateId
.isEmpty())
71 m_xRelocate
= m_xBuilder
->weld_container(rRelocateId
);
72 m_xOrigParent
= m_xRelocate
->weld_parent();
73 //fdo#75121, a bit tricky because the widgets we want to align with
74 //don't actually exist in the ui description, they're implied
75 m_xOrigParent
->move(m_xRelocate
.get(), m_xContentArea
.get());
79 MessageDialogController::~MessageDialogController()
83 m_xContentArea
->move(m_xRelocate
.get(), m_xOrigParent
.get());
87 AssistantController::AssistantController(weld::Widget
* pParent
, const OUString
& rUIFile
,
88 const OUString
& rDialogId
)
89 : m_xBuilder(Application::CreateBuilder(pParent
, rUIFile
))
90 , m_xAssistant(m_xBuilder
->weld_assistant(rDialogId
))
94 Dialog
* AssistantController::getDialog() { return m_xAssistant
.get(); }
96 AssistantController::~AssistantController() {}
98 void TriStateEnabled::ButtonToggled(weld::Toggleable
& rToggle
)
100 if (bTriStateEnabled
)
105 rToggle
.set_state(TRISTATE_FALSE
);
108 rToggle
.set_state(TRISTATE_INDET
);
111 rToggle
.set_state(TRISTATE_TRUE
);
115 eState
= rToggle
.get_state();
118 void RemoveParentKeepChildren(weld::TreeView
& rTreeView
, const weld::TreeIter
& rParent
)
120 if (rTreeView
.iter_has_child(rParent
))
122 std::unique_ptr
<weld::TreeIter
> xNewParent(rTreeView
.make_iterator(&rParent
));
123 if (!rTreeView
.iter_parent(*xNewParent
))
128 std::unique_ptr
<weld::TreeIter
> xChild(rTreeView
.make_iterator(&rParent
));
129 if (!rTreeView
.iter_children(*xChild
))
131 rTreeView
.move_subtree(*xChild
, xNewParent
.get(), -1);
134 rTreeView
.remove(rParent
);
137 EntryFormatter::EntryFormatter(weld::FormattedSpinButton
& rSpinButton
)
138 : m_rEntry(rSpinButton
)
139 , m_pSpinButton(&rSpinButton
)
140 , m_eOptions(Application::GetSettings().GetStyleSettings().GetSelectionOptions())
145 EntryFormatter::EntryFormatter(weld::Entry
& rEntry
)
147 , m_pSpinButton(nullptr)
148 , m_eOptions(Application::GetSettings().GetStyleSettings().GetSelectionOptions())
153 EntryFormatter::~EntryFormatter()
155 m_rEntry
.connect_changed(Link
<weld::Entry
&, void>());
156 m_rEntry
.connect_focus_out(Link
<weld::Widget
&, void>());
158 m_pSpinButton
->SetFormatter(nullptr);
161 void EntryFormatter::Init()
163 m_rEntry
.connect_changed(LINK(this, EntryFormatter
, ModifyHdl
));
164 m_rEntry
.connect_focus_out(LINK(this, EntryFormatter
, FocusOutHdl
));
166 m_pSpinButton
->SetFormatter(this);
169 Selection
EntryFormatter::GetEntrySelection() const
171 int nStartPos
, nEndPos
;
172 m_rEntry
.get_selection_bounds(nStartPos
, nEndPos
);
173 return Selection(nStartPos
, nEndPos
);
176 OUString
EntryFormatter::GetEntryText() const { return m_rEntry
.get_text(); }
178 void EntryFormatter::SetEntryText(const OUString
& rText
, const Selection
& rSel
)
180 m_rEntry
.set_text(rText
);
181 auto nMin
= rSel
.Min();
182 auto nMax
= rSel
.Max();
183 m_rEntry
.select_region(nMin
< 0 ? 0 : nMin
, nMax
== SELECTION_MAX
? -1 : nMax
);
186 void EntryFormatter::SetEntryTextColor(const Color
* pColor
)
188 m_rEntry
.set_font_color(pColor
? *pColor
: COL_AUTO
);
191 void EntryFormatter::UpdateCurrentValue(double dCurrentValue
)
193 Formatter::UpdateCurrentValue(dCurrentValue
);
195 m_pSpinButton
->sync_value_from_formatter();
198 void EntryFormatter::ClearMinValue()
200 Formatter::ClearMinValue();
202 m_pSpinButton
->sync_range_from_formatter();
205 void EntryFormatter::SetMinValue(double dMin
)
207 Formatter::SetMinValue(dMin
);
209 m_pSpinButton
->sync_range_from_formatter();
212 void EntryFormatter::ClearMaxValue()
214 Formatter::ClearMaxValue();
216 m_pSpinButton
->sync_range_from_formatter();
219 void EntryFormatter::SetMaxValue(double dMin
)
221 Formatter::SetMaxValue(dMin
);
223 m_pSpinButton
->sync_range_from_formatter();
226 void EntryFormatter::SetSpinSize(double dStep
)
228 Formatter::SetSpinSize(dStep
);
230 m_pSpinButton
->sync_increments_from_formatter();
233 SelectionOptions
EntryFormatter::GetEntrySelectionOptions() const { return m_eOptions
; }
235 void EntryFormatter::FieldModified() { m_aModifyHdl
.Call(m_rEntry
); }
237 IMPL_LINK_NOARG(EntryFormatter
, ModifyHdl
, weld::Entry
&, void)
239 // This leads to FieldModified getting called at the end of Modify() and
240 // FieldModified then calls any modification callback
244 IMPL_LINK_NOARG(EntryFormatter
, FocusOutHdl
, weld::Widget
&, void)
248 m_pSpinButton
->signal_value_changed();
249 m_aFocusOutHdl
.Call(m_rEntry
);
252 DoubleNumericFormatter::DoubleNumericFormatter(weld::Entry
& rEntry
)
253 : EntryFormatter(rEntry
)
255 ResetConformanceTester();
258 DoubleNumericFormatter::DoubleNumericFormatter(weld::FormattedSpinButton
& rSpinButton
)
259 : EntryFormatter(rSpinButton
)
261 ResetConformanceTester();
264 DoubleNumericFormatter::~DoubleNumericFormatter() = default;
266 void DoubleNumericFormatter::FormatChanged(FORMAT_CHANGE_TYPE nWhat
)
268 ResetConformanceTester();
269 EntryFormatter::FormatChanged(nWhat
);
272 bool DoubleNumericFormatter::CheckText(const OUString
& sText
) const
274 // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
275 // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
276 // Thus, the roundabout way via a regular expression
277 return m_pNumberValidator
->isValidNumericFragment(sText
);
280 void DoubleNumericFormatter::ResetConformanceTester()
282 // the thousands and the decimal separator are language dependent
283 const SvNumberformat
* pFormatEntry
= GetOrCreateFormatter()->GetEntry(m_nFormatKey
);
285 sal_Unicode cSeparatorThousand
= ',';
286 sal_Unicode cSeparatorDecimal
= '.';
289 LocaleDataWrapper
aLocaleInfo(LanguageTag(pFormatEntry
->GetLanguage()));
291 OUString sSeparator
= aLocaleInfo
.getNumThousandSep();
292 if (!sSeparator
.isEmpty())
293 cSeparatorThousand
= sSeparator
[0];
295 sSeparator
= aLocaleInfo
.getNumDecimalSep();
296 if (!sSeparator
.isEmpty())
297 cSeparatorDecimal
= sSeparator
[0];
300 m_pNumberValidator
.reset(
301 new validation::NumberValidator(cSeparatorThousand
, cSeparatorDecimal
));
304 LongCurrencyFormatter::LongCurrencyFormatter(weld::Entry
& rEntry
)
305 : EntryFormatter(rEntry
)
306 , m_bThousandSep(true)
311 LongCurrencyFormatter::LongCurrencyFormatter(weld::FormattedSpinButton
& rSpinButton
)
312 : EntryFormatter(rSpinButton
)
313 , m_bThousandSep(true)
318 void LongCurrencyFormatter::Init()
320 SetOutputHdl(LINK(this, LongCurrencyFormatter
, FormatOutputHdl
));
321 SetInputHdl(LINK(this, LongCurrencyFormatter
, ParseInputHdl
));
324 void LongCurrencyFormatter::SetUseThousandSep(bool b
)
330 void LongCurrencyFormatter::SetCurrencySymbol(const OUString
& rStr
)
332 m_aCurrencySymbol
= rStr
;
336 LongCurrencyFormatter::~LongCurrencyFormatter() = default;
338 TimeFormatter::TimeFormatter(weld::Entry
& rEntry
)
339 : EntryFormatter(rEntry
)
340 , m_eFormat(TimeFieldFormat::F_NONE
)
341 , m_eTimeFormat(TimeFormat::Hour24
)
347 TimeFormatter::TimeFormatter(weld::FormattedSpinButton
& rSpinButton
)
348 : EntryFormatter(rSpinButton
)
349 , m_eFormat(TimeFieldFormat::F_NONE
)
350 , m_eTimeFormat(TimeFormat::Hour24
)
356 void TimeFormatter::Init()
358 DisableRemainderFactor(); //so with hh::mm::ss, incrementing mm will not reset ss
360 SetOutputHdl(LINK(this, TimeFormatter
, FormatOutputHdl
));
361 SetInputHdl(LINK(this, TimeFormatter
, ParseInputHdl
));
363 SetMin(tools::Time(0, 0));
364 SetMax(tools::Time(23, 59, 59, 999999999));
366 // so the spin size can depend on which zone the cursor is in
367 get_widget().connect_cursor_position(LINK(this, TimeFormatter
, CursorChangedHdl
));
368 // and set the initial spin size
369 CursorChangedHdl(get_widget());
372 void TimeFormatter::SetExtFormat(ExtTimeFieldFormat eFormat
)
376 case ExtTimeFieldFormat::Short24H
:
378 m_eTimeFormat
= TimeFormat::Hour24
;
380 m_eFormat
= TimeFieldFormat::F_NONE
;
383 case ExtTimeFieldFormat::Long24H
:
385 m_eTimeFormat
= TimeFormat::Hour24
;
387 m_eFormat
= TimeFieldFormat::F_SEC
;
390 case ExtTimeFieldFormat::Short12H
:
392 m_eTimeFormat
= TimeFormat::Hour12
;
394 m_eFormat
= TimeFieldFormat::F_NONE
;
397 case ExtTimeFieldFormat::Long12H
:
399 m_eTimeFormat
= TimeFormat::Hour12
;
401 m_eFormat
= TimeFieldFormat::F_SEC
;
404 case ExtTimeFieldFormat::ShortDuration
:
407 m_eFormat
= TimeFieldFormat::F_NONE
;
410 case ExtTimeFieldFormat::LongDuration
:
413 m_eFormat
= TimeFieldFormat::F_SEC
;
421 void TimeFormatter::SetDuration(bool bDuration
)
423 m_bDuration
= bDuration
;
427 void TimeFormatter::SetTimeFormat(TimeFieldFormat eTimeFormat
)
429 m_eFormat
= eTimeFormat
;
433 TimeFormatter::~TimeFormatter() = default;
435 DateFormatter::DateFormatter(weld::Entry
& rEntry
)
436 : EntryFormatter(rEntry
)
437 , m_eFormat(ExtDateFieldFormat::SystemShort
)
442 void DateFormatter::Init()
444 SetOutputHdl(LINK(this, DateFormatter
, FormatOutputHdl
));
445 SetInputHdl(LINK(this, DateFormatter
, ParseInputHdl
));
447 SetMin(Date(1, 1, 1900));
448 SetMax(Date(31, 12, 2200));
451 void DateFormatter::SetExtDateFormat(ExtDateFieldFormat eFormat
)
457 DateFormatter::~DateFormatter() = default;
459 PatternFormatter::PatternFormatter(weld::Entry
& rEntry
)
461 , m_bStrictFormat(false)
464 , m_bInPattKeyInput(false)
466 m_rEntry
.connect_changed(LINK(this, PatternFormatter
, ModifyHdl
));
467 m_rEntry
.connect_focus_in(LINK(this, PatternFormatter
, FocusInHdl
));
468 m_rEntry
.connect_focus_out(LINK(this, PatternFormatter
, FocusOutHdl
));
469 m_rEntry
.connect_key_press(LINK(this, PatternFormatter
, KeyInputHdl
));
472 IMPL_LINK_NOARG(PatternFormatter
, ModifyHdl
, weld::Entry
&, void) { Modify(); }
474 IMPL_LINK_NOARG(PatternFormatter
, FocusOutHdl
, weld::Widget
&, void)
477 m_aFocusOutHdl
.Call(m_rEntry
);
480 IMPL_LINK_NOARG(PatternFormatter
, FocusInHdl
, weld::Widget
&, void)
483 m_aFocusInHdl
.Call(m_rEntry
);
486 PatternFormatter::~PatternFormatter()
488 m_rEntry
.connect_changed(Link
<weld::Entry
&, void>());
489 m_rEntry
.connect_focus_out(Link
<weld::Widget
&, void>());
492 WidgetStatusListener::WidgetStatusListener(weld::Widget
* widget
, const OUString
& aCommand
)
495 const css::uno::Reference
<css::uno::XComponentContext
>& xContext
496 = ::comphelper::getProcessComponentContext();
497 css::uno::Reference
<css::frame::XDesktop2
> xDesktop
= css::frame::Desktop::create(xContext
);
499 css::uno::Reference
<css::frame::XFrame
> xFrame(xDesktop
->getActiveFrame());
503 mxFrame
= std::move(xFrame
);
505 maCommandURL
.Complete
= aCommand
;
506 css::uno::Reference
<css::util::XURLTransformer
> xParser
507 = css::util::URLTransformer::create(xContext
);
508 xParser
->parseStrict(maCommandURL
);
511 void WidgetStatusListener::startListening()
514 mxDispatch
->removeStatusListener(this, maCommandURL
);
516 css::uno::Reference
<css::frame::XDispatchProvider
> xDispatchProvider(mxFrame
,
517 css::uno::UNO_QUERY
);
518 if (!xDispatchProvider
.is())
521 mxDispatch
= xDispatchProvider
->queryDispatch(maCommandURL
, u
""_ustr
, 0);
523 mxDispatch
->addStatusListener(this, maCommandURL
);
526 void WidgetStatusListener::statusChanged(const css::frame::FeatureStateEvent
& rEvent
)
528 mWidget
->set_sensitive(rEvent
.IsEnabled
);
531 void WidgetStatusListener::disposing(const css::lang::EventObject
& /*Source*/)
536 void WidgetStatusListener::dispose()
540 mxDispatch
->removeStatusListener(this, maCommandURL
);
547 ButtonPressRepeater::ButtonPressRepeater(weld::Button
& rButton
, const Link
<Button
&, void>& rLink
,
548 const Link
<const CommandEvent
&, void>& rContextLink
)
550 , m_aRepeat("vcl ButtonPressRepeater m_aRepeat")
552 , m_aContextLink(rContextLink
)
555 // instead of connect_clicked because we want a button held down to
556 // repeat the next/prev
557 m_rButton
.connect_mouse_press(LINK(this, ButtonPressRepeater
, MousePressHdl
));
558 m_rButton
.connect_mouse_release(LINK(this, ButtonPressRepeater
, MouseReleaseHdl
));
560 m_aRepeat
.SetInvokeHandler(LINK(this, ButtonPressRepeater
, RepeatTimerHdl
));
563 IMPL_LINK(ButtonPressRepeater
, MousePressHdl
, const MouseEvent
&, rMouseEvent
, bool)
565 if (rMouseEvent
.IsRight())
568 CommandEvent(rMouseEvent
.GetPosPixel(), CommandEventId::ContextMenu
, true));
571 m_bModKey
= rMouseEvent
.IsMod1();
572 if (!m_rButton
.get_sensitive())
574 auto self
= weak_from_this();
575 RepeatTimerHdl(nullptr);
578 if (!m_rButton
.get_sensitive())
580 m_aRepeat
.SetTimeout(MouseSettings::GetButtonStartRepeat());
585 IMPL_LINK_NOARG(ButtonPressRepeater
, MouseReleaseHdl
, const MouseEvent
&, bool)
592 IMPL_LINK_NOARG(ButtonPressRepeater
, RepeatTimerHdl
, Timer
*, void)
594 m_aRepeat
.SetTimeout(Application::GetSettings().GetMouseSettings().GetButtonRepeat());
595 m_aLink
.Call(m_rButton
);
598 weld::Window
* GetPopupParent(vcl::Window
& rOutWin
, tools::Rectangle
& rRect
)
600 rRect
.SetPos(rOutWin
.OutputToScreenPixel(rRect
.TopLeft()));
601 AbsoluteScreenPixelRectangle aRectAbs
= FloatingWindow::ImplConvertToAbsPos(&rOutWin
, rRect
);
603 vcl::Window
* pWin
= rOutWin
.GetFrameWindow();
604 // resolve from a possible BorderWindow to the ClientWindow (returns itself if not)
605 pWin
= pWin
->ImplGetWindow();
607 rRect
= FloatingWindow::ImplConvertToRelPos(pWin
, aRectAbs
);
608 rRect
.SetPos(pWin
->ScreenToOutputPixel(rRect
.TopLeft()));
610 return rOutWin
.GetFrameWeld();
613 void SetPointFont(OutputDevice
& rDevice
, const vcl::Font
& rFont
, bool bUseDeviceDPI
)
615 auto pDefaultDevice
= Application::GetDefaultDevice();
617 if (vcl::Window
* pDefaultWindow
= pDefaultDevice
->GetOwnerWindow())
618 pDefaultWindow
->SetPointFont(rDevice
, rFont
, bUseDeviceDPI
);
621 ReorderingDropTarget::ReorderingDropTarget(weld::TreeView
& rTreeView
)
622 : DropTargetHelper(rTreeView
.get_drop_target())
623 , m_rTreeView(rTreeView
)
627 sal_Int8
ReorderingDropTarget::AcceptDrop(const AcceptDropEvent
& rEvt
)
629 // to enable the autoscroll when we're close to the edges
630 m_rTreeView
.get_dest_row_at_pos(rEvt
.maPosPixel
, nullptr, true);
631 return DND_ACTION_MOVE
;
634 sal_Int8
ReorderingDropTarget::ExecuteDrop(const ExecuteDropEvent
& rEvt
)
636 weld::TreeView
* pSource
= m_rTreeView
.get_drag_source();
637 // only dragging within the same widget allowed
638 if (!pSource
|| pSource
!= &m_rTreeView
)
639 return DND_ACTION_NONE
;
641 std::unique_ptr
<weld::TreeIter
> xSource(m_rTreeView
.make_iterator());
642 if (!m_rTreeView
.get_selected(xSource
.get()))
643 return DND_ACTION_NONE
;
645 std::unique_ptr
<weld::TreeIter
> xTarget(m_rTreeView
.make_iterator());
647 if (m_rTreeView
.get_dest_row_at_pos(rEvt
.maPosPixel
, xTarget
.get(), true))
648 nTargetPos
= m_rTreeView
.get_iter_index_in_parent(*xTarget
);
649 m_rTreeView
.move_subtree(*xSource
, nullptr, nTargetPos
);
651 return DND_ACTION_NONE
;
655 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */