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 <svl/eitem.hxx>
21 #include <svl/solar.hrc>
22 #include <vcl/event.hxx>
23 #include <vcl/settings.hxx>
24 
25 #include <vcl/svapp.hxx>
26 #include <vcl/timer.hxx>
27 #include <vcl/idle.hxx>
28 #include <o3tl/safeint.hxx>
29 #include <osl/diagnose.h>
30 #include <rtl/instance.hxx>
31 #include <toolkit/helper/vclunohelper.hxx>
32 #include <tools/debug.hxx>
33 #include <comphelper/processfactory.hxx>
34 #include <comphelper/propertysequence.hxx>
35 
36 #include <sfx2/dockwin.hxx>
37 #include <sfx2/bindings.hxx>
38 #include <sfx2/viewfrm.hxx>
39 #include <sfx2/dispatch.hxx>
40 #include <workwin.hxx>
41 #include <splitwin.hxx>
42 #include <sfx2/viewsh.hxx>
43 
44 #include <com/sun/star/beans/UnknownPropertyException.hpp>
45 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
46 #include <com/sun/star/awt/XWindow.hpp>
47 #include <com/sun/star/uno/XComponentContext.hpp>
48 #include <com/sun/star/frame/ModuleManager.hpp>
49 #include <com/sun/star/container/XNameAccess.hpp>
50 #include <com/sun/star/ui/theWindowStateConfiguration.hpp>
51 #include <com/sun/star/ui/theWindowContentFactoryManager.hpp>
52 
53 #define MAX_TOGGLEAREA_WIDTH        20
54 #define MAX_TOGGLEAREA_HEIGHT       20
55 
56 using namespace ::com::sun::star;
57 
58 // If you want to change the number you also have to:
59 // - Add new slot ids to sfxsids.hrc
60 // - Add new slots to frmslots.sdi
61 // - Add new slot definitions to sfx.sdi
62 const int NUM_OF_DOCKINGWINDOWS = 10;
63 
64 namespace {
65 
66 class SfxTitleDockingWindow : public SfxDockingWindow
67 {
68     VclPtr<vcl::Window>   m_pWrappedWindow;
69 
70 public:
71                         SfxTitleDockingWindow(
72                             SfxBindings* pBindings ,
73                             SfxChildWindow* pChildWin ,
74                             vcl::Window* pParent ,
75                             WinBits nBits);
76     virtual             ~SfxTitleDockingWindow() override;
77     virtual void        dispose() override;
78 
GetWrappedWindow() const79     vcl::Window*        GetWrappedWindow() const { return m_pWrappedWindow; }
80     void                SetWrappedWindow(vcl::Window* const pWindow);
81 
82     virtual void        StateChanged( StateChangedType nType ) override;
83     virtual void        Resize() override;
84     virtual void        Resizing( Size& rSize ) override;
85 };
86 
87     struct WindowState
88     {
89         OUString sTitle;
90     };
91 }
92 
lcl_getWindowState(const uno::Reference<container::XNameAccess> & xWindowStateMgr,const OUString & rResourceURL,WindowState & rWindowState)93 static bool lcl_getWindowState( const uno::Reference< container::XNameAccess >& xWindowStateMgr, const OUString& rResourceURL, WindowState& rWindowState )
94 {
95     bool bResult = false;
96 
97     try
98     {
99         uno::Any a;
100         uno::Sequence< beans::PropertyValue > aWindowState;
101         a = xWindowStateMgr->getByName( rResourceURL );
102         if ( a >>= aWindowState )
103         {
104             for ( const auto& rProp : std::as_const(aWindowState) )
105             {
106                 if ( rProp.Name == "UIName" )
107                 {
108                     rProp.Value >>= rWindowState.sTitle;
109                 }
110             }
111         }
112 
113         bResult = true;
114     }
115     catch ( container::NoSuchElementException& )
116     {
117         bResult = false;
118     }
119 
120     return bResult;
121 }
122 
SfxDockingWrapper(vcl::Window * pParentWnd,sal_uInt16 nId,SfxBindings * pBindings,SfxChildWinInfo * pInfo)123 SfxDockingWrapper::SfxDockingWrapper( vcl::Window* pParentWnd ,
124                                       sal_uInt16 nId ,
125                                       SfxBindings* pBindings ,
126                                       SfxChildWinInfo* pInfo )
127                     : SfxChildWindow( pParentWnd , nId )
128 {
129     uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
130 
131     VclPtr<SfxTitleDockingWindow> pTitleDockWindow = VclPtr<SfxTitleDockingWindow>::Create( pBindings, this, pParentWnd,
132         WB_STDDOCKWIN | WB_CLIPCHILDREN | WB_SIZEABLE | WB_3DLOOK);
133     SetWindow( pTitleDockWindow );
134 
135     // Use factory manager to retrieve XWindow factory. That can be used to instantiate
136     // the real window factory.
137     uno::Reference< lang::XSingleComponentFactory > xFactoryMgr = ui::theWindowContentFactoryManager::get(xContext);
138 
139     SfxDispatcher* pDispatcher = pBindings->GetDispatcher();
140     uno::Reference< frame::XFrame > xFrame = pDispatcher->GetFrame()->GetFrame().GetFrameInterface();
141     // create a resource URL from the nId provided by the sfx2
142     OUString aResourceURL =  "private:resource/dockingwindow/" + OUString::number(nId);
143     uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
144     {
145         {"Frame", uno::Any(xFrame)},
146         {"ResourceURL", uno::Any(aResourceURL)},
147     }));
148 
149     uno::Reference< awt::XWindow > xWindow;
150     try
151     {
152         xWindow.set(
153             xFactoryMgr->createInstanceWithArgumentsAndContext( aArgs, xContext ),
154             uno::UNO_QUERY );
155 
156         static uno::WeakReference< frame::XModuleManager2 >  s_xModuleManager;
157 
158         uno::Reference< frame::XModuleManager2 > xModuleManager( s_xModuleManager );
159         if ( !xModuleManager.is() )
160         {
161             xModuleManager = frame::ModuleManager::create(xContext);
162             s_xModuleManager = xModuleManager;
163         }
164 
165         static uno::WeakReference< container::XNameAccess > s_xWindowStateConfiguration;
166 
167         uno::Reference< container::XNameAccess > xWindowStateConfiguration( s_xWindowStateConfiguration );
168         if ( !xWindowStateConfiguration.is() )
169         {
170             xWindowStateConfiguration = ui::theWindowStateConfiguration::get( xContext );
171             s_xWindowStateConfiguration = xWindowStateConfiguration;
172         }
173 
174         OUString sModuleIdentifier = xModuleManager->identify( xFrame );
175 
176         uno::Reference< container::XNameAccess > xModuleWindowState(
177                                                     xWindowStateConfiguration->getByName( sModuleIdentifier ),
178                                                     uno::UNO_QUERY );
179         if ( xModuleWindowState.is() )
180         {
181             WindowState aDockWinState;
182             if ( lcl_getWindowState( xModuleWindowState, aResourceURL, aDockWinState ))
183                 pTitleDockWindow->SetText( aDockWinState.sTitle );
184         }
185     }
186     catch ( beans::UnknownPropertyException& )
187     {
188     }
189     catch ( uno::RuntimeException& )
190     {
191     }
192     catch ( uno::Exception& )
193     {
194     }
195 
196     VclPtr<vcl::Window> pContentWindow = VCLUnoHelper::GetWindow(xWindow);
197     if ( pContentWindow )
198         pContentWindow->SetStyle( pContentWindow->GetStyle() | WB_DIALOGCONTROL | WB_CHILDDLGCTRL );
199     pTitleDockWindow->SetWrappedWindow(pContentWindow);
200 
201     GetWindow()->SetOutputSizePixel( Size( 270, 240 ) );
202 
203     static_cast<SfxDockingWindow*>( GetWindow() )->Initialize( pInfo );
204     SetHideNotDelete( true );
205 }
206 
CreateImpl(vcl::Window * pParent,sal_uInt16 nId,SfxBindings * pBindings,SfxChildWinInfo * pInfo)207 std::unique_ptr<SfxChildWindow> SfxDockingWrapper::CreateImpl(vcl::Window *pParent, sal_uInt16 nId,
208                                               SfxBindings *pBindings, SfxChildWinInfo* pInfo)
209 {
210     return std::make_unique<SfxDockingWrapper>(pParent, nId, pBindings, pInfo);
211 }
212 
RegisterChildWindow(bool bVis,SfxModule * pMod,SfxChildWindowFlags nFlags)213 void SfxDockingWrapper::RegisterChildWindow (bool bVis, SfxModule *pMod, SfxChildWindowFlags nFlags)
214 {
215     // pre-register a couple of docking windows
216     for (int i=0; i < NUM_OF_DOCKINGWINDOWS; i++ )
217     {
218         sal_uInt16 nID = sal_uInt16(SID_DOCKWIN_START+i);
219         auto pFact = std::make_unique<SfxChildWinFactory>( SfxDockingWrapper::CreateImpl, nID, 0xffff );
220         pFact->aInfo.nFlags |= nFlags;
221         pFact->aInfo.bVisible = bVis;
222         SfxChildWindow::RegisterChildWindow(pMod, std::move(pFact));
223     }
224 }
225 
GetInfo() const226 SfxChildWinInfo  SfxDockingWrapper::GetInfo() const
227 {
228     SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
229     static_cast<SfxDockingWindow*>(GetWindow())->FillInfo( aInfo );
230     return aInfo;
231 };
232 
SfxTitleDockingWindow(SfxBindings * pBind,SfxChildWindow * pChildWin,vcl::Window * pParent,WinBits nBits)233 SfxTitleDockingWindow::SfxTitleDockingWindow(SfxBindings* pBind, SfxChildWindow* pChildWin,
234                                              vcl::Window* pParent, WinBits nBits)
235     : SfxDockingWindow(pBind, pChildWin, pParent, nBits)
236     , m_pWrappedWindow(nullptr)
237 {
238 }
239 
~SfxTitleDockingWindow()240 SfxTitleDockingWindow::~SfxTitleDockingWindow()
241 {
242     disposeOnce();
243 }
244 
dispose()245 void SfxTitleDockingWindow::dispose()
246 {
247     m_pWrappedWindow.disposeAndClear();
248     SfxDockingWindow::dispose();
249 }
250 
SetWrappedWindow(vcl::Window * const pWindow)251 void SfxTitleDockingWindow::SetWrappedWindow( vcl::Window* const pWindow )
252 {
253     m_pWrappedWindow = pWindow;
254     if (m_pWrappedWindow)
255     {
256         m_pWrappedWindow->SetParent(this);
257         m_pWrappedWindow->SetSizePixel( GetOutputSizePixel() );
258         m_pWrappedWindow->Show();
259     }
260 }
261 
StateChanged(StateChangedType nType)262 void SfxTitleDockingWindow::StateChanged( StateChangedType nType )
263 {
264     if ( nType == StateChangedType::InitShow )
265     {
266         vcl::Window* pWindow = GetWrappedWindow();
267         if ( pWindow )
268         {
269             pWindow->SetSizePixel( GetOutputSizePixel() );
270             pWindow->Show();
271         }
272     }
273 
274     SfxDockingWindow::StateChanged(nType);
275 }
276 
Resize()277 void SfxTitleDockingWindow::Resize()
278 {
279     SfxDockingWindow::Resize();
280     if (m_pWrappedWindow)
281         m_pWrappedWindow->SetSizePixel( GetOutputSizePixel() );
282 }
283 
Resizing(Size & rSize)284 void SfxTitleDockingWindow::Resizing( Size &rSize )
285 {
286     SfxDockingWindow::Resizing( rSize );
287     if (m_pWrappedWindow)
288         m_pWrappedWindow->SetSizePixel( GetOutputSizePixel() );
289 }
290 
291 namespace
292 {
293     struct ChildrenRegisteredMap : public rtl::Static< bool, ChildrenRegisteredMap > {};
294 }
295 
lcl_checkDockingWindowID(sal_uInt16 nID)296 static bool lcl_checkDockingWindowID( sal_uInt16 nID )
297 {
298     return nID >= SID_DOCKWIN_START && nID < o3tl::make_unsigned(SID_DOCKWIN_START+NUM_OF_DOCKINGWINDOWS);
299 }
300 
lcl_getWorkWindowFromXFrame(const uno::Reference<frame::XFrame> & rFrame)301 static SfxWorkWindow* lcl_getWorkWindowFromXFrame( const uno::Reference< frame::XFrame >& rFrame )
302 {
303     // We need to find the corresponding SfxFrame of our XFrame
304     SfxFrame* pFrame  = SfxFrame::GetFirst();
305     SfxFrame* pXFrame = nullptr;
306     while ( pFrame )
307     {
308         uno::Reference< frame::XFrame > xViewShellFrame( pFrame->GetFrameInterface() );
309         if ( xViewShellFrame == rFrame )
310         {
311             pXFrame = pFrame;
312             break;
313         }
314         else
315             pFrame = SfxFrame::GetNext( *pFrame );
316     }
317 
318     // If we have a SfxFrame we can retrieve the work window (Sfx layout manager for docking windows)
319     if ( pXFrame )
320         return pXFrame->GetWorkWindow_Impl();
321     else
322         return nullptr;
323 }
324 
325 /** Factory function used by the framework layout manager to "create" a docking window with a special name.
326     The string rDockingWindowName MUST BE a valid ID! The ID is pre-defined by a certain slot range located
327     in sfxsids.hrc (currently SID_DOCKWIN_START = 9800).
328 */
SfxDockingWindowFactory(const uno::Reference<frame::XFrame> & rFrame,const OUString & rDockingWindowName)329 void SfxDockingWindowFactory( const uno::Reference< frame::XFrame >& rFrame, const OUString& rDockingWindowName )
330 {
331     SolarMutexGuard aGuard;
332     sal_uInt16 nID = sal_uInt16(rDockingWindowName.toInt32());
333 
334     // Check the range of the provided ID otherwise nothing will happen
335     if ( !lcl_checkDockingWindowID( nID ))
336         return;
337 
338     SfxWorkWindow* pWorkWindow = lcl_getWorkWindowFromXFrame( rFrame );
339     if ( pWorkWindow )
340     {
341         SfxChildWindow* pChildWindow = pWorkWindow->GetChildWindow_Impl(nID);
342         if ( !pChildWindow )
343         {
344             // Register window at the workwindow child window list
345             pWorkWindow->SetChildWindow_Impl( nID, true, false );
346         }
347     }
348 }
349 
350 /** Function used by the framework layout manager to determine the visibility state of a docking window with
351     a special name. The string rDockingWindowName MUST BE a valid ID! The ID is pre-defined by a certain slot
352     range located in sfxsids.hrc (currently SID_DOCKWIN_START = 9800).
353 */
IsDockingWindowVisible(const uno::Reference<frame::XFrame> & rFrame,const OUString & rDockingWindowName)354 bool IsDockingWindowVisible( const uno::Reference< frame::XFrame >& rFrame, const OUString& rDockingWindowName )
355 {
356     SolarMutexGuard aGuard;
357 
358     sal_uInt16 nID = sal_uInt16(rDockingWindowName.toInt32());
359 
360     // Check the range of the provided ID otherwise nothing will happen
361     if ( lcl_checkDockingWindowID( nID ))
362     {
363         SfxWorkWindow* pWorkWindow = lcl_getWorkWindowFromXFrame( rFrame );
364         if ( pWorkWindow )
365         {
366             SfxChildWindow* pChildWindow = pWorkWindow->GetChildWindow_Impl(nID);
367             if ( pChildWindow )
368                 return true;
369         }
370     }
371 
372     return false;
373 }
374 
375 class SfxDockingWindow_Impl
376 {
377 friend class SfxDockingWindow;
378 
379     SfxChildAlignment   eLastAlignment;
380     SfxChildAlignment   eDockAlignment;
381     bool                bConstructed;
382     Size                aMinSize;
383     VclPtr<SfxSplitWindow>  pSplitWin;
384     Idle                aMoveIdle;
385 
386     // The following members are only valid in the time from startDocking to
387     // EndDocking:
388     Size                aSplitSize;
389     tools::Long                nHorizontalSize;
390     tools::Long                nVerticalSize;
391     sal_uInt16          nLine;
392     sal_uInt16          nPos;
393     sal_uInt16          nDockLine;
394     sal_uInt16          nDockPos;
395     bool                bNewLine;
396     bool                bDockingPrevented;
397     OString             aWinState;
398 
399     explicit            SfxDockingWindow_Impl(SfxDockingWindow *pBase);
GetLastAlignment() const400     SfxChildAlignment   GetLastAlignment() const
401                         { return eLastAlignment; }
SetLastAlignment(SfxChildAlignment eAlign)402     void                SetLastAlignment(SfxChildAlignment eAlign)
403                         { eLastAlignment = eAlign; }
GetDockAlignment() const404     SfxChildAlignment   GetDockAlignment() const
405                         { return eDockAlignment; }
SetDockAlignment(SfxChildAlignment eAlign)406     void                SetDockAlignment(SfxChildAlignment eAlign)
407                         { eDockAlignment = eAlign; }
408 };
409 
SfxDockingWindow_Impl(SfxDockingWindow * pBase)410 SfxDockingWindow_Impl::SfxDockingWindow_Impl(SfxDockingWindow* pBase)
411     :eLastAlignment(SfxChildAlignment::NOALIGNMENT)
412     ,eDockAlignment(SfxChildAlignment::NOALIGNMENT)
413     ,bConstructed(false)
414     ,pSplitWin(nullptr)
415     ,nHorizontalSize(0)
416     ,nVerticalSize(0)
417     ,nLine(0)
418     ,nPos(0)
419     ,nDockLine(0)
420     ,nDockPos(0)
421     ,bNewLine(false)
422     ,bDockingPrevented(false)
423 {
424     aMoveIdle.SetPriority(TaskPriority::RESIZE);
425     aMoveIdle.SetInvokeHandler(LINK(pBase,SfxDockingWindow,TimerHdl));
426     aMoveIdle.SetDebugName( "sfx::SfxDockingWindow_Impl aMoveIdle" );
427 }
428 
429 /*  [Description]
430 
431     This virtual method of the class FloatingWindow keeps track of changes in
432     FloatingSize. If this method is overridden by a derived class,
433     then the FloatingWindow: Resize() must also be called.
434 */
Resize()435 void SfxDockingWindow::Resize()
436 {
437     ResizableDockingWindow::Resize();
438     Invalidate();
439     if ( !pImpl || !pImpl->bConstructed || !pMgr )
440         return;
441 
442     if ( IsFloatingMode() )
443     {
444         // start timer for saving window status information
445         pImpl->aMoveIdle.Start();
446     }
447     else
448     {
449         Size aSize( GetSizePixel() );
450         switch ( pImpl->GetDockAlignment() )
451         {
452             case SfxChildAlignment::LEFT:
453             case SfxChildAlignment::FIRSTLEFT:
454             case SfxChildAlignment::LASTLEFT:
455             case SfxChildAlignment::RIGHT:
456             case SfxChildAlignment::FIRSTRIGHT:
457             case SfxChildAlignment::LASTRIGHT:
458                 pImpl->nHorizontalSize = aSize.Width();
459                 pImpl->aSplitSize = aSize;
460                 break;
461             case SfxChildAlignment::TOP:
462             case SfxChildAlignment::LOWESTTOP:
463             case SfxChildAlignment::HIGHESTTOP:
464             case SfxChildAlignment::BOTTOM:
465             case SfxChildAlignment::HIGHESTBOTTOM:
466             case SfxChildAlignment::LOWESTBOTTOM:
467                 pImpl->nVerticalSize = aSize.Height();
468                 pImpl->aSplitSize = aSize;
469                 break;
470             default:
471                 break;
472         }
473     }
474 }
475 
476 /*  [Description]
477 
478     This virtual method of the class DockingWindow makes it possible to
479     intervene in the switching of the floating mode.
480     If this method is overridden by a derived class,
481     then the SfxDockingWindow::PrepareToggleFloatingMode() must be called
482     afterwards, if not FALSE is returned.
483 */
PrepareToggleFloatingMode()484 bool SfxDockingWindow::PrepareToggleFloatingMode()
485 {
486     if (!pImpl || !pImpl->bConstructed)
487         return true;
488 
489     if ( (Application::IsInModalMode() && IsFloatingMode()) || !pMgr )
490         return false;
491 
492     if ( pImpl->bDockingPrevented )
493         return false;
494 
495     if (!IsFloatingMode())
496     {
497         // Test, if FloatingMode is permitted.
498         if ( CheckAlignment(GetAlignment(),SfxChildAlignment::NOALIGNMENT) != SfxChildAlignment::NOALIGNMENT )
499             return false;
500 
501         if ( pImpl->pSplitWin )
502         {
503             // The DockingWindow is inside a SplitWindow and will be teared of.
504             pImpl->pSplitWin->RemoveWindow(this/*, sal_False*/);
505             pImpl->pSplitWin = nullptr;
506         }
507     }
508     else if ( pMgr )
509     {
510         pImpl->aWinState = GetFloatingWindow()->GetWindowState();
511 
512         // Test if it is allowed to dock,
513         if (CheckAlignment(GetAlignment(),pImpl->GetLastAlignment()) == SfxChildAlignment::NOALIGNMENT)
514             return false;
515 
516         // Test, if the Workwindow allows for docking at the moment.
517         SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
518         if ( !pWorkWin->IsDockingAllowed() || !pWorkWin->IsInternalDockingAllowed() )
519             return false;
520     }
521 
522     return true;
523 }
524 
525 /*  [Description]
526 
527     This virtual method of the DockingWindow class sets the internal data of
528     the SfxDockingWindow and ensures the correct alignment on the parent window.
529     Through PrepareToggleFloatMode and Initialize it is ensured that
530     pImpl-> GetLastAlignment() always delivers an allowed alignment. If this
531     method is overridden by a derived class, then first the
532     SfxDockingWindow::ToggleFloatingMode() must be called.
533 */
ToggleFloatingMode()534 void SfxDockingWindow::ToggleFloatingMode()
535 {
536     if ( !pImpl || !pImpl->bConstructed || !pMgr )
537         return;                                 // No Handler call
538 
539     // Remember old alignment and then switch.
540     // SV has already switched, but the alignment SfxDockingWindow is still
541     // the old one. What I was before?
542     SfxChildAlignment eLastAlign = GetAlignment();
543 
544     SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
545 
546     if (IsFloatingMode())
547     {
548         SetAlignment(SfxChildAlignment::NOALIGNMENT);
549         if ( !pImpl->aWinState.isEmpty() )
550             GetFloatingWindow()->SetWindowState( pImpl->aWinState );
551         else
552             GetFloatingWindow()->SetOutputSizePixel( GetFloatingSize() );
553     }
554     else
555     {
556         if (pImpl->GetDockAlignment() == eLastAlign)
557         {
558             // If ToggleFloatingMode was called, but the DockAlignment still
559             // is unchanged, then this means that it must have been a toggling
560             // through DClick, so use last alignment
561             SetAlignment (pImpl->GetLastAlignment());
562         }
563         else
564         {
565 
566             // Toggling was triggered by dragging
567             pImpl->nLine = pImpl->nDockLine;
568             pImpl->nPos = pImpl->nDockPos;
569             SetAlignment (pImpl->GetDockAlignment());
570         }
571 
572         // The DockingWindow is now in a SplitWindow
573         pImpl->pSplitWin = pWorkWin->GetSplitWindow_Impl(GetAlignment());
574 
575         // The LastAlignment is still the last docked
576         SfxSplitWindow *pSplit = pWorkWin->GetSplitWindow_Impl(pImpl->GetLastAlignment());
577 
578         DBG_ASSERT( pSplit, "LastAlignment is not correct!" );
579         if ( pSplit && pSplit != pImpl->pSplitWin )
580             pSplit->ReleaseWindow_Impl(this);
581         if ( pImpl->GetDockAlignment() == eLastAlign )
582             pImpl->pSplitWin->InsertWindow( this, pImpl->aSplitSize );
583         else
584             pImpl->pSplitWin->InsertWindow( this, pImpl->aSplitSize, pImpl->nLine, pImpl->nPos, pImpl->bNewLine );
585         if ( !pImpl->pSplitWin->IsFadeIn() )
586             pImpl->pSplitWin->FadeIn();
587     }
588 
589     // Keep the old alignment for the next toggle; set it only now due to the
590     // unregister SplitWindow!
591     pImpl->SetLastAlignment(eLastAlign);
592 
593     // Reset DockAlignment, if EndDocking is still called
594     pImpl->SetDockAlignment(GetAlignment());
595 
596     // Dock or undock SfxChildWindow correctly.
597     pWorkWin->ConfigChild_Impl( SfxChildIdentifier::SPLITWINDOW, SfxDockingConfig::TOGGLEFLOATMODE, pMgr->GetType() );
598 }
599 
600 /*  [Description]
601 
602     This virtual method of the DockingWindow class takes the inner and outer
603     docking rectangle from the parent window. If this method is overridden by
604     a derived class, then SfxDockingWindow:StartDocking() has to be called at
605     the end.
606 */
StartDocking()607 void SfxDockingWindow::StartDocking()
608 {
609     if ( !pImpl || !pImpl->bConstructed || !pMgr )
610         return;
611     SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
612     pWorkWin->ConfigChild_Impl( SfxChildIdentifier::SPLITWINDOW, SfxDockingConfig::SETDOCKINGRECTS, pMgr->GetType() );
613     pImpl->SetDockAlignment(GetAlignment());
614 
615     if ( pImpl->pSplitWin )
616     {
617         // Get the current docking data
618         pImpl->pSplitWin->GetWindowPos(this, pImpl->nLine, pImpl->nPos);
619         pImpl->nDockLine = pImpl->nLine;
620         pImpl->nDockPos = pImpl->nPos;
621         pImpl->bNewLine = false;
622     }
623 }
624 
625 /*  [Description]
626 
627     This virtual method of the DockingWindow class calculates the current
628     tracking rectangle. For this purpose the method CalcAlignment(RPOs, rRect)
629     is used, the behavior can be influenced by the derived classes (see below).
630     This method should if possible not be overwritten.
631 */
Docking(const Point & rPos,tools::Rectangle & rRect)632 bool SfxDockingWindow::Docking( const Point& rPos, tools::Rectangle& rRect )
633 {
634     if ( Application::IsInModalMode() )
635         return true;
636 
637     if ( !pImpl || !pImpl->bConstructed || !pMgr )
638     {
639         rRect.SetSize( Size() );
640         return IsFloatingMode();
641     }
642 
643     SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
644     if ( pImpl->bDockingPrevented || !pWorkWin->IsInternalDockingAllowed() )
645         return false;
646 
647     bool bFloatMode = false;
648 
649     if ( GetOuterRect().IsInside( rPos ) )
650     {
651         // Mouse within OuterRect: calculate Alignment and Rectangle
652         SfxChildAlignment eAlign = CalcAlignment(rPos, rRect);
653         if (eAlign == SfxChildAlignment::NOALIGNMENT)
654             bFloatMode = true;
655         pImpl->SetDockAlignment(eAlign);
656     }
657     else
658     {
659         // Mouse is not within OuterRect: must be FloatingWindow
660         // Is this allowed?
661         if (CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::NOALIGNMENT) != SfxChildAlignment::NOALIGNMENT)
662             return false;
663         bFloatMode = true;
664         if ( SfxChildAlignment::NOALIGNMENT != pImpl->GetDockAlignment() )
665         {
666             // Due to a bug the rRect may only be changed when the
667             // alignment is changed!
668             pImpl->SetDockAlignment(SfxChildAlignment::NOALIGNMENT);
669             rRect.SetSize(CalcDockingSize(SfxChildAlignment::NOALIGNMENT));
670         }
671     }
672 
673     return bFloatMode;
674 }
675 
676 /** Virtual method of the DockingWindow class ensures the correct alignment on
677     the parent window. If this method is overridden by a derived class, then
678     SfxDockingWindow::EndDocking() must be called first.
679 */
EndDocking(const tools::Rectangle & rRect,bool bFloatMode)680 void SfxDockingWindow::EndDocking( const tools::Rectangle& rRect, bool bFloatMode )
681 {
682     if ( !pImpl || !pImpl->bConstructed || IsDockingCanceled() || !pMgr )
683         return;
684 
685     SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
686 
687     // If the alignment changes and the window is in a docked state in a
688     // SplitWindow, then it must be re-registered. If it is docked again,
689     // PrepareToggleFloatingMode() and ToggleFloatingMode() perform the
690     // re-registered
691     bool bReArrange = !bFloatMode;
692 
693     if ( bReArrange )
694     {
695         if ( GetAlignment() != pImpl->GetDockAlignment() )
696         {
697             // before Show() is called must the reassignment have been made,
698             // therefore the base class can not be called
699             if ( IsFloatingMode() )
700                 Show( false, ShowFlags::NoFocusChange );
701 
702             // Set the size for toggling.
703             pImpl->aSplitSize = rRect.GetSize();
704             if ( IsFloatingMode() )
705             {
706                 SetFloatingMode( bFloatMode );
707                 if ( IsFloatingMode() )
708                     Show( true, ShowFlags::NoFocusChange );
709             }
710             else
711             {
712                 pImpl->pSplitWin->RemoveWindow(this,false);
713                 pImpl->nLine = pImpl->nDockLine;
714                 pImpl->nPos = pImpl->nDockPos;
715                 pImpl->pSplitWin->ReleaseWindow_Impl(this);
716                 pImpl->pSplitWin = pWorkWin->GetSplitWindow_Impl(pImpl->GetDockAlignment());
717                 pImpl->pSplitWin->InsertWindow( this, pImpl->aSplitSize, pImpl->nDockLine, pImpl->nDockPos, pImpl->bNewLine );
718                 if ( !pImpl->pSplitWin->IsFadeIn() )
719                     pImpl->pSplitWin->FadeIn();
720             }
721         }
722         else if ( pImpl->nLine != pImpl->nDockLine || pImpl->nPos != pImpl->nDockPos || pImpl->bNewLine )
723         {
724             // Moved within Splitwindows
725             if ( pImpl->nLine != pImpl->nDockLine )
726                 pImpl->aSplitSize = rRect.GetSize();
727             pImpl->pSplitWin->MoveWindow( this, pImpl->aSplitSize, pImpl->nDockLine, pImpl->nDockPos, pImpl->bNewLine );
728         }
729     }
730     else
731     {
732         ResizableDockingWindow::EndDocking(rRect, bFloatMode);
733     }
734 
735     SetAlignment( IsFloatingMode() ? SfxChildAlignment::NOALIGNMENT : pImpl->GetDockAlignment() );
736 }
737 
738 /*  [Description]
739 
740     Virtual method of the DockingWindow class. Here, the interactive resize in
741     FloatingMode can be influenced, for example by only allowing for discrete
742     values for width and / or height. The base implementation prevents that the
743     output size is smaller than one set with SetMinOutputSizePixel().
744 */
Resizing(Size &)745 void SfxDockingWindow::Resizing( Size& /*rSize*/ )
746 {
747 
748 }
749 
750 /*  [Description]
751 
752     Constructor for the SfxDockingWindow class. A SfxChildWindow will be
753     required because the docking is implemented in Sfx through SfxChildWindows.
754 */
SfxDockingWindow(SfxBindings * pBindinx,SfxChildWindow * pCW,vcl::Window * pParent,WinBits nWinBits)755 SfxDockingWindow::SfxDockingWindow( SfxBindings *pBindinx, SfxChildWindow *pCW,
756     vcl::Window* pParent, WinBits nWinBits)
757     : ResizableDockingWindow(pParent, nWinBits)
758     , pBindings(pBindinx)
759     , pMgr(pCW)
760 {
761     pImpl.reset(new SfxDockingWindow_Impl(this));
762 }
763 
764 /** Constructor for the SfxDockingWindow class. A SfxChildWindow will be
765     required because the docking is implemented in Sfx through SfxChildWindows.
766 */
SfxDockingWindow(SfxBindings * pBindinx,SfxChildWindow * pCW,vcl::Window * pParent,const OString & rID,const OUString & rUIXMLDescription)767 SfxDockingWindow::SfxDockingWindow( SfxBindings *pBindinx, SfxChildWindow *pCW,
768     vcl::Window* pParent, const OString& rID, const OUString& rUIXMLDescription)
769     : ResizableDockingWindow(pParent)
770     , pBindings(pBindinx)
771     , pMgr(pCW)
772 {
773     m_xBuilder.reset(Application::CreateInterimBuilder(m_xBox, rUIXMLDescription, true));
774     m_xContainer = m_xBuilder->weld_box(rID);
775 
776     pImpl.reset(new SfxDockingWindow_Impl(this));
777 }
778 
779 /** Initialization of the SfxDockingDialog class via a SfxChildWinInfo.
780     The initialization is done only in a 2nd step after the constructor, this
781     constructor should be called from the derived class or from the
782     SfxChildWindows.
783 */
Initialize(SfxChildWinInfo * pInfo)784 void SfxDockingWindow::Initialize(SfxChildWinInfo *pInfo)
785 {
786     if ( !pMgr )
787     {
788         pImpl->SetDockAlignment( SfxChildAlignment::NOALIGNMENT );
789         pImpl->bConstructed = true;
790         return;
791     }
792 
793     if (pInfo && (pInfo->nFlags & SfxChildWindowFlags::FORCEDOCK))
794         pImpl->bDockingPrevented = true;
795 
796     pImpl->aSplitSize = GetOutputSizePixel();
797     if ( !GetFloatingSize().Width() )
798     {
799         Size aMinSize( GetMinOutputSizePixel() );
800         SetFloatingSize( pImpl->aSplitSize );
801         if ( pImpl->aSplitSize.Width() < aMinSize.Width() )
802             pImpl->aSplitSize.setWidth( aMinSize.Width() );
803         if ( pImpl->aSplitSize.Height() < aMinSize.Height() )
804             pImpl->aSplitSize.setHeight( aMinSize.Height() );
805     }
806 
807     bool bVertHorzRead( false );
808     if (pInfo && !pInfo->aExtraString.isEmpty())
809     {
810         // get information about alignment, split size and position in SplitWindow
811         OUString aStr;
812         sal_Int32 nPos = pInfo->aExtraString.indexOf("AL:");
813         if ( nPos != -1 )
814         {
815             // alignment information
816             sal_Int32 n1 = pInfo->aExtraString.indexOf('(', nPos);
817             if ( n1 != -1 )
818             {
819                 sal_Int32 n2 = pInfo->aExtraString.indexOf(')', n1);
820                 if ( n2 != -1 )
821                 {
822                     // extract alignment information from extrastring
823                     aStr = pInfo->aExtraString.copy(nPos, n2 - nPos + 1);
824                     pInfo->aExtraString = pInfo->aExtraString.replaceAt(nPos, n2 - nPos + 1, "");
825                     aStr = aStr.replaceAt(nPos, n1-nPos+1, "");
826                 }
827             }
828         }
829 
830         if ( !aStr.isEmpty() )
831         {
832             // accept window state only if alignment is also set
833             pImpl->aWinState = pInfo->aWinState;
834 
835             // check for valid alignment
836             SfxChildAlignment eLocalAlignment = static_cast<SfxChildAlignment>(static_cast<sal_uInt16>(aStr.toInt32()));
837             bool bIgnoreFloatConfig = (eLocalAlignment == SfxChildAlignment::NOALIGNMENT &&
838                                        !StyleSettings::GetDockingFloatsSupported());
839             if (pImpl->bDockingPrevented || bIgnoreFloatConfig)
840                 // docking prevented, ignore old configuration and take alignment from default
841                 aStr.clear();
842             else
843                 SetAlignment( eLocalAlignment );
844 
845             SfxChildAlignment eAlign = CheckAlignment(GetAlignment(),GetAlignment());
846             if ( eAlign != GetAlignment() )
847             {
848                 OSL_FAIL("Invalid Alignment!");
849                 SetAlignment( eAlign );
850                 aStr.clear();
851             }
852 
853             // get last alignment (for toggling)
854             nPos = aStr.indexOf(',');
855             if ( nPos != -1 )
856             {
857                 aStr = aStr.copy(nPos+1);
858                 pImpl->SetLastAlignment( static_cast<SfxChildAlignment>(static_cast<sal_uInt16>(aStr.toInt32())) );
859             }
860 
861             nPos = aStr.indexOf(',');
862             if ( nPos != -1 )
863             {
864                 // get split size and position in SplitWindow
865                 Point aPos;
866                 aStr = aStr.copy(nPos+1);
867                 if ( GetPosSizeFromString( aStr, aPos, pImpl->aSplitSize ) )
868                 {
869                     pImpl->nLine = pImpl->nDockLine = static_cast<sal_uInt16>(aPos.X());
870                     pImpl->nPos  = pImpl->nDockPos  = static_cast<sal_uInt16>(aPos.Y());
871                     pImpl->nVerticalSize = pImpl->aSplitSize.Height();
872                     pImpl->nHorizontalSize = pImpl->aSplitSize.Width();
873                     if ( GetSplitSizeFromString( aStr, pImpl->aSplitSize ))
874                         bVertHorzRead = true;
875                 }
876             }
877         }
878         else {
879             OSL_FAIL( "Information is missing!" );
880         }
881     }
882 
883     if ( !bVertHorzRead )
884     {
885         pImpl->nVerticalSize = pImpl->aSplitSize.Height();
886         pImpl->nHorizontalSize = pImpl->aSplitSize.Width();
887     }
888 
889     SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
890     if ( GetAlignment() != SfxChildAlignment::NOALIGNMENT )
891     {
892         // check if SfxWorkWindow is able to allow docking at its border
893         if (
894             !pWorkWin->IsDockingAllowed() ||
895             !pWorkWin->IsInternalDockingAllowed() ||
896             ( (GetFloatStyle() & WB_STANDALONE) && Application::IsInModalMode()) )
897         {
898             SetAlignment( SfxChildAlignment::NOALIGNMENT );
899         }
900     }
901 
902     // detect floating mode
903     // toggling mode will not execute code in handlers, because pImpl->bConstructed is not set yet
904     bool bFloatMode = IsFloatingMode();
905     if ( bFloatMode != (GetAlignment() == SfxChildAlignment::NOALIGNMENT) )
906     {
907         bFloatMode = !bFloatMode;
908         SetFloatingMode( bFloatMode );
909         if ( bFloatMode )
910         {
911             if ( !pImpl->aWinState.isEmpty() )
912                 GetFloatingWindow()->SetWindowState( pImpl->aWinState );
913             else
914                 GetFloatingWindow()->SetOutputSizePixel( GetFloatingSize() );
915         }
916     }
917 
918     if ( IsFloatingMode() )
919     {
920         // validate last alignment
921         SfxChildAlignment eLastAlign = pImpl->GetLastAlignment();
922         if ( eLastAlign == SfxChildAlignment::NOALIGNMENT)
923             eLastAlign = CheckAlignment(eLastAlign, SfxChildAlignment::LEFT);
924         if ( eLastAlign == SfxChildAlignment::NOALIGNMENT)
925             eLastAlign = CheckAlignment(eLastAlign, SfxChildAlignment::RIGHT);
926         if ( eLastAlign == SfxChildAlignment::NOALIGNMENT)
927             eLastAlign = CheckAlignment(eLastAlign, SfxChildAlignment::TOP);
928         if ( eLastAlign == SfxChildAlignment::NOALIGNMENT)
929             eLastAlign = CheckAlignment(eLastAlign, SfxChildAlignment::BOTTOM);
930         pImpl->SetLastAlignment(eLastAlign);
931     }
932     else
933     {
934         // docked window must have NOALIGNMENT as last alignment
935         pImpl->SetLastAlignment(SfxChildAlignment::NOALIGNMENT);
936 
937         pImpl->pSplitWin = pWorkWin->GetSplitWindow_Impl(GetAlignment());
938         pImpl->pSplitWin->InsertWindow(this, pImpl->aSplitSize);
939     }
940 
941     // save alignment
942     pImpl->SetDockAlignment( GetAlignment() );
943 }
944 
Initialize_Impl()945 void SfxDockingWindow::Initialize_Impl()
946 {
947     if ( !pMgr )
948     {
949         pImpl->bConstructed = true;
950         return;
951     }
952 
953     SystemWindow* pFloatWin = GetFloatingWindow();
954     bool bSet = false;
955     if ( pFloatWin )
956     {
957         bSet = !pFloatWin->IsDefaultPos();
958     }
959     else
960     {
961         Point aPos = GetFloatingPos();
962         if ( aPos != Point() )
963             bSet = true;
964     }
965 
966     if ( !bSet)
967     {
968         SfxViewFrame *pFrame = pBindings->GetDispatcher_Impl()->GetFrame();
969         vcl::Window* pEditWin = pFrame->GetViewShell()->GetWindow();
970         Point aPos = pEditWin->OutputToScreenPixel( pEditWin->GetPosPixel() );
971         aPos = GetParent()->ScreenToOutputPixel( aPos );
972         SetFloatingPos( aPos );
973     }
974 
975     if ( pFloatWin )
976     {
977         // initialize floating window
978         if ( pImpl->aWinState.isEmpty() )
979             // window state never set before, get if from defaults
980             pImpl->aWinState = pFloatWin->GetWindowState();
981 
982         // trick: use VCL method SetWindowState to adjust position and size
983         pFloatWin->SetWindowState( pImpl->aWinState );
984         Size aSize(pFloatWin->GetSizePixel());
985 
986         // remember floating size for calculating alignment and tracking rectangle
987         SetFloatingSize(aSize);
988 
989     }
990 
991     // allow calling of docking handlers
992     pImpl->bConstructed = true;
993 }
994 
995 /** Fills a SfxChildWinInfo with specific data from SfxDockingWindow,
996     so that it can be written in the INI file. It is assumed that rinfo
997     receives all other possible relevant data in the ChildWindow class.
998     Insertions are marked with size and the ZoomIn flag.
999     If this method is overridden, the base implementation must be called first.
1000 */
FillInfo(SfxChildWinInfo & rInfo) const1001 void SfxDockingWindow::FillInfo(SfxChildWinInfo& rInfo) const
1002 {
1003     if (!pMgr || !pImpl)
1004         return;
1005 
1006     if (GetFloatingWindow() && pImpl->bConstructed)
1007         pImpl->aWinState = GetFloatingWindow()->GetWindowState();
1008 
1009     rInfo.aWinState = pImpl->aWinState;
1010     rInfo.aExtraString = "AL:(";
1011     rInfo.aExtraString += OUString::number(static_cast<sal_uInt16>(GetAlignment()));
1012     rInfo.aExtraString += ",";
1013     rInfo.aExtraString += OUString::number (static_cast<sal_uInt16>(pImpl->GetLastAlignment()));
1014 
1015     Point aPos(pImpl->nLine, pImpl->nPos);
1016     rInfo.aExtraString += ",";
1017     rInfo.aExtraString += OUString::number( aPos.X() );
1018     rInfo.aExtraString += "/";
1019     rInfo.aExtraString += OUString::number( aPos.Y() );
1020     rInfo.aExtraString += "/";
1021     rInfo.aExtraString += OUString::number( pImpl->nHorizontalSize );
1022     rInfo.aExtraString += "/";
1023     rInfo.aExtraString += OUString::number( pImpl->nVerticalSize );
1024     rInfo.aExtraString += ",";
1025     rInfo.aExtraString += OUString::number( pImpl->aSplitSize.Width() );
1026     rInfo.aExtraString += ";";
1027     rInfo.aExtraString += OUString::number( pImpl->aSplitSize.Height() );
1028 
1029     rInfo.aExtraString += ")";
1030 }
1031 
~SfxDockingWindow()1032 SfxDockingWindow::~SfxDockingWindow()
1033 {
1034     disposeOnce();
1035 }
1036 
dispose()1037 void SfxDockingWindow::dispose()
1038 {
1039     ReleaseChildWindow_Impl();
1040     pImpl.reset();
1041     m_xContainer.reset();
1042     m_xBuilder.reset();
1043     ResizableDockingWindow::dispose();
1044 }
1045 
ReleaseChildWindow_Impl()1046 void SfxDockingWindow::ReleaseChildWindow_Impl()
1047 {
1048     if ( pMgr && pMgr->GetFrame() == pBindings->GetActiveFrame() )
1049         pBindings->SetActiveFrame( nullptr );
1050 
1051     if ( pMgr && pImpl->pSplitWin && pImpl->pSplitWin->IsItemValid( GetType() ) )
1052         pImpl->pSplitWin->RemoveWindow(this);
1053 
1054     pMgr=nullptr;
1055 }
1056 
1057 /** This method calculates a resulting alignment for the given mouse position
1058     and tracking rectangle. When changing the alignment it can also be that
1059     the tracking rectangle is changed, so that an altered rectangle is
1060     returned. The user of this class can influence behaviour of this method,
1061     and thus the behavior of his DockinWindow class when docking where the
1062     called virtual method:
1063 
1064     SfxDockingWindow::CalcDockingSize (SfxChildAlignment eAlign)
1065 
1066     is overridden (see below).
1067 */
CalcAlignment(const Point & rPos,tools::Rectangle & rRect)1068 SfxChildAlignment SfxDockingWindow::CalcAlignment(const Point& rPos, tools::Rectangle& rRect)
1069 {
1070     // calculate hypothetical sizes for different modes
1071     Size aFloatingSize(CalcDockingSize(SfxChildAlignment::NOALIGNMENT));
1072 
1073     // check if docking is permitted
1074     SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
1075     if ( !pWorkWin->IsDockingAllowed() )
1076     {
1077         rRect.SetSize( aFloatingSize );
1078         return pImpl->GetDockAlignment();
1079     }
1080 
1081     // calculate borders to shrink inner area before checking for intersection with tracking rectangle
1082     tools::Long nLRBorder, nTBBorder;
1083 
1084     // take the smaller size of docked and floating mode
1085     Size aBorderTmp = pImpl->aSplitSize;
1086     if ( GetFloatingSize().Height() < aBorderTmp.Height() )
1087         aBorderTmp.setHeight( GetFloatingSize().Height() );
1088     if ( GetFloatingSize().Width() < aBorderTmp.Width() )
1089         aBorderTmp.setWidth( GetFloatingSize().Width() );
1090 
1091     nLRBorder = aBorderTmp.Width();
1092     nTBBorder = aBorderTmp.Height();
1093 
1094     // limit border to predefined constant values
1095     if ( nLRBorder > MAX_TOGGLEAREA_WIDTH )
1096         nLRBorder = MAX_TOGGLEAREA_WIDTH;
1097     if ( nTBBorder > MAX_TOGGLEAREA_WIDTH )
1098         nTBBorder = MAX_TOGGLEAREA_WIDTH;
1099 
1100     // shrink area for floating mode if possible
1101     tools::Rectangle aInRect = GetInnerRect();
1102     if ( aInRect.GetWidth() > nLRBorder )
1103         aInRect.AdjustLeft(nLRBorder/2 );
1104     if ( aInRect.GetWidth() > nLRBorder )
1105         aInRect.AdjustRight( -(nLRBorder/2) );
1106     if ( aInRect.GetHeight() > nTBBorder )
1107         aInRect.AdjustTop(nTBBorder/2 );
1108     if ( aInRect.GetHeight() > nTBBorder )
1109         aInRect.AdjustBottom( -(nTBBorder/2) );
1110 
1111     // calculate alignment resulting from docking rectangle
1112     bool bBecomesFloating = false;
1113     SfxChildAlignment eDockAlign = pImpl->GetDockAlignment();
1114     tools::Rectangle aDockingRect( rRect );
1115     if ( !IsFloatingMode() )
1116     {
1117         // don't use tracking rectangle for alignment check, because it will be too large
1118         // to get a floating mode as result - switch to floating size
1119         // so the calculation only depends on the position of the rectangle, not the current
1120         // docking state of the window
1121         aDockingRect.SetSize( GetFloatingSize() );
1122 
1123         // in this mode docking is never done by keyboard, so it's OK to use the mouse position
1124         aDockingRect.SetPos( pWorkWin->GetWindow()->OutputToScreenPixel( pWorkWin->GetWindow()->GetPointerPosPixel() ) );
1125     }
1126 
1127     Point aPos = aDockingRect.TopLeft();
1128     tools::Rectangle aIntersect = GetOuterRect().GetIntersection( aDockingRect );
1129     if ( aIntersect.IsEmpty() )
1130         // docking rectangle completely outside docking area -> floating mode
1131         bBecomesFloating = true;
1132     else
1133     {
1134         // create a small test rect around the mouse position and use this one
1135         // instead of the passed rRect to not dock too easily or by accident
1136         tools::Rectangle aSmallDockingRect;
1137         aSmallDockingRect.SetSize( Size( MAX_TOGGLEAREA_WIDTH, MAX_TOGGLEAREA_HEIGHT ) );
1138         Point aNewPos(rPos);
1139         aNewPos.AdjustX( -(aSmallDockingRect.GetWidth()/2) );
1140         aNewPos.AdjustY( -(aSmallDockingRect.GetHeight()/2) );
1141         aSmallDockingRect.SetPos(aNewPos);
1142         tools::Rectangle aIntersectRect = aInRect.GetIntersection( aSmallDockingRect );
1143         if ( aIntersectRect == aSmallDockingRect )
1144             // docking rectangle completely inside (shrunk) inner area -> floating mode
1145             bBecomesFloating = true;
1146     }
1147 
1148     if ( bBecomesFloating )
1149     {
1150         eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::NOALIGNMENT);
1151     }
1152     else
1153     {
1154         // docking rectangle is in the "sensible area"
1155         Point aInPosTL( aPos.X()-aInRect.Left(), aPos.Y()-aInRect.Top() );
1156         Point aInPosBR( aPos.X()-aInRect.Left() + aDockingRect.GetWidth(), aPos.Y()-aInRect.Top() + aDockingRect.GetHeight() );
1157         Size  aInSize = aInRect.GetSize();
1158         bool  bNoChange = false;
1159 
1160         // check if alignment is still unchanged
1161         switch ( GetAlignment() )
1162         {
1163             case SfxChildAlignment::LEFT:
1164             case SfxChildAlignment::FIRSTLEFT:
1165             case SfxChildAlignment::LASTLEFT:
1166                 if (aInPosTL.X() <= 0)
1167                 {
1168                     eDockAlign = GetAlignment();
1169                     bNoChange = true;
1170                 }
1171                 break;
1172             case SfxChildAlignment::TOP:
1173             case SfxChildAlignment::LOWESTTOP:
1174             case SfxChildAlignment::HIGHESTTOP:
1175                 if ( aInPosTL.Y() <= 0)
1176                 {
1177                     eDockAlign = GetAlignment();
1178                     bNoChange = true;
1179                 }
1180                 break;
1181             case SfxChildAlignment::RIGHT:
1182             case SfxChildAlignment::FIRSTRIGHT:
1183             case SfxChildAlignment::LASTRIGHT:
1184                 if ( aInPosBR.X() >= aInSize.Width())
1185                 {
1186                     eDockAlign = GetAlignment();
1187                     bNoChange = true;
1188                 }
1189                 break;
1190             case SfxChildAlignment::BOTTOM:
1191             case SfxChildAlignment::LOWESTBOTTOM:
1192             case SfxChildAlignment::HIGHESTBOTTOM:
1193                 if ( aInPosBR.Y() >= aInSize.Height())
1194                 {
1195                     eDockAlign = GetAlignment();
1196                     bNoChange = true;
1197                 }
1198                 break;
1199             default:
1200                 break;
1201         }
1202 
1203         if ( !bNoChange )
1204         {
1205             // alignment will change, test alignment according to distance of the docking rectangles edges
1206             bool bForbidden = true;
1207             if ( aInPosTL.X() <= 0)
1208             {
1209                 eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::LEFT);
1210                 bForbidden = ( eDockAlign != SfxChildAlignment::LEFT &&
1211                                eDockAlign != SfxChildAlignment::FIRSTLEFT &&
1212                                eDockAlign != SfxChildAlignment::LASTLEFT );
1213             }
1214 
1215             if ( bForbidden && aInPosTL.Y() <= 0)
1216             {
1217                 eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::TOP);
1218                 bForbidden = ( eDockAlign != SfxChildAlignment::TOP &&
1219                                eDockAlign != SfxChildAlignment::HIGHESTTOP &&
1220                                eDockAlign != SfxChildAlignment::LOWESTTOP );
1221             }
1222 
1223             if ( bForbidden && aInPosBR.X() >= aInSize.Width())
1224             {
1225                 eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::RIGHT);
1226                 bForbidden = ( eDockAlign != SfxChildAlignment::RIGHT &&
1227                                eDockAlign != SfxChildAlignment::FIRSTRIGHT &&
1228                                eDockAlign != SfxChildAlignment::LASTRIGHT );
1229             }
1230 
1231             if ( bForbidden && aInPosBR.Y() >= aInSize.Height())
1232             {
1233                 eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::BOTTOM);
1234                 bForbidden = ( eDockAlign != SfxChildAlignment::BOTTOM &&
1235                                eDockAlign != SfxChildAlignment::HIGHESTBOTTOM &&
1236                                eDockAlign != SfxChildAlignment::LOWESTBOTTOM );
1237             }
1238 
1239             // the calculated alignment was rejected by the window -> take floating mode
1240             if ( bForbidden )
1241                 eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::NOALIGNMENT);
1242         }
1243     }
1244 
1245     if ( eDockAlign == SfxChildAlignment::NOALIGNMENT )
1246     {
1247         // In the FloatingMode the tracking rectangle will get the floating
1248         // size. Due to a bug the rRect may only be changed when the
1249         // alignment is changed!
1250         if ( eDockAlign != pImpl->GetDockAlignment() )
1251             aDockingRect.SetSize( aFloatingSize );
1252     }
1253     else
1254     {
1255         sal_uInt16 nLine, nPos;
1256         SfxSplitWindow *pSplitWin = pWorkWin->GetSplitWindow_Impl(eDockAlign);
1257         aPos = pSplitWin->ScreenToOutputPixel( aPos );
1258         if ( pSplitWin->GetWindowPos( aPos, nLine, nPos ) )
1259         {
1260             // mouse over splitwindow, get line and position
1261             pImpl->nDockLine = nLine;
1262             pImpl->nDockPos = nPos;
1263             pImpl->bNewLine = false;
1264         }
1265         else
1266         {
1267             // mouse touches inner border -> create new line
1268             if ( eDockAlign == GetAlignment() && pImpl->pSplitWin &&
1269                  pImpl->nLine == pImpl->pSplitWin->GetLineCount()-1 && pImpl->pSplitWin->GetWindowCount(pImpl->nLine) == 1 )
1270             {
1271                 // if this window is the only one in the last line, it can't be docked as new line in the same splitwindow
1272                 pImpl->nDockLine = pImpl->nLine;
1273                 pImpl->nDockPos = pImpl->nPos;
1274                 pImpl->bNewLine = false;
1275             }
1276             else
1277             {
1278                 // create new line
1279                 pImpl->nDockLine = pSplitWin->GetLineCount();
1280                 pImpl->nDockPos = 0;
1281                 pImpl->bNewLine = true;
1282             }
1283         }
1284 
1285         bool bChanged = pImpl->nLine != pImpl->nDockLine || pImpl->nPos != pImpl->nDockPos || eDockAlign != GetAlignment();
1286         if ( !bChanged && !IsFloatingMode() )
1287         {
1288             // window only slightly moved, no change of any property
1289             rRect.SetSize( pImpl->aSplitSize );
1290             rRect.SetPos( aDockingRect.TopLeft() );
1291             return eDockAlign;
1292         }
1293 
1294         // calculate new size and position
1295         Size aSize;
1296         Point aPoint = aDockingRect.TopLeft();
1297         Size aInnerSize = GetInnerRect().GetSize();
1298         if ( eDockAlign == SfxChildAlignment::LEFT || eDockAlign == SfxChildAlignment::RIGHT )
1299         {
1300             if ( pImpl->bNewLine )
1301             {
1302                 // set height to height of free area
1303                 aSize.setHeight( aInnerSize.Height() );
1304                 aSize.setWidth( pImpl->nHorizontalSize );
1305                 if ( eDockAlign == SfxChildAlignment::LEFT )
1306                 {
1307                     aPoint = aInnerRect.TopLeft();
1308                 }
1309                 else
1310                 {
1311                     aPoint = aInnerRect.TopRight();
1312                     aPoint.AdjustX( -(aSize.Width()) );
1313                 }
1314             }
1315             else
1316             {
1317                 // get width from splitwindow
1318                 aSize.setWidth( pSplitWin->GetLineSize(nLine) );
1319                 aSize.setHeight( pImpl->aSplitSize.Height() );
1320             }
1321         }
1322         else
1323         {
1324             if ( pImpl->bNewLine )
1325             {
1326                 // set width to width of free area
1327                 aSize.setWidth( aInnerSize.Width() );
1328                 aSize.setHeight( pImpl->nVerticalSize );
1329                 if ( eDockAlign == SfxChildAlignment::TOP )
1330                 {
1331                     aPoint = aInnerRect.TopLeft();
1332                 }
1333                 else
1334                 {
1335                     aPoint = aInnerRect.BottomLeft();
1336                     aPoint.AdjustY( -(aSize.Height()) );
1337                 }
1338             }
1339             else
1340             {
1341                 // get height from splitwindow
1342                 aSize.setHeight( pSplitWin->GetLineSize(nLine) );
1343                 aSize.setWidth( pImpl->aSplitSize.Width() );
1344             }
1345         }
1346 
1347         aDockingRect.SetSize( aSize );
1348         aDockingRect.SetPos( aPoint );
1349     }
1350 
1351     rRect = aDockingRect;
1352     return eDockAlign;
1353 }
1354 
1355 /** Virtual method of the SfxDockingWindow class. This method determines how
1356     the size of the DockingWindows changes depending on the alignment. The base
1357     implementation uses the floating mode, the size of the marked Floating
1358     Size. For horizontal alignment, the width will be the width of the outer
1359     DockingRectangle, with vertical alignment the height will be the height of
1360     the inner DockingRectangle (resulting from the order in which the SFX child
1361     windows are displayed). The other size is set to the current floating-size,
1362     this could changed by a to intervening derived class. The docking size must
1363     be the same for Left/Right and Top/Bottom.
1364 */
CalcDockingSize(SfxChildAlignment eAlign)1365 Size SfxDockingWindow::CalcDockingSize(SfxChildAlignment eAlign)
1366 {
1367     // Note: if the resizing is also possible in the docked state, then the
1368     // Floating-size does also have to be adjusted?
1369 
1370     Size aSize = GetFloatingSize();
1371     switch (eAlign)
1372     {
1373         case SfxChildAlignment::TOP:
1374         case SfxChildAlignment::BOTTOM:
1375         case SfxChildAlignment::LOWESTTOP:
1376         case SfxChildAlignment::HIGHESTTOP:
1377         case SfxChildAlignment::LOWESTBOTTOM:
1378         case SfxChildAlignment::HIGHESTBOTTOM:
1379             aSize.setWidth( aOuterRect.Right() - aOuterRect.Left() );
1380             break;
1381         case SfxChildAlignment::LEFT:
1382         case SfxChildAlignment::RIGHT:
1383         case SfxChildAlignment::FIRSTLEFT:
1384         case SfxChildAlignment::LASTLEFT:
1385         case SfxChildAlignment::FIRSTRIGHT:
1386         case SfxChildAlignment::LASTRIGHT:
1387             aSize.setHeight( aInnerRect.Bottom() - aInnerRect.Top() );
1388             break;
1389         case SfxChildAlignment::NOALIGNMENT:
1390             break;
1391               default:
1392                   break;
1393     }
1394 
1395     return aSize;
1396 }
1397 
1398 /** Virtual method of the SfxDockingWindow class. Here a derived class can
1399     disallow certain alignments. The base implementation does not
1400     prohibit alignment.
1401 */
CheckAlignment(SfxChildAlignment,SfxChildAlignment eAlign)1402 SfxChildAlignment SfxDockingWindow::CheckAlignment(SfxChildAlignment,
1403     SfxChildAlignment eAlign)
1404 {
1405     return eAlign;
1406 }
1407 
1408 /** The window is closed when the ChildWindow is destroyed by running the
1409     ChildWindow-slots. If this is method is overridden by a derived class
1410     method, then the SfxDockingDialogWindow: Close() must be called afterwards
1411     if the Close() was not cancelled with "return sal_False".
1412 */
Close()1413 bool SfxDockingWindow::Close()
1414 {
1415     // Execute with Parameters, since Toggle is ignored by some ChildWindows.
1416     if ( !pMgr )
1417         return true;
1418 
1419     SfxBoolItem aValue( pMgr->GetType(), false);
1420     pBindings->GetDispatcher_Impl()->ExecuteList(
1421         pMgr->GetType(), SfxCallMode::RECORD | SfxCallMode::ASYNCHRON,
1422         { &aValue });
1423     return true;
1424 }
1425 
Paint(vcl::RenderContext &,const tools::Rectangle &)1426 void SfxDockingWindow::Paint(vcl::RenderContext&, const tools::Rectangle& /*rRect*/)
1427 {
1428 }
1429 
1430 /** With this method, a minimal OutputSize be can set, that is queried in
1431     the Resizing()-Handler.
1432 */
SetMinOutputSizePixel(const Size & rSize)1433 void SfxDockingWindow::SetMinOutputSizePixel( const Size& rSize )
1434 {
1435     pImpl->aMinSize = rSize;
1436     ResizableDockingWindow::SetMinOutputSizePixel( rSize );
1437 }
1438 
1439 /** Set the minimum size which is returned.*/
GetMinOutputSizePixel() const1440 const Size& SfxDockingWindow::GetMinOutputSizePixel() const
1441 {
1442     return pImpl->aMinSize;
1443 }
1444 
EventNotify(NotifyEvent & rEvt)1445 bool SfxDockingWindow::EventNotify( NotifyEvent& rEvt )
1446 {
1447     if ( !pImpl )
1448         return ResizableDockingWindow::EventNotify( rEvt );
1449 
1450     if ( rEvt.GetType() == MouseNotifyEvent::GETFOCUS )
1451     {
1452         if (pMgr != nullptr)
1453             pBindings->SetActiveFrame( pMgr->GetFrame() );
1454 
1455         if ( pImpl->pSplitWin )
1456             pImpl->pSplitWin->SetActiveWindow_Impl( this );
1457         else if (pMgr != nullptr)
1458             pMgr->Activate_Impl();
1459 
1460         // In VCL EventNotify goes first to the window itself, also call the
1461         // base class, otherwise the parent learns nothing
1462         // if ( rEvt.GetWindow() == this )  PB: #i74693# not necessary any longer
1463         ResizableDockingWindow::EventNotify( rEvt );
1464         return true;
1465     }
1466     else if( rEvt.GetType() == MouseNotifyEvent::KEYINPUT )
1467     {
1468         // First, allow KeyInput for Dialog functions
1469         if (!DockingWindow::EventNotify(rEvt) && SfxViewShell::Current())
1470         {
1471             // then also for valid global accelerators.
1472             return SfxViewShell::Current()->GlobalKeyInput_Impl( *rEvt.GetKeyEvent() );
1473         }
1474         return true;
1475     }
1476     else if ( rEvt.GetType() == MouseNotifyEvent::LOSEFOCUS && !HasChildPathFocus() )
1477     {
1478         pBindings->SetActiveFrame( nullptr );
1479     }
1480 
1481     return ResizableDockingWindow::EventNotify( rEvt );
1482 }
1483 
SetItemSize_Impl(const Size & rSize)1484 void SfxDockingWindow::SetItemSize_Impl( const Size& rSize )
1485 {
1486     pImpl->aSplitSize = rSize;
1487 
1488     SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
1489     pWorkWin->ConfigChild_Impl( SfxChildIdentifier::SPLITWINDOW, SfxDockingConfig::ALIGNDOCKINGWINDOW, pMgr->GetType() );
1490 }
1491 
Disappear_Impl()1492 void SfxDockingWindow::Disappear_Impl()
1493 {
1494     if ( pImpl->pSplitWin && pImpl->pSplitWin->IsItemValid( GetType() ) )
1495         pImpl->pSplitWin->RemoveWindow(this);
1496 }
1497 
Reappear_Impl()1498 void SfxDockingWindow::Reappear_Impl()
1499 {
1500     if ( pImpl->pSplitWin && !pImpl->pSplitWin->IsItemValid( GetType() ) )
1501     {
1502         pImpl->pSplitWin->InsertWindow( this, pImpl->aSplitSize );
1503     }
1504 }
1505 
IsAutoHide_Impl() const1506 bool SfxDockingWindow::IsAutoHide_Impl() const
1507 {
1508     if ( pImpl->pSplitWin )
1509         return !pImpl->pSplitWin->IsFadeIn();
1510     else
1511         return false;
1512 }
1513 
AutoShow_Impl()1514 void SfxDockingWindow::AutoShow_Impl()
1515 {
1516     if ( pImpl->pSplitWin )
1517     {
1518         pImpl->pSplitWin->FadeIn();
1519     }
1520 }
1521 
StateChanged(StateChangedType nStateChange)1522 void SfxDockingWindow::StateChanged( StateChangedType nStateChange )
1523 {
1524     if ( nStateChange == StateChangedType::InitShow )
1525         Initialize_Impl();
1526 
1527     ResizableDockingWindow::StateChanged( nStateChange );
1528 }
1529 
Move()1530 void SfxDockingWindow::Move()
1531 {
1532     if ( pImpl )
1533         pImpl->aMoveIdle.Start();
1534 }
1535 
IMPL_LINK_NOARG(SfxDockingWindow,TimerHdl,Timer *,void)1536 IMPL_LINK_NOARG(SfxDockingWindow, TimerHdl, Timer *, void)
1537 {
1538     pImpl->aMoveIdle.Stop();
1539     if ( IsReallyVisible() && IsFloatingMode() )
1540     {
1541         SetFloatingSize( GetOutputSizePixel() );
1542         pImpl->aWinState = GetFloatingWindow()->GetWindowState();
1543         SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
1544         pWorkWin->ConfigChild_Impl( SfxChildIdentifier::SPLITWINDOW, SfxDockingConfig::ALIGNDOCKINGWINDOW, pMgr->GetType() );
1545     }
1546 }
1547 
1548 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1549