Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / sidebar / SidebarController.cxx
blobc91381d01db9424ab845d8b7038c6b72c8e02691
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
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>
65 using namespace css;
66 using namespace css::uno;
68 namespace
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";
97 return "";
101 namespace sfx2::sidebar {
103 namespace {
104 enum MenuId
106 MID_UNLOCK_TASK_PANEL = 1,
107 MID_LOCK_TASK_PANEL,
108 MID_HIDE_SIDEBAR,
109 MID_CUSTOMIZATION,
110 MID_RESTORE_DEFAULT,
111 MID_FIRST_PANEL,
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),
124 mpCurrentDeck(),
125 mpParentWindow(pParentWindow),
126 mpViewFrame(pViewFrame),
127 mxFrame(pViewFrame->GetFrame().GetFrameInterface()),
128 mpTabBar(VclPtr<TabBar>::Create(
129 mpParentWindow,
130 mxFrame,
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); },
134 this)),
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(),
144 mbIsDeckOpen(),
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),
152 mpResourceManager()
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
183 return instance;
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");
197 return nullptr;
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),
216 xController);
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),
229 xController);
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));
241 if (!hide.empty())
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();
263 saveDeckState();
265 // clear decks
266 ResourceManager::DeckContextDescriptorContainer aDecks;
268 mpResourceManager->GetMatchingDecks (
269 aDecks,
270 GetCurrentContext(),
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;
279 if (aDeck)
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,
321 rEvent.ContextName);
323 if (maRequestedContext != maCurrentContext)
325 mxCurrentController.set(rEvent.Source, css::uno::UNO_QUERY);
326 maContextChangeUpdate.RequestCall(); // async call, not a prob
327 // calling with held
328 // solarmutex
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;
338 dispose();
341 void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& )
343 SolarMutexGuard aSolarMutexGuard;
345 maPropertyChangeForwarder.RequestCall(); // async call, not a prob
346 // to call with held
347 // solarmutex
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()
392 if (!mpTabBar)
394 OSL_ASSERT(mpTabBar!=nullptr);
395 return;
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;
409 bool bIsDeckVisible;
410 const bool bIsOpening (nWidth > mnWidthOnSplitterButtonDown);
411 if (bIsOpening)
412 bIsDeckVisible = nWidth >= nTabBarDefaultWidth + gnWidthOpenThreshold;
413 else
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;
426 nTabX = 0;
428 else // attach the Sidebar towards the right-side of screen
430 nDeckX = 0;
431 nTabX = nWidth - nTabBarDefaultWidth;
434 // Place the deck first.
435 if (bIsDeckVisible)
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);
454 else
455 mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth - nTabBarDefaultWidth, nHeight);
456 mpCurrentDeck->Show();
457 mpCurrentDeck->RequestLayout();
459 else
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)
486 return;
488 if (*mbIsDeckRequestedOpen)
490 // Deck became large enough to be shown. Show it.
491 mnSavedSidebarWidth = nNewWidth;
492 if (!*mbIsDeckOpen)
493 RequestOpenDeck();
495 else
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.
500 mbIsDeckOpen = true;
501 RequestCloseDeck();
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)
518 return;
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 (
546 aDecks,
547 maCurrentContext,
548 mbIsDocumentReadOnly,
549 xController);
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).
559 OUString sNewDeckId;
560 for (const auto& rDeck : aDecks)
562 if (rDeck.mbIsEnabled)
564 if (rDeck.msId == msCurrentDeckId)
566 sNewDeckId = msCurrentDeckId;
567 break;
569 else if (sNewDeckId.getLength() == 0)
570 sNewDeckId = rDeck.msId;
574 if (sNewDeckId.getLength() == 0)
576 // We did not find a valid deck.
577 RequestCloseDeck();
578 return;
581 // Tell the tab bar to highlight the button associated
582 // with the deck.
583 mpTabBar->HighlightDeck(sNewDeckId);
585 std::shared_ptr<DeckDescriptor> xDescriptor = mpResourceManager->GetDeckDescriptor(sNewDeckId);
587 if (xDescriptor)
589 SwitchToDeck(*xDescriptor, maCurrentContext);
593 namespace {
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();
621 return;
623 else
625 // tdf#67627 Clicking a second time on a Deck icon will close the Deck
626 RequestCloseDeck();
627 return;
630 RequestOpenDeck();
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)
648 RequestOpenDeck();
649 SwitchToDeck(rsDeckId);
653 void SidebarController::SwitchToDefaultDeck()
655 SwitchToDeck(gsDefaultDeckId);
658 void SidebarController::SwitchToDeck (
659 const OUString& rsDeckId)
661 if ( msCurrentDeckId != rsDeckId
662 || ! mbIsDeckOpen
663 || mnRequestedForceFlags!=SwitchFlag_NoForce)
665 std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rsDeckId);
667 if (xDeckDescriptor)
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)
681 return;
683 VclPtr<Deck> aDeck = xDeckDescriptor->mpDeck;
684 if (!aDeck || bForceCreate)
686 if (aDeck)
687 aDeck.disposeAndClear();
689 aDeck = VclPtr<Deck>::Create(
690 *xDeckDescriptor,
691 mpParentWindow,
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,
712 rContext,
713 rDeckId,
714 xController);
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)
731 continue;
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 );
739 ++nWriteIndex;
741 else
743 VclPtr<Panel> aPanel = CreatePanel(
744 rPanelContexDescriptor.msId,
745 pDeck->GetPanelParentWindow(),
746 rPanelContexDescriptor.mbIsInitiallyVisible,
747 rContext,
748 pDeck);
749 if (aPanel )
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();
756 if (pTitleBar)
758 pTitleBar->SetMoreOptionsCommand(
759 rPanelContexDescriptor.msMenuCommand,
760 mxFrame, xController);
762 ++nWriteIndex;
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));
783 if (!hide.empty())
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));
789 if (!show.empty())
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
802 || bForceNewDeck)
804 if (mpCurrentDeck)
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,
819 rContext,
820 rDeckDescriptor.msId,
821 xController);
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.
829 SwitchToDeck(
830 rDeckDescriptor,
831 Context(
832 rContext.msApplication,
833 vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Empty)));
834 return;
836 else
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)
855 return;
857 #ifdef DEBUG
858 // Show the context name in the deck title bar.
859 VclPtr<DeckTitleBar> pDebugTitleBar = mpCurrentDeck->GetTitleBar();
860 if (pDebugTitleBar)
861 pDebugTitleBar->SetTitle(rDeckDescriptor.msTitle + " (" + maCurrentContext.msContext + ")");
862 #endif
864 SfxSplitWindow* pSplitWindow = GetSplitWindow();
865 sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth() * mpTabBar->GetDPIScaleFactor();
866 WindowAlign eAlign = pSplitWindow ? pSplitWindow->GetAlign() : WindowAlign::Right;
867 tools::Long nDeckX;
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
874 nDeckX = 0;
877 // Activate the deck and the new set of panels.
878 mpCurrentDeck->setPosSizePixel(
879 nDeckX,
881 mpParentWindow->GetSizePixel().Width() - nTabBarDefaultWidth,
882 mpParentWindow->GetSizePixel().Height());
884 mpCurrentDeck->Show();
886 mpParentWindow->SetText(rDeckDescriptor.msTitle);
888 NotifyResize();
890 // Tell the focus manager about the new panels and tab bar
891 // buttons.
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)
919 return nullptr;
921 // Create the panel which is the parent window of the UIElement.
922 VclPtr<Panel> pPanel = VclPtr<Panel>::Create(
923 *xPanelDescriptor,
924 pParentWindow,
925 bIsInitiallyExpanded,
926 [pDeck]() { return pDeck->RequestLayout(); },
927 [this]() { return this->GetCurrentContext(); },
928 mxFrame);
930 // Create the XUIElement.
931 Reference<ui::XUIElement> xUIElement (CreateUIElement(
932 pPanel->GetComponentInterface(),
933 xPanelDescriptor->msImplementationURL,
934 xPanelDescriptor->mbWantsCanvas,
935 rContext));
936 if (xUIElement.is())
938 // Initialize the panel and add it to the active deck.
939 pPanel->SetUIElement(xUIElement);
941 else
943 pPanel.disposeAndClear();
946 return pPanel;
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))));
970 if (bWantsCanvas)
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(
991 rsImplementationURL,
992 aCreationArguments.getPropertyValues()),
993 UNO_SET_THROW);
995 return xUIElement;
997 catch(const Exception&)
999 TOOLS_WARN_EXCEPTION("sfx.sidebar", "Cannot create panel " << rsImplementationURL);
1000 return nullptr;
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:
1012 NotifyResize();
1013 break;
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();
1023 break;
1025 case VclEventId::ObjectDying:
1026 dispose();
1027 break;
1029 case VclEventId::WindowPaint:
1030 SAL_INFO("sfx.sidebar", "Paint");
1031 break;
1033 default:
1034 break;
1037 else if (rEvent.GetWindow()==mpSplitWindow && mpSplitWindow!=nullptr)
1039 switch (rEvent.GetId())
1041 case VclEventId::WindowMouseButtonDown:
1042 mnWidthOnSplitterButtonDown = mpParentWindow->GetSizePixel().Width();
1043 break;
1045 case VclEventId::WindowMouseButtonUp:
1047 ProcessNewWidth(mpParentWindow->GetSizePixel().Width());
1048 mnWidthOnSplitterButtonDown = 0;
1049 break;
1052 case VclEventId::ObjectDying:
1053 dispose();
1054 break;
1056 default: break;
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);
1092 else
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);
1100 ++nIndex;
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())
1110 bHideLock = false;
1111 else
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));
1139 if (xDispatch.is())
1140 xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>());
1142 else
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().
1147 RequestCloseDeck();
1150 else
1154 OString sNumber;
1155 if (rCurItemId.startsWith("select", &sNumber))
1157 RequestOpenDeck();
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();
1172 else
1176 OString sNumber;
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 (
1184 aDecks,
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())
1212 // Mobile phone.
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());
1223 else if (pNotifier)
1224 pNotifier->notifyWindow(mpCurrentDeck->GetLOKWindowId(), "close");
1227 mbIsDeckRequestedOpen = false;
1228 UpdateDeckOpenState();
1230 if (!mpCurrentDeck)
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)
1247 if (nIndex >= 0)
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.
1264 return;
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 )
1271 return;
1273 if (*mbIsDeckRequestedOpen)
1275 if (!mpParentWindow->IsFloatingMode())
1277 if (mnSavedSidebarWidth <= nTabBarDefaultWidth)
1278 SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow));
1279 else
1280 SetChildWindowWidth(mnSavedSidebarWidth);
1282 else
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));
1301 if (!uno.empty())
1302 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
1303 (uno + "=true").c_str());
1308 else
1310 if ( ! mpParentWindow->IsFloatingMode())
1311 mnSavedSidebarWidth = SetChildWindowWidth(nTabBarDefaultWidth);
1312 else
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);
1326 else
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));
1339 if (!uno.empty())
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);
1354 NotifyResize();
1357 bool SidebarController::CanModifyChildWindowWidth()
1359 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1360 if (pSplitWindow == nullptr)
1361 return false;
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;
1370 else
1371 return false;
1374 sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth)
1376 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1377 if (pSplitWindow == nullptr)
1378 return 0;
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(
1389 mpParentWindow,
1390 Size(nNewWidth, aWindowSize.Height()),
1391 nColumn,
1392 nRow,
1393 false);
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(
1410 nSetId,
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;
1433 else
1434 return nullptr;
1437 void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag)
1439 if (mpParentWindow == nullptr)
1440 return;
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(
1459 Point(
1460 aWindowSize.Width() - TabBar::GetDefaultWidth() * mpTabBar->GetDPIScaleFactor() - aImageSize.Width(),
1461 (aWindowSize.Height() - aImageSize.Height())/2));
1462 mpCloseIndicator->Show();
1464 else
1466 // Hide but don't delete the indicator.
1467 if (mpCloseIndicator)
1468 mpCloseIndicator->Hide();
1472 void SidebarController::UpdateTitleBarIcons()
1474 if ( ! mpCurrentDeck)
1475 return;
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)
1496 if ( ! rxPanel)
1497 continue;
1498 if (!rxPanel->GetTitleBar())
1499 continue;
1500 std::shared_ptr<PanelDescriptor> xPanelDescriptor = rResourceManager.GetPanelDescriptor(rxPanel->GetId());
1501 if (!xPanelDescriptor)
1502 continue;
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)
1513 if (mpCurrentDeck)
1515 if (!IsDeckOpen())
1516 RequestOpenDeck();
1517 mpCurrentDeck->ShowPanel(rPanel);
1521 ResourceManager::DeckContextDescriptorContainer SidebarController::GetMatchingDecks()
1523 ResourceManager::DeckContextDescriptorContainer aDecks;
1524 mpResourceManager->GetMatchingDecks (aDecks,
1525 GetCurrentContext(),
1526 IsDocumentReadOnly(),
1527 mxFrame->getController());
1528 return aDecks;
1531 ResourceManager::PanelContextDescriptorContainer SidebarController::GetMatchingPanels(const OUString& rDeckId)
1533 ResourceManager::PanelContextDescriptorContainer aPanels;
1535 mpResourceManager->GetMatchingPanels(aPanels,
1536 GetCurrentContext(),
1537 rDeckId,
1538 mxFrame->getController());
1539 return aPanels;
1542 void SidebarController::updateModel(const css::uno::Reference<css::frame::XModel>& xModel)
1544 mpResourceManager->UpdateModel(xModel);
1547 void SidebarController::FadeOut()
1549 if (mpSplitWindow)
1550 mpSplitWindow->FadeOut();
1553 void SidebarController::FadeIn()
1555 if (mpSplitWindow)
1556 mpSplitWindow->FadeIn();
1559 tools::Rectangle SidebarController::GetDeckDragArea() const
1561 tools::Rectangle aRect;
1562 if (mpCurrentDeck)
1564 VclPtr<DeckTitleBar> pTitleBar(mpCurrentDeck->GetTitleBar());
1566 if (pTitleBar)
1568 aRect = pTitleBar->GetDragArea();
1571 return aRect;
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: */