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 <officecfg/Office/Common.hxx>
33 #include <osl/diagnose.h>
39 #include <salinst.hxx>
41 #include <rtl/bootstrap.hxx>
42 #include <rtl/strbuf.hxx>
43 #include <sal/log.hxx>
45 #include <vcl/abstdlg.hxx>
46 #include <vcl/builder.hxx>
47 #include <vcl/toolkit/floatwin.hxx>
48 #include <vcl/layout.hxx>
49 #include <vcl/svapp.hxx>
50 #include <vcl/event.hxx>
51 #include <vcl/locktoplevels.hxx>
52 #include <vcl/wrkwin.hxx>
53 #include <vcl/toolkit/button.hxx>
54 #include <vcl/mnemonic.hxx>
55 #include <vcl/toolkit/dialog.hxx>
56 #include <vcl/dialoghelper.hxx>
57 #include <vcl/settings.hxx>
58 #include <vcl/virdev.hxx>
59 #include <vcl/weld.hxx>
60 #include <vcl/uitest/uiobject.hxx>
61 #include <vcl/uitest/logger.hxx>
62 #include <vcl/IDialogRenderable.hxx>
63 #include <messagedialog.hxx>
64 #include <salframe.hxx>
65 #include <tools/json_writer.hxx>
72 static OString
ImplGetDialogText( Dialog
* pDialog
)
74 OUString
aErrorStr(pDialog
->GetText());
77 if (MessageDialog
* pMessDialog
= dynamic_cast<MessageDialog
*>(pDialog
))
79 sMessage
= pMessDialog
->get_primary_text();
82 if (!sMessage
.isEmpty())
84 aErrorStr
+= ", " + sMessage
;
86 return OUStringToOString(aErrorStr
, RTL_TEXTENCODING_UTF8
);
89 static bool ImplIsMnemonicCtrl( vcl::Window
* pWindow
)
91 if( ! pWindow
->GetSettings().GetStyleSettings().GetAutoMnemonic() )
94 if ( (pWindow
->GetType() == WindowType::RADIOBUTTON
) ||
95 (pWindow
->GetType() == WindowType::CHECKBOX
) ||
96 (pWindow
->GetType() == WindowType::TRISTATEBOX
) ||
97 (pWindow
->GetType() == WindowType::PUSHBUTTON
) )
100 if ( pWindow
->GetType() == WindowType::FIXEDTEXT
)
102 FixedText
*pText
= static_cast<FixedText
*>(pWindow
);
103 if (pText
->get_mnemonic_widget())
105 //This is the legacy pre-layout logic which we retain
106 //until we can be sure we can remove it
107 if (pWindow
->GetStyle() & WB_NOLABEL
)
109 vcl::Window
* pNextWindow
= pWindow
->GetWindow( GetWindowType::Next
);
112 pNextWindow
= pNextWindow
->GetWindow( GetWindowType::Client
);
113 return !(!(pNextWindow
->GetStyle() & WB_TABSTOP
) ||
114 (pNextWindow
->GetType() == WindowType::FIXEDTEXT
) ||
115 (pNextWindow
->GetType() == WindowType::GROUPBOX
) ||
116 (pNextWindow
->GetType() == WindowType::RADIOBUTTON
) ||
117 (pNextWindow
->GetType() == WindowType::CHECKBOX
) ||
118 (pNextWindow
->GetType() == WindowType::TRISTATEBOX
) ||
119 (pNextWindow
->GetType() == WindowType::PUSHBUTTON
));
125 // Called by native error dialog popup implementations
126 void ImplHideSplash()
128 ImplSVData
* pSVData
= ImplGetSVData();
129 if( pSVData
->mpIntroWindow
)
130 pSVData
->mpIntroWindow
->Hide();
133 vcl::Window
* nextLogicalChildOfParent(const vcl::Window
*pTopLevel
, const vcl::Window
*pChild
)
135 const vcl::Window
*pLastChild
= pChild
;
137 if (pChild
->GetType() == WindowType::SCROLLWINDOW
)
138 pChild
= static_cast<const VclScrolledWindow
*>(pChild
)->get_child();
139 else if (isContainerWindow(*pChild
))
140 pChild
= pChild
->GetWindow(GetWindowType::FirstChild
);
142 pChild
= pChild
->GetWindow(GetWindowType::Next
);
146 vcl::Window
*pParent
= pLastChild
->GetParent();
149 if (pParent
== pTopLevel
)
151 pLastChild
= pParent
;
152 pChild
= pParent
->GetWindow(GetWindowType::Next
);
155 if (isContainerWindow(*pChild
))
156 pChild
= nextLogicalChildOfParent(pTopLevel
, pChild
);
158 return const_cast<vcl::Window
*>(pChild
);
161 vcl::Window
* prevLogicalChildOfParent(const vcl::Window
*pTopLevel
, const vcl::Window
*pChild
)
163 const vcl::Window
*pLastChild
= pChild
;
165 if (pChild
->GetType() == WindowType::SCROLLWINDOW
)
166 pChild
= static_cast<const VclScrolledWindow
*>(pChild
)->get_child();
167 else if (isContainerWindow(*pChild
))
168 pChild
= pChild
->GetWindow(GetWindowType::LastChild
);
170 pChild
= pChild
->GetWindow(GetWindowType::Prev
);
174 vcl::Window
*pParent
= pLastChild
->GetParent();
177 if (pParent
== pTopLevel
)
179 pLastChild
= pParent
;
180 pChild
= pParent
->GetWindow(GetWindowType::Prev
);
183 if (isContainerWindow(*pChild
))
184 pChild
= prevLogicalChildOfParent(pTopLevel
, pChild
);
186 return const_cast<vcl::Window
*>(pChild
);
189 vcl::Window
* firstLogicalChildOfParent(const vcl::Window
*pTopLevel
)
191 const vcl::Window
*pChild
= pTopLevel
->GetWindow(GetWindowType::FirstChild
);
192 if (pChild
&& isContainerWindow(*pChild
))
193 pChild
= nextLogicalChildOfParent(pTopLevel
, pChild
);
194 return const_cast<vcl::Window
*>(pChild
);
197 vcl::Window
* lastLogicalChildOfParent(const vcl::Window
*pTopLevel
)
199 const vcl::Window
*pChild
= pTopLevel
->GetWindow(GetWindowType::LastChild
);
200 if (pChild
&& isContainerWindow(*pChild
))
201 pChild
= prevLogicalChildOfParent(pTopLevel
, pChild
);
202 return const_cast<vcl::Window
*>(pChild
);
205 void GenerateAutoMnemonicsOnHierarchy(const vcl::Window
* pWindow
)
207 MnemonicGenerator aMnemonicGenerator
;
208 vcl::Window
* pGetChild
;
211 // register the assigned mnemonics
212 pGetChild
= pWindow
->GetWindow( GetWindowType::FirstChild
);
215 pChild
= pGetChild
->ImplGetWindow();
216 aMnemonicGenerator
.RegisterMnemonic( pChild
->GetText() );
217 pGetChild
= nextLogicalChildOfParent(pWindow
, pGetChild
);
220 // take the Controls of the dialog into account for TabPages
221 if ( pWindow
->GetType() == WindowType::TABPAGE
)
223 vcl::Window
* pParent
= pWindow
->GetParent();
224 if (pParent
&& pParent
->GetType() == WindowType::TABCONTROL
)
225 pParent
= pParent
->GetParent();
227 if (pParent
&& (pParent
->GetStyle() & (WB_DIALOGCONTROL
| WB_NODIALOGCONTROL
)) == WB_DIALOGCONTROL
)
229 pGetChild
= pParent
->GetWindow( GetWindowType::FirstChild
);
232 pChild
= pGetChild
->ImplGetWindow();
233 aMnemonicGenerator
.RegisterMnemonic( pChild
->GetText() );
234 pGetChild
= nextLogicalChildOfParent(pWindow
, pGetChild
);
239 // assign mnemonics to Controls which have none
240 pGetChild
= pWindow
->GetWindow( GetWindowType::FirstChild
);
243 pChild
= pGetChild
->ImplGetWindow();
244 if ( ImplIsMnemonicCtrl( pChild
) )
246 OUString aText
= pChild
->GetText();
247 OUString aNewText
= aMnemonicGenerator
.CreateMnemonic( aText
);
248 if ( aText
!= aNewText
)
249 pChild
->SetText( aNewText
);
252 pGetChild
= nextLogicalChildOfParent(pWindow
, pGetChild
);
256 static VclButtonBox
* getActionArea(Dialog
const *pDialog
)
258 VclButtonBox
*pButtonBox
= nullptr;
259 if (pDialog
->isLayoutEnabled())
261 vcl::Window
*pBox
= pDialog
->GetWindow(GetWindowType::FirstChild
);
262 vcl::Window
*pChild
= pBox
->GetWindow(GetWindowType::LastChild
);
265 pButtonBox
= dynamic_cast<VclButtonBox
*>(pChild
);
268 pChild
= pChild
->GetWindow(GetWindowType::Prev
);
274 static vcl::Window
* getActionAreaButtonList(Dialog
const *pDialog
)
276 VclButtonBox
* pButtonBox
= getActionArea(pDialog
);
278 return pButtonBox
->GetWindow(GetWindowType::FirstChild
);
279 return pDialog
->GetWindow(GetWindowType::FirstChild
);
282 static PushButton
* ImplGetDefaultButton( Dialog
const * pDialog
)
284 vcl::Window
* pChild
= getActionAreaButtonList(pDialog
);
287 if ( pChild
->ImplIsPushButton() )
289 PushButton
* pPushButton
= static_cast<PushButton
*>(pChild
);
290 if ( pPushButton
->ImplIsDefButton() )
294 pChild
= pChild
->GetWindow( GetWindowType::Next
);
300 static PushButton
* ImplGetOKButton( Dialog
const * pDialog
)
302 vcl::Window
* pChild
= getActionAreaButtonList(pDialog
);
305 if ( pChild
->GetType() == WindowType::OKBUTTON
)
306 return static_cast<PushButton
*>(pChild
);
308 pChild
= pChild
->GetWindow( GetWindowType::Next
);
314 static PushButton
* ImplGetCancelButton( Dialog
const * pDialog
)
316 vcl::Window
* pChild
= getActionAreaButtonList(pDialog
);
320 if ( pChild
->GetType() == WindowType::CANCELBUTTON
)
321 return static_cast<PushButton
*>(pChild
);
323 pChild
= pChild
->GetWindow( GetWindowType::Next
);
329 static void ImplMouseAutoPos( Dialog
* pDialog
)
331 MouseSettingsOptions nMouseOptions
= pDialog
->GetSettings().GetMouseSettings().GetOptions();
332 if ( nMouseOptions
& MouseSettingsOptions::AutoCenterPos
)
334 Size aSize
= pDialog
->GetOutputSizePixel();
335 pDialog
->SetPointerPosPixel( Point( aSize
.Width()/2, aSize
.Height()/2 ) );
337 else if ( nMouseOptions
& MouseSettingsOptions::AutoDefBtnPos
)
339 vcl::Window
* pWindow
= ImplGetDefaultButton( pDialog
);
341 pWindow
= ImplGetOKButton( pDialog
);
343 pWindow
= ImplGetCancelButton( pDialog
);
346 Size aSize
= pWindow
->GetOutputSizePixel();
347 pWindow
->SetPointerPosPixel( Point( aSize
.Width()/2, aSize
.Height()/2 ) );
353 std::vector
<VclPtr
<PushButton
>> maOwnedButtons
;
354 std::map
<VclPtr
<vcl::Window
>, short> maResponses
;
355 tools::Long mnResult
;
357 VclAbstractDialog::AsyncContext maEndCtx
;
358 Link
<const CommandEvent
&, bool> m_aPopupMenuHdl
;
359 Link
<void*, vcl::ILibreOfficeKitNotifier
*> m_aInstallLOKNotifierHdl
;
360 bool m_bLOKTunneling
;
362 DialogImpl() : mnResult( -1 ), mbStartedModal( false ), m_bLOKTunneling( true ) {}
365 short get_response(vcl::Window
*pWindow
) const
367 auto aFind
= maResponses
.find(pWindow
);
368 if (aFind
!= maResponses
.end())
369 return aFind
->second
;
376 for (VclPtr
<PushButton
> & pOwnedButton
: maOwnedButtons
)
377 pOwnedButton
.disposeAndClear();
381 void Dialog::disposeOwnedButtons()
383 for (VclPtr
<PushButton
> & pOwnedButton
: mpDialogImpl
->maOwnedButtons
)
384 pOwnedButton
.disposeAndClear();
387 void Dialog::ImplInitDialogData()
389 mpWindowImpl
->mbDialog
= true;
391 mbInSyncExecute
= false;
394 mpContentArea
.clear();
395 mpActionArea
.clear();
396 mnMousePositioned
= 0;
397 mpDialogImpl
.reset(new DialogImpl
);
400 void Dialog::PixelInvalidate(const tools::Rectangle
* pRectangle
)
402 if (!mpDialogImpl
->m_bLOKTunneling
)
405 Window::PixelInvalidate(pRectangle
);
408 vcl::Window
* Dialog::GetDefaultParent(WinBits nStyle
)
410 vcl::Window
* pParent
= Dialog::GetDefDialogParent();
411 if (!pParent
&& !(nStyle
& WB_SYSTEMWINDOW
))
412 pParent
= ImplGetSVData()->maFrameData
.mpAppWin
;
414 // If Parent is disabled, then we search for a modal dialog
416 if (pParent
&& (!pParent
->IsInputEnabled() || pParent
->IsInModalMode()))
418 ImplSVData
* pSVData
= ImplGetSVData();
419 auto& rExecuteDialogs
= pSVData
->mpWinData
->mpExecuteDialogs
;
420 auto it
= std::find_if(rExecuteDialogs
.rbegin(), rExecuteDialogs
.rend(),
421 [&pParent
](VclPtr
<Dialog
>& rDialogPtr
) {
422 return pParent
->ImplGetFirstOverlapWindow() &&
423 pParent
->ImplGetFirstOverlapWindow()->IsWindowOrChild(rDialogPtr
, true) &&
424 rDialogPtr
->IsReallyVisible() && rDialogPtr
->IsEnabled() &&
425 rDialogPtr
->IsInputEnabled() && !rDialogPtr
->IsInModalMode(); });
426 if (it
!= rExecuteDialogs
.rend())
433 VclPtr
<vcl::Window
> Dialog::AddBorderWindow(vcl::Window
* pParent
, WinBits nStyle
)
435 VclPtrInstance
<ImplBorderWindow
> pBorderWin( pParent
, nStyle
, BorderWindowStyle::Frame
);
436 ImplInit( pBorderWin
, nStyle
& ~WB_BORDER
, nullptr );
437 pBorderWin
->mpWindowImpl
->mpClientWindow
= this;
438 pBorderWin
->GetBorder( mpWindowImpl
->mnLeftBorder
, mpWindowImpl
->mnTopBorder
, mpWindowImpl
->mnRightBorder
, mpWindowImpl
->mnBottomBorder
);
439 mpWindowImpl
->mpBorderWindow
= pBorderWin
;
440 mpWindowImpl
->mpRealParent
= pParent
;
445 void Dialog::ImplInitDialog( vcl::Window
* pParent
, WinBits nStyle
, InitFlag eFlag
)
447 SystemWindowFlags nSysWinMode
= Application::GetSystemWindowMode();
449 if ( !(nStyle
& WB_NODIALOGCONTROL
) )
450 nStyle
|= WB_DIALOGCONTROL
;
452 // Now, all Dialogs are per default system windows !!!
453 nStyle
|= WB_SYSTEMWINDOW
;
455 if (InitFlag::NoParent
== eFlag
)
459 else if (!pParent
) // parent is NULL: get the default Dialog parent
461 pParent
= Dialog::GetDefaultParent(nStyle
);
464 if ( !pParent
|| (nStyle
& WB_SYSTEMWINDOW
) ||
465 (pParent
->mpWindowImpl
->mpFrameData
->mbNeedSysWindow
&& !(nSysWinMode
& SystemWindowFlags::NOAUTOMODE
)) ||
466 (nSysWinMode
& SystemWindowFlags::DIALOG
) )
468 // create window with a small border ?
469 if ((nStyle
& WB_ALLOWMENUBAR
) || ((nStyle
& (WB_BORDER
| WB_NOBORDER
| WB_MOVEABLE
| WB_SIZEABLE
| WB_CLOSEABLE
)) == WB_BORDER
))
471 AddBorderWindow(pParent
, nStyle
);
475 mpWindowImpl
->mbFrame
= true;
476 mpWindowImpl
->mbOverlapWin
= true;
477 ImplInit( pParent
, (nStyle
& (WB_MOVEABLE
| WB_SIZEABLE
| WB_STANDALONE
)) | WB_CLOSEABLE
, nullptr );
478 // Now set all style bits
479 mpWindowImpl
->mnStyle
= nStyle
;
484 VclPtrInstance
<ImplBorderWindow
> pBorderWin( pParent
, nStyle
, BorderWindowStyle::Overlap
);
485 ImplInit( pBorderWin
, nStyle
& ~WB_BORDER
, nullptr );
486 pBorderWin
->mpWindowImpl
->mpClientWindow
= this;
487 pBorderWin
->GetBorder( mpWindowImpl
->mnLeftBorder
, mpWindowImpl
->mnTopBorder
, mpWindowImpl
->mnRightBorder
, mpWindowImpl
->mnBottomBorder
);
488 mpWindowImpl
->mpBorderWindow
= pBorderWin
;
489 mpWindowImpl
->mpRealParent
= pParent
;
492 SetActivateMode( ActivateModeFlags::GrabFocus
);
497 void Dialog::ApplySettings(vcl::RenderContext
& rRenderContext
)
499 if (IsControlBackground())
502 SetBackground(GetControlBackground());
504 else if (rRenderContext
.IsNativeControlSupported(ControlType::WindowBackground
, ControlPart::BackgroundDialog
))
507 mpWindowImpl
->mnNativeBackground
= ControlPart::BackgroundDialog
;
508 EnableChildTransparentMode();
512 // fallback to settings color
513 rRenderContext
.SetBackground(GetSettings().GetStyleSettings().GetDialogColor());
517 void Dialog::ImplInitSettings()
520 if (IsControlBackground())
521 SetBackground(GetControlBackground());
523 else if( IsNativeControlSupported(ControlType::WindowBackground
, ControlPart::BackgroundDialog
))
525 mpWindowImpl
->mnNativeBackground
= ControlPart::BackgroundDialog
;
526 EnableChildTransparentMode();
528 // fallback to settings color
530 SetBackground(GetSettings().GetStyleSettings().GetDialogColor());
533 void Dialog::ImplLOKNotifier(vcl::Window
* pParent
)
535 if (comphelper::LibreOfficeKit::isActive() && pParent
)
537 if (VclPtr
<vcl::Window
> pWin
= pParent
->GetParentWithLOKNotifier())
539 SetLOKNotifier(pWin
->GetLOKNotifier());
544 Dialog::Dialog( WindowType nType
)
545 : SystemWindow( nType
, "vcl::Dialog maLayoutIdle" )
546 , mnInitFlag(InitFlag::Default
)
548 ImplInitDialogData();
551 void VclBuilderContainer::disposeBuilder()
554 m_pUIBuilder
->disposeBuilder();
557 OUString
AllSettings::GetUIRootDir()
559 OUString
sShareLayer("$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/config/soffice.cfg/");
560 rtl::Bootstrap::expandMacros(sShareLayer
);
564 //we can't change sizeable after the fact, so need to defer until we know and then
565 //do the init. Find the real parent stashed in mpDialogParent.
566 void Dialog::doDeferredInit(WinBits nBits
)
568 VclPtr
<vcl::Window
> pParent
= mpDialogParent
;
569 mpDialogParent
= nullptr;
570 ImplInitDialog(pParent
, nBits
| WB_BORDER
, mnInitFlag
);
571 mbIsDeferredInit
= false;
574 Dialog::Dialog(vcl::Window
* pParent
, const OUString
& rID
, const OUString
& rUIXMLDescription
)
575 : SystemWindow(WindowType::DIALOG
, "vcl::Dialog maLayoutIdle")
576 , mnInitFlag(InitFlag::Default
)
578 ImplLOKNotifier(pParent
);
579 ImplInitDialogData();
580 loadUI(pParent
, rID
, rUIXMLDescription
);
583 Dialog::Dialog(vcl::Window
* pParent
, WinBits nStyle
, InitFlag eFlag
)
584 : SystemWindow(WindowType::DIALOG
, "vcl::Dialog maLayoutIdle")
587 ImplLOKNotifier(pParent
);
588 ImplInitDialogData();
589 ImplInitDialog( pParent
, nStyle
, eFlag
);
592 void Dialog::set_action_area(VclButtonBox
* pBox
)
594 mpActionArea
.set(pBox
);
597 const DialogStyle
& rDialogStyle
=
598 GetSettings().GetStyleSettings().GetDialogStyle();
599 pBox
->set_border_width(rDialogStyle
.action_area_border
);
603 void Dialog::set_content_area(VclBox
* pBox
)
605 mpContentArea
.set(pBox
);
608 void Dialog::settingOptimalLayoutSize(Window
*pBox
)
610 const DialogStyle
& rDialogStyle
=
611 GetSettings().GetStyleSettings().GetDialogStyle();
612 VclBox
* pBox2
= static_cast<VclBox
*>(pBox
);
613 pBox2
->set_border_width(rDialogStyle
.content_area_border
);
621 void Dialog::dispose()
623 bool bTunnelingEnabled
= mpDialogImpl
->m_bLOKTunneling
;
625 mpDialogImpl
.reset();
627 mpActionArea
.clear();
628 mpContentArea
.clear();
630 css::uno::Reference
< css::uno::XComponentContext
> xContext(
631 comphelper::getProcessComponentContext() );
632 css::uno::Reference
<css::frame::XGlobalEventBroadcaster
> xEventBroadcaster(css::frame::theGlobalEventBroadcaster::get(xContext
), css::uno::UNO_SET_THROW
);
633 css::document::DocumentEvent aObject
;
634 aObject
.EventName
= "DialogClosed";
635 xEventBroadcaster
->documentEventOccured(aObject
);
636 UITestLogger::getInstance().log(u
"Close Dialog");
638 if (comphelper::LibreOfficeKit::isActive())
640 if(const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier())
642 if (bTunnelingEnabled
)
643 pNotifier
->notifyWindow(GetLOKWindowId(), "close");
644 ReleaseLOKNotifier();
648 SystemWindow::dispose();
651 IMPL_LINK_NOARG(Dialog
, ImplAsyncCloseHdl
, void*, void)
656 bool Dialog::EventNotify( NotifyEvent
& rNEvt
)
658 // first call the base class due to Tab control
659 bool bRet
= SystemWindow::EventNotify( rNEvt
);
662 if ( rNEvt
.GetType() == NotifyEventType::KEYINPUT
)
664 const KeyEvent
* pKEvt
= rNEvt
.GetKeyEvent();
665 vcl::KeyCode aKeyCode
= pKEvt
->GetKeyCode();
666 sal_uInt16 nKeyCode
= aKeyCode
.GetCode();
668 if ( (nKeyCode
== KEY_ESCAPE
) &&
669 ((GetStyle() & WB_CLOSEABLE
) || ImplGetCancelButton( this ) || ImplGetOKButton( this )) )
671 // #i89505# for the benefit of slightly mentally challenged implementations
672 // like e.g. SfxModelessDialog which destroy themselves inside Close()
673 // post this Close asynchronous so we can leave our key handler before
675 PostUserEvent( LINK( this, Dialog
, ImplAsyncCloseHdl
), nullptr, true);
679 else if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
681 // make sure the dialog is still modal
682 // changing focus between application frames may
683 // have re-enabled input for our parent
684 if( mbInExecute
&& mbModalMode
)
686 ImplSetModalInputMode( false );
687 ImplSetModalInputMode( true );
689 // #93022# def-button might have changed after show
690 if( !mnMousePositioned
)
692 mnMousePositioned
= 1;
693 ImplMouseAutoPos( this );
703 //What we really want here is something that gives the available width and
704 //height of a users screen, taking away the space taken up the OS
705 //taskbar, menus, etc.
706 Size
bestmaxFrameSizeForScreenSize(const Size
&rScreenSize
)
709 tools::Long w
= rScreenSize
.Width();
717 tools::Long h
= rScreenSize
.Height();
723 return Size(std::max
<tools::Long
>(w
, 640 - 15),
724 std::max
<tools::Long
>(h
, 480 - 50));
726 // Don't bother with ancient magic numbers of unclear relevance on non-desktop apps anyway. It
727 // seems that at least currently in the iOS app, this function is called just once per dialog,
728 // with a rScreenSize parameter of 1x1 (!). This would lead to always returning 625x430 which is
729 // a bit random and needlessly small on an iPad at least. We want something that closely will
730 // just fit on the display in either orientation.
732 // We ignore the rScreenSize as it will be the dummy 1x1 from iosinst.cxx (see "Totally wrong of course").
735 const int n
= std::min
<CGFloat
>([[UIScreen mainScreen
] bounds
].size
.width
, [[UIScreen mainScreen
] bounds
].size
.height
);
736 return Size(n
-10, n
-10);
740 void Dialog::SetPopupMenuHdl(const Link
<const CommandEvent
&, bool>& rLink
)
742 mpDialogImpl
->m_aPopupMenuHdl
= rLink
;
745 void Dialog::SetInstallLOKNotifierHdl(const Link
<void*, vcl::ILibreOfficeKitNotifier
*>& rLink
)
747 mpDialogImpl
->m_aInstallLOKNotifierHdl
= rLink
;
750 void Dialog::SetLOKTunnelingState(bool bEnabled
)
752 mpDialogImpl
->m_bLOKTunneling
= bEnabled
;
755 void Dialog::StateChanged( StateChangedType nType
)
757 bool bTunnelingEnabled
= mpDialogImpl
->m_bLOKTunneling
;
759 if (nType
== StateChangedType::InitShow
)
763 const bool bKitActive
= comphelper::LibreOfficeKit::isActive();
764 if (bKitActive
&& bTunnelingEnabled
)
766 std::vector
<vcl::LOKPayloadItem
> aItems
;
767 aItems
.emplace_back("type", "dialog");
768 aItems
.emplace_back("size", GetSizePixel().toString());
769 aItems
.emplace_back("unique_id", this->get_id().toUtf8());
770 if (!GetText().isEmpty())
771 aItems
.emplace_back("title", GetText().toUtf8());
773 if (const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier())
775 pNotifier
->notifyWindow(GetLOKWindowId(), "created", aItems
);
776 pNotifier
->notifyWindow(GetLOKWindowId(), "created", aItems
);
780 vcl::ILibreOfficeKitNotifier
* pViewShell
= mpDialogImpl
->m_aInstallLOKNotifierHdl
.Call(nullptr);
783 SetLOKNotifier(pViewShell
);
784 pViewShell
->notifyWindow(GetLOKWindowId(), "created", aItems
);
789 if ( !HasChildPathFocus() || HasFocus() )
790 GrabFocusToFirstControl();
791 if ( !(GetStyle() & WB_CLOSEABLE
) )
793 if ( ImplGetCancelButton( this ) || ImplGetOKButton( this ) )
795 if ( ImplGetBorderWindow() )
796 static_cast<ImplBorderWindow
*>(ImplGetBorderWindow())->SetCloseButton();
800 ImplMouseAutoPos( this );
802 else if (nType
== StateChangedType::Text
)
804 const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier();
805 if (pNotifier
&& bTunnelingEnabled
)
807 std::vector
<vcl::LOKPayloadItem
> aPayload
;
808 aPayload
.emplace_back("title", GetText().toUtf8());
809 pNotifier
->notifyWindow(GetLOKWindowId(), "title_changed", aPayload
);
813 SystemWindow::StateChanged( nType
);
815 if (nType
== StateChangedType::ControlBackground
)
821 if (!mbModalMode
&& nType
== StateChangedType::Visible
)
823 const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier();
824 if (pNotifier
&& bTunnelingEnabled
)
826 std::vector
<vcl::LOKPayloadItem
> aPayload
;
827 aPayload
.emplace_back("title", GetText().toUtf8());
828 pNotifier
->notifyWindow(GetLOKWindowId(), IsVisible()? OUString("show"): OUString("hide"), aPayload
);
833 void Dialog::DataChanged( const DataChangedEvent
& rDCEvt
)
835 SystemWindow::DataChanged( rDCEvt
);
837 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) &&
838 (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
) )
847 VclPtr
<vcl::Window
> xWindow
= this;
848 CallEventListeners( VclEventId::WindowClose
);
849 if ( xWindow
->isDisposed() )
852 if ( mpWindowImpl
->mxWindowPeer
.is() && IsCreatedWithToolkit() && !IsInExecute() )
855 // If there's a cancel button with a custom handler, then always give it a chance to
856 // handle Dialog::Close
857 PushButton
* pCustomCancelButton
;
858 PushButton
* pCancelButton
= dynamic_cast<PushButton
*>(get_widget_for_response(RET_CANCEL
));
859 if (!mbInClose
&& pCancelButton
&& pCancelButton
->GetClickHdl().IsSet())
860 pCustomCancelButton
= pCancelButton
;
862 pCustomCancelButton
= nullptr;
866 if (pCustomCancelButton
)
868 pCustomCancelButton
->Click();
869 if (xWindow
->isDisposed())
875 if ( !(GetStyle() & WB_CLOSEABLE
) )
878 PushButton
* pButton
= ImplGetCancelButton( this );
883 pButton
= ImplGetOKButton( this );
889 if ( xWindow
->isDisposed() )
894 if (IsInExecute() || mpDialogImpl
->maEndCtx
.isSet())
903 return SystemWindow::Close();
907 bool Dialog::ImplStartExecute()
909 setDeferredProperties();
911 if (IsInExecute() || mpDialogImpl
->maEndCtx
.isSet())
914 SAL_WARN( "vcl", "Dialog::StartExecuteModal() is called in Dialog::StartExecuteModal(): "
915 << ImplGetDialogText(this) );
920 ImplSVData
* pSVData
= ImplGetSVData();
922 const bool bKitActive
= comphelper::LibreOfficeKit::isActive();
924 const bool bModal
= GetType() != WindowType::MODELESSDIALOG
;
928 if (bKitActive
&& !GetLOKNotifier())
931 // gh#5908 handle pasting disallowed clipboard contents on iOS
932 // When another app owns the current clipboard contents, pasting
933 // will display a "allow or disallow" dialog. If the disallow
934 // option is selected, the data from the UIPasteboard will be
935 // garbage and we will find ourselves here. Since calling
936 // SetLOKNotifier() with a nullptr aborts in an assert(), fix
937 // the crash by failing gracefully.
940 SetLOKNotifier(mpDialogImpl
->m_aInstallLOKNotifierHdl
.Call(nullptr));
944 switch ( Application::GetDialogCancelMode() )
946 case DialogCancelMode::Off
:
948 case DialogCancelMode::Silent
:
949 if (bModal
&& GetLOKNotifier())
951 // check if there's already some dialog being ::Execute()d
952 const bool bDialogExecuting
= std::any_of(pSVData
->mpWinData
->mpExecuteDialogs
.begin(),
953 pSVData
->mpWinData
->mpExecuteDialogs
.end(),
954 [](const Dialog
* pDialog
) {
955 return pDialog
->IsInSyncExecute();
957 if (!(bDialogExecuting
&& IsInSyncExecute()))
960 SAL_WARN("lok.dialog", "Dialog \"" << ImplGetDialogText(this) << "\" is being synchronously executed over an existing synchronously executing dialog.");
963 if (SalInstance::IsRunningUnitTest())
964 { // helps starbasic unit tests show their errors
965 std::cerr
<< "Dialog \"" << ImplGetDialogText(this)
966 << "\"cancelled in silent mode";
971 "Dialog \"" << ImplGetDialogText(this)
972 << "\"cancelled in silent mode");
975 case DialogCancelMode::LOKSilent
:
978 default: // default cannot happen
979 case DialogCancelMode::Fatal
:
984 vcl::Window
* pParent
= GetParent();
987 pParent
= pParent
->ImplGetFirstOverlapWindow();
990 SAL_WARN_IF( !pParent
->IsReallyVisible(), "vcl",
991 "Dialog::StartExecuteModal() - Parent not visible" );
992 SAL_WARN_IF( !pParent
->IsInputEnabled(), "vcl",
993 "Dialog::StartExecuteModal() - Parent input disabled, use another parent to ensure modality!" );
994 SAL_WARN_IF( pParent
->IsInModalMode(), "vcl",
995 "Dialog::StartExecuteModal() - Parent already modally disabled, use another parent to ensure modality!" );
1000 // link all dialogs which are being executed
1001 pSVData
->mpWinData
->mpExecuteDialogs
.push_back(this);
1003 // stop capturing, in order to have control over the dialog
1004 if (pSVData
->mpWinData
->mpTrackWin
)
1005 pSVData
->mpWinData
->mpTrackWin
->EndTracking(TrackingEventFlags::Cancel
);
1006 if (pSVData
->mpWinData
->mpCaptureWin
)
1007 pSVData
->mpWinData
->mpCaptureWin
->ReleaseMouse();
1012 // no real modality in LibreOfficeKit
1013 if (!bKitActive
&& bModal
)
1014 SetModalInputMode(true);
1016 // FIXME: no layouting, workaround some clipping issues
1017 ImplAdjustNWFSizes();
1019 css::uno::Reference
< css::uno::XComponentContext
> xContext(
1020 comphelper::getProcessComponentContext());
1021 bool bForceFocusAndToFront(officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get());
1022 ShowFlags showFlags
= bForceFocusAndToFront
? ShowFlags::ForegroundTask
: ShowFlags::NONE
;
1023 Show(true, showFlags
);
1026 pSVData
->maAppData
.mnModalMode
++;
1028 css::uno::Reference
<css::frame::XGlobalEventBroadcaster
> xEventBroadcaster(
1029 css::frame::theGlobalEventBroadcaster::get(xContext
), css::uno::UNO_SET_THROW
);
1030 css::document::DocumentEvent aObject
;
1031 aObject
.EventName
= "DialogExecute";
1032 xEventBroadcaster
->documentEventOccured(aObject
);
1034 UITestLogger::getInstance().log(Concat2View("Open Modal " + get_id()));
1036 UITestLogger::getInstance().log(Concat2View("Open Modeless " + get_id()));
1038 bool bTunnelingEnabled
= mpDialogImpl
->m_bLOKTunneling
;
1039 if (comphelper::LibreOfficeKit::isActive() && bTunnelingEnabled
)
1041 if (const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier())
1043 // Dialog boxes don't get the Resize call and they
1044 // can have invalid size at 'created' message above.
1045 // If there is no difference, the client should detect it and ignore us,
1046 // otherwise, this should make sure that the window has the correct size.
1047 std::vector
<vcl::LOKPayloadItem
> aItems
;
1048 aItems
.emplace_back("size", GetSizePixel().toString());
1049 aItems
.emplace_back("unique_id", this->get_id().toUtf8());
1050 pNotifier
->notifyWindow(GetLOKWindowId(), "size_changed", aItems
);
1057 void Dialog::ImplEndExecuteModal()
1059 ImplSVData
* pSVData
= ImplGetSVData();
1060 pSVData
->maAppData
.mnModalMode
--;
1063 short Dialog::Execute()
1065 VclPtr
<vcl::Window
> xWindow
= this;
1067 mbInSyncExecute
= true;
1068 comphelper::ScopeGuard
aGuard([&]() {
1069 mbInSyncExecute
= false;
1072 if ( !ImplStartExecute() )
1075 // Yield util EndDialog is called or dialog gets destroyed
1076 // (the latter should not happen, but better safe than sorry
1077 while ( !xWindow
->isDisposed() && mbInExecute
&& !Application::IsQuit() )
1078 Application::Yield();
1080 ImplEndExecuteModal();
1082 assert (!mpDialogParent
|| !mpDialogParent
->isDisposed());
1084 if ( !xWindow
->isDisposed() )
1088 OSL_FAIL( "Dialog::Execute() - Dialog destroyed in Execute()" );
1091 assert(mpDialogImpl
);
1095 tools::Long nRet
= mpDialogImpl
->mnResult
;
1096 mpDialogImpl
->mnResult
= -1;
1098 return static_cast<short>(nRet
);
1102 SAL_WARN( "vcl", "Dialog::Execute() : missing mpDialogImpl " );
1108 bool Dialog::StartExecuteAsync( VclAbstractDialog::AsyncContext
&rCtx
)
1110 const bool bModal
= GetType() != WindowType::MODELESSDIALOG
;
1111 if (!ImplStartExecute())
1113 rCtx
.mxOwner
.disposeAndClear();
1114 rCtx
.mxOwnerDialogController
.reset();
1115 rCtx
.mxOwnerSelf
.reset();
1119 mpDialogImpl
->maEndCtx
= rCtx
;
1120 mpDialogImpl
->mbStartedModal
= bModal
;
1125 void Dialog::RemoveFromDlgList()
1127 ImplSVData
* pSVData
= ImplGetSVData();
1128 auto& rExecuteDialogs
= pSVData
->mpWinData
->mpExecuteDialogs
;
1130 // remove dialog from the list of dialogs which are being executed
1131 rExecuteDialogs
.erase(std::remove_if(rExecuteDialogs
.begin(), rExecuteDialogs
.end(), [this](VclPtr
<Dialog
>& dialog
){ return dialog
.get() == this; }), rExecuteDialogs
.end());
1134 void Dialog::EndDialog( tools::Long nResult
)
1136 if (!mbInExecute
|| isDisposed())
1139 const bool bModal
= GetType() != WindowType::MODELESSDIALOG
;
1143 if (comphelper::LibreOfficeKit::isActive())
1145 if(const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier())
1147 if (mpDialogImpl
->m_bLOKTunneling
)
1148 pNotifier
->notifyWindow(GetLOKWindowId(), "close");
1149 ReleaseLOKNotifier();
1155 SetModalInputMode(false);
1157 RemoveFromDlgList();
1159 // set focus to previous modal dialog if it is modal for
1160 // the same frame parent (or NULL)
1161 ImplSVData
* pSVData
= ImplGetSVData();
1162 if (!pSVData
->mpWinData
->mpExecuteDialogs
.empty())
1164 VclPtr
<Dialog
> pPrevious
= pSVData
->mpWinData
->mpExecuteDialogs
.back();
1166 vcl::Window
* pFrameParent
= ImplGetFrameWindow()->ImplGetParent();
1167 vcl::Window
* pPrevFrameParent
= pPrevious
->ImplGetFrameWindow()? pPrevious
->ImplGetFrameWindow()->ImplGetParent(): nullptr;
1168 if( ( !pFrameParent
&& !pPrevFrameParent
) ||
1169 ( pFrameParent
&& pPrevFrameParent
&& pFrameParent
->ImplGetFrame() == pPrevFrameParent
->ImplGetFrame() )
1172 pPrevious
->GrabFocus();
1177 mpDialogImpl
->mnResult
= nResult
;
1179 if ( mpDialogImpl
->mbStartedModal
)
1180 ImplEndExecuteModal();
1182 // coverity[check_after_deref] - ImplEndExecuteModal might trigger destruction of mpDialogImpl
1183 if ( mpDialogImpl
&& mpDialogImpl
->maEndCtx
.isSet() )
1185 auto fn
= std::move(mpDialogImpl
->maEndCtx
.maEndDialogFn
);
1186 // std::move leaves maEndDialogFn in a valid state with unspecified
1187 // value. For the SwSyncBtnDlg case gcc and msvc left maEndDialogFn
1188 // unset, but clang left maEndDialogFn at its original value, keeping
1189 // an extra reference to the DialogController in its lambda giving
1190 // an inconsistent lifecycle for the dialog. Force it to be unset.
1191 mpDialogImpl
->maEndCtx
.maEndDialogFn
= nullptr;
1195 if ( mpDialogImpl
&& mpDialogImpl
->mbStartedModal
)
1197 mpDialogImpl
->mbStartedModal
= false;
1198 mpDialogImpl
->mnResult
= -1;
1200 mbInExecute
= false;
1204 // Destroy ourselves (if we have a context with VclPtr owner)
1205 std::shared_ptr
<weld::DialogController
> xOwnerDialogController
= std::move(mpDialogImpl
->maEndCtx
.mxOwnerDialogController
);
1206 std::shared_ptr
<weld::Dialog
> xOwnerSelf
= std::move(mpDialogImpl
->maEndCtx
.mxOwnerSelf
);
1207 mpDialogImpl
->maEndCtx
.mxOwner
.disposeAndClear();
1208 xOwnerDialogController
.reset();
1215 void EndAllDialogs( vcl::Window
const * pParent
)
1217 ImplSVData
* pSVData
= ImplGetSVData();
1218 auto& rExecuteDialogs
= pSVData
->mpWinData
->mpExecuteDialogs
;
1220 for (auto it
= rExecuteDialogs
.rbegin(); it
!= rExecuteDialogs
.rend(); ++it
)
1222 if (!pParent
|| pParent
->IsWindowOrChild(*it
, true))
1225 (*it
)->PostUserEvent(Link
<void*, void>());
1230 void EnableDialogInput(vcl::Window
* pWindow
)
1232 if (Dialog
* pDialog
= dynamic_cast<Dialog
*>(pWindow
))
1234 pDialog
->EnableInput();
1238 void CloseTopLevel(vcl::Window
* pWindow
)
1240 if (Dialog
* pDialog
= dynamic_cast<Dialog
*>(pWindow
))
1242 else if (FloatingWindow
* pFloatWin
= dynamic_cast<FloatingWindow
*>(pWindow
))
1243 pFloatWin
->EndPopupMode(FloatWinPopupEndFlags::Cancel
| FloatWinPopupEndFlags::CloseAll
);
1247 void Dialog::SetModalInputMode( bool bModal
)
1249 if ( bModal
== mbModalMode
)
1252 ImplGetFrame()->SetModal(bModal
);
1256 SalFrame
* pFrame
= GetParent()->ImplGetFrame();
1257 pFrame
->NotifyModalHierarchy(bModal
);
1260 ImplSetModalInputMode(bModal
);
1263 void Dialog::ImplSetModalInputMode( bool bModal
)
1265 if ( bModal
== mbModalMode
)
1268 // previously Execute()'d dialog - the one below the top-most one
1269 VclPtr
<Dialog
> pPrevious
;
1270 ImplSVData
* pSVData
= ImplGetSVData();
1271 auto& rExecuteDialogs
= pSVData
->mpWinData
->mpExecuteDialogs
;
1272 if (rExecuteDialogs
.size() > 1)
1273 pPrevious
= rExecuteDialogs
[rExecuteDialogs
.size() - 2];
1275 mbModalMode
= bModal
;
1278 // Disable the prev Modal Dialog, because our dialog must close at first,
1279 // before the other dialog can be closed (because the other dialog
1280 // is on stack since our dialog returns)
1281 if (pPrevious
&& !pPrevious
->IsWindowOrChild(this, true))
1282 pPrevious
->EnableInput(false, this);
1284 // determine next overlap dialog parent
1285 vcl::Window
* pParent
= GetParent();
1288 // #103716# dialogs should always be modal to the whole frame window
1289 // #115933# disable the whole frame hierarchy, useful if our parent
1290 // is a modeless dialog
1291 mpDialogParent
= pParent
->mpWindowImpl
->mpFrameWindow
;
1292 mpDialogParent
->IncModalCount();
1297 if ( mpDialogParent
)
1299 // #115933# re-enable the whole frame hierarchy again (see above)
1300 // note that code in getfocus assures that we do not accidentally enable
1301 // windows that were disabled before
1302 mpDialogParent
->DecModalCount();
1305 // Enable the prev Modal Dialog
1306 if (pPrevious
&& !pPrevious
->IsWindowOrChild(this, true))
1308 pPrevious
->EnableInput(true, this);
1310 // ensure continued modality of prev dialog
1311 // do not change modality counter
1313 // #i119994# need find the last modal dialog before reactive it
1314 if (pPrevious
->IsModalInputMode() || !pPrevious
->IsWindowOrChild(this, true))
1316 pPrevious
->ImplSetModalInputMode(false);
1317 pPrevious
->ImplSetModalInputMode(true);
1323 vcl::Window
* Dialog::GetFirstControlForFocus()
1325 vcl::Window
* pFocusControl
= nullptr;
1326 vcl::Window
* pFirstOverlapWindow
= ImplGetFirstOverlapWindow();
1328 // find focus control, even if the dialog has focus
1329 if (!HasFocus() && pFirstOverlapWindow
&& pFirstOverlapWindow
->mpWindowImpl
)
1331 // prefer a child window which had focus before
1332 pFocusControl
= ImplGetFirstOverlapWindow()->mpWindowImpl
->mpLastFocusWindow
;
1333 // find the control out of the dialog control
1334 if ( pFocusControl
)
1335 pFocusControl
= ImplFindDlgCtrlWindow( pFocusControl
);
1337 // no control had the focus before or the control is not
1338 // part of the tab-control, now give focus to the
1339 // first control in the tab-control
1340 if ( !pFocusControl
||
1341 !(pFocusControl
->GetStyle() & WB_TABSTOP
) ||
1342 !isVisibleInLayout(pFocusControl
) ||
1343 !isEnabledInLayout(pFocusControl
) || !pFocusControl
->IsInputEnabled() )
1345 pFocusControl
= ImplGetDlgWindow( 0, GetDlgWindowType::First
);
1348 return pFocusControl
;
1351 void Dialog::GrabFocusToFirstControl()
1353 vcl::Window
* pFocusControl
= GetFirstControlForFocus();
1354 if ( pFocusControl
)
1355 pFocusControl
->ImplControlFocus( GetFocusFlags::Init
);
1358 void Dialog::GetDrawWindowBorder( sal_Int32
& rLeftBorder
, sal_Int32
& rTopBorder
, sal_Int32
& rRightBorder
, sal_Int32
& rBottomBorder
) const
1360 ScopedVclPtrInstance
<ImplBorderWindow
> aImplWin( static_cast<vcl::Window
*>(const_cast<Dialog
*>(this)), WB_BORDER
|WB_STDWORK
, BorderWindowStyle::Overlap
);
1361 aImplWin
->GetBorder( rLeftBorder
, rTopBorder
, rRightBorder
, rBottomBorder
);
1364 void Dialog::Draw( OutputDevice
* pDev
, const Point
& rPos
, SystemTextColorFlags
)
1366 Point aPos
= pDev
->LogicToPixel( rPos
);
1367 Size aSize
= GetSizePixel();
1369 Wallpaper aWallpaper
= GetBackground();
1370 if ( !aWallpaper
.IsBitmap() )
1375 pDev
->SetLineColor();
1377 if ( aWallpaper
.IsBitmap() )
1378 pDev
->DrawBitmapEx( aPos
, aSize
, aWallpaper
.GetBitmap() );
1381 pDev
->SetFillColor( aWallpaper
.GetColor() );
1382 pDev
->DrawRect( tools::Rectangle( aPos
, aSize
) );
1385 if (!( GetStyle() & WB_NOBORDER
))
1387 ScopedVclPtrInstance
< ImplBorderWindow
> aImplWin( this, WB_BORDER
|WB_STDWORK
, BorderWindowStyle::Overlap
);
1388 aImplWin
->SetText( GetText() );
1389 aImplWin
->setPosSizePixel( aPos
.X(), aPos
.Y(), aSize
.Width(), aSize
.Height() );
1390 aImplWin
->SetDisplayActive( true );
1391 aImplWin
->InitView();
1393 aImplWin
->Draw( pDev
, aPos
);
1399 void Dialog::queue_resize(StateChangedType eReason
)
1403 SystemWindow::queue_resize(eReason
);
1406 void Dialog::Resize()
1408 SystemWindow::Resize();
1410 if (comphelper::LibreOfficeKit::isDialogPainting())
1413 bool bTunnelingEnabled
= mpDialogImpl
->m_bLOKTunneling
;
1414 const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier();
1415 if (pNotifier
&& bTunnelingEnabled
)
1417 std::vector
<vcl::LOKPayloadItem
> aItems
;
1418 aItems
.emplace_back("size", GetSizePixel().toString());
1419 aItems
.emplace_back("unique_id", this->get_id().toUtf8());
1420 pNotifier
->notifyWindow(GetLOKWindowId(), "size_changed", aItems
);
1424 bool Dialog::set_property(const OUString
&rKey
, const OUString
&rValue
)
1426 if (rKey
== "border-width")
1427 set_border_width(rValue
.toInt32());
1429 return SystemWindow::set_property(rKey
, rValue
);
1433 FactoryFunction
Dialog::GetUITestFactory() const
1435 return DialogUIObject::create
;
1438 IMPL_LINK(Dialog
, ResponseHdl
, Button
*, pButton
, void)
1440 auto aFind
= mpDialogImpl
->maResponses
.find(pButton
);
1441 if (aFind
== mpDialogImpl
->maResponses
.end())
1443 short nResponse
= aFind
->second
;
1444 if (nResponse
== RET_HELP
)
1446 vcl::Window
* pFocusWin
= Application::GetFocusWindow();
1447 if (!pFocusWin
|| comphelper::LibreOfficeKit::isActive())
1448 pFocusWin
= pButton
;
1449 HelpEvent
aEvt(pFocusWin
->GetPointerPosPixel(), HelpEventMode::CONTEXT
);
1450 pFocusWin
->RequestHelp(aEvt
);
1453 EndDialog(nResponse
);
1456 void Dialog::add_button(PushButton
* pButton
, int response
, bool bTransferOwnership
)
1458 if (bTransferOwnership
)
1459 mpDialogImpl
->maOwnedButtons
.push_back(pButton
);
1460 mpDialogImpl
->maResponses
[pButton
] = response
;
1461 switch (pButton
->GetType())
1463 case WindowType::PUSHBUTTON
:
1465 if (!pButton
->GetClickHdl().IsSet())
1466 pButton
->SetClickHdl(LINK(this, Dialog
, ResponseHdl
));
1469 //insist that the response ids match the default actions for those
1470 //widgets, and leave their default handlers in place
1471 case WindowType::OKBUTTON
:
1472 assert(mpDialogImpl
->get_response(pButton
) == RET_OK
);
1474 case WindowType::CANCELBUTTON
:
1475 assert(mpDialogImpl
->get_response(pButton
) == RET_CANCEL
|| mpDialogImpl
->get_response(pButton
) == RET_CLOSE
);
1477 case WindowType::HELPBUTTON
:
1478 assert(mpDialogImpl
->get_response(pButton
) == RET_HELP
);
1481 SAL_WARN("vcl.layout", "The type of widget " <<
1482 pButton
->GetHelpId() << " is currently not handled");
1487 vcl::Window
* Dialog::get_widget_for_response(int response
)
1489 //copy explicit responses
1490 std::map
<VclPtr
<vcl::Window
>, short> aResponses(mpDialogImpl
->maResponses
);
1494 //add implicit responses
1495 for (vcl::Window
* pChild
= mpActionArea
->GetWindow(GetWindowType::FirstChild
); pChild
;
1496 pChild
= pChild
->GetWindow(GetWindowType::Next
))
1498 if (aResponses
.find(pChild
) != aResponses
.end())
1500 switch (pChild
->GetType())
1502 case WindowType::OKBUTTON
:
1503 aResponses
[pChild
] = RET_OK
;
1505 case WindowType::CANCELBUTTON
:
1506 aResponses
[pChild
] = RET_CANCEL
;
1508 case WindowType::HELPBUTTON
:
1509 aResponses
[pChild
] = RET_HELP
;
1517 for (const auto& a
: aResponses
)
1519 if (a
.second
== response
)
1526 int Dialog::get_default_response() const
1528 //copy explicit responses
1529 std::map
<VclPtr
<vcl::Window
>, short> aResponses(mpDialogImpl
->maResponses
);
1533 //add implicit responses
1534 for (vcl::Window
* pChild
= mpActionArea
->GetWindow(GetWindowType::FirstChild
); pChild
;
1535 pChild
= pChild
->GetWindow(GetWindowType::Next
))
1537 if (aResponses
.find(pChild
) != aResponses
.end())
1539 switch (pChild
->GetType())
1541 case WindowType::OKBUTTON
:
1542 aResponses
[pChild
] = RET_OK
;
1544 case WindowType::CANCELBUTTON
:
1545 aResponses
[pChild
] = RET_CANCEL
;
1547 case WindowType::HELPBUTTON
:
1548 aResponses
[pChild
] = RET_HELP
;
1556 for (const auto& a
: aResponses
)
1558 if (a
.first
->GetStyle() & WB_DEFBUTTON
)
1566 void Dialog::set_default_response(int response
)
1568 //copy explicit responses
1569 std::map
<VclPtr
<vcl::Window
>, short> aResponses(mpDialogImpl
->maResponses
);
1573 //add implicit responses
1574 for (vcl::Window
* pChild
= mpActionArea
->GetWindow(GetWindowType::FirstChild
); pChild
;
1575 pChild
= pChild
->GetWindow(GetWindowType::Next
))
1577 if (aResponses
.find(pChild
) != aResponses
.end())
1579 switch (pChild
->GetType())
1581 case WindowType::OKBUTTON
:
1582 aResponses
[pChild
] = RET_OK
;
1584 case WindowType::CANCELBUTTON
:
1585 aResponses
[pChild
] = RET_CANCEL
;
1587 case WindowType::HELPBUTTON
:
1588 aResponses
[pChild
] = RET_HELP
;
1596 for (auto& a
: aResponses
)
1598 if (a
.second
== response
)
1600 a
.first
->SetStyle(a
.first
->GetStyle() | WB_DEFBUTTON
);
1601 a
.first
->GrabFocus();
1605 a
.first
->SetStyle(a
.first
->GetStyle() & ~WB_DEFBUTTON
);
1610 VclBuilderContainer::VclBuilderContainer()
1614 void VclBuilderContainer::setDeferredProperties()
1618 m_pUIBuilder
->setDeferredProperties();
1621 VclBuilderContainer::~VclBuilderContainer()
1625 void Dialog::Activate()
1627 if (GetType() == WindowType::MODELESSDIALOG
)
1629 css::uno::Reference
< css::uno::XComponentContext
> xContext(
1630 comphelper::getProcessComponentContext() );
1631 css::uno::Reference
<css::frame::XGlobalEventBroadcaster
> xEventBroadcaster(css::frame::theGlobalEventBroadcaster::get(xContext
), css::uno::UNO_SET_THROW
);
1632 css::document::DocumentEvent aObject
;
1633 aObject
.EventName
= "ModelessDialogVisible";
1634 xEventBroadcaster
->documentEventOccured(aObject
);
1636 SystemWindow::Activate();
1639 void Dialog::Command(const CommandEvent
& rCEvt
)
1641 if (mpDialogImpl
&& mpDialogImpl
->m_aPopupMenuHdl
.Call(rCEvt
))
1643 SystemWindow::Command(rCEvt
);
1646 struct TopLevelWindowLockerImpl
1648 std::stack
<std::vector
<VclPtr
<vcl::Window
>>> m_aBusyStack
;
1651 TopLevelWindowLocker::TopLevelWindowLocker()
1652 : m_xImpl(std::make_unique
<TopLevelWindowLockerImpl
>())
1656 void TopLevelWindowLocker::incBusy(const weld::Widget
* pIgnore
)
1658 // lock any toplevel windows from being closed until busy is over
1659 std::vector
<VclPtr
<vcl::Window
>> aTopLevels
;
1660 vcl::Window
*pTopWin
= Application::GetFirstTopLevelWindow();
1663 vcl::Window
* pCandidate
= pTopWin
;
1664 if (pCandidate
->GetType() == WindowType::BORDERWINDOW
)
1665 pCandidate
= pCandidate
->GetWindow(GetWindowType::FirstChild
);
1666 // tdf#125266 ignore HelpTextWindows
1668 pCandidate
->GetType() != WindowType::HELPTEXTWINDOW
&&
1669 pCandidate
->GetType() != WindowType::FLOATINGWINDOW
&&
1670 pCandidate
->GetFrameWeld() != pIgnore
)
1672 aTopLevels
.push_back(pCandidate
);
1674 pTopWin
= Application::GetNextTopLevelWindow(pTopWin
);
1676 for (auto& a
: aTopLevels
)
1679 a
->ImplGetFrame()->NotifyModalHierarchy(true);
1681 m_xImpl
->m_aBusyStack
.push(aTopLevels
);
1684 void TopLevelWindowLocker::decBusy()
1686 // unlock locked toplevel windows from being closed now busy is over
1687 for (auto& a
: m_xImpl
->m_aBusyStack
.top())
1689 if (a
->isDisposed())
1692 a
->ImplGetFrame()->NotifyModalHierarchy(false);
1694 m_xImpl
->m_aBusyStack
.pop();
1697 bool TopLevelWindowLocker::isBusy() const
1699 return !m_xImpl
->m_aBusyStack
.empty();
1702 TopLevelWindowLocker::~TopLevelWindowLocker()
1706 void Dialog::DumpAsPropertyTree(tools::JsonWriter
& rJsonWriter
)
1708 SystemWindow::DumpAsPropertyTree(rJsonWriter
);
1709 rJsonWriter
.put("title", GetText());
1710 if (vcl::Window
* pActionArea
= get_action_area())
1712 if (!pActionArea
->IsVisible())
1713 rJsonWriter
.put("collapsed", true);
1716 OUString sDialogId
= GetHelpId();
1717 sal_Int32 nStartPos
= sDialogId
.lastIndexOf('/');
1718 nStartPos
= nStartPos
>= 0 ? nStartPos
+ 1 : 0;
1719 rJsonWriter
.put("dialogid", sDialogId
.copy(nStartPos
));
1722 auto aResponses
= rJsonWriter
.startArray("responses");
1723 for (const auto& rResponse
: mpDialogImpl
->maResponses
)
1725 auto aResponse
= rJsonWriter
.startStruct();
1726 rJsonWriter
.put("id", rResponse
.first
->get_id());
1727 rJsonWriter
.put("response", rResponse
.second
);
1731 vcl::Window
* pFocusControl
= GetFirstControlForFocus();
1733 rJsonWriter
.put("init_focus_id", pFocusControl
->get_id());
1736 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */