bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / window / dialog.cxx
blobd17f2d1d0695dc40fa8233f9a7d4ea2e221d5552
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
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>
22 #ifdef IOS
23 #include <premac.h>
24 #include <UIKit/UIKit.h>
25 #include <postmac.h>
26 #endif
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>
35 #include <svdata.hxx>
36 #include <window.h>
37 #include <accel.hxx>
38 #include <brdwin.hxx>
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>
67 #include <iostream>
68 #include <stack>
69 #include <utility>
70 #include <vector>
72 static OString ImplGetDialogText( Dialog* pDialog )
74 OUString aErrorStr(pDialog->GetText());
76 OUString sMessage;
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() )
92 return false;
94 if ( (pWindow->GetType() == WindowType::RADIOBUTTON) ||
95 (pWindow->GetType() == WindowType::CHECKBOX) ||
96 (pWindow->GetType() == WindowType::TRISTATEBOX) ||
97 (pWindow->GetType() == WindowType::PUSHBUTTON) )
98 return true;
100 if ( pWindow->GetType() == WindowType::FIXEDTEXT )
102 FixedText *pText = static_cast<FixedText*>(pWindow);
103 if (pText->get_mnemonic_widget())
104 return true;
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)
108 return false;
109 vcl::Window* pNextWindow = pWindow->GetWindow( GetWindowType::Next );
110 if ( !pNextWindow )
111 return false;
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));
122 return false;
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);
141 else
142 pChild = pChild->GetWindow(GetWindowType::Next);
144 while (!pChild)
146 vcl::Window *pParent = pLastChild->GetParent();
147 if (!pParent)
148 return nullptr;
149 if (pParent == pTopLevel)
150 return nullptr;
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);
169 else
170 pChild = pChild->GetWindow(GetWindowType::Prev);
172 while (!pChild)
174 vcl::Window *pParent = pLastChild->GetParent();
175 if (!pParent)
176 return nullptr;
177 if (pParent == pTopLevel)
178 return nullptr;
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;
209 vcl::Window* pChild;
211 // register the assigned mnemonics
212 pGetChild = pWindow->GetWindow( GetWindowType::FirstChild );
213 while ( pGetChild )
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 );
230 while ( pGetChild )
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 );
241 while ( pGetChild )
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);
263 while (pChild)
265 pButtonBox = dynamic_cast<VclButtonBox*>(pChild);
266 if (pButtonBox)
267 break;
268 pChild = pChild->GetWindow(GetWindowType::Prev);
271 return pButtonBox;
274 static vcl::Window* getActionAreaButtonList(Dialog const *pDialog)
276 VclButtonBox* pButtonBox = getActionArea(pDialog);
277 if (pButtonBox)
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);
285 while ( pChild )
287 if ( pChild->ImplIsPushButton() )
289 PushButton* pPushButton = static_cast<PushButton*>(pChild);
290 if ( pPushButton->ImplIsDefButton() )
291 return pPushButton;
294 pChild = pChild->GetWindow( GetWindowType::Next );
297 return nullptr;
300 static PushButton* ImplGetOKButton( Dialog const * pDialog )
302 vcl::Window* pChild = getActionAreaButtonList(pDialog);
303 while ( pChild )
305 if ( pChild->GetType() == WindowType::OKBUTTON )
306 return static_cast<PushButton*>(pChild);
308 pChild = pChild->GetWindow( GetWindowType::Next );
311 return nullptr;
314 static PushButton* ImplGetCancelButton( Dialog const * pDialog )
316 vcl::Window* pChild = getActionAreaButtonList(pDialog);
318 while ( pChild )
320 if ( pChild->GetType() == WindowType::CANCELBUTTON )
321 return static_cast<PushButton*>(pChild);
323 pChild = pChild->GetWindow( GetWindowType::Next );
326 return nullptr;
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 );
340 if ( !pWindow )
341 pWindow = ImplGetOKButton( pDialog );
342 if ( !pWindow )
343 pWindow = ImplGetCancelButton( pDialog );
344 if ( !pWindow )
345 pWindow = pDialog;
346 Size aSize = pWindow->GetOutputSizePixel();
347 pWindow->SetPointerPosPixel( Point( aSize.Width()/2, aSize.Height()/2 ) );
351 struct DialogImpl
353 std::vector<VclPtr<PushButton>> maOwnedButtons;
354 std::map<VclPtr<vcl::Window>, short> maResponses;
355 tools::Long mnResult;
356 bool mbStartedModal;
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 ) {}
364 #ifndef NDEBUG
365 short get_response(vcl::Window *pWindow) const
367 auto aFind = maResponses.find(pWindow);
368 if (aFind != maResponses.end())
369 return aFind->second;
370 return RET_CANCEL;
372 #endif
374 ~DialogImpl()
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;
390 mbInExecute = false;
391 mbInSyncExecute = false;
392 mbInClose = false;
393 mbModalMode = 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)
403 return;
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
415 // in this frame
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())
427 pParent = it->get();
430 return pParent;
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;
442 return pBorderWin;
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)
457 pParent = nullptr;
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);
473 else
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;
482 else
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 );
494 ImplInitSettings();
497 void Dialog::ApplySettings(vcl::RenderContext& rRenderContext)
499 if (IsControlBackground())
501 // user override
502 SetBackground(GetControlBackground());
504 else if (rRenderContext.IsNativeControlSupported(ControlType::WindowBackground, ControlPart::BackgroundDialog))
506 // NWF background
507 mpWindowImpl->mnNativeBackground = ControlPart::BackgroundDialog;
508 EnableChildTransparentMode();
510 else
512 // fallback to settings color
513 rRenderContext.SetBackground(GetSettings().GetStyleSettings().GetDialogColor());
517 void Dialog::ImplInitSettings()
519 // user override
520 if (IsControlBackground())
521 SetBackground(GetControlBackground());
522 // NWF background
523 else if( IsNativeControlSupported(ControlType::WindowBackground, ControlPart::BackgroundDialog))
525 mpWindowImpl->mnNativeBackground = ControlPart::BackgroundDialog;
526 EnableChildTransparentMode();
528 // fallback to settings color
529 else
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()
553 if (m_pUIBuilder)
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);
561 return 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")
585 , mnInitFlag(eFlag)
587 ImplLOKNotifier(pParent);
588 ImplInitDialogData();
589 ImplInitDialog( pParent, nStyle, eFlag );
592 void Dialog::set_action_area(VclButtonBox* pBox)
594 mpActionArea.set(pBox);
595 if (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);
616 Dialog::~Dialog()
618 disposeOnce();
621 void Dialog::dispose()
623 bool bTunnelingEnabled = mpDialogImpl->m_bLOKTunneling;
625 mpDialogImpl.reset();
626 RemoveFromDlgList();
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)
653 Close();
656 bool Dialog::EventNotify( NotifyEvent& rNEvt )
658 // first call the base class due to Tab control
659 bool bRet = SystemWindow::EventNotify( rNEvt );
660 if ( !bRet )
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
674 // we get destroyed
675 PostUserEvent( LINK( this, Dialog, ImplAsyncCloseHdl ), nullptr, true);
676 return 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 );
700 return bRet;
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)
708 #ifndef IOS
709 tools::Long w = rScreenSize.Width();
710 if (w <= 800)
711 w -= 15;
712 else if (w <= 1024)
713 w -= 65;
714 else
715 w -= 115;
717 tools::Long h = rScreenSize.Height();
718 if (h <= 768)
719 h -= 50;
720 else
721 h -= 100;
723 return Size(std::max<tools::Long>(w, 640 - 15),
724 std::max<tools::Long>(h, 480 - 50));
725 #else
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").
733 (void) rScreenSize;
735 const int n = std::min<CGFloat>([[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height);
736 return Size(n-10, n-10);
737 #endif
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)
761 DoInitialLayout();
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);
778 else
780 vcl::ILibreOfficeKitNotifier* pViewShell = mpDialogImpl->m_aInstallLOKNotifierHdl.Call(nullptr);
781 if (pViewShell)
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)
817 ImplInitSettings();
818 Invalidate();
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) )
840 ImplInitSettings();
841 Invalidate();
845 bool Dialog::Close()
847 VclPtr<vcl::Window> xWindow = this;
848 CallEventListeners( VclEventId::WindowClose );
849 if ( xWindow->isDisposed() )
850 return false;
852 if ( mpWindowImpl->mxWindowPeer.is() && IsCreatedWithToolkit() && !IsInExecute() )
853 return false;
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;
861 else
862 pCustomCancelButton = nullptr;
864 mbInClose = true;
866 if (pCustomCancelButton)
868 pCustomCancelButton->Click();
869 if (xWindow->isDisposed())
870 return true;
871 mbInClose = false;
872 return false;
875 if ( !(GetStyle() & WB_CLOSEABLE) )
877 bool bRet = true;
878 PushButton* pButton = ImplGetCancelButton( this );
879 if ( pButton )
880 pButton->Click();
881 else
883 pButton = ImplGetOKButton( this );
884 if ( pButton )
885 pButton->Click();
886 else
887 bRet = false;
889 if ( xWindow->isDisposed() )
890 return true;
891 return bRet;
894 if (IsInExecute() || mpDialogImpl->maEndCtx.isSet())
896 EndDialog();
897 mbInClose = false;
898 return true;
900 else
902 mbInClose = false;
903 return SystemWindow::Close();
907 bool Dialog::ImplStartExecute()
909 setDeferredProperties();
911 if (IsInExecute() || mpDialogImpl->maEndCtx.isSet())
913 #ifdef DBG_UTIL
914 SAL_WARN( "vcl", "Dialog::StartExecuteModal() is called in Dialog::StartExecuteModal(): "
915 << ImplGetDialogText(this) );
916 #endif
917 return false;
920 ImplSVData* pSVData = ImplGetSVData();
922 const bool bKitActive = comphelper::LibreOfficeKit::isActive();
924 const bool bModal = GetType() != WindowType::MODELESSDIALOG;
926 if (bModal)
928 if (bKitActive && !GetLOKNotifier())
930 #ifdef IOS
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.
938 return false;
939 #else
940 SetLOKNotifier(mpDialogImpl->m_aInstallLOKNotifierHdl.Call(nullptr));
941 #endif
944 switch ( Application::GetDialogCancelMode() )
946 case DialogCancelMode::Off:
947 break;
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()))
958 break;
959 else
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";
969 SAL_INFO(
970 "vcl",
971 "Dialog \"" << ImplGetDialogText(this)
972 << "\"cancelled in silent mode");
973 return false;
975 case DialogCancelMode::LOKSilent:
976 return false;
978 default: // default cannot happen
979 case DialogCancelMode::Fatal:
980 std::abort();
983 #ifdef DBG_UTIL
984 vcl::Window* pParent = GetParent();
985 if ( pParent )
987 pParent = pParent->ImplGetFirstOverlapWindow();
988 if (pParent)
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!" );
998 #endif
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();
1008 EnableInput();
1011 mbInExecute = true;
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);
1025 if (bModal)
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);
1033 if (bModal)
1034 UITestLogger::getInstance().log(Concat2View("Open Modal " + get_id()));
1035 else
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);
1054 return true;
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() )
1073 return 0;
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();
1081 #ifdef DBG_UTIL
1082 assert (!mpDialogParent || !mpDialogParent->isDisposed());
1083 #endif
1084 if ( !xWindow->isDisposed() )
1085 xWindow.clear();
1086 else
1088 OSL_FAIL( "Dialog::Execute() - Dialog destroyed in Execute()" );
1091 assert(mpDialogImpl);
1093 if (mpDialogImpl)
1095 tools::Long nRet = mpDialogImpl->mnResult;
1096 mpDialogImpl->mnResult = -1;
1098 return static_cast<short>(nRet);
1100 else
1102 SAL_WARN( "vcl", "Dialog::Execute() : missing mpDialogImpl " );
1103 return 0;
1107 // virtual
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();
1116 return false;
1119 mpDialogImpl->maEndCtx = rCtx;
1120 mpDialogImpl->mbStartedModal = bModal;
1122 return true;
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())
1137 return;
1139 const bool bModal = GetType() != WindowType::MODELESSDIALOG;
1141 Hide();
1143 if (comphelper::LibreOfficeKit::isActive())
1145 if(const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
1147 if (mpDialogImpl->m_bLOKTunneling)
1148 pNotifier->notifyWindow(GetLOKWindowId(), "close");
1149 ReleaseLOKNotifier();
1153 if (bModal)
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;
1192 fn(nResult);
1195 if ( mpDialogImpl && mpDialogImpl->mbStartedModal )
1197 mpDialogImpl->mbStartedModal = false;
1198 mpDialogImpl->mnResult = -1;
1200 mbInExecute = false;
1202 if ( mpDialogImpl )
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();
1209 xOwnerSelf.reset();
1213 namespace vcl
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))
1224 (*it)->EndDialog();
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))
1241 pDialog->Close();
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 )
1250 return;
1252 ImplGetFrame()->SetModal(bModal);
1254 if (GetParent())
1256 SalFrame* pFrame = GetParent()->ImplGetFrame();
1257 pFrame->NotifyModalHierarchy(bModal);
1260 ImplSetModalInputMode(bModal);
1263 void Dialog::ImplSetModalInputMode( bool bModal )
1265 if ( bModal == mbModalMode )
1266 return;
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;
1276 if ( 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();
1286 if ( pParent )
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();
1295 else
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() )
1371 ImplInitSettings();
1373 pDev->Push();
1374 pDev->SetMapMode();
1375 pDev->SetLineColor();
1377 if ( aWallpaper.IsBitmap() )
1378 pDev->DrawBitmapEx( aPos, aSize, aWallpaper.GetBitmap() );
1379 else
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 );
1396 pDev->Pop();
1399 void Dialog::queue_resize(StateChangedType eReason)
1401 if (IsInClose())
1402 return;
1403 SystemWindow::queue_resize(eReason);
1406 void Dialog::Resize()
1408 SystemWindow::Resize();
1410 if (comphelper::LibreOfficeKit::isDialogPainting())
1411 return;
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());
1428 else
1429 return SystemWindow::set_property(rKey, rValue);
1430 return true;
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())
1442 return;
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);
1451 return;
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));
1467 break;
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);
1473 break;
1474 case WindowType::CANCELBUTTON:
1475 assert(mpDialogImpl->get_response(pButton) == RET_CANCEL || mpDialogImpl->get_response(pButton) == RET_CLOSE);
1476 break;
1477 case WindowType::HELPBUTTON:
1478 assert(mpDialogImpl->get_response(pButton) == RET_HELP);
1479 break;
1480 default:
1481 SAL_WARN("vcl.layout", "The type of widget " <<
1482 pButton->GetHelpId() << " is currently not handled");
1483 break;
1487 vcl::Window* Dialog::get_widget_for_response(int response)
1489 //copy explicit responses
1490 std::map<VclPtr<vcl::Window>, short> aResponses(mpDialogImpl->maResponses);
1492 if (mpActionArea)
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())
1499 continue;
1500 switch (pChild->GetType())
1502 case WindowType::OKBUTTON:
1503 aResponses[pChild] = RET_OK;
1504 break;
1505 case WindowType::CANCELBUTTON:
1506 aResponses[pChild] = RET_CANCEL;
1507 break;
1508 case WindowType::HELPBUTTON:
1509 aResponses[pChild] = RET_HELP;
1510 break;
1511 default:
1512 break;
1517 for (const auto& a : aResponses)
1519 if (a.second == response)
1520 return a.first;
1523 return nullptr;
1526 int Dialog::get_default_response() const
1528 //copy explicit responses
1529 std::map<VclPtr<vcl::Window>, short> aResponses(mpDialogImpl->maResponses);
1531 if (mpActionArea)
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())
1538 continue;
1539 switch (pChild->GetType())
1541 case WindowType::OKBUTTON:
1542 aResponses[pChild] = RET_OK;
1543 break;
1544 case WindowType::CANCELBUTTON:
1545 aResponses[pChild] = RET_CANCEL;
1546 break;
1547 case WindowType::HELPBUTTON:
1548 aResponses[pChild] = RET_HELP;
1549 break;
1550 default:
1551 break;
1556 for (const auto& a : aResponses)
1558 if (a.first->GetStyle() & WB_DEFBUTTON)
1560 return a.second;
1563 return RET_CANCEL;
1566 void Dialog::set_default_response(int response)
1568 //copy explicit responses
1569 std::map<VclPtr<vcl::Window>, short> aResponses(mpDialogImpl->maResponses);
1571 if (mpActionArea)
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())
1578 continue;
1579 switch (pChild->GetType())
1581 case WindowType::OKBUTTON:
1582 aResponses[pChild] = RET_OK;
1583 break;
1584 case WindowType::CANCELBUTTON:
1585 aResponses[pChild] = RET_CANCEL;
1586 break;
1587 case WindowType::HELPBUTTON:
1588 aResponses[pChild] = RET_HELP;
1589 break;
1590 default:
1591 break;
1596 for (auto& a : aResponses)
1598 if (a.second == response)
1600 a.first->SetStyle(a.first->GetStyle() | WB_DEFBUTTON);
1601 a.first->GrabFocus();
1603 else
1605 a.first->SetStyle(a.first->GetStyle() & ~WB_DEFBUTTON);
1610 VclBuilderContainer::VclBuilderContainer()
1614 void VclBuilderContainer::setDeferredProperties()
1616 if (!m_pUIBuilder)
1617 return;
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))
1642 return;
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();
1661 while (pTopWin)
1663 vcl::Window* pCandidate = pTopWin;
1664 if (pCandidate->GetType() == WindowType::BORDERWINDOW)
1665 pCandidate = pCandidate->GetWindow(GetWindowType::FirstChild);
1666 // tdf#125266 ignore HelpTextWindows
1667 if (pCandidate &&
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)
1678 a->IncModalCount();
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())
1690 continue;
1691 a->DecModalCount();
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();
1732 if (pFocusControl)
1733 rJsonWriter.put("init_focus_id", pFocusControl->get_id());
1736 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */