1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <memory>
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>
31
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>
41
42 const sal_uInt16 nVersion = 2;
43
SfxChildWinFactory(SfxChildWinCtor pTheCtor,sal_uInt16 nID,sal_uInt16 n)44 SfxChildWinFactory::SfxChildWinFactory( SfxChildWinCtor pTheCtor, sal_uInt16 nID,
45 sal_uInt16 n )
46 : pCtor(pTheCtor)
47 , nId( nID )
48 , nPos(n)
49 {}
50
~SfxChildWinFactory()51 SfxChildWinFactory::~SfxChildWinFactory()
52 {
53 }
54
55 struct SfxChildWindow_Impl
56 {
57 css::uno::Reference< css::frame::XFrame > xFrame;
58 css::uno::Reference< css::lang::XEventListener > xListener;
59 SfxChildWinFactory* pFact;
60 bool bHideNotDelete;
61 bool bVisible;
62 bool bWantsFocus;
63 SfxWorkWindow* pWorkWin;
64 };
65
66 namespace {
67
68 class DisposeListener : public ::cppu::WeakImplHelper< css::lang::XEventListener >
69 {
70 public:
DisposeListener(SfxChildWindow * pOwner,SfxChildWindow_Impl * pData)71 DisposeListener( SfxChildWindow* pOwner ,
72 SfxChildWindow_Impl* pData )
73 : m_pOwner( pOwner )
74 , m_pData ( pData )
75 {}
76
disposing(const css::lang::EventObject & aSource)77 virtual void SAL_CALL disposing( const css::lang::EventObject& aSource ) override
78 {
79 css::uno::Reference< css::lang::XEventListener > xSelfHold( this );
80
81 css::uno::Reference< css::lang::XComponent > xComp( aSource.Source, css::uno::UNO_QUERY );
82 if( xComp.is() )
83 xComp->removeEventListener( this );
84
85 if( !m_pOwner || !m_pData )
86 return;
87
88 m_pData->xListener.clear();
89
90 if ( m_pData->pWorkWin )
91 {
92 // m_pOwner and m_pData will be killed
93 m_pData->xFrame.clear();
94 m_pData->pWorkWin->GetBindings().Execute( m_pOwner->GetType() );
95 }
96 else
97 {
98 delete m_pOwner;
99 }
100
101 m_pOwner = nullptr;
102 m_pData = nullptr;
103 }
104
105 private:
106 SfxChildWindow* m_pOwner;
107 SfxChildWindow_Impl* m_pData ;
108 };
109
110 }
111
GetPosSizeFromString(const OUString & rStr,Point & rPos,Size & rSize)112 bool GetPosSizeFromString( const OUString& rStr, Point& rPos, Size& rSize )
113 {
114 if ( comphelper::string::getTokenCount(rStr, '/') != 4 )
115 return false;
116
117 sal_Int32 nIdx = 0;
118 rPos.setX( rStr.getToken(0, '/', nIdx).toInt32() );
119 rPos.setY( rStr.getToken(0, '/', nIdx).toInt32() );
120 rSize.setWidth( rStr.getToken(0, '/', nIdx).toInt32() );
121 rSize.setHeight( rStr.getToken(0, '/', nIdx).toInt32() );
122
123 // negative sizes are invalid
124 return rSize.Width() >= 0 && rSize.Height() >= 0;
125 }
126
GetSplitSizeFromString(const OUString & rStr,Size & rSize)127 bool GetSplitSizeFromString( const OUString& rStr, Size& rSize )
128 {
129 sal_Int32 nIndex = rStr.indexOf( ',' );
130 if ( nIndex != -1 )
131 {
132 OUString aStr = rStr.copy( nIndex+1 );
133
134 sal_Int32 nCount = comphelper::string::getTokenCount(aStr, ';');
135 if ( nCount != 2 )
136 return false;
137
138 sal_Int32 nIdx{ 0 };
139 rSize.setWidth( aStr.getToken(0, ';', nIdx ).toInt32() );
140 rSize.setHeight( aStr.getToken(0, ';', nIdx ).toInt32() );
141
142 // negative sizes are invalid
143 return rSize.Width() >= 0 && rSize.Height() >= 0;
144 }
145
146 return false;
147 }
148
SfxChildWindow(vcl::Window * pParentWindow,sal_uInt16 nId)149 SfxChildWindow::SfxChildWindow(vcl::Window *pParentWindow, sal_uInt16 nId)
150 : pParent(pParentWindow)
151 , nType(nId)
152 , eChildAlignment(SfxChildAlignment::NOALIGNMENT)
153 , pImpl(new SfxChildWindow_Impl)
154 {
155 pImpl->pFact = nullptr;
156 pImpl->bHideNotDelete = false;
157 pImpl->bWantsFocus = true;
158 pImpl->bVisible = true;
159 pImpl->pWorkWin = nullptr;
160 }
161
Destroy()162 void SfxChildWindow::Destroy()
163 {
164 if ( GetFrame().is() )
165 {
166 ClearWorkwin();
167 try
168 {
169 css::uno::Reference < css::util::XCloseable > xClose( GetFrame(), css::uno::UNO_QUERY );
170 if ( xClose.is() )
171 xClose->close( true );
172 else
173 GetFrame()->dispose();
174 }
175 catch (const css::uno::Exception&)
176 {
177 }
178 }
179 else
180 delete this;
181 }
182
ClearWorkwin()183 void SfxChildWindow::ClearWorkwin()
184 {
185 if (pImpl->pWorkWin)
186 {
187 if (pImpl->pWorkWin->GetActiveChild_Impl() == pWindow)
188 pImpl->pWorkWin->SetActiveChild_Impl(nullptr);
189 pImpl->pWorkWin = nullptr;
190 }
191 }
192
~SfxChildWindow()193 SfxChildWindow::~SfxChildWindow()
194 {
195 ClearWorkwin();
196 if (xController)
197 {
198 xController->ChildWinDispose();
199 xController.reset();
200 }
201 pWindow.disposeAndClear();
202 }
203
CreateChildWindow(sal_uInt16 nId,vcl::Window * pParent,SfxBindings * pBindings,SfxChildWinInfo const & rInfo)204 std::unique_ptr<SfxChildWindow> SfxChildWindow::CreateChildWindow( sal_uInt16 nId,
205 vcl::Window *pParent, SfxBindings* pBindings, SfxChildWinInfo const & rInfo)
206 {
207 std::unique_ptr<SfxChildWindow> pChild;
208 SfxChildWinFactory* pFact=nullptr;
209 SystemWindowFlags nOldMode = Application::GetSystemWindowMode();
210
211 // First search for ChildWindow in SDT; Overlay windows are realized
212 // by using ChildWindowContext
213 SfxApplication *pApp = SfxGetpApp();
214 {
215 SfxChildWinFactArr_Impl &rFactories = pApp->GetChildWinFactories_Impl();
216 for ( size_t nFactory = 0; nFactory < rFactories.size(); ++nFactory )
217 {
218 pFact = &rFactories[nFactory];
219 if ( pFact->nId == nId )
220 {
221 SfxChildWinInfo& rFactInfo = pFact->aInfo;
222 if ( rInfo.bVisible )
223 {
224 if ( pBindings )
225 pBindings->ENTERREGISTRATIONS();
226 SfxChildWinInfo aInfo = rFactInfo;
227 Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE );
228 pChild = pFact->pCtor( pParent, nId, pBindings, &aInfo );
229 Application::SetSystemWindowMode( nOldMode );
230 if ( pBindings )
231 pBindings->LEAVEREGISTRATIONS();
232 }
233
234 break;
235 }
236 }
237 }
238
239 SfxDispatcher *pDisp = pBindings ? pBindings->GetDispatcher_Impl() : nullptr;
240 SfxModule *pMod = pDisp ? SfxModule::GetActiveModule( pDisp->GetFrame() ) : nullptr;
241 if (!pChild && pMod)
242 {
243 SfxChildWinFactArr_Impl *pFactories = pMod->GetChildWinFactories_Impl();
244 if ( pFactories )
245 {
246 SfxChildWinFactArr_Impl &rFactories = *pFactories;
247 for ( size_t nFactory = 0; nFactory < rFactories.size(); ++nFactory )
248 {
249 pFact = &rFactories[nFactory];
250 if ( pFact->nId == nId )
251 {
252 SfxChildWinInfo& rFactInfo = pFact->aInfo;
253 if ( rInfo.bVisible )
254 {
255 if ( pBindings )
256 pBindings->ENTERREGISTRATIONS();
257 SfxChildWinInfo aInfo = rFactInfo;
258 Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE );
259 pChild = pFact->pCtor( pParent, nId, pBindings, &aInfo );
260 Application::SetSystemWindowMode( nOldMode );
261 if ( pBindings )
262 pBindings->LEAVEREGISTRATIONS();
263 }
264
265 break;
266 }
267 }
268 }
269 }
270
271 if ( pChild )
272 pChild->SetFactory_Impl( pFact );
273
274 DBG_ASSERT(pFact && (pChild || !rInfo.bVisible), "ChildWindow-Typ not registered!");
275
276 if (pChild && (!pChild->pWindow && !pChild->xController))
277 {
278 pChild.reset();
279 SAL_INFO("sfx.appl", "ChildWindow has no Window!");
280 }
281
282 return pChild;
283 }
284
285
SaveStatus(const SfxChildWinInfo & rInfo)286 void SfxChildWindow::SaveStatus(const SfxChildWinInfo& rInfo)
287 {
288 sal_uInt16 nID = GetType();
289
290 OUString aInfoVisible = rInfo.bVisible ? OUString("V") : OUString("H");
291
292 OUString aWinData = "V"
293 + OUString::number(static_cast<sal_Int32>(nVersion))
294 + ","
295 + aInfoVisible
296 + ","
297 + OUString::number(static_cast<sal_Int32>(rInfo.nFlags));
298
299 if ( !rInfo.aExtraString.isEmpty() )
300 aWinData += "," + rInfo.aExtraString;
301
302 OUString sName(OUString::number(nID));
303 //Try and save window state per-module, e.g. sidebar on in one application
304 //but off in another
305 if (!rInfo.aModule.isEmpty())
306 sName = rInfo.aModule + "/" + sName;
307 SvtViewOptions aWinOpt(EViewType::Window, sName);
308 aWinOpt.SetWindowState(OStringToOUString(rInfo.aWinState, RTL_TEXTENCODING_UTF8));
309
310 css::uno::Sequence < css::beans::NamedValue > aSeq
311 { { "Data", css::uno::makeAny(aWinData) } };
312 aWinOpt.SetUserData( aSeq );
313
314 // ... but save status at runtime!
315 pImpl->pFact->aInfo = rInfo;
316 }
317
SetAlignment(SfxChildAlignment eAlign)318 void SfxChildWindow::SetAlignment(SfxChildAlignment eAlign)
319 {
320 eChildAlignment = eAlign;
321 }
322
GetInfo() const323 SfxChildWinInfo SfxChildWindow::GetInfo() const
324 {
325 SfxChildWinInfo aInfo(pImpl->pFact->aInfo);
326 if (xController)
327 {
328 weld::Dialog* pDialog = xController->getDialog();
329 aInfo.aPos = pDialog->get_position();
330 aInfo.aSize = pDialog->get_size();
331 WindowStateMask nMask = WindowStateMask::Pos | WindowStateMask::State;
332 if (pDialog->get_resizable())
333 nMask |= WindowStateMask::Width | WindowStateMask::Height;
334 aInfo.aWinState = pDialog->get_window_state(nMask);
335 }
336 else if (pWindow)
337 {
338 aInfo.aPos = pWindow->GetPosPixel();
339 aInfo.aSize = pWindow->GetSizePixel();
340 if ( pWindow->IsSystemWindow() )
341 {
342 WindowStateMask nMask = WindowStateMask::Pos | WindowStateMask::State;
343 if ( pWindow->GetStyle() & WB_SIZEABLE )
344 nMask |= WindowStateMask::Width | WindowStateMask::Height;
345 aInfo.aWinState = static_cast<SystemWindow*>(pWindow.get())->GetWindowState( nMask );
346 }
347 else if (DockingWindow* pDockingWindow = dynamic_cast<DockingWindow*>(pWindow.get()))
348 {
349 if (pDockingWindow->GetFloatingWindow())
350 aInfo.aWinState = pDockingWindow->GetFloatingWindow()->GetWindowState();
351 else if (SfxDockingWindow* pSfxDockingWindow = dynamic_cast<SfxDockingWindow*>(pDockingWindow))
352 {
353 SfxChildWinInfo aTmpInfo;
354 pSfxDockingWindow->FillInfo( aTmpInfo );
355 aInfo.aExtraString = aTmpInfo.aExtraString;
356 }
357 }
358 }
359
360 aInfo.bVisible = pImpl->bVisible;
361 aInfo.nFlags = SfxChildWindowFlags::NONE;
362 return aInfo;
363 }
364
GetPosition() const365 sal_uInt16 SfxChildWindow::GetPosition() const
366 {
367 return pImpl->pFact->nPos;
368 }
369
InitializeChildWinFactory_Impl(sal_uInt16 nId,SfxChildWinInfo & rInfo)370 void SfxChildWindow::InitializeChildWinFactory_Impl(sal_uInt16 nId, SfxChildWinInfo& rInfo)
371 {
372 // load configuration
373
374 std::unique_ptr<SvtViewOptions> xWinOpt;
375 // first see if a module specific id exists
376 if (rInfo.aModule.getLength())
377 xWinOpt.reset(new SvtViewOptions(EViewType::Window, rInfo.aModule + "/" + OUString::number(nId)));
378
379 // if not then try the generic id
380 if (!xWinOpt || !xWinOpt->Exists())
381 xWinOpt.reset(new SvtViewOptions(EViewType::Window, OUString::number(nId)));
382
383 if (xWinOpt->Exists() && xWinOpt->HasVisible() )
384 rInfo.bVisible = xWinOpt->IsVisible(); // set state from configuration. Can be overwritten by UserData, see below
385
386 css::uno::Sequence < css::beans::NamedValue > aSeq = xWinOpt->GetUserData();
387
388 OUString aTmp;
389 if ( aSeq.hasElements() )
390 aSeq[0].Value >>= aTmp;
391
392 OUString aWinData( aTmp );
393 rInfo.aWinState = OUStringToOString(xWinOpt->GetWindowState(), RTL_TEXTENCODING_UTF8);
394
395 if ( aWinData.isEmpty() )
396 return;
397
398 // Search for version ID
399 if ( aWinData[0] != 0x0056 ) // 'V' = 56h
400 // A version ID, so do not use
401 return;
402
403 // Delete 'V'
404 aWinData = aWinData.copy(1);
405
406 // Read version
407 char cToken = ',';
408 sal_Int32 nPos = aWinData.indexOf( cToken );
409 sal_uInt16 nActVersion = static_cast<sal_uInt16>(aWinData.copy( 0, nPos + 1 ).toInt32());
410 if ( nActVersion != nVersion )
411 return;
412
413 aWinData = aWinData.copy(nPos+1);
414
415 // Load Visibility: is coded as a char
416 rInfo.bVisible = (aWinData[0] == 0x0056); // 'V' = 56h
417 aWinData = aWinData.copy(1);
418 nPos = aWinData.indexOf( cToken );
419 if (nPos == -1)
420 return;
421
422 sal_Int32 nNextPos = aWinData.indexOf( cToken, 2 );
423 if ( nNextPos != -1 )
424 {
425 // there is extra information
426 rInfo.nFlags = static_cast<SfxChildWindowFlags>(static_cast<sal_uInt16>(aWinData.copy( nPos+1, nNextPos - nPos - 1 ).toInt32()));
427 aWinData = aWinData.replaceAt( nPos, nNextPos-nPos+1, "" );
428 rInfo.aExtraString = aWinData;
429 }
430 else
431 rInfo.nFlags = static_cast<SfxChildWindowFlags>(static_cast<sal_uInt16>(aWinData.copy( nPos+1 ).toInt32()));
432 }
433
ParentIsFloatingWindow(vcl::Window * pParent)434 bool ParentIsFloatingWindow(vcl::Window *pParent)
435 {
436 if (!pParent)
437 return false;
438 if (pParent->GetType() == WindowType::DOCKINGWINDOW || pParent->GetType() == WindowType::TOOLBOX)
439 return static_cast<DockingWindow*>(pParent)->GetFloatingWindow() != nullptr;
440 if (pParent->GetType() == WindowType::FLOATINGWINDOW)
441 return true;
442 return false;
443 }
444
SetFactory_Impl(SfxChildWinFactory * pF)445 void SfxChildWindow::SetFactory_Impl( SfxChildWinFactory *pF )
446 {
447 pImpl->pFact = pF;
448 }
449
SetHideNotDelete(bool bOn)450 void SfxChildWindow::SetHideNotDelete( bool bOn )
451 {
452 pImpl->bHideNotDelete = bOn;
453 }
454
IsHideNotDelete() const455 bool SfxChildWindow::IsHideNotDelete() const
456 {
457 return pImpl->bHideNotDelete;
458 }
459
SetWantsFocus(bool bSet)460 void SfxChildWindow::SetWantsFocus( bool bSet )
461 {
462 pImpl->bWantsFocus = bSet;
463 }
464
WantsFocus() const465 bool SfxChildWindow::WantsFocus() const
466 {
467 return pImpl->bWantsFocus;
468 }
469
GetExtraData_Impl(SfxChildAlignment * pAlign) const470 bool SfxChildWinInfo::GetExtraData_Impl
471 (
472 SfxChildAlignment *pAlign
473 ) const
474 {
475 // invalid?
476 if ( aExtraString.isEmpty() )
477 return false;
478 OUString aStr;
479 sal_Int32 nPos = aExtraString.indexOf("AL:");
480 if ( nPos == -1 )
481 return false;
482
483 // Try to read the alignment string "ALIGN :(...)", but if
484 // it is not present, then use an older version
485 sal_Int32 n1 = aExtraString.indexOf('(', nPos);
486 if ( n1 != -1 )
487 {
488 sal_Int32 n2 = aExtraString.indexOf(')', n1);
489 if ( n2 != -1 )
490 {
491 // Cut out Alignment string
492 aStr = aExtraString.copy(nPos, n2 - nPos + 1);
493 aStr = aStr.replaceAt(nPos, n1-nPos+1, "");
494 }
495 }
496
497 // First extract the Alignment
498 if ( aStr.isEmpty() )
499 return false;
500 if ( pAlign )
501 *pAlign = static_cast<SfxChildAlignment>(static_cast<sal_uInt16>(aStr.toInt32()));
502
503 // then the LastAlignment
504 nPos = aStr.indexOf(',');
505 if ( nPos == -1 )
506 return false;
507 aStr = aStr.copy(nPos+1);
508
509 // Then the splitting information
510 nPos = aStr.indexOf(',');
511 if ( nPos == -1 )
512 // No docking in a Splitwindow
513 return true;
514 aStr = aStr.copy(nPos+1);
515 Point aChildPos;
516 Size aChildSize;
517 return GetPosSizeFromString( aStr, aChildPos, aChildSize );
518 }
519
IsVisible() const520 bool SfxChildWindow::IsVisible() const
521 {
522 return pImpl->bVisible;
523 }
524
SetVisible_Impl(bool bVis)525 void SfxChildWindow::SetVisible_Impl( bool bVis )
526 {
527 pImpl->bVisible = bVis;
528 }
529
Hide()530 void SfxChildWindow::Hide()
531 {
532 if (xController)
533 xController->EndDialog();
534 else
535 pWindow->Hide();
536 }
537
Show(ShowFlags nFlags)538 void SfxChildWindow::Show( ShowFlags nFlags )
539 {
540 if (xController)
541 {
542 if (!xController->getDialog()->get_visible())
543 {
544 weld::DialogController::runAsync(xController,
545 [this](sal_Int32 /*nResult*/){ xController->Close(); });
546 }
547 }
548 else
549 pWindow->Show(true, nFlags);
550 }
551
SetWorkWindow_Impl(SfxWorkWindow * pWin)552 void SfxChildWindow::SetWorkWindow_Impl( SfxWorkWindow* pWin )
553 {
554 pImpl->pWorkWin = pWin;
555 if (pWin)
556 {
557 if ( (xController && xController->getDialog()->has_toplevel_focus()) ||
558 (pWindow && pWindow->HasChildPathFocus()) )
559 {
560 pImpl->pWorkWin->SetActiveChild_Impl( pWindow );
561 }
562 }
563 }
564
Activate_Impl()565 void SfxChildWindow::Activate_Impl()
566 {
567 if(pImpl->pWorkWin!=nullptr)
568 pImpl->pWorkWin->SetActiveChild_Impl( pWindow );
569 }
570
QueryClose()571 bool SfxChildWindow::QueryClose()
572 {
573 bool bAllow = true;
574
575 if ( pImpl->xFrame.is() )
576 {
577 css::uno::Reference< css::frame::XController > xCtrl = pImpl->xFrame->getController();
578 if ( xCtrl.is() )
579 bAllow = xCtrl->suspend( true );
580 }
581
582 if ( bAllow )
583 {
584 if (GetController())
585 {
586 weld::Dialog* pDialog = GetController()->getDialog();
587 bAllow = !pDialog->get_visible() || !pDialog->get_modal();
588 }
589 else if (GetWindow())
590 bAllow = !GetWindow()->IsInModalMode();
591 }
592
593 return bAllow;
594 }
595
GetFrame() const596 const css::uno::Reference< css::frame::XFrame >& SfxChildWindow::GetFrame() const
597 {
598 return pImpl->xFrame;
599 }
600
SetFrame(const css::uno::Reference<css::frame::XFrame> & rFrame)601 void SfxChildWindow::SetFrame( const css::uno::Reference< css::frame::XFrame > & rFrame )
602 {
603 // Do nothing if nothing will be changed ...
604 if( pImpl->xFrame == rFrame )
605 return;
606
607 // ... but stop listening on old frame, if connection exist!
608 if( pImpl->xFrame.is() )
609 pImpl->xFrame->removeEventListener( pImpl->xListener );
610
611 // If new frame is not NULL -> we must guarantee valid listener for disposing events.
612 // Use already existing or create new one.
613 if( rFrame.is() )
614 if( !pImpl->xListener.is() )
615 pImpl->xListener.set( new DisposeListener( this, pImpl.get() ) );
616
617 // Set new frame in data container
618 // and build new listener connection, if necessary.
619 pImpl->xFrame = rFrame;
620 if( pImpl->xFrame.is() )
621 pImpl->xFrame->addEventListener( pImpl->xListener );
622 }
623
RegisterChildWindow(SfxModule * pMod,std::unique_ptr<SfxChildWinFactory> pFact)624 void SfxChildWindow::RegisterChildWindow(SfxModule* pMod, std::unique_ptr<SfxChildWinFactory> pFact)
625 {
626 SfxGetpApp()->RegisterChildWindow_Impl( pMod, std::move(pFact) );
627 }
628
629 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
630