1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <unotools/viewoptions.hxx>
22 #include <com/sun/star/frame/XController.hpp>
23 #include <com/sun/star/frame/XFrame.hpp>
24 #include <com/sun/star/util/XCloseable.hpp>
25 #include <com/sun/star/beans/NamedValue.hpp>
26 #include <comphelper/string.hxx>
27 #include <cppuhelper/implbase.hxx>
28 #include <sal/log.hxx>
29 #include <tools/debug.hxx>
31 #include <vcl/svapp.hxx>
32 #include <sfx2/childwin.hxx>
33 #include <sfx2/app.hxx>
34 #include <sfx2/bindings.hxx>
35 #include <sfx2/module.hxx>
36 #include <sfx2/dockwin.hxx>
37 #include <sfx2/dispatch.hxx>
38 #include <workwin.hxx>
40 #include <sfx2/sfxsids.hrc>
41 #include <o3tl/string_view.hxx>
43 const sal_uInt16 nVersion
= 2;
45 SfxChildWinFactory::SfxChildWinFactory( SfxChildWinCtor pTheCtor
, sal_uInt16 nID
,
52 struct SfxChildWindow_Impl
54 css::uno::Reference
< css::frame::XFrame
> xFrame
;
55 css::uno::Reference
< css::lang::XEventListener
> xListener
;
56 SfxChildWinFactory aFact
= { nullptr, 0, 0 };
60 SfxWorkWindow
* pWorkWin
;
65 class DisposeListener
: public ::cppu::WeakImplHelper
< css::lang::XEventListener
>
68 DisposeListener( SfxChildWindow
* pOwner
,
69 SfxChildWindow_Impl
* pData
)
74 virtual void SAL_CALL
disposing( const css::lang::EventObject
& aSource
) override
76 css::uno::Reference
< css::lang::XEventListener
> xSelfHold( this );
78 css::uno::Reference
< css::lang::XComponent
> xComp( aSource
.Source
, css::uno::UNO_QUERY
);
80 xComp
->removeEventListener( this );
82 if( !m_pOwner
|| !m_pData
)
85 m_pData
->xListener
.clear();
87 if ( m_pData
->pWorkWin
)
89 // m_pOwner and m_pData will be killed
90 m_pData
->xFrame
.clear();
91 m_pData
->pWorkWin
->GetBindings().Execute( m_pOwner
->GetType() );
103 SfxChildWindow
* m_pOwner
;
104 SfxChildWindow_Impl
* m_pData
;
109 bool GetPosSizeFromString( std::u16string_view rStr
, Point
& rPos
, Size
& rSize
)
111 if ( comphelper::string::getTokenCount(rStr
, '/') != 4 )
115 rPos
.setX( o3tl::toInt32(o3tl::getToken(rStr
, 0, '/', nIdx
)) );
116 rPos
.setY( o3tl::toInt32(o3tl::getToken(rStr
, 0, '/', nIdx
)) );
117 rSize
.setWidth( o3tl::toInt32(o3tl::getToken(rStr
, 0, '/', nIdx
)) );
118 rSize
.setHeight( o3tl::toInt32(o3tl::getToken(rStr
, 0, '/', nIdx
)) );
120 // negative sizes are invalid
121 return rSize
.Width() >= 0 && rSize
.Height() >= 0;
124 bool GetSplitSizeFromString( std::u16string_view rStr
, Size
& rSize
)
126 size_t nIndex
= rStr
.find( ',' );
127 if ( nIndex
!= std::u16string_view::npos
)
129 std::u16string_view aStr
= rStr
.substr( nIndex
+1 );
131 sal_Int32 nCount
= comphelper::string::getTokenCount(aStr
, ';');
136 rSize
.setWidth( o3tl::toInt32(o3tl::getToken(aStr
, 0, ';', nIdx
)) );
137 rSize
.setHeight( o3tl::toInt32(o3tl::getToken(aStr
, 0, ';', nIdx
)) );
139 // negative sizes are invalid
140 return rSize
.Width() >= 0 && rSize
.Height() >= 0;
146 SfxChildWindow::SfxChildWindow(vcl::Window
*pParentWindow
, sal_uInt16 nId
)
147 : pParent(pParentWindow
)
148 , pImpl(new SfxChildWindow_Impl
)
149 , eChildAlignment(SfxChildAlignment::NOALIGNMENT
)
152 pImpl
->bHideNotDelete
= false;
153 pImpl
->bWantsFocus
= true;
154 pImpl
->bVisible
= true;
155 pImpl
->pWorkWin
= nullptr;
158 void SfxChildWindow::Destroy()
160 if ( GetFrame().is() )
165 css::uno::Reference
< css::util::XCloseable
> xClose( GetFrame(), css::uno::UNO_QUERY
);
167 xClose
->close( true );
169 GetFrame()->dispose();
171 catch (const css::uno::Exception
&)
179 void SfxChildWindow::ClearWorkwin()
183 if (pImpl
->pWorkWin
->GetActiveChild_Impl() == pWindow
)
184 pImpl
->pWorkWin
->SetActiveChild_Impl(nullptr);
185 pImpl
->pWorkWin
= nullptr;
189 SfxChildWindow::~SfxChildWindow()
194 xController
->ChildWinDispose();
197 pWindow
.disposeAndClear();
200 std::unique_ptr
<SfxChildWindow
> SfxChildWindow::CreateChildWindow( sal_uInt16 nId
,
201 vcl::Window
*pParent
, SfxBindings
* pBindings
, SfxChildWinInfo
& rInfo
)
203 std::unique_ptr
<SfxChildWindow
> pChild
;
204 SfxChildWinFactory
* pFact
=nullptr;
205 SystemWindowFlags nOldMode
= Application::GetSystemWindowMode();
207 // First search for ChildWindow in SDT; Overlay windows are realized
208 // by using ChildWindowContext
209 SfxApplication
*pApp
= SfxGetpApp();
211 pFact
= pApp
->GetChildWinFactoryById(nId
);
214 if ( rInfo
.bVisible
)
217 pBindings
->ENTERREGISTRATIONS();
218 SfxChildWinInfo aInfo
= rInfo
;
219 Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE
);
220 pChild
= pFact
->pCtor( pParent
, nId
, pBindings
, &aInfo
);
221 Application::SetSystemWindowMode( nOldMode
);
223 pBindings
->LEAVEREGISTRATIONS();
228 SfxDispatcher
*pDisp
= pBindings
? pBindings
->GetDispatcher_Impl() : nullptr;
229 SfxModule
*pMod
= pDisp
? SfxModule::GetActiveModule( pDisp
->GetFrame() ) : nullptr;
232 pFact
= pMod
->GetChildWinFactoryById(nId
);
235 if ( rInfo
.bVisible
)
238 pBindings
->ENTERREGISTRATIONS();
239 SfxChildWinInfo aInfo
= rInfo
;
240 Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE
);
241 pChild
= pFact
->pCtor( pParent
, nId
, pBindings
, &aInfo
);
242 rInfo
.nFlags
|= aInfo
.nFlags
;
243 Application::SetSystemWindowMode( nOldMode
);
245 pBindings
->LEAVEREGISTRATIONS();
252 assert(pFact
&& "pChild is returned by a call on pFact, so pFact cannot be null");
253 pChild
->SetFactory_Impl( pFact
);
256 DBG_ASSERT(pFact
&& (pChild
|| !rInfo
.bVisible
), "ChildWindow-Typ not registered!");
258 if (pChild
&& (!pChild
->pWindow
&& !pChild
->xController
))
261 SAL_INFO("sfx.appl", "ChildWindow has no Window!");
268 void SfxChildWindow::SaveStatus(const SfxChildWinInfo
& rInfo
)
270 sal_uInt16 nID
= GetType();
272 OUString aInfoVisible
= rInfo
.bVisible
? u
"V"_ustr
: u
"H"_ustr
;
274 OUString aWinData
= "V"
275 + OUString::number(static_cast<sal_Int32
>(nVersion
))
279 + OUString::number(static_cast<sal_Int32
>(rInfo
.nFlags
));
281 if ( !rInfo
.aExtraString
.isEmpty() )
282 aWinData
+= "," + rInfo
.aExtraString
;
284 OUString
sName(OUString::number(nID
));
285 //Try and save window state per-module, e.g. sidebar on in one application
287 if (!rInfo
.aModule
.isEmpty())
288 sName
= rInfo
.aModule
+ "/" + sName
;
289 SvtViewOptions
aWinOpt(EViewType::Window
, sName
);
290 aWinOpt
.SetWindowState(rInfo
.aWinState
);
292 css::uno::Sequence
< css::beans::NamedValue
> aSeq
293 { { u
"Data"_ustr
, css::uno::Any(aWinData
) } };
294 aWinOpt
.SetUserData( aSeq
);
296 // ... but save status at runtime!
297 pImpl
->aFact
.aInfo
= rInfo
;
300 void SfxChildWindow::SetAlignment(SfxChildAlignment eAlign
)
302 eChildAlignment
= eAlign
;
305 SfxChildWinInfo
SfxChildWindow::GetInfo() const
307 SfxChildWinInfo
aInfo(pImpl
->aFact
.aInfo
);
310 weld::Dialog
* pDialog
= xController
->getDialog();
311 aInfo
.aPos
= pDialog
->get_position();
312 aInfo
.aSize
= pDialog
->get_size();
313 vcl::WindowDataMask nMask
= vcl::WindowDataMask::Pos
| vcl::WindowDataMask::State
;
314 if (pDialog
->get_resizable())
315 nMask
|= vcl::WindowDataMask::Size
;
316 aInfo
.aWinState
= pDialog
->get_window_state(nMask
);
320 aInfo
.aPos
= pWindow
->GetPosPixel();
321 aInfo
.aSize
= pWindow
->GetSizePixel();
322 if ( pWindow
->IsSystemWindow() )
324 vcl::WindowDataMask nMask
= vcl::WindowDataMask::Pos
| vcl::WindowDataMask::State
;
325 if ( pWindow
->GetStyle() & WB_SIZEABLE
)
326 nMask
|= vcl::WindowDataMask::Size
;
327 aInfo
.aWinState
= static_cast<SystemWindow
*>(pWindow
.get())->GetWindowState( nMask
);
329 else if (DockingWindow
* pDockingWindow
= dynamic_cast<DockingWindow
*>(pWindow
.get()))
331 if (pDockingWindow
->GetFloatingWindow())
332 aInfo
.aWinState
= pDockingWindow
->GetFloatingWindow()->GetWindowState();
333 else if (SfxDockingWindow
* pSfxDockingWindow
= dynamic_cast<SfxDockingWindow
*>(pDockingWindow
))
335 SfxChildWinInfo aTmpInfo
;
336 pSfxDockingWindow
->FillInfo( aTmpInfo
);
337 aInfo
.aExtraString
= aTmpInfo
.aExtraString
;
342 aInfo
.bVisible
= pImpl
->bVisible
;
343 aInfo
.nFlags
= SfxChildWindowFlags::NONE
;
347 sal_uInt16
SfxChildWindow::GetPosition() const
349 return pImpl
->aFact
.nPos
;
352 void SfxChildWindow::InitializeChildWinFactory_Impl(sal_uInt16 nId
, SfxChildWinInfo
& rInfo
)
354 // load configuration
356 std::optional
<SvtViewOptions
> xWinOpt
;
357 // first see if a module specific id exists
358 if (rInfo
.aModule
.getLength())
359 xWinOpt
.emplace(EViewType::Window
, rInfo
.aModule
+ "/" + OUString::number(nId
));
361 // if not then try the generic id
362 if (!xWinOpt
|| !xWinOpt
->Exists())
363 xWinOpt
.emplace(EViewType::Window
, OUString::number(nId
));
365 if (xWinOpt
->Exists() && xWinOpt
->HasVisible() )
366 rInfo
.bVisible
= xWinOpt
->IsVisible(); // set state from configuration. Can be overwritten by UserData, see below
368 css::uno::Sequence
< css::beans::NamedValue
> aSeq
= xWinOpt
->GetUserData();
371 if ( aSeq
.hasElements() )
372 aSeq
[0].Value
>>= aTmp
;
374 OUString
aWinData( aTmp
);
375 rInfo
.aWinState
= xWinOpt
->GetWindowState();
377 if ( aWinData
.isEmpty() )
380 // Search for version ID
381 if ( aWinData
[0] != 0x0056 ) // 'V' = 56h
382 // A version ID, so do not use
386 aWinData
= aWinData
.copy(1);
390 sal_Int32 nPos
= aWinData
.indexOf( cToken
);
391 sal_uInt16 nActVersion
= static_cast<sal_uInt16
>(o3tl::toInt32(aWinData
.subView( 0, nPos
+ 1 )));
392 if ( nActVersion
!= nVersion
)
395 aWinData
= aWinData
.copy(nPos
+1);
397 // Load Visibility: is coded as a char
398 rInfo
.bVisible
= (aWinData
[0] == 0x0056); // 'V' = 56h
399 aWinData
= aWinData
.copy(1);
400 nPos
= aWinData
.indexOf( cToken
);
404 sal_Int32 nNextPos
= aWinData
.indexOf( cToken
, 2 );
405 if ( nNextPos
!= -1 )
407 // there is extra information
408 rInfo
.nFlags
= static_cast<SfxChildWindowFlags
>(static_cast<sal_uInt16
>(o3tl::toInt32(aWinData
.subView( nPos
+1, nNextPos
- nPos
- 1 ))));
409 aWinData
= aWinData
.replaceAt( nPos
, nNextPos
-nPos
+1, u
"" );
410 rInfo
.aExtraString
= aWinData
;
413 rInfo
.nFlags
= static_cast<SfxChildWindowFlags
>(static_cast<sal_uInt16
>(o3tl::toInt32(aWinData
.subView( nPos
+1 ))));
416 bool ParentIsFloatingWindow(const vcl::Window
*pParent
)
420 if (pParent
->GetType() == WindowType::DOCKINGWINDOW
|| pParent
->GetType() == WindowType::TOOLBOX
)
421 return static_cast<const DockingWindow
*>(pParent
)->GetFloatingWindow() != nullptr;
422 if (pParent
->GetType() == WindowType::FLOATINGWINDOW
)
427 void SfxChildWindow::SetFactory_Impl( const SfxChildWinFactory
*pF
)
432 void SfxChildWindow::SetHideNotDelete( bool bOn
)
434 pImpl
->bHideNotDelete
= bOn
;
437 bool SfxChildWindow::IsHideNotDelete() const
439 return pImpl
->bHideNotDelete
;
442 void SfxChildWindow::SetWantsFocus( bool bSet
)
444 pImpl
->bWantsFocus
= bSet
;
447 bool SfxChildWindow::WantsFocus() const
449 return pImpl
->bWantsFocus
;
452 bool SfxChildWinInfo::GetExtraData_Impl
454 SfxChildAlignment
*pAlign
458 if ( aExtraString
.isEmpty() )
461 sal_Int32 nPos
= aExtraString
.indexOf("AL:");
465 // Try to read the alignment string "ALIGN :(...)", but if
466 // it is not present, then use an older version
467 sal_Int32 n1
= aExtraString
.indexOf('(', nPos
);
470 sal_Int32 n2
= aExtraString
.indexOf(')', n1
);
473 // Cut out Alignment string
474 aStr
= aExtraString
.copy(nPos
, n2
- nPos
+ 1);
475 aStr
= aStr
.replaceAt(nPos
, n1
-nPos
+1, u
"");
479 // First extract the Alignment
480 if ( aStr
.isEmpty() )
483 *pAlign
= static_cast<SfxChildAlignment
>(static_cast<sal_uInt16
>(aStr
.toInt32()));
485 // then the LastAlignment
486 nPos
= aStr
.indexOf(',');
489 aStr
= aStr
.copy(nPos
+1);
491 // Then the splitting information
492 nPos
= aStr
.indexOf(',');
494 // No docking in a Splitwindow
496 aStr
= aStr
.copy(nPos
+1);
499 return GetPosSizeFromString( aStr
, aChildPos
, aChildSize
);
502 bool SfxChildWindow::IsVisible() const
504 return pImpl
->bVisible
;
507 void SfxChildWindow::SetVisible_Impl( bool bVis
)
509 pImpl
->bVisible
= bVis
;
512 void SfxChildWindow::Hide()
515 xController
->EndDialog(nCloseResponseToJustHide
);
520 void SfxChildWindow::Show( ShowFlags nFlags
)
524 if (!xController
->getDialog()->get_visible())
526 if (!xController
->CloseOnHide())
528 // tdf#155708 - do not run a new (Async) validation window,
529 // because we already have one in sync mode, just show the running one
530 xController
->getDialog()->show();
534 weld::DialogController::runAsync(xController
,
535 [this](sal_Int32 nResult
) {
536 if (nResult
== nCloseResponseToJustHide
)
538 xController
->Close();
544 pWindow
->Show(true, nFlags
);
547 void SfxChildWindow::SetWorkWindow_Impl( SfxWorkWindow
* pWin
)
549 pImpl
->pWorkWin
= pWin
;
552 if ( (xController
&& xController
->getDialog()->has_toplevel_focus()) ||
553 (pWindow
&& pWindow
->HasChildPathFocus()) )
555 pImpl
->pWorkWin
->SetActiveChild_Impl( pWindow
);
560 void SfxChildWindow::Activate_Impl()
562 if(pImpl
->pWorkWin
!=nullptr)
563 pImpl
->pWorkWin
->SetActiveChild_Impl( pWindow
);
566 bool SfxChildWindow::QueryClose()
570 if ( pImpl
->xFrame
.is() )
572 css::uno::Reference
< css::frame::XController
> xCtrl
= pImpl
->xFrame
->getController();
574 bAllow
= xCtrl
->suspend( true );
581 weld::Dialog
* pDialog
= GetController()->getDialog();
582 bAllow
= !pDialog
->get_visible() || !pDialog
->get_modal();
584 else if (GetWindow())
585 bAllow
= !GetWindow()->IsInModalMode();
591 const css::uno::Reference
< css::frame::XFrame
>& SfxChildWindow::GetFrame() const
593 return pImpl
->xFrame
;
596 void SfxChildWindow::SetFrame( const css::uno::Reference
< css::frame::XFrame
> & rFrame
)
598 // Do nothing if nothing will be changed ...
599 if( pImpl
->xFrame
== rFrame
)
602 // ... but stop listening on old frame, if connection exist!
603 if( pImpl
->xFrame
.is() )
604 pImpl
->xFrame
->removeEventListener( pImpl
->xListener
);
606 // If new frame is not NULL -> we must guarantee valid listener for disposing events.
607 // Use already existing or create new one.
609 if( !pImpl
->xListener
.is() )
610 pImpl
->xListener
.set( new DisposeListener( this, pImpl
.get() ) );
612 // Set new frame in data container
613 // and build new listener connection, if necessary.
614 pImpl
->xFrame
= rFrame
;
615 if( pImpl
->xFrame
.is() )
616 pImpl
->xFrame
->addEventListener( pImpl
->xListener
);
619 void SfxChildWindow::RegisterChildWindow(SfxModule
* pMod
, const SfxChildWinFactory
& rFact
)
621 SfxGetpApp()->RegisterChildWindow_Impl( pMod
, rFact
);
624 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */