1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 #include <sfx2/sidebar/SidebarController.hxx>
20 #include <sfx2/sidebar/Deck.hxx>
21 #include <sfx2/sidebar/DeckDescriptor.hxx>
22 #include <sfx2/sidebar/DeckTitleBar.hxx>
23 #include <sfx2/sidebar/Panel.hxx>
24 #include <sfx2/sidebar/PanelDescriptor.hxx>
25 #include <sfx2/sidebar/PanelTitleBar.hxx>
26 #include <sfx2/sidebar/TabBar.hxx>
27 #include <sfx2/sidebar/Theme.hxx>
28 #include <sfx2/sidebar/SidebarChildWindow.hxx>
29 #include <sfx2/sidebar/Tools.hxx>
30 #include <sfx2/sidebar/SidebarDockingWindow.hxx>
31 #include <sfx2/sidebar/Context.hxx>
32 #include <sfx2/sidebar/ContextList.hxx>
35 #include <sfx2/lokhelper.hxx>
36 #include <sfx2/sfxresid.hxx>
37 #include <sfx2/sfxsids.hrc>
38 #include <sfx2/titledockwin.hxx>
39 #include <sfx2/strings.hrc>
40 #include <framework/ContextChangeEventMultiplexerTunnel.hxx>
41 #include <vcl/floatwin.hxx>
42 #include <vcl/fixed.hxx>
43 #include <vcl/uitest/logger.hxx>
44 #include <vcl/uitest/eventdescription.hxx>
45 #include <vcl/svapp.hxx>
46 #include <splitwin.hxx>
47 #include <tools/diagnose_ex.h>
48 #include <tools/link.hxx>
49 #include <toolkit/helper/vclunohelper.hxx>
50 #include <comphelper/processfactory.hxx>
51 #include <comphelper/namedvaluecollection.hxx>
52 #include <comphelper/lok.hxx>
53 #include <sal/log.hxx>
54 #include <officecfg/Office/UI/Sidebar.hxx>
55 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
57 #include <com/sun/star/awt/XWindowPeer.hpp>
58 #include <com/sun/star/frame/XDispatch.hpp>
59 #include <com/sun/star/lang/XInitialization.hpp>
60 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
61 #include <com/sun/star/ui/ContextChangeEventObject.hpp>
62 #include <com/sun/star/ui/theUIElementFactoryManager.hpp>
63 #include <com/sun/star/util/XURLTransformer.hpp>
64 #include <com/sun/star/util/URL.hpp>
65 #include <com/sun/star/rendering/XSpriteCanvas.hpp>
69 using namespace css::uno
;
73 const static char gsReadOnlyCommandName
[] = ".uno:EditDoc";
74 const static sal_Int32
gnWidthCloseThreshold (70);
75 const static sal_Int32
gnWidthOpenThreshold (40);
77 std::string
UnoNameFromDeckId(const OUString
& rsDeckId
, bool isDraw
=false)
79 if (rsDeckId
== "SdCustomAnimationDeck")
80 return ".uno:CustomAnimation";
82 if (rsDeckId
== "PropertyDeck")
83 return isDraw
? ".uno:ModifyPage" : ".uno:Sidebar";
85 if (rsDeckId
== "SdLayoutsDeck")
86 return ".uno:ModifyPage";
88 if (rsDeckId
== "SdSlideTransitionDeck")
89 return ".uno:SlideChangeWindow";
91 if (rsDeckId
== "SdAllMasterPagesDeck")
92 return ".uno:MasterSlidesPanel";
94 if (rsDeckId
== "SdMasterPagesDeck")
95 return ".uno:MasterSlidesPanel";
97 if (rsDeckId
== "GalleryDeck")
98 return ".uno:Gallery";
104 namespace sfx2
{ namespace sidebar
{
109 MID_UNLOCK_TASK_PANEL
= 1,
115 MID_FIRST_HIDE
= 1000
118 /** When in doubt, show this deck.
120 static const char gsDefaultDeckId
[] = "PropertyDeck";
123 SidebarController::SidebarController (
124 SidebarDockingWindow
* pParentWindow
,
125 const SfxViewFrame
* pViewFrame
)
126 : SidebarControllerInterfaceBase(m_aMutex
),
128 mpParentWindow(pParentWindow
),
129 mpViewFrame(pViewFrame
),
130 mxFrame(pViewFrame
->GetFrame().GetFrameInterface()),
131 mpTabBar(VclPtr
<TabBar
>::Create(
134 [this](const OUString
& rsDeckId
) { return this->OpenThenToggleDeck(rsDeckId
); },
135 [this](const tools::Rectangle
& rButtonBox
,const ::std::vector
<TabBar::DeckMenuData
>& rMenuData
) { return this->ShowPopupMenu(rButtonBox
,rMenuData
); },
137 maCurrentContext(OUString(), OUString()),
138 maRequestedContext(),
139 mnRequestedForceFlags(SwitchFlag_NoForce
),
140 mnMaximumSidebarWidth(officecfg::Office::UI::Sidebar::General::MaximumWidth::get()),
141 msCurrentDeckId(gsDefaultDeckId
),
142 maPropertyChangeForwarder([this](){ return this->BroadcastPropertyChange(); }),
143 maContextChangeUpdate([this](){ return this->UpdateConfigurations(); }),
144 maAsynchronousDeckSwitch(),
145 mbIsDeckRequestedOpen(),
147 mbFloatingDeckClosed(!pParentWindow
->IsFloatingMode()),
148 mnSavedSidebarWidth(pParentWindow
->GetSizePixel().Width()),
149 maFocusManager([this](const Panel
& rPanel
){ return this->ShowPanel(rPanel
); },
150 [this](const sal_Int32 nIndex
){ return this->IsDeckOpen(nIndex
); }),
151 mxReadOnlyModeDispatch(),
152 mbIsDocumentReadOnly(false),
153 mpSplitWindow(nullptr),
154 mnWidthOnSplitterButtonDown(0),
157 // Decks and panel collections for this sidebar
158 mpResourceManager
= std::make_unique
<ResourceManager
>();
161 rtl::Reference
<SidebarController
> SidebarController::create(SidebarDockingWindow
* pParentWindow
,
162 const SfxViewFrame
* pViewFrame
)
164 rtl::Reference
<SidebarController
> instance(new SidebarController(pParentWindow
, pViewFrame
));
166 const css::uno::Reference
<css::frame::XFrame
>& rxFrame
= pViewFrame
->GetFrame().GetFrameInterface();
167 registerSidebarForFrame(instance
.get(), rxFrame
->getController());
168 rxFrame
->addFrameActionListener(instance
.get());
169 // Listen for window events.
170 instance
->mpParentWindow
->AddEventListener(LINK(instance
.get(), SidebarController
, WindowEventHandler
));
172 // Listen for theme property changes.
173 Theme::GetPropertySet()->addPropertyChangeListener(
175 static_cast<css::beans::XPropertyChangeListener
*>(instance
.get()));
177 // Get the dispatch object as preparation to listen for changes of
178 // the read-only state.
179 const util::URL
aURL (Tools::GetURL(gsReadOnlyCommandName
));
180 instance
->mxReadOnlyModeDispatch
= Tools::GetDispatch(rxFrame
, aURL
);
181 if (instance
->mxReadOnlyModeDispatch
.is())
182 instance
->mxReadOnlyModeDispatch
->addStatusListener(instance
.get(), aURL
);
184 //first UpdateConfigurations call will SwitchToDeck
189 SidebarController::~SidebarController()
193 SidebarController
* SidebarController::GetSidebarControllerForFrame (
194 const css::uno::Reference
<css::frame::XFrame
>& rxFrame
)
196 uno::Reference
<frame::XController
> const xController(rxFrame
->getController());
197 if (!xController
.is()) // this may happen during dispose of Draw controller but perhaps it's a bug
199 SAL_WARN("sfx.sidebar", "GetSidebarControllerForFrame: frame has no XController");
202 uno::Reference
<ui::XContextChangeEventListener
> const xListener(
203 framework::GetFirstListenerWith(xController
,
204 [] (uno::Reference
<uno::XInterface
> const& xRef
)
205 { return nullptr != dynamic_cast<SidebarController
*>(xRef
.get()); }
208 return dynamic_cast<SidebarController
*>(xListener
.get());
211 void SidebarController::registerSidebarForFrame(SidebarController
* pController
, const css::uno::Reference
<css::frame::XController
>& xController
)
213 // Listen for context change events.
214 css::uno::Reference
<css::ui::XContextChangeEventMultiplexer
> xMultiplexer (
215 css::ui::ContextChangeEventMultiplexer::get(
216 ::comphelper::getProcessComponentContext()));
217 xMultiplexer
->addContextChangeEventListener(
218 static_cast<css::ui::XContextChangeEventListener
*>(pController
),
222 void SidebarController::unregisterSidebarForFrame(SidebarController
* pController
, const css::uno::Reference
<css::frame::XController
>& xController
)
224 css::uno::Reference
<css::ui::XContextChangeEventMultiplexer
> xMultiplexer (
225 css::ui::ContextChangeEventMultiplexer::get(
226 ::comphelper::getProcessComponentContext()));
227 xMultiplexer
->removeContextChangeEventListener(
228 static_cast<css::ui::XContextChangeEventListener
*>(pController
),
232 void SidebarController::disposeDecks()
234 SolarMutexGuard aSolarMutexGuard
;
236 if (comphelper::LibreOfficeKit::isActive())
238 if (const SfxViewShell
* pViewShell
= mpViewFrame
->GetViewShell())
240 const std::string hide
= UnoNameFromDeckId(msCurrentDeckId
);
242 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
243 (hide
+ "=false").c_str());
246 mpParentWindow
->ReleaseLOKNotifier();
249 mpCurrentDeck
.clear();
250 maFocusManager
.Clear();
251 mpResourceManager
->disposeDecks();
254 void SAL_CALL
SidebarController::disposing()
256 mpCloseIndicator
.disposeAndClear();
258 maFocusManager
.Clear();
259 mpTabBar
.disposeAndClear();
261 // save decks settings
262 // Impress shutdown : context (frame) is disposed before sidebar disposing
263 // calc writer : context (frame) is disposed after sidebar disposing
264 // so need to test if GetCurrentContext is still valid regarding msApplication
266 if (GetCurrentContext().msApplication
!= "none")
268 mpResourceManager
->SaveDecksSettings(GetCurrentContext());
269 mpResourceManager
->SaveLastActiveDeck(GetCurrentContext(), msCurrentDeckId
);
275 uno::Reference
<css::frame::XController
> xController
= mxFrame
->getController();
276 if (!xController
.is())
277 xController
= mxCurrentController
;
279 mxFrame
->removeFrameActionListener(this);
280 unregisterSidebarForFrame(this, xController
);
282 if (mxReadOnlyModeDispatch
.is())
283 mxReadOnlyModeDispatch
->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName
));
284 if (mpSplitWindow
!= nullptr)
286 mpSplitWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
287 mpSplitWindow
= nullptr;
290 if (mpParentWindow
!= nullptr)
292 mpParentWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
293 mpParentWindow
= nullptr;
296 Theme::GetPropertySet()->removePropertyChangeListener(
298 static_cast<css::beans::XPropertyChangeListener
*>(this));
300 maContextChangeUpdate
.CancelRequest();
301 maAsynchronousDeckSwitch
.CancelRequest();
305 void SAL_CALL
SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject
& rEvent
)
307 // Update to the requested new context asynchronously to avoid
308 // subtle errors caused by SFX2 which in rare cases can not
309 // properly handle a synchronous update.
311 maRequestedContext
= Context(
312 rEvent
.ApplicationName
,
315 if (maRequestedContext
!= maCurrentContext
)
317 mxCurrentController
.set(rEvent
.Source
, css::uno::UNO_QUERY
);
318 maAsynchronousDeckSwitch
.CancelRequest();
319 maContextChangeUpdate
.RequestCall();
320 // TODO: this call is redundant but mandatory for unit test to update context on document loading
321 UpdateConfigurations();
325 void SAL_CALL
SidebarController::disposing (const css::lang::EventObject
& )
330 void SAL_CALL
SidebarController::propertyChange (const css::beans::PropertyChangeEvent
& )
332 maPropertyChangeForwarder
.RequestCall();
335 void SAL_CALL
SidebarController::statusChanged (const css::frame::FeatureStateEvent
& rEvent
)
337 bool bIsReadWrite (true);
338 if (rEvent
.IsEnabled
)
339 rEvent
.State
>>= bIsReadWrite
;
341 if (mbIsDocumentReadOnly
!= !bIsReadWrite
)
343 mbIsDocumentReadOnly
= !bIsReadWrite
;
345 // Force the current deck to update its panel list.
346 if ( ! mbIsDocumentReadOnly
)
347 SwitchToDefaultDeck();
349 mnRequestedForceFlags
|= SwitchFlag_ForceSwitch
;
350 maAsynchronousDeckSwitch
.CancelRequest();
351 maContextChangeUpdate
.RequestCall();
355 void SAL_CALL
SidebarController::requestLayout()
357 sal_Int32 nMinimalWidth
= 0;
358 if (mpCurrentDeck
&& !mpCurrentDeck
->isDisposed())
360 mpCurrentDeck
->RequestLayout();
361 nMinimalWidth
= mpCurrentDeck
->GetMinimalWidth();
363 RestrictWidth(nMinimalWidth
);
366 void SidebarController::BroadcastPropertyChange()
368 mpParentWindow
->Invalidate(InvalidateFlags::Children
);
371 void SidebarController::NotifyResize()
375 OSL_ASSERT(mpTabBar
!=nullptr);
379 vcl::Window
* pParentWindow
= mpTabBar
->GetParent();
380 const sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor();
382 const sal_Int32
nWidth (pParentWindow
->GetSizePixel().Width());
383 const sal_Int32
nHeight (pParentWindow
->GetSizePixel().Height());
385 mbIsDeckOpen
= (nWidth
> nTabBarDefaultWidth
);
387 if (mnSavedSidebarWidth
<= 0)
388 mnSavedSidebarWidth
= nWidth
;
391 const bool bIsOpening (nWidth
> mnWidthOnSplitterButtonDown
);
393 bIsDeckVisible
= nWidth
>= nTabBarDefaultWidth
+ gnWidthOpenThreshold
;
395 bIsDeckVisible
= nWidth
>= nTabBarDefaultWidth
+ gnWidthCloseThreshold
;
396 mbIsDeckRequestedOpen
= bIsDeckVisible
;
397 UpdateCloseIndicator(!bIsDeckVisible
);
399 if (mpCurrentDeck
&& !mpCurrentDeck
->isDisposed())
401 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
402 WindowAlign eAlign
= pSplitWindow
? pSplitWindow
->GetAlign() : WindowAlign::Right
;
404 if (eAlign
== WindowAlign::Left
) // attach the Sidebar towards the left-side of screen
406 nDeckX
= nTabBarDefaultWidth
;
409 else // attach the Sidebar towards the right-side of screen
412 nTabX
= nWidth
- nTabBarDefaultWidth
;
415 // Place the deck first.
418 if (comphelper::LibreOfficeKit::isActive())
420 // We want to let the layouter use up as much of the
421 // height as necessary to make sure no scrollbar is
422 // visible. This only works when there are no greedy
423 // panes that fill up all available area. So we only
424 // use this for the PropertyDeck, which has no such
425 // panes, while most other do. This is fine, since
426 // it's the PropertyDeck that really has many panes
427 // that can collapse or expand. For others, limit
428 // the height to something sensible.
429 const sal_Int32 nExtHeight
= (msCurrentDeckId
== "PropertyDeck" ? 2000 : 600);
430 // No TabBar in LOK (use nWidth in full).
431 mpCurrentDeck
->setPosSizePixel(nDeckX
, 0, nWidth
, nExtHeight
);
434 mpCurrentDeck
->setPosSizePixel(nDeckX
, 0, nWidth
- nTabBarDefaultWidth
, nHeight
);
435 mpCurrentDeck
->Show();
436 mpCurrentDeck
->RequestLayout();
439 mpCurrentDeck
->Hide();
441 // Now place the tab bar.
442 mpTabBar
->setPosSizePixel(nTabX
, 0, nTabBarDefaultWidth
, nHeight
);
443 if (!comphelper::LibreOfficeKit::isActive())
444 mpTabBar
->Show(); // Don't show TabBar in LOK.
447 // Determine if the closer of the deck can be shown.
448 sal_Int32 nMinimalWidth
= 0;
449 if (mpCurrentDeck
&& !mpCurrentDeck
->isDisposed())
451 VclPtr
<DeckTitleBar
> pTitleBar
= mpCurrentDeck
->GetTitleBar();
452 if (pTitleBar
&& pTitleBar
->IsVisible())
453 pTitleBar
->SetCloserVisible(CanModifyChildWindowWidth());
454 nMinimalWidth
= mpCurrentDeck
->GetMinimalWidth();
457 RestrictWidth(nMinimalWidth
);
459 mpParentWindow
->NotifyResize();
462 void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth
)
464 if ( ! mbIsDeckRequestedOpen
)
467 if (mbIsDeckRequestedOpen
.get())
469 // Deck became large enough to be shown. Show it.
470 mnSavedSidebarWidth
= nNewWidth
;
475 // Deck became too small. Close it completely.
476 // If window is wider than the tab bar then mark the deck as being visible, even when it is not.
477 // This is to trigger an adjustment of the width to the width of the tab bar.
481 if (mnWidthOnSplitterButtonDown
> TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor())
482 mnSavedSidebarWidth
= mnWidthOnSplitterButtonDown
;
486 void SidebarController::UpdateConfigurations()
488 if (maCurrentContext
== maRequestedContext
489 && mnRequestedForceFlags
== SwitchFlag_NoForce
)
492 if ((maCurrentContext
.msApplication
!= "none") &&
493 !maCurrentContext
.msApplication
.isEmpty())
495 mpResourceManager
->SaveDecksSettings(maCurrentContext
);
496 mpResourceManager
->SetLastActiveDeck(maCurrentContext
, msCurrentDeckId
);
499 // get last active deck for this application on first update
500 if (!maRequestedContext
.msApplication
.isEmpty() &&
501 (maCurrentContext
.msApplication
!= maRequestedContext
.msApplication
))
503 OUString sLastActiveDeck
= mpResourceManager
->GetLastActiveDeck( maRequestedContext
);
504 if (!sLastActiveDeck
.isEmpty())
505 msCurrentDeckId
= sLastActiveDeck
;
508 maCurrentContext
= maRequestedContext
;
510 mpResourceManager
->InitDeckContext(GetCurrentContext());
512 // Find the set of decks that could be displayed for the new context.
513 ResourceManager::DeckContextDescriptorContainer aDecks
;
515 css::uno::Reference
<css::frame::XController
> xController
= mxCurrentController
.is() ? mxCurrentController
: mxFrame
->getController();
517 mpResourceManager
->GetMatchingDecks (
520 mbIsDocumentReadOnly
,
523 // Notify the tab bar about the updated set of decks.
524 mpTabBar
->SetDecks(aDecks
);
526 // Find the new deck. By default that is the same as the old
527 // one. If that is not set or not enabled, then choose the
528 // first enabled deck (which is PropertyDeck).
530 for (const auto& rDeck
: aDecks
)
532 if (rDeck
.mbIsEnabled
)
534 if (rDeck
.msId
== msCurrentDeckId
)
536 sNewDeckId
= msCurrentDeckId
;
539 else if (sNewDeckId
.getLength() == 0)
540 sNewDeckId
= rDeck
.msId
;
544 if (sNewDeckId
.getLength() == 0)
546 // We did not find a valid deck.
551 // Tell the tab bar to highlight the button associated
553 mpTabBar
->HighlightDeck(sNewDeckId
);
555 std::shared_ptr
<DeckDescriptor
> xDescriptor
= mpResourceManager
->GetDeckDescriptor(sNewDeckId
);
559 SwitchToDeck(*xDescriptor
, maCurrentContext
);
565 void collectUIInformation(const OUString
& rDeckId
)
567 EventDescription aDescription
;
568 aDescription
.aAction
= "SIDEBAR";
569 aDescription
.aParent
= "MainWindow";
570 aDescription
.aParameters
= {{"PANEL", rDeckId
}};
571 aDescription
.aKeyWord
= "CurrentApp";
573 UITestLogger::getInstance().logEvent(aDescription
);
578 void SidebarController::OpenThenToggleDeck (
579 const OUString
& rsDeckId
)
581 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
582 if ( pSplitWindow
&& !pSplitWindow
->IsFadeIn() )
583 // tdf#83546 Collapsed sidebar should expand first
584 pSplitWindow
->FadeIn();
585 else if ( IsDeckVisible( rsDeckId
) )
587 if( !WasFloatingDeckClosed() )
589 // tdf#88241 Summoning an undocked sidebar a second time should close sidebar
590 mpParentWindow
->Close();
595 // tdf#67627 Clicking a second time on a Deck icon will close the Deck
601 SwitchToDeck(rsDeckId
);
603 // Make sure the sidebar is wide enough to fit the requested content
604 sal_Int32 nRequestedWidth
= (mpCurrentDeck
->GetMinimalWidth() + TabBar::GetDefaultWidth())
605 * mpTabBar
->GetDPIScaleFactor();
606 if (mnSavedSidebarWidth
< nRequestedWidth
)
607 SetChildWindowWidth(nRequestedWidth
);
609 collectUIInformation(rsDeckId
);
612 void SidebarController::OpenThenSwitchToDeck (
613 const OUString
& rsDeckId
)
616 SwitchToDeck(rsDeckId
);
620 void SidebarController::SwitchToDefaultDeck()
622 SwitchToDeck(gsDefaultDeckId
);
625 void SidebarController::SwitchToDeck (
626 const OUString
& rsDeckId
)
628 if ( msCurrentDeckId
!= rsDeckId
630 || mnRequestedForceFlags
!=SwitchFlag_NoForce
)
632 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= mpResourceManager
->GetDeckDescriptor(rsDeckId
);
635 SwitchToDeck(*xDeckDescriptor
, maCurrentContext
);
639 void SidebarController::CreateDeck(const OUString
& rDeckId
) {
640 CreateDeck(rDeckId
, maCurrentContext
);
643 void SidebarController::CreateDeck(const OUString
& rDeckId
, const Context
& rContext
, bool bForceCreate
)
645 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= mpResourceManager
->GetDeckDescriptor(rDeckId
);
647 if (!xDeckDescriptor
)
650 VclPtr
<Deck
> aDeck
= xDeckDescriptor
->mpDeck
;
651 if (aDeck
.get()==nullptr || bForceCreate
)
653 if (aDeck
.get()!=nullptr)
654 aDeck
.disposeAndClear();
656 aDeck
= VclPtr
<Deck
>::Create(
659 [this]() { return this->RequestCloseDeck(); });
661 xDeckDescriptor
->mpDeck
= aDeck
;
662 CreatePanels(rDeckId
, rContext
);
665 void SidebarController::CreatePanels(const OUString
& rDeckId
, const Context
& rContext
)
667 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= mpResourceManager
->GetDeckDescriptor(rDeckId
);
669 // init panels bounded to that deck, do not wait them being displayed as may be accessed through API
671 VclPtr
<Deck
> pDeck
= xDeckDescriptor
->mpDeck
;
673 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors
;
675 css::uno::Reference
<css::frame::XController
> xController
= mxCurrentController
.is() ? mxCurrentController
: mxFrame
->getController();
677 mpResourceManager
->GetMatchingPanels(
678 aPanelContextDescriptors
,
683 // Update the panel list.
684 const sal_Int32
nNewPanelCount (aPanelContextDescriptors
.size());
685 SharedPanelContainer aNewPanels
;
686 sal_Int32
nWriteIndex (0);
688 aNewPanels
.resize(nNewPanelCount
);
690 for (sal_Int32 nReadIndex
=0; nReadIndex
<nNewPanelCount
; ++nReadIndex
)
692 const ResourceManager::PanelContextDescriptor
& rPanelContexDescriptor (
693 aPanelContextDescriptors
[nReadIndex
]);
695 // Determine if the panel can be displayed.
696 const bool bIsPanelVisible (!mbIsDocumentReadOnly
|| rPanelContexDescriptor
.mbShowForReadOnlyDocuments
);
697 if ( ! bIsPanelVisible
)
700 Panel
*const pPanel(pDeck
->GetPanel(rPanelContexDescriptor
.msId
));
701 if (pPanel
!= nullptr)
703 aNewPanels
[nWriteIndex
] = pPanel
;
704 pPanel
->SetExpanded( rPanelContexDescriptor
.mbIsInitiallyVisible
);
709 VclPtr
<Panel
> aPanel
= CreatePanel(
710 rPanelContexDescriptor
.msId
,
711 pDeck
->GetPanelParentWindow(),
712 rPanelContexDescriptor
.mbIsInitiallyVisible
,
715 if (aPanel
.get()!=nullptr )
717 aNewPanels
[nWriteIndex
] = aPanel
;
719 // Depending on the context we have to change the command
720 // for the "more options" dialog.
721 VclPtr
<PanelTitleBar
> pTitleBar
= aNewPanels
[nWriteIndex
]->GetTitleBar();
724 pTitleBar
->SetMoreOptionsCommand(
725 rPanelContexDescriptor
.msMenuCommand
,
726 mxFrame
, xController
);
733 // mpCurrentPanels - may miss stuff (?)
734 aNewPanels
.resize(nWriteIndex
);
735 pDeck
->ResetPanels(aNewPanels
);
738 void SidebarController::SwitchToDeck (
739 const DeckDescriptor
& rDeckDescriptor
,
740 const Context
& rContext
)
742 if (comphelper::LibreOfficeKit::isActive())
744 if (const SfxViewShell
* pViewShell
= mpViewFrame
->GetViewShell())
746 if (msCurrentDeckId
!= rDeckDescriptor
.msId
)
748 const std::string hide
= UnoNameFromDeckId(msCurrentDeckId
);
750 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
751 (hide
+ "=false").c_str());
754 const std::string show
= UnoNameFromDeckId(rDeckDescriptor
.msId
);
756 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
757 (show
+ "=true").c_str());
761 maFocusManager
.Clear();
763 const bool bForceNewDeck ((mnRequestedForceFlags
&SwitchFlag_ForceNewDeck
)!=0);
764 const bool bForceNewPanels ((mnRequestedForceFlags
&SwitchFlag_ForceNewPanels
)!=0);
765 mnRequestedForceFlags
= SwitchFlag_NoForce
;
767 if ( msCurrentDeckId
!= rDeckDescriptor
.msId
771 mpCurrentDeck
->Hide();
773 msCurrentDeckId
= rDeckDescriptor
.msId
;
775 mpTabBar
->Invalidate();
776 mpTabBar
->HighlightDeck(msCurrentDeckId
);
778 // Determine the panels to display in the deck.
779 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors
;
781 css::uno::Reference
<css::frame::XController
> xController
= mxCurrentController
.is() ? mxCurrentController
: mxFrame
->getController();
783 mpResourceManager
->GetMatchingPanels(
784 aPanelContextDescriptors
,
786 rDeckDescriptor
.msId
,
789 if (aPanelContextDescriptors
.empty())
791 // There are no panels to be displayed in the current context.
792 if (vcl::EnumContext::GetContextEnum(rContext
.msContext
) != vcl::EnumContext::Context::Empty
)
794 // Switch to the "empty" context and try again.
798 rContext
.msApplication
,
799 vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Empty
)));
804 // This is already the "empty" context. Looks like we have
805 // to live with an empty deck.
809 // Provide a configuration and Deck object.
811 CreateDeck(rDeckDescriptor
.msId
, rContext
, bForceNewDeck
);
813 if (bForceNewPanels
&& !bForceNewDeck
) // already forced if bForceNewDeck
814 CreatePanels(rDeckDescriptor
.msId
, rContext
);
816 if (mpCurrentDeck
&& mpCurrentDeck
!= rDeckDescriptor
.mpDeck
)
817 mpCurrentDeck
->Hide();
818 mpCurrentDeck
.reset(rDeckDescriptor
.mpDeck
);
820 if ( ! mpCurrentDeck
)
824 // Show the context name in the deck title bar.
825 VclPtr
<DeckTitleBar
> pDebugTitleBar
= mpCurrentDeck
->GetTitleBar();
827 pDebugTitleBar
->SetTitle(rDeckDescriptor
.msTitle
+ " (" + maCurrentContext
.msContext
+ ")");
830 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
831 sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor();
832 WindowAlign eAlign
= pSplitWindow
? pSplitWindow
->GetAlign() : WindowAlign::Right
;
834 if (eAlign
== WindowAlign::Left
) // attach the Sidebar towards the left-side of screen
836 nDeckX
= nTabBarDefaultWidth
;
838 else // attach the Sidebar towards the right-side of screen
843 // Activate the deck and the new set of panels.
844 mpCurrentDeck
->setPosSizePixel(
847 mpParentWindow
->GetSizePixel().Width() - nTabBarDefaultWidth
,
848 mpParentWindow
->GetSizePixel().Height());
850 mpCurrentDeck
->Show();
852 mpParentWindow
->SetText(rDeckDescriptor
.msTitle
);
856 // Tell the focus manager about the new panels and tab bar
858 maFocusManager
.SetDeckTitle(mpCurrentDeck
->GetTitleBar());
859 maFocusManager
.SetPanels(mpCurrentDeck
->GetPanels());
861 mpTabBar
->UpdateFocusManager(maFocusManager
);
862 UpdateTitleBarIcons();
865 void SidebarController::notifyDeckTitle(const OUString
& targetDeckId
)
867 if (msCurrentDeckId
== targetDeckId
)
869 maFocusManager
.SetDeckTitle(mpCurrentDeck
->GetTitleBar());
870 mpTabBar
->UpdateFocusManager(maFocusManager
);
871 UpdateTitleBarIcons();
875 VclPtr
<Panel
> SidebarController::CreatePanel (
876 const OUString
& rsPanelId
,
877 vcl::Window
* pParentWindow
,
878 const bool bIsInitiallyExpanded
,
879 const Context
& rContext
,
880 const VclPtr
<Deck
>& pDeck
)
882 std::shared_ptr
<PanelDescriptor
> xPanelDescriptor
= mpResourceManager
->GetPanelDescriptor(rsPanelId
);
884 if (!xPanelDescriptor
)
887 // Create the panel which is the parent window of the UIElement.
888 VclPtr
<Panel
> pPanel
= VclPtr
<Panel
>::Create(
891 bIsInitiallyExpanded
,
892 [pDeck
]() { return pDeck
.get()->RequestLayout(); },
893 [this]() { return this->GetCurrentContext(); },
896 // Create the XUIElement.
897 Reference
<ui::XUIElement
> xUIElement (CreateUIElement(
898 pPanel
->GetComponentInterface(),
899 xPanelDescriptor
->msImplementationURL
,
900 xPanelDescriptor
->mbWantsCanvas
,
904 // Initialize the panel and add it to the active deck.
905 pPanel
->SetUIElement(xUIElement
);
909 pPanel
.disposeAndClear();
915 Reference
<ui::XUIElement
> SidebarController::CreateUIElement (
916 const Reference
<awt::XWindowPeer
>& rxWindow
,
917 const OUString
& rsImplementationURL
,
918 const bool bWantsCanvas
,
919 const Context
& rContext
)
923 const Reference
<XComponentContext
> xComponentContext (::comphelper::getProcessComponentContext() );
924 const Reference
<ui::XUIElementFactory
> xUIElementFactory
=
925 ui::theUIElementFactoryManager::get( xComponentContext
);
927 // Create the XUIElement.
928 ::comphelper::NamedValueCollection aCreationArguments
;
929 aCreationArguments
.put("Frame", makeAny(mxFrame
));
930 aCreationArguments
.put("ParentWindow", makeAny(rxWindow
));
931 SfxDockingWindow
* pSfxDockingWindow
= dynamic_cast<SfxDockingWindow
*>(mpParentWindow
.get());
932 if (pSfxDockingWindow
!= nullptr)
933 aCreationArguments
.put("SfxBindings", makeAny(sal_uInt64(&pSfxDockingWindow
->GetBindings())));
934 aCreationArguments
.put("Theme", Theme::GetPropertySet());
935 aCreationArguments
.put("Sidebar", makeAny(Reference
<ui::XSidebar
>(static_cast<ui::XSidebar
*>(this))));
938 Reference
<rendering::XSpriteCanvas
> xCanvas (VCLUnoHelper::GetWindow(rxWindow
)->GetSpriteCanvas());
939 aCreationArguments
.put("Canvas", makeAny(xCanvas
));
942 if (mxCurrentController
.is())
944 OUString aModule
= Tools::GetModuleName(mxCurrentController
);
945 if (!aModule
.isEmpty())
947 aCreationArguments
.put("Module", makeAny(aModule
));
949 aCreationArguments
.put("Controller", makeAny(mxCurrentController
));
952 aCreationArguments
.put("ApplicationName", makeAny(rContext
.msApplication
));
953 aCreationArguments
.put("ContextName", makeAny(rContext
.msContext
));
955 Reference
<ui::XUIElement
> xUIElement(
956 xUIElementFactory
->createUIElement(
958 aCreationArguments
.getPropertyValues()),
963 catch(const Exception
&)
965 TOOLS_WARN_EXCEPTION("sfx.sidebar", "Cannot create panel " << rsImplementationURL
);
970 IMPL_LINK(SidebarController
, WindowEventHandler
, VclWindowEvent
&, rEvent
, void)
972 if (rEvent
.GetWindow() == mpParentWindow
)
974 switch (rEvent
.GetId())
976 case VclEventId::WindowShow
:
977 case VclEventId::WindowResize
:
981 case VclEventId::WindowDataChanged
:
982 // Force an update of deck and tab bar to reflect
983 // changes in theme (high contrast mode).
984 Theme::HandleDataChange();
985 UpdateTitleBarIcons();
986 mpParentWindow
->Invalidate();
987 mnRequestedForceFlags
|= SwitchFlag_ForceNewDeck
| SwitchFlag_ForceNewPanels
;
988 maAsynchronousDeckSwitch
.CancelRequest();
989 maContextChangeUpdate
.RequestCall();
992 case VclEventId::ObjectDying
:
996 case VclEventId::WindowPaint
:
997 SAL_INFO("sfx.sidebar", "Paint");
1004 else if (rEvent
.GetWindow()==mpSplitWindow
&& mpSplitWindow
!=nullptr)
1006 switch (rEvent
.GetId())
1008 case VclEventId::WindowMouseButtonDown
:
1009 mnWidthOnSplitterButtonDown
= mpParentWindow
->GetSizePixel().Width();
1012 case VclEventId::WindowMouseButtonUp
:
1014 ProcessNewWidth(mpParentWindow
->GetSizePixel().Width());
1015 mnWidthOnSplitterButtonDown
= 0;
1019 case VclEventId::ObjectDying
:
1028 void SidebarController::ShowPopupMenu (
1029 const tools::Rectangle
& rButtonBox
,
1030 const ::std::vector
<TabBar::DeckMenuData
>& rMenuData
) const
1032 VclPtr
<PopupMenu
> pMenu
= CreatePopupMenu(rMenuData
);
1033 pMenu
->SetSelectHdl(LINK(const_cast<SidebarController
*>(this), SidebarController
, OnMenuItemSelected
));
1035 // pass toolbox button rect so the menu can stay open on button up
1036 tools::Rectangle
aBox (rButtonBox
);
1037 aBox
.Move(mpTabBar
->GetPosPixel().X(), 0);
1038 const PopupMenuFlags aMenuDirection
1039 = (comphelper::LibreOfficeKit::isActive() ? PopupMenuFlags::ExecuteLeft
1040 : PopupMenuFlags::ExecuteDown
);
1041 pMenu
->Execute(mpParentWindow
, aBox
, aMenuDirection
);
1042 pMenu
.disposeAndClear();
1046 SidebarController::CreatePopupMenu(const ::std::vector
<TabBar::DeckMenuData
>& rMenuData
) const
1048 // Create the top level popup menu.
1049 auto pMenu
= VclPtr
<PopupMenu
>::Create();
1050 FloatingWindow
* pMenuWindow
= dynamic_cast<FloatingWindow
*>(pMenu
->GetWindow());
1051 if (pMenuWindow
!= nullptr)
1053 pMenuWindow
->SetPopupModeFlags(pMenuWindow
->GetPopupModeFlags()
1054 | FloatWinPopupFlags::NoMouseUpClose
);
1057 // Create sub menu for customization (hiding of deck tabs), only on desktop.
1058 VclPtr
<PopupMenu
> pCustomizationMenu
1059 = (comphelper::LibreOfficeKit::isActive() ? nullptr : VclPtr
<PopupMenu
>::Create());
1061 // Add one entry for every tool panel element to individually make
1062 // them visible or hide them.
1063 sal_Int32
nIndex (0);
1064 for (const auto& rItem
: rMenuData
)
1066 const sal_Int32
nMenuIndex (nIndex
+MID_FIRST_PANEL
);
1067 pMenu
->InsertItem(nMenuIndex
, rItem
.msDisplayName
, MenuItemBits::RADIOCHECK
);
1068 pMenu
->CheckItem(nMenuIndex
, rItem
.mbIsCurrentDeck
);
1069 pMenu
->EnableItem(nMenuIndex
, rItem
.mbIsEnabled
&& rItem
.mbIsActive
);
1071 if (!comphelper::LibreOfficeKit::isActive())
1073 const sal_Int32
nSubMenuIndex(nIndex
+ MID_FIRST_HIDE
);
1074 if (rItem
.mbIsCurrentDeck
)
1076 // Don't allow the currently visible deck to be disabled.
1077 pCustomizationMenu
->InsertItem(nSubMenuIndex
, rItem
.msDisplayName
,
1078 MenuItemBits::RADIOCHECK
);
1079 pCustomizationMenu
->CheckItem(nSubMenuIndex
);
1083 pCustomizationMenu
->InsertItem(nSubMenuIndex
, rItem
.msDisplayName
,
1084 MenuItemBits::CHECKABLE
);
1085 pCustomizationMenu
->CheckItem(nSubMenuIndex
, rItem
.mbIsEnabled
&& rItem
.mbIsActive
);
1092 pMenu
->InsertSeparator();
1094 // LOK doesn't support docked/undocked; Sidebar is floating but rendered docked in browser.
1095 if (!comphelper::LibreOfficeKit::isActive())
1097 // Add entry for docking or un-docking the tool panel.
1098 if (mpParentWindow
->IsFloatingMode())
1099 pMenu
->InsertItem(MID_LOCK_TASK_PANEL
, SfxResId(STR_SFX_DOCK
));
1101 pMenu
->InsertItem(MID_UNLOCK_TASK_PANEL
, SfxResId(STR_SFX_UNDOCK
));
1104 pMenu
->InsertItem(MID_HIDE_SIDEBAR
, SfxResId(SFX_STR_SIDEBAR_HIDE_SIDEBAR
));
1106 // No Restore or Customize options for LoKit.
1107 if (!comphelper::LibreOfficeKit::isActive())
1109 pCustomizationMenu
->InsertSeparator();
1110 pCustomizationMenu
->InsertItem(MID_RESTORE_DEFAULT
, SfxResId(SFX_STR_SIDEBAR_RESTORE
));
1112 pMenu
->InsertItem(MID_CUSTOMIZATION
, SfxResId(SFX_STR_SIDEBAR_CUSTOMIZATION
));
1113 pMenu
->SetPopupMenu(MID_CUSTOMIZATION
, pCustomizationMenu
);
1116 pMenu
->RemoveDisabledEntries(false);
1121 IMPL_LINK(SidebarController
, OnMenuItemSelected
, Menu
*, pMenu
, bool)
1123 if (pMenu
== nullptr)
1125 OSL_ENSURE(pMenu
!=nullptr, "sfx2::sidebar::SidebarController::OnMenuItemSelected: illegal menu!");
1129 pMenu
->Deactivate();
1130 const sal_Int32
nIndex (pMenu
->GetCurItemId());
1133 case MID_UNLOCK_TASK_PANEL
:
1134 mpParentWindow
->SetFloatingMode(true);
1135 if (mpParentWindow
->IsFloatingMode())
1136 mpParentWindow
->ToTop(ToTopFlags::GrabFocusOnly
);
1139 case MID_LOCK_TASK_PANEL
:
1140 mpParentWindow
->SetFloatingMode(false);
1143 case MID_RESTORE_DEFAULT
:
1144 mpTabBar
->RestoreHideFlags();
1147 case MID_HIDE_SIDEBAR
:
1149 if (!comphelper::LibreOfficeKit::isActive())
1151 const util::URL
aURL(Tools::GetURL(".uno:Sidebar"));
1152 Reference
<frame::XDispatch
> xDispatch(Tools::GetDispatch(mxFrame
, aURL
));
1154 xDispatch
->dispatch(aURL
, Sequence
<beans::PropertyValue
>());
1158 // In LOK we don't really destroy the sidebar when "closing";
1159 // we simply hide it. This is because recreating it is problematic
1160 // See notes in SidebarDockingWindow::NotifyResize().
1169 if (nIndex
>= MID_FIRST_PANEL
&& nIndex
<MID_FIRST_HIDE
)
1172 SwitchToDeck(mpTabBar
->GetDeckIdForIndex(nIndex
- MID_FIRST_PANEL
));
1174 else if (nIndex
>=MID_FIRST_HIDE
)
1175 if (pMenu
->GetItemBits(nIndex
) == MenuItemBits::CHECKABLE
)
1177 mpTabBar
->ToggleHideFlag(nIndex
-MID_FIRST_HIDE
);
1179 // Find the set of decks that could be displayed for the new context.
1180 ResourceManager::DeckContextDescriptorContainer aDecks
;
1181 mpResourceManager
->GetMatchingDecks (
1183 GetCurrentContext(),
1184 IsDocumentReadOnly(),
1185 mxFrame
->getController());
1186 // Notify the tab bar about the updated set of decks.
1187 mpTabBar
->SetDecks(aDecks
);
1188 mpTabBar
->HighlightDeck(mpCurrentDeck
->GetId());
1189 mpTabBar
->UpdateFocusManager(maFocusManager
);
1191 mpParentWindow
->GrabFocusToDocument();
1193 catch (RuntimeException
&)
1203 void SidebarController::RequestCloseDeck()
1205 mbIsDeckRequestedOpen
= false;
1206 UpdateDeckOpenState();
1208 if (!mpCurrentDeck
.get())
1209 mpTabBar
->RemoveDeckHighlight();
1212 void SidebarController::RequestOpenDeck()
1214 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1215 if ( pSplitWindow
&& !pSplitWindow
->IsFadeIn() )
1216 // tdf#83546 Collapsed sidebar should expand first
1217 pSplitWindow
->FadeIn();
1219 mbIsDeckRequestedOpen
= true;
1220 UpdateDeckOpenState();
1223 bool SidebarController::IsDeckOpen(const sal_Int32 nIndex
)
1227 OUString
asDeckId(mpTabBar
->GetDeckIdForIndex(nIndex
));
1228 return IsDeckVisible(asDeckId
);
1230 return mbIsDeckOpen
&& mbIsDeckOpen
.get();
1233 bool SidebarController::IsDeckVisible(const OUString
& rsDeckId
)
1235 return mbIsDeckOpen
&& mbIsDeckOpen
.get() && msCurrentDeckId
== rsDeckId
;
1238 void SidebarController::UpdateDeckOpenState()
1240 if ( ! mbIsDeckRequestedOpen
)
1241 // No state requested.
1244 const sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor();
1246 // Update (change) the open state when it either has not yet been initialized
1247 // or when its value differs from the requested state.
1248 if ( mbIsDeckOpen
&& mbIsDeckOpen
.get() == mbIsDeckRequestedOpen
.get() )
1251 if (mbIsDeckRequestedOpen
.get())
1253 if (!mpParentWindow
->IsFloatingMode())
1255 if (mnSavedSidebarWidth
<= nTabBarDefaultWidth
)
1256 SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow
));
1258 SetChildWindowWidth(mnSavedSidebarWidth
);
1262 // Show the Deck by resizing back to the original size (before hiding).
1263 Size
aNewSize(mpParentWindow
->GetFloatingWindow()->GetSizePixel());
1264 Point
aNewPos(mpParentWindow
->GetFloatingWindow()->GetPosPixel());
1266 aNewPos
.setX(aNewPos
.X() - mnSavedSidebarWidth
+ nTabBarDefaultWidth
);
1267 aNewSize
.setWidth(mnSavedSidebarWidth
);
1269 mpParentWindow
->GetFloatingWindow()->SetPosSizePixel(aNewPos
, aNewSize
);
1271 if (comphelper::LibreOfficeKit::isActive())
1273 // Sidebar wide enough to render the menu; enable it.
1274 mpTabBar
->EnableMenuButton(true);
1276 if (const SfxViewShell
* pViewShell
= mpViewFrame
->GetViewShell())
1278 const std::string uno
= UnoNameFromDeckId(msCurrentDeckId
);
1280 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
1281 (uno
+ "=true").c_str());
1288 if ( ! mpParentWindow
->IsFloatingMode())
1289 mnSavedSidebarWidth
= SetChildWindowWidth(nTabBarDefaultWidth
);
1292 // Hide the Deck by resizing to the width of the TabBar.
1293 Size
aNewSize(mpParentWindow
->GetFloatingWindow()->GetSizePixel());
1294 Point
aNewPos(mpParentWindow
->GetFloatingWindow()->GetPosPixel());
1295 mnSavedSidebarWidth
= aNewSize
.Width(); // Save the current width to restore.
1297 aNewPos
.setX(aNewPos
.X() + mnSavedSidebarWidth
- nTabBarDefaultWidth
);
1298 if (comphelper::LibreOfficeKit::isActive())
1300 // Hide by collapsing, otherwise with 0x0 the client might expect
1301 // to get valid dimensions on rendering and not collapse the sidebar.
1302 aNewSize
.setWidth(1);
1305 aNewSize
.setWidth(nTabBarDefaultWidth
);
1307 mpParentWindow
->GetFloatingWindow()->SetPosSizePixel(aNewPos
, aNewSize
);
1309 if (comphelper::LibreOfficeKit::isActive())
1311 // Sidebar too narrow to render the menu; disable it.
1312 mpTabBar
->EnableMenuButton(false);
1314 if (const SfxViewShell
* pViewShell
= mpViewFrame
->GetViewShell())
1316 const std::string uno
= UnoNameFromDeckId(msCurrentDeckId
);
1318 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
1319 (uno
+ "=false").c_str());
1324 if (mnWidthOnSplitterButtonDown
> nTabBarDefaultWidth
)
1325 mnSavedSidebarWidth
= mnWidthOnSplitterButtonDown
;
1326 mpParentWindow
->SetStyle(mpParentWindow
->GetStyle() & ~WB_SIZEABLE
);
1329 mbIsDeckOpen
= mbIsDeckRequestedOpen
.get();
1330 if (mbIsDeckOpen
.get() && mpCurrentDeck
)
1331 mpCurrentDeck
->Show(mbIsDeckOpen
.get());
1335 bool SidebarController::CanModifyChildWindowWidth()
1337 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1338 if (pSplitWindow
== nullptr)
1341 sal_uInt16
nRow (0xffff);
1342 sal_uInt16
nColumn (0xffff);
1343 if (pSplitWindow
->GetWindowPos(mpParentWindow
, nColumn
, nRow
))
1345 sal_uInt16
nRowCount (pSplitWindow
->GetWindowCount(nColumn
));
1346 return nRowCount
==1;
1352 sal_Int32
SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth
)
1354 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1355 if (pSplitWindow
== nullptr)
1358 sal_uInt16
nRow (0xffff);
1359 sal_uInt16
nColumn (0xffff);
1360 pSplitWindow
->GetWindowPos(mpParentWindow
, nColumn
, nRow
);
1361 const long nColumnWidth (pSplitWindow
->GetLineSize(nColumn
));
1363 vcl::Window
* pWindow
= mpParentWindow
;
1364 const Size
aWindowSize (pWindow
->GetSizePixel());
1366 pSplitWindow
->MoveWindow(
1368 Size(nNewWidth
, aWindowSize
.Height()),
1372 static_cast<SplitWindow
*>(pSplitWindow
)->Split();
1374 return static_cast<sal_Int32
>(nColumnWidth
);
1377 void SidebarController::RestrictWidth (sal_Int32 nWidth
)
1379 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1380 if (pSplitWindow
!= nullptr)
1382 const sal_uInt16
nId (pSplitWindow
->GetItemId(mpParentWindow
.get()));
1383 const sal_uInt16
nSetId (pSplitWindow
->GetSet(nId
));
1384 const sal_Int32 nRequestedWidth
1385 = (TabBar::GetDefaultWidth() + nWidth
) * mpTabBar
->GetDPIScaleFactor();
1387 pSplitWindow
->SetItemSizeRange(
1389 Range(nRequestedWidth
,
1390 getMaximumWidth() * mpTabBar
->GetDPIScaleFactor()));
1394 SfxSplitWindow
* SidebarController::GetSplitWindow()
1396 if (mpParentWindow
!= nullptr)
1398 SfxSplitWindow
* pSplitWindow
= dynamic_cast<SfxSplitWindow
*>(mpParentWindow
->GetParent());
1399 if (pSplitWindow
!= mpSplitWindow
)
1401 if (mpSplitWindow
!= nullptr)
1402 mpSplitWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
1404 mpSplitWindow
= pSplitWindow
;
1406 if (mpSplitWindow
!= nullptr)
1407 mpSplitWindow
->AddEventListener(LINK(this, SidebarController
, WindowEventHandler
));
1409 return mpSplitWindow
;
1415 void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag
)
1417 if (mpParentWindow
== nullptr)
1420 if (bCloseAfterDrag
)
1422 // Make sure that the indicator exists.
1423 if ( ! mpCloseIndicator
)
1425 mpCloseIndicator
.reset(VclPtr
<FixedImage
>::Create(mpParentWindow
));
1426 FixedImage
* pFixedImage
= static_cast<FixedImage
*>(mpCloseIndicator
.get());
1427 const Image
aImage (Theme::GetImage(Theme::Image_CloseIndicator
));
1428 pFixedImage
->SetImage(aImage
);
1429 pFixedImage
->SetSizePixel(aImage
.GetSizePixel());
1430 pFixedImage
->SetBackground(Theme::GetWallpaper(Theme::Paint_DeckBackground
));
1433 // Place and show the indicator.
1434 const Size
aWindowSize (mpParentWindow
->GetSizePixel());
1435 const Size
aImageSize (mpCloseIndicator
->GetSizePixel());
1436 mpCloseIndicator
->SetPosPixel(
1438 aWindowSize
.Width() - TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor() - aImageSize
.Width(),
1439 (aWindowSize
.Height() - aImageSize
.Height())/2));
1440 mpCloseIndicator
->Show();
1444 // Hide but don't delete the indicator.
1445 if (mpCloseIndicator
)
1446 mpCloseIndicator
->Hide();
1450 void SidebarController::UpdateTitleBarIcons()
1452 if ( ! mpCurrentDeck
)
1455 const bool bIsHighContrastModeActive (Theme::IsHighContrastMode());
1457 const ResourceManager
& rResourceManager
= *mpResourceManager
;
1459 // Update the deck icon.
1460 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= rResourceManager
.GetDeckDescriptor(mpCurrentDeck
->GetId());
1461 if (xDeckDescriptor
&& mpCurrentDeck
->GetTitleBar())
1463 const OUString
sIconURL(
1464 bIsHighContrastModeActive
1465 ? xDeckDescriptor
->msHighContrastTitleBarIconURL
1466 : xDeckDescriptor
->msTitleBarIconURL
);
1467 mpCurrentDeck
->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL
, mxFrame
));
1470 // Update the panel icons.
1471 const SharedPanelContainer
& rPanels (mpCurrentDeck
->GetPanels());
1472 for (const auto& rxPanel
: rPanels
)
1476 if (!rxPanel
->GetTitleBar())
1478 std::shared_ptr
<PanelDescriptor
> xPanelDescriptor
= rResourceManager
.GetPanelDescriptor(rxPanel
->GetId());
1479 if (!xPanelDescriptor
)
1481 const OUString
sIconURL (
1482 bIsHighContrastModeActive
1483 ? xPanelDescriptor
->msHighContrastTitleBarIconURL
1484 : xPanelDescriptor
->msTitleBarIconURL
);
1485 rxPanel
->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL
, mxFrame
));
1489 void SidebarController::ShowPanel (const Panel
& rPanel
)
1495 mpCurrentDeck
->ShowPanel(rPanel
);
1499 ResourceManager::DeckContextDescriptorContainer
SidebarController::GetMatchingDecks()
1501 ResourceManager::DeckContextDescriptorContainer aDecks
;
1502 mpResourceManager
->GetMatchingDecks (aDecks
,
1503 GetCurrentContext(),
1504 IsDocumentReadOnly(),
1505 mxFrame
->getController());
1509 ResourceManager::PanelContextDescriptorContainer
SidebarController::GetMatchingPanels(const OUString
& rDeckId
)
1511 ResourceManager::PanelContextDescriptorContainer aPanels
;
1513 mpResourceManager
->GetMatchingPanels(aPanels
,
1514 GetCurrentContext(),
1516 mxFrame
->getController());
1520 void SidebarController::updateModel(const css::uno::Reference
<css::frame::XModel
>& xModel
)
1522 mpResourceManager
->UpdateModel(xModel
);
1525 void SidebarController::FadeOut()
1528 mpSplitWindow
->FadeOut();
1531 void SidebarController::FadeIn()
1534 mpSplitWindow
->FadeIn();
1537 tools::Rectangle
SidebarController::GetDeckDragArea() const
1539 tools::Rectangle aRect
;
1543 VclPtr
<DeckTitleBar
> pTitleBar(mpCurrentDeck
->GetTitleBar());
1547 aRect
= DeckTitleBar::GetDragArea();
1554 void SidebarController::frameAction(const css::frame::FrameActionEvent
& rEvent
)
1556 if (rEvent
.Frame
== mxFrame
)
1558 if (rEvent
.Action
== css::frame::FrameAction_COMPONENT_DETACHING
)
1559 unregisterSidebarForFrame(this, mxFrame
->getController());
1560 else if (rEvent
.Action
== css::frame::FrameAction_COMPONENT_REATTACHED
)
1561 registerSidebarForFrame(this, mxFrame
->getController());
1565 } } // end of namespace sfx2::sidebar
1567 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */