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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_feature_desktop.h>
24 #include <UIKit/UIKit.h>
28 #include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
29 #include <comphelper/lok.hxx>
30 #include <comphelper/scopeguard.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <o3tl/test_info.hxx>
33 #include <officecfg/Office/Common.hxx>
34 #include <osl/diagnose.h>
40 #include <salinst.hxx>
42 #include <rtl/bootstrap.hxx>
43 #include <rtl/strbuf.hxx>
44 #include <sal/log.hxx>
46 #include <vcl/abstdlg.hxx>
47 #include <vcl/builder.hxx>
48 #include <vcl/toolkit/floatwin.hxx>
49 #include <vcl/layout.hxx>
50 #include <vcl/svapp.hxx>
51 #include <vcl/event.hxx>
52 #include <vcl/locktoplevels.hxx>
53 #include <vcl/wrkwin.hxx>
54 #include <vcl/toolkit/button.hxx>
55 #include <vcl/mnemonic.hxx>
56 #include <vcl/toolkit/dialog.hxx>
57 #include <vcl/dialoghelper.hxx>
58 #include <vcl/settings.hxx>
59 #include <vcl/virdev.hxx>
60 #include <vcl/weld.hxx>
61 #include <vcl/uitest/uiobject.hxx>
62 #include <vcl/uitest/logger.hxx>
63 #include <vcl/IDialogRenderable.hxx>
64 #include <messagedialog.hxx>
65 #include <salframe.hxx>
66 #include <tools/json_writer.hxx>
73 static OString
ImplGetDialogText( Dialog
* pDialog
)
75 OUString
aErrorStr(pDialog
->GetText());
78 if (MessageDialog
* pMessDialog
= dynamic_cast<MessageDialog
*>(pDialog
))
80 sMessage
= pMessDialog
->get_primary_text();
83 if (!sMessage
.isEmpty())
85 aErrorStr
+= ", " + sMessage
;
87 return OUStringToOString(aErrorStr
, RTL_TEXTENCODING_UTF8
);
90 static bool ImplIsMnemonicCtrl( vcl::Window
* pWindow
)
92 if( ! pWindow
->GetSettings().GetStyleSettings().GetAutoMnemonic() )
95 if ( (pWindow
->GetType() == WindowType::RADIOBUTTON
) ||
96 (pWindow
->GetType() == WindowType::CHECKBOX
) ||
97 (pWindow
->GetType() == WindowType::TRISTATEBOX
) ||
98 (pWindow
->GetType() == WindowType::PUSHBUTTON
) )
101 if ( pWindow
->GetType() == WindowType::FIXEDTEXT
)
103 FixedText
*pText
= static_cast<FixedText
*>(pWindow
);
104 if (pText
->get_mnemonic_widget())
106 //This is the legacy pre-layout logic which we retain
107 //until we can be sure we can remove it
108 if (pWindow
->GetStyle() & WB_NOLABEL
)
110 vcl::Window
* pNextWindow
= pWindow
->GetWindow( GetWindowType::Next
);
113 pNextWindow
= pNextWindow
->GetWindow( GetWindowType::Client
);
114 return !(!(pNextWindow
->GetStyle() & WB_TABSTOP
) ||
115 (pNextWindow
->GetType() == WindowType::FIXEDTEXT
) ||
116 (pNextWindow
->GetType() == WindowType::GROUPBOX
) ||
117 (pNextWindow
->GetType() == WindowType::RADIOBUTTON
) ||
118 (pNextWindow
->GetType() == WindowType::CHECKBOX
) ||
119 (pNextWindow
->GetType() == WindowType::TRISTATEBOX
) ||
120 (pNextWindow
->GetType() == WindowType::PUSHBUTTON
));
126 // Called by native error dialog popup implementations
127 void ImplHideSplash()
129 ImplSVData
* pSVData
= ImplGetSVData();
130 if( pSVData
->mpIntroWindow
)
131 pSVData
->mpIntroWindow
->Hide();
134 vcl::Window
* nextLogicalChildOfParent(const vcl::Window
*pTopLevel
, const vcl::Window
*pChild
)
136 const vcl::Window
*pLastChild
= pChild
;
138 if (pChild
->GetType() == WindowType::SCROLLWINDOW
)
139 pChild
= static_cast<const VclScrolledWindow
*>(pChild
)->get_child();
140 else if (isContainerWindow(*pChild
))
141 pChild
= pChild
->GetWindow(GetWindowType::FirstChild
);
143 pChild
= pChild
->GetWindow(GetWindowType::Next
);
147 vcl::Window
*pParent
= pLastChild
->GetParent();
150 if (pParent
== pTopLevel
)
152 pLastChild
= pParent
;
153 pChild
= pParent
->GetWindow(GetWindowType::Next
);
156 if (isContainerWindow(*pChild
))
157 pChild
= nextLogicalChildOfParent(pTopLevel
, pChild
);
159 return const_cast<vcl::Window
*>(pChild
);
162 vcl::Window
* prevLogicalChildOfParent(const vcl::Window
*pTopLevel
, const vcl::Window
*pChild
)
164 const vcl::Window
*pLastChild
= pChild
;
166 if (pChild
->GetType() == WindowType::SCROLLWINDOW
)
167 pChild
= static_cast<const VclScrolledWindow
*>(pChild
)->get_child();
168 else if (isContainerWindow(*pChild
))
169 pChild
= pChild
->GetWindow(GetWindowType::LastChild
);
171 pChild
= pChild
->GetWindow(GetWindowType::Prev
);
175 vcl::Window
*pParent
= pLastChild
->GetParent();
178 if (pParent
== pTopLevel
)
180 pLastChild
= pParent
;
181 pChild
= pParent
->GetWindow(GetWindowType::Prev
);
184 if (isContainerWindow(*pChild
))
185 pChild
= prevLogicalChildOfParent(pTopLevel
, pChild
);
187 return const_cast<vcl::Window
*>(pChild
);
190 vcl::Window
* firstLogicalChildOfParent(const vcl::Window
*pTopLevel
)
192 const vcl::Window
*pChild
= pTopLevel
->GetWindow(GetWindowType::FirstChild
);
193 if (pChild
&& isContainerWindow(*pChild
))
194 pChild
= nextLogicalChildOfParent(pTopLevel
, pChild
);
195 return const_cast<vcl::Window
*>(pChild
);
198 vcl::Window
* lastLogicalChildOfParent(const vcl::Window
*pTopLevel
)
200 const vcl::Window
*pChild
= pTopLevel
->GetWindow(GetWindowType::LastChild
);
201 if (pChild
&& isContainerWindow(*pChild
))
202 pChild
= prevLogicalChildOfParent(pTopLevel
, pChild
);
203 return const_cast<vcl::Window
*>(pChild
);
206 void GenerateAutoMnemonicsOnHierarchy(const vcl::Window
* pWindow
)
208 MnemonicGenerator aMnemonicGenerator
;
209 vcl::Window
* pGetChild
;
212 // register the assigned mnemonics
213 pGetChild
= pWindow
->GetWindow( GetWindowType::FirstChild
);
216 pChild
= pGetChild
->ImplGetWindow();
217 aMnemonicGenerator
.RegisterMnemonic( pChild
->GetText() );
218 pGetChild
= nextLogicalChildOfParent(pWindow
, pGetChild
);
221 // take the Controls of the dialog into account for TabPages
222 if ( pWindow
->GetType() == WindowType::TABPAGE
)
224 vcl::Window
* pParent
= pWindow
->GetParent();
225 if (pParent
&& pParent
->GetType() == WindowType::TABCONTROL
)
226 pParent
= pParent
->GetParent();
228 if (pParent
&& (pParent
->GetStyle() & (WB_DIALOGCONTROL
| WB_NODIALOGCONTROL
)) == WB_DIALOGCONTROL
)
230 pGetChild
= pParent
->GetWindow( GetWindowType::FirstChild
);
233 pChild
= pGetChild
->ImplGetWindow();
234 aMnemonicGenerator
.RegisterMnemonic( pChild
->GetText() );
235 pGetChild
= nextLogicalChildOfParent(pWindow
, pGetChild
);
240 // assign mnemonics to Controls which have none
241 pGetChild
= pWindow
->GetWindow( GetWindowType::FirstChild
);
244 pChild
= pGetChild
->ImplGetWindow();
245 if ( ImplIsMnemonicCtrl( pChild
) )
247 OUString aText
= pChild
->GetText();
248 OUString aNewText
= aMnemonicGenerator
.CreateMnemonic( aText
);
249 if ( aText
!= aNewText
)
250 pChild
->SetText( aNewText
);
253 pGetChild
= nextLogicalChildOfParent(pWindow
, pGetChild
);
257 static VclButtonBox
* getActionArea(Dialog
const *pDialog
)
259 VclButtonBox
*pButtonBox
= nullptr;
260 if (pDialog
->isLayoutEnabled())
262 vcl::Window
*pBox
= pDialog
->GetWindow(GetWindowType::FirstChild
);
263 vcl::Window
*pChild
= pBox
->GetWindow(GetWindowType::LastChild
);
266 pButtonBox
= dynamic_cast<VclButtonBox
*>(pChild
);
269 pChild
= pChild
->GetWindow(GetWindowType::Prev
);
275 static vcl::Window
* getActionAreaButtonList(Dialog
const *pDialog
)
277 VclButtonBox
* pButtonBox
= getActionArea(pDialog
);
279 return pButtonBox
->GetWindow(GetWindowType::FirstChild
);
280 return pDialog
->GetWindow(GetWindowType::FirstChild
);
283 static PushButton
* ImplGetDefaultButton( Dialog
const * pDialog
)
285 vcl::Window
* pChild
= getActionAreaButtonList(pDialog
);
288 if ( pChild
->ImplIsPushButton() )
290 PushButton
* pPushButton
= static_cast<PushButton
*>(pChild
);
291 if ( pPushButton
->ImplIsDefButton() )
295 pChild
= pChild
->GetWindow( GetWindowType::Next
);
301 static PushButton
* ImplGetOKButton( Dialog
const * pDialog
)
303 vcl::Window
* pChild
= getActionAreaButtonList(pDialog
);
306 if ( pChild
->GetType() == WindowType::OKBUTTON
)
307 return static_cast<PushButton
*>(pChild
);
309 pChild
= pChild
->GetWindow( GetWindowType::Next
);
315 static PushButton
* ImplGetCancelButton( Dialog
const * pDialog
)
317 vcl::Window
* pChild
= getActionAreaButtonList(pDialog
);
321 if ( pChild
->GetType() == WindowType::CANCELBUTTON
)
322 return static_cast<PushButton
*>(pChild
);
324 pChild
= pChild
->GetWindow( GetWindowType::Next
);
330 static void ImplMouseAutoPos( Dialog
* pDialog
)
332 MouseSettingsOptions nMouseOptions
= pDialog
->GetSettings().GetMouseSettings().GetOptions();
333 if ( nMouseOptions
& MouseSettingsOptions::AutoCenterPos
)
335 Size aSize
= pDialog
->GetOutputSizePixel();
336 pDialog
->SetPointerPosPixel( Point( aSize
.Width()/2, aSize
.Height()/2 ) );
338 else if ( nMouseOptions
& MouseSettingsOptions::AutoDefBtnPos
)
340 vcl::Window
* pWindow
= ImplGetDefaultButton( pDialog
);
342 pWindow
= ImplGetOKButton( pDialog
);
344 pWindow
= ImplGetCancelButton( pDialog
);
347 Size aSize
= pWindow
->GetOutputSizePixel();
348 pWindow
->SetPointerPosPixel( Point( aSize
.Width()/2, aSize
.Height()/2 ) );
354 std::vector
<VclPtr
<PushButton
>> maOwnedButtons
;
355 std::map
<VclPtr
<vcl::Window
>, short> maResponses
;
356 tools::Long mnResult
;
358 VclAbstractDialog::AsyncContext maEndCtx
;
359 Link
<const CommandEvent
&, bool> m_aPopupMenuHdl
;
360 Link
<void*, vcl::ILibreOfficeKitNotifier
*> m_aInstallLOKNotifierHdl
;
361 bool m_bLOKTunneling
;
363 DialogImpl() : mnResult( -1 ), mbStartedModal( false ), m_bLOKTunneling( true ) {}
366 short get_response(vcl::Window
*pWindow
) const
368 auto aFind
= maResponses
.find(pWindow
);
369 if (aFind
!= maResponses
.end())
370 return aFind
->second
;
377 for (VclPtr
<PushButton
> & pOwnedButton
: maOwnedButtons
)
378 pOwnedButton
.disposeAndClear();
382 void Dialog::disposeOwnedButtons()
384 for (VclPtr
<PushButton
> & pOwnedButton
: mpDialogImpl
->maOwnedButtons
)
385 pOwnedButton
.disposeAndClear();
388 void Dialog::ImplInitDialogData()
390 mpWindowImpl
->mbDialog
= true;
392 mbInSyncExecute
= false;
395 mpContentArea
.clear();
396 mpActionArea
.clear();
397 mnMousePositioned
= 0;
398 mpDialogImpl
.reset(new DialogImpl
);
401 void Dialog::PixelInvalidate(const tools::Rectangle
* pRectangle
)
403 if (!mpDialogImpl
->m_bLOKTunneling
)
406 Window::PixelInvalidate(pRectangle
);
409 vcl::Window
* Dialog::GetDefaultParent(WinBits nStyle
)
411 vcl::Window
* pParent
= Dialog::GetDefDialogParent();
412 if (!pParent
&& !(nStyle
& WB_SYSTEMWINDOW
))
413 pParent
= ImplGetSVData()->maFrameData
.mpAppWin
;
415 // If Parent is disabled, then we search for a modal dialog
417 if (pParent
&& (!pParent
->IsInputEnabled() || pParent
->IsInModalMode()))
419 ImplSVData
* pSVData
= ImplGetSVData();
420 auto& rExecuteDialogs
= pSVData
->mpWinData
->mpExecuteDialogs
;
421 auto it
= std::find_if(rExecuteDialogs
.rbegin(), rExecuteDialogs
.rend(),
422 [&pParent
](VclPtr
<Dialog
>& rDialogPtr
) {
423 return pParent
->ImplGetFirstOverlapWindow() &&
424 pParent
->ImplGetFirstOverlapWindow()->IsWindowOrChild(rDialogPtr
, true) &&
425 rDialogPtr
->IsReallyVisible() && rDialogPtr
->IsEnabled() &&
426 rDialogPtr
->IsInputEnabled() && !rDialogPtr
->IsInModalMode(); });
427 if (it
!= rExecuteDialogs
.rend())
434 VclPtr
<vcl::Window
> Dialog::AddBorderWindow(vcl::Window
* pParent
, WinBits nStyle
)
436 VclPtrInstance
<ImplBorderWindow
> pBorderWin( pParent
, nStyle
, BorderWindowStyle::Frame
);
437 ImplInit( pBorderWin
, nStyle
& ~WB_BORDER
, nullptr );
438 pBorderWin
->mpWindowImpl
->mpClientWindow
= this;
439 pBorderWin
->GetBorder( mpWindowImpl
->mnLeftBorder
, mpWindowImpl
->mnTopBorder
, mpWindowImpl
->mnRightBorder
, mpWindowImpl
->mnBottomBorder
);
440 mpWindowImpl
->mpBorderWindow
= pBorderWin
;
441 mpWindowImpl
->mpRealParent
= pParent
;
446 void Dialog::ImplInitDialog( vcl::Window
* pParent
, WinBits nStyle
, InitFlag eFlag
)
448 SystemWindowFlags nSysWinMode
= Application::GetSystemWindowMode();
450 if ( !(nStyle
& WB_NODIALOGCONTROL
) )
451 nStyle
|= WB_DIALOGCONTROL
;
453 // Now, all Dialogs are per default system windows !!!
454 nStyle
|= WB_SYSTEMWINDOW
;
456 if (InitFlag::NoParent
== eFlag
)
460 else if (!pParent
) // parent is NULL: get the default Dialog parent
462 pParent
= Dialog::GetDefaultParent(nStyle
);
465 if ( !pParent
|| (nStyle
& WB_SYSTEMWINDOW
) ||
466 (pParent
->mpWindowImpl
->mpFrameData
->mbNeedSysWindow
&& !(nSysWinMode
& SystemWindowFlags::NOAUTOMODE
)) ||
467 (nSysWinMode
& SystemWindowFlags::DIALOG
) )
469 // create window with a small border ?
470 if ((nStyle
& WB_ALLOWMENUBAR
) || ((nStyle
& (WB_BORDER
| WB_NOBORDER
| WB_MOVEABLE
| WB_SIZEABLE
| WB_CLOSEABLE
)) == WB_BORDER
))
472 AddBorderWindow(pParent
, nStyle
);
476 mpWindowImpl
->mbFrame
= true;
477 mpWindowImpl
->mbOverlapWin
= true;
478 ImplInit( pParent
, (nStyle
& (WB_MOVEABLE
| WB_SIZEABLE
| WB_STANDALONE
)) | WB_CLOSEABLE
, nullptr );
479 // Now set all style bits
480 mpWindowImpl
->mnStyle
= nStyle
;
485 VclPtrInstance
<ImplBorderWindow
> pBorderWin( pParent
, nStyle
, BorderWindowStyle::Overlap
);
486 ImplInit( pBorderWin
, nStyle
& ~WB_BORDER
, nullptr );
487 pBorderWin
->mpWindowImpl
->mpClientWindow
= this;
488 pBorderWin
->GetBorder( mpWindowImpl
->mnLeftBorder
, mpWindowImpl
->mnTopBorder
, mpWindowImpl
->mnRightBorder
, mpWindowImpl
->mnBottomBorder
);
489 mpWindowImpl
->mpBorderWindow
= pBorderWin
;
490 mpWindowImpl
->mpRealParent
= pParent
;
493 SetActivateMode( ActivateModeFlags::GrabFocus
);
498 void Dialog::ApplySettings(vcl::RenderContext
& rRenderContext
)
500 if (IsControlBackground())
503 SetBackground(GetControlBackground());
505 else if (rRenderContext
.IsNativeControlSupported(ControlType::WindowBackground
, ControlPart::BackgroundDialog
))
508 mpWindowImpl
->mnNativeBackground
= ControlPart::BackgroundDialog
;
509 EnableChildTransparentMode();
513 // fallback to settings color
514 rRenderContext
.SetBackground(GetSettings().GetStyleSettings().GetDialogColor());
518 void Dialog::ImplInitSettings()
521 if (IsControlBackground())
522 SetBackground(GetControlBackground());
524 else if( IsNativeControlSupported(ControlType::WindowBackground
, ControlPart::BackgroundDialog
))
526 mpWindowImpl
->mnNativeBackground
= ControlPart::BackgroundDialog
;
527 EnableChildTransparentMode();
529 // fallback to settings color
531 SetBackground(GetSettings().GetStyleSettings().GetDialogColor());
534 void Dialog::ImplLOKNotifier(vcl::Window
* pParent
)
536 if (comphelper::LibreOfficeKit::isActive() && pParent
)
538 if (VclPtr
<vcl::Window
> pWin
= pParent
->GetParentWithLOKNotifier())
540 SetLOKNotifier(pWin
->GetLOKNotifier());
545 Dialog::Dialog( WindowType nType
)
546 : SystemWindow( nType
, "vcl::Dialog maLayoutIdle" )
547 , mnInitFlag(InitFlag::Default
)
549 ImplInitDialogData();
552 void VclBuilderContainer::disposeBuilder()
555 m_pUIBuilder
->disposeBuilder();
558 OUString
AllSettings::GetUIRootDir()
560 OUString
sShareLayer(u
"$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/config/soffice.cfg/"_ustr
);
561 rtl::Bootstrap::expandMacros(sShareLayer
);
565 //we can't change sizeable after the fact, so need to defer until we know and then do the init.
566 void Dialog::ImplDeferredInit(vcl::Window
* pParent
, WinBits nBits
)
568 ImplInitDialog(pParent
, nBits
| WB_BORDER
, mnInitFlag
);
571 Dialog::Dialog(vcl::Window
* pParent
, const OUString
& rID
, const OUString
& rUIXMLDescription
)
572 : SystemWindow(WindowType::DIALOG
, "vcl::Dialog maLayoutIdle")
573 , mnInitFlag(InitFlag::Default
)
575 ImplLOKNotifier(pParent
);
576 ImplInitDialogData();
577 loadUI(pParent
, rID
, rUIXMLDescription
);
580 Dialog::Dialog(vcl::Window
* pParent
, WinBits nStyle
, InitFlag eFlag
)
581 : SystemWindow(WindowType::DIALOG
, "vcl::Dialog maLayoutIdle")
584 ImplLOKNotifier(pParent
);
585 ImplInitDialogData();
586 ImplInitDialog( pParent
, nStyle
, eFlag
);
589 void Dialog::set_action_area(VclButtonBox
* pBox
)
591 mpActionArea
.set(pBox
);
594 const DialogStyle
& rDialogStyle
=
595 GetSettings().GetStyleSettings().GetDialogStyle();
596 pBox
->set_border_width(rDialogStyle
.action_area_border
);
600 void Dialog::set_content_area(VclBox
* pBox
)
602 mpContentArea
.set(pBox
);
605 void Dialog::settingOptimalLayoutSize(Window
*pBox
)
607 const DialogStyle
& rDialogStyle
=
608 GetSettings().GetStyleSettings().GetDialogStyle();
609 VclBox
* pBox2
= static_cast<VclBox
*>(pBox
);
610 pBox2
->set_border_width(rDialogStyle
.content_area_border
);
618 void Dialog::dispose()
620 bool bTunnelingEnabled
= mpDialogImpl
->m_bLOKTunneling
;
622 mpDialogImpl
.reset();
624 mpActionArea
.clear();
625 mpContentArea
.clear();
627 const css::uno::Reference
< css::uno::XComponentContext
>& xContext(
628 comphelper::getProcessComponentContext() );
629 css::uno::Reference
<css::frame::XGlobalEventBroadcaster
> xEventBroadcaster(css::frame::theGlobalEventBroadcaster::get(xContext
), css::uno::UNO_SET_THROW
);
630 css::document::DocumentEvent aObject
;
631 aObject
.EventName
= "DialogClosed";
632 aObject
.Supplement
<<= GetText(); // title
633 xEventBroadcaster
->documentEventOccured(aObject
);
634 UITestLogger::getInstance().log(u
"Close Dialog");
636 if (comphelper::LibreOfficeKit::isActive())
638 if(const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier())
640 if (bTunnelingEnabled
)
641 pNotifier
->notifyWindow(GetLOKWindowId(), u
"close"_ustr
);
642 ReleaseLOKNotifier();
646 SystemWindow::dispose();
649 IMPL_LINK_NOARG(Dialog
, ImplAsyncCloseHdl
, void*, void)
654 bool Dialog::EventNotify( NotifyEvent
& rNEvt
)
656 // first call the base class due to Tab control
657 bool bRet
= SystemWindow::EventNotify( rNEvt
);
660 if ( rNEvt
.GetType() == NotifyEventType::KEYINPUT
)
662 const KeyEvent
* pKEvt
= rNEvt
.GetKeyEvent();
663 vcl::KeyCode aKeyCode
= pKEvt
->GetKeyCode();
664 sal_uInt16 nKeyCode
= aKeyCode
.GetCode();
666 if ( (nKeyCode
== KEY_ESCAPE
) &&
667 ((GetStyle() & WB_CLOSEABLE
) || ImplGetCancelButton( this ) || ImplGetOKButton( this )) )
669 // #i89505# for the benefit of slightly mentally challenged implementations
670 // like e.g. SfxModelessDialog which destroy themselves inside Close()
671 // post this Close asynchronous so we can leave our key handler before
673 PostUserEvent( LINK( this, Dialog
, ImplAsyncCloseHdl
), nullptr, true);
677 else if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
679 // make sure the dialog is still modal
680 // changing focus between application frames may
681 // have re-enabled input for our parent
682 if( mbInExecute
&& mbModalMode
)
684 ImplSetModalInputMode( false );
685 ImplSetModalInputMode( true );
687 // #93022# def-button might have changed after show
688 if( !mnMousePositioned
)
690 mnMousePositioned
= 1;
691 ImplMouseAutoPos( this );
701 //What we really want here is something that gives the available width and
702 //height of a users screen, taking away the space taken up the OS
703 //taskbar, menus, etc.
704 Size
bestmaxFrameSizeForScreenSize(const Size
&rScreenSize
)
707 tools::Long w
= rScreenSize
.Width();
715 tools::Long h
= rScreenSize
.Height();
721 return Size(std::max
<tools::Long
>(w
, 640 - 15),
722 std::max
<tools::Long
>(h
, 480 - 50));
724 // Don't bother with ancient magic numbers of unclear relevance on non-desktop apps anyway. It
725 // seems that at least currently in the iOS app, this function is called just once per dialog,
726 // with a rScreenSize parameter of 1x1 (!). This would lead to always returning 625x430 which is
727 // a bit random and needlessly small on an iPad at least. We want something that closely will
728 // just fit on the display in either orientation.
730 // We ignore the rScreenSize as it will be the dummy 1x1 from iosinst.cxx (see "Totally wrong of course").
733 const int n
= std::min
<CGFloat
>([[UIScreen mainScreen
] bounds
].size
.width
, [[UIScreen mainScreen
] bounds
].size
.height
);
734 return Size(n
-10, n
-10);
738 void Dialog::SetPopupMenuHdl(const Link
<const CommandEvent
&, bool>& rLink
)
740 mpDialogImpl
->m_aPopupMenuHdl
= rLink
;
743 void Dialog::SetInstallLOKNotifierHdl(const Link
<void*, vcl::ILibreOfficeKitNotifier
*>& rLink
)
745 mpDialogImpl
->m_aInstallLOKNotifierHdl
= rLink
;
748 void Dialog::SetLOKTunnelingState(bool bEnabled
)
750 mpDialogImpl
->m_bLOKTunneling
= bEnabled
;
753 void Dialog::StateChanged( StateChangedType nType
)
755 bool bTunnelingEnabled
= mpDialogImpl
->m_bLOKTunneling
;
757 if (nType
== StateChangedType::InitShow
)
761 const bool bKitActive
= comphelper::LibreOfficeKit::isActive();
762 if (bKitActive
&& bTunnelingEnabled
)
764 std::vector
<vcl::LOKPayloadItem
> aItems
;
765 aItems
.emplace_back("type", "dialog");
766 aItems
.emplace_back("size", GetSizePixel().toString());
767 aItems
.emplace_back("unique_id", this->get_id().toUtf8());
768 if (!GetText().isEmpty())
769 aItems
.emplace_back("title", GetText().toUtf8());
771 if (const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier())
773 pNotifier
->notifyWindow(GetLOKWindowId(), u
"created"_ustr
, aItems
);
774 pNotifier
->notifyWindow(GetLOKWindowId(), u
"created"_ustr
, aItems
);
778 vcl::ILibreOfficeKitNotifier
* pViewShell
= mpDialogImpl
->m_aInstallLOKNotifierHdl
.Call(nullptr);
781 SetLOKNotifier(pViewShell
);
782 pViewShell
->notifyWindow(GetLOKWindowId(), u
"created"_ustr
, aItems
);
787 if ( !HasChildPathFocus() || HasFocus() )
788 GrabFocusToFirstControl();
789 if ( !(GetStyle() & WB_CLOSEABLE
) )
791 if ( ImplGetCancelButton( this ) || ImplGetOKButton( this ) )
793 if ( ImplGetBorderWindow() )
794 static_cast<ImplBorderWindow
*>(ImplGetBorderWindow())->SetCloseButton();
798 ImplMouseAutoPos( this );
800 else if (nType
== StateChangedType::Text
)
802 const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier();
803 if (pNotifier
&& bTunnelingEnabled
)
805 std::vector
<vcl::LOKPayloadItem
> aPayload
;
806 aPayload
.emplace_back("title", GetText().toUtf8());
807 pNotifier
->notifyWindow(GetLOKWindowId(), u
"title_changed"_ustr
, aPayload
);
811 SystemWindow::StateChanged( nType
);
813 if (nType
== StateChangedType::ControlBackground
)
819 if (!mbModalMode
&& nType
== StateChangedType::Visible
)
821 const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier();
822 if (pNotifier
&& bTunnelingEnabled
)
824 std::vector
<vcl::LOKPayloadItem
> aPayload
;
825 aPayload
.emplace_back("title", GetText().toUtf8());
826 pNotifier
->notifyWindow(GetLOKWindowId(), IsVisible()? u
"show"_ustr
: u
"hide"_ustr
, aPayload
);
831 void Dialog::DataChanged( const DataChangedEvent
& rDCEvt
)
833 SystemWindow::DataChanged( rDCEvt
);
835 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) &&
836 (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
) )
845 VclPtr
<vcl::Window
> xWindow
= this;
846 CallEventListeners( VclEventId::WindowClose
);
847 if ( xWindow
->isDisposed() )
850 if ( mpWindowImpl
->mxWindowPeer
.is() && IsCreatedWithToolkit() && !IsInExecute() )
853 // If there's a cancel button with a custom handler, then always give it a chance to
854 // handle Dialog::Close
855 PushButton
* pCustomCancelButton
;
856 PushButton
* pCancelButton
= dynamic_cast<PushButton
*>(get_widget_for_response(RET_CANCEL
));
857 if (!mbInClose
&& pCancelButton
&& pCancelButton
->GetClickHdl().IsSet())
858 pCustomCancelButton
= pCancelButton
;
860 pCustomCancelButton
= nullptr;
864 if (pCustomCancelButton
)
866 pCustomCancelButton
->Click();
867 if (xWindow
->isDisposed())
873 if ( !(GetStyle() & WB_CLOSEABLE
) )
876 PushButton
* pButton
= ImplGetCancelButton( this );
881 pButton
= ImplGetOKButton( this );
887 if ( xWindow
->isDisposed() )
892 if (IsInExecute() || mpDialogImpl
->maEndCtx
.isSet())
901 return SystemWindow::Close();
905 bool Dialog::ImplStartExecute()
907 setDeferredProperties();
909 if (IsInExecute() || mpDialogImpl
->maEndCtx
.isSet())
912 SAL_WARN( "vcl", "Dialog::StartExecuteModal() is called in Dialog::StartExecuteModal(): "
913 << ImplGetDialogText(this) );
918 ImplSVData
* pSVData
= ImplGetSVData();
920 const bool bKitActive
= comphelper::LibreOfficeKit::isActive();
922 const bool bModal
= GetType() != WindowType::MODELESSDIALOG
;
926 if (bKitActive
&& !GetLOKNotifier())
928 if (auto pNotifier
= mpDialogImpl
->m_aInstallLOKNotifierHdl
.Call(nullptr))
929 SetLOKNotifier(pNotifier
);
932 // gh#5908 handle pasting disallowed clipboard contents on iOS
933 // When another app owns the current clipboard contents, pasting
934 // will display a "allow or disallow" dialog. If the disallow
935 // option is selected, the data from the UIPasteboard will be
936 // garbage and we will find ourselves here. Since calling
937 // SetLOKNotifier() with a nullptr aborts in an assert(), fix
938 // the crash by failing gracefully.
940 // Also pNotifier may be nullptr when a dialog (e.g., "update
941 // links?") is to be shown when loading a document.
946 switch ( Application::GetDialogCancelMode() )
948 case DialogCancelMode::Off
:
950 case DialogCancelMode::Silent
:
951 if (bModal
&& GetLOKNotifier())
953 // check if there's already some dialog being ::Execute()d
954 const bool bDialogExecuting
= std::any_of(pSVData
->mpWinData
->mpExecuteDialogs
.begin(),
955 pSVData
->mpWinData
->mpExecuteDialogs
.end(),
956 [](const Dialog
* pDialog
) {
957 return pDialog
->IsInSyncExecute();
959 if (!(bDialogExecuting
&& IsInSyncExecute()))
962 SAL_WARN("lok.dialog", "Dialog \"" << ImplGetDialogText(this) << "\" is being synchronously executed over an existing synchronously executing dialog.");
965 if (o3tl::IsRunningUnitTest())
966 { // helps starbasic unit tests show their errors
967 std::cerr
<< "Dialog \"" << ImplGetDialogText(this)
968 << "\" cancelled in silent mode\n";
973 "Dialog \"" << ImplGetDialogText(this)
974 << "\" cancelled in silent mode");
977 case DialogCancelMode::LOKSilent
:
980 default: // default cannot happen
981 case DialogCancelMode::Fatal
:
986 vcl::Window
* pParent
= GetParent();
989 pParent
= pParent
->ImplGetFirstOverlapWindow();
992 SAL_WARN_IF( !pParent
->IsReallyVisible(), "vcl",
993 "Dialog::StartExecuteModal() - Parent not visible" );
994 SAL_WARN_IF( !pParent
->IsInputEnabled(), "vcl",
995 "Dialog::StartExecuteModal() - Parent input disabled, use another parent to ensure modality!" );
996 SAL_WARN_IF( pParent
->IsInModalMode(), "vcl",
997 "Dialog::StartExecuteModal() - Parent already modally disabled, use another parent to ensure modality!" );
1002 // link all dialogs which are being executed
1003 pSVData
->mpWinData
->mpExecuteDialogs
.push_back(this);
1005 // stop capturing, in order to have control over the dialog
1006 if (pSVData
->mpWinData
->mpTrackWin
)
1007 pSVData
->mpWinData
->mpTrackWin
->EndTracking(TrackingEventFlags::Cancel
);
1008 if (pSVData
->mpWinData
->mpCaptureWin
)
1009 pSVData
->mpWinData
->mpCaptureWin
->ReleaseMouse();
1014 // no real modality in LibreOfficeKit
1015 if (!bKitActive
&& bModal
)
1016 SetModalInputMode(true);
1018 // FIXME: no layouting, workaround some clipping issues
1019 ImplAdjustNWFSizes();
1021 const css::uno::Reference
< css::uno::XComponentContext
>& xContext(
1022 comphelper::getProcessComponentContext());
1023 bool bForceFocusAndToFront(officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get());
1024 ShowFlags showFlags
= bForceFocusAndToFront
? ShowFlags::ForegroundTask
: ShowFlags::NONE
;
1025 Show(true, showFlags
);
1028 pSVData
->maAppData
.mnModalMode
++;
1030 css::uno::Reference
<css::frame::XGlobalEventBroadcaster
> xEventBroadcaster(
1031 css::frame::theGlobalEventBroadcaster::get(xContext
), css::uno::UNO_SET_THROW
);
1032 css::document::DocumentEvent aObject
;
1033 aObject
.EventName
= "DialogExecute";
1034 aObject
.Supplement
<<= GetText(); // title
1035 xEventBroadcaster
->documentEventOccured(aObject
);
1037 UITestLogger::getInstance().log(Concat2View("Open Modal " + get_id()));
1039 UITestLogger::getInstance().log(Concat2View("Open Modeless " + get_id()));
1041 bool bTunnelingEnabled
= mpDialogImpl
->m_bLOKTunneling
;
1042 if (comphelper::LibreOfficeKit::isActive() && bTunnelingEnabled
)
1044 if (const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier())
1046 // Dialog boxes don't get the Resize call and they
1047 // can have invalid size at 'created' message above.
1048 // If there is no difference, the client should detect it and ignore us,
1049 // otherwise, this should make sure that the window has the correct size.
1050 std::vector
<vcl::LOKPayloadItem
> aItems
;
1051 aItems
.emplace_back("size", GetSizePixel().toString());
1052 aItems
.emplace_back("unique_id", this->get_id().toUtf8());
1053 pNotifier
->notifyWindow(GetLOKWindowId(), u
"size_changed"_ustr
, aItems
);
1060 void Dialog::ImplEndExecuteModal()
1062 ImplSVData
* pSVData
= ImplGetSVData();
1063 pSVData
->maAppData
.mnModalMode
--;
1066 short Dialog::Execute()
1068 VclPtr
<vcl::Window
> xWindow
= this;
1070 mbInSyncExecute
= true;
1071 comphelper::ScopeGuard
aGuard([&]() {
1072 mbInSyncExecute
= false;
1075 if ( !ImplStartExecute() )
1078 // Yield util EndDialog is called or dialog gets destroyed
1079 // (the latter should not happen, but better safe than sorry
1080 while ( !xWindow
->isDisposed() && mbInExecute
&& !Application::IsQuit() )
1081 Application::Yield();
1083 ImplEndExecuteModal();
1085 assert (!mpDialogParent
|| !mpDialogParent
->isDisposed());
1087 if ( !xWindow
->isDisposed() )
1091 OSL_FAIL( "Dialog::Execute() - Dialog destroyed in Execute()" );
1094 assert(mpDialogImpl
);
1098 tools::Long nRet
= mpDialogImpl
->mnResult
;
1099 mpDialogImpl
->mnResult
= -1;
1101 return static_cast<short>(nRet
);
1105 SAL_WARN( "vcl", "Dialog::Execute() : missing mpDialogImpl " );
1111 bool Dialog::StartExecuteAsync( VclAbstractDialog::AsyncContext
&rCtx
)
1113 const bool bModal
= GetType() != WindowType::MODELESSDIALOG
;
1114 if (!ImplStartExecute())
1116 rCtx
.mxOwner
.disposeAndClear();
1117 rCtx
.mxOwnerDialogController
.reset();
1118 rCtx
.mxOwnerSelf
.reset();
1122 mpDialogImpl
->maEndCtx
= rCtx
;
1123 mpDialogImpl
->mbStartedModal
= bModal
;
1128 void Dialog::RemoveFromDlgList()
1130 ImplSVData
* pSVData
= ImplGetSVData();
1131 auto& rExecuteDialogs
= pSVData
->mpWinData
->mpExecuteDialogs
;
1133 // remove dialog from the list of dialogs which are being executed
1134 std::erase_if(rExecuteDialogs
, [this](VclPtr
<Dialog
>& dialog
){ return dialog
.get() == this; });
1137 void Dialog::EndDialog( tools::Long nResult
)
1139 if (!mbInExecute
|| isDisposed())
1142 const bool bModal
= GetType() != WindowType::MODELESSDIALOG
;
1146 if (comphelper::LibreOfficeKit::isActive())
1148 if(const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier())
1150 if (mpDialogImpl
->m_bLOKTunneling
)
1151 pNotifier
->notifyWindow(GetLOKWindowId(), u
"close"_ustr
);
1152 ReleaseLOKNotifier();
1158 SetModalInputMode(false);
1160 RemoveFromDlgList();
1162 // set focus to previous modal dialog if it is modal for
1163 // the same frame parent (or NULL)
1164 ImplSVData
* pSVData
= ImplGetSVData();
1165 if (!pSVData
->mpWinData
->mpExecuteDialogs
.empty())
1167 VclPtr
<Dialog
> pPrevious
= pSVData
->mpWinData
->mpExecuteDialogs
.back();
1169 vcl::Window
* pFrameParent
= ImplGetFrameWindow()->ImplGetParent();
1170 vcl::Window
* pPrevFrameParent
= pPrevious
->ImplGetFrameWindow()? pPrevious
->ImplGetFrameWindow()->ImplGetParent(): nullptr;
1171 if( ( !pFrameParent
&& !pPrevFrameParent
) ||
1172 ( pFrameParent
&& pPrevFrameParent
&& pFrameParent
->ImplGetFrame() == pPrevFrameParent
->ImplGetFrame() )
1175 pPrevious
->GrabFocus();
1180 mpDialogImpl
->mnResult
= nResult
;
1182 if ( mpDialogImpl
->mbStartedModal
)
1183 ImplEndExecuteModal();
1185 // coverity[check_after_deref] - ImplEndExecuteModal might trigger destruction of mpDialogImpl
1186 if ( mpDialogImpl
&& mpDialogImpl
->maEndCtx
.isSet() )
1188 // We have a special case with async-dialogs that re-execute themselves.
1189 // In order to prevent overwriting state we need here, we need to extract
1190 // all the state we need before calling maEndDialogFn, because
1191 // maEndDialogFn might itself call StartExecuteAsync and store new state.
1192 std::shared_ptr
<weld::DialogController
> xOwnerDialogController
= std::move(mpDialogImpl
->maEndCtx
.mxOwnerDialogController
);
1193 std::shared_ptr
<weld::Dialog
> xOwnerSelf
= std::move(mpDialogImpl
->maEndCtx
.mxOwnerSelf
);
1194 auto xOwner
= std::move(mpDialogImpl
->maEndCtx
.mxOwner
);
1195 if ( mpDialogImpl
->mbStartedModal
)
1197 mpDialogImpl
->mbStartedModal
= false;
1198 mpDialogImpl
->mnResult
= -1;
1200 mbInExecute
= false;
1201 auto fn
= std::move(mpDialogImpl
->maEndCtx
.maEndDialogFn
);
1203 // std::move leaves maEndDialogFn in a valid state with unspecified
1204 // value. For the SwSyncBtnDlg case gcc and msvc left maEndDialogFn
1205 // unset, but clang left maEndDialogFn at its original value, keeping
1206 // an extra reference to the DialogController in its lambda giving
1207 // an inconsistent lifecycle for the dialog. Force it to be unset.
1208 mpDialogImpl
->maEndCtx
.maEndDialogFn
= nullptr;
1211 // Destroy ourselves, if we have a context with VclPtr owner, and
1212 // we have not been re-executed.
1213 if (!mpDialogImpl
|| !mpDialogImpl
->maEndCtx
.isSet())
1214 xOwner
.disposeAndClear();
1215 xOwnerDialogController
.reset();
1220 if ( mpDialogImpl
&& mpDialogImpl
->mbStartedModal
)
1222 mpDialogImpl
->mbStartedModal
= false;
1223 mpDialogImpl
->mnResult
= -1;
1225 mbInExecute
= false;
1228 // Destroy ourselves (if we have a context with VclPtr owner)
1229 std::shared_ptr
<weld::DialogController
> xOwnerDialogController
= std::move(mpDialogImpl
->maEndCtx
.mxOwnerDialogController
);
1230 std::shared_ptr
<weld::Dialog
> xOwnerSelf
= std::move(mpDialogImpl
->maEndCtx
.mxOwnerSelf
);
1231 mpDialogImpl
->maEndCtx
.mxOwner
.disposeAndClear();
1238 void EndAllDialogs( vcl::Window
const * pParent
)
1240 ImplSVData
* pSVData
= ImplGetSVData();
1241 auto& rExecuteDialogs
= pSVData
->mpWinData
->mpExecuteDialogs
;
1243 for (auto it
= rExecuteDialogs
.rbegin(); it
!= rExecuteDialogs
.rend(); ++it
)
1245 if (!pParent
|| pParent
->IsWindowOrChild(*it
, true))
1248 (*it
)->PostUserEvent(Link
<void*, void>());
1253 void EnableDialogInput(vcl::Window
* pWindow
)
1255 if (Dialog
* pDialog
= dynamic_cast<Dialog
*>(pWindow
))
1257 pDialog
->EnableInput();
1261 void CloseTopLevel(vcl::Window
* pWindow
)
1263 if (Dialog
* pDialog
= dynamic_cast<Dialog
*>(pWindow
))
1265 else if (FloatingWindow
* pFloatWin
= dynamic_cast<FloatingWindow
*>(pWindow
))
1266 pFloatWin
->EndPopupMode(FloatWinPopupEndFlags::Cancel
| FloatWinPopupEndFlags::CloseAll
);
1270 void Dialog::SetModalInputMode( bool bModal
)
1272 if ( bModal
== mbModalMode
)
1275 ImplGetFrame()->SetModal(bModal
);
1279 SalFrame
* pFrame
= GetParent()->ImplGetFrame();
1280 pFrame
->NotifyModalHierarchy(bModal
);
1283 ImplSetModalInputMode(bModal
);
1286 void Dialog::ImplSetModalInputMode( bool bModal
)
1288 if ( bModal
== mbModalMode
)
1291 // previously Execute()'d dialog - the one below the top-most one
1292 VclPtr
<Dialog
> pPrevious
;
1293 ImplSVData
* pSVData
= ImplGetSVData();
1294 auto& rExecuteDialogs
= pSVData
->mpWinData
->mpExecuteDialogs
;
1295 if (rExecuteDialogs
.size() > 1)
1296 pPrevious
= rExecuteDialogs
[rExecuteDialogs
.size() - 2];
1298 mbModalMode
= bModal
;
1301 // Disable the prev Modal Dialog, because our dialog must close at first,
1302 // before the other dialog can be closed (because the other dialog
1303 // is on stack since our dialog returns)
1304 if (pPrevious
&& !pPrevious
->IsWindowOrChild(this, true))
1305 pPrevious
->EnableInput(false, this);
1307 // determine next overlap dialog parent
1308 vcl::Window
* pParent
= GetParent();
1311 // #103716# dialogs should always be modal to the whole frame window
1312 // #115933# disable the whole frame hierarchy, useful if our parent
1313 // is a modeless dialog
1314 mpDialogParent
= pParent
->mpWindowImpl
->mpFrameWindow
;
1315 mpDialogParent
->IncModalCount();
1320 if ( mpDialogParent
)
1322 // #115933# re-enable the whole frame hierarchy again (see above)
1323 // note that code in getfocus assures that we do not accidentally enable
1324 // windows that were disabled before
1325 mpDialogParent
->DecModalCount();
1328 // Enable the prev Modal Dialog
1329 if (pPrevious
&& !pPrevious
->IsWindowOrChild(this, true))
1331 pPrevious
->EnableInput(true, this);
1333 // ensure continued modality of prev dialog
1334 // do not change modality counter
1336 // #i119994# need find the last modal dialog before reactive it
1337 if (pPrevious
->IsModalInputMode() || !pPrevious
->IsWindowOrChild(this, true))
1339 pPrevious
->ImplSetModalInputMode(false);
1340 pPrevious
->ImplSetModalInputMode(true);
1346 vcl::Window
* Dialog::GetFirstControlForFocus()
1348 vcl::Window
* pFocusControl
= nullptr;
1349 vcl::Window
* pFirstOverlapWindow
= ImplGetFirstOverlapWindow();
1351 // find focus control, even if the dialog has focus
1352 if (!HasFocus() && pFirstOverlapWindow
&& pFirstOverlapWindow
->mpWindowImpl
)
1354 // prefer a child window which had focus before
1355 pFocusControl
= ImplGetFirstOverlapWindow()->mpWindowImpl
->mpLastFocusWindow
;
1356 // find the control out of the dialog control
1357 if ( pFocusControl
)
1358 pFocusControl
= ImplFindDlgCtrlWindow( pFocusControl
);
1360 // no control had the focus before or the control is not
1361 // part of the tab-control, now give focus to the
1362 // first control in the tab-control
1363 if ( !pFocusControl
||
1364 !(pFocusControl
->GetStyle() & WB_TABSTOP
) ||
1365 !isVisibleInLayout(pFocusControl
) ||
1366 !isEnabledInLayout(pFocusControl
) || !pFocusControl
->IsInputEnabled() )
1368 pFocusControl
= ImplGetDlgWindow( 0, GetDlgWindowType::First
);
1371 return pFocusControl
;
1374 void Dialog::GrabFocusToFirstControl()
1376 vcl::Window
* pFocusControl
= GetFirstControlForFocus();
1377 if ( pFocusControl
)
1378 pFocusControl
->ImplControlFocus( GetFocusFlags::Init
);
1381 void Dialog::GetDrawWindowBorder( sal_Int32
& rLeftBorder
, sal_Int32
& rTopBorder
, sal_Int32
& rRightBorder
, sal_Int32
& rBottomBorder
) const
1383 ScopedVclPtrInstance
<ImplBorderWindow
> aImplWin( static_cast<vcl::Window
*>(const_cast<Dialog
*>(this)), WB_BORDER
|WB_STDWORK
, BorderWindowStyle::Overlap
);
1384 aImplWin
->GetBorder( rLeftBorder
, rTopBorder
, rRightBorder
, rBottomBorder
);
1387 void Dialog::Draw( OutputDevice
* pDev
, const Point
& rPos
, SystemTextColorFlags
)
1389 Point aPos
= pDev
->LogicToPixel( rPos
);
1390 Size aSize
= GetSizePixel();
1392 Wallpaper aWallpaper
= GetBackground();
1393 if ( !aWallpaper
.IsBitmap() )
1398 pDev
->SetLineColor();
1400 if ( aWallpaper
.IsBitmap() )
1401 pDev
->DrawBitmapEx( aPos
, aSize
, aWallpaper
.GetBitmap() );
1404 pDev
->SetFillColor( aWallpaper
.GetColor() );
1405 pDev
->DrawRect( tools::Rectangle( aPos
, aSize
) );
1408 if (!( GetStyle() & WB_NOBORDER
))
1410 ScopedVclPtrInstance
< ImplBorderWindow
> aImplWin( this, WB_BORDER
|WB_STDWORK
, BorderWindowStyle::Overlap
);
1411 aImplWin
->SetText( GetText() );
1412 aImplWin
->setPosSizePixel( aPos
.X(), aPos
.Y(), aSize
.Width(), aSize
.Height() );
1413 aImplWin
->SetDisplayActive( true );
1414 aImplWin
->InitView();
1416 aImplWin
->Draw( pDev
, aPos
);
1422 void Dialog::queue_resize(StateChangedType eReason
)
1426 SystemWindow::queue_resize(eReason
);
1429 void Dialog::Resize()
1431 SystemWindow::Resize();
1433 if (comphelper::LibreOfficeKit::isDialogPainting())
1436 bool bTunnelingEnabled
= mpDialogImpl
->m_bLOKTunneling
;
1437 const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier();
1438 if (pNotifier
&& bTunnelingEnabled
)
1440 std::vector
<vcl::LOKPayloadItem
> aItems
;
1441 aItems
.emplace_back("size", GetSizePixel().toString());
1442 aItems
.emplace_back("unique_id", this->get_id().toUtf8());
1443 pNotifier
->notifyWindow(GetLOKWindowId(), u
"size_changed"_ustr
, aItems
);
1447 bool Dialog::set_property(const OUString
&rKey
, const OUString
&rValue
)
1449 if (rKey
== "border-width")
1450 set_border_width(rValue
.toInt32());
1452 return SystemWindow::set_property(rKey
, rValue
);
1456 FactoryFunction
Dialog::GetUITestFactory() const
1458 return DialogUIObject::create
;
1461 IMPL_LINK(Dialog
, ResponseHdl
, Button
*, pButton
, void)
1463 auto aFind
= mpDialogImpl
->maResponses
.find(pButton
);
1464 if (aFind
== mpDialogImpl
->maResponses
.end())
1466 short nResponse
= aFind
->second
;
1467 if (nResponse
== RET_HELP
)
1469 vcl::Window
* pFocusWin
= Application::GetFocusWindow();
1470 if (!pFocusWin
|| comphelper::LibreOfficeKit::isActive())
1471 pFocusWin
= pButton
;
1472 HelpEvent
aEvt(pFocusWin
->GetPointerPosPixel(), HelpEventMode::CONTEXT
);
1473 pFocusWin
->RequestHelp(aEvt
);
1476 EndDialog(nResponse
);
1479 void Dialog::add_button(PushButton
* pButton
, int response
, bool bTransferOwnership
)
1481 if (bTransferOwnership
)
1482 mpDialogImpl
->maOwnedButtons
.push_back(pButton
);
1483 mpDialogImpl
->maResponses
[pButton
] = response
;
1484 switch (pButton
->GetType())
1486 case WindowType::PUSHBUTTON
:
1488 if (!pButton
->GetClickHdl().IsSet())
1489 pButton
->SetClickHdl(LINK(this, Dialog
, ResponseHdl
));
1492 //insist that the response ids match the default actions for those
1493 //widgets, and leave their default handlers in place
1494 case WindowType::OKBUTTON
:
1495 assert(mpDialogImpl
->get_response(pButton
) == RET_OK
);
1497 case WindowType::CANCELBUTTON
:
1498 assert(mpDialogImpl
->get_response(pButton
) == RET_CANCEL
|| mpDialogImpl
->get_response(pButton
) == RET_CLOSE
);
1500 case WindowType::HELPBUTTON
:
1501 assert(mpDialogImpl
->get_response(pButton
) == RET_HELP
);
1504 SAL_WARN("vcl.layout", "The type of widget " <<
1505 pButton
->GetHelpId() << " is currently not handled");
1510 vcl::Window
* Dialog::get_widget_for_response(int response
)
1512 //copy explicit responses
1513 std::map
<VclPtr
<vcl::Window
>, short> aResponses(mpDialogImpl
->maResponses
);
1517 //add implicit responses
1518 for (vcl::Window
* pChild
= mpActionArea
->GetWindow(GetWindowType::FirstChild
); pChild
;
1519 pChild
= pChild
->GetWindow(GetWindowType::Next
))
1521 if (aResponses
.find(pChild
) != aResponses
.end())
1523 switch (pChild
->GetType())
1525 case WindowType::OKBUTTON
:
1526 aResponses
[pChild
] = RET_OK
;
1528 case WindowType::CANCELBUTTON
:
1529 aResponses
[pChild
] = RET_CANCEL
;
1531 case WindowType::HELPBUTTON
:
1532 aResponses
[pChild
] = RET_HELP
;
1540 for (const auto& a
: aResponses
)
1542 if (a
.second
== response
)
1549 int Dialog::get_default_response() const
1551 //copy explicit responses
1552 std::map
<VclPtr
<vcl::Window
>, short> aResponses(mpDialogImpl
->maResponses
);
1556 //add implicit responses
1557 for (vcl::Window
* pChild
= mpActionArea
->GetWindow(GetWindowType::FirstChild
); pChild
;
1558 pChild
= pChild
->GetWindow(GetWindowType::Next
))
1560 if (aResponses
.find(pChild
) != aResponses
.end())
1562 switch (pChild
->GetType())
1564 case WindowType::OKBUTTON
:
1565 aResponses
[pChild
] = RET_OK
;
1567 case WindowType::CANCELBUTTON
:
1568 aResponses
[pChild
] = RET_CANCEL
;
1570 case WindowType::HELPBUTTON
:
1571 aResponses
[pChild
] = RET_HELP
;
1579 for (const auto& a
: aResponses
)
1581 if (a
.first
->GetStyle() & WB_DEFBUTTON
)
1589 void Dialog::set_default_response(int response
)
1591 //copy explicit responses
1592 std::map
<VclPtr
<vcl::Window
>, short> aResponses(mpDialogImpl
->maResponses
);
1596 //add implicit responses
1597 for (vcl::Window
* pChild
= mpActionArea
->GetWindow(GetWindowType::FirstChild
); pChild
;
1598 pChild
= pChild
->GetWindow(GetWindowType::Next
))
1600 if (aResponses
.find(pChild
) != aResponses
.end())
1602 switch (pChild
->GetType())
1604 case WindowType::OKBUTTON
:
1605 aResponses
[pChild
] = RET_OK
;
1607 case WindowType::CANCELBUTTON
:
1608 aResponses
[pChild
] = RET_CANCEL
;
1610 case WindowType::HELPBUTTON
:
1611 aResponses
[pChild
] = RET_HELP
;
1619 for (auto& a
: aResponses
)
1621 if (a
.second
== response
)
1623 a
.first
->SetStyle(a
.first
->GetStyle() | WB_DEFBUTTON
);
1624 a
.first
->GrabFocus();
1628 a
.first
->SetStyle(a
.first
->GetStyle() & ~WB_DEFBUTTON
);
1633 VclBuilderContainer::VclBuilderContainer()
1637 void VclBuilderContainer::setDeferredProperties()
1641 m_pUIBuilder
->setDeferredProperties();
1644 VclBuilderContainer::~VclBuilderContainer()
1648 void Dialog::Activate()
1650 if (GetType() == WindowType::MODELESSDIALOG
)
1652 const css::uno::Reference
< css::uno::XComponentContext
>& xContext(
1653 comphelper::getProcessComponentContext() );
1654 css::uno::Reference
<css::frame::XGlobalEventBroadcaster
> xEventBroadcaster(css::frame::theGlobalEventBroadcaster::get(xContext
), css::uno::UNO_SET_THROW
);
1655 css::document::DocumentEvent aObject
;
1656 aObject
.EventName
= "ModelessDialogVisible";
1657 aObject
.Supplement
<<= GetText(); // title
1658 xEventBroadcaster
->documentEventOccured(aObject
);
1660 SystemWindow::Activate();
1663 void Dialog::Command(const CommandEvent
& rCEvt
)
1665 if (mpDialogImpl
&& mpDialogImpl
->m_aPopupMenuHdl
.Call(rCEvt
))
1667 SystemWindow::Command(rCEvt
);
1670 struct TopLevelWindowLockerImpl
1672 std::stack
<std::vector
<VclPtr
<vcl::Window
>>> m_aBusyStack
;
1675 TopLevelWindowLocker::TopLevelWindowLocker()
1676 : m_xImpl(std::make_unique
<TopLevelWindowLockerImpl
>())
1680 void TopLevelWindowLocker::incBusy(const weld::Widget
* pIgnore
)
1682 // lock any toplevel windows from being closed until busy is over
1683 std::vector
<VclPtr
<vcl::Window
>> aTopLevels
;
1684 vcl::Window
*pTopWin
= Application::GetFirstTopLevelWindow();
1687 vcl::Window
* pCandidate
= pTopWin
;
1688 if (pCandidate
->GetType() == WindowType::BORDERWINDOW
)
1689 pCandidate
= pCandidate
->GetWindow(GetWindowType::FirstChild
);
1690 // tdf#125266 ignore HelpTextWindows
1692 pCandidate
->GetType() != WindowType::HELPTEXTWINDOW
&&
1693 pCandidate
->GetType() != WindowType::FLOATINGWINDOW
&&
1694 pCandidate
->GetFrameWeld() != pIgnore
)
1696 aTopLevels
.push_back(pCandidate
);
1698 pTopWin
= Application::GetNextTopLevelWindow(pTopWin
);
1700 for (auto& a
: aTopLevels
)
1703 a
->ImplGetFrame()->NotifyModalHierarchy(true);
1705 m_xImpl
->m_aBusyStack
.push(aTopLevels
);
1708 void TopLevelWindowLocker::decBusy()
1710 // unlock locked toplevel windows from being closed now busy is over
1711 for (auto& a
: m_xImpl
->m_aBusyStack
.top())
1713 if (a
->isDisposed())
1716 a
->ImplGetFrame()->NotifyModalHierarchy(false);
1718 m_xImpl
->m_aBusyStack
.pop();
1721 bool TopLevelWindowLocker::isBusy() const
1723 return !m_xImpl
->m_aBusyStack
.empty();
1726 TopLevelWindowLocker::~TopLevelWindowLocker()
1730 void Dialog::DumpAsPropertyTree(tools::JsonWriter
& rJsonWriter
)
1732 SystemWindow::DumpAsPropertyTree(rJsonWriter
);
1733 rJsonWriter
.put("title", GetText());
1734 if (vcl::Window
* pActionArea
= get_action_area())
1736 if (!pActionArea
->IsVisible())
1737 rJsonWriter
.put("collapsed", true);
1740 OUString sDialogId
= GetHelpId();
1741 sal_Int32 nStartPos
= sDialogId
.lastIndexOf('/');
1742 nStartPos
= nStartPos
>= 0 ? nStartPos
+ 1 : 0;
1743 rJsonWriter
.put("dialogid", sDialogId
.copy(nStartPos
));
1746 auto aResponses
= rJsonWriter
.startArray("responses");
1747 for (const auto& rResponse
: mpDialogImpl
->maResponses
)
1749 auto aResponse
= rJsonWriter
.startStruct();
1750 rJsonWriter
.put("id", rResponse
.first
->get_id());
1751 rJsonWriter
.put("response", rResponse
.second
);
1755 vcl::Window
* pFocusControl
= GetFirstControlForFocus();
1757 rJsonWriter
.put("init_focus_id", pFocusControl
->get_id());
1760 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */