build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / window / dialog.cxx
blobc3e22efdeda46e701e35ac2cc10bd62df2444004
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_features.h>
22 #include <com/sun/star/beans/XPropertySet.hpp>
23 #include <com/sun/star/util/thePathSettings.hpp>
24 #include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
25 #include <comphelper/lok.hxx>
26 #include <comphelper/processfactory.hxx>
27 #include <comphelper/scopeguard.hxx>
28 #include <officecfg/Office/Common.hxx>
29 #include <osl/file.hxx>
31 #include <tools/debug.hxx>
33 #include <tools/rc.h>
34 #include <svdata.hxx>
35 #include <window.h>
36 #include <brdwin.hxx>
38 #include <rtl/strbuf.hxx>
39 #include <sal/log.hxx>
41 #include "window.h"
43 #include <vcl/abstdlg.hxx>
44 #include <vcl/builder.hxx>
45 #include <vcl/layout.hxx>
46 #include <vcl/svapp.hxx>
47 #include <vcl/event.hxx>
48 #include <vcl/floatwin.hxx>
49 #include <vcl/wrkwin.hxx>
50 #include <vcl/button.hxx>
51 #include <vcl/mnemonic.hxx>
52 #include <vcl/dialog.hxx>
53 #include <vcl/tabctrl.hxx>
54 #include <vcl/tabpage.hxx>
55 #include <vcl/decoview.hxx>
56 #include <vcl/msgbox.hxx>
57 #include <vcl/unowrap.hxx>
58 #include <vcl/settings.hxx>
59 #include <vcl/uitest/uiobject.hxx>
60 #include <vcl/virdev.hxx>
61 #include <vcl/IDialogRenderable.hxx>
62 #include <salframe.hxx>
64 #include <iostream>
65 #include <utility>
67 static OString ImplGetDialogText( Dialog* pDialog )
69 OStringBuffer aErrorStr(OUStringToOString(
70 pDialog->GetText(), RTL_TEXTENCODING_UTF8));
72 OUString sMessage;
73 if (MessBox* pMessBox = dynamic_cast<MessBox*>(pDialog))
75 sMessage = pMessBox->GetMessText();
77 else if (MessageDialog* pMessDialog = dynamic_cast<MessageDialog*>(pDialog))
79 sMessage = pMessDialog->get_primary_text();
82 if (!sMessage.isEmpty())
84 aErrorStr.append(", ");
85 aErrorStr.append(OUStringToOString(
86 sMessage, RTL_TEXTENCODING_UTF8));
88 return aErrorStr.makeStringAndClear();
91 static bool ImplIsMnemonicCtrl( vcl::Window* pWindow )
93 if( ! pWindow->GetSettings().GetStyleSettings().GetAutoMnemonic() )
94 return false;
96 if ( (pWindow->GetType() == WINDOW_RADIOBUTTON) ||
97 (pWindow->GetType() == WINDOW_CHECKBOX) ||
98 (pWindow->GetType() == WINDOW_TRISTATEBOX) ||
99 (pWindow->GetType() == WINDOW_PUSHBUTTON) )
100 return true;
102 if ( pWindow->GetType() == WINDOW_FIXEDTEXT )
104 FixedText *pText = static_cast<FixedText*>(pWindow);
105 if (pText->get_mnemonic_widget())
106 return true;
107 //This is the legacy pre-layout logic which we retain
108 //until we can be sure we can remove it
109 if (pWindow->GetStyle() & WB_NOLABEL)
110 return false;
111 vcl::Window* pNextWindow = pWindow->GetWindow( GetWindowType::Next );
112 if ( !pNextWindow )
113 return false;
114 pNextWindow = pNextWindow->GetWindow( GetWindowType::Client );
115 if ( !(pNextWindow->GetStyle() & WB_TABSTOP) ||
116 (pNextWindow->GetType() == WINDOW_FIXEDTEXT) ||
117 (pNextWindow->GetType() == WINDOW_GROUPBOX) ||
118 (pNextWindow->GetType() == WINDOW_RADIOBUTTON) ||
119 (pNextWindow->GetType() == WINDOW_CHECKBOX) ||
120 (pNextWindow->GetType() == WINDOW_TRISTATEBOX) ||
121 (pNextWindow->GetType() == WINDOW_PUSHBUTTON) )
122 return false;
124 return true;
127 return false;
130 // Called by native error dialog popup implementations
131 void ImplHideSplash()
133 ImplSVData* pSVData = ImplGetSVData();
134 if( pSVData->mpIntroWindow )
135 pSVData->mpIntroWindow->Hide();
138 //Get next window after pChild of a pTopLevel window as
139 //if any intermediate layout widgets didn't exist
140 vcl::Window * nextLogicalChildOfParent(vcl::Window *pTopLevel, vcl::Window *pChild)
142 vcl::Window *pLastChild = pChild;
144 if (isContainerWindow(*pChild))
145 pChild = pChild->GetWindow(GetWindowType::FirstChild);
146 else
147 pChild = pChild->GetWindow(GetWindowType::Next);
149 while (!pChild)
151 vcl::Window *pParent = pLastChild->GetParent();
152 if (!pParent)
153 return nullptr;
154 if (pParent == pTopLevel)
155 return nullptr;
156 pLastChild = pParent;
157 pChild = pParent->GetWindow(GetWindowType::Next);
160 if (pChild && isContainerWindow(*pChild))
161 pChild = nextLogicalChildOfParent(pTopLevel, pChild);
163 return pChild;
166 vcl::Window * prevLogicalChildOfParent(vcl::Window *pTopLevel, vcl::Window *pChild)
168 vcl::Window *pLastChild = pChild;
170 if (isContainerWindow(*pChild))
171 pChild = pChild->GetWindow(GetWindowType::LastChild);
172 else
173 pChild = pChild->GetWindow(GetWindowType::Prev);
175 while (!pChild)
177 vcl::Window *pParent = pLastChild->GetParent();
178 if (!pParent)
179 return nullptr;
180 if (pParent == pTopLevel)
181 return nullptr;
182 pLastChild = pParent;
183 pChild = pParent->GetWindow(GetWindowType::Prev);
186 if (pChild && isContainerWindow(*pChild))
187 pChild = prevLogicalChildOfParent(pTopLevel, pChild);
189 return pChild;
192 //Get first window of a pTopLevel window as
193 //if any intermediate layout widgets didn't exist
194 vcl::Window * firstLogicalChildOfParent(vcl::Window *pTopLevel)
196 vcl::Window *pChild = pTopLevel->GetWindow(GetWindowType::FirstChild);
197 if (pChild && isContainerWindow(*pChild))
198 pChild = nextLogicalChildOfParent(pTopLevel, pChild);
199 return pChild;
202 void Accelerator::GenerateAutoMnemonicsOnHierarchy(vcl::Window* pWindow)
204 MnemonicGenerator aMnemonicGenerator;
205 vcl::Window* pGetChild;
206 vcl::Window* pChild;
208 // register the assigned mnemonics
209 pGetChild = pWindow->GetWindow( GetWindowType::FirstChild );
210 while ( pGetChild )
212 pChild = pGetChild->ImplGetWindow();
213 aMnemonicGenerator.RegisterMnemonic( pChild->GetText() );
214 pGetChild = nextLogicalChildOfParent(pWindow, pGetChild);
217 // take the Controls of the dialog into account for TabPages
218 if ( pWindow->GetType() == WINDOW_TABPAGE )
220 vcl::Window* pParent = pWindow->GetParent();
221 if ( pParent->GetType() == WINDOW_TABCONTROL )
222 pParent = pParent->GetParent();
224 if ( (pParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL )
226 pGetChild = pParent->GetWindow( GetWindowType::FirstChild );
227 while ( pGetChild )
229 pChild = pGetChild->ImplGetWindow();
230 aMnemonicGenerator.RegisterMnemonic( pChild->GetText() );
231 pGetChild = nextLogicalChildOfParent(pWindow, pGetChild);
236 // assign mnemonics to Controls which have none
237 pGetChild = pWindow->GetWindow( GetWindowType::FirstChild );
238 while ( pGetChild )
240 pChild = pGetChild->ImplGetWindow();
241 if ( ImplIsMnemonicCtrl( pChild ) )
243 OUString aText = pChild->GetText();
244 OUString aNewText = aMnemonicGenerator.CreateMnemonic( aText );
245 if ( aText != aNewText )
246 pChild->SetText( aNewText );
249 pGetChild = nextLogicalChildOfParent(pWindow, pGetChild);
253 static VclButtonBox* getActionArea(Dialog *pDialog)
255 VclButtonBox *pButtonBox = nullptr;
256 if (pDialog->isLayoutEnabled())
258 vcl::Window *pBox = pDialog->GetWindow(GetWindowType::FirstChild);
259 vcl::Window *pChild = pBox->GetWindow(GetWindowType::LastChild);
260 while (pChild)
262 pButtonBox = dynamic_cast<VclButtonBox*>(pChild);
263 if (pButtonBox)
264 break;
265 pChild = pChild->GetWindow(GetWindowType::Prev);
268 return pButtonBox;
271 static vcl::Window* getActionAreaButtonList(Dialog *pDialog)
273 VclButtonBox* pButtonBox = getActionArea(pDialog);
274 if (pButtonBox)
275 return pButtonBox->GetWindow(GetWindowType::FirstChild);
276 return pDialog->GetWindow(GetWindowType::FirstChild);
279 static PushButton* ImplGetDefaultButton( Dialog* pDialog )
281 vcl::Window* pChild = getActionAreaButtonList(pDialog);
282 while ( pChild )
284 if ( pChild->ImplIsPushButton() )
286 PushButton* pPushButton = static_cast<PushButton*>(pChild);
287 if ( pPushButton->ImplIsDefButton() )
288 return pPushButton;
291 pChild = pChild->GetWindow( GetWindowType::Next );
294 return nullptr;
297 static PushButton* ImplGetOKButton( Dialog* pDialog )
299 vcl::Window* pChild = getActionAreaButtonList(pDialog);
300 while ( pChild )
302 if ( pChild->GetType() == WINDOW_OKBUTTON )
303 return static_cast<PushButton*>(pChild);
305 pChild = pChild->GetWindow( GetWindowType::Next );
308 return nullptr;
311 static PushButton* ImplGetCancelButton( Dialog* pDialog )
313 vcl::Window* pChild = getActionAreaButtonList(pDialog);
315 while ( pChild )
317 if ( pChild->GetType() == WINDOW_CANCELBUTTON )
318 return static_cast<PushButton*>(pChild);
320 pChild = pChild->GetWindow( GetWindowType::Next );
323 return nullptr;
326 static void ImplMouseAutoPos( Dialog* pDialog )
328 MouseSettingsOptions nMouseOptions = pDialog->GetSettings().GetMouseSettings().GetOptions();
329 if ( nMouseOptions & MouseSettingsOptions::AutoCenterPos )
331 Size aSize = pDialog->GetOutputSizePixel();
332 pDialog->SetPointerPosPixel( Point( aSize.Width()/2, aSize.Height()/2 ) );
334 else if ( nMouseOptions & MouseSettingsOptions::AutoDefBtnPos )
336 vcl::Window* pWindow = ImplGetDefaultButton( pDialog );
337 if ( !pWindow )
338 pWindow = ImplGetOKButton( pDialog );
339 if ( !pWindow )
340 pWindow = ImplGetCancelButton( pDialog );
341 if ( !pWindow )
342 pWindow = pDialog;
343 Size aSize = pWindow->GetOutputSizePixel();
344 pWindow->SetPointerPosPixel( Point( aSize.Width()/2, aSize.Height()/2 ) );
348 struct DialogImpl
350 long mnResult;
351 bool mbStartedModal;
352 VclAbstractDialog::AsyncContext maEndCtx;
354 DialogImpl() : mnResult( -1 ), mbStartedModal( false ) {}
357 void Dialog::ImplInitDialogData()
359 mpWindowImpl->mbDialog = true;
360 mpPrevExecuteDlg = nullptr;
361 mpNextExecuteDlg = nullptr;
362 mbInExecute = false;
363 mbInClose = false;
364 mbModalMode = false;
365 mbPaintComplete = false;
366 mpContentArea.clear();
367 mpActionArea.clear();
368 mnMousePositioned = 0;
369 mpDialogImpl.reset(new DialogImpl);
372 void Dialog::ImplInit( vcl::Window* pParent, WinBits nStyle, InitFlag eFlag )
374 SystemWindowFlags nSysWinMode = Application::GetSystemWindowMode();
376 if ( !(nStyle & WB_NODIALOGCONTROL) )
377 nStyle |= WB_DIALOGCONTROL;
378 nStyle |= WB_ROLLABLE;
380 // Now, all Dialogs are per default system windows !!!
381 nStyle |= WB_SYSTEMWINDOW;
383 if (eFlag == InitFlag::NoParent)
384 pParent = nullptr;
385 else if (!pParent) // parent is NULL: get the default Dialog parent
387 pParent = Application::GetDefDialogParent();
388 if ( !pParent && !(nStyle & WB_SYSTEMWINDOW) )
389 pParent = ImplGetSVData()->maWinData.mpAppWin;
391 // If Parent is disabled, then we search for a modal dialog
392 // in this frame
393 if ( pParent && (!pParent->IsInputEnabled() || pParent->IsInModalMode()) )
395 ImplSVData* pSVData = ImplGetSVData();
396 Dialog* pExeDlg = pSVData->maWinData.mpLastExecuteDlg;
397 while ( pExeDlg )
399 // only if visible and enabled
400 if ( pParent->ImplGetFirstOverlapWindow()->IsWindowOrChild( pExeDlg, true ) &&
401 pExeDlg->IsReallyVisible() &&
402 pExeDlg->IsEnabled() && pExeDlg->IsInputEnabled() && !pExeDlg->IsInModalMode() )
404 pParent = pExeDlg;
405 break;
408 pExeDlg = pExeDlg->mpPrevExecuteDlg;
413 if ( !pParent || (nStyle & WB_SYSTEMWINDOW) ||
414 (pParent->mpWindowImpl->mpFrameData->mbNeedSysWindow && !(nSysWinMode & SystemWindowFlags::NOAUTOMODE)) ||
415 (nSysWinMode & SystemWindowFlags::DIALOG) )
417 // create window with a small border ?
418 if ( (nStyle & (WB_BORDER | WB_NOBORDER | WB_MOVEABLE | WB_SIZEABLE | WB_CLOSEABLE)) == WB_BORDER )
420 VclPtrInstance<ImplBorderWindow> pBorderWin( pParent, nStyle, BorderWindowStyle::Frame );
421 SystemWindow::ImplInit( pBorderWin, nStyle & ~WB_BORDER, nullptr );
422 pBorderWin->mpWindowImpl->mpClientWindow = this;
423 pBorderWin->GetBorder( mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder, mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder );
424 mpWindowImpl->mpBorderWindow = pBorderWin;
425 mpWindowImpl->mpRealParent = pParent;
427 else
429 mpWindowImpl->mbFrame = true;
430 mpWindowImpl->mbOverlapWin = true;
431 SystemWindow::ImplInit( pParent, (nStyle & (WB_MOVEABLE | WB_SIZEABLE | WB_ROLLABLE | WB_CLOSEABLE | WB_STANDALONE)) | WB_CLOSEABLE, nullptr );
432 // Now set all style bits
433 mpWindowImpl->mnStyle = nStyle;
436 else
438 VclPtrInstance<ImplBorderWindow> pBorderWin( pParent, nStyle, BorderWindowStyle::Overlap | BorderWindowStyle::Border );
439 SystemWindow::ImplInit( pBorderWin, nStyle & ~WB_BORDER, nullptr );
440 pBorderWin->mpWindowImpl->mpClientWindow = this;
441 pBorderWin->GetBorder( mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder, mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder );
442 mpWindowImpl->mpBorderWindow = pBorderWin;
443 mpWindowImpl->mpRealParent = pParent;
446 SetActivateMode( ActivateModeFlags::GrabFocus );
448 ImplInitSettings();
451 void Dialog::ApplySettings(vcl::RenderContext& rRenderContext)
453 if (IsControlBackground())
455 // user override
456 SetBackground(GetControlBackground());
458 else if (rRenderContext.IsNativeControlSupported(ControlType::WindowBackground, ControlPart::BackgroundDialog))
460 // NWF background
461 mpWindowImpl->mnNativeBackground = ControlPart::BackgroundDialog;
462 EnableChildTransparentMode();
464 else
466 // fallback to settings color
467 rRenderContext.SetBackground(GetSettings().GetStyleSettings().GetDialogColor());
471 void Dialog::ImplInitSettings()
473 // user override
474 if (IsControlBackground())
475 SetBackground(GetControlBackground());
476 // NWF background
477 else if( IsNativeControlSupported(ControlType::WindowBackground, ControlPart::BackgroundDialog))
479 mpWindowImpl->mnNativeBackground = ControlPart::BackgroundDialog;
480 EnableChildTransparentMode();
482 // fallback to settings color
483 else
484 SetBackground(GetSettings().GetStyleSettings().GetDialogColor());
487 void Dialog::ImplLOKNotifier(vcl::Window* pParent)
489 if (comphelper::LibreOfficeKit::isActive() && pParent)
491 if (VclPtr<vcl::Window> pWin = pParent->GetParentWithLOKNotifier())
493 SetLOKNotifier(pWin->GetLOKNotifier());
498 Dialog::Dialog( WindowType nType )
499 : SystemWindow( nType )
500 , mnInitFlag(InitFlag::Default)
502 ImplInitDialogData();
505 void VclBuilderContainer::disposeBuilder()
507 if (m_pUIBuilder)
508 m_pUIBuilder->disposeBuilder();
511 OUString VclBuilderContainer::getUIRootDir()
513 /*to-do, check if user config has an override before using shared one, etc*/
514 css::uno::Reference< css::util::XPathSettings > xPathSettings = css::util::thePathSettings::get(
515 ::comphelper::getProcessComponentContext() );
517 OUString sShareLayer = xPathSettings->getBasePathShareLayer();
519 // "UIConfig" is a "multi path" ... use first part only here!
520 sal_Int32 nPos = sShareLayer.indexOf(';');
521 if (nPos > 0)
522 sShareLayer = sShareLayer.copy(0, nPos);
524 // Note: May be an user uses URLs without a final slash! Check it ...
525 if (!sShareLayer.endsWith("/"))
526 sShareLayer += "/";
528 sShareLayer += "soffice.cfg/";
529 /*to-do, can we merge all this foo with existing soffice.cfg finding code, etc*/
530 return sShareLayer;
533 //we can't change sizeable after the fact, so need to defer until we know and then
534 //do the init. Find the real parent stashed in mpDialogParent.
535 void Dialog::doDeferredInit(WinBits nBits)
537 VclPtr<vcl::Window> pParent = mpDialogParent;
538 mpDialogParent = nullptr;
539 ImplInit(pParent, nBits, mnInitFlag);
540 mbIsDefferedInit = false;
543 Dialog::Dialog(vcl::Window* pParent, const OUString& rID, const OUString& rUIXMLDescription)
544 : SystemWindow(WINDOW_DIALOG)
545 , mnInitFlag(InitFlag::Default)
547 ImplLOKNotifier(pParent);
548 ImplInitDialogData();
549 loadUI(pParent, OUStringToOString(rID, RTL_TEXTENCODING_UTF8), rUIXMLDescription);
552 Dialog::Dialog(vcl::Window* pParent, const OUString& rID, const OUString& rUIXMLDescription, WindowType nType, InitFlag eFlag)
553 : SystemWindow(nType)
554 , mnInitFlag(eFlag)
556 ImplLOKNotifier(pParent);
557 ImplInitDialogData();
558 loadUI(pParent, OUStringToOString(rID, RTL_TEXTENCODING_UTF8), rUIXMLDescription);
561 Dialog::Dialog(vcl::Window* pParent, WinBits nStyle, InitFlag eFlag)
562 : SystemWindow(WINDOW_DIALOG)
563 , mnInitFlag(eFlag)
565 ImplLOKNotifier(pParent);
566 ImplInitDialogData();
567 ImplInit( pParent, nStyle, eFlag );
570 void Dialog::set_action_area(VclButtonBox* pBox)
572 mpActionArea.set(pBox);
575 void Dialog::set_content_area(VclBox* pBox)
577 mpContentArea.set(pBox);
580 void Dialog::settingOptimalLayoutSize(Window *pBox)
582 const DialogStyle& rDialogStyle =
583 GetSettings().GetStyleSettings().GetDialogStyle();
584 VclBox * pBox2 = static_cast<VclBox*>(pBox);
585 pBox2->set_border_width(rDialogStyle.content_area_border);
586 pBox2->set_spacing(pBox2->get_spacing() +
587 rDialogStyle.content_area_spacing);
589 VclButtonBox *pActionArea = getActionArea(this);
590 if (pActionArea)
592 pActionArea->set_border_width(rDialogStyle.action_area_border);
593 pActionArea->set_spacing(rDialogStyle.button_spacing);
597 Dialog::~Dialog()
599 disposeOnce();
602 void Dialog::dispose()
604 mpDialogImpl.reset();
605 RemoveFromDlgList();
606 mpPrevExecuteDlg.clear();
607 mpNextExecuteDlg.clear();
608 mpActionArea.clear();
609 mpContentArea.clear();
611 css::uno::Reference< css::uno::XComponentContext > xContext(
612 comphelper::getProcessComponentContext() );
613 css::uno::Reference<css::frame::XGlobalEventBroadcaster> xEventBroadcaster(css::frame::theGlobalEventBroadcaster::get(xContext), css::uno::UNO_QUERY_THROW);
614 css::document::DocumentEvent aObject;
615 aObject.EventName = "DialogClosed";
616 xEventBroadcaster->documentEventOccured(aObject);
618 if (comphelper::LibreOfficeKit::isActive())
620 if(const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
622 pNotifier->notifyWindow(GetLOKWindowId(), "close");
623 ReleaseLOKNotifier();
627 SystemWindow::dispose();
630 IMPL_LINK_NOARG(Dialog, ImplAsyncCloseHdl, void*, void)
632 Close();
635 bool Dialog::EventNotify( NotifyEvent& rNEvt )
637 // first call the base class due to Tab control
638 bool bRet = SystemWindow::EventNotify( rNEvt );
639 if ( !bRet )
641 if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
643 const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
644 vcl::KeyCode aKeyCode = pKEvt->GetKeyCode();
645 sal_uInt16 nKeyCode = aKeyCode.GetCode();
647 if ( (nKeyCode == KEY_ESCAPE) &&
648 ((GetStyle() & WB_CLOSEABLE) || ImplGetCancelButton( this ) || ImplGetOKButton( this )) )
650 // #i89505# for the benefit of slightly mentally challenged implementations
651 // like e.g. SfxModelessDialog which destroy themselves inside Close()
652 // post this Close asynchronous so we can leave our key handler before
653 // we get destroyed
654 PostUserEvent( LINK( this, Dialog, ImplAsyncCloseHdl ), nullptr, true);
655 return true;
658 else if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
660 // make sure the dialog is still modal
661 // changing focus between application frames may
662 // have re-enabled input for our parent
663 if( mbInExecute && mbModalMode )
665 SetModalInputMode( false );
666 SetModalInputMode( true );
668 // #93022# def-button might have changed after show
669 if( !mnMousePositioned )
671 mnMousePositioned = 1;
672 ImplMouseAutoPos( this );
679 return bRet;
682 //What we really want here is something that gives the available width and
683 //height of a users screen, taking away the space taken up the OS
684 //taskbar, menus, etc.
685 Size bestmaxFrameSizeForScreenSize(const Size &rScreenSize)
687 long w = rScreenSize.Width();
688 if (w <= 800)
689 w -= 15;
690 else if (w <= 1024)
691 w -= 65;
692 else
693 w -= 115;
695 long h = rScreenSize.Height();
696 if (h <= 768)
697 h -= 50;
698 else
699 h -= 100;
701 return Size(std::max<long>(w, 640 - 15),
702 std::max<long>(h, 480 - 50));
705 void Dialog::StateChanged( StateChangedType nType )
707 if (nType == StateChangedType::InitShow)
709 DoInitialLayout();
711 if ( !HasChildPathFocus() || HasFocus() )
712 GrabFocusToFirstControl();
713 if ( !(GetStyle() & WB_CLOSEABLE) )
715 if ( ImplGetCancelButton( this ) || ImplGetOKButton( this ) )
717 if ( ImplGetBorderWindow() )
718 static_cast<ImplBorderWindow*>(ImplGetBorderWindow())->SetCloseButton();
722 ImplMouseAutoPos( this );
724 else if (nType == StateChangedType::Text)
726 if (const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
728 std::vector<vcl::LOKPayloadItem> aPayload;
729 aPayload.emplace_back("title", GetText().toUtf8());
730 pNotifier->notifyWindow(GetLOKWindowId(), "title_changed", aPayload);
734 SystemWindow::StateChanged( nType );
736 if (nType == StateChangedType::ControlBackground)
738 ImplInitSettings();
739 Invalidate();
742 if (!mbModalMode && nType == StateChangedType::Visible)
744 css::uno::Reference< css::uno::XComponentContext > xContext(
745 comphelper::getProcessComponentContext() );
746 css::uno::Reference<css::frame::XGlobalEventBroadcaster> xEventBroadcaster(css::frame::theGlobalEventBroadcaster::get(xContext), css::uno::UNO_QUERY_THROW);
747 css::document::DocumentEvent aObject;
748 aObject.EventName = "ModelessDialogVisible";
749 xEventBroadcaster->documentEventOccured(aObject);
753 void Dialog::DataChanged( const DataChangedEvent& rDCEvt )
755 SystemWindow::DataChanged( rDCEvt );
757 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
758 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
760 ImplInitSettings();
761 Invalidate();
765 bool Dialog::Close()
767 VclPtr<vcl::Window> xWindow = this;
768 CallEventListeners( VCLEVENT_WINDOW_CLOSE );
769 if ( xWindow->IsDisposed() )
770 return false;
772 if ( mpWindowImpl->mxWindowPeer.is() && IsCreatedWithToolkit() && !IsInExecute() )
773 return false;
775 mbInClose = true;
777 if ( !(GetStyle() & WB_CLOSEABLE) )
779 bool bRet = true;
780 PushButton* pButton = ImplGetCancelButton( this );
781 if ( pButton )
782 pButton->Click();
783 else
785 pButton = ImplGetOKButton( this );
786 if ( pButton )
787 pButton->Click();
788 else
789 bRet = false;
791 if ( xWindow->IsDisposed() )
792 return true;
793 return bRet;
796 if ( IsInExecute() )
798 EndDialog();
799 mbInClose = false;
800 return true;
802 else
804 mbInClose = false;
805 return SystemWindow::Close();
809 bool Dialog::ImplStartExecuteModal()
811 setDeferredProperties();
813 if ( mbInExecute || mpDialogImpl->maEndCtx.isSet() )
815 #ifdef DBG_UTIL
816 OStringBuffer aErrorStr;
817 aErrorStr.append("Dialog::StartExecuteModal() is called in Dialog::StartExecuteModal(): ");
818 aErrorStr.append(ImplGetDialogText(this));
819 OSL_FAIL(aErrorStr.getStr());
820 #endif
821 return false;
824 ImplSVData* pSVData = ImplGetSVData();
826 switch ( Application::GetDialogCancelMode() )
828 case Application::DialogCancelMode::Off:
829 break;
830 case Application::DialogCancelMode::Silent:
831 if (GetLOKNotifier())
833 // check if there's already some dialog being ::Execute()d
834 Dialog* pExeDlg = pSVData->maWinData.mpLastExecuteDlg;
835 while (pExeDlg && !pExeDlg->IsInSyncExecute())
836 pExeDlg = pExeDlg->mpPrevExecuteDlg;
838 const bool bDialogExecuting = pExeDlg ? pExeDlg->IsInSyncExecute() : false;
839 if (!(bDialogExecuting && IsInSyncExecute()))
840 break;
841 else
842 SAL_WARN("lok.dialog", "Dialog \"" << ImplGetDialogText(this) << "\" is being synchronously executed over an existing synchronously executing dialog.");
845 SAL_INFO(
846 "vcl",
847 "Dialog \"" << ImplGetDialogText(this).getStr()
848 << "\"cancelled in silent mode");
849 return false;
850 default: // default cannot happen
851 case Application::DialogCancelMode::Fatal:
852 std::abort();
855 #ifdef DBG_UTIL
856 vcl::Window* pParent = GetParent();
857 if ( pParent )
859 pParent = pParent->ImplGetFirstOverlapWindow();
860 SAL_WARN_IF( !pParent->IsReallyVisible(), "vcl",
861 "Dialog::StartExecuteModal() - Parent not visible" );
862 SAL_WARN_IF( !pParent->IsInputEnabled(), "vcl",
863 "Dialog::StartExecuteModal() - Parent input disabled, use another parent to ensure modality!" );
864 SAL_WARN_IF( pParent->IsInModalMode(), "vcl",
865 "Dialog::StartExecuteModal() - Parent already modally disabled, use another parent to ensure modality!" );
868 #endif
870 // link all dialogs which are being executed
871 mpPrevExecuteDlg = pSVData->maWinData.mpLastExecuteDlg;
872 if (mpPrevExecuteDlg)
873 mpPrevExecuteDlg->mpNextExecuteDlg = this;
874 pSVData->maWinData.mpLastExecuteDlg = this;
876 // stop capturing, in order to have control over the dialog
877 if ( pSVData->maWinData.mpTrackWin )
878 pSVData->maWinData.mpTrackWin->EndTracking( TrackingEventFlags::Cancel );
879 if ( pSVData->maWinData.mpCaptureWin )
880 pSVData->maWinData.mpCaptureWin->ReleaseMouse();
881 EnableInput();
883 if ( GetParent() )
885 NotifyEvent aNEvt( MouseNotifyEvent::EXECUTEDIALOG, this );
886 GetParent()->CompatNotify( aNEvt );
888 mbInExecute = true;
889 // no real modality in LibreOfficeKit
890 if (!comphelper::LibreOfficeKit::isActive())
891 SetModalInputMode(true);
893 // FIXME: no layouting, workaround some clipping issues
894 ImplAdjustNWFSizes();
896 css::uno::Reference< css::uno::XComponentContext > xContext(
897 comphelper::getProcessComponentContext());
898 bool bForceFocusAndToFront(officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get(xContext));
899 ShowFlags showFlags = bForceFocusAndToFront ? ShowFlags::ForegroundTask : ShowFlags::NONE;
900 Show(true, showFlags);
902 pSVData->maAppData.mnModalMode++;
904 css::uno::Reference<css::frame::XGlobalEventBroadcaster> xEventBroadcaster(css::frame::theGlobalEventBroadcaster::get(xContext), css::uno::UNO_QUERY_THROW);
905 css::document::DocumentEvent aObject;
906 aObject.EventName = "DialogExecute";
907 xEventBroadcaster->documentEventOccured(aObject);
909 if (comphelper::LibreOfficeKit::isActive())
911 if(const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
913 std::vector<vcl::LOKPayloadItem> aItems;
914 aItems.emplace_back("type", "dialog");
915 aItems.emplace_back("size", GetSizePixel().toString());
916 if (!GetText().isEmpty())
917 aItems.emplace_back("title", GetText().toUtf8());
918 pNotifier->notifyWindow(GetLOKWindowId(), "created", aItems);
922 return true;
925 void Dialog::ImplEndExecuteModal()
927 ImplSVData* pSVData = ImplGetSVData();
928 pSVData->maAppData.mnModalMode--;
931 void Dialog::PrePaint(vcl::RenderContext& rRenderContext)
933 SystemWindow::PrePaint(rRenderContext);
934 mbPaintComplete = false;
937 void Dialog::PostPaint(vcl::RenderContext& rRenderContext)
939 SystemWindow::PostPaint(rRenderContext);
940 mbPaintComplete = true;
943 std::vector<OString> Dialog::getAllPageUIXMLDescriptions() const
945 // default has no pages
946 return std::vector<OString>();
949 bool Dialog::selectPageByUIXMLDescription(const OString& /*rUIXMLDescription*/)
951 // default cannot select anything (which is okay, return true)
952 return true;
955 void Dialog::ensureRepaint()
957 // ensure repaint
958 Invalidate();
959 mbPaintComplete = false;
961 while (!mbPaintComplete)
963 Application::Yield();
967 Bitmap Dialog::createScreenshot()
969 // same prerequisites as in Execute()
970 setDeferredProperties();
971 ImplAdjustNWFSizes();
972 Show();
973 ToTop();
974 ensureRepaint();
976 return GetBitmap(Point(), GetOutputSizePixel());
979 short Dialog::Execute()
981 #if HAVE_FEATURE_DESKTOP
982 VclPtr<vcl::Window> xWindow = this;
984 mbInSyncExecute = true;
985 comphelper::ScopeGuard aGuard([&]() {
986 mbInSyncExecute = false;
988 if ( !ImplStartExecuteModal() )
989 return 0;
991 // Yield util EndDialog is called or dialog gets destroyed
992 // (the latter should not happen, but better safe than sorry
993 while ( !xWindow->IsDisposed() && mbInExecute )
994 Application::Yield();
996 mbInSyncExecute = false;
997 ImplEndExecuteModal();
998 #ifdef DBG_UTIL
999 assert (!mpDialogParent || !mpDialogParent->IsDisposed());
1000 #endif
1001 if ( !xWindow->IsDisposed() )
1002 xWindow.clear();
1003 #ifdef DBG_UTIL
1004 else
1006 OSL_FAIL( "Dialog::Execute() - Dialog destroyed in Execute()" );
1008 #endif
1010 long nRet = mpDialogImpl->mnResult;
1011 mpDialogImpl->mnResult = -1;
1012 return (short)nRet;
1014 #else
1016 // touch_ui_dialog_modal was dummied out both for Android and iOS (well, TiledLibreOffice anyway)
1017 // For Android it returned MLODialogOK always, for iOS Cancel. Let's go with OK.
1018 // MLODialogResult result = touch_ui_dialog_modal(kind, ImplGetDialogText(this).getStr());
1019 return RET_OK;
1021 #endif
1024 // virtual
1025 void Dialog::StartExecuteModal( const Link<Dialog&,void>& rEndDialogHdl )
1027 VclAbstractDialog::AsyncContext aCtx;
1028 VclPtr<Dialog> ref(this);
1029 aCtx.maEndDialogFn = [ref,rEndDialogHdl](sal_Int32){ rEndDialogHdl.Call(*ref.get()); };
1030 StartExecuteAsync(aCtx);
1033 // virtual
1034 bool Dialog::StartExecuteAsync( VclAbstractDialog::AsyncContext &rCtx )
1036 if ( !ImplStartExecuteModal() )
1038 rCtx.mxOwner.disposeAndClear();
1039 return false;
1042 mpDialogImpl->maEndCtx = rCtx;
1043 mpDialogImpl->mbStartedModal = true;
1045 return true;
1048 void Dialog::RemoveFromDlgList()
1050 // remove dialog from the list of dialogs which are being executed
1051 ImplSVData* pSVData = ImplGetSVData();
1052 if (pSVData->maWinData.mpLastExecuteDlg == this)
1054 if (mpPrevExecuteDlg)
1055 pSVData->maWinData.mpLastExecuteDlg = mpPrevExecuteDlg;
1056 else
1057 pSVData->maWinData.mpLastExecuteDlg = mpNextExecuteDlg;
1059 if (mpPrevExecuteDlg)
1060 mpPrevExecuteDlg->mpNextExecuteDlg = mpNextExecuteDlg;
1061 if (mpNextExecuteDlg)
1062 mpNextExecuteDlg->mpPrevExecuteDlg = mpPrevExecuteDlg;
1063 mpPrevExecuteDlg.clear();
1064 mpNextExecuteDlg.clear();
1067 void Dialog::EndDialog( long nResult )
1069 if ( mbInExecute )
1071 SetModalInputMode( false );
1073 RemoveFromDlgList();
1075 // set focus to previous modal dialogue if it is modal for
1076 // the same frame parent (or NULL)
1077 if( mpPrevExecuteDlg )
1079 vcl::Window* pFrameParent = ImplGetFrameWindow()->ImplGetParent();
1080 vcl::Window* pPrevFrameParent = mpPrevExecuteDlg->ImplGetFrameWindow()? mpPrevExecuteDlg->ImplGetFrameWindow()->ImplGetParent(): nullptr;
1081 if( ( !pFrameParent && !pPrevFrameParent ) ||
1082 ( pFrameParent && pPrevFrameParent && pFrameParent->ImplGetFrame() == pPrevFrameParent->ImplGetFrame() )
1085 mpPrevExecuteDlg->GrabFocus();
1088 mpPrevExecuteDlg = nullptr;
1089 mpNextExecuteDlg = nullptr;
1091 Hide();
1092 if ( GetParent() )
1094 NotifyEvent aNEvt( MouseNotifyEvent::ENDEXECUTEDIALOG, this );
1095 GetParent()->CompatNotify( aNEvt );
1098 mpDialogImpl->mnResult = nResult;
1100 if ( mpDialogImpl->mbStartedModal )
1102 ImplEndExecuteModal();
1103 if (mpDialogImpl->maEndCtx.isSet())
1105 mpDialogImpl->maEndCtx.maEndDialogFn(nResult);
1106 mpDialogImpl->maEndCtx.maEndDialogFn = nullptr;
1108 mpDialogImpl->mbStartedModal = false;
1109 mpDialogImpl->mnResult = -1;
1111 mbInExecute = false;
1114 // Destroy ourselves (if we have a context with VclPtr owner)
1115 mpDialogImpl->maEndCtx.mxOwner.disposeAndClear();
1118 long Dialog::GetResult() const
1120 return mpDialogImpl->mnResult;
1123 void Dialog::EndAllDialogs( vcl::Window* pParent )
1125 ImplSVData* pSVData = ImplGetSVData();
1126 Dialog* pTempModDialog;
1127 Dialog* pModDialog = pSVData->maWinData.mpLastExecuteDlg;
1128 while (pModDialog)
1130 pTempModDialog = pModDialog->mpPrevExecuteDlg;
1131 if(!pParent || pParent->IsWindowOrChild(pModDialog,true))
1133 pModDialog->EndDialog();
1134 pModDialog->PostUserEvent( Link<void*,void>() );
1136 pModDialog = pTempModDialog;
1140 void Dialog::SetModalInputMode( bool bModal )
1142 if ( bModal == mbModalMode )
1143 return;
1145 mbModalMode = bModal;
1146 if ( bModal )
1148 // Disable the prev Modal Dialog, because our dialog must close at first,
1149 // before the other dialog can be closed (because the other dialog
1150 // is on stack since our dialog returns)
1151 if ( mpPrevExecuteDlg && !mpPrevExecuteDlg->IsWindowOrChild( this, true ) )
1152 mpPrevExecuteDlg->EnableInput( false, this );
1154 // determine next overlap dialog parent
1155 vcl::Window* pParent = GetParent();
1156 if ( pParent )
1158 // #103716# dialogs should always be modal to the whole frame window
1159 // #115933# disable the whole frame hierarchy, useful if our parent
1160 // is a modeless dialog
1161 mpDialogParent = pParent->mpWindowImpl->mpFrameWindow;
1162 mpDialogParent->ImplIncModalCount();
1165 else
1167 if ( mpDialogParent )
1169 // #115933# re-enable the whole frame hierarchy again (see above)
1170 // note that code in getfocus assures that we do not accidentally enable
1171 // windows that were disabled before
1172 mpDialogParent->ImplDecModalCount();
1175 // Enable the prev Modal Dialog
1176 if ( mpPrevExecuteDlg && !mpPrevExecuteDlg->IsWindowOrChild( this, true ) )
1178 mpPrevExecuteDlg->EnableInput( true, this );
1179 // ensure continued modality of prev dialog
1180 // do not change modality counter
1182 // #i119994# need find the last modal dialog before reactive it
1183 Dialog * pPrevModalDlg = mpPrevExecuteDlg;
1185 while( pPrevModalDlg && !pPrevModalDlg->IsModalInputMode() )
1186 pPrevModalDlg = pPrevModalDlg->mpPrevExecuteDlg;
1188 if( pPrevModalDlg &&
1189 ( pPrevModalDlg == mpPrevExecuteDlg.get()
1190 || !pPrevModalDlg->IsWindowOrChild( this, true ) ) )
1192 mpPrevExecuteDlg->SetModalInputMode( false );
1193 mpPrevExecuteDlg->SetModalInputMode( true );
1197 ImplGetFrame()->SetModal(bModal);
1200 void Dialog::SetModalInputMode( bool bModal, bool bSubModalDialogs )
1202 if ( bSubModalDialogs )
1204 vcl::Window* pOverlap = ImplGetFirstOverlapWindow();
1205 pOverlap = pOverlap->mpWindowImpl->mpFirstOverlap;
1206 while ( pOverlap )
1208 if ( pOverlap->IsDialog() )
1209 static_cast<Dialog*>(pOverlap)->SetModalInputMode( bModal, true );
1210 pOverlap = pOverlap->mpWindowImpl->mpNext;
1214 SetModalInputMode( bModal );
1217 void Dialog::GrabFocusToFirstControl()
1219 vcl::Window* pFocusControl;
1221 // find focus control, even if the dialog has focus
1222 if ( HasFocus() )
1223 pFocusControl = nullptr;
1224 else
1226 // prefer a child window which had focus before
1227 pFocusControl = ImplGetFirstOverlapWindow()->mpWindowImpl->mpLastFocusWindow;
1228 // find the control out of the dialog control
1229 if ( pFocusControl )
1230 pFocusControl = ImplFindDlgCtrlWindow( pFocusControl );
1232 // no control had the focus before or the control is not
1233 // part of the tab-control, now give focus to the
1234 // first control in the tab-control
1235 if ( !pFocusControl ||
1236 !(pFocusControl->GetStyle() & WB_TABSTOP) ||
1237 !isVisibleInLayout(pFocusControl) ||
1238 !isEnabledInLayout(pFocusControl) || !pFocusControl->IsInputEnabled() )
1240 sal_uInt16 n = 0;
1241 pFocusControl = ImplGetDlgWindow( n, GetDlgWindowType::First );
1243 if ( pFocusControl )
1244 pFocusControl->ImplControlFocus( GetFocusFlags::Init );
1247 void Dialog::GetDrawWindowBorder( sal_Int32& rLeftBorder, sal_Int32& rTopBorder, sal_Int32& rRightBorder, sal_Int32& rBottomBorder ) const
1249 ScopedVclPtrInstance<ImplBorderWindow> aImplWin( static_cast<vcl::Window*>(const_cast<Dialog *>(this)), WB_BORDER|WB_STDWORK, BorderWindowStyle::Overlap );
1250 aImplWin->GetBorder( rLeftBorder, rTopBorder, rRightBorder, rBottomBorder );
1253 void Dialog::Draw( OutputDevice* pDev, const Point& rPos, const Size& rSize, DrawFlags )
1255 Point aPos = pDev->LogicToPixel( rPos );
1256 Size aSize = pDev->LogicToPixel( rSize );
1258 Wallpaper aWallpaper = GetBackground();
1259 if ( !aWallpaper.IsBitmap() )
1260 ImplInitSettings();
1262 pDev->Push();
1263 pDev->SetMapMode();
1264 pDev->SetLineColor();
1266 if ( aWallpaper.IsBitmap() )
1267 pDev->DrawBitmapEx( aPos, aSize, aWallpaper.GetBitmap() );
1268 else
1270 pDev->SetFillColor( aWallpaper.GetColor() );
1271 pDev->DrawRect( Rectangle( aPos, aSize ) );
1274 if (!( GetStyle() & WB_NOBORDER ))
1276 ScopedVclPtrInstance< ImplBorderWindow > aImplWin( this, WB_BORDER|WB_STDWORK, BorderWindowStyle::Overlap );
1277 aImplWin->SetText( GetText() );
1278 aImplWin->setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
1279 aImplWin->SetDisplayActive( true );
1280 aImplWin->InitView();
1282 aImplWin->Draw( Rectangle( aPos, aSize ), pDev, aPos );
1285 pDev->Pop();
1288 void Dialog::queue_resize(StateChangedType eReason)
1290 if (IsInClose())
1291 return;
1292 SystemWindow::queue_resize(eReason);
1295 void Dialog::Resize()
1297 SystemWindow::Resize();
1299 if (comphelper::LibreOfficeKit::isDialogPainting())
1300 return;
1302 if (const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
1304 std::vector<vcl::LOKPayloadItem> aItems;
1305 aItems.emplace_back("size", GetSizePixel().toString());
1306 pNotifier->notifyWindow(GetLOKWindowId(), "size_changed", aItems);
1310 bool Dialog::set_property(const OString &rKey, const OString &rValue)
1312 if (rKey == "border-width")
1313 set_border_width(rValue.toInt32());
1314 else
1315 return SystemWindow::set_property(rKey, rValue);
1316 return true;
1319 FactoryFunction Dialog::GetUITestFactory() const
1321 return DialogUIObject::create;
1324 VclBuilderContainer::VclBuilderContainer()
1325 : m_pUIBuilder(nullptr)
1329 VclBuilderContainer::~VclBuilderContainer()
1333 ModelessDialog::ModelessDialog(vcl::Window* pParent, const OUString& rID, const OUString& rUIXMLDescription, InitFlag eFlag)
1334 : Dialog(pParent, rID, rUIXMLDescription, WINDOW_MODELESSDIALOG, eFlag)
1338 ModalDialog::ModalDialog( vcl::Window* pParent, WinBits nStyle ) :
1339 Dialog( WINDOW_MODALDIALOG )
1341 ImplInit( pParent, nStyle );
1344 ModalDialog::ModalDialog( vcl::Window* pParent, const OUString& rID, const OUString& rUIXMLDescription ) :
1345 Dialog(pParent, rID, rUIXMLDescription, WINDOW_MODALDIALOG)
1349 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */