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