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>
41 const sal_uInt16 nVersion
= 2;
43 SfxChildWinFactory::SfxChildWinFactory( SfxChildWinCtor pTheCtor
, sal_uInt16 nID
,
50 struct SfxChildWindow_Impl
52 css::uno::Reference
< css::frame::XFrame
> xFrame
;
53 css::uno::Reference
< css::lang::XEventListener
> xListener
;
54 SfxChildWinFactory aFact
= { nullptr, 0, 0 };
58 SfxWorkWindow
* pWorkWin
;
63 class DisposeListener
: public ::cppu::WeakImplHelper
< css::lang::XEventListener
>
66 DisposeListener( SfxChildWindow
* pOwner
,
67 SfxChildWindow_Impl
* pData
)
72 virtual void SAL_CALL
disposing( const css::lang::EventObject
& aSource
) override
74 css::uno::Reference
< css::lang::XEventListener
> xSelfHold( this );
76 css::uno::Reference
< css::lang::XComponent
> xComp( aSource
.Source
, css::uno::UNO_QUERY
);
78 xComp
->removeEventListener( this );
80 if( !m_pOwner
|| !m_pData
)
83 m_pData
->xListener
.clear();
85 if ( m_pData
->pWorkWin
)
87 // m_pOwner and m_pData will be killed
88 m_pData
->xFrame
.clear();
89 m_pData
->pWorkWin
->GetBindings().Execute( m_pOwner
->GetType() );
101 SfxChildWindow
* m_pOwner
;
102 SfxChildWindow_Impl
* m_pData
;
107 bool GetPosSizeFromString( const OUString
& rStr
, Point
& rPos
, Size
& rSize
)
109 if ( comphelper::string::getTokenCount(rStr
, '/') != 4 )
113 rPos
.setX( rStr
.getToken(0, '/', nIdx
).toInt32() );
114 rPos
.setY( rStr
.getToken(0, '/', nIdx
).toInt32() );
115 rSize
.setWidth( rStr
.getToken(0, '/', nIdx
).toInt32() );
116 rSize
.setHeight( rStr
.getToken(0, '/', nIdx
).toInt32() );
118 // negative sizes are invalid
119 return rSize
.Width() >= 0 && rSize
.Height() >= 0;
122 bool GetSplitSizeFromString( const OUString
& rStr
, Size
& rSize
)
124 sal_Int32 nIndex
= rStr
.indexOf( ',' );
127 OUString aStr
= rStr
.copy( nIndex
+1 );
129 sal_Int32 nCount
= comphelper::string::getTokenCount(aStr
, ';');
134 rSize
.setWidth( aStr
.getToken(0, ';', nIdx
).toInt32() );
135 rSize
.setHeight( aStr
.getToken(0, ';', nIdx
).toInt32() );
137 // negative sizes are invalid
138 return rSize
.Width() >= 0 && rSize
.Height() >= 0;
144 SfxChildWindow::SfxChildWindow(vcl::Window
*pParentWindow
, sal_uInt16 nId
)
145 : pParent(pParentWindow
)
146 , pImpl(new SfxChildWindow_Impl
)
147 , eChildAlignment(SfxChildAlignment::NOALIGNMENT
)
150 pImpl
->bHideNotDelete
= false;
151 pImpl
->bWantsFocus
= true;
152 pImpl
->bVisible
= true;
153 pImpl
->pWorkWin
= nullptr;
156 void SfxChildWindow::Destroy()
158 if ( GetFrame().is() )
163 css::uno::Reference
< css::util::XCloseable
> xClose( GetFrame(), css::uno::UNO_QUERY
);
165 xClose
->close( true );
167 GetFrame()->dispose();
169 catch (const css::uno::Exception
&)
177 void SfxChildWindow::ClearWorkwin()
181 if (pImpl
->pWorkWin
->GetActiveChild_Impl() == pWindow
)
182 pImpl
->pWorkWin
->SetActiveChild_Impl(nullptr);
183 pImpl
->pWorkWin
= nullptr;
187 SfxChildWindow::~SfxChildWindow()
192 xController
->ChildWinDispose();
195 pWindow
.disposeAndClear();
198 std::unique_ptr
<SfxChildWindow
> SfxChildWindow::CreateChildWindow( sal_uInt16 nId
,
199 vcl::Window
*pParent
, SfxBindings
* pBindings
, SfxChildWinInfo
const & rInfo
)
201 std::unique_ptr
<SfxChildWindow
> pChild
;
202 SfxChildWinFactory
* pFact
=nullptr;
203 SystemWindowFlags nOldMode
= Application::GetSystemWindowMode();
205 // First search for ChildWindow in SDT; Overlay windows are realized
206 // by using ChildWindowContext
207 SfxApplication
*pApp
= SfxGetpApp();
209 pFact
= pApp
->GetChildWinFactoryById(nId
);
212 SfxChildWinInfo
& rFactInfo
= pFact
->aInfo
;
213 if ( rInfo
.bVisible
)
216 pBindings
->ENTERREGISTRATIONS();
217 SfxChildWinInfo aInfo
= rFactInfo
;
218 Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE
);
219 pChild
= pFact
->pCtor( pParent
, nId
, pBindings
, &aInfo
);
220 Application::SetSystemWindowMode( nOldMode
);
222 pBindings
->LEAVEREGISTRATIONS();
227 SfxDispatcher
*pDisp
= pBindings
? pBindings
->GetDispatcher_Impl() : nullptr;
228 SfxModule
*pMod
= pDisp
? SfxModule::GetActiveModule( pDisp
->GetFrame() ) : nullptr;
231 pFact
= pMod
->GetChildWinFactoryById(nId
);
234 SfxChildWinInfo
& rFactInfo
= pFact
->aInfo
;
235 if ( rInfo
.bVisible
)
238 pBindings
->ENTERREGISTRATIONS();
239 SfxChildWinInfo aInfo
= rFactInfo
;
240 Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE
);
241 pChild
= pFact
->pCtor( pParent
, nId
, pBindings
, &aInfo
);
242 Application::SetSystemWindowMode( nOldMode
);
244 pBindings
->LEAVEREGISTRATIONS();
251 assert(pFact
&& "pChild is returned by a call on pFact, so pFact cannot be null");
252 pChild
->SetFactory_Impl( pFact
);
255 DBG_ASSERT(pFact
&& (pChild
|| !rInfo
.bVisible
), "ChildWindow-Typ not registered!");
257 if (pChild
&& (!pChild
->pWindow
&& !pChild
->xController
))
260 SAL_INFO("sfx.appl", "ChildWindow has no Window!");
267 void SfxChildWindow::SaveStatus(const SfxChildWinInfo
& rInfo
)
269 sal_uInt16 nID
= GetType();
271 OUString aInfoVisible
= rInfo
.bVisible
? OUString("V") : OUString("H");
273 OUString aWinData
= "V"
274 + OUString::number(static_cast<sal_Int32
>(nVersion
))
278 + OUString::number(static_cast<sal_Int32
>(rInfo
.nFlags
));
280 if ( !rInfo
.aExtraString
.isEmpty() )
281 aWinData
+= "," + rInfo
.aExtraString
;
283 OUString
sName(OUString::number(nID
));
284 //Try and save window state per-module, e.g. sidebar on in one application
286 if (!rInfo
.aModule
.isEmpty())
287 sName
= rInfo
.aModule
+ "/" + sName
;
288 SvtViewOptions
aWinOpt(EViewType::Window
, sName
);
289 aWinOpt
.SetWindowState(OStringToOUString(rInfo
.aWinState
, RTL_TEXTENCODING_UTF8
));
291 css::uno::Sequence
< css::beans::NamedValue
> aSeq
292 { { "Data", css::uno::makeAny(aWinData
) } };
293 aWinOpt
.SetUserData( aSeq
);
295 // ... but save status at runtime!
296 pImpl
->aFact
.aInfo
= rInfo
;
299 void SfxChildWindow::SetAlignment(SfxChildAlignment eAlign
)
301 eChildAlignment
= eAlign
;
304 SfxChildWinInfo
SfxChildWindow::GetInfo() const
306 SfxChildWinInfo
aInfo(pImpl
->aFact
.aInfo
);
309 weld::Dialog
* pDialog
= xController
->getDialog();
310 aInfo
.aPos
= pDialog
->get_position();
311 aInfo
.aSize
= pDialog
->get_size();
312 WindowStateMask nMask
= WindowStateMask::Pos
| WindowStateMask::State
;
313 if (pDialog
->get_resizable())
314 nMask
|= WindowStateMask::Width
| WindowStateMask::Height
;
315 aInfo
.aWinState
= pDialog
->get_window_state(nMask
);
319 aInfo
.aPos
= pWindow
->GetPosPixel();
320 aInfo
.aSize
= pWindow
->GetSizePixel();
321 if ( pWindow
->IsSystemWindow() )
323 WindowStateMask nMask
= WindowStateMask::Pos
| WindowStateMask::State
;
324 if ( pWindow
->GetStyle() & WB_SIZEABLE
)
325 nMask
|= WindowStateMask::Width
| WindowStateMask::Height
;
326 aInfo
.aWinState
= static_cast<SystemWindow
*>(pWindow
.get())->GetWindowState( nMask
);
328 else if (DockingWindow
* pDockingWindow
= dynamic_cast<DockingWindow
*>(pWindow
.get()))
330 if (pDockingWindow
->GetFloatingWindow())
331 aInfo
.aWinState
= pDockingWindow
->GetFloatingWindow()->GetWindowState();
332 else if (SfxDockingWindow
* pSfxDockingWindow
= dynamic_cast<SfxDockingWindow
*>(pDockingWindow
))
334 SfxChildWinInfo aTmpInfo
;
335 pSfxDockingWindow
->FillInfo( aTmpInfo
);
336 aInfo
.aExtraString
= aTmpInfo
.aExtraString
;
341 aInfo
.bVisible
= pImpl
->bVisible
;
342 aInfo
.nFlags
= SfxChildWindowFlags::NONE
;
346 sal_uInt16
SfxChildWindow::GetPosition() const
348 return pImpl
->aFact
.nPos
;
351 void SfxChildWindow::InitializeChildWinFactory_Impl(sal_uInt16 nId
, SfxChildWinInfo
& rInfo
)
353 // load configuration
355 std::unique_ptr
<SvtViewOptions
> xWinOpt
;
356 // first see if a module specific id exists
357 if (rInfo
.aModule
.getLength())
358 xWinOpt
.reset(new SvtViewOptions(EViewType::Window
, rInfo
.aModule
+ "/" + OUString::number(nId
)));
360 // if not then try the generic id
361 if (!xWinOpt
|| !xWinOpt
->Exists())
362 xWinOpt
.reset(new SvtViewOptions(EViewType::Window
, OUString::number(nId
)));
364 if (xWinOpt
->Exists() && xWinOpt
->HasVisible() )
365 rInfo
.bVisible
= xWinOpt
->IsVisible(); // set state from configuration. Can be overwritten by UserData, see below
367 css::uno::Sequence
< css::beans::NamedValue
> aSeq
= xWinOpt
->GetUserData();
370 if ( aSeq
.hasElements() )
371 aSeq
[0].Value
>>= aTmp
;
373 OUString
aWinData( aTmp
);
374 rInfo
.aWinState
= OUStringToOString(xWinOpt
->GetWindowState(), RTL_TEXTENCODING_UTF8
);
376 if ( aWinData
.isEmpty() )
379 // Search for version ID
380 if ( aWinData
[0] != 0x0056 ) // 'V' = 56h
381 // A version ID, so do not use
385 aWinData
= aWinData
.copy(1);
389 sal_Int32 nPos
= aWinData
.indexOf( cToken
);
390 sal_uInt16 nActVersion
= static_cast<sal_uInt16
>(aWinData
.copy( 0, nPos
+ 1 ).toInt32());
391 if ( nActVersion
!= nVersion
)
394 aWinData
= aWinData
.copy(nPos
+1);
396 // Load Visibility: is coded as a char
397 rInfo
.bVisible
= (aWinData
[0] == 0x0056); // 'V' = 56h
398 aWinData
= aWinData
.copy(1);
399 nPos
= aWinData
.indexOf( cToken
);
403 sal_Int32 nNextPos
= aWinData
.indexOf( cToken
, 2 );
404 if ( nNextPos
!= -1 )
406 // there is extra information
407 rInfo
.nFlags
= static_cast<SfxChildWindowFlags
>(static_cast<sal_uInt16
>(aWinData
.copy( nPos
+1, nNextPos
- nPos
- 1 ).toInt32()));
408 aWinData
= aWinData
.replaceAt( nPos
, nNextPos
-nPos
+1, u
"" );
409 rInfo
.aExtraString
= aWinData
;
412 rInfo
.nFlags
= static_cast<SfxChildWindowFlags
>(static_cast<sal_uInt16
>(aWinData
.copy( nPos
+1 ).toInt32()));
415 bool ParentIsFloatingWindow(const vcl::Window
*pParent
)
419 if (pParent
->GetType() == WindowType::DOCKINGWINDOW
|| pParent
->GetType() == WindowType::TOOLBOX
)
420 return static_cast<const DockingWindow
*>(pParent
)->GetFloatingWindow() != nullptr;
421 if (pParent
->GetType() == WindowType::FLOATINGWINDOW
)
426 void SfxChildWindow::SetFactory_Impl( const SfxChildWinFactory
*pF
)
431 void SfxChildWindow::SetHideNotDelete( bool bOn
)
433 pImpl
->bHideNotDelete
= bOn
;
436 bool SfxChildWindow::IsHideNotDelete() const
438 return pImpl
->bHideNotDelete
;
441 void SfxChildWindow::SetWantsFocus( bool bSet
)
443 pImpl
->bWantsFocus
= bSet
;
446 bool SfxChildWindow::WantsFocus() const
448 return pImpl
->bWantsFocus
;
451 bool SfxChildWinInfo::GetExtraData_Impl
453 SfxChildAlignment
*pAlign
457 if ( aExtraString
.isEmpty() )
460 sal_Int32 nPos
= aExtraString
.indexOf("AL:");
464 // Try to read the alignment string "ALIGN :(...)", but if
465 // it is not present, then use an older version
466 sal_Int32 n1
= aExtraString
.indexOf('(', nPos
);
469 sal_Int32 n2
= aExtraString
.indexOf(')', n1
);
472 // Cut out Alignment string
473 aStr
= aExtraString
.copy(nPos
, n2
- nPos
+ 1);
474 aStr
= aStr
.replaceAt(nPos
, n1
-nPos
+1, u
"");
478 // First extract the Alignment
479 if ( aStr
.isEmpty() )
482 *pAlign
= static_cast<SfxChildAlignment
>(static_cast<sal_uInt16
>(aStr
.toInt32()));
484 // then the LastAlignment
485 nPos
= aStr
.indexOf(',');
488 aStr
= aStr
.copy(nPos
+1);
490 // Then the splitting information
491 nPos
= aStr
.indexOf(',');
493 // No docking in a Splitwindow
495 aStr
= aStr
.copy(nPos
+1);
498 return GetPosSizeFromString( aStr
, aChildPos
, aChildSize
);
501 bool SfxChildWindow::IsVisible() const
503 return pImpl
->bVisible
;
506 void SfxChildWindow::SetVisible_Impl( bool bVis
)
508 pImpl
->bVisible
= bVis
;
511 void SfxChildWindow::Hide()
514 xController
->EndDialog();
519 void SfxChildWindow::Show( ShowFlags nFlags
)
523 if (!xController
->getDialog()->get_visible())
525 weld::DialogController::runAsync(xController
,
526 [this](sal_Int32
/*nResult*/){ xController
->Close(); });
530 pWindow
->Show(true, nFlags
);
533 void SfxChildWindow::SetWorkWindow_Impl( SfxWorkWindow
* pWin
)
535 pImpl
->pWorkWin
= pWin
;
538 if ( (xController
&& xController
->getDialog()->has_toplevel_focus()) ||
539 (pWindow
&& pWindow
->HasChildPathFocus()) )
541 pImpl
->pWorkWin
->SetActiveChild_Impl( pWindow
);
546 void SfxChildWindow::Activate_Impl()
548 if(pImpl
->pWorkWin
!=nullptr)
549 pImpl
->pWorkWin
->SetActiveChild_Impl( pWindow
);
552 bool SfxChildWindow::QueryClose()
556 if ( pImpl
->xFrame
.is() )
558 css::uno::Reference
< css::frame::XController
> xCtrl
= pImpl
->xFrame
->getController();
560 bAllow
= xCtrl
->suspend( true );
567 weld::Dialog
* pDialog
= GetController()->getDialog();
568 bAllow
= !pDialog
->get_visible() || !pDialog
->get_modal();
570 else if (GetWindow())
571 bAllow
= !GetWindow()->IsInModalMode();
577 const css::uno::Reference
< css::frame::XFrame
>& SfxChildWindow::GetFrame() const
579 return pImpl
->xFrame
;
582 void SfxChildWindow::SetFrame( const css::uno::Reference
< css::frame::XFrame
> & rFrame
)
584 // Do nothing if nothing will be changed ...
585 if( pImpl
->xFrame
== rFrame
)
588 // ... but stop listening on old frame, if connection exist!
589 if( pImpl
->xFrame
.is() )
590 pImpl
->xFrame
->removeEventListener( pImpl
->xListener
);
592 // If new frame is not NULL -> we must guarantee valid listener for disposing events.
593 // Use already existing or create new one.
595 if( !pImpl
->xListener
.is() )
596 pImpl
->xListener
.set( new DisposeListener( this, pImpl
.get() ) );
598 // Set new frame in data container
599 // and build new listener connection, if necessary.
600 pImpl
->xFrame
= rFrame
;
601 if( pImpl
->xFrame
.is() )
602 pImpl
->xFrame
->addEventListener( pImpl
->xListener
);
605 void SfxChildWindow::RegisterChildWindow(SfxModule
* pMod
, const SfxChildWinFactory
& rFact
)
607 SfxGetpApp()->RegisterChildWindow_Impl( pMod
, rFact
);
610 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */