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 <osl/diagnose.h>
29 #include <sal/log.hxx>
30 #include <tools/debug.hxx>
32 #include <vcl/svapp.hxx>
33 #include <sfx2/childwin.hxx>
34 #include <sfx2/app.hxx>
35 #include <sfx2/bindings.hxx>
36 #include <sfx2/module.hxx>
37 #include <sfx2/dockwin.hxx>
38 #include <sfx2/dispatch.hxx>
39 #include <workwin.hxx>
40 #include <childwinimpl.hxx>
42 const sal_uInt16 nVersion
= 2;
44 SfxChildWinFactory::SfxChildWinFactory( SfxChildWinCtor pTheCtor
, sal_uInt16 nID
,
51 SfxChildWinFactory::~SfxChildWinFactory()
55 struct SfxChildWindow_Impl
57 css::uno::Reference
< css::frame::XFrame
> xFrame
;
58 css::uno::Reference
< css::lang::XEventListener
> xListener
;
59 SfxChildWinFactory
* pFact
;
63 SfxModule
* pContextModule
;
64 SfxWorkWindow
* pWorkWin
;
69 class DisposeListener
: public ::cppu::WeakImplHelper
< css::lang::XEventListener
>
72 DisposeListener( SfxChildWindow
* pOwner
,
73 SfxChildWindow_Impl
* pData
)
78 virtual void SAL_CALL
disposing( const css::lang::EventObject
& aSource
) override
80 css::uno::Reference
< css::lang::XEventListener
> xSelfHold( this );
82 css::uno::Reference
< css::lang::XComponent
> xComp( aSource
.Source
, css::uno::UNO_QUERY
);
84 xComp
->removeEventListener( this );
86 if( !m_pOwner
|| !m_pData
)
89 m_pData
->xListener
.clear();
91 if ( m_pData
->pWorkWin
)
93 // m_pOwner and m_pData will be killed
94 m_pData
->xFrame
.clear();
95 m_pData
->pWorkWin
->GetBindings().Execute( m_pOwner
->GetType() );
107 SfxChildWindow
* m_pOwner
;
108 SfxChildWindow_Impl
* m_pData
;
113 bool GetPosSizeFromString( const OUString
& rStr
, Point
& rPos
, Size
& rSize
)
115 if ( comphelper::string::getTokenCount(rStr
, '/') != 4 )
119 rPos
.setX( rStr
.getToken(0, '/', nIdx
).toInt32() );
120 rPos
.setY( rStr
.getToken(0, '/', nIdx
).toInt32() );
121 rSize
.setWidth( rStr
.getToken(0, '/', nIdx
).toInt32() );
122 rSize
.setHeight( rStr
.getToken(0, '/', nIdx
).toInt32() );
124 // negative sizes are invalid
125 return rSize
.Width() >= 0 && rSize
.Height() >= 0;
128 bool GetSplitSizeFromString( const OUString
& rStr
, Size
& rSize
)
130 sal_Int32 nIndex
= rStr
.indexOf( ',' );
133 OUString aStr
= rStr
.copy( nIndex
+1 );
135 sal_Int32 nCount
= comphelper::string::getTokenCount(aStr
, ';');
140 rSize
.setWidth( aStr
.getToken(0, ';', nIdx
).toInt32() );
141 rSize
.setHeight( aStr
.getToken(0, ';', nIdx
).toInt32() );
143 // negative sizes are invalid
144 return rSize
.Width() >= 0 && rSize
.Height() >= 0;
151 SfxChildWindow::SfxChildWindow(vcl::Window
*pParentWindow
, sal_uInt16 nId
)
152 : pParent(pParentWindow
)
154 , eChildAlignment(SfxChildAlignment::NOALIGNMENT
)
155 , pImpl(new SfxChildWindow_Impl
)
157 pImpl
->pFact
= nullptr;
158 pImpl
->bHideNotDelete
= false;
159 pImpl
->bWantsFocus
= true;
160 pImpl
->bVisible
= true;
161 pImpl
->pContextModule
= nullptr;
162 pImpl
->pWorkWin
= nullptr;
167 void SfxChildWindow::Destroy()
169 if ( GetFrame().is() )
174 css::uno::Reference
< css::util::XCloseable
> xClose( GetFrame(), css::uno::UNO_QUERY
);
176 xClose
->close( true );
178 GetFrame()->dispose();
180 catch (const css::uno::Exception
&)
188 void SfxChildWindow::ClearWorkwin()
192 if (pImpl
->pWorkWin
->GetActiveChild_Impl() == pWindow
)
193 pImpl
->pWorkWin
->SetActiveChild_Impl(nullptr);
194 pImpl
->pWorkWin
= nullptr;
198 SfxChildWindow::~SfxChildWindow()
204 xController
->ChildWinDispose();
207 pWindow
.disposeAndClear();
210 std::unique_ptr
<SfxChildWindow
> SfxChildWindow::CreateChildWindow( sal_uInt16 nId
,
211 vcl::Window
*pParent
, SfxBindings
* pBindings
, SfxChildWinInfo
const & rInfo
)
213 std::unique_ptr
<SfxChildWindow
> pChild
;
214 SfxChildWinFactory
* pFact
=nullptr;
215 SystemWindowFlags nOldMode
= Application::GetSystemWindowMode();
217 // First search for ChildWindow in SDT; Overlay windows are realized
218 // by using ChildWindowContext
219 SfxApplication
*pApp
= SfxGetpApp();
221 SfxChildWinFactArr_Impl
&rFactories
= pApp
->GetChildWinFactories_Impl();
222 for ( size_t nFactory
= 0; nFactory
< rFactories
.size(); ++nFactory
)
224 pFact
= &rFactories
[nFactory
];
225 if ( pFact
->nId
== nId
)
227 SfxChildWinInfo
& rFactInfo
= pFact
->aInfo
;
228 if ( rInfo
.bVisible
)
231 pBindings
->ENTERREGISTRATIONS();
232 SfxChildWinInfo aInfo
= rFactInfo
;
233 Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE
);
234 pChild
= pFact
->pCtor( pParent
, nId
, pBindings
, &aInfo
);
235 Application::SetSystemWindowMode( nOldMode
);
237 pBindings
->LEAVEREGISTRATIONS();
245 SfxDispatcher
*pDisp
= pBindings
? pBindings
->GetDispatcher_Impl() : nullptr;
246 SfxModule
*pMod
= pDisp
? SfxModule::GetActiveModule( pDisp
->GetFrame() ) : nullptr;
249 SfxChildWinFactArr_Impl
*pFactories
= pMod
->GetChildWinFactories_Impl();
252 SfxChildWinFactArr_Impl
&rFactories
= *pFactories
;
253 for ( size_t nFactory
= 0; nFactory
< rFactories
.size(); ++nFactory
)
255 pFact
= &rFactories
[nFactory
];
256 if ( pFact
->nId
== nId
)
258 SfxChildWinInfo
& rFactInfo
= pFact
->aInfo
;
259 if ( rInfo
.bVisible
)
262 pBindings
->ENTERREGISTRATIONS();
263 SfxChildWinInfo aInfo
= rFactInfo
;
264 Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE
);
265 pChild
= pFact
->pCtor( pParent
, nId
, pBindings
, &aInfo
);
266 Application::SetSystemWindowMode( nOldMode
);
268 pBindings
->LEAVEREGISTRATIONS();
278 pChild
->SetFactory_Impl( pFact
);
280 DBG_ASSERT(pFact
&& (pChild
|| !rInfo
.bVisible
), "ChildWindow-Typ not registered!");
282 if (pChild
&& (!pChild
->pWindow
&& !pChild
->xController
))
285 SAL_INFO("sfx.appl", "ChildWindow has no Window!");
292 void SfxChildWindow::SaveStatus(const SfxChildWinInfo
& rInfo
)
294 sal_uInt16 nID
= GetType();
296 OUString aInfoVisible
= rInfo
.bVisible
? OUString("V") : OUString("H");
298 OUString aWinData
= "V"
299 + OUString::number(static_cast<sal_Int32
>(nVersion
))
303 + OUString::number(static_cast<sal_Int32
>(rInfo
.nFlags
));
305 if ( !rInfo
.aExtraString
.isEmpty() )
306 aWinData
+= "," + rInfo
.aExtraString
;
308 OUString
sName(OUString::number(nID
));
309 //Try and save window state per-module, e.g. sidebar on in one application
311 if (!rInfo
.aModule
.isEmpty())
312 sName
= rInfo
.aModule
+ "/" + sName
;
313 SvtViewOptions
aWinOpt(EViewType::Window
, sName
);
314 aWinOpt
.SetWindowState(OStringToOUString(rInfo
.aWinState
, RTL_TEXTENCODING_UTF8
));
316 css::uno::Sequence
< css::beans::NamedValue
> aSeq
317 { { "Data", css::uno::makeAny(aWinData
) } };
318 aWinOpt
.SetUserData( aSeq
);
320 // ... but save status at runtime!
321 pImpl
->pFact
->aInfo
= rInfo
;
324 void SfxChildWindow::SetAlignment(SfxChildAlignment eAlign
)
326 eChildAlignment
= eAlign
;
329 SfxChildWinInfo
SfxChildWindow::GetInfo() const
331 SfxChildWinInfo
aInfo(pImpl
->pFact
->aInfo
);
334 weld::Dialog
* pDialog
= xController
->getDialog();
335 aInfo
.aPos
= pDialog
->get_position();
336 aInfo
.aSize
= pDialog
->get_size();
337 WindowStateMask nMask
= WindowStateMask::Pos
| WindowStateMask::State
;
338 if (pDialog
->get_resizable())
339 nMask
|= WindowStateMask::Width
| WindowStateMask::Height
;
340 aInfo
.aWinState
= pDialog
->get_window_state(nMask
);
344 aInfo
.aPos
= pWindow
->GetPosPixel();
345 aInfo
.aSize
= pWindow
->GetSizePixel();
346 if ( pWindow
->IsSystemWindow() )
348 WindowStateMask nMask
= WindowStateMask::Pos
| WindowStateMask::State
;
349 if ( pWindow
->GetStyle() & WB_SIZEABLE
)
350 nMask
|= WindowStateMask::Width
| WindowStateMask::Height
;
351 aInfo
.aWinState
= static_cast<SystemWindow
*>(pWindow
.get())->GetWindowState( nMask
);
353 else if (DockingWindow
* pDockingWindow
= dynamic_cast<DockingWindow
*>(pWindow
.get()))
355 if (pDockingWindow
->GetFloatingWindow())
356 aInfo
.aWinState
= pDockingWindow
->GetFloatingWindow()->GetWindowState();
357 else if (SfxDockingWindow
* pSfxDockingWindow
= dynamic_cast<SfxDockingWindow
*>(pDockingWindow
))
359 SfxChildWinInfo aTmpInfo
;
360 pSfxDockingWindow
->FillInfo( aTmpInfo
);
361 aInfo
.aExtraString
= aTmpInfo
.aExtraString
;
366 aInfo
.bVisible
= pImpl
->bVisible
;
367 aInfo
.nFlags
= SfxChildWindowFlags::NONE
;
371 sal_uInt16
SfxChildWindow::GetPosition() const
373 return pImpl
->pFact
->nPos
;
376 void SfxChildWindow::InitializeChildWinFactory_Impl(sal_uInt16 nId
, SfxChildWinInfo
& rInfo
)
378 // load configuration
380 std::unique_ptr
<SvtViewOptions
> xWinOpt
;
381 // first see if a module specific id exists
382 if (rInfo
.aModule
.getLength())
383 xWinOpt
.reset(new SvtViewOptions(EViewType::Window
, rInfo
.aModule
+ "/" + OUString::number(nId
)));
385 // if not then try the generic id
386 if (!xWinOpt
|| !xWinOpt
->Exists())
387 xWinOpt
.reset(new SvtViewOptions(EViewType::Window
, OUString::number(nId
)));
389 if (xWinOpt
->Exists() && xWinOpt
->HasVisible() )
390 rInfo
.bVisible
= xWinOpt
->IsVisible(); // set state from configuration. Can be overwritten by UserData, see below
392 css::uno::Sequence
< css::beans::NamedValue
> aSeq
= xWinOpt
->GetUserData();
395 if ( aSeq
.hasElements() )
396 aSeq
[0].Value
>>= aTmp
;
398 OUString
aWinData( aTmp
);
399 rInfo
.aWinState
= OUStringToOString(xWinOpt
->GetWindowState(), RTL_TEXTENCODING_UTF8
);
401 if ( aWinData
.isEmpty() )
404 // Search for version ID
405 if ( aWinData
[0] != 0x0056 ) // 'V' = 56h
406 // A version ID, so do not use
410 aWinData
= aWinData
.copy(1);
414 sal_Int32 nPos
= aWinData
.indexOf( cToken
);
415 sal_uInt16 nActVersion
= static_cast<sal_uInt16
>(aWinData
.copy( 0, nPos
+ 1 ).toInt32());
416 if ( nActVersion
!= nVersion
)
419 aWinData
= aWinData
.copy(nPos
+1);
421 // Load Visibility: is coded as a char
422 rInfo
.bVisible
= (aWinData
[0] == 0x0056); // 'V' = 56h
423 aWinData
= aWinData
.copy(1);
424 nPos
= aWinData
.indexOf( cToken
);
428 sal_Int32 nNextPos
= aWinData
.indexOf( cToken
, 2 );
429 if ( nNextPos
!= -1 )
431 // there is extra information
432 rInfo
.nFlags
= static_cast<SfxChildWindowFlags
>(static_cast<sal_uInt16
>(aWinData
.copy( nPos
+1, nNextPos
- nPos
- 1 ).toInt32()));
433 aWinData
= aWinData
.replaceAt( nPos
, nNextPos
-nPos
+1, "" );
434 rInfo
.aExtraString
= aWinData
;
437 rInfo
.nFlags
= static_cast<SfxChildWindowFlags
>(static_cast<sal_uInt16
>(aWinData
.copy( nPos
+1 ).toInt32()));
440 void SfxChildWindow::CreateContext( sal_uInt16 nContextId
, SfxBindings
& rBindings
)
442 std::unique_ptr
<SfxChildWindowContext
> pCon
;
443 SfxChildWinFactory
* pFact
=nullptr;
444 SfxApplication
*pApp
= SfxGetpApp();
445 SfxDispatcher
*pDisp
= rBindings
.GetDispatcher_Impl();
446 SfxModule
*pMod
= pDisp
? SfxModule::GetActiveModule( pDisp
->GetFrame() ) :nullptr;
449 SfxChildWinFactArr_Impl
*pFactories
= pMod
->GetChildWinFactories_Impl();
452 SfxChildWinFactArr_Impl
&rFactories
= *pFactories
;
453 for ( size_t nFactory
= 0; nFactory
< rFactories
.size(); ++nFactory
)
455 pFact
= &rFactories
[nFactory
];
456 if ( pFact
->nId
== GetType() )
458 DBG_ASSERT( pFact
->pArr
, "No context registered!" );
462 for ( size_t n
=0; n
<pFact
->pArr
->size(); ++n
)
464 SfxChildWinContextFactory
*pConFact
= &(*pFact
->pArr
)[n
];
465 rBindings
.ENTERREGISTRATIONS();
466 if ( pConFact
->nContextId
== nContextId
)
468 SfxChildWinInfo aInfo
= pFact
->aInfo
;
469 pCon
= pConFact
->pCtor( GetWindow(), &rBindings
, &aInfo
);
470 pCon
->nContextId
= pConFact
->nContextId
;
471 pImpl
->pContextModule
= pMod
;
473 rBindings
.LEAVEREGISTRATIONS();
483 SfxChildWinFactArr_Impl
&rFactories
= pApp
->GetChildWinFactories_Impl();
484 for ( size_t nFactory
= 0; nFactory
< rFactories
.size(); ++nFactory
)
486 pFact
= &rFactories
[nFactory
];
487 if ( pFact
->nId
== GetType() )
489 DBG_ASSERT( pFact
->pArr
, "No context registered!" );
493 for ( size_t n
=0; n
<pFact
->pArr
->size(); ++n
)
495 SfxChildWinContextFactory
*pConFact
= &(*pFact
->pArr
)[n
];
496 rBindings
.ENTERREGISTRATIONS();
497 if ( pConFact
->nContextId
== nContextId
)
499 SfxChildWinInfo aInfo
= pFact
->aInfo
;
500 pCon
= pConFact
->pCtor( GetWindow(), &rBindings
, &aInfo
);
501 pCon
->nContextId
= pConFact
->nContextId
;
502 pImpl
->pContextModule
= nullptr;
504 rBindings
.LEAVEREGISTRATIONS();
513 OSL_FAIL( "No suitable context found! ");
517 pContext
= std::move(pCon
);
518 pContext
->GetWindow()->SetSizePixel( pWindow
->GetOutputSizePixel() );
519 pContext
->GetWindow()->Show();
522 SfxChildWindowContext::SfxChildWindowContext( sal_uInt16 nId
)
527 SfxChildWindowContext::~SfxChildWindowContext()
529 pWindow
.disposeAndClear();
532 FloatingWindow
* SfxChildWindowContext::GetFloatingWindow(vcl::Window
*pParent
)
534 if (pParent
->GetType() == WindowType::DOCKINGWINDOW
|| pParent
->GetType() == WindowType::TOOLBOX
)
536 return static_cast<DockingWindow
*>(pParent
)->GetFloatingWindow();
538 if (pParent
->GetType() == WindowType::FLOATINGWINDOW
)
540 return static_cast<FloatingWindow
*>(pParent
);
545 void SfxChildWindow::SetFactory_Impl( SfxChildWinFactory
*pF
)
550 void SfxChildWindow::SetHideNotDelete( bool bOn
)
552 pImpl
->bHideNotDelete
= bOn
;
555 bool SfxChildWindow::IsHideNotDelete() const
557 return pImpl
->bHideNotDelete
;
560 void SfxChildWindow::SetWantsFocus( bool bSet
)
562 pImpl
->bWantsFocus
= bSet
;
565 bool SfxChildWindow::WantsFocus() const
567 return pImpl
->bWantsFocus
;
570 bool SfxChildWinInfo::GetExtraData_Impl
572 SfxChildAlignment
*pAlign
576 if ( aExtraString
.isEmpty() )
579 sal_Int32 nPos
= aExtraString
.indexOf("AL:");
583 // Try to read the alignment string "ALIGN :(...)", but if
584 // it is not present, then use an older version
585 sal_Int32 n1
= aExtraString
.indexOf('(', nPos
);
588 sal_Int32 n2
= aExtraString
.indexOf(')', n1
);
591 // Cut out Alignment string
592 aStr
= aExtraString
.copy(nPos
, n2
- nPos
+ 1);
593 aStr
= aStr
.replaceAt(nPos
, n1
-nPos
+1, "");
597 // First extract the Alignment
598 if ( aStr
.isEmpty() )
601 *pAlign
= static_cast<SfxChildAlignment
>(static_cast<sal_uInt16
>(aStr
.toInt32()));
603 // then the LastAlignment
604 nPos
= aStr
.indexOf(',');
607 aStr
= aStr
.copy(nPos
+1);
609 // Then the splitting information
610 nPos
= aStr
.indexOf(',');
612 // No docking in a Splitwindow
614 aStr
= aStr
.copy(nPos
+1);
617 return GetPosSizeFromString( aStr
, aChildPos
, aChildSize
);
620 bool SfxChildWindow::IsVisible() const
622 return pImpl
->bVisible
;
625 void SfxChildWindow::SetVisible_Impl( bool bVis
)
627 pImpl
->bVisible
= bVis
;
630 void SfxChildWindow::Hide()
633 xController
->EndDialog();
638 void SfxChildWindow::Show( ShowFlags nFlags
)
642 if (!xController
->getDialog()->get_visible())
644 weld::DialogController::runAsync(xController
,
645 [this](sal_Int32
/*nResult*/){ xController
->Close(); });
649 pWindow
->Show(true, nFlags
);
652 vcl::Window
* SfxChildWindow::GetContextWindow( SfxModule
const *pModule
) const
654 return pModule
== pImpl
->pContextModule
&& pContext
? pContext
->GetWindow(): nullptr;
657 void SfxChildWindow::SetWorkWindow_Impl( SfxWorkWindow
* pWin
)
659 pImpl
->pWorkWin
= pWin
;
662 if ( (xController
&& xController
->getDialog()->has_toplevel_focus()) ||
663 (pWindow
&& pWindow
->HasChildPathFocus()) )
665 pImpl
->pWorkWin
->SetActiveChild_Impl( pWindow
);
670 void SfxChildWindow::Activate_Impl()
672 if(pImpl
->pWorkWin
!=nullptr)
673 pImpl
->pWorkWin
->SetActiveChild_Impl( pWindow
);
676 bool SfxChildWindow::QueryClose()
680 if ( pImpl
->xFrame
.is() )
682 css::uno::Reference
< css::frame::XController
> xCtrl
= pImpl
->xFrame
->getController();
684 bAllow
= xCtrl
->suspend( true );
691 weld::Dialog
* pDialog
= GetController()->getDialog();
692 bAllow
= !pDialog
->get_visible() || !pDialog
->get_modal();
694 else if (GetWindow())
695 bAllow
= !GetWindow()->IsInModalMode();
701 const css::uno::Reference
< css::frame::XFrame
>& SfxChildWindow::GetFrame() const
703 return pImpl
->xFrame
;
706 void SfxChildWindow::SetFrame( const css::uno::Reference
< css::frame::XFrame
> & rFrame
)
708 // Do nothing if nothing will be changed ...
709 if( pImpl
->xFrame
== rFrame
)
712 // ... but stop listening on old frame, if connection exist!
713 if( pImpl
->xFrame
.is() )
714 pImpl
->xFrame
->removeEventListener( pImpl
->xListener
);
716 // If new frame is not NULL -> we must guarantee valid listener for disposing events.
717 // Use already existing or create new one.
719 if( !pImpl
->xListener
.is() )
720 pImpl
->xListener
.set( new DisposeListener( this, pImpl
.get() ) );
722 // Set new frame in data container
723 // and build new listener connection, if necessary.
724 pImpl
->xFrame
= rFrame
;
725 if( pImpl
->xFrame
.is() )
726 pImpl
->xFrame
->addEventListener( pImpl
->xListener
);
729 void SfxChildWindowContext::RegisterChildWindowContext(SfxModule
* pMod
, sal_uInt16 nId
, std::unique_ptr
<SfxChildWinContextFactory
> pFact
)
731 SfxGetpApp()->RegisterChildWindowContext_Impl( pMod
, nId
, std::move(pFact
) );
734 void SfxChildWindow::RegisterChildWindow(SfxModule
* pMod
, std::unique_ptr
<SfxChildWinFactory
> pFact
)
736 SfxGetpApp()->RegisterChildWindow_Impl( pMod
, std::move(pFact
) );
739 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */