cid#1640468 Dereference after null check
[LibreOffice.git] / sfx2 / source / appl / childwin.cxx
blob24b114a706b32d6f82ef1118ec027bda75f96b1b
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 <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 <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,
46 sal_uInt16 n )
47 : pCtor(pTheCtor)
48 , nId( nID )
49 , nPos(n)
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 };
57 bool bHideNotDelete;
58 bool bVisible;
59 bool bWantsFocus;
60 SfxWorkWindow* pWorkWin;
63 namespace {
65 class DisposeListener : public ::cppu::WeakImplHelper< css::lang::XEventListener >
67 public:
68 DisposeListener( SfxChildWindow* pOwner ,
69 SfxChildWindow_Impl* pData )
70 : m_pOwner( pOwner )
71 , m_pData ( 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 );
79 if( xComp.is() )
80 xComp->removeEventListener( this );
82 if( !m_pOwner || !m_pData )
83 return;
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() );
93 else
95 delete m_pOwner;
98 m_pOwner = nullptr;
99 m_pData = nullptr;
102 private:
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 )
112 return false;
114 sal_Int32 nIdx = 0;
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, ';');
132 if ( nCount != 2 )
133 return false;
135 sal_Int32 nIdx{ 0 };
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;
143 return false;
146 SfxChildWindow::SfxChildWindow(vcl::Window *pParentWindow, sal_uInt16 nId)
147 : pParent(pParentWindow)
148 , pImpl(new SfxChildWindow_Impl)
149 , eChildAlignment(SfxChildAlignment::NOALIGNMENT)
150 , nType(nId)
152 pImpl->bHideNotDelete = false;
153 pImpl->bWantsFocus = true;
154 pImpl->bVisible = true;
155 pImpl->pWorkWin = nullptr;
158 void SfxChildWindow::Destroy()
160 if ( GetFrame().is() )
162 ClearWorkwin();
165 css::uno::Reference < css::util::XCloseable > xClose( GetFrame(), css::uno::UNO_QUERY );
166 if ( xClose.is() )
167 xClose->close( true );
168 else
169 GetFrame()->dispose();
171 catch (const css::uno::Exception&)
175 else
176 delete this;
179 void SfxChildWindow::ClearWorkwin()
181 if (pImpl->pWorkWin)
183 if (pImpl->pWorkWin->GetActiveChild_Impl() == pWindow)
184 pImpl->pWorkWin->SetActiveChild_Impl(nullptr);
185 pImpl->pWorkWin = nullptr;
189 SfxChildWindow::~SfxChildWindow()
191 ClearWorkwin();
192 if (xController)
194 xController->ChildWinDispose();
195 xController.reset();
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);
212 if ( pFact )
214 if ( rInfo.bVisible )
216 if ( pBindings )
217 pBindings->ENTERREGISTRATIONS();
218 SfxChildWinInfo aInfo = rInfo;
219 Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE );
220 pChild = pFact->pCtor( pParent, nId, pBindings, &aInfo );
221 Application::SetSystemWindowMode( nOldMode );
222 if ( pBindings )
223 pBindings->LEAVEREGISTRATIONS();
228 SfxDispatcher *pDisp = pBindings ? pBindings->GetDispatcher_Impl() : nullptr;
229 SfxModule *pMod = pDisp ? SfxModule::GetActiveModule( pDisp->GetFrame() ) : nullptr;
230 if (!pChild && pMod)
232 pFact = pMod->GetChildWinFactoryById(nId);
233 if ( pFact )
235 if ( rInfo.bVisible )
237 if ( pBindings )
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 );
244 if ( pBindings )
245 pBindings->LEAVEREGISTRATIONS();
250 if (pChild)
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))
260 pChild.reset();
261 SAL_INFO("sfx.appl", "ChildWindow has no Window!");
264 return pChild;
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))
276 + ","
277 + aInfoVisible
278 + ","
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
286 //but off in another
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);
308 if (xController)
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);
318 else if (pWindow)
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;
344 return aInfo;
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();
370 OUString aTmp;
371 if ( aSeq.hasElements() )
372 aSeq[0].Value >>= aTmp;
374 OUString aWinData( aTmp );
375 rInfo.aWinState = xWinOpt->GetWindowState();
377 if ( aWinData.isEmpty() )
378 return;
380 // Search for version ID
381 if ( aWinData[0] != 0x0056 ) // 'V' = 56h
382 // A version ID, so do not use
383 return;
385 // Delete 'V'
386 aWinData = aWinData.copy(1);
388 // Read version
389 char cToken = ',';
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 )
393 return;
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 );
401 if (nPos == -1)
402 return;
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;
412 else
413 rInfo.nFlags = static_cast<SfxChildWindowFlags>(static_cast<sal_uInt16>(o3tl::toInt32(aWinData.subView( nPos+1 ))));
416 bool ParentIsFloatingWindow(const vcl::Window *pParent)
418 if (!pParent)
419 return false;
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)
423 return true;
424 return false;
427 void SfxChildWindow::SetFactory_Impl( const SfxChildWinFactory *pF )
429 pImpl->aFact = *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
455 ) const
457 // invalid?
458 if ( aExtraString.isEmpty() )
459 return false;
460 OUString aStr;
461 sal_Int32 nPos = aExtraString.indexOf("AL:");
462 if ( nPos == -1 )
463 return false;
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);
468 if ( n1 != -1 )
470 sal_Int32 n2 = aExtraString.indexOf(')', n1);
471 if ( n2 != -1 )
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() )
481 return false;
482 if ( pAlign )
483 *pAlign = static_cast<SfxChildAlignment>(static_cast<sal_uInt16>(aStr.toInt32()));
485 // then the LastAlignment
486 nPos = aStr.indexOf(',');
487 if ( nPos == -1 )
488 return false;
489 aStr = aStr.copy(nPos+1);
491 // Then the splitting information
492 nPos = aStr.indexOf(',');
493 if ( nPos == -1 )
494 // No docking in a Splitwindow
495 return true;
496 aStr = aStr.copy(nPos+1);
497 Point aChildPos;
498 Size aChildSize;
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()
514 if (xController)
515 xController->EndDialog(nCloseResponseToJustHide);
516 else
517 pWindow->Hide();
520 void SfxChildWindow::Show( ShowFlags nFlags )
522 if (xController)
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();
532 else
534 weld::DialogController::runAsync(xController,
535 [this](sal_Int32 nResult) {
536 if (nResult == nCloseResponseToJustHide)
537 return;
538 xController->Close();
543 else
544 pWindow->Show(true, nFlags);
547 void SfxChildWindow::SetWorkWindow_Impl( SfxWorkWindow* pWin )
549 pImpl->pWorkWin = pWin;
550 if (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()
568 bool bAllow = true;
570 if ( pImpl->xFrame.is() )
572 css::uno::Reference< css::frame::XController > xCtrl = pImpl->xFrame->getController();
573 if ( xCtrl.is() )
574 bAllow = xCtrl->suspend( true );
577 if ( bAllow )
579 if (GetController())
581 weld::Dialog* pDialog = GetController()->getDialog();
582 bAllow = !pDialog->get_visible() || !pDialog->get_modal();
584 else if (GetWindow())
585 bAllow = !GetWindow()->IsInModalMode();
588 return bAllow;
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 )
600 return;
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.
608 if( rFrame.is() )
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: */