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 <sidebar/DeckDescriptor.hxx>
22 #include <sidebar/DeckTitleBar.hxx>
23 #include <sfx2/sidebar/Panel.hxx>
24 #include <sidebar/PanelDescriptor.hxx>
25 #include <sidebar/PanelTitleBar.hxx>
26 #include <sfx2/sidebar/TabBar.hxx>
27 #include <sfx2/sidebar/Theme.hxx>
28 #include <sfx2/sidebar/SidebarChildWindow.hxx>
29 #include <sidebar/Tools.hxx>
30 #include <sfx2/sidebar/SidebarDockingWindow.hxx>
31 #include <sfx2/sidebar/Context.hxx>
34 #include <sfx2/lokhelper.hxx>
35 #include <sfx2/sfxresid.hxx>
36 #include <sfx2/strings.hrc>
37 #include <framework/ContextChangeEventMultiplexerTunnel.hxx>
38 #include <vcl/floatwin.hxx>
39 #include <vcl/fixed.hxx>
40 #include <vcl/uitest/logger.hxx>
41 #include <vcl/uitest/eventdescription.hxx>
42 #include <vcl/svapp.hxx>
43 #include <splitwin.hxx>
44 #include <tools/diagnose_ex.h>
45 #include <tools/link.hxx>
46 #include <toolkit/helper/vclunohelper.hxx>
47 #include <comphelper/processfactory.hxx>
48 #include <comphelper/namedvaluecollection.hxx>
49 #include <comphelper/lok.hxx>
50 #include <sal/log.hxx>
51 #include <officecfg/Office/UI/Sidebar.hxx>
52 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
53 #include <boost/property_tree/ptree.hpp>
54 #include <boost/property_tree/json_parser.hpp>
56 #include <com/sun/star/awt/XWindowPeer.hpp>
57 #include <com/sun/star/frame/XDispatch.hpp>
58 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
59 #include <com/sun/star/ui/ContextChangeEventObject.hpp>
60 #include <com/sun/star/ui/theUIElementFactoryManager.hpp>
61 #include <com/sun/star/util/URL.hpp>
62 #include <com/sun/star/rendering/XSpriteCanvas.hpp>
66 using namespace css::uno
;
70 const char gsReadOnlyCommandName
[] = ".uno:EditDoc";
71 const sal_Int32
gnWidthCloseThreshold (70);
72 const sal_Int32
gnWidthOpenThreshold (40);
74 std::string
UnoNameFromDeckId(const OUString
& rsDeckId
, bool isImpress
= false)
76 if (rsDeckId
== "SdCustomAnimationDeck")
77 return ".uno:CustomAnimation";
79 if (rsDeckId
== "PropertyDeck")
80 return isImpress
? ".uno:ModifyPage" : ".uno:Sidebar";
82 if (rsDeckId
== "SdLayoutsDeck")
83 return ".uno:ModifyPage";
85 if (rsDeckId
== "SdSlideTransitionDeck")
86 return ".uno:SlideChangeWindow";
88 if (rsDeckId
== "SdAllMasterPagesDeck")
89 return ".uno:MasterSlidesPanel";
91 if (rsDeckId
== "SdMasterPagesDeck")
92 return ".uno:MasterSlidesPanel";
94 if (rsDeckId
== "GalleryDeck")
95 return ".uno:Gallery";
101 namespace sfx2::sidebar
{
106 MID_UNLOCK_TASK_PANEL
= 1,
112 MID_FIRST_HIDE
= 1000
115 /** When in doubt, show this deck.
117 const char gsDefaultDeckId
[] = "PropertyDeck";
120 SidebarController::SidebarController (
121 SidebarDockingWindow
* pParentWindow
,
122 const SfxViewFrame
* pViewFrame
)
123 : SidebarControllerInterfaceBase(m_aMutex
),
125 mpParentWindow(pParentWindow
),
126 mpViewFrame(pViewFrame
),
127 mxFrame(pViewFrame
->GetFrame().GetFrameInterface()),
128 mpTabBar(VclPtr
<TabBar
>::Create(
131 [this](const OUString
& rsDeckId
) { return this->OpenThenToggleDeck(rsDeckId
); },
132 [this](weld::Menu
& rMainMenu
, weld::Menu
& rSubMenu
,
133 const ::std::vector
<TabBar::DeckMenuData
>& rMenuData
) { return this->ShowPopupMenu(rMainMenu
, rSubMenu
, rMenuData
); },
135 maCurrentContext(OUString(), OUString()),
136 maRequestedContext(),
137 mnRequestedForceFlags(SwitchFlag_NoForce
),
138 mnMaximumSidebarWidth(officecfg::Office::UI::Sidebar::General::MaximumWidth::get()),
139 mbMinimumSidebarWidth(officecfg::Office::UI::Sidebar::General::MinimumWidth::get()),
140 msCurrentDeckId(gsDefaultDeckId
),
141 maPropertyChangeForwarder([this](){ return this->BroadcastPropertyChange(); }),
142 maContextChangeUpdate([this](){ return this->UpdateConfigurations(); }),
143 mbIsDeckRequestedOpen(),
145 mbFloatingDeckClosed(!pParentWindow
->IsFloatingMode()),
146 mnSavedSidebarWidth(pParentWindow
->GetSizePixel().Width()),
147 maFocusManager([this](const Panel
& rPanel
){ return this->ShowPanel(rPanel
); }),
148 mxReadOnlyModeDispatch(),
149 mbIsDocumentReadOnly(false),
150 mpSplitWindow(nullptr),
151 mnWidthOnSplitterButtonDown(0),
154 // Decks and panel collections for this sidebar
155 mpResourceManager
= std::make_unique
<ResourceManager
>();
158 rtl::Reference
<SidebarController
> SidebarController::create(SidebarDockingWindow
* pParentWindow
,
159 const SfxViewFrame
* pViewFrame
)
161 rtl::Reference
<SidebarController
> instance(new SidebarController(pParentWindow
, pViewFrame
));
163 const css::uno::Reference
<css::frame::XFrame
>& rxFrame
= pViewFrame
->GetFrame().GetFrameInterface();
164 registerSidebarForFrame(instance
.get(), rxFrame
->getController());
165 rxFrame
->addFrameActionListener(instance
.get());
166 // Listen for window events.
167 instance
->mpParentWindow
->AddEventListener(LINK(instance
.get(), SidebarController
, WindowEventHandler
));
169 // Listen for theme property changes.
170 Theme::GetPropertySet()->addPropertyChangeListener(
172 static_cast<css::beans::XPropertyChangeListener
*>(instance
.get()));
174 // Get the dispatch object as preparation to listen for changes of
175 // the read-only state.
176 const util::URL
aURL (Tools::GetURL(gsReadOnlyCommandName
));
177 instance
->mxReadOnlyModeDispatch
= Tools::GetDispatch(rxFrame
, aURL
);
178 if (instance
->mxReadOnlyModeDispatch
.is())
179 instance
->mxReadOnlyModeDispatch
->addStatusListener(instance
.get(), aURL
);
181 //first UpdateConfigurations call will SwitchToDeck
186 SidebarController::~SidebarController()
190 SidebarController
* SidebarController::GetSidebarControllerForFrame (
191 const css::uno::Reference
<css::frame::XFrame
>& rxFrame
)
193 uno::Reference
<frame::XController
> const xController(rxFrame
->getController());
194 if (!xController
.is()) // this may happen during dispose of Draw controller but perhaps it's a bug
196 SAL_WARN("sfx.sidebar", "GetSidebarControllerForFrame: frame has no XController");
199 uno::Reference
<ui::XContextChangeEventListener
> const xListener(
200 framework::GetFirstListenerWith(xController
,
201 [] (uno::Reference
<uno::XInterface
> const& xRef
)
202 { return nullptr != dynamic_cast<SidebarController
*>(xRef
.get()); }
205 return dynamic_cast<SidebarController
*>(xListener
.get());
208 void SidebarController::registerSidebarForFrame(SidebarController
* pController
, const css::uno::Reference
<css::frame::XController
>& xController
)
210 // Listen for context change events.
211 css::uno::Reference
<css::ui::XContextChangeEventMultiplexer
> xMultiplexer (
212 css::ui::ContextChangeEventMultiplexer::get(
213 ::comphelper::getProcessComponentContext()));
214 xMultiplexer
->addContextChangeEventListener(
215 static_cast<css::ui::XContextChangeEventListener
*>(pController
),
219 void SidebarController::unregisterSidebarForFrame(SidebarController
* pController
, const css::uno::Reference
<css::frame::XController
>& xController
)
221 pController
->saveDeckState();
222 pController
->disposeDecks();
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
, vcl::EnumContext::Application::Impress
== vcl::EnumContext::GetApplicationEnum(GetCurrentContext().msApplication
));
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 SolarMutexGuard aSolarMutexGuard
;
258 mpCloseIndicator
.disposeAndClear();
260 maFocusManager
.Clear();
261 mpTabBar
.disposeAndClear();
266 ResourceManager::DeckContextDescriptorContainer aDecks
;
268 mpResourceManager
->GetMatchingDecks (
271 IsDocumentReadOnly(),
272 mxFrame
->getController());
274 for (const auto& rDeck
: aDecks
)
276 std::shared_ptr
<DeckDescriptor
> deckDesc
= mpResourceManager
->GetDeckDescriptor(rDeck
.msId
);
278 VclPtr
<Deck
> aDeck
= deckDesc
->mpDeck
;
280 aDeck
.disposeAndClear();
283 uno::Reference
<css::frame::XController
> xController
= mxFrame
->getController();
284 if (!xController
.is())
285 xController
= mxCurrentController
;
287 mxFrame
->removeFrameActionListener(this);
288 unregisterSidebarForFrame(this, xController
);
290 if (mxReadOnlyModeDispatch
.is())
291 mxReadOnlyModeDispatch
->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName
));
292 if (mpSplitWindow
!= nullptr)
294 mpSplitWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
295 mpSplitWindow
= nullptr;
298 if (mpParentWindow
!= nullptr)
300 mpParentWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
301 mpParentWindow
= nullptr;
304 Theme::GetPropertySet()->removePropertyChangeListener(
306 static_cast<css::beans::XPropertyChangeListener
*>(this));
308 maContextChangeUpdate
.CancelRequest();
311 void SAL_CALL
SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject
& rEvent
)
313 SolarMutexGuard aSolarMutexGuard
;
315 // Update to the requested new context asynchronously to avoid
316 // subtle errors caused by SFX2 which in rare cases can not
317 // properly handle a synchronous update.
319 maRequestedContext
= Context(
320 rEvent
.ApplicationName
,
323 if (maRequestedContext
!= maCurrentContext
)
325 mxCurrentController
.set(rEvent
.Source
, css::uno::UNO_QUERY
);
326 maContextChangeUpdate
.RequestCall(); // async call, not a prob
329 // TODO: this call is redundant but mandatory for unit test to update context on document loading
330 UpdateConfigurations();
334 void SAL_CALL
SidebarController::disposing (const css::lang::EventObject
& )
336 SolarMutexGuard aSolarMutexGuard
;
341 void SAL_CALL
SidebarController::propertyChange (const css::beans::PropertyChangeEvent
& )
343 SolarMutexGuard aSolarMutexGuard
;
345 maPropertyChangeForwarder
.RequestCall(); // async call, not a prob
350 void SAL_CALL
SidebarController::statusChanged (const css::frame::FeatureStateEvent
& rEvent
)
352 SolarMutexGuard aSolarMutexGuard
;
354 bool bIsReadWrite (true);
355 if (rEvent
.IsEnabled
)
356 rEvent
.State
>>= bIsReadWrite
;
358 if (mbIsDocumentReadOnly
!= !bIsReadWrite
)
360 mbIsDocumentReadOnly
= !bIsReadWrite
;
362 // Force the current deck to update its panel list.
363 if ( ! mbIsDocumentReadOnly
)
364 SwitchToDefaultDeck();
366 mnRequestedForceFlags
|= SwitchFlag_ForceSwitch
;
367 maContextChangeUpdate
.RequestCall(); // async call, ok to call
368 // with held solarmutex
372 void SAL_CALL
SidebarController::requestLayout()
374 SolarMutexGuard aSolarMutexGuard
;
376 sal_Int32 nMinimalWidth
= 0;
377 if (mpCurrentDeck
&& !mpCurrentDeck
->isDisposed())
379 mpCurrentDeck
->RequestLayout();
380 nMinimalWidth
= mbMinimumSidebarWidth
? mpCurrentDeck
->GetMinimalWidth() : 0;
382 RestrictWidth(nMinimalWidth
);
385 void SidebarController::BroadcastPropertyChange()
387 mpParentWindow
->Invalidate(InvalidateFlags::Children
);
390 void SidebarController::NotifyResize()
394 OSL_ASSERT(mpTabBar
!=nullptr);
398 vcl::Window
* pParentWindow
= mpTabBar
->GetParent();
399 const sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor();
401 const sal_Int32
nWidth (pParentWindow
->GetSizePixel().Width());
402 const sal_Int32
nHeight (pParentWindow
->GetSizePixel().Height());
404 mbIsDeckOpen
= (nWidth
> nTabBarDefaultWidth
);
406 if (mnSavedSidebarWidth
<= 0)
407 mnSavedSidebarWidth
= nWidth
;
410 const bool bIsOpening (nWidth
> mnWidthOnSplitterButtonDown
);
412 bIsDeckVisible
= nWidth
>= nTabBarDefaultWidth
+ gnWidthOpenThreshold
;
414 bIsDeckVisible
= nWidth
>= nTabBarDefaultWidth
+ gnWidthCloseThreshold
;
415 mbIsDeckRequestedOpen
= bIsDeckVisible
;
416 UpdateCloseIndicator(!bIsDeckVisible
);
418 if (mpCurrentDeck
&& !mpCurrentDeck
->isDisposed())
420 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
421 WindowAlign eAlign
= pSplitWindow
? pSplitWindow
->GetAlign() : WindowAlign::Right
;
422 tools::Long nDeckX
, nTabX
;
423 if (eAlign
== WindowAlign::Left
) // attach the Sidebar towards the left-side of screen
425 nDeckX
= nTabBarDefaultWidth
;
428 else // attach the Sidebar towards the right-side of screen
431 nTabX
= nWidth
- nTabBarDefaultWidth
;
434 // Place the deck first.
437 if (comphelper::LibreOfficeKit::isActive())
439 // We want to let the layouter use up as much of the
440 // height as necessary to make sure no scrollbar is
441 // visible. This only works when there are no greedy
442 // panes that fill up all available area. So we only
443 // use this for the PropertyDeck, which has no such
444 // panes, while most other do. This is fine, since
445 // it's the PropertyDeck that really has many panes
446 // that can collapse or expand. For others, limit
447 // the height to something sensible.
448 // tdf#130348: Add special case for ChartDeck, too.
449 const sal_Int32 nExtHeight
= (msCurrentDeckId
== "PropertyDeck" ? 2000 :
450 (msCurrentDeckId
== "ChartDeck" ? 1200 : 600));
451 // No TabBar in LOK (use nWidth in full).
452 mpCurrentDeck
->setPosSizePixel(nDeckX
, 0, nWidth
, nExtHeight
);
455 mpCurrentDeck
->setPosSizePixel(nDeckX
, 0, nWidth
- nTabBarDefaultWidth
, nHeight
);
456 mpCurrentDeck
->Show();
457 mpCurrentDeck
->RequestLayout();
460 mpCurrentDeck
->Hide();
462 // Now place the tab bar.
463 mpTabBar
->setPosSizePixel(nTabX
, 0, nTabBarDefaultWidth
, nHeight
);
464 if (!comphelper::LibreOfficeKit::isActive())
465 mpTabBar
->Show(); // Don't show TabBar in LOK.
468 // Determine if the closer of the deck can be shown.
469 sal_Int32 nMinimalWidth
= 0;
470 if (mpCurrentDeck
&& !mpCurrentDeck
->isDisposed())
472 VclPtr
<DeckTitleBar
> pTitleBar
= mpCurrentDeck
->GetTitleBar();
473 if (pTitleBar
&& pTitleBar
->IsVisible())
474 pTitleBar
->SetCloserVisible(CanModifyChildWindowWidth());
475 nMinimalWidth
= mbMinimumSidebarWidth
? mpCurrentDeck
->GetMinimalWidth() : 0;
478 RestrictWidth(nMinimalWidth
);
480 mpParentWindow
->NotifyResize();
483 void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth
)
485 if ( ! mbIsDeckRequestedOpen
)
488 if (*mbIsDeckRequestedOpen
)
490 // Deck became large enough to be shown. Show it.
491 mnSavedSidebarWidth
= nNewWidth
;
497 // Deck became too small. Close it completely.
498 // If window is wider than the tab bar then mark the deck as being visible, even when it is not.
499 // This is to trigger an adjustment of the width to the width of the tab bar.
503 if (mnWidthOnSplitterButtonDown
> TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor())
504 mnSavedSidebarWidth
= mnWidthOnSplitterButtonDown
;
508 void SidebarController::SyncUpdate()
510 maPropertyChangeForwarder
.Sync();
511 maContextChangeUpdate
.Sync();
514 void SidebarController::UpdateConfigurations()
516 if (maCurrentContext
== maRequestedContext
517 && mnRequestedForceFlags
== SwitchFlag_NoForce
)
520 if ((maCurrentContext
.msApplication
!= "none") &&
521 !maCurrentContext
.msApplication
.isEmpty())
523 mpResourceManager
->SaveDecksSettings(maCurrentContext
);
524 mpResourceManager
->SetLastActiveDeck(maCurrentContext
, msCurrentDeckId
);
527 // get last active deck for this application on first update
528 if (!maRequestedContext
.msApplication
.isEmpty() &&
529 (maCurrentContext
.msApplication
!= maRequestedContext
.msApplication
))
531 OUString sLastActiveDeck
= mpResourceManager
->GetLastActiveDeck( maRequestedContext
);
532 if (!sLastActiveDeck
.isEmpty())
533 msCurrentDeckId
= sLastActiveDeck
;
536 maCurrentContext
= maRequestedContext
;
538 mpResourceManager
->InitDeckContext(GetCurrentContext());
540 // Find the set of decks that could be displayed for the new context.
541 ResourceManager::DeckContextDescriptorContainer aDecks
;
543 css::uno::Reference
<css::frame::XController
> xController
= mxCurrentController
.is() ? mxCurrentController
: mxFrame
->getController();
545 mpResourceManager
->GetMatchingDecks (
548 mbIsDocumentReadOnly
,
551 maFocusManager
.Clear();
553 // Notify the tab bar about the updated set of decks.
554 mpTabBar
->SetDecks(aDecks
);
556 // Find the new deck. By default that is the same as the old
557 // one. If that is not set or not enabled, then choose the
558 // first enabled deck (which is PropertyDeck).
560 for (const auto& rDeck
: aDecks
)
562 if (rDeck
.mbIsEnabled
)
564 if (rDeck
.msId
== msCurrentDeckId
)
566 sNewDeckId
= msCurrentDeckId
;
569 else if (sNewDeckId
.getLength() == 0)
570 sNewDeckId
= rDeck
.msId
;
574 if (sNewDeckId
.getLength() == 0)
576 // We did not find a valid deck.
581 // Tell the tab bar to highlight the button associated
583 mpTabBar
->HighlightDeck(sNewDeckId
);
585 std::shared_ptr
<DeckDescriptor
> xDescriptor
= mpResourceManager
->GetDeckDescriptor(sNewDeckId
);
589 SwitchToDeck(*xDescriptor
, maCurrentContext
);
595 void collectUIInformation(const OUString
& rDeckId
)
597 EventDescription aDescription
;
598 aDescription
.aAction
= "SIDEBAR";
599 aDescription
.aParent
= "MainWindow";
600 aDescription
.aParameters
= {{"PANEL", rDeckId
}};
601 aDescription
.aKeyWord
= "CurrentApp";
603 UITestLogger::getInstance().logEvent(aDescription
);
608 void SidebarController::OpenThenToggleDeck (
609 const OUString
& rsDeckId
)
611 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
612 if ( pSplitWindow
&& !pSplitWindow
->IsFadeIn() )
613 // tdf#83546 Collapsed sidebar should expand first
614 pSplitWindow
->FadeIn();
615 else if ( IsDeckVisible( rsDeckId
) )
617 if( !WasFloatingDeckClosed() )
619 // tdf#88241 Summoning an undocked sidebar a second time should close sidebar
620 mpParentWindow
->Close();
625 // tdf#67627 Clicking a second time on a Deck icon will close the Deck
631 // before SwitchToDeck which may cause the rsDeckId string to be released
632 collectUIInformation(rsDeckId
);
633 SwitchToDeck(rsDeckId
);
635 // Make sure the sidebar is wide enough to fit the requested content
636 if (mpCurrentDeck
&& mpTabBar
)
638 sal_Int32 nRequestedWidth
= (mpCurrentDeck
->GetMinimalWidth() + TabBar::GetDefaultWidth())
639 * mpTabBar
->GetDPIScaleFactor();
640 if (mnSavedSidebarWidth
< nRequestedWidth
)
641 SetChildWindowWidth(nRequestedWidth
);
645 void SidebarController::OpenThenSwitchToDeck (
646 const OUString
& rsDeckId
)
649 SwitchToDeck(rsDeckId
);
653 void SidebarController::SwitchToDefaultDeck()
655 SwitchToDeck(gsDefaultDeckId
);
658 void SidebarController::SwitchToDeck (
659 const OUString
& rsDeckId
)
661 if ( msCurrentDeckId
!= rsDeckId
663 || mnRequestedForceFlags
!=SwitchFlag_NoForce
)
665 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= mpResourceManager
->GetDeckDescriptor(rsDeckId
);
668 SwitchToDeck(*xDeckDescriptor
, maCurrentContext
);
672 void SidebarController::CreateDeck(const OUString
& rDeckId
) {
673 CreateDeck(rDeckId
, maCurrentContext
);
676 void SidebarController::CreateDeck(const OUString
& rDeckId
, const Context
& rContext
, bool bForceCreate
)
678 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= mpResourceManager
->GetDeckDescriptor(rDeckId
);
680 if (!xDeckDescriptor
)
683 VclPtr
<Deck
> aDeck
= xDeckDescriptor
->mpDeck
;
684 if (!aDeck
|| bForceCreate
)
687 aDeck
.disposeAndClear();
689 aDeck
= VclPtr
<Deck
>::Create(
692 [this]() { return this->RequestCloseDeck(); });
694 xDeckDescriptor
->mpDeck
= aDeck
;
695 CreatePanels(rDeckId
, rContext
);
698 void SidebarController::CreatePanels(const OUString
& rDeckId
, const Context
& rContext
)
700 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= mpResourceManager
->GetDeckDescriptor(rDeckId
);
702 // init panels bounded to that deck, do not wait them being displayed as may be accessed through API
704 VclPtr
<Deck
> pDeck
= xDeckDescriptor
->mpDeck
;
706 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors
;
708 css::uno::Reference
<css::frame::XController
> xController
= mxCurrentController
.is() ? mxCurrentController
: mxFrame
->getController();
710 mpResourceManager
->GetMatchingPanels(
711 aPanelContextDescriptors
,
716 // Update the panel list.
717 const sal_Int32
nNewPanelCount (aPanelContextDescriptors
.size());
718 SharedPanelContainer aNewPanels
;
719 sal_Int32
nWriteIndex (0);
721 aNewPanels
.resize(nNewPanelCount
);
723 for (sal_Int32 nReadIndex
=0; nReadIndex
<nNewPanelCount
; ++nReadIndex
)
725 const ResourceManager::PanelContextDescriptor
& rPanelContexDescriptor (
726 aPanelContextDescriptors
[nReadIndex
]);
728 // Determine if the panel can be displayed.
729 const bool bIsPanelVisible (!mbIsDocumentReadOnly
|| rPanelContexDescriptor
.mbShowForReadOnlyDocuments
);
730 if ( ! bIsPanelVisible
)
733 Panel
*const pPanel(pDeck
->GetPanel(rPanelContexDescriptor
.msId
));
734 if (pPanel
!= nullptr)
736 pPanel
->SetLurkMode(false);
737 aNewPanels
[nWriteIndex
] = pPanel
;
738 pPanel
->SetExpanded( rPanelContexDescriptor
.mbIsInitiallyVisible
);
743 VclPtr
<Panel
> aPanel
= CreatePanel(
744 rPanelContexDescriptor
.msId
,
745 pDeck
->GetPanelParentWindow(),
746 rPanelContexDescriptor
.mbIsInitiallyVisible
,
751 aNewPanels
[nWriteIndex
] = aPanel
;
753 // Depending on the context we have to change the command
754 // for the "more options" dialog.
755 VclPtr
<PanelTitleBar
> pTitleBar
= aNewPanels
[nWriteIndex
]->GetTitleBar();
758 pTitleBar
->SetMoreOptionsCommand(
759 rPanelContexDescriptor
.msMenuCommand
,
760 mxFrame
, xController
);
767 // mpCurrentPanels - may miss stuff (?)
768 aNewPanels
.resize(nWriteIndex
);
769 pDeck
->ResetPanels(aNewPanels
);
772 void SidebarController::SwitchToDeck (
773 const DeckDescriptor
& rDeckDescriptor
,
774 const Context
& rContext
)
776 if (comphelper::LibreOfficeKit::isActive())
778 if (const SfxViewShell
* pViewShell
= mpViewFrame
->GetViewShell())
780 if (msCurrentDeckId
!= rDeckDescriptor
.msId
)
782 const std::string hide
= UnoNameFromDeckId(msCurrentDeckId
, vcl::EnumContext::Application::Impress
== vcl::EnumContext::GetApplicationEnum(GetCurrentContext().msApplication
));
784 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
785 (hide
+ "=false").c_str());
788 const std::string show
= UnoNameFromDeckId(rDeckDescriptor
.msId
, vcl::EnumContext::Application::Impress
== vcl::EnumContext::GetApplicationEnum(GetCurrentContext().msApplication
));
790 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
791 (show
+ "=true").c_str());
795 maFocusManager
.Clear();
797 const bool bForceNewDeck ((mnRequestedForceFlags
&SwitchFlag_ForceNewDeck
)!=0);
798 const bool bForceNewPanels ((mnRequestedForceFlags
&SwitchFlag_ForceNewPanels
)!=0);
799 mnRequestedForceFlags
= SwitchFlag_NoForce
;
801 if ( msCurrentDeckId
!= rDeckDescriptor
.msId
805 mpCurrentDeck
->Hide();
807 msCurrentDeckId
= rDeckDescriptor
.msId
;
809 mpTabBar
->Invalidate();
810 mpTabBar
->HighlightDeck(msCurrentDeckId
);
812 // Determine the panels to display in the deck.
813 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors
;
815 css::uno::Reference
<css::frame::XController
> xController
= mxCurrentController
.is() ? mxCurrentController
: mxFrame
->getController();
817 mpResourceManager
->GetMatchingPanels(
818 aPanelContextDescriptors
,
820 rDeckDescriptor
.msId
,
823 if (aPanelContextDescriptors
.empty())
825 // There are no panels to be displayed in the current context.
826 if (vcl::EnumContext::GetContextEnum(rContext
.msContext
) != vcl::EnumContext::Context::Empty
)
828 // Switch to the "empty" context and try again.
832 rContext
.msApplication
,
833 vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Empty
)));
838 // This is already the "empty" context. Looks like we have
839 // to live with an empty deck.
843 // Provide a configuration and Deck object.
845 CreateDeck(rDeckDescriptor
.msId
, rContext
, bForceNewDeck
);
847 if (bForceNewPanels
&& !bForceNewDeck
) // already forced if bForceNewDeck
848 CreatePanels(rDeckDescriptor
.msId
, rContext
);
850 if (mpCurrentDeck
&& mpCurrentDeck
!= rDeckDescriptor
.mpDeck
)
851 mpCurrentDeck
->Hide();
852 mpCurrentDeck
.reset(rDeckDescriptor
.mpDeck
);
854 if ( ! mpCurrentDeck
)
858 // Show the context name in the deck title bar.
859 VclPtr
<DeckTitleBar
> pDebugTitleBar
= mpCurrentDeck
->GetTitleBar();
861 pDebugTitleBar
->SetTitle(rDeckDescriptor
.msTitle
+ " (" + maCurrentContext
.msContext
+ ")");
864 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
865 sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor();
866 WindowAlign eAlign
= pSplitWindow
? pSplitWindow
->GetAlign() : WindowAlign::Right
;
868 if (eAlign
== WindowAlign::Left
) // attach the Sidebar towards the left-side of screen
870 nDeckX
= nTabBarDefaultWidth
;
872 else // attach the Sidebar towards the right-side of screen
877 // Activate the deck and the new set of panels.
878 mpCurrentDeck
->setPosSizePixel(
881 mpParentWindow
->GetSizePixel().Width() - nTabBarDefaultWidth
,
882 mpParentWindow
->GetSizePixel().Height());
884 mpCurrentDeck
->Show();
886 mpParentWindow
->SetText(rDeckDescriptor
.msTitle
);
890 // Tell the focus manager about the new panels and tab bar
892 maFocusManager
.SetDeckTitle(mpCurrentDeck
->GetTitleBar());
893 maFocusManager
.SetPanels(mpCurrentDeck
->GetPanels());
895 mpTabBar
->UpdateFocusManager(maFocusManager
);
896 UpdateTitleBarIcons();
899 void SidebarController::notifyDeckTitle(const OUString
& targetDeckId
)
901 if (msCurrentDeckId
== targetDeckId
)
903 maFocusManager
.SetDeckTitle(mpCurrentDeck
->GetTitleBar());
904 mpTabBar
->UpdateFocusManager(maFocusManager
);
905 UpdateTitleBarIcons();
909 VclPtr
<Panel
> SidebarController::CreatePanel (
910 const OUString
& rsPanelId
,
911 vcl::Window
* pParentWindow
,
912 const bool bIsInitiallyExpanded
,
913 const Context
& rContext
,
914 const VclPtr
<Deck
>& pDeck
)
916 std::shared_ptr
<PanelDescriptor
> xPanelDescriptor
= mpResourceManager
->GetPanelDescriptor(rsPanelId
);
918 if (!xPanelDescriptor
)
921 // Create the panel which is the parent window of the UIElement.
922 VclPtr
<Panel
> pPanel
= VclPtr
<Panel
>::Create(
925 bIsInitiallyExpanded
,
926 [pDeck
]() { return pDeck
->RequestLayout(); },
927 [this]() { return this->GetCurrentContext(); },
930 // Create the XUIElement.
931 Reference
<ui::XUIElement
> xUIElement (CreateUIElement(
932 pPanel
->GetComponentInterface(),
933 xPanelDescriptor
->msImplementationURL
,
934 xPanelDescriptor
->mbWantsCanvas
,
938 // Initialize the panel and add it to the active deck.
939 pPanel
->SetUIElement(xUIElement
);
943 pPanel
.disposeAndClear();
949 Reference
<ui::XUIElement
> SidebarController::CreateUIElement (
950 const Reference
<awt::XWindowPeer
>& rxWindow
,
951 const OUString
& rsImplementationURL
,
952 const bool bWantsCanvas
,
953 const Context
& rContext
)
957 const Reference
<XComponentContext
> xComponentContext (::comphelper::getProcessComponentContext() );
958 const Reference
<ui::XUIElementFactory
> xUIElementFactory
=
959 ui::theUIElementFactoryManager::get( xComponentContext
);
961 // Create the XUIElement.
962 ::comphelper::NamedValueCollection aCreationArguments
;
963 aCreationArguments
.put("Frame", makeAny(mxFrame
));
964 aCreationArguments
.put("ParentWindow", makeAny(rxWindow
));
965 SfxDockingWindow
* pSfxDockingWindow
= dynamic_cast<SfxDockingWindow
*>(mpParentWindow
.get());
966 if (pSfxDockingWindow
!= nullptr)
967 aCreationArguments
.put("SfxBindings", makeAny(reinterpret_cast<sal_uInt64
>(&pSfxDockingWindow
->GetBindings())));
968 aCreationArguments
.put("Theme", Theme::GetPropertySet());
969 aCreationArguments
.put("Sidebar", makeAny(Reference
<ui::XSidebar
>(static_cast<ui::XSidebar
*>(this))));
972 Reference
<rendering::XSpriteCanvas
> xCanvas (VCLUnoHelper::GetWindow(rxWindow
)->GetSpriteCanvas());
973 aCreationArguments
.put("Canvas", makeAny(xCanvas
));
976 if (mxCurrentController
.is())
978 OUString aModule
= Tools::GetModuleName(mxCurrentController
);
979 if (!aModule
.isEmpty())
981 aCreationArguments
.put("Module", makeAny(aModule
));
983 aCreationArguments
.put("Controller", makeAny(mxCurrentController
));
986 aCreationArguments
.put("ApplicationName", makeAny(rContext
.msApplication
));
987 aCreationArguments
.put("ContextName", makeAny(rContext
.msContext
));
989 Reference
<ui::XUIElement
> xUIElement(
990 xUIElementFactory
->createUIElement(
992 aCreationArguments
.getPropertyValues()),
997 catch(const Exception
&)
999 TOOLS_WARN_EXCEPTION("sfx.sidebar", "Cannot create panel " << rsImplementationURL
);
1004 IMPL_LINK(SidebarController
, WindowEventHandler
, VclWindowEvent
&, rEvent
, void)
1006 if (rEvent
.GetWindow() == mpParentWindow
)
1008 switch (rEvent
.GetId())
1010 case VclEventId::WindowShow
:
1011 case VclEventId::WindowResize
:
1015 case VclEventId::WindowDataChanged
:
1016 // Force an update of deck and tab bar to reflect
1017 // changes in theme (high contrast mode).
1018 Theme::HandleDataChange();
1019 UpdateTitleBarIcons();
1020 mpParentWindow
->Invalidate();
1021 mnRequestedForceFlags
|= SwitchFlag_ForceNewDeck
| SwitchFlag_ForceNewPanels
;
1022 maContextChangeUpdate
.RequestCall();
1025 case VclEventId::ObjectDying
:
1029 case VclEventId::WindowPaint
:
1030 SAL_INFO("sfx.sidebar", "Paint");
1037 else if (rEvent
.GetWindow()==mpSplitWindow
&& mpSplitWindow
!=nullptr)
1039 switch (rEvent
.GetId())
1041 case VclEventId::WindowMouseButtonDown
:
1042 mnWidthOnSplitterButtonDown
= mpParentWindow
->GetSizePixel().Width();
1045 case VclEventId::WindowMouseButtonUp
:
1047 ProcessNewWidth(mpParentWindow
->GetSizePixel().Width());
1048 mnWidthOnSplitterButtonDown
= 0;
1052 case VclEventId::ObjectDying
:
1061 void SidebarController::ShowPopupMenu(
1062 weld::Menu
& rMainMenu
, weld::Menu
& rSubMenu
,
1063 const ::std::vector
<TabBar::DeckMenuData
>& rMenuData
) const
1065 PopulatePopupMenus(rMainMenu
, rSubMenu
, rMenuData
);
1066 rMainMenu
.connect_activate(LINK(const_cast<SidebarController
*>(this), SidebarController
, OnMenuItemSelected
));
1067 rSubMenu
.connect_activate(LINK(const_cast<SidebarController
*>(this), SidebarController
, OnSubMenuItemSelected
));
1070 void SidebarController::PopulatePopupMenus(weld::Menu
& rMenu
, weld::Menu
& rCustomizationMenu
,
1071 const std::vector
<TabBar::DeckMenuData
>& rMenuData
) const
1073 // Add one entry for every tool panel element to individually make
1074 // them visible or hide them.
1075 sal_Int32
nIndex (0);
1076 for (const auto& rItem
: rMenuData
)
1078 OString
sIdent("select" + OString::number(nIndex
));
1079 rMenu
.insert(nIndex
, OUString::fromUtf8(sIdent
), rItem
.msDisplayName
, nullptr, nullptr, TRISTATE_FALSE
);
1080 rMenu
.set_active(sIdent
, rItem
.mbIsCurrentDeck
);
1081 rMenu
.set_sensitive(sIdent
, rItem
.mbIsEnabled
&& rItem
.mbIsActive
);
1083 if (!comphelper::LibreOfficeKit::isActive())
1085 if (rItem
.mbIsCurrentDeck
)
1087 // Don't allow the currently visible deck to be disabled.
1088 OString
sSubIdent("nocustomize" + OString::number(nIndex
));
1089 rCustomizationMenu
.insert(nIndex
, OUString::fromUtf8(sSubIdent
), rItem
.msDisplayName
, nullptr, nullptr, TRISTATE_FALSE
);
1090 rCustomizationMenu
.set_active(sSubIdent
, true);
1094 OString
sSubIdent("customize" + OString::number(nIndex
));
1095 rCustomizationMenu
.insert(nIndex
, OUString::fromUtf8(sSubIdent
), rItem
.msDisplayName
, nullptr, nullptr, TRISTATE_TRUE
);
1096 rCustomizationMenu
.set_active(sSubIdent
, rItem
.mbIsEnabled
&& rItem
.mbIsActive
);
1103 bool bHideLock
= true;
1104 bool bHideUnLock
= true;
1105 // LOK doesn't support docked/undocked; Sidebar is floating but rendered docked in browser.
1106 if (!comphelper::LibreOfficeKit::isActive())
1108 // Add entry for docking or un-docking the tool panel.
1109 if (mpParentWindow
->IsFloatingMode())
1112 bHideUnLock
= false;
1114 rMenu
.set_visible("locktaskpanel", !bHideLock
);
1115 rMenu
.set_visible("unlocktaskpanel", !bHideUnLock
);
1117 // No Restore or Customize options for LoKit.
1118 rMenu
.set_visible("customization", !comphelper::LibreOfficeKit::isActive());
1121 IMPL_LINK(SidebarController
, OnMenuItemSelected
, const OString
&, rCurItemId
, void)
1123 if (rCurItemId
== "unlocktaskpanel")
1125 mpParentWindow
->SetFloatingMode(true);
1126 if (mpParentWindow
->IsFloatingMode())
1127 mpParentWindow
->ToTop(ToTopFlags::GrabFocusOnly
);
1129 else if (rCurItemId
== "locktaskpanel")
1131 mpParentWindow
->SetFloatingMode(false);
1133 else if (rCurItemId
== "hidesidebar")
1135 if (!comphelper::LibreOfficeKit::isActive())
1137 const util::URL
aURL(Tools::GetURL(".uno:Sidebar"));
1138 Reference
<frame::XDispatch
> xDispatch(Tools::GetDispatch(mxFrame
, aURL
));
1140 xDispatch
->dispatch(aURL
, Sequence
<beans::PropertyValue
>());
1144 // In LOK we don't really destroy the sidebar when "closing";
1145 // we simply hide it. This is because recreating it is problematic
1146 // See notes in SidebarDockingWindow::NotifyResize().
1155 if (rCurItemId
.startsWith("select", &sNumber
))
1158 SwitchToDeck(mpTabBar
->GetDeckIdForIndex(sNumber
.toInt32()));
1160 mpParentWindow
->GrabFocusToDocument();
1162 catch (RuntimeException
&)
1168 IMPL_LINK(SidebarController
, OnSubMenuItemSelected
, const OString
&, rCurItemId
, void)
1170 if (rCurItemId
== "restoredefault")
1171 mpTabBar
->RestoreHideFlags();
1177 if (rCurItemId
.startsWith("customize", &sNumber
))
1179 mpTabBar
->ToggleHideFlag(sNumber
.toInt32());
1181 // Find the set of decks that could be displayed for the new context.
1182 ResourceManager::DeckContextDescriptorContainer aDecks
;
1183 mpResourceManager
->GetMatchingDecks (
1185 GetCurrentContext(),
1186 IsDocumentReadOnly(),
1187 mxFrame
->getController());
1188 // Notify the tab bar about the updated set of decks.
1189 maFocusManager
.Clear();
1190 mpTabBar
->SetDecks(aDecks
);
1191 mpTabBar
->HighlightDeck(mpCurrentDeck
->GetId());
1192 mpTabBar
->UpdateFocusManager(maFocusManager
);
1194 mpParentWindow
->GrabFocusToDocument();
1196 catch (RuntimeException
&)
1203 void SidebarController::RequestCloseDeck()
1205 if (comphelper::LibreOfficeKit::isActive() && mpCurrentDeck
)
1207 const vcl::ILibreOfficeKitNotifier
* pNotifier
= mpCurrentDeck
->GetLOKNotifier();
1208 auto pMobileNotifier
= SfxViewShell::Current();
1209 const SfxViewShell
* pViewShell
= SfxViewShell::Current();
1210 if (pMobileNotifier
&& pViewShell
&& pViewShell
->isLOKMobilePhone())
1213 std::stringstream aStream
;
1214 boost::property_tree::ptree aTree
;
1215 aTree
.put("id", mpParentWindow
->get_id()); // TODO could be missing - sort out
1216 aTree
.put("type", "dockingwindow");
1217 aTree
.put("text", mpParentWindow
->GetText());
1218 aTree
.put("enabled", false);
1219 boost::property_tree::write_json(aStream
, aTree
);
1220 const std::string message
= aStream
.str();
1221 pMobileNotifier
->libreOfficeKitViewCallback(LOK_CALLBACK_JSDIALOG
, message
.c_str());
1224 pNotifier
->notifyWindow(mpCurrentDeck
->GetLOKWindowId(), "close");
1227 mbIsDeckRequestedOpen
= false;
1228 UpdateDeckOpenState();
1231 mpTabBar
->RemoveDeckHighlight();
1234 void SidebarController::RequestOpenDeck()
1236 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1237 if ( pSplitWindow
&& !pSplitWindow
->IsFadeIn() )
1238 // tdf#83546 Collapsed sidebar should expand first
1239 pSplitWindow
->FadeIn();
1241 mbIsDeckRequestedOpen
= true;
1242 UpdateDeckOpenState();
1245 bool SidebarController::IsDeckOpen(const sal_Int32 nIndex
)
1249 OUString
asDeckId(mpTabBar
->GetDeckIdForIndex(nIndex
));
1250 return IsDeckVisible(asDeckId
);
1252 return mbIsDeckOpen
&& *mbIsDeckOpen
;
1255 bool SidebarController::IsDeckVisible(const OUString
& rsDeckId
)
1257 return mbIsDeckOpen
&& *mbIsDeckOpen
&& msCurrentDeckId
== rsDeckId
;
1260 void SidebarController::UpdateDeckOpenState()
1262 if ( ! mbIsDeckRequestedOpen
)
1263 // No state requested.
1266 const sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor();
1268 // Update (change) the open state when it either has not yet been initialized
1269 // or when its value differs from the requested state.
1270 if ( mbIsDeckOpen
&& *mbIsDeckOpen
== *mbIsDeckRequestedOpen
)
1273 if (*mbIsDeckRequestedOpen
)
1275 if (!mpParentWindow
->IsFloatingMode())
1277 if (mnSavedSidebarWidth
<= nTabBarDefaultWidth
)
1278 SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow
));
1280 SetChildWindowWidth(mnSavedSidebarWidth
);
1284 // Show the Deck by resizing back to the original size (before hiding).
1285 Size
aNewSize(mpParentWindow
->GetFloatingWindow()->GetSizePixel());
1286 Point
aNewPos(mpParentWindow
->GetFloatingWindow()->GetPosPixel());
1288 aNewPos
.setX(aNewPos
.X() - mnSavedSidebarWidth
+ nTabBarDefaultWidth
);
1289 aNewSize
.setWidth(mnSavedSidebarWidth
);
1291 mpParentWindow
->GetFloatingWindow()->SetPosSizePixel(aNewPos
, aNewSize
);
1293 if (comphelper::LibreOfficeKit::isActive())
1295 // Sidebar wide enough to render the menu; enable it.
1296 mpTabBar
->EnableMenuButton(true);
1298 if (const SfxViewShell
* pViewShell
= mpViewFrame
->GetViewShell())
1300 const std::string uno
= UnoNameFromDeckId(msCurrentDeckId
, vcl::EnumContext::Application::Impress
== vcl::EnumContext::GetApplicationEnum(GetCurrentContext().msApplication
));
1302 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
1303 (uno
+ "=true").c_str());
1310 if ( ! mpParentWindow
->IsFloatingMode())
1311 mnSavedSidebarWidth
= SetChildWindowWidth(nTabBarDefaultWidth
);
1314 // Hide the Deck by resizing to the width of the TabBar.
1315 Size
aNewSize(mpParentWindow
->GetFloatingWindow()->GetSizePixel());
1316 Point
aNewPos(mpParentWindow
->GetFloatingWindow()->GetPosPixel());
1317 mnSavedSidebarWidth
= aNewSize
.Width(); // Save the current width to restore.
1319 aNewPos
.setX(aNewPos
.X() + mnSavedSidebarWidth
- nTabBarDefaultWidth
);
1320 if (comphelper::LibreOfficeKit::isActive())
1322 // Hide by collapsing, otherwise with 0x0 the client might expect
1323 // to get valid dimensions on rendering and not collapse the sidebar.
1324 aNewSize
.setWidth(1);
1327 aNewSize
.setWidth(nTabBarDefaultWidth
);
1329 mpParentWindow
->GetFloatingWindow()->SetPosSizePixel(aNewPos
, aNewSize
);
1331 if (comphelper::LibreOfficeKit::isActive())
1333 // Sidebar too narrow to render the menu; disable it.
1334 mpTabBar
->EnableMenuButton(false);
1336 if (const SfxViewShell
* pViewShell
= mpViewFrame
->GetViewShell())
1338 const std::string uno
= UnoNameFromDeckId(msCurrentDeckId
, vcl::EnumContext::Application::Impress
== vcl::EnumContext::GetApplicationEnum(GetCurrentContext().msApplication
));
1340 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
1341 (uno
+ "=false").c_str());
1346 if (mnWidthOnSplitterButtonDown
> nTabBarDefaultWidth
)
1347 mnSavedSidebarWidth
= mnWidthOnSplitterButtonDown
;
1348 mpParentWindow
->SetStyle(mpParentWindow
->GetStyle() & ~WB_SIZEABLE
);
1351 mbIsDeckOpen
= *mbIsDeckRequestedOpen
;
1352 if (*mbIsDeckOpen
&& mpCurrentDeck
)
1353 mpCurrentDeck
->Show(*mbIsDeckOpen
);
1357 bool SidebarController::CanModifyChildWindowWidth()
1359 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1360 if (pSplitWindow
== nullptr)
1363 sal_uInt16
nRow (0xffff);
1364 sal_uInt16
nColumn (0xffff);
1365 if (pSplitWindow
->GetWindowPos(mpParentWindow
, nColumn
, nRow
))
1367 sal_uInt16
nRowCount (pSplitWindow
->GetWindowCount(nColumn
));
1368 return nRowCount
==1;
1374 sal_Int32
SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth
)
1376 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1377 if (pSplitWindow
== nullptr)
1380 sal_uInt16
nRow (0xffff);
1381 sal_uInt16
nColumn (0xffff);
1382 pSplitWindow
->GetWindowPos(mpParentWindow
, nColumn
, nRow
);
1383 const tools::Long
nColumnWidth (pSplitWindow
->GetLineSize(nColumn
));
1385 vcl::Window
* pWindow
= mpParentWindow
;
1386 const Size
aWindowSize (pWindow
->GetSizePixel());
1388 pSplitWindow
->MoveWindow(
1390 Size(nNewWidth
, aWindowSize
.Height()),
1394 static_cast<SplitWindow
*>(pSplitWindow
)->Split();
1396 return static_cast<sal_Int32
>(nColumnWidth
);
1399 void SidebarController::RestrictWidth (sal_Int32 nWidth
)
1401 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1402 if (pSplitWindow
!= nullptr)
1404 const sal_uInt16
nId (pSplitWindow
->GetItemId(mpParentWindow
.get()));
1405 const sal_uInt16
nSetId (pSplitWindow
->GetSet(nId
));
1406 const sal_Int32 nRequestedWidth
1407 = (TabBar::GetDefaultWidth() + nWidth
) * mpTabBar
->GetDPIScaleFactor();
1409 pSplitWindow
->SetItemSizeRange(
1411 Range(nRequestedWidth
,
1412 getMaximumWidth() * mpTabBar
->GetDPIScaleFactor()));
1416 SfxSplitWindow
* SidebarController::GetSplitWindow()
1418 if (mpParentWindow
!= nullptr)
1420 SfxSplitWindow
* pSplitWindow
= dynamic_cast<SfxSplitWindow
*>(mpParentWindow
->GetParent());
1421 if (pSplitWindow
!= mpSplitWindow
)
1423 if (mpSplitWindow
!= nullptr)
1424 mpSplitWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
1426 mpSplitWindow
= pSplitWindow
;
1428 if (mpSplitWindow
!= nullptr)
1429 mpSplitWindow
->AddEventListener(LINK(this, SidebarController
, WindowEventHandler
));
1431 return mpSplitWindow
;
1437 void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag
)
1439 if (mpParentWindow
== nullptr)
1442 if (bCloseAfterDrag
)
1444 // Make sure that the indicator exists.
1445 if ( ! mpCloseIndicator
)
1447 mpCloseIndicator
.reset(VclPtr
<FixedImage
>::Create(mpParentWindow
));
1448 FixedImage
* pFixedImage
= static_cast<FixedImage
*>(mpCloseIndicator
.get());
1449 const Image
aImage (Theme::GetImage(Theme::Image_CloseIndicator
));
1450 pFixedImage
->SetImage(aImage
);
1451 pFixedImage
->SetSizePixel(aImage
.GetSizePixel());
1452 pFixedImage
->SetBackground(Theme::GetColor(Theme::Color_DeckBackground
));
1455 // Place and show the indicator.
1456 const Size
aWindowSize (mpParentWindow
->GetSizePixel());
1457 const Size
aImageSize (mpCloseIndicator
->GetSizePixel());
1458 mpCloseIndicator
->SetPosPixel(
1460 aWindowSize
.Width() - TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor() - aImageSize
.Width(),
1461 (aWindowSize
.Height() - aImageSize
.Height())/2));
1462 mpCloseIndicator
->Show();
1466 // Hide but don't delete the indicator.
1467 if (mpCloseIndicator
)
1468 mpCloseIndicator
->Hide();
1472 void SidebarController::UpdateTitleBarIcons()
1474 if ( ! mpCurrentDeck
)
1477 const bool bIsHighContrastModeActive (Theme::IsHighContrastMode());
1479 const ResourceManager
& rResourceManager
= *mpResourceManager
;
1481 // Update the deck icon.
1482 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= rResourceManager
.GetDeckDescriptor(mpCurrentDeck
->GetId());
1483 if (xDeckDescriptor
&& mpCurrentDeck
->GetTitleBar())
1485 const OUString
sIconURL(
1486 bIsHighContrastModeActive
1487 ? xDeckDescriptor
->msHighContrastTitleBarIconURL
1488 : xDeckDescriptor
->msTitleBarIconURL
);
1489 mpCurrentDeck
->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL
, mxFrame
));
1492 // Update the panel icons.
1493 const SharedPanelContainer
& rPanels (mpCurrentDeck
->GetPanels());
1494 for (const auto& rxPanel
: rPanels
)
1498 if (!rxPanel
->GetTitleBar())
1500 std::shared_ptr
<PanelDescriptor
> xPanelDescriptor
= rResourceManager
.GetPanelDescriptor(rxPanel
->GetId());
1501 if (!xPanelDescriptor
)
1503 const OUString
sIconURL (
1504 bIsHighContrastModeActive
1505 ? xPanelDescriptor
->msHighContrastTitleBarIconURL
1506 : xPanelDescriptor
->msTitleBarIconURL
);
1507 rxPanel
->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL
, mxFrame
));
1511 void SidebarController::ShowPanel (const Panel
& rPanel
)
1517 mpCurrentDeck
->ShowPanel(rPanel
);
1521 ResourceManager::DeckContextDescriptorContainer
SidebarController::GetMatchingDecks()
1523 ResourceManager::DeckContextDescriptorContainer aDecks
;
1524 mpResourceManager
->GetMatchingDecks (aDecks
,
1525 GetCurrentContext(),
1526 IsDocumentReadOnly(),
1527 mxFrame
->getController());
1531 ResourceManager::PanelContextDescriptorContainer
SidebarController::GetMatchingPanels(const OUString
& rDeckId
)
1533 ResourceManager::PanelContextDescriptorContainer aPanels
;
1535 mpResourceManager
->GetMatchingPanels(aPanels
,
1536 GetCurrentContext(),
1538 mxFrame
->getController());
1542 void SidebarController::updateModel(const css::uno::Reference
<css::frame::XModel
>& xModel
)
1544 mpResourceManager
->UpdateModel(xModel
);
1547 void SidebarController::FadeOut()
1550 mpSplitWindow
->FadeOut();
1553 void SidebarController::FadeIn()
1556 mpSplitWindow
->FadeIn();
1559 tools::Rectangle
SidebarController::GetDeckDragArea() const
1561 tools::Rectangle aRect
;
1564 VclPtr
<DeckTitleBar
> pTitleBar(mpCurrentDeck
->GetTitleBar());
1568 aRect
= pTitleBar
->GetDragArea();
1574 void SidebarController::frameAction(const css::frame::FrameActionEvent
& rEvent
)
1576 if (rEvent
.Frame
== mxFrame
)
1578 if (rEvent
.Action
== css::frame::FrameAction_COMPONENT_DETACHING
)
1579 unregisterSidebarForFrame(this, mxFrame
->getController());
1580 else if (rEvent
.Action
== css::frame::FrameAction_COMPONENT_REATTACHED
)
1581 registerSidebarForFrame(this, mxFrame
->getController());
1585 void SidebarController::saveDeckState()
1587 // Impress shutdown : context (frame) is disposed before sidebar disposing
1588 // calc writer : context (frame) is disposed after sidebar disposing
1589 // so need to test if GetCurrentContext is still valid regarding msApplication
1590 if (GetCurrentContext().msApplication
!= "none")
1592 mpResourceManager
->SaveDecksSettings(GetCurrentContext());
1593 mpResourceManager
->SaveLastActiveDeck(GetCurrentContext(), msCurrentDeckId
);
1597 } // end of namespace sfx2::sidebar
1599 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */