Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sfx2 / source / sidebar / SidebarController.cxx
blob278c0ca34719855445d37196e9b11d57ed0f9b83
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 <com/sun/star/ui/XSidebarProvider.hpp>
32 #include <com/sun/star/frame/XController2.hpp>
33 #include <sfx2/sidebar/Context.hxx>
34 #include <sfx2/viewsh.hxx>
37 #include <framework/ContextChangeEventMultiplexerTunnel.hxx>
38 #include <vcl/EnumContext.hxx>
39 #include <vcl/uitest/logger.hxx>
40 #include <vcl/uitest/eventdescription.hxx>
41 #include <vcl/svapp.hxx>
42 #include <splitwin.hxx>
43 #include <comphelper/diagnose_ex.hxx>
44 #include <tools/json_writer.hxx>
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>
54 #include <com/sun/star/awt/XWindowPeer.hpp>
55 #include <com/sun/star/frame/XDispatch.hpp>
56 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
57 #include <com/sun/star/ui/ContextChangeEventObject.hpp>
58 #include <com/sun/star/ui/theUIElementFactoryManager.hpp>
59 #include <com/sun/star/util/URL.hpp>
60 #include <com/sun/star/rendering/XSpriteCanvas.hpp>
62 #include <bitmaps.hlst>
64 using namespace css;
65 using namespace css::uno;
67 namespace
69 constexpr OUStringLiteral gsReadOnlyCommandName = u".uno:EditDoc";
70 const sal_Int32 gnWidthCloseThreshold (70);
71 const sal_Int32 gnWidthOpenThreshold (40);
73 std::string UnoNameFromDeckId(std::u16string_view rsDeckId, const sfx2::sidebar::Context& context)
75 if (rsDeckId == u"SdCustomAnimationDeck")
76 return ".uno:CustomAnimation";
78 if (rsDeckId == u"PropertyDeck")
79 return vcl::EnumContext::Application::Impress == vcl::EnumContext::GetApplicationEnum(context.msApplication) ? ".uno:ModifyPage" : ".uno:Sidebar";
81 if (rsDeckId == u"SdLayoutsDeck")
82 return ".uno:ModifyPage";
84 if (rsDeckId == u"SdSlideTransitionDeck")
85 return ".uno:SlideChangeWindow";
87 if (rsDeckId == u"SdAllMasterPagesDeck")
88 return ".uno:MasterSlidesPanel";
90 if (rsDeckId == u"SdMasterPagesDeck")
91 return ".uno:MasterSlidesPanel";
93 if (rsDeckId == u"GalleryDeck")
94 return ".uno:Gallery";
96 OString sUno = ".uno:SidebarDeck." + OUStringToOString(rsDeckId, RTL_TEXTENCODING_ASCII_US);
97 return std::string(sUno);
101 namespace sfx2::sidebar {
103 namespace {
105 /** When in doubt, show this deck.
107 constexpr OUStringLiteral gsDefaultDeckId(u"PropertyDeck");
110 SidebarController::SidebarController (
111 SidebarDockingWindow* pParentWindow,
112 const SfxViewFrame* pViewFrame)
113 : mpParentWindow(pParentWindow),
114 mpViewFrame(pViewFrame),
115 mxFrame(pViewFrame->GetFrame().GetFrameInterface()),
116 mpTabBar(VclPtr<TabBar>::Create(
117 mpParentWindow,
118 mxFrame,
119 [this](const OUString& rsDeckId) { return this->OpenThenToggleDeck(rsDeckId); },
120 [this](weld::Menu& rMainMenu, weld::Menu& rSubMenu,
121 const ::std::vector<TabBar::DeckMenuData>& rMenuData) { return this->ShowPopupMenu(rMainMenu, rSubMenu, rMenuData); },
122 this)),
123 maCurrentContext(OUString(), OUString()),
124 maRequestedContext(OUString(), OUString()),
125 mnRequestedForceFlags(SwitchFlag_NoForce),
126 mbMinimumSidebarWidth(officecfg::Office::UI::Sidebar::General::MinimumWidth::get()),
127 msCurrentDeckId(gsDefaultDeckId),
128 maPropertyChangeForwarder([this](){ return this->BroadcastPropertyChange(); }),
129 maContextChangeUpdate([this](){ return this->UpdateConfigurations(); }),
130 mbFloatingDeckClosed(!pParentWindow->IsFloatingMode()),
131 mnSavedSidebarWidth(pParentWindow->GetSizePixel().Width()),
132 maFocusManager([this](const Panel& rPanel){ return this->ShowPanel(rPanel); }),
133 mbIsDocumentReadOnly(false),
134 mpSplitWindow(nullptr),
135 mnWidthOnSplitterButtonDown(0)
137 mnMaximumSidebarWidth = officecfg::Office::UI::Sidebar::General::MaximumWidth::get() * mpTabBar->GetDPIScaleFactor();
138 // Decks and panel collections for this sidebar
139 mpResourceManager = std::make_unique<ResourceManager>();
142 rtl::Reference<SidebarController> SidebarController::create(SidebarDockingWindow* pParentWindow,
143 const SfxViewFrame* pViewFrame)
145 rtl::Reference<SidebarController> instance(new SidebarController(pParentWindow, pViewFrame));
147 const css::uno::Reference<css::frame::XFrame>& rxFrame = pViewFrame->GetFrame().GetFrameInterface();
148 instance->registerSidebarForFrame(rxFrame->getController());
149 rxFrame->addFrameActionListener(instance);
150 // Listen for window events.
151 instance->mpParentWindow->AddEventListener(LINK(instance.get(), SidebarController, WindowEventHandler));
153 // Listen for theme property changes.
154 instance->mxThemePropertySet = Theme::GetPropertySet();
155 instance->mxThemePropertySet->addPropertyChangeListener(
157 static_cast<css::beans::XPropertyChangeListener*>(instance.get()));
159 // Get the dispatch object as preparation to listen for changes of
160 // the read-only state.
161 const util::URL aURL (Tools::GetURL(gsReadOnlyCommandName));
162 instance->mxReadOnlyModeDispatch = Tools::GetDispatch(rxFrame, aURL);
163 if (instance->mxReadOnlyModeDispatch.is())
164 instance->mxReadOnlyModeDispatch->addStatusListener(instance, aURL);
166 //first UpdateConfigurations call will SwitchToDeck
168 return instance;
171 SidebarController::~SidebarController()
175 SidebarController* SidebarController::GetSidebarControllerForFrame (
176 const css::uno::Reference<css::frame::XFrame>& rxFrame)
178 uno::Reference<frame::XController> const xController(rxFrame->getController());
179 if (!xController.is()) // this may happen during dispose of Draw controller but perhaps it's a bug
181 SAL_WARN("sfx.sidebar", "GetSidebarControllerForFrame: frame has no XController");
182 return nullptr;
184 uno::Reference<ui::XContextChangeEventListener> const xListener(
185 framework::GetFirstListenerWith(
186 ::comphelper::getProcessComponentContext(),
187 xController,
188 [] (uno::Reference<uno::XInterface> const& xRef)
189 { return nullptr != dynamic_cast<SidebarController*>(xRef.get()); }
192 return dynamic_cast<SidebarController*>(xListener.get());
195 void SidebarController::registerSidebarForFrame(const css::uno::Reference<css::frame::XController>& xController)
197 // Listen for context change events.
198 css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
199 css::ui::ContextChangeEventMultiplexer::get(
200 ::comphelper::getProcessComponentContext()));
201 xMultiplexer->addContextChangeEventListener(
202 static_cast<css::ui::XContextChangeEventListener*>(this),
203 xController);
206 void SidebarController::unregisterSidebarForFrame(const css::uno::Reference<css::frame::XController>& xController)
208 saveDeckState();
209 disposeDecks();
211 css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
212 css::ui::ContextChangeEventMultiplexer::get(
213 ::comphelper::getProcessComponentContext()));
214 xMultiplexer->removeContextChangeEventListener(
215 static_cast<css::ui::XContextChangeEventListener*>(this),
216 xController);
219 void SidebarController::disposeDecks()
221 SolarMutexGuard aSolarMutexGuard;
223 if (comphelper::LibreOfficeKit::isActive())
225 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
227 const std::string hide = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
228 if (!hide.empty())
229 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
230 OString(hide + "=false"));
233 if (mpParentWindow)
234 mpParentWindow->ReleaseLOKNotifier();
237 mpCurrentDeck.clear();
238 maFocusManager.Clear();
239 mpResourceManager->disposeDecks();
242 namespace
244 class CloseIndicator final : public InterimItemWindow
246 public:
247 CloseIndicator(vcl::Window* pParent)
248 : InterimItemWindow(pParent, "svt/ui/fixedimagecontrol.ui", "FixedImageControl")
249 , m_xWidget(m_xBuilder->weld_image("image"))
251 InitControlBase(m_xWidget.get());
253 m_xWidget->set_from_icon_name(SIDEBAR_CLOSE_INDICATOR);
255 SetSizePixel(get_preferred_size());
257 SetBackground(Theme::GetColor(Theme::Color_DeckBackground));
260 virtual ~CloseIndicator() override
262 disposeOnce();
265 virtual void dispose() override
267 m_xWidget.reset();
268 InterimItemWindow::dispose();
271 private:
272 std::unique_ptr<weld::Image> m_xWidget;
276 void SidebarController::disposing(std::unique_lock<std::mutex>&)
278 SolarMutexGuard aSolarMutexGuard;
280 mpCloseIndicator.disposeAndClear();
282 maFocusManager.Clear();
283 mpTabBar.disposeAndClear();
285 saveDeckState();
287 // clear decks
288 ResourceManager::DeckContextDescriptorContainer aDecks;
290 mpResourceManager->GetMatchingDecks (
291 aDecks,
292 GetCurrentContext(),
293 IsDocumentReadOnly(),
294 mxFrame->getController());
296 for (const auto& rDeck : aDecks)
298 std::shared_ptr<DeckDescriptor> deckDesc = mpResourceManager->GetDeckDescriptor(rDeck.msId);
300 VclPtr<Deck> aDeck = deckDesc->mpDeck;
301 if (aDeck)
302 aDeck.disposeAndClear();
305 maContextChangeUpdate.CancelRequest();
307 if (mxReadOnlyModeDispatch.is())
308 mxReadOnlyModeDispatch->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName));
310 if (mxThemePropertySet.is())
311 mxThemePropertySet->removePropertyChangeListener(
313 static_cast<css::beans::XPropertyChangeListener*>(this));
315 if (mpParentWindow != nullptr)
317 mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
318 mpParentWindow = nullptr;
321 if (mpSplitWindow != nullptr)
323 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
324 mpSplitWindow = nullptr;
327 mxFrame->removeFrameActionListener(this);
329 uno::Reference<css::frame::XController> xController = mxFrame->getController();
330 if (!xController.is())
331 xController = mxCurrentController;
333 unregisterSidebarForFrame(xController);
336 void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent)
338 SolarMutexGuard aSolarMutexGuard;
340 // Update to the requested new context asynchronously to avoid
341 // subtle errors caused by SFX2 which in rare cases can not
342 // properly handle a synchronous update.
344 maRequestedContext = Context(
345 rEvent.ApplicationName,
346 rEvent.ContextName);
348 if (maRequestedContext != maCurrentContext)
350 mxCurrentController.set(rEvent.Source, css::uno::UNO_QUERY);
351 maContextChangeUpdate.RequestCall(); // async call, not a prob
352 // calling with held
353 // solarmutex
354 // TODO: this call is redundant but mandatory for unit test to update context on document loading
355 if (!comphelper::LibreOfficeKit::isActive())
356 UpdateConfigurations();
360 void SAL_CALL SidebarController::disposing (const css::lang::EventObject& )
362 dispose();
365 void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& )
367 SolarMutexGuard aSolarMutexGuard;
369 maPropertyChangeForwarder.RequestCall(); // async call, not a prob
370 // to call with held
371 // solarmutex
374 void SAL_CALL SidebarController::statusChanged (const css::frame::FeatureStateEvent& rEvent)
376 SolarMutexGuard aSolarMutexGuard;
378 bool bIsReadWrite (true);
379 if (rEvent.IsEnabled)
380 rEvent.State >>= bIsReadWrite;
382 if (mbIsDocumentReadOnly != !bIsReadWrite)
384 mbIsDocumentReadOnly = !bIsReadWrite;
386 // Force the current deck to update its panel list.
387 if ( ! mbIsDocumentReadOnly)
388 SwitchToDefaultDeck();
390 mnRequestedForceFlags |= SwitchFlag_ForceSwitch;
391 maContextChangeUpdate.RequestCall(); // async call, ok to call
392 // with held solarmutex
396 void SAL_CALL SidebarController::requestLayout()
398 SolarMutexGuard aSolarMutexGuard;
400 sal_Int32 nMinimalWidth = 0;
401 if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
403 mpCurrentDeck->RequestLayout();
404 nMinimalWidth = mbMinimumSidebarWidth ? mpCurrentDeck->GetMinimalWidth() : 0;
406 RestrictWidth(nMinimalWidth);
409 void SidebarController::BroadcastPropertyChange()
411 mpParentWindow->Invalidate(InvalidateFlags::Children);
414 void SidebarController::NotifyResize()
416 if (!mpTabBar)
418 OSL_ASSERT(mpTabBar!=nullptr);
419 return;
422 const sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
424 const sal_Int32 nWidth(mpParentWindow->GetSizePixel().Width());
425 const sal_Int32 nHeight(mpParentWindow->GetSizePixel().Height());
427 mbIsDeckOpen = (nWidth > nTabBarDefaultWidth);
429 if (mnSavedSidebarWidth <= 0)
430 mnSavedSidebarWidth = nWidth;
432 bool bIsDeckVisible;
433 const bool bIsOpening (nWidth > mnWidthOnSplitterButtonDown);
434 if (bIsOpening)
435 bIsDeckVisible = nWidth >= nTabBarDefaultWidth + gnWidthOpenThreshold;
436 else
437 bIsDeckVisible = nWidth >= nTabBarDefaultWidth + gnWidthCloseThreshold;
438 mbIsDeckRequestedOpen = bIsDeckVisible;
439 UpdateCloseIndicator(!bIsDeckVisible);
441 if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
443 SfxSplitWindow* pSplitWindow = GetSplitWindow();
444 WindowAlign eAlign = pSplitWindow ? pSplitWindow->GetAlign() : WindowAlign::Right;
445 tools::Long nDeckX, nTabX;
446 if (eAlign == WindowAlign::Left) // attach the Sidebar towards the left-side of screen
448 nDeckX = nTabBarDefaultWidth;
449 nTabX = 0;
451 else // attach the Sidebar towards the right-side of screen
453 nDeckX = 0;
454 nTabX = nWidth - nTabBarDefaultWidth;
457 // Place the deck first.
458 if (bIsDeckVisible)
460 if (comphelper::LibreOfficeKit::isActive())
462 // We want to let the layouter use up as much of the
463 // height as necessary to make sure no scrollbar is
464 // visible. This only works when there are no greedy
465 // panes that fill up all available area. So we only
466 // use this for the PropertyDeck, which has no such
467 // panes, while most other do. This is fine, since
468 // it's the PropertyDeck that really has many panes
469 // that can collapse or expand. For others, limit
470 // the height to something sensible.
471 const sal_Int32 nExtHeight = (msCurrentDeckId == "PropertyDeck" ? 2000 : 600);
472 // No TabBar in LOK (use nWidth in full).
473 mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth, nExtHeight);
475 else
476 mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth - nTabBarDefaultWidth, nHeight);
477 mpCurrentDeck->Show();
478 mpCurrentDeck->RequestLayout();
479 mpTabBar->HighlightDeck(mpCurrentDeck->GetId());
481 else
482 mpCurrentDeck->Hide();
484 // Now place the tab bar.
485 mpTabBar->setPosSizePixel(nTabX, 0, nTabBarDefaultWidth, nHeight);
486 if (!comphelper::LibreOfficeKit::isActive())
487 mpTabBar->Show(); // Don't show TabBar in LOK.
490 // Determine if the closer of the deck can be shown.
491 sal_Int32 nMinimalWidth = 0;
492 if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
494 DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar();
495 if (pTitleBar && pTitleBar->GetVisible())
496 pTitleBar->SetCloserVisible(CanModifyChildWindowWidth());
497 nMinimalWidth = mbMinimumSidebarWidth ? mpCurrentDeck->GetMinimalWidth() : 0;
500 RestrictWidth(nMinimalWidth);
503 void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth)
505 if ( ! mbIsDeckRequestedOpen)
506 return;
508 if (*mbIsDeckRequestedOpen)
510 // Deck became large enough to be shown. Show it.
511 mnSavedSidebarWidth = nNewWidth;
512 // Store nNewWidth to mnWidthOnSplitterButtonDown when dragging sidebar Splitter
513 mnWidthOnSplitterButtonDown = nNewWidth;
514 if (!*mbIsDeckOpen)
515 RequestOpenDeck();
517 else
519 // Deck became too small. Close it completely.
520 // If window is wider than the tab bar then mark the deck as being visible, even when it is not.
521 // This is to trigger an adjustment of the width to the width of the tab bar.
522 mbIsDeckOpen = true;
523 RequestCloseDeck();
525 if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth())
526 mnSavedSidebarWidth = mnWidthOnSplitterButtonDown;
530 void SidebarController::SyncUpdate()
532 maPropertyChangeForwarder.Sync();
533 maContextChangeUpdate.Sync();
536 void SidebarController::UpdateConfigurations()
538 if (maCurrentContext == maRequestedContext
539 && mnRequestedForceFlags == SwitchFlag_NoForce)
540 return;
542 if ((maCurrentContext.msApplication != "none") &&
543 !maCurrentContext.msApplication.isEmpty())
545 mpResourceManager->SaveDecksSettings(maCurrentContext);
546 mpResourceManager->SetLastActiveDeck(maCurrentContext, msCurrentDeckId);
549 // get last active deck for this application on first update
550 if (!maRequestedContext.msApplication.isEmpty() &&
551 (maCurrentContext.msApplication != maRequestedContext.msApplication))
553 OUString sLastActiveDeck = mpResourceManager->GetLastActiveDeck( maRequestedContext );
554 if (!sLastActiveDeck.isEmpty())
555 msCurrentDeckId = sLastActiveDeck;
558 maCurrentContext = maRequestedContext;
560 mpResourceManager->InitDeckContext(GetCurrentContext());
562 // Find the set of decks that could be displayed for the new context.
563 ResourceManager::DeckContextDescriptorContainer aDecks;
565 css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
567 mpResourceManager->GetMatchingDecks (
568 aDecks,
569 maCurrentContext,
570 mbIsDocumentReadOnly,
571 xController);
573 maFocusManager.Clear();
575 // Notify the tab bar about the updated set of decks.
576 mpTabBar->SetDecks(aDecks);
578 // Find the new deck. By default that is the same as the old
579 // one. If that is not set or not enabled, then choose the
580 // first enabled deck (which is PropertyDeck).
581 OUString sNewDeckId;
582 for (const auto& rDeck : aDecks)
584 if (rDeck.mbIsEnabled)
586 if (rDeck.msId == msCurrentDeckId)
588 sNewDeckId = msCurrentDeckId;
589 break;
591 else if (sNewDeckId.getLength() == 0)
592 sNewDeckId = rDeck.msId;
596 if (sNewDeckId.getLength() == 0)
598 // We did not find a valid deck.
599 RequestCloseDeck();
600 return;
603 std::shared_ptr<DeckDescriptor> xDescriptor = mpResourceManager->GetDeckDescriptor(sNewDeckId);
605 if (xDescriptor)
607 SwitchToDeck(*xDescriptor, maCurrentContext);
611 namespace {
613 void collectUIInformation(const OUString& rDeckId)
615 EventDescription aDescription;
616 aDescription.aAction = "SIDEBAR";
617 aDescription.aParent = "MainWindow";
618 aDescription.aParameters = {{"PANEL", rDeckId}};
619 aDescription.aKeyWord = "CurrentApp";
621 UITestLogger::getInstance().logEvent(aDescription);
626 void SidebarController::OpenThenToggleDeck (
627 const OUString& rsDeckId)
629 SfxSplitWindow* pSplitWindow = GetSplitWindow();
630 if ( pSplitWindow && !pSplitWindow->IsFadeIn() )
631 // tdf#83546 Collapsed sidebar should expand first
632 pSplitWindow->FadeIn();
633 else if ( IsDeckVisible( rsDeckId ) )
635 if( !WasFloatingDeckClosed() )
637 // tdf#88241 Summoning an undocked sidebar a second time should close sidebar
638 mpParentWindow->Close();
639 return;
641 else
643 // tdf#67627 Clicking a second time on a Deck icon will close the Deck
644 RequestCloseDeck();
645 return;
648 RequestOpenDeck();
649 // before SwitchToDeck which may cause the rsDeckId string to be released
650 collectUIInformation(rsDeckId);
651 SwitchToDeck(rsDeckId);
653 // Make sure the sidebar is wide enough to fit the requested content
654 if (mpCurrentDeck && mpTabBar)
656 sal_Int32 nRequestedWidth = mpCurrentDeck->GetMinimalWidth() + TabBar::GetDefaultWidth();
657 // if sidebar was dragged
658 if(mnWidthOnSplitterButtonDown > 0 && mnWidthOnSplitterButtonDown > nRequestedWidth){
659 SetChildWindowWidth(mnWidthOnSplitterButtonDown);
660 }else{
661 // tdf#150639 The mnWidthOnSplitterButtonDown is initialized to 0 at program start.
662 // This makes every call to take the else case until the user manually changes the
663 // width, but some decks such as Master Slides have the mnMinimalWidth too low which
664 // makes them too narrow for the content they should display to the user.
665 SetChildWindowWidth(nRequestedWidth > mnSavedSidebarWidth ? nRequestedWidth
666 : mnSavedSidebarWidth);
671 void SidebarController::OpenThenSwitchToDeck (
672 std::u16string_view rsDeckId)
674 RequestOpenDeck();
675 SwitchToDeck(rsDeckId);
679 void SidebarController::SwitchToDefaultDeck()
681 SwitchToDeck(gsDefaultDeckId);
684 void SidebarController::SwitchToDeck (
685 std::u16string_view rsDeckId)
687 if ( msCurrentDeckId != rsDeckId
688 || ! mbIsDeckOpen
689 || mnRequestedForceFlags!=SwitchFlag_NoForce)
691 std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rsDeckId);
693 if (xDeckDescriptor)
694 SwitchToDeck(*xDeckDescriptor, maCurrentContext);
698 void SidebarController::CreateDeck(std::u16string_view rDeckId) {
699 CreateDeck(rDeckId, maCurrentContext);
702 void SidebarController::CreateDeck(std::u16string_view rDeckId, const Context& rContext, bool bForceCreate)
704 std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rDeckId);
706 if (!xDeckDescriptor)
707 return;
709 VclPtr<Deck> aDeck = xDeckDescriptor->mpDeck;
710 if (!aDeck || bForceCreate)
712 if (aDeck)
713 aDeck.disposeAndClear();
715 aDeck = VclPtr<Deck>::Create(
716 *xDeckDescriptor,
717 mpParentWindow,
718 [this]() { return this->RequestCloseDeck(); });
720 xDeckDescriptor->mpDeck = aDeck;
721 CreatePanels(rDeckId, rContext);
724 void SidebarController::CreatePanels(std::u16string_view rDeckId, const Context& rContext)
726 std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rDeckId);
728 // init panels bounded to that deck, do not wait them being displayed as may be accessed through API
730 VclPtr<Deck> pDeck = xDeckDescriptor->mpDeck;
732 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors;
734 css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
736 mpResourceManager->GetMatchingPanels(
737 aPanelContextDescriptors,
738 rContext,
739 rDeckId,
740 xController);
742 // Update the panel list.
743 const sal_Int32 nNewPanelCount (aPanelContextDescriptors.size());
744 SharedPanelContainer aNewPanels;
745 sal_Int32 nWriteIndex (0);
747 aNewPanels.resize(nNewPanelCount);
749 for (sal_Int32 nReadIndex=0; nReadIndex<nNewPanelCount; ++nReadIndex)
751 const ResourceManager::PanelContextDescriptor& rPanelContexDescriptor (
752 aPanelContextDescriptors[nReadIndex]);
754 // Determine if the panel can be displayed.
755 const bool bIsPanelVisible (!mbIsDocumentReadOnly || rPanelContexDescriptor.mbShowForReadOnlyDocuments);
756 if ( ! bIsPanelVisible)
757 continue;
759 auto xOldPanel(pDeck->GetPanel(rPanelContexDescriptor.msId));
760 if (xOldPanel)
762 xOldPanel->SetLurkMode(false);
763 aNewPanels[nWriteIndex] = xOldPanel;
764 xOldPanel->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible);
765 ++nWriteIndex;
767 else
769 auto aPanel = CreatePanel(rPanelContexDescriptor.msId,
770 pDeck->GetPanelParentWindow(),
771 rPanelContexDescriptor.mbIsInitiallyVisible,
772 rContext,
773 pDeck);
774 if (aPanel)
776 aNewPanels[nWriteIndex] = std::move(aPanel);
778 // Depending on the context we have to change the command
779 // for the "more options" dialog.
780 PanelTitleBar* pTitleBar = aNewPanels[nWriteIndex]->GetTitleBar();
781 if (pTitleBar)
783 pTitleBar->SetMoreOptionsCommand(
784 rPanelContexDescriptor.msMenuCommand,
785 mxFrame, xController);
787 ++nWriteIndex;
792 // mpCurrentPanels - may miss stuff (?)
793 aNewPanels.resize(nWriteIndex);
794 pDeck->ResetPanels(std::move(aNewPanels));
797 void SidebarController::SwitchToDeck (
798 const DeckDescriptor& rDeckDescriptor,
799 const Context& rContext)
801 if (comphelper::LibreOfficeKit::isActive())
803 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
805 if (msCurrentDeckId != rDeckDescriptor.msId)
807 const std::string hide = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
808 if (!hide.empty())
809 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
810 OString(hide + "=false"));
813 const std::string show = UnoNameFromDeckId(rDeckDescriptor.msId, GetCurrentContext());
814 if (!show.empty())
815 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
816 OString(show + "=true"));
820 maFocusManager.Clear();
822 const bool bForceNewDeck ((mnRequestedForceFlags&SwitchFlag_ForceNewDeck)!=0);
823 const bool bForceNewPanels ((mnRequestedForceFlags&SwitchFlag_ForceNewPanels)!=0);
824 mnRequestedForceFlags = SwitchFlag_NoForce;
826 if ( msCurrentDeckId != rDeckDescriptor.msId
827 || bForceNewDeck)
829 if (mpCurrentDeck)
830 mpCurrentDeck->Hide();
832 msCurrentDeckId = rDeckDescriptor.msId;
835 // Determine the panels to display in the deck.
836 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors;
838 css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
840 mpResourceManager->GetMatchingPanels(
841 aPanelContextDescriptors,
842 rContext,
843 rDeckDescriptor.msId,
844 xController);
846 if (aPanelContextDescriptors.empty())
848 // There are no panels to be displayed in the current context.
849 if (vcl::EnumContext::GetContextEnum(rContext.msContext) != vcl::EnumContext::Context::Empty)
851 // Switch to the "empty" context and try again.
852 SwitchToDeck(
853 rDeckDescriptor,
854 Context(
855 rContext.msApplication,
856 vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Empty)));
857 return;
859 else
861 // This is already the "empty" context. Looks like we have
862 // to live with an empty deck.
866 // Provide a configuration and Deck object.
868 CreateDeck(rDeckDescriptor.msId, rContext, bForceNewDeck);
870 if (bForceNewPanels && !bForceNewDeck) // already forced if bForceNewDeck
871 CreatePanels(rDeckDescriptor.msId, rContext);
873 if (mpCurrentDeck && mpCurrentDeck != rDeckDescriptor.mpDeck)
874 mpCurrentDeck->Hide();
875 mpCurrentDeck.reset(rDeckDescriptor.mpDeck);
877 if ( ! mpCurrentDeck)
878 return;
880 #ifdef DEBUG
881 // Show the context name in the deck title bar.
882 DeckTitleBar* pDebugTitleBar = mpCurrentDeck->GetTitleBar();
883 if (pDebugTitleBar)
884 pDebugTitleBar->SetTitle(rDeckDescriptor.msTitle + " (" + maCurrentContext.msContext + ")");
885 #endif
887 SfxSplitWindow* pSplitWindow = GetSplitWindow();
888 sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
889 WindowAlign eAlign = pSplitWindow ? pSplitWindow->GetAlign() : WindowAlign::Right;
890 tools::Long nDeckX;
891 if (eAlign == WindowAlign::Left) // attach the Sidebar towards the left-side of screen
893 nDeckX = nTabBarDefaultWidth;
895 else // attach the Sidebar towards the right-side of screen
897 nDeckX = 0;
900 // Activate the deck and the new set of panels.
901 mpCurrentDeck->setPosSizePixel(
902 nDeckX,
904 mpParentWindow->GetSizePixel().Width() - nTabBarDefaultWidth,
905 mpParentWindow->GetSizePixel().Height());
907 mpCurrentDeck->Show();
909 mpParentWindow->SetText(rDeckDescriptor.msTitle);
911 NotifyResize();
913 // Tell the focus manager about the new panels and tab bar
914 // buttons.
915 maFocusManager.SetDeck(mpCurrentDeck);
916 maFocusManager.SetPanels(mpCurrentDeck->GetPanels());
918 mpTabBar->UpdateFocusManager(maFocusManager);
919 UpdateTitleBarIcons();
922 void SidebarController::notifyDeckTitle(std::u16string_view targetDeckId)
924 if (msCurrentDeckId == targetDeckId)
926 maFocusManager.SetDeck(mpCurrentDeck);
927 mpTabBar->UpdateFocusManager(maFocusManager);
928 UpdateTitleBarIcons();
932 std::shared_ptr<Panel> SidebarController::CreatePanel (
933 std::u16string_view rsPanelId,
934 weld::Widget* pParentWindow,
935 const bool bIsInitiallyExpanded,
936 const Context& rContext,
937 const VclPtr<Deck>& pDeck)
939 std::shared_ptr<PanelDescriptor> xPanelDescriptor = mpResourceManager->GetPanelDescriptor(rsPanelId);
941 if (!xPanelDescriptor)
942 return nullptr;
944 // Create the panel which is the parent window of the UIElement.
945 auto xPanel = std::make_shared<Panel>(
946 *xPanelDescriptor,
947 pParentWindow,
948 bIsInitiallyExpanded,
949 pDeck,
950 [this]() { return this->GetCurrentContext(); },
951 mxFrame);
953 // Create the XUIElement.
954 Reference<ui::XUIElement> xUIElement (CreateUIElement(
955 xPanel->GetElementParentWindow(),
956 xPanelDescriptor->msImplementationURL,
957 xPanelDescriptor->mbWantsCanvas,
958 rContext));
959 if (xUIElement.is())
961 // Initialize the panel and add it to the active deck.
962 xPanel->SetUIElement(xUIElement);
964 else
966 xPanel.reset();
969 return xPanel;
972 Reference<ui::XUIElement> SidebarController::CreateUIElement (
973 const Reference<awt::XWindow>& rxWindow,
974 const OUString& rsImplementationURL,
975 const bool bWantsCanvas,
976 const Context& rContext)
980 const Reference<XComponentContext> xComponentContext (::comphelper::getProcessComponentContext() );
981 const Reference<ui::XUIElementFactory> xUIElementFactory =
982 ui::theUIElementFactoryManager::get( xComponentContext );
984 // Create the XUIElement.
985 ::comphelper::NamedValueCollection aCreationArguments;
986 aCreationArguments.put("Frame", Any(mxFrame));
987 aCreationArguments.put("ParentWindow", Any(rxWindow));
988 SidebarDockingWindow* pSfxDockingWindow = mpParentWindow.get();
989 if (pSfxDockingWindow != nullptr)
990 aCreationArguments.put("SfxBindings", Any(reinterpret_cast<sal_uInt64>(&pSfxDockingWindow->GetBindings())));
991 aCreationArguments.put("Theme", Theme::GetPropertySet());
992 aCreationArguments.put("Sidebar", Any(Reference<ui::XSidebar>(static_cast<ui::XSidebar*>(this))));
993 if (bWantsCanvas)
995 Reference<rendering::XSpriteCanvas> xCanvas (VCLUnoHelper::GetWindow(rxWindow)->GetOutDev()->GetSpriteCanvas());
996 aCreationArguments.put("Canvas", Any(xCanvas));
999 if (mxCurrentController.is())
1001 OUString aModule = Tools::GetModuleName(mxCurrentController);
1002 if (!aModule.isEmpty())
1004 aCreationArguments.put("Module", Any(aModule));
1006 aCreationArguments.put("Controller", Any(mxCurrentController));
1009 aCreationArguments.put("ApplicationName", Any(rContext.msApplication));
1010 aCreationArguments.put("ContextName", Any(rContext.msContext));
1012 Reference<ui::XUIElement> xUIElement(
1013 xUIElementFactory->createUIElement(
1014 rsImplementationURL,
1015 aCreationArguments.getPropertyValues()),
1016 UNO_SET_THROW);
1018 return xUIElement;
1020 catch(const Exception&)
1022 TOOLS_WARN_EXCEPTION("sfx.sidebar", "Cannot create panel " << rsImplementationURL);
1023 return nullptr;
1027 IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent&, rEvent, void)
1029 if (rEvent.GetWindow() == mpParentWindow)
1031 switch (rEvent.GetId())
1033 case VclEventId::WindowShow:
1034 case VclEventId::WindowResize:
1035 NotifyResize();
1036 break;
1038 case VclEventId::WindowDataChanged:
1039 // Force an update of deck and tab bar to reflect
1040 // changes in theme (high contrast mode).
1041 Theme::HandleDataChange();
1042 UpdateTitleBarIcons();
1043 mpParentWindow->Invalidate();
1044 mnRequestedForceFlags |= SwitchFlag_ForceNewDeck | SwitchFlag_ForceNewPanels;
1045 maContextChangeUpdate.RequestCall();
1046 break;
1048 case VclEventId::ObjectDying:
1049 dispose();
1050 break;
1052 case VclEventId::WindowPaint:
1053 SAL_INFO("sfx.sidebar", "Paint");
1054 break;
1056 default:
1057 break;
1060 else if (rEvent.GetWindow()==mpSplitWindow && mpSplitWindow!=nullptr)
1062 switch (rEvent.GetId())
1064 case VclEventId::WindowMouseButtonDown:
1065 mnWidthOnSplitterButtonDown = mpParentWindow->GetSizePixel().Width();
1066 break;
1068 case VclEventId::WindowMouseButtonUp:
1070 ProcessNewWidth(mpParentWindow->GetSizePixel().Width());
1071 break;
1074 case VclEventId::ObjectDying:
1075 dispose();
1076 break;
1078 default: break;
1083 void SidebarController::ShowPopupMenu(
1084 weld::Menu& rMainMenu, weld::Menu& rSubMenu,
1085 const ::std::vector<TabBar::DeckMenuData>& rMenuData) const
1087 PopulatePopupMenus(rMainMenu, rSubMenu, rMenuData);
1088 rMainMenu.connect_activate(LINK(const_cast<SidebarController*>(this), SidebarController, OnMenuItemSelected));
1089 rSubMenu.connect_activate(LINK(const_cast<SidebarController*>(this), SidebarController, OnSubMenuItemSelected));
1092 void SidebarController::PopulatePopupMenus(weld::Menu& rMenu, weld::Menu& rCustomizationMenu,
1093 const std::vector<TabBar::DeckMenuData>& rMenuData) const
1095 // Add one entry for every tool panel element to individually make
1096 // them visible or hide them.
1097 sal_Int32 nIndex (0);
1098 for (const auto& rItem : rMenuData)
1100 OUString sIdent("select" + OUString::number(nIndex));
1101 rMenu.insert(nIndex, sIdent, rItem.msDisplayName,
1102 nullptr, nullptr, nullptr, TRISTATE_FALSE);
1103 rMenu.set_active(sIdent, rItem.mbIsCurrentDeck);
1104 rMenu.set_sensitive(sIdent, rItem.mbIsEnabled && rItem.mbIsActive);
1106 if (!comphelper::LibreOfficeKit::isActive())
1108 if (rItem.mbIsCurrentDeck)
1110 // Don't allow the currently visible deck to be disabled.
1111 OUString sSubIdent("nocustomize" + OUString::number(nIndex));
1112 rCustomizationMenu.insert(nIndex, sSubIdent, rItem.msDisplayName,
1113 nullptr, nullptr, nullptr, TRISTATE_FALSE);
1114 rCustomizationMenu.set_active(sSubIdent, true);
1116 else
1118 OUString sSubIdent("customize" + OUString::number(nIndex));
1119 rCustomizationMenu.insert(nIndex, sSubIdent, rItem.msDisplayName,
1120 nullptr, nullptr, nullptr, TRISTATE_TRUE);
1121 rCustomizationMenu.set_active(sSubIdent, rItem.mbIsEnabled && rItem.mbIsActive);
1125 ++nIndex;
1128 bool bHideLock = true;
1129 bool bHideUnLock = true;
1130 // LOK doesn't support docked/undocked; Sidebar is floating but rendered docked in browser.
1131 if (!comphelper::LibreOfficeKit::isActive())
1133 // Add entry for docking or un-docking the tool panel.
1134 if (mpParentWindow->IsFloatingMode())
1135 bHideLock = false;
1136 else
1137 bHideUnLock = false;
1139 rMenu.set_visible("locktaskpanel", !bHideLock);
1140 rMenu.set_visible("unlocktaskpanel", !bHideUnLock);
1142 // No Restore or Customize options for LoKit.
1143 rMenu.set_visible("customization", !comphelper::LibreOfficeKit::isActive());
1146 IMPL_LINK(SidebarController, OnMenuItemSelected, const OUString&, rCurItemId, void)
1148 if (rCurItemId == "unlocktaskpanel")
1150 mpParentWindow->SetFloatingMode(true);
1151 if (mpParentWindow->IsFloatingMode())
1152 mpParentWindow->ToTop(ToTopFlags::GrabFocusOnly);
1154 else if (rCurItemId == "locktaskpanel")
1156 mpParentWindow->SetFloatingMode(false);
1158 else if (rCurItemId == "hidesidebar")
1160 if (!comphelper::LibreOfficeKit::isActive())
1162 const util::URL aURL(Tools::GetURL(".uno:Sidebar"));
1163 Reference<frame::XDispatch> xDispatch(Tools::GetDispatch(mxFrame, aURL));
1164 if (xDispatch.is())
1165 xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>());
1167 else
1169 // In LOK we don't really destroy the sidebar when "closing";
1170 // we simply hide it. This is because recreating it is problematic
1171 // See notes in SidebarDockingWindow::NotifyResize().
1172 RequestCloseDeck();
1175 else
1179 OUString sNumber;
1180 if (rCurItemId.startsWith("select", &sNumber))
1182 RequestOpenDeck();
1183 SwitchToDeck(mpTabBar->GetDeckIdForIndex(sNumber.toInt32()));
1185 mpParentWindow->GrabFocusToDocument();
1187 catch (RuntimeException&)
1193 IMPL_LINK(SidebarController, OnSubMenuItemSelected, const OUString&, rCurItemId, void)
1195 if (rCurItemId == "restoredefault")
1196 mpTabBar->RestoreHideFlags();
1197 else
1201 OUString sNumber;
1202 if (rCurItemId.startsWith("customize", &sNumber))
1204 mpTabBar->ToggleHideFlag(sNumber.toInt32());
1206 // Find the set of decks that could be displayed for the new context.
1207 ResourceManager::DeckContextDescriptorContainer aDecks;
1208 mpResourceManager->GetMatchingDecks (
1209 aDecks,
1210 GetCurrentContext(),
1211 IsDocumentReadOnly(),
1212 mxFrame->getController());
1213 // Notify the tab bar about the updated set of decks.
1214 maFocusManager.Clear();
1215 mpTabBar->SetDecks(aDecks);
1216 mpTabBar->HighlightDeck(mpCurrentDeck->GetId());
1217 mpTabBar->UpdateFocusManager(maFocusManager);
1219 mpParentWindow->GrabFocusToDocument();
1221 catch (RuntimeException&)
1228 void SidebarController::RequestCloseDeck()
1230 if (comphelper::LibreOfficeKit::isActive() && mpCurrentDeck)
1232 const SfxViewShell* pViewShell = SfxViewShell::Current();
1233 if (pViewShell && pViewShell->isLOKMobilePhone())
1235 // Mobile phone - TODO: unify with desktop
1236 tools::JsonWriter aJsonWriter;
1237 aJsonWriter.put("id", mpParentWindow->get_id());
1238 aJsonWriter.put("type", "dockingwindow");
1239 aJsonWriter.put("text", mpParentWindow->GetText());
1240 aJsonWriter.put("enabled", false);
1241 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_JSDIALOG, aJsonWriter.finishAndGetAsOString());
1243 else if (pViewShell)
1245 tools::JsonWriter aJsonWriter;
1246 aJsonWriter.put("id", mpParentWindow->get_id());
1247 aJsonWriter.put("action", "close");
1248 aJsonWriter.put("jsontype", "sidebar");
1249 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_JSDIALOG, aJsonWriter.finishAndGetAsOString());
1253 mbIsDeckRequestedOpen = false;
1254 UpdateDeckOpenState();
1256 mpTabBar->RemoveDeckHighlight();
1259 void SidebarController::RequestOpenDeck()
1261 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1262 if ( pSplitWindow && !pSplitWindow->IsFadeIn() )
1263 // tdf#83546 Collapsed sidebar should expand first
1264 pSplitWindow->FadeIn();
1266 mbIsDeckRequestedOpen = true;
1267 UpdateDeckOpenState();
1270 bool SidebarController::IsDeckOpen(const sal_Int32 nIndex)
1272 if (nIndex >= 0)
1274 OUString asDeckId(mpTabBar->GetDeckIdForIndex(nIndex));
1275 return IsDeckVisible(asDeckId);
1277 return mbIsDeckOpen && *mbIsDeckOpen;
1280 bool SidebarController::IsDeckVisible(std::u16string_view rsDeckId)
1282 return mbIsDeckOpen && *mbIsDeckOpen && msCurrentDeckId == rsDeckId;
1285 void SidebarController::UpdateDeckOpenState()
1287 if ( ! mbIsDeckRequestedOpen)
1288 // No state requested.
1289 return;
1291 const sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
1293 // Update (change) the open state when it either has not yet been initialized
1294 // or when its value differs from the requested state.
1295 if ( mbIsDeckOpen && *mbIsDeckOpen == *mbIsDeckRequestedOpen )
1296 return;
1298 if (*mbIsDeckRequestedOpen)
1300 if (!mpParentWindow->IsFloatingMode())
1302 if (mnSavedSidebarWidth <= nTabBarDefaultWidth)
1303 SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow));
1304 else
1305 SetChildWindowWidth(mnSavedSidebarWidth);
1307 else
1309 // Show the Deck by resizing back to the original size (before hiding).
1310 Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel());
1311 Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel());
1313 aNewPos.setX(aNewPos.X() - mnSavedSidebarWidth + nTabBarDefaultWidth);
1314 aNewSize.setWidth(mnSavedSidebarWidth);
1316 mpParentWindow->GetFloatingWindow()->SetPosSizePixel(aNewPos, aNewSize);
1318 if (comphelper::LibreOfficeKit::isActive())
1320 // Sidebar wide enough to render the menu; enable it.
1321 mpTabBar->EnableMenuButton(true);
1323 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
1325 const std::string uno = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
1326 if (!uno.empty())
1327 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
1328 OString(uno + "=true"));
1333 else
1335 if ( ! mpParentWindow->IsFloatingMode())
1336 mnSavedSidebarWidth = SetChildWindowWidth(nTabBarDefaultWidth);
1337 else
1339 // Hide the Deck by resizing to the width of the TabBar.
1340 Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel());
1341 Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel());
1342 mnSavedSidebarWidth = aNewSize.Width(); // Save the current width to restore.
1344 aNewPos.setX(aNewPos.X() + mnSavedSidebarWidth - nTabBarDefaultWidth);
1345 if (comphelper::LibreOfficeKit::isActive())
1347 // Hide by collapsing, otherwise with 0x0 the client might expect
1348 // to get valid dimensions on rendering and not collapse the sidebar.
1349 aNewSize.setWidth(1);
1351 else
1352 aNewSize.setWidth(nTabBarDefaultWidth);
1354 mpParentWindow->GetFloatingWindow()->SetPosSizePixel(aNewPos, aNewSize);
1356 if (comphelper::LibreOfficeKit::isActive())
1358 // Sidebar too narrow to render the menu; disable it.
1359 mpTabBar->EnableMenuButton(false);
1361 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
1363 const std::string uno = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
1364 if (!uno.empty())
1365 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
1366 OString(uno + "=false"));
1371 if (mnWidthOnSplitterButtonDown > nTabBarDefaultWidth)
1372 mnSavedSidebarWidth = mnWidthOnSplitterButtonDown;
1373 mpParentWindow->SetStyle(mpParentWindow->GetStyle() & ~WB_SIZEABLE);
1376 NotifyResize();
1379 bool SidebarController::CanModifyChildWindowWidth()
1381 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1382 if (pSplitWindow == nullptr)
1383 return false;
1385 sal_uInt16 nRow (0xffff);
1386 sal_uInt16 nColumn (0xffff);
1387 if (pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow))
1389 sal_uInt16 nRowCount (pSplitWindow->GetWindowCount(nColumn));
1390 return nRowCount==1;
1392 else
1393 return false;
1396 sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth)
1398 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1399 if (pSplitWindow == nullptr)
1400 return 0;
1402 sal_uInt16 nRow (0xffff);
1403 sal_uInt16 nColumn (0xffff);
1404 pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow);
1405 const tools::Long nColumnWidth (pSplitWindow->GetLineSize(nColumn));
1407 vcl::Window* pWindow = mpParentWindow;
1408 const Size aWindowSize (pWindow->GetSizePixel());
1410 pSplitWindow->MoveWindow(
1411 mpParentWindow,
1412 Size(nNewWidth, aWindowSize.Height()),
1413 nColumn,
1414 nRow,
1415 false);
1416 static_cast<SplitWindow*>(pSplitWindow)->Split();
1418 return static_cast<sal_Int32>(nColumnWidth);
1421 void SidebarController::RestrictWidth (sal_Int32 nWidth)
1423 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1424 if (pSplitWindow != nullptr)
1426 const sal_uInt16 nId (pSplitWindow->GetItemId(mpParentWindow.get()));
1427 const sal_uInt16 nSetId (pSplitWindow->GetSet(nId));
1428 const sal_Int32 nRequestedWidth = TabBar::GetDefaultWidth() + nWidth;
1430 pSplitWindow->SetItemSizeRange(
1431 nSetId,
1432 Range(nRequestedWidth, std::max(nRequestedWidth, getMaximumWidth())));
1436 SfxSplitWindow* SidebarController::GetSplitWindow()
1438 if (mpParentWindow != nullptr)
1440 SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent());
1441 if (pSplitWindow != mpSplitWindow)
1443 if (mpSplitWindow != nullptr)
1444 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
1446 mpSplitWindow = pSplitWindow;
1448 if (mpSplitWindow != nullptr)
1449 mpSplitWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler));
1451 return mpSplitWindow;
1453 else
1454 return nullptr;
1457 void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag)
1459 if (mpParentWindow == nullptr)
1460 return;
1462 if (bCloseAfterDrag)
1464 // Make sure that the indicator exists.
1465 if (!mpCloseIndicator)
1466 mpCloseIndicator.reset(VclPtr<CloseIndicator>::Create(mpParentWindow));
1468 // Place and show the indicator.
1469 const Size aWindowSize (mpParentWindow->GetSizePixel());
1470 const Size aImageSize (mpCloseIndicator->GetSizePixel());
1471 mpCloseIndicator->SetPosPixel(
1472 Point(
1473 aWindowSize.Width() - TabBar::GetDefaultWidth() - aImageSize.Width(),
1474 (aWindowSize.Height() - aImageSize.Height())/2));
1475 mpCloseIndicator->Show();
1477 else
1479 // Hide but don't delete the indicator.
1480 if (mpCloseIndicator)
1481 mpCloseIndicator->Hide();
1485 void SidebarController::UpdateTitleBarIcons()
1487 if ( ! mpCurrentDeck)
1488 return;
1490 const bool bIsHighContrastModeActive (Theme::IsHighContrastMode());
1492 const ResourceManager& rResourceManager = *mpResourceManager;
1494 // Update the deck icon.
1495 std::shared_ptr<DeckDescriptor> xDeckDescriptor = rResourceManager.GetDeckDescriptor(mpCurrentDeck->GetId());
1496 if (xDeckDescriptor && mpCurrentDeck->GetTitleBar())
1498 const OUString sIconURL(
1499 bIsHighContrastModeActive
1500 ? xDeckDescriptor->msHighContrastTitleBarIconURL
1501 : xDeckDescriptor->msTitleBarIconURL);
1502 mpCurrentDeck->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
1505 // Update the panel icons.
1506 const SharedPanelContainer& rPanels (mpCurrentDeck->GetPanels());
1507 for (const auto& rxPanel : rPanels)
1509 if ( ! rxPanel)
1510 continue;
1511 if (!rxPanel->GetTitleBar())
1512 continue;
1513 std::shared_ptr<PanelDescriptor> xPanelDescriptor = rResourceManager.GetPanelDescriptor(rxPanel->GetId());
1514 if (!xPanelDescriptor)
1515 continue;
1516 const OUString sIconURL (
1517 bIsHighContrastModeActive
1518 ? xPanelDescriptor->msHighContrastTitleBarIconURL
1519 : xPanelDescriptor->msTitleBarIconURL);
1520 rxPanel->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
1524 void SidebarController::ShowPanel (const Panel& rPanel)
1526 if (mpCurrentDeck)
1528 if (!IsDeckOpen())
1529 RequestOpenDeck();
1530 mpCurrentDeck->ShowPanel(rPanel);
1534 ResourceManager::DeckContextDescriptorContainer SidebarController::GetMatchingDecks()
1536 ResourceManager::DeckContextDescriptorContainer aDecks;
1537 mpResourceManager->GetMatchingDecks (aDecks,
1538 GetCurrentContext(),
1539 IsDocumentReadOnly(),
1540 mxFrame->getController());
1541 return aDecks;
1544 ResourceManager::PanelContextDescriptorContainer SidebarController::GetMatchingPanels(std::u16string_view rDeckId)
1546 ResourceManager::PanelContextDescriptorContainer aPanels;
1548 mpResourceManager->GetMatchingPanels(aPanels,
1549 GetCurrentContext(),
1550 rDeckId,
1551 mxFrame->getController());
1552 return aPanels;
1555 void SidebarController::updateModel(const css::uno::Reference<css::frame::XModel>& xModel)
1557 mpResourceManager->UpdateModel(xModel);
1560 void SidebarController::FadeOut()
1562 if (mpSplitWindow)
1563 mpSplitWindow->FadeOut();
1566 void SidebarController::FadeIn()
1568 if (mpSplitWindow)
1569 mpSplitWindow->FadeIn();
1572 tools::Rectangle SidebarController::GetDeckDragArea() const
1574 tools::Rectangle aRect;
1575 if (mpCurrentDeck)
1577 if (DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar())
1579 aRect = pTitleBar->GetDragArea();
1582 return aRect;
1585 void SidebarController::frameAction(const css::frame::FrameActionEvent& rEvent)
1587 if (rEvent.Frame == mxFrame)
1589 if (rEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING)
1590 unregisterSidebarForFrame(mxFrame->getController());
1591 else if (rEvent.Action == css::frame::FrameAction_COMPONENT_REATTACHED)
1592 registerSidebarForFrame(mxFrame->getController());
1596 void SidebarController::saveDeckState()
1598 // Impress shutdown : context (frame) is disposed before sidebar disposing
1599 // calc writer : context (frame) is disposed after sidebar disposing
1600 // so need to test if GetCurrentContext is still valid regarding msApplication
1601 if (GetCurrentContext().msApplication != "none")
1603 mpResourceManager->SaveDecksSettings(GetCurrentContext());
1604 mpResourceManager->SaveLastActiveDeck(GetCurrentContext(), msCurrentDeckId);
1608 static bool isChartOrMathContext(const Context& context)
1610 return context.msApplication == "com.sun.star.chart2.ChartDocument"
1611 || context.msApplication == "com.sun.star.formula.FormulaProperties";
1614 bool SidebarController::hasChartOrMathContextCurrently() const
1616 if ((maRequestedContext != maCurrentContext) && isChartOrMathContext(maRequestedContext))
1617 return true; // We are not yet changed, but in the process
1619 return isChartOrMathContext(maCurrentContext);
1622 sfx2::sidebar::SidebarController* SidebarController::GetSidebarControllerForView(const SfxViewShell* pViewShell)
1624 if (!pViewShell)
1625 return nullptr;
1627 Reference<css::frame::XController2> xController(pViewShell->GetController(), UNO_QUERY);
1628 if (!xController.is())
1629 return nullptr;
1631 // Make sure there is a model behind the controller, otherwise getSidebar() can crash.
1632 if (!xController->getModel().is())
1633 return nullptr;
1635 Reference<css::ui::XSidebarProvider> xSidebarProvider = xController->getSidebar();
1636 if (!xSidebarProvider.is())
1637 return nullptr;
1639 Reference<css::ui::XSidebar> xSidebar = xSidebarProvider->getSidebar();
1640 if (!xSidebar.is())
1641 return nullptr;
1643 return dynamic_cast<sfx2::sidebar::SidebarController*>(xSidebar.get());
1646 } // end of namespace sfx2::sidebar
1648 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */