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