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