bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / app / weldutils.cxx
blobf67ea73d89a31263fcbd90dce1384faf0a2604f5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
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 {}
39 namespace weld
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()
81 if (m_xRelocate)
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)
102 switch (eState)
104 case TRISTATE_INDET:
105 rToggle.set_state(TRISTATE_FALSE);
106 break;
107 case TRISTATE_TRUE:
108 rToggle.set_state(TRISTATE_INDET);
109 break;
110 case TRISTATE_FALSE:
111 rToggle.set_state(TRISTATE_TRUE);
112 break;
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))
124 xNewParent.reset();
126 while (true)
128 std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator(&rParent));
129 if (!rTreeView.iter_children(*xChild))
130 break;
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())
142 Init();
145 EntryFormatter::EntryFormatter(weld::Entry& rEntry)
146 : m_rEntry(rEntry)
147 , m_pSpinButton(nullptr)
148 , m_eOptions(Application::GetSettings().GetStyleSettings().GetSelectionOptions())
150 Init();
153 EntryFormatter::~EntryFormatter()
155 m_rEntry.connect_changed(Link<weld::Entry&, void>());
156 m_rEntry.connect_focus_out(Link<weld::Widget&, void>());
157 if (m_pSpinButton)
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));
165 if (m_pSpinButton)
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);
194 if (m_pSpinButton)
195 m_pSpinButton->sync_value_from_formatter();
198 void EntryFormatter::ClearMinValue()
200 Formatter::ClearMinValue();
201 if (m_pSpinButton)
202 m_pSpinButton->sync_range_from_formatter();
205 void EntryFormatter::SetMinValue(double dMin)
207 Formatter::SetMinValue(dMin);
208 if (m_pSpinButton)
209 m_pSpinButton->sync_range_from_formatter();
212 void EntryFormatter::ClearMaxValue()
214 Formatter::ClearMaxValue();
215 if (m_pSpinButton)
216 m_pSpinButton->sync_range_from_formatter();
219 void EntryFormatter::SetMaxValue(double dMin)
221 Formatter::SetMaxValue(dMin);
222 if (m_pSpinButton)
223 m_pSpinButton->sync_range_from_formatter();
226 void EntryFormatter::SetSpinSize(double dStep)
228 Formatter::SetSpinSize(dStep);
229 if (m_pSpinButton)
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
241 Modify();
244 IMPL_LINK_NOARG(EntryFormatter, FocusOutHdl, weld::Widget&, void)
246 EntryLostFocus();
247 if (m_pSpinButton)
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 = '.';
287 if (pFormatEntry)
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)
308 Init();
311 LongCurrencyFormatter::LongCurrencyFormatter(weld::FormattedSpinButton& rSpinButton)
312 : EntryFormatter(rSpinButton)
313 , m_bThousandSep(true)
315 Init();
318 void LongCurrencyFormatter::Init()
320 SetOutputHdl(LINK(this, LongCurrencyFormatter, FormatOutputHdl));
321 SetInputHdl(LINK(this, LongCurrencyFormatter, ParseInputHdl));
324 void LongCurrencyFormatter::SetUseThousandSep(bool b)
326 m_bThousandSep = b;
327 ReFormat();
330 void LongCurrencyFormatter::SetCurrencySymbol(const OUString& rStr)
332 m_aCurrencySymbol = rStr;
333 ReFormat();
336 LongCurrencyFormatter::~LongCurrencyFormatter() = default;
338 TimeFormatter::TimeFormatter(weld::Entry& rEntry)
339 : EntryFormatter(rEntry)
340 , m_eFormat(TimeFieldFormat::F_NONE)
341 , m_eTimeFormat(TimeFormat::Hour24)
342 , m_bDuration(false)
344 Init();
347 TimeFormatter::TimeFormatter(weld::FormattedSpinButton& rSpinButton)
348 : EntryFormatter(rSpinButton)
349 , m_eFormat(TimeFieldFormat::F_NONE)
350 , m_eTimeFormat(TimeFormat::Hour24)
351 , m_bDuration(false)
353 Init();
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)
374 switch (eFormat)
376 case ExtTimeFieldFormat::Short24H:
378 m_eTimeFormat = TimeFormat::Hour24;
379 m_bDuration = false;
380 m_eFormat = TimeFieldFormat::F_NONE;
382 break;
383 case ExtTimeFieldFormat::Long24H:
385 m_eTimeFormat = TimeFormat::Hour24;
386 m_bDuration = false;
387 m_eFormat = TimeFieldFormat::F_SEC;
389 break;
390 case ExtTimeFieldFormat::Short12H:
392 m_eTimeFormat = TimeFormat::Hour12;
393 m_bDuration = false;
394 m_eFormat = TimeFieldFormat::F_NONE;
396 break;
397 case ExtTimeFieldFormat::Long12H:
399 m_eTimeFormat = TimeFormat::Hour12;
400 m_bDuration = false;
401 m_eFormat = TimeFieldFormat::F_SEC;
403 break;
404 case ExtTimeFieldFormat::ShortDuration:
406 m_bDuration = true;
407 m_eFormat = TimeFieldFormat::F_NONE;
409 break;
410 case ExtTimeFieldFormat::LongDuration:
412 m_bDuration = true;
413 m_eFormat = TimeFieldFormat::F_SEC;
415 break;
418 ReFormat();
421 void TimeFormatter::SetDuration(bool bDuration)
423 m_bDuration = bDuration;
424 ReFormat();
427 void TimeFormatter::SetTimeFormat(TimeFieldFormat eTimeFormat)
429 m_eFormat = eTimeFormat;
430 ReFormat();
433 TimeFormatter::~TimeFormatter() = default;
435 DateFormatter::DateFormatter(weld::Entry& rEntry)
436 : EntryFormatter(rEntry)
437 , m_eFormat(ExtDateFieldFormat::SystemShort)
439 Init();
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)
453 m_eFormat = eFormat;
454 ReFormat();
457 DateFormatter::~DateFormatter() = default;
459 PatternFormatter::PatternFormatter(weld::Entry& rEntry)
460 : m_rEntry(rEntry)
461 , m_bStrictFormat(false)
462 , m_bSameMask(true)
463 , m_bReformat(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)
476 EntryLostFocus();
477 m_aFocusOutHdl.Call(m_rEntry);
480 IMPL_LINK_NOARG(PatternFormatter, FocusInHdl, weld::Widget&, void)
482 EntryGainFocus();
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 int GetMinimumEditHeight()
494 // load this little .ui just to measure the height of an Entry
495 std::unique_ptr<weld::Builder> xBuilder(
496 Application::CreateBuilder(nullptr, "cui/ui/namedialog.ui"));
497 std::unique_ptr<weld::Entry> xEntry(xBuilder->weld_entry("name_entry"));
498 return xEntry->get_preferred_size().Height();
501 WidgetStatusListener::WidgetStatusListener(weld::Widget* widget, const OUString& aCommand)
502 : mWidget(widget)
504 css::uno::Reference<css::uno::XComponentContext> xContext
505 = ::comphelper::getProcessComponentContext();
506 css::uno::Reference<css::frame::XDesktop2> xDesktop = css::frame::Desktop::create(xContext);
508 css::uno::Reference<css::frame::XFrame> xFrame(xDesktop->getActiveFrame());
509 if (!xFrame.is())
510 xFrame = xDesktop;
512 mxFrame = xFrame;
514 maCommandURL.Complete = aCommand;
515 css::uno::Reference<css::util::XURLTransformer> xParser
516 = css::util::URLTransformer::create(xContext);
517 xParser->parseStrict(maCommandURL);
520 void WidgetStatusListener::startListening()
522 if (mxDispatch.is())
523 mxDispatch->removeStatusListener(this, maCommandURL);
525 css::uno::Reference<css::frame::XDispatchProvider> xDispatchProvider(mxFrame,
526 css::uno::UNO_QUERY);
527 if (!xDispatchProvider.is())
528 return;
530 mxDispatch = xDispatchProvider->queryDispatch(maCommandURL, "", 0);
531 if (mxDispatch.is())
532 mxDispatch->addStatusListener(this, maCommandURL);
535 void WidgetStatusListener::statusChanged(const css::frame::FeatureStateEvent& rEvent)
537 mWidget->set_sensitive(rEvent.IsEnabled);
540 void WidgetStatusListener::disposing(const css::lang::EventObject& /*Source*/)
542 mxDispatch.clear();
545 void WidgetStatusListener::dispose()
547 if (mxDispatch.is())
549 mxDispatch->removeStatusListener(this, maCommandURL);
550 mxDispatch.clear();
552 mxFrame.clear();
553 mWidget = nullptr;
556 ButtonPressRepeater::ButtonPressRepeater(weld::Button& rButton, const Link<Button&, void>& rLink,
557 const Link<const CommandEvent&, void>& rContextLink)
558 : m_rButton(rButton)
559 , m_aRepeat("vcl ButtonPressRepeater m_aRepeat")
560 , m_aLink(rLink)
561 , m_aContextLink(rContextLink)
562 , m_bModKey(false)
564 // instead of connect_clicked because we want a button held down to
565 // repeat the next/prev
566 m_rButton.connect_mouse_press(LINK(this, ButtonPressRepeater, MousePressHdl));
567 m_rButton.connect_mouse_release(LINK(this, ButtonPressRepeater, MouseReleaseHdl));
569 m_aRepeat.SetInvokeHandler(LINK(this, ButtonPressRepeater, RepeatTimerHdl));
572 IMPL_LINK(ButtonPressRepeater, MousePressHdl, const MouseEvent&, rMouseEvent, bool)
574 if (rMouseEvent.IsRight())
576 m_aContextLink.Call(
577 CommandEvent(rMouseEvent.GetPosPixel(), CommandEventId::ContextMenu, true));
578 return false;
580 m_bModKey = rMouseEvent.IsMod1();
581 if (!m_rButton.get_sensitive())
582 return false;
583 auto self = weak_from_this();
584 RepeatTimerHdl(nullptr);
585 if (!self.lock())
586 return false;
587 if (!m_rButton.get_sensitive())
588 return false;
589 m_aRepeat.SetTimeout(MouseSettings::GetButtonStartRepeat());
590 m_aRepeat.Start();
591 return true;
594 IMPL_LINK_NOARG(ButtonPressRepeater, MouseReleaseHdl, const MouseEvent&, bool)
596 m_bModKey = false;
597 m_aRepeat.Stop();
598 return true;
601 IMPL_LINK_NOARG(ButtonPressRepeater, RepeatTimerHdl, Timer*, void)
603 m_aRepeat.SetTimeout(Application::GetSettings().GetMouseSettings().GetButtonRepeat());
604 m_aLink.Call(m_rButton);
607 weld::Window* GetPopupParent(vcl::Window& rOutWin, tools::Rectangle& rRect)
609 rRect.SetPos(rOutWin.OutputToScreenPixel(rRect.TopLeft()));
610 rRect = FloatingWindow::ImplConvertToAbsPos(&rOutWin, rRect);
612 vcl::Window* pWin = rOutWin.GetFrameWindow();
614 rRect = FloatingWindow::ImplConvertToRelPos(pWin, rRect);
615 rRect.SetPos(pWin->ScreenToOutputPixel(rRect.TopLeft()));
617 return rOutWin.GetFrameWeld();
620 void SetPointFont(OutputDevice& rDevice, const vcl::Font& rFont)
622 auto pDefaultDevice = Application::GetDefaultDevice();
623 if (pDefaultDevice)
624 if (vcl::Window* pDefaultWindow = pDefaultDevice->GetOwnerWindow())
625 pDefaultWindow->SetPointFont(rDevice, rFont);
628 ReorderingDropTarget::ReorderingDropTarget(weld::TreeView& rTreeView)
629 : DropTargetHelper(rTreeView.get_drop_target())
630 , m_rTreeView(rTreeView)
634 sal_Int8 ReorderingDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
636 // to enable the autoscroll when we're close to the edges
637 m_rTreeView.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
638 return DND_ACTION_MOVE;
641 sal_Int8 ReorderingDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
643 weld::TreeView* pSource = m_rTreeView.get_drag_source();
644 // only dragging within the same widget allowed
645 if (!pSource || pSource != &m_rTreeView)
646 return DND_ACTION_NONE;
648 std::unique_ptr<weld::TreeIter> xSource(m_rTreeView.make_iterator());
649 if (!m_rTreeView.get_selected(xSource.get()))
650 return DND_ACTION_NONE;
652 std::unique_ptr<weld::TreeIter> xTarget(m_rTreeView.make_iterator());
653 int nTargetPos = -1;
654 if (m_rTreeView.get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true))
655 nTargetPos = m_rTreeView.get_iter_index_in_parent(*xTarget);
656 m_rTreeView.move_subtree(*xSource, nullptr, nTargetPos);
658 return DND_ACTION_NONE;
662 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */