LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sfx2 / source / sidebar / SidebarController.cxx
blob3c1208d0da00f76eecbe526577ddf0824482d857
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/uitest/logger.hxx>
39 #include <vcl/uitest/eventdescription.hxx>
40 #include <vcl/svapp.hxx>
41 #include <splitwin.hxx>
42 #include <tools/diagnose_ex.h>
43 #include <tools/json_writer.hxx>
44 #include <tools/link.hxx>
45 #include <toolkit/helper/vclunohelper.hxx>
46 #include <comphelper/processfactory.hxx>
47 #include <comphelper/namedvaluecollection.hxx>
48 #include <comphelper/lok.hxx>
49 #include <sal/log.hxx>
50 #include <officecfg/Office/UI/Sidebar.hxx>
51 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
52 #include <boost/property_tree/ptree.hpp>
53 #include <boost/property_tree/json_parser.hpp>
55 #include <com/sun/star/awt/XWindowPeer.hpp>
56 #include <com/sun/star/frame/XDispatch.hpp>
57 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
58 #include <com/sun/star/ui/ContextChangeEventObject.hpp>
59 #include <com/sun/star/ui/theUIElementFactoryManager.hpp>
60 #include <com/sun/star/util/URL.hpp>
61 #include <com/sun/star/rendering/XSpriteCanvas.hpp>
63 #include <bitmaps.hlst>
65 using namespace css;
66 using namespace css::uno;
68 namespace
70 constexpr OUStringLiteral gsReadOnlyCommandName = u".uno:EditDoc";
71 const sal_Int32 gnWidthCloseThreshold (70);
72 const sal_Int32 gnWidthOpenThreshold (40);
74 std::string UnoNameFromDeckId(std::u16string_view rsDeckId, const sfx2::sidebar::Context& context)
76 if (rsDeckId == u"SdCustomAnimationDeck")
77 return ".uno:CustomAnimation";
79 if (rsDeckId == u"PropertyDeck")
80 return vcl::EnumContext::Application::Impress == vcl::EnumContext::GetApplicationEnum(context.msApplication) ? ".uno:ModifyPage" : ".uno:Sidebar";
82 if (rsDeckId == u"SdLayoutsDeck")
83 return ".uno:ModifyPage";
85 if (rsDeckId == u"SdSlideTransitionDeck")
86 return ".uno:SlideChangeWindow";
88 if (rsDeckId == u"SdAllMasterPagesDeck")
89 return ".uno:MasterSlidesPanel";
91 if (rsDeckId == u"SdMasterPagesDeck")
92 return ".uno:MasterSlidesPanel";
94 if (rsDeckId == u"GalleryDeck")
95 return ".uno:Gallery";
97 OString sUno = ".uno:SidebarDeck." + OUStringToOString(rsDeckId, RTL_TEXTENCODING_ASCII_US);
98 return std::string(sUno);
102 namespace sfx2::sidebar {
104 namespace {
106 /** When in doubt, show this deck.
108 constexpr OUStringLiteral gsDefaultDeckId(u"PropertyDeck");
111 SidebarController::SidebarController (
112 SidebarDockingWindow* pParentWindow,
113 const SfxViewFrame* pViewFrame)
114 : SidebarControllerInterfaceBase(m_aMutex),
115 mpParentWindow(pParentWindow),
116 mpViewFrame(pViewFrame),
117 mxFrame(pViewFrame->GetFrame().GetFrameInterface()),
118 mpTabBar(VclPtr<TabBar>::Create(
119 mpParentWindow,
120 mxFrame,
121 [this](const OUString& rsDeckId) { return this->OpenThenToggleDeck(rsDeckId); },
122 [this](weld::Menu& rMainMenu, weld::Menu& rSubMenu,
123 const ::std::vector<TabBar::DeckMenuData>& rMenuData) { return this->ShowPopupMenu(rMainMenu, rSubMenu, rMenuData); },
124 this)),
125 maCurrentContext(OUString(), OUString()),
126 maRequestedContext(OUString(), OUString()),
127 mnRequestedForceFlags(SwitchFlag_NoForce),
128 mbMinimumSidebarWidth(officecfg::Office::UI::Sidebar::General::MinimumWidth::get()),
129 msCurrentDeckId(gsDefaultDeckId),
130 maPropertyChangeForwarder([this](){ return this->BroadcastPropertyChange(); }),
131 maContextChangeUpdate([this](){ return this->UpdateConfigurations(); }),
132 mbFloatingDeckClosed(!pParentWindow->IsFloatingMode()),
133 mnSavedSidebarWidth(pParentWindow->GetSizePixel().Width()),
134 maFocusManager([this](const Panel& rPanel){ return this->ShowPanel(rPanel); }),
135 mbIsDocumentReadOnly(false),
136 mpSplitWindow(nullptr),
137 mnWidthOnSplitterButtonDown(0)
139 mnMaximumSidebarWidth = officecfg::Office::UI::Sidebar::General::MaximumWidth::get() * mpTabBar->GetDPIScaleFactor();
140 // Decks and panel collections for this sidebar
141 mpResourceManager = std::make_unique<ResourceManager>();
144 rtl::Reference<SidebarController> SidebarController::create(SidebarDockingWindow* pParentWindow,
145 const SfxViewFrame* pViewFrame)
147 rtl::Reference<SidebarController> instance(new SidebarController(pParentWindow, pViewFrame));
149 const css::uno::Reference<css::frame::XFrame>& rxFrame = pViewFrame->GetFrame().GetFrameInterface();
150 instance->registerSidebarForFrame(rxFrame->getController());
151 rxFrame->addFrameActionListener(instance);
152 // Listen for window events.
153 instance->mpParentWindow->AddEventListener(LINK(instance.get(), SidebarController, WindowEventHandler));
155 // Listen for theme property changes.
156 instance->mxThemePropertySet = Theme::GetPropertySet();
157 instance->mxThemePropertySet->addPropertyChangeListener(
159 static_cast<css::beans::XPropertyChangeListener*>(instance.get()));
161 // Get the dispatch object as preparation to listen for changes of
162 // the read-only state.
163 const util::URL aURL (Tools::GetURL(gsReadOnlyCommandName));
164 instance->mxReadOnlyModeDispatch = Tools::GetDispatch(rxFrame, aURL);
165 if (instance->mxReadOnlyModeDispatch.is())
166 instance->mxReadOnlyModeDispatch->addStatusListener(instance, aURL);
168 //first UpdateConfigurations call will SwitchToDeck
170 return instance;
173 SidebarController::~SidebarController()
177 SidebarController* SidebarController::GetSidebarControllerForFrame (
178 const css::uno::Reference<css::frame::XFrame>& rxFrame)
180 uno::Reference<frame::XController> const xController(rxFrame->getController());
181 if (!xController.is()) // this may happen during dispose of Draw controller but perhaps it's a bug
183 SAL_WARN("sfx.sidebar", "GetSidebarControllerForFrame: frame has no XController");
184 return nullptr;
186 uno::Reference<ui::XContextChangeEventListener> const xListener(
187 framework::GetFirstListenerWith(
188 ::comphelper::getProcessComponentContext(),
189 xController,
190 [] (uno::Reference<uno::XInterface> const& xRef)
191 { return nullptr != dynamic_cast<SidebarController*>(xRef.get()); }
194 return dynamic_cast<SidebarController*>(xListener.get());
197 void SidebarController::registerSidebarForFrame(const css::uno::Reference<css::frame::XController>& xController)
199 // Listen for context change events.
200 css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
201 css::ui::ContextChangeEventMultiplexer::get(
202 ::comphelper::getProcessComponentContext()));
203 xMultiplexer->addContextChangeEventListener(
204 static_cast<css::ui::XContextChangeEventListener*>(this),
205 xController);
208 void SidebarController::unregisterSidebarForFrame(const css::uno::Reference<css::frame::XController>& xController)
210 saveDeckState();
211 disposeDecks();
213 css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
214 css::ui::ContextChangeEventMultiplexer::get(
215 ::comphelper::getProcessComponentContext()));
216 xMultiplexer->removeContextChangeEventListener(
217 static_cast<css::ui::XContextChangeEventListener*>(this),
218 xController);
221 void SidebarController::disposeDecks()
223 SolarMutexGuard aSolarMutexGuard;
225 if (comphelper::LibreOfficeKit::isActive())
227 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
229 const std::string hide = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
230 if (!hide.empty())
231 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
232 (hide + "=false").c_str());
235 if (mpParentWindow)
236 mpParentWindow->ReleaseLOKNotifier();
239 mpCurrentDeck.clear();
240 maFocusManager.Clear();
241 mpResourceManager->disposeDecks();
244 namespace
246 class CloseIndicator final : public InterimItemWindow
248 public:
249 CloseIndicator(vcl::Window* pParent)
250 : InterimItemWindow(pParent, "svt/ui/fixedimagecontrol.ui", "FixedImageControl")
251 , m_xWidget(m_xBuilder->weld_image("image"))
253 InitControlBase(m_xWidget.get());
255 m_xWidget->set_from_icon_name(SIDEBAR_CLOSE_INDICATOR);
257 SetSizePixel(get_preferred_size());
259 SetBackground(Theme::GetColor(Theme::Color_DeckBackground));
262 virtual ~CloseIndicator() override
264 disposeOnce();
267 virtual void dispose() override
269 m_xWidget.reset();
270 InterimItemWindow::dispose();
273 private:
274 std::unique_ptr<weld::Image> m_xWidget;
278 void SAL_CALL SidebarController::disposing()
280 SolarMutexGuard aSolarMutexGuard;
282 mpCloseIndicator.disposeAndClear();
284 maFocusManager.Clear();
285 mpTabBar.disposeAndClear();
287 saveDeckState();
289 // clear decks
290 ResourceManager::DeckContextDescriptorContainer aDecks;
292 mpResourceManager->GetMatchingDecks (
293 aDecks,
294 GetCurrentContext(),
295 IsDocumentReadOnly(),
296 mxFrame->getController());
298 for (const auto& rDeck : aDecks)
300 std::shared_ptr<DeckDescriptor> deckDesc = mpResourceManager->GetDeckDescriptor(rDeck.msId);
302 VclPtr<Deck> aDeck = deckDesc->mpDeck;
303 if (aDeck)
304 aDeck.disposeAndClear();
307 maContextChangeUpdate.CancelRequest();
309 if (mxReadOnlyModeDispatch.is())
310 mxReadOnlyModeDispatch->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName));
312 if (mxThemePropertySet.is())
313 mxThemePropertySet->removePropertyChangeListener(
315 static_cast<css::beans::XPropertyChangeListener*>(this));
317 if (mpParentWindow != nullptr)
319 mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
320 mpParentWindow = nullptr;
323 if (mpSplitWindow != nullptr)
325 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
326 mpSplitWindow = nullptr;
329 mxFrame->removeFrameActionListener(this);
331 uno::Reference<css::frame::XController> xController = mxFrame->getController();
332 if (!xController.is())
333 xController = mxCurrentController;
335 unregisterSidebarForFrame(xController);
338 void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent)
340 SolarMutexGuard aSolarMutexGuard;
342 // Update to the requested new context asynchronously to avoid
343 // subtle errors caused by SFX2 which in rare cases can not
344 // properly handle a synchronous update.
346 maRequestedContext = Context(
347 rEvent.ApplicationName,
348 rEvent.ContextName);
350 if (maRequestedContext != maCurrentContext)
352 mxCurrentController.set(rEvent.Source, css::uno::UNO_QUERY);
353 maContextChangeUpdate.RequestCall(); // async call, not a prob
354 // calling with held
355 // solarmutex
356 // TODO: this call is redundant but mandatory for unit test to update context on document loading
357 if (!comphelper::LibreOfficeKit::isActive())
358 UpdateConfigurations();
362 void SAL_CALL SidebarController::disposing (const css::lang::EventObject& )
364 SolarMutexGuard aSolarMutexGuard;
366 dispose();
369 void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& )
371 SolarMutexGuard aSolarMutexGuard;
373 maPropertyChangeForwarder.RequestCall(); // async call, not a prob
374 // to call with held
375 // solarmutex
378 void SAL_CALL SidebarController::statusChanged (const css::frame::FeatureStateEvent& rEvent)
380 SolarMutexGuard aSolarMutexGuard;
382 bool bIsReadWrite (true);
383 if (rEvent.IsEnabled)
384 rEvent.State >>= bIsReadWrite;
386 if (mbIsDocumentReadOnly != !bIsReadWrite)
388 mbIsDocumentReadOnly = !bIsReadWrite;
390 // Force the current deck to update its panel list.
391 if ( ! mbIsDocumentReadOnly)
392 SwitchToDefaultDeck();
394 mnRequestedForceFlags |= SwitchFlag_ForceSwitch;
395 maContextChangeUpdate.RequestCall(); // async call, ok to call
396 // with held solarmutex
400 void SAL_CALL SidebarController::requestLayout()
402 SolarMutexGuard aSolarMutexGuard;
404 sal_Int32 nMinimalWidth = 0;
405 if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
407 mpCurrentDeck->RequestLayout();
408 nMinimalWidth = mbMinimumSidebarWidth ? mpCurrentDeck->GetMinimalWidth() : 0;
410 RestrictWidth(nMinimalWidth);
413 void SidebarController::BroadcastPropertyChange()
415 mpParentWindow->Invalidate(InvalidateFlags::Children);
418 void SidebarController::NotifyResize()
420 if (!mpTabBar)
422 OSL_ASSERT(mpTabBar!=nullptr);
423 return;
426 const sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
428 const sal_Int32 nWidth(mpParentWindow->GetSizePixel().Width());
429 const sal_Int32 nHeight(mpParentWindow->GetSizePixel().Height());
431 mbIsDeckOpen = (nWidth > nTabBarDefaultWidth);
433 if (mnSavedSidebarWidth <= 0)
434 mnSavedSidebarWidth = nWidth;
436 bool bIsDeckVisible;
437 const bool bIsOpening (nWidth > mnWidthOnSplitterButtonDown);
438 if (bIsOpening)
439 bIsDeckVisible = nWidth >= nTabBarDefaultWidth + gnWidthOpenThreshold;
440 else
441 bIsDeckVisible = nWidth >= nTabBarDefaultWidth + gnWidthCloseThreshold;
442 mbIsDeckRequestedOpen = bIsDeckVisible;
443 UpdateCloseIndicator(!bIsDeckVisible);
445 if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
447 SfxSplitWindow* pSplitWindow = GetSplitWindow();
448 WindowAlign eAlign = pSplitWindow ? pSplitWindow->GetAlign() : WindowAlign::Right;
449 tools::Long nDeckX, nTabX;
450 if (eAlign == WindowAlign::Left) // attach the Sidebar towards the left-side of screen
452 nDeckX = nTabBarDefaultWidth;
453 nTabX = 0;
455 else // attach the Sidebar towards the right-side of screen
457 nDeckX = 0;
458 nTabX = nWidth - nTabBarDefaultWidth;
461 // Place the deck first.
462 if (bIsDeckVisible)
464 if (comphelper::LibreOfficeKit::isActive())
466 // We want to let the layouter use up as much of the
467 // height as necessary to make sure no scrollbar is
468 // visible. This only works when there are no greedy
469 // panes that fill up all available area. So we only
470 // use this for the PropertyDeck, which has no such
471 // panes, while most other do. This is fine, since
472 // it's the PropertyDeck that really has many panes
473 // that can collapse or expand. For others, limit
474 // the height to something sensible.
475 const sal_Int32 nExtHeight = (msCurrentDeckId == "PropertyDeck" ? 2000 : 600);
476 // No TabBar in LOK (use nWidth in full).
477 mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth, nExtHeight);
479 else
480 mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth - nTabBarDefaultWidth, nHeight);
481 mpCurrentDeck->Show();
482 mpCurrentDeck->RequestLayout();
484 else
485 mpCurrentDeck->Hide();
487 // Now place the tab bar.
488 mpTabBar->setPosSizePixel(nTabX, 0, nTabBarDefaultWidth, nHeight);
489 if (!comphelper::LibreOfficeKit::isActive())
490 mpTabBar->Show(); // Don't show TabBar in LOK.
493 // Determine if the closer of the deck can be shown.
494 sal_Int32 nMinimalWidth = 0;
495 if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
497 DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar();
498 if (pTitleBar && pTitleBar->GetVisible())
499 pTitleBar->SetCloserVisible(CanModifyChildWindowWidth());
500 nMinimalWidth = mbMinimumSidebarWidth ? mpCurrentDeck->GetMinimalWidth() : 0;
503 RestrictWidth(nMinimalWidth);
506 void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth)
508 if ( ! mbIsDeckRequestedOpen)
509 return;
511 if (*mbIsDeckRequestedOpen)
513 // Deck became large enough to be shown. Show it.
514 mnSavedSidebarWidth = nNewWidth;
515 if (!*mbIsDeckOpen)
516 RequestOpenDeck();
518 else
520 // Deck became too small. Close it completely.
521 // If window is wider than the tab bar then mark the deck as being visible, even when it is not.
522 // This is to trigger an adjustment of the width to the width of the tab bar.
523 mbIsDeckOpen = true;
524 RequestCloseDeck();
526 if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth())
527 mnSavedSidebarWidth = mnWidthOnSplitterButtonDown;
531 void SidebarController::SyncUpdate()
533 maPropertyChangeForwarder.Sync();
534 maContextChangeUpdate.Sync();
537 void SidebarController::UpdateConfigurations()
539 if (maCurrentContext == maRequestedContext
540 && mnRequestedForceFlags == SwitchFlag_NoForce)
541 return;
543 if ((maCurrentContext.msApplication != "none") &&
544 !maCurrentContext.msApplication.isEmpty())
546 mpResourceManager->SaveDecksSettings(maCurrentContext);
547 mpResourceManager->SetLastActiveDeck(maCurrentContext, msCurrentDeckId);
550 // get last active deck for this application on first update
551 if (!maRequestedContext.msApplication.isEmpty() &&
552 (maCurrentContext.msApplication != maRequestedContext.msApplication))
554 OUString sLastActiveDeck = mpResourceManager->GetLastActiveDeck( maRequestedContext );
555 if (!sLastActiveDeck.isEmpty())
556 msCurrentDeckId = sLastActiveDeck;
559 maCurrentContext = maRequestedContext;
561 mpResourceManager->InitDeckContext(GetCurrentContext());
563 // Find the set of decks that could be displayed for the new context.
564 ResourceManager::DeckContextDescriptorContainer aDecks;
566 css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
568 mpResourceManager->GetMatchingDecks (
569 aDecks,
570 maCurrentContext,
571 mbIsDocumentReadOnly,
572 xController);
574 maFocusManager.Clear();
576 // Notify the tab bar about the updated set of decks.
577 mpTabBar->SetDecks(aDecks);
579 // Find the new deck. By default that is the same as the old
580 // one. If that is not set or not enabled, then choose the
581 // first enabled deck (which is PropertyDeck).
582 OUString sNewDeckId;
583 for (const auto& rDeck : aDecks)
585 if (rDeck.mbIsEnabled)
587 if (rDeck.msId == msCurrentDeckId)
589 sNewDeckId = msCurrentDeckId;
590 break;
592 else if (sNewDeckId.getLength() == 0)
593 sNewDeckId = rDeck.msId;
597 if (sNewDeckId.getLength() == 0)
599 // We did not find a valid deck.
600 RequestCloseDeck();
601 return;
604 // Tell the tab bar to highlight the button associated
605 // with the deck.
606 mpTabBar->HighlightDeck(sNewDeckId);
608 std::shared_ptr<DeckDescriptor> xDescriptor = mpResourceManager->GetDeckDescriptor(sNewDeckId);
610 if (xDescriptor)
612 SwitchToDeck(*xDescriptor, maCurrentContext);
616 namespace {
618 void collectUIInformation(const OUString& rDeckId)
620 EventDescription aDescription;
621 aDescription.aAction = "SIDEBAR";
622 aDescription.aParent = "MainWindow";
623 aDescription.aParameters = {{"PANEL", rDeckId}};
624 aDescription.aKeyWord = "CurrentApp";
626 UITestLogger::getInstance().logEvent(aDescription);
631 void SidebarController::OpenThenToggleDeck (
632 const OUString& rsDeckId)
634 SfxSplitWindow* pSplitWindow = GetSplitWindow();
635 if ( pSplitWindow && !pSplitWindow->IsFadeIn() )
636 // tdf#83546 Collapsed sidebar should expand first
637 pSplitWindow->FadeIn();
638 else if ( IsDeckVisible( rsDeckId ) )
640 if( !WasFloatingDeckClosed() )
642 // tdf#88241 Summoning an undocked sidebar a second time should close sidebar
643 mpParentWindow->Close();
644 return;
646 else
648 // tdf#67627 Clicking a second time on a Deck icon will close the Deck
649 RequestCloseDeck();
650 return;
653 RequestOpenDeck();
654 // before SwitchToDeck which may cause the rsDeckId string to be released
655 collectUIInformation(rsDeckId);
656 SwitchToDeck(rsDeckId);
658 // Make sure the sidebar is wide enough to fit the requested content
659 if (mpCurrentDeck && mpTabBar)
661 sal_Int32 nRequestedWidth = mpCurrentDeck->GetMinimalWidth() + TabBar::GetDefaultWidth();
662 if (mnSavedSidebarWidth < nRequestedWidth)
663 SetChildWindowWidth(nRequestedWidth);
667 void SidebarController::OpenThenSwitchToDeck (
668 std::u16string_view rsDeckId)
670 RequestOpenDeck();
671 SwitchToDeck(rsDeckId);
675 void SidebarController::SwitchToDefaultDeck()
677 SwitchToDeck(gsDefaultDeckId);
680 void SidebarController::SwitchToDeck (
681 std::u16string_view rsDeckId)
683 if ( msCurrentDeckId != rsDeckId
684 || ! mbIsDeckOpen
685 || mnRequestedForceFlags!=SwitchFlag_NoForce)
687 std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rsDeckId);
689 if (xDeckDescriptor)
690 SwitchToDeck(*xDeckDescriptor, maCurrentContext);
694 void SidebarController::CreateDeck(std::u16string_view rDeckId) {
695 CreateDeck(rDeckId, maCurrentContext);
698 void SidebarController::CreateDeck(std::u16string_view rDeckId, const Context& rContext, bool bForceCreate)
700 std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rDeckId);
702 if (!xDeckDescriptor)
703 return;
705 VclPtr<Deck> aDeck = xDeckDescriptor->mpDeck;
706 if (!aDeck || bForceCreate)
708 if (aDeck)
709 aDeck.disposeAndClear();
711 aDeck = VclPtr<Deck>::Create(
712 *xDeckDescriptor,
713 mpParentWindow,
714 [this]() { return this->RequestCloseDeck(); });
716 xDeckDescriptor->mpDeck = aDeck;
717 CreatePanels(rDeckId, rContext);
720 void SidebarController::CreatePanels(std::u16string_view rDeckId, const Context& rContext)
722 std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rDeckId);
724 // init panels bounded to that deck, do not wait them being displayed as may be accessed through API
726 VclPtr<Deck> pDeck = xDeckDescriptor->mpDeck;
728 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors;
730 css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
732 mpResourceManager->GetMatchingPanels(
733 aPanelContextDescriptors,
734 rContext,
735 rDeckId,
736 xController);
738 // Update the panel list.
739 const sal_Int32 nNewPanelCount (aPanelContextDescriptors.size());
740 SharedPanelContainer aNewPanels;
741 sal_Int32 nWriteIndex (0);
743 aNewPanels.resize(nNewPanelCount);
745 for (sal_Int32 nReadIndex=0; nReadIndex<nNewPanelCount; ++nReadIndex)
747 const ResourceManager::PanelContextDescriptor& rPanelContexDescriptor (
748 aPanelContextDescriptors[nReadIndex]);
750 // Determine if the panel can be displayed.
751 const bool bIsPanelVisible (!mbIsDocumentReadOnly || rPanelContexDescriptor.mbShowForReadOnlyDocuments);
752 if ( ! bIsPanelVisible)
753 continue;
755 auto xOldPanel(pDeck->GetPanel(rPanelContexDescriptor.msId));
756 if (xOldPanel)
758 xOldPanel->SetLurkMode(false);
759 aNewPanels[nWriteIndex] = xOldPanel;
760 xOldPanel->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible);
761 ++nWriteIndex;
763 else
765 auto aPanel = CreatePanel(rPanelContexDescriptor.msId,
766 pDeck->GetPanelParentWindow(),
767 rPanelContexDescriptor.mbIsInitiallyVisible,
768 rContext,
769 pDeck);
770 if (aPanel)
772 aNewPanels[nWriteIndex] = std::move(aPanel);
774 // Depending on the context we have to change the command
775 // for the "more options" dialog.
776 PanelTitleBar* pTitleBar = aNewPanels[nWriteIndex]->GetTitleBar();
777 if (pTitleBar)
779 pTitleBar->SetMoreOptionsCommand(
780 rPanelContexDescriptor.msMenuCommand,
781 mxFrame, xController);
783 ++nWriteIndex;
788 // mpCurrentPanels - may miss stuff (?)
789 aNewPanels.resize(nWriteIndex);
790 pDeck->ResetPanels(std::move(aNewPanels));
793 void SidebarController::SwitchToDeck (
794 const DeckDescriptor& rDeckDescriptor,
795 const Context& rContext)
797 if (comphelper::LibreOfficeKit::isActive())
799 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
801 if (msCurrentDeckId != rDeckDescriptor.msId)
803 const std::string hide = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
804 if (!hide.empty())
805 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
806 (hide + "=false").c_str());
809 const std::string show = UnoNameFromDeckId(rDeckDescriptor.msId, GetCurrentContext());
810 if (!show.empty())
811 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
812 (show + "=true").c_str());
816 maFocusManager.Clear();
818 const bool bForceNewDeck ((mnRequestedForceFlags&SwitchFlag_ForceNewDeck)!=0);
819 const bool bForceNewPanels ((mnRequestedForceFlags&SwitchFlag_ForceNewPanels)!=0);
820 mnRequestedForceFlags = SwitchFlag_NoForce;
822 if ( msCurrentDeckId != rDeckDescriptor.msId
823 || bForceNewDeck)
825 if (mpCurrentDeck)
826 mpCurrentDeck->Hide();
828 msCurrentDeckId = rDeckDescriptor.msId;
831 mpTabBar->HighlightDeck(msCurrentDeckId);
833 // Determine the panels to display in the deck.
834 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors;
836 css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
838 mpResourceManager->GetMatchingPanels(
839 aPanelContextDescriptors,
840 rContext,
841 rDeckDescriptor.msId,
842 xController);
844 if (aPanelContextDescriptors.empty())
846 // There are no panels to be displayed in the current context.
847 if (vcl::EnumContext::GetContextEnum(rContext.msContext) != vcl::EnumContext::Context::Empty)
849 // Switch to the "empty" context and try again.
850 SwitchToDeck(
851 rDeckDescriptor,
852 Context(
853 rContext.msApplication,
854 vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Empty)));
855 return;
857 else
859 // This is already the "empty" context. Looks like we have
860 // to live with an empty deck.
864 // Provide a configuration and Deck object.
866 CreateDeck(rDeckDescriptor.msId, rContext, bForceNewDeck);
868 if (bForceNewPanels && !bForceNewDeck) // already forced if bForceNewDeck
869 CreatePanels(rDeckDescriptor.msId, rContext);
871 if (mpCurrentDeck && mpCurrentDeck != rDeckDescriptor.mpDeck)
872 mpCurrentDeck->Hide();
873 mpCurrentDeck.reset(rDeckDescriptor.mpDeck);
875 if ( ! mpCurrentDeck)
876 return;
878 #ifdef DEBUG
879 // Show the context name in the deck title bar.
880 DeckTitleBar* pDebugTitleBar = mpCurrentDeck->GetTitleBar();
881 if (pDebugTitleBar)
882 pDebugTitleBar->SetTitle(rDeckDescriptor.msTitle + " (" + maCurrentContext.msContext + ")");
883 #endif
885 SfxSplitWindow* pSplitWindow = GetSplitWindow();
886 sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
887 WindowAlign eAlign = pSplitWindow ? pSplitWindow->GetAlign() : WindowAlign::Right;
888 tools::Long nDeckX;
889 if (eAlign == WindowAlign::Left) // attach the Sidebar towards the left-side of screen
891 nDeckX = nTabBarDefaultWidth;
893 else // attach the Sidebar towards the right-side of screen
895 nDeckX = 0;
898 // Activate the deck and the new set of panels.
899 mpCurrentDeck->setPosSizePixel(
900 nDeckX,
902 mpParentWindow->GetSizePixel().Width() - nTabBarDefaultWidth,
903 mpParentWindow->GetSizePixel().Height());
905 mpCurrentDeck->Show();
907 mpParentWindow->SetText(rDeckDescriptor.msTitle);
909 NotifyResize();
911 // Tell the focus manager about the new panels and tab bar
912 // buttons.
913 maFocusManager.SetDeck(mpCurrentDeck);
914 maFocusManager.SetPanels(mpCurrentDeck->GetPanels());
916 mpTabBar->UpdateFocusManager(maFocusManager);
917 UpdateTitleBarIcons();
920 void SidebarController::notifyDeckTitle(std::u16string_view targetDeckId)
922 if (msCurrentDeckId == targetDeckId)
924 maFocusManager.SetDeck(mpCurrentDeck);
925 mpTabBar->UpdateFocusManager(maFocusManager);
926 UpdateTitleBarIcons();
930 std::shared_ptr<Panel> SidebarController::CreatePanel (
931 std::u16string_view rsPanelId,
932 weld::Widget* pParentWindow,
933 const bool bIsInitiallyExpanded,
934 const Context& rContext,
935 const VclPtr<Deck>& pDeck)
937 std::shared_ptr<PanelDescriptor> xPanelDescriptor = mpResourceManager->GetPanelDescriptor(rsPanelId);
939 if (!xPanelDescriptor)
940 return nullptr;
942 // Create the panel which is the parent window of the UIElement.
943 auto xPanel = std::make_shared<Panel>(
944 *xPanelDescriptor,
945 pParentWindow,
946 bIsInitiallyExpanded,
947 pDeck,
948 [this]() { return this->GetCurrentContext(); },
949 mxFrame);
951 // Create the XUIElement.
952 Reference<ui::XUIElement> xUIElement (CreateUIElement(
953 xPanel->GetElementParentWindow(),
954 xPanelDescriptor->msImplementationURL,
955 xPanelDescriptor->mbWantsCanvas,
956 rContext));
957 if (xUIElement.is())
959 // Initialize the panel and add it to the active deck.
960 xPanel->SetUIElement(xUIElement);
962 else
964 xPanel.reset();
967 return xPanel;
970 Reference<ui::XUIElement> SidebarController::CreateUIElement (
971 const Reference<awt::XWindow>& rxWindow,
972 const OUString& rsImplementationURL,
973 const bool bWantsCanvas,
974 const Context& rContext)
978 const Reference<XComponentContext> xComponentContext (::comphelper::getProcessComponentContext() );
979 const Reference<ui::XUIElementFactory> xUIElementFactory =
980 ui::theUIElementFactoryManager::get( xComponentContext );
982 // Create the XUIElement.
983 ::comphelper::NamedValueCollection aCreationArguments;
984 aCreationArguments.put("Frame", makeAny(mxFrame));
985 aCreationArguments.put("ParentWindow", makeAny(rxWindow));
986 SidebarDockingWindow* pSfxDockingWindow = mpParentWindow.get();
987 if (pSfxDockingWindow != nullptr)
988 aCreationArguments.put("SfxBindings", makeAny(reinterpret_cast<sal_uInt64>(&pSfxDockingWindow->GetBindings())));
989 aCreationArguments.put("Theme", Theme::GetPropertySet());
990 aCreationArguments.put("Sidebar", makeAny(Reference<ui::XSidebar>(static_cast<ui::XSidebar*>(this))));
991 if (bWantsCanvas)
993 Reference<rendering::XSpriteCanvas> xCanvas (VCLUnoHelper::GetWindow(rxWindow)->GetOutDev()->GetSpriteCanvas());
994 aCreationArguments.put("Canvas", makeAny(xCanvas));
997 if (mxCurrentController.is())
999 OUString aModule = Tools::GetModuleName(mxCurrentController);
1000 if (!aModule.isEmpty())
1002 aCreationArguments.put("Module", makeAny(aModule));
1004 aCreationArguments.put("Controller", makeAny(mxCurrentController));
1007 aCreationArguments.put("ApplicationName", makeAny(rContext.msApplication));
1008 aCreationArguments.put("ContextName", makeAny(rContext.msContext));
1010 Reference<ui::XUIElement> xUIElement(
1011 xUIElementFactory->createUIElement(
1012 rsImplementationURL,
1013 aCreationArguments.getPropertyValues()),
1014 UNO_SET_THROW);
1016 return xUIElement;
1018 catch(const Exception&)
1020 TOOLS_WARN_EXCEPTION("sfx.sidebar", "Cannot create panel " << rsImplementationURL);
1021 return nullptr;
1025 IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent&, rEvent, void)
1027 if (rEvent.GetWindow() == mpParentWindow)
1029 switch (rEvent.GetId())
1031 case VclEventId::WindowShow:
1032 case VclEventId::WindowResize:
1033 NotifyResize();
1034 break;
1036 case VclEventId::WindowDataChanged:
1037 // Force an update of deck and tab bar to reflect
1038 // changes in theme (high contrast mode).
1039 Theme::HandleDataChange();
1040 UpdateTitleBarIcons();
1041 mpParentWindow->Invalidate();
1042 mnRequestedForceFlags |= SwitchFlag_ForceNewDeck | SwitchFlag_ForceNewPanels;
1043 maContextChangeUpdate.RequestCall();
1044 break;
1046 case VclEventId::ObjectDying:
1047 dispose();
1048 break;
1050 case VclEventId::WindowPaint:
1051 SAL_INFO("sfx.sidebar", "Paint");
1052 break;
1054 default:
1055 break;
1058 else if (rEvent.GetWindow()==mpSplitWindow && mpSplitWindow!=nullptr)
1060 switch (rEvent.GetId())
1062 case VclEventId::WindowMouseButtonDown:
1063 mnWidthOnSplitterButtonDown = mpParentWindow->GetSizePixel().Width();
1064 break;
1066 case VclEventId::WindowMouseButtonUp:
1068 ProcessNewWidth(mpParentWindow->GetSizePixel().Width());
1069 mnWidthOnSplitterButtonDown = 0;
1070 break;
1073 case VclEventId::ObjectDying:
1074 dispose();
1075 break;
1077 default: break;
1082 void SidebarController::ShowPopupMenu(
1083 weld::Menu& rMainMenu, weld::Menu& rSubMenu,
1084 const ::std::vector<TabBar::DeckMenuData>& rMenuData) const
1086 PopulatePopupMenus(rMainMenu, rSubMenu, rMenuData);
1087 rMainMenu.connect_activate(LINK(const_cast<SidebarController*>(this), SidebarController, OnMenuItemSelected));
1088 rSubMenu.connect_activate(LINK(const_cast<SidebarController*>(this), SidebarController, OnSubMenuItemSelected));
1091 void SidebarController::PopulatePopupMenus(weld::Menu& rMenu, weld::Menu& rCustomizationMenu,
1092 const std::vector<TabBar::DeckMenuData>& rMenuData) const
1094 // Add one entry for every tool panel element to individually make
1095 // them visible or hide them.
1096 sal_Int32 nIndex (0);
1097 for (const auto& rItem : rMenuData)
1099 OString sIdent("select" + OString::number(nIndex));
1100 rMenu.insert(nIndex, OUString::fromUtf8(sIdent), rItem.msDisplayName,
1101 nullptr, nullptr, nullptr, TRISTATE_FALSE);
1102 rMenu.set_active(sIdent, rItem.mbIsCurrentDeck);
1103 rMenu.set_sensitive(sIdent, rItem.mbIsEnabled && rItem.mbIsActive);
1105 if (!comphelper::LibreOfficeKit::isActive())
1107 if (rItem.mbIsCurrentDeck)
1109 // Don't allow the currently visible deck to be disabled.
1110 OString sSubIdent("nocustomize" + OString::number(nIndex));
1111 rCustomizationMenu.insert(nIndex, OUString::fromUtf8(sSubIdent), rItem.msDisplayName,
1112 nullptr, nullptr, nullptr, TRISTATE_FALSE);
1113 rCustomizationMenu.set_active(sSubIdent, true);
1115 else
1117 OString sSubIdent("customize" + OString::number(nIndex));
1118 rCustomizationMenu.insert(nIndex, OUString::fromUtf8(sSubIdent), rItem.msDisplayName,
1119 nullptr, nullptr, nullptr, TRISTATE_TRUE);
1120 rCustomizationMenu.set_active(sSubIdent, rItem.mbIsEnabled && rItem.mbIsActive);
1124 ++nIndex;
1127 bool bHideLock = true;
1128 bool bHideUnLock = true;
1129 // LOK doesn't support docked/undocked; Sidebar is floating but rendered docked in browser.
1130 if (!comphelper::LibreOfficeKit::isActive())
1132 // Add entry for docking or un-docking the tool panel.
1133 if (mpParentWindow->IsFloatingMode())
1134 bHideLock = false;
1135 else
1136 bHideUnLock = false;
1138 rMenu.set_visible("locktaskpanel", !bHideLock);
1139 rMenu.set_visible("unlocktaskpanel", !bHideUnLock);
1141 // No Restore or Customize options for LoKit.
1142 rMenu.set_visible("customization", !comphelper::LibreOfficeKit::isActive());
1145 IMPL_LINK(SidebarController, OnMenuItemSelected, const OString&, rCurItemId, void)
1147 if (rCurItemId == "unlocktaskpanel")
1149 mpParentWindow->SetFloatingMode(true);
1150 if (mpParentWindow->IsFloatingMode())
1151 mpParentWindow->ToTop(ToTopFlags::GrabFocusOnly);
1153 else if (rCurItemId == "locktaskpanel")
1155 mpParentWindow->SetFloatingMode(false);
1157 else if (rCurItemId == "hidesidebar")
1159 if (!comphelper::LibreOfficeKit::isActive())
1161 const util::URL aURL(Tools::GetURL(".uno:Sidebar"));
1162 Reference<frame::XDispatch> xDispatch(Tools::GetDispatch(mxFrame, aURL));
1163 if (xDispatch.is())
1164 xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>());
1166 else
1168 // In LOK we don't really destroy the sidebar when "closing";
1169 // we simply hide it. This is because recreating it is problematic
1170 // See notes in SidebarDockingWindow::NotifyResize().
1171 RequestCloseDeck();
1174 else
1178 OString sNumber;
1179 if (rCurItemId.startsWith("select", &sNumber))
1181 RequestOpenDeck();
1182 SwitchToDeck(mpTabBar->GetDeckIdForIndex(sNumber.toInt32()));
1184 mpParentWindow->GrabFocusToDocument();
1186 catch (RuntimeException&)
1192 IMPL_LINK(SidebarController, OnSubMenuItemSelected, const OString&, rCurItemId, void)
1194 if (rCurItemId == "restoredefault")
1195 mpTabBar->RestoreHideFlags();
1196 else
1200 OString sNumber;
1201 if (rCurItemId.startsWith("customize", &sNumber))
1203 mpTabBar->ToggleHideFlag(sNumber.toInt32());
1205 // Find the set of decks that could be displayed for the new context.
1206 ResourceManager::DeckContextDescriptorContainer aDecks;
1207 mpResourceManager->GetMatchingDecks (
1208 aDecks,
1209 GetCurrentContext(),
1210 IsDocumentReadOnly(),
1211 mxFrame->getController());
1212 // Notify the tab bar about the updated set of decks.
1213 maFocusManager.Clear();
1214 mpTabBar->SetDecks(aDecks);
1215 mpTabBar->HighlightDeck(mpCurrentDeck->GetId());
1216 mpTabBar->UpdateFocusManager(maFocusManager);
1218 mpParentWindow->GrabFocusToDocument();
1220 catch (RuntimeException&)
1227 void SidebarController::RequestCloseDeck()
1229 if (comphelper::LibreOfficeKit::isActive() && mpCurrentDeck)
1231 const SfxViewShell* pViewShell = SfxViewShell::Current();
1232 if (pViewShell && pViewShell->isLOKMobilePhone())
1234 // Mobile phone - TODO: unify with desktop
1235 tools::JsonWriter aJsonWriter;
1236 aJsonWriter.put("id", mpParentWindow->get_id());
1237 aJsonWriter.put("type", "dockingwindow");
1238 aJsonWriter.put("text", mpParentWindow->GetText());
1239 aJsonWriter.put("enabled", false);
1240 const std::string message = aJsonWriter.extractAsStdString();
1241 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_JSDIALOG, message.c_str());
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 const std::string message = aJsonWriter.extractAsStdString();
1250 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_JSDIALOG, message.c_str());
1254 mbIsDeckRequestedOpen = false;
1255 UpdateDeckOpenState();
1257 if (!mpCurrentDeck)
1258 mpTabBar->RemoveDeckHighlight();
1261 void SidebarController::RequestOpenDeck()
1263 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1264 if ( pSplitWindow && !pSplitWindow->IsFadeIn() )
1265 // tdf#83546 Collapsed sidebar should expand first
1266 pSplitWindow->FadeIn();
1268 mbIsDeckRequestedOpen = true;
1269 UpdateDeckOpenState();
1272 bool SidebarController::IsDeckOpen(const sal_Int32 nIndex)
1274 if (nIndex >= 0)
1276 OUString asDeckId(mpTabBar->GetDeckIdForIndex(nIndex));
1277 return IsDeckVisible(asDeckId);
1279 return mbIsDeckOpen && *mbIsDeckOpen;
1282 bool SidebarController::IsDeckVisible(std::u16string_view rsDeckId)
1284 return mbIsDeckOpen && *mbIsDeckOpen && msCurrentDeckId == rsDeckId;
1287 void SidebarController::UpdateDeckOpenState()
1289 if ( ! mbIsDeckRequestedOpen)
1290 // No state requested.
1291 return;
1293 const sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
1295 // Update (change) the open state when it either has not yet been initialized
1296 // or when its value differs from the requested state.
1297 if ( mbIsDeckOpen && *mbIsDeckOpen == *mbIsDeckRequestedOpen )
1298 return;
1300 if (*mbIsDeckRequestedOpen)
1302 if (!mpParentWindow->IsFloatingMode())
1304 if (mnSavedSidebarWidth <= nTabBarDefaultWidth)
1305 SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow));
1306 else
1307 SetChildWindowWidth(mnSavedSidebarWidth);
1309 else
1311 // Show the Deck by resizing back to the original size (before hiding).
1312 Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel());
1313 Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel());
1315 aNewPos.setX(aNewPos.X() - mnSavedSidebarWidth + nTabBarDefaultWidth);
1316 aNewSize.setWidth(mnSavedSidebarWidth);
1318 mpParentWindow->GetFloatingWindow()->SetPosSizePixel(aNewPos, aNewSize);
1320 if (comphelper::LibreOfficeKit::isActive())
1322 // Sidebar wide enough to render the menu; enable it.
1323 mpTabBar->EnableMenuButton(true);
1325 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
1327 const std::string uno = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
1328 if (!uno.empty())
1329 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
1330 (uno + "=true").c_str());
1335 else
1337 if ( ! mpParentWindow->IsFloatingMode())
1338 mnSavedSidebarWidth = SetChildWindowWidth(nTabBarDefaultWidth);
1339 else
1341 // Hide the Deck by resizing to the width of the TabBar.
1342 Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel());
1343 Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel());
1344 mnSavedSidebarWidth = aNewSize.Width(); // Save the current width to restore.
1346 aNewPos.setX(aNewPos.X() + mnSavedSidebarWidth - nTabBarDefaultWidth);
1347 if (comphelper::LibreOfficeKit::isActive())
1349 // Hide by collapsing, otherwise with 0x0 the client might expect
1350 // to get valid dimensions on rendering and not collapse the sidebar.
1351 aNewSize.setWidth(1);
1353 else
1354 aNewSize.setWidth(nTabBarDefaultWidth);
1356 mpParentWindow->GetFloatingWindow()->SetPosSizePixel(aNewPos, aNewSize);
1358 if (comphelper::LibreOfficeKit::isActive())
1360 // Sidebar too narrow to render the menu; disable it.
1361 mpTabBar->EnableMenuButton(false);
1363 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
1365 const std::string uno = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
1366 if (!uno.empty())
1367 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
1368 (uno + "=false").c_str());
1373 if (mnWidthOnSplitterButtonDown > nTabBarDefaultWidth)
1374 mnSavedSidebarWidth = mnWidthOnSplitterButtonDown;
1375 mpParentWindow->SetStyle(mpParentWindow->GetStyle() & ~WB_SIZEABLE);
1378 mbIsDeckOpen = *mbIsDeckRequestedOpen;
1379 if (*mbIsDeckOpen && mpCurrentDeck)
1380 mpCurrentDeck->Show();
1381 NotifyResize();
1384 bool SidebarController::CanModifyChildWindowWidth()
1386 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1387 if (pSplitWindow == nullptr)
1388 return false;
1390 sal_uInt16 nRow (0xffff);
1391 sal_uInt16 nColumn (0xffff);
1392 if (pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow))
1394 sal_uInt16 nRowCount (pSplitWindow->GetWindowCount(nColumn));
1395 return nRowCount==1;
1397 else
1398 return false;
1401 sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth)
1403 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1404 if (pSplitWindow == nullptr)
1405 return 0;
1407 sal_uInt16 nRow (0xffff);
1408 sal_uInt16 nColumn (0xffff);
1409 pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow);
1410 const tools::Long nColumnWidth (pSplitWindow->GetLineSize(nColumn));
1412 vcl::Window* pWindow = mpParentWindow;
1413 const Size aWindowSize (pWindow->GetSizePixel());
1415 pSplitWindow->MoveWindow(
1416 mpParentWindow,
1417 Size(nNewWidth, aWindowSize.Height()),
1418 nColumn,
1419 nRow,
1420 false);
1421 static_cast<SplitWindow*>(pSplitWindow)->Split();
1423 return static_cast<sal_Int32>(nColumnWidth);
1426 void SidebarController::RestrictWidth (sal_Int32 nWidth)
1428 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1429 if (pSplitWindow != nullptr)
1431 const sal_uInt16 nId (pSplitWindow->GetItemId(mpParentWindow.get()));
1432 const sal_uInt16 nSetId (pSplitWindow->GetSet(nId));
1433 const sal_Int32 nRequestedWidth = TabBar::GetDefaultWidth() + nWidth;
1435 pSplitWindow->SetItemSizeRange(
1436 nSetId,
1437 Range(nRequestedWidth, std::max(nRequestedWidth, getMaximumWidth())));
1441 SfxSplitWindow* SidebarController::GetSplitWindow()
1443 if (mpParentWindow != nullptr)
1445 SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent());
1446 if (pSplitWindow != mpSplitWindow)
1448 if (mpSplitWindow != nullptr)
1449 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
1451 mpSplitWindow = pSplitWindow;
1453 if (mpSplitWindow != nullptr)
1454 mpSplitWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler));
1456 return mpSplitWindow;
1458 else
1459 return nullptr;
1462 void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag)
1464 if (mpParentWindow == nullptr)
1465 return;
1467 if (bCloseAfterDrag)
1469 // Make sure that the indicator exists.
1470 if (!mpCloseIndicator)
1471 mpCloseIndicator.reset(VclPtr<CloseIndicator>::Create(mpParentWindow));
1473 // Place and show the indicator.
1474 const Size aWindowSize (mpParentWindow->GetSizePixel());
1475 const Size aImageSize (mpCloseIndicator->GetSizePixel());
1476 mpCloseIndicator->SetPosPixel(
1477 Point(
1478 aWindowSize.Width() - TabBar::GetDefaultWidth() - aImageSize.Width(),
1479 (aWindowSize.Height() - aImageSize.Height())/2));
1480 mpCloseIndicator->Show();
1482 else
1484 // Hide but don't delete the indicator.
1485 if (mpCloseIndicator)
1486 mpCloseIndicator->Hide();
1490 void SidebarController::UpdateTitleBarIcons()
1492 if ( ! mpCurrentDeck)
1493 return;
1495 const bool bIsHighContrastModeActive (Theme::IsHighContrastMode());
1497 const ResourceManager& rResourceManager = *mpResourceManager;
1499 // Update the deck icon.
1500 std::shared_ptr<DeckDescriptor> xDeckDescriptor = rResourceManager.GetDeckDescriptor(mpCurrentDeck->GetId());
1501 if (xDeckDescriptor && mpCurrentDeck->GetTitleBar())
1503 const OUString sIconURL(
1504 bIsHighContrastModeActive
1505 ? xDeckDescriptor->msHighContrastTitleBarIconURL
1506 : xDeckDescriptor->msTitleBarIconURL);
1507 mpCurrentDeck->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
1510 // Update the panel icons.
1511 const SharedPanelContainer& rPanels (mpCurrentDeck->GetPanels());
1512 for (const auto& rxPanel : rPanels)
1514 if ( ! rxPanel)
1515 continue;
1516 if (!rxPanel->GetTitleBar())
1517 continue;
1518 std::shared_ptr<PanelDescriptor> xPanelDescriptor = rResourceManager.GetPanelDescriptor(rxPanel->GetId());
1519 if (!xPanelDescriptor)
1520 continue;
1521 const OUString sIconURL (
1522 bIsHighContrastModeActive
1523 ? xPanelDescriptor->msHighContrastTitleBarIconURL
1524 : xPanelDescriptor->msTitleBarIconURL);
1525 rxPanel->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
1529 void SidebarController::ShowPanel (const Panel& rPanel)
1531 if (mpCurrentDeck)
1533 if (!IsDeckOpen())
1534 RequestOpenDeck();
1535 mpCurrentDeck->ShowPanel(rPanel);
1539 ResourceManager::DeckContextDescriptorContainer SidebarController::GetMatchingDecks()
1541 ResourceManager::DeckContextDescriptorContainer aDecks;
1542 mpResourceManager->GetMatchingDecks (aDecks,
1543 GetCurrentContext(),
1544 IsDocumentReadOnly(),
1545 mxFrame->getController());
1546 return aDecks;
1549 ResourceManager::PanelContextDescriptorContainer SidebarController::GetMatchingPanels(std::u16string_view rDeckId)
1551 ResourceManager::PanelContextDescriptorContainer aPanels;
1553 mpResourceManager->GetMatchingPanels(aPanels,
1554 GetCurrentContext(),
1555 rDeckId,
1556 mxFrame->getController());
1557 return aPanels;
1560 void SidebarController::updateModel(const css::uno::Reference<css::frame::XModel>& xModel)
1562 mpResourceManager->UpdateModel(xModel);
1565 void SidebarController::FadeOut()
1567 if (mpSplitWindow)
1568 mpSplitWindow->FadeOut();
1571 void SidebarController::FadeIn()
1573 if (mpSplitWindow)
1574 mpSplitWindow->FadeIn();
1577 tools::Rectangle SidebarController::GetDeckDragArea() const
1579 tools::Rectangle aRect;
1580 if (mpCurrentDeck)
1582 if (DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar())
1584 aRect = pTitleBar->GetDragArea();
1587 return aRect;
1590 void SidebarController::frameAction(const css::frame::FrameActionEvent& rEvent)
1592 if (rEvent.Frame == mxFrame)
1594 if (rEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING)
1595 unregisterSidebarForFrame(mxFrame->getController());
1596 else if (rEvent.Action == css::frame::FrameAction_COMPONENT_REATTACHED)
1597 registerSidebarForFrame(mxFrame->getController());
1601 void SidebarController::saveDeckState()
1603 // Impress shutdown : context (frame) is disposed before sidebar disposing
1604 // calc writer : context (frame) is disposed after sidebar disposing
1605 // so need to test if GetCurrentContext is still valid regarding msApplication
1606 if (GetCurrentContext().msApplication != "none")
1608 mpResourceManager->SaveDecksSettings(GetCurrentContext());
1609 mpResourceManager->SaveLastActiveDeck(GetCurrentContext(), msCurrentDeckId);
1613 static bool isChartOrMathContext(const Context& context)
1615 return context.msApplication == "com.sun.star.chart2.ChartDocument"
1616 || context.msApplication == "com.sun.star.formula.FormulaProperties";
1619 bool SidebarController::hasChartOrMathContextCurrently() const
1621 if ((maRequestedContext != maCurrentContext) && isChartOrMathContext(maRequestedContext))
1622 return true; // We are not yet changed, but in the process
1624 return isChartOrMathContext(maCurrentContext);
1627 sfx2::sidebar::SidebarController* SidebarController::GetSidebarControllerForView(const SfxViewShell* pViewShell)
1629 if (!pViewShell)
1630 return nullptr;
1632 Reference<css::frame::XController2> xController(pViewShell->GetController(), UNO_QUERY);
1633 if (!xController.is())
1634 return nullptr;
1636 // Make sure there is a model behind the controller, otherwise getSidebar() can crash.
1637 if (!xController->getModel().is())
1638 return nullptr;
1640 Reference<css::ui::XSidebarProvider> xSidebarProvider = xController->getSidebar();
1641 if (!xSidebarProvider.is())
1642 return nullptr;
1644 Reference<css::ui::XSidebar> xSidebar = xSidebarProvider->getSidebar();
1645 if (!xSidebar.is())
1646 return nullptr;
1648 return dynamic_cast<sfx2::sidebar::SidebarController*>(xSidebar.get());
1651 } // end of namespace sfx2::sidebar
1653 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */