LibreOffice Module sfx2 (master) 1
SidebarController.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
20#include <sfx2/sidebar/Deck.hxx>
29#include <sidebar/Tools.hxx>
31#include <com/sun/star/ui/XSidebarProvider.hpp>
32#include <com/sun/star/frame/XController2.hpp>
34#include <sfx2/viewsh.hxx>
35
36
38#include <vcl/EnumContext.hxx>
39#include <vcl/uitest/logger.hxx>
41#include <vcl/svapp.hxx>
42#include <splitwin.hxx>
44#include <tools/json_writer.hxx>
45#include <tools/link.hxx>
49#include <comphelper/lok.hxx>
50#include <sal/log.hxx>
51#include <officecfg/Office/UI/Sidebar.hxx>
52#include <LibreOfficeKit/LibreOfficeKitEnums.h>
53
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>
61
62#include <bitmaps.hlst>
63
64using namespace css;
65using namespace css::uno;
66
67namespace
68{
69 constexpr OUStringLiteral gsReadOnlyCommandName = u".uno:EditDoc";
70 const sal_Int32 gnWidthCloseThreshold (70);
71 const sal_Int32 gnWidthOpenThreshold (40);
72
73 std::string UnoNameFromDeckId(std::u16string_view rsDeckId, const sfx2::sidebar::Context& context)
74 {
75 if (rsDeckId == u"SdCustomAnimationDeck")
76 return ".uno:CustomAnimation";
77
78 if (rsDeckId == u"PropertyDeck")
79 return vcl::EnumContext::Application::Impress == vcl::EnumContext::GetApplicationEnum(context.msApplication) ? ".uno:ModifyPage" : ".uno:Sidebar";
80
81 if (rsDeckId == u"SdLayoutsDeck")
82 return ".uno:ModifyPage";
83
84 if (rsDeckId == u"SdSlideTransitionDeck")
85 return ".uno:SlideChangeWindow";
86
87 if (rsDeckId == u"SdAllMasterPagesDeck")
88 return ".uno:MasterSlidesPanel";
89
90 if (rsDeckId == u"SdMasterPagesDeck")
91 return ".uno:MasterSlidesPanel";
92
93 if (rsDeckId == u"GalleryDeck")
94 return ".uno:Gallery";
95
96 OString sUno = ".uno:SidebarDeck." + OUStringToOString(rsDeckId, RTL_TEXTENCODING_ASCII_US);
97 return std::string(sUno);
98 }
99}
100
101namespace sfx2::sidebar {
102
103namespace {
104
107 constexpr OUStringLiteral gsDefaultDeckId(u"PropertyDeck");
108}
109
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)
136{
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>();
140}
141
143 const SfxViewFrame* pViewFrame)
144{
145 rtl::Reference<SidebarController> instance(new SidebarController(pParentWindow, pViewFrame));
146
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));
152
153 // Listen for theme property changes.
154 instance->mxThemePropertySet = Theme::GetPropertySet();
155 instance->mxThemePropertySet->addPropertyChangeListener(
156 "",
157 static_cast<css::beans::XPropertyChangeListener*>(instance.get()));
158
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);
165
166 //first UpdateConfigurations call will SwitchToDeck
167
168 return instance;
169}
170
172{
173}
174
176 const css::uno::Reference<css::frame::XFrame>& rxFrame)
177{
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
180 {
181 SAL_WARN("sfx.sidebar", "GetSidebarControllerForFrame: frame has no XController");
182 return nullptr;
183 }
184 uno::Reference<ui::XContextChangeEventListener> const xListener(
186 ::comphelper::getProcessComponentContext(),
188 [] (uno::Reference<uno::XInterface> const& xRef)
189 { return nullptr != dynamic_cast<SidebarController*>(xRef.get()); }
190 ));
191
192 return dynamic_cast<SidebarController*>(xListener.get());
193}
194
195void SidebarController::registerSidebarForFrame(const css::uno::Reference<css::frame::XController>& xController)
196{
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),
204}
205
206void SidebarController::unregisterSidebarForFrame(const css::uno::Reference<css::frame::XController>& xController)
207{
209 disposeDecks();
210
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),
217}
218
220{
221 SolarMutexGuard aSolarMutexGuard;
222
224 {
225 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
226 {
227 const std::string hide = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
228 if (!hide.empty())
229 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
230 OString(hide + "=false"));
231 }
232
233 if (mpParentWindow)
234 mpParentWindow->ReleaseLOKNotifier();
235 }
236
237 mpCurrentDeck.clear();
239 mpResourceManager->disposeDecks();
240}
241
242namespace
243{
244 class CloseIndicator final : public InterimItemWindow
245 {
246 public:
247 CloseIndicator(vcl::Window* pParent)
248 : InterimItemWindow(pParent, "svt/ui/fixedimagecontrol.ui", "FixedImageControl")
249 , m_xWidget(m_xBuilder->weld_image("image"))
250 {
251 InitControlBase(m_xWidget.get());
252
253 m_xWidget->set_from_icon_name(SIDEBAR_CLOSE_INDICATOR);
254
255 SetSizePixel(get_preferred_size());
256
258 }
259
260 virtual ~CloseIndicator() override
261 {
262 disposeOnce();
263 }
264
265 virtual void dispose() override
266 {
267 m_xWidget.reset();
269 }
270
271 private:
272 std::unique_ptr<weld::Image> m_xWidget;
273 };
274}
275
276void SidebarController::disposing(std::unique_lock<std::mutex>&)
277{
278 SolarMutexGuard aSolarMutexGuard;
279
281
284
286
287 // clear decks
289
290 mpResourceManager->GetMatchingDecks (
291 aDecks,
294 mxFrame->getController());
295
296 for (const auto& rDeck : aDecks)
297 {
298 std::shared_ptr<DeckDescriptor> deckDesc = mpResourceManager->GetDeckDescriptor(rDeck.msId);
299
300 VclPtr<Deck> aDeck = deckDesc->mpDeck;
301 if (aDeck)
302 aDeck.disposeAndClear();
303 }
304
306
307 if (mxReadOnlyModeDispatch.is())
308 mxReadOnlyModeDispatch->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName));
309
310 if (mxThemePropertySet.is())
311 mxThemePropertySet->removePropertyChangeListener(
312 "",
313 static_cast<css::beans::XPropertyChangeListener*>(this));
314
315 if (mpParentWindow != nullptr)
316 {
317 mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
318 mpParentWindow = nullptr;
319 }
320
321 if (mpSplitWindow != nullptr)
322 {
323 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
324 mpSplitWindow = nullptr;
325 }
326
327 mxFrame->removeFrameActionListener(this);
328
329 uno::Reference<css::frame::XController> xController = mxFrame->getController();
330 if (!xController.is())
332
334}
335
336void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent)
337{
338 SolarMutexGuard aSolarMutexGuard;
339
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.
343
345 rEvent.ApplicationName,
346 rEvent.ContextName);
347
349 {
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
357 }
358}
359
360void SAL_CALL SidebarController::disposing (const css::lang::EventObject& )
361{
362 dispose();
363}
364
365void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& )
366{
367 SolarMutexGuard aSolarMutexGuard;
368
369 maPropertyChangeForwarder.RequestCall(); // async call, not a prob
370 // to call with held
371 // solarmutex
372}
373
374void SAL_CALL SidebarController::statusChanged (const css::frame::FeatureStateEvent& rEvent)
375{
376 SolarMutexGuard aSolarMutexGuard;
377
378 bool bIsReadWrite (true);
379 if (rEvent.IsEnabled)
380 rEvent.State >>= bIsReadWrite;
381
382 if (mbIsDocumentReadOnly != !bIsReadWrite)
383 {
384 mbIsDocumentReadOnly = !bIsReadWrite;
385
386 // Force the current deck to update its panel list.
389
391 maContextChangeUpdate.RequestCall(); // async call, ok to call
392 // with held solarmutex
393 }
394}
395
397{
398 SolarMutexGuard aSolarMutexGuard;
399
400 sal_Int32 nMinimalWidth = 0;
401 if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
402 {
403 mpCurrentDeck->RequestLayout();
404 nMinimalWidth = mbMinimumSidebarWidth ? mpCurrentDeck->GetMinimalWidth() : 0;
405 }
406 RestrictWidth(nMinimalWidth);
407}
408
410{
411 mpParentWindow->Invalidate(InvalidateFlags::Children);
412}
413
415{
416 if (!mpTabBar)
417 {
418 OSL_ASSERT(mpTabBar!=nullptr);
419 return;
420 }
421
422 const sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
423
424 const sal_Int32 nWidth(mpParentWindow->GetSizePixel().Width());
425 const sal_Int32 nHeight(mpParentWindow->GetSizePixel().Height());
426
427 mbIsDeckOpen = (nWidth > nTabBarDefaultWidth);
428
429 if (mnSavedSidebarWidth <= 0)
430 mnSavedSidebarWidth = nWidth;
431
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);
440
441 if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
442 {
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
447 {
448 nDeckX = nTabBarDefaultWidth;
449 nTabX = 0;
450 }
451 else // attach the Sidebar towards the right-side of screen
452 {
453 nDeckX = 0;
454 nTabX = nWidth - nTabBarDefaultWidth;
455 }
456
457 // Place the deck first.
458 if (bIsDeckVisible)
459 {
461 {
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);
474 }
475 else
476 mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth - nTabBarDefaultWidth, nHeight);
477 mpCurrentDeck->Show();
478 mpCurrentDeck->RequestLayout();
479 mpTabBar->HighlightDeck(mpCurrentDeck->GetId());
480 }
481 else
482 mpCurrentDeck->Hide();
483
484 // Now place the tab bar.
485 mpTabBar->setPosSizePixel(nTabX, 0, nTabBarDefaultWidth, nHeight);
487 mpTabBar->Show(); // Don't show TabBar in LOK.
488 }
489
490 // Determine if the closer of the deck can be shown.
491 sal_Int32 nMinimalWidth = 0;
492 if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
493 {
494 DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar();
495 if (pTitleBar && pTitleBar->GetVisible())
497 nMinimalWidth = mbMinimumSidebarWidth ? mpCurrentDeck->GetMinimalWidth() : 0;
498 }
499
500 RestrictWidth(nMinimalWidth);
501}
502
503void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth)
504{
506 return;
507
509 {
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)
516 }
517 else
518 {
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;
524
527 }
528}
529
531{
534}
535
537{
540 return;
541
542 if ((maCurrentContext.msApplication != "none") &&
544 {
545 mpResourceManager->SaveDecksSettings(maCurrentContext);
547 }
548
549 // get last active deck for this application on first update
550 if (!maRequestedContext.msApplication.isEmpty() &&
552 {
553 OUString sLastActiveDeck = mpResourceManager->GetLastActiveDeck( maRequestedContext );
554 if (!sLastActiveDeck.isEmpty())
555 msCurrentDeckId = sLastActiveDeck;
556 }
557
559
560 mpResourceManager->InitDeckContext(GetCurrentContext());
561
562 // Find the set of decks that could be displayed for the new context.
564
565 css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
566
567 mpResourceManager->GetMatchingDecks (
568 aDecks,
572
574
575 // Notify the tab bar about the updated set of decks.
576 mpTabBar->SetDecks(aDecks);
577
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)
583 {
584 if (rDeck.mbIsEnabled)
585 {
586 if (rDeck.msId == msCurrentDeckId)
587 {
588 sNewDeckId = msCurrentDeckId;
589 break;
590 }
591 else if (sNewDeckId.getLength() == 0)
592 sNewDeckId = rDeck.msId;
593 }
594 }
595
596 if (sNewDeckId.getLength() == 0)
597 {
598 // We did not find a valid deck.
600 return;
601 }
602
603 std::shared_ptr<DeckDescriptor> xDescriptor = mpResourceManager->GetDeckDescriptor(sNewDeckId);
604
605 if (xDescriptor)
606 {
607 SwitchToDeck(*xDescriptor, maCurrentContext);
608 }
609}
610
611namespace {
612
613void collectUIInformation(const OUString& rDeckId)
614{
615 EventDescription aDescription;
616 aDescription.aAction = "SIDEBAR";
617 aDescription.aParent = "MainWindow";
618 aDescription.aParameters = {{"PANEL", rDeckId}};
619 aDescription.aKeyWord = "CurrentApp";
620
621 UITestLogger::getInstance().logEvent(aDescription);
622}
623
624}
625
627 const OUString& rsDeckId)
628{
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 ) )
634 {
635 if( !WasFloatingDeckClosed() )
636 {
637 // tdf#88241 Summoning an undocked sidebar a second time should close sidebar
638 mpParentWindow->Close();
639 return;
640 }
641 else
642 {
643 // tdf#67627 Clicking a second time on a Deck icon will close the Deck
645 return;
646 }
647 }
649 // before SwitchToDeck which may cause the rsDeckId string to be released
650 collectUIInformation(rsDeckId);
651 SwitchToDeck(rsDeckId);
652
653 // Make sure the sidebar is wide enough to fit the requested content
654 if (mpCurrentDeck && mpTabBar)
655 {
656 sal_Int32 nRequestedWidth = mpCurrentDeck->GetMinimalWidth() + TabBar::GetDefaultWidth();
657 // if sidebar was dragged
658 if(mnWidthOnSplitterButtonDown > 0 && mnWidthOnSplitterButtonDown > nRequestedWidth){
660 }else{
661 SetChildWindowWidth(nRequestedWidth);
662 }
663 }
664}
665
667 std::u16string_view rsDeckId)
668{
670 SwitchToDeck(rsDeckId);
671
672}
673
675{
676 SwitchToDeck(gsDefaultDeckId);
677}
678
680 std::u16string_view rsDeckId)
681{
682 if ( msCurrentDeckId != rsDeckId
683 || ! mbIsDeckOpen
685 {
686 std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rsDeckId);
687
688 if (xDeckDescriptor)
689 {
690 SwitchToDeck(*xDeckDescriptor, maCurrentContext);
692 }
693 }
694}
695
696void SidebarController::CreateDeck(std::u16string_view rDeckId) {
698}
699
700void SidebarController::CreateDeck(std::u16string_view rDeckId, const Context& rContext, bool bForceCreate)
701{
702 std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rDeckId);
703
704 if (!xDeckDescriptor)
705 return;
706
707 VclPtr<Deck> aDeck = xDeckDescriptor->mpDeck;
708 if (!aDeck || bForceCreate)
709 {
710 if (aDeck)
711 aDeck.disposeAndClear();
712
713 aDeck = VclPtr<Deck>::Create(
714 *xDeckDescriptor,
716 [this]() { return this->RequestCloseDeck(); });
717 }
718 xDeckDescriptor->mpDeck = aDeck;
719 CreatePanels(rDeckId, rContext);
720}
721
722void SidebarController::CreatePanels(std::u16string_view rDeckId, const Context& rContext)
723{
724 std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rDeckId);
725
726 // init panels bounded to that deck, do not wait them being displayed as may be accessed through API
727
728 VclPtr<Deck> pDeck = xDeckDescriptor->mpDeck;
729
731
732 css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
733
734 mpResourceManager->GetMatchingPanels(
735 aPanelContextDescriptors,
736 rContext,
737 rDeckId,
739
740 // Update the panel list.
741 const sal_Int32 nNewPanelCount (aPanelContextDescriptors.size());
742 SharedPanelContainer aNewPanels;
743 sal_Int32 nWriteIndex (0);
744
745 aNewPanels.resize(nNewPanelCount);
746
747 for (sal_Int32 nReadIndex=0; nReadIndex<nNewPanelCount; ++nReadIndex)
748 {
749 const ResourceManager::PanelContextDescriptor& rPanelContexDescriptor (
750 aPanelContextDescriptors[nReadIndex]);
751
752 // Determine if the panel can be displayed.
753 const bool bIsPanelVisible (!mbIsDocumentReadOnly || rPanelContexDescriptor.mbShowForReadOnlyDocuments);
754 if ( ! bIsPanelVisible)
755 continue;
756
757 auto xOldPanel(pDeck->GetPanel(rPanelContexDescriptor.msId));
758 if (xOldPanel)
759 {
760 xOldPanel->SetLurkMode(false);
761 aNewPanels[nWriteIndex] = xOldPanel;
762 xOldPanel->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible);
763 ++nWriteIndex;
764 }
765 else
766 {
767 auto aPanel = CreatePanel(rPanelContexDescriptor.msId,
768 pDeck->GetPanelParentWindow(),
769 rPanelContexDescriptor.mbIsInitiallyVisible,
770 rContext,
771 pDeck);
772 if (aPanel)
773 {
774 aNewPanels[nWriteIndex] = std::move(aPanel);
775
776 // Depending on the context we have to change the command
777 // for the "more options" dialog.
778 PanelTitleBar* pTitleBar = aNewPanels[nWriteIndex]->GetTitleBar();
779 if (pTitleBar)
780 {
781 pTitleBar->SetMoreOptionsCommand(
782 rPanelContexDescriptor.msMenuCommand,
784 }
785 ++nWriteIndex;
786 }
787 }
788 }
789
790 // mpCurrentPanels - may miss stuff (?)
791 aNewPanels.resize(nWriteIndex);
792 pDeck->ResetPanels(std::move(aNewPanels));
793}
794
796 const DeckDescriptor& rDeckDescriptor,
797 const Context& rContext)
798{
800 {
801 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
802 {
803 if (msCurrentDeckId != rDeckDescriptor.msId)
804 {
805 const std::string hide = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
806 if (!hide.empty())
807 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
808 OString(hide + "=false"));
809 }
810
811 const std::string show = UnoNameFromDeckId(rDeckDescriptor.msId, GetCurrentContext());
812 if (!show.empty())
813 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
814 OString(show + "=true"));
815 }
816 }
817
819
820 const bool bForceNewDeck ((mnRequestedForceFlags&SwitchFlag_ForceNewDeck)!=0);
821 const bool bForceNewPanels ((mnRequestedForceFlags&SwitchFlag_ForceNewPanels)!=0);
823
824 if ( msCurrentDeckId != rDeckDescriptor.msId
825 || bForceNewDeck)
826 {
827 if (mpCurrentDeck)
828 mpCurrentDeck->Hide();
829
830 msCurrentDeckId = rDeckDescriptor.msId;
831 }
832
833 // Determine the panels to display in the deck.
835
836 css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController();
837
838 mpResourceManager->GetMatchingPanels(
839 aPanelContextDescriptors,
840 rContext,
841 rDeckDescriptor.msId,
843
844 if (aPanelContextDescriptors.empty())
845 {
846 // There are no panels to be displayed in the current context.
848 {
849 // Switch to the "empty" context and try again.
851 rDeckDescriptor,
852 Context(
853 rContext.msApplication,
855 return;
856 }
857 else
858 {
859 // This is already the "empty" context. Looks like we have
860 // to live with an empty deck.
861 }
862 }
863
864 // Provide a configuration and Deck object.
865
866 CreateDeck(rDeckDescriptor.msId, rContext, bForceNewDeck);
867
868 if (bForceNewPanels && !bForceNewDeck) // already forced if bForceNewDeck
869 CreatePanels(rDeckDescriptor.msId, rContext);
870
871 if (mpCurrentDeck && mpCurrentDeck != rDeckDescriptor.mpDeck)
872 mpCurrentDeck->Hide();
873 mpCurrentDeck.reset(rDeckDescriptor.mpDeck);
874
875 if ( ! mpCurrentDeck)
876 return;
877
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
884
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
890 {
891 nDeckX = nTabBarDefaultWidth;
892 }
893 else // attach the Sidebar towards the right-side of screen
894 {
895 nDeckX = 0;
896 }
897
898 // Activate the deck and the new set of panels.
899 mpCurrentDeck->setPosSizePixel(
900 nDeckX,
901 0,
902 mpParentWindow->GetSizePixel().Width() - nTabBarDefaultWidth,
903 mpParentWindow->GetSizePixel().Height());
904
905 mpCurrentDeck->Show();
906
907 mpParentWindow->SetText(rDeckDescriptor.msTitle);
908
909 NotifyResize();
910
911 // Tell the focus manager about the new panels and tab bar
912 // buttons.
915
916 mpTabBar->UpdateFocusManager(maFocusManager);
918}
919
920void SidebarController::notifyDeckTitle(std::u16string_view targetDeckId)
921{
922 if (msCurrentDeckId == targetDeckId)
923 {
925 mpTabBar->UpdateFocusManager(maFocusManager);
927 }
928}
929
930std::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)
936{
937 std::shared_ptr<PanelDescriptor> xPanelDescriptor = mpResourceManager->GetPanelDescriptor(rsPanelId);
938
939 if (!xPanelDescriptor)
940 return nullptr;
941
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);
950
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())
958 {
959 // Initialize the panel and add it to the active deck.
960 xPanel->SetUIElement(xUIElement);
961 }
962 else
963 {
964 xPanel.reset();
965 }
966
967 return xPanel;
968}
969
970Reference<ui::XUIElement> SidebarController::CreateUIElement (
971 const Reference<awt::XWindow>& rxWindow,
972 const OUString& rsImplementationURL,
973 const bool bWantsCanvas,
974 const Context& rContext)
975{
976 try
977 {
978 const Reference<XComponentContext> xComponentContext (::comphelper::getProcessComponentContext() );
979 const Reference<ui::XUIElementFactory> xUIElementFactory =
980 ui::theUIElementFactoryManager::get( xComponentContext );
981
982 // Create the XUIElement.
983 ::comphelper::NamedValueCollection aCreationArguments;
984 aCreationArguments.put("Frame", Any(mxFrame));
985 aCreationArguments.put("ParentWindow", Any(rxWindow));
986 SidebarDockingWindow* pSfxDockingWindow = mpParentWindow.get();
987 if (pSfxDockingWindow != nullptr)
988 aCreationArguments.put("SfxBindings", Any(reinterpret_cast<sal_uInt64>(&pSfxDockingWindow->GetBindings())));
989 aCreationArguments.put("Theme", Theme::GetPropertySet());
990 aCreationArguments.put("Sidebar", Any(Reference<ui::XSidebar>(static_cast<ui::XSidebar*>(this))));
991 if (bWantsCanvas)
992 {
993 Reference<rendering::XSpriteCanvas> xCanvas (VCLUnoHelper::GetWindow(rxWindow)->GetOutDev()->GetSpriteCanvas());
994 aCreationArguments.put("Canvas", Any(xCanvas));
995 }
996
997 if (mxCurrentController.is())
998 {
999 OUString aModule = Tools::GetModuleName(mxCurrentController);
1000 if (!aModule.isEmpty())
1001 {
1002 aCreationArguments.put("Module", Any(aModule));
1003 }
1004 aCreationArguments.put("Controller", Any(mxCurrentController));
1005 }
1006
1007 aCreationArguments.put("ApplicationName", Any(rContext.msApplication));
1008 aCreationArguments.put("ContextName", Any(rContext.msContext));
1009
1010 Reference<ui::XUIElement> xUIElement(
1011 xUIElementFactory->createUIElement(
1012 rsImplementationURL,
1013 aCreationArguments.getPropertyValues()),
1014 UNO_SET_THROW);
1015
1016 return xUIElement;
1017 }
1018 catch(const Exception&)
1019 {
1020 TOOLS_WARN_EXCEPTION("sfx.sidebar", "Cannot create panel " << rsImplementationURL);
1021 return nullptr;
1022 }
1023}
1024
1025IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent&, rEvent, void)
1026{
1027 if (rEvent.GetWindow() == mpParentWindow)
1028 {
1029 switch (rEvent.GetId())
1030 {
1031 case VclEventId::WindowShow:
1032 case VclEventId::WindowResize:
1033 NotifyResize();
1034 break;
1035
1036 case VclEventId::WindowDataChanged:
1037 // Force an update of deck and tab bar to reflect
1038 // changes in theme (high contrast mode).
1040 UpdateTitleBarIcons();
1041 mpParentWindow->Invalidate();
1042 mnRequestedForceFlags |= SwitchFlag_ForceNewDeck | SwitchFlag_ForceNewPanels;
1043 maContextChangeUpdate.RequestCall();
1044 break;
1045
1046 case VclEventId::ObjectDying:
1047 dispose();
1048 break;
1049
1050 case VclEventId::WindowPaint:
1051 SAL_INFO("sfx.sidebar", "Paint");
1052 break;
1053
1054 default:
1055 break;
1056 }
1057 }
1058 else if (rEvent.GetWindow()==mpSplitWindow && mpSplitWindow!=nullptr)
1059 {
1060 switch (rEvent.GetId())
1061 {
1062 case VclEventId::WindowMouseButtonDown:
1063 mnWidthOnSplitterButtonDown = mpParentWindow->GetSizePixel().Width();
1064 break;
1065
1066 case VclEventId::WindowMouseButtonUp:
1067 {
1068 ProcessNewWidth(mpParentWindow->GetSizePixel().Width());
1069 break;
1070 }
1071
1072 case VclEventId::ObjectDying:
1073 dispose();
1074 break;
1075
1076 default: break;
1077 }
1078 }
1079}
1080
1082 weld::Menu& rMainMenu, weld::Menu& rSubMenu,
1083 const ::std::vector<TabBar::DeckMenuData>& rMenuData) const
1084{
1085 PopulatePopupMenus(rMainMenu, rSubMenu, rMenuData);
1086 rMainMenu.connect_activate(LINK(const_cast<SidebarController*>(this), SidebarController, OnMenuItemSelected));
1087 rSubMenu.connect_activate(LINK(const_cast<SidebarController*>(this), SidebarController, OnSubMenuItemSelected));
1088}
1089
1091 const std::vector<TabBar::DeckMenuData>& rMenuData) const
1092{
1093 // Add one entry for every tool panel element to individually make
1094 // them visible or hide them.
1095 sal_Int32 nIndex (0);
1096 for (const auto& rItem : rMenuData)
1097 {
1098 OUString sIdent("select" + OUString::number(nIndex));
1099 rMenu.insert(nIndex, sIdent, rItem.msDisplayName,
1100 nullptr, nullptr, nullptr, TRISTATE_FALSE);
1101 rMenu.set_active(sIdent, rItem.mbIsCurrentDeck);
1102 rMenu.set_sensitive(sIdent, rItem.mbIsEnabled && rItem.mbIsActive);
1103
1105 {
1106 if (rItem.mbIsCurrentDeck)
1107 {
1108 // Don't allow the currently visible deck to be disabled.
1109 OUString sSubIdent("nocustomize" + OUString::number(nIndex));
1110 rCustomizationMenu.insert(nIndex, sSubIdent, rItem.msDisplayName,
1111 nullptr, nullptr, nullptr, TRISTATE_FALSE);
1112 rCustomizationMenu.set_active(sSubIdent, true);
1113 }
1114 else
1115 {
1116 OUString sSubIdent("customize" + OUString::number(nIndex));
1117 rCustomizationMenu.insert(nIndex, sSubIdent, rItem.msDisplayName,
1118 nullptr, nullptr, nullptr, TRISTATE_TRUE);
1119 rCustomizationMenu.set_active(sSubIdent, rItem.mbIsEnabled && rItem.mbIsActive);
1120 }
1121 }
1122
1123 ++nIndex;
1124 }
1125
1126 bool bHideLock = true;
1127 bool bHideUnLock = true;
1128 // LOK doesn't support docked/undocked; Sidebar is floating but rendered docked in browser.
1130 {
1131 // Add entry for docking or un-docking the tool panel.
1132 if (mpParentWindow->IsFloatingMode())
1133 bHideLock = false;
1134 else
1135 bHideUnLock = false;
1136 }
1137 rMenu.set_visible("locktaskpanel", !bHideLock);
1138 rMenu.set_visible("unlocktaskpanel", !bHideUnLock);
1139
1140 // No Restore or Customize options for LoKit.
1141 rMenu.set_visible("customization", !comphelper::LibreOfficeKit::isActive());
1142}
1143
1144IMPL_LINK(SidebarController, OnMenuItemSelected, const OUString&, rCurItemId, void)
1145{
1146 if (rCurItemId == "unlocktaskpanel")
1147 {
1148 mpParentWindow->SetFloatingMode(true);
1149 if (mpParentWindow->IsFloatingMode())
1150 mpParentWindow->ToTop(ToTopFlags::GrabFocusOnly);
1151 }
1152 else if (rCurItemId == "locktaskpanel")
1153 {
1154 mpParentWindow->SetFloatingMode(false);
1155 }
1156 else if (rCurItemId == "hidesidebar")
1157 {
1159 {
1160 const util::URL aURL(Tools::GetURL(".uno:Sidebar"));
1161 Reference<frame::XDispatch> xDispatch(Tools::GetDispatch(mxFrame, aURL));
1162 if (xDispatch.is())
1163 xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>());
1164 }
1165 else
1166 {
1167 // In LOK we don't really destroy the sidebar when "closing";
1168 // we simply hide it. This is because recreating it is problematic
1169 // See notes in SidebarDockingWindow::NotifyResize().
1170 RequestCloseDeck();
1171 }
1172 }
1173 else
1174 {
1175 try
1176 {
1177 OUString sNumber;
1178 if (rCurItemId.startsWith("select", &sNumber))
1179 {
1180 RequestOpenDeck();
1181 SwitchToDeck(mpTabBar->GetDeckIdForIndex(sNumber.toInt32()));
1182 }
1183 mpParentWindow->GrabFocusToDocument();
1184 }
1185 catch (RuntimeException&)
1186 {
1187 }
1188 }
1189}
1190
1191IMPL_LINK(SidebarController, OnSubMenuItemSelected, const OUString&, rCurItemId, void)
1192{
1193 if (rCurItemId == "restoredefault")
1194 mpTabBar->RestoreHideFlags();
1195 else
1196 {
1197 try
1198 {
1199 OUString sNumber;
1200 if (rCurItemId.startsWith("customize", &sNumber))
1201 {
1202 mpTabBar->ToggleHideFlag(sNumber.toInt32());
1203
1204 // Find the set of decks that could be displayed for the new context.
1206 mpResourceManager->GetMatchingDecks (
1207 aDecks,
1208 GetCurrentContext(),
1209 IsDocumentReadOnly(),
1210 mxFrame->getController());
1211 // Notify the tab bar about the updated set of decks.
1212 maFocusManager.Clear();
1213 mpTabBar->SetDecks(aDecks);
1214 mpTabBar->HighlightDeck(mpCurrentDeck->GetId());
1215 mpTabBar->UpdateFocusManager(maFocusManager);
1216 }
1217 mpParentWindow->GrabFocusToDocument();
1218 }
1219 catch (RuntimeException&)
1220 {
1221 }
1222 }
1223}
1224
1225
1227{
1229 {
1230 const SfxViewShell* pViewShell = SfxViewShell::Current();
1231 if (pViewShell && pViewShell->isLOKMobilePhone())
1232 {
1233 // Mobile phone - TODO: unify with desktop
1234 tools::JsonWriter aJsonWriter;
1235 aJsonWriter.put("id", mpParentWindow->get_id());
1236 aJsonWriter.put("type", "dockingwindow");
1237 aJsonWriter.put("text", mpParentWindow->GetText());
1238 aJsonWriter.put("enabled", false);
1239 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_JSDIALOG, aJsonWriter.finishAndGetAsOString());
1240 }
1241 else if (pViewShell)
1242 {
1243 tools::JsonWriter aJsonWriter;
1244 aJsonWriter.put("id", mpParentWindow->get_id());
1245 aJsonWriter.put("action", "close");
1246 aJsonWriter.put("jsontype", "sidebar");
1247 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_JSDIALOG, aJsonWriter.finishAndGetAsOString());
1248 }
1249 }
1250
1251 mbIsDeckRequestedOpen = false;
1253
1254 mpTabBar->RemoveDeckHighlight();
1255}
1256
1258{
1259 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1260 if ( pSplitWindow && !pSplitWindow->IsFadeIn() )
1261 // tdf#83546 Collapsed sidebar should expand first
1262 pSplitWindow->FadeIn();
1263
1264 mbIsDeckRequestedOpen = true;
1266}
1267
1268bool SidebarController::IsDeckOpen(const sal_Int32 nIndex)
1269{
1270 if (nIndex >= 0)
1271 {
1272 OUString asDeckId(mpTabBar->GetDeckIdForIndex(nIndex));
1273 return IsDeckVisible(asDeckId);
1274 }
1275 return mbIsDeckOpen && *mbIsDeckOpen;
1276}
1277
1278bool SidebarController::IsDeckVisible(std::u16string_view rsDeckId)
1279{
1280 return mbIsDeckOpen && *mbIsDeckOpen && msCurrentDeckId == rsDeckId;
1281}
1282
1284{
1285 if ( ! mbIsDeckRequestedOpen)
1286 // No state requested.
1287 return;
1288
1289 const sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
1290
1291 // Update (change) the open state when it either has not yet been initialized
1292 // or when its value differs from the requested state.
1294 return;
1295
1297 {
1298 if (!mpParentWindow->IsFloatingMode())
1299 {
1300 if (mnSavedSidebarWidth <= nTabBarDefaultWidth)
1302 else
1304 }
1305 else
1306 {
1307 // Show the Deck by resizing back to the original size (before hiding).
1308 Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel());
1309 Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel());
1310
1311 aNewPos.setX(aNewPos.X() - mnSavedSidebarWidth + nTabBarDefaultWidth);
1312 aNewSize.setWidth(mnSavedSidebarWidth);
1313
1314 mpParentWindow->GetFloatingWindow()->SetPosSizePixel(aNewPos, aNewSize);
1315
1317 {
1318 // Sidebar wide enough to render the menu; enable it.
1319 mpTabBar->EnableMenuButton(true);
1320
1321 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
1322 {
1323 const std::string uno = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
1324 if (!uno.empty())
1325 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
1326 OString(uno + "=true"));
1327 }
1328 }
1329 }
1330 }
1331 else
1332 {
1333 if ( ! mpParentWindow->IsFloatingMode())
1334 mnSavedSidebarWidth = SetChildWindowWidth(nTabBarDefaultWidth);
1335 else
1336 {
1337 // Hide the Deck by resizing to the width of the TabBar.
1338 Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel());
1339 Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel());
1340 mnSavedSidebarWidth = aNewSize.Width(); // Save the current width to restore.
1341
1342 aNewPos.setX(aNewPos.X() + mnSavedSidebarWidth - nTabBarDefaultWidth);
1344 {
1345 // Hide by collapsing, otherwise with 0x0 the client might expect
1346 // to get valid dimensions on rendering and not collapse the sidebar.
1347 aNewSize.setWidth(1);
1348 }
1349 else
1350 aNewSize.setWidth(nTabBarDefaultWidth);
1351
1352 mpParentWindow->GetFloatingWindow()->SetPosSizePixel(aNewPos, aNewSize);
1353
1355 {
1356 // Sidebar too narrow to render the menu; disable it.
1357 mpTabBar->EnableMenuButton(false);
1358
1359 if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
1360 {
1361 const std::string uno = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext());
1362 if (!uno.empty())
1363 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
1364 OString(uno + "=false"));
1365 }
1366 }
1367 }
1368
1369 if (mnWidthOnSplitterButtonDown > nTabBarDefaultWidth)
1371 mpParentWindow->SetStyle(mpParentWindow->GetStyle() & ~WB_SIZEABLE);
1372 }
1373
1374 NotifyResize();
1375}
1376
1378{
1379 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1380 if (pSplitWindow == nullptr)
1381 return false;
1382
1383 sal_uInt16 nRow (0xffff);
1384 sal_uInt16 nColumn (0xffff);
1385 if (pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow))
1386 {
1387 sal_uInt16 nRowCount (pSplitWindow->GetWindowCount(nColumn));
1388 return nRowCount==1;
1389 }
1390 else
1391 return false;
1392}
1393
1394sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth)
1395{
1396 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1397 if (pSplitWindow == nullptr)
1398 return 0;
1399
1400 sal_uInt16 nRow (0xffff);
1401 sal_uInt16 nColumn (0xffff);
1402 pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow);
1403 const tools::Long nColumnWidth (pSplitWindow->GetLineSize(nColumn));
1404
1405 vcl::Window* pWindow = mpParentWindow;
1406 const Size aWindowSize (pWindow->GetSizePixel());
1407
1408 pSplitWindow->MoveWindow(
1410 Size(nNewWidth, aWindowSize.Height()),
1411 nColumn,
1412 nRow,
1413 false);
1414 static_cast<SplitWindow*>(pSplitWindow)->Split();
1415
1416 return static_cast<sal_Int32>(nColumnWidth);
1417}
1418
1420{
1421 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1422 if (pSplitWindow != nullptr)
1423 {
1424 const sal_uInt16 nId (pSplitWindow->GetItemId(mpParentWindow.get()));
1425 const sal_uInt16 nSetId (pSplitWindow->GetSet(nId));
1426 const sal_Int32 nRequestedWidth = TabBar::GetDefaultWidth() + nWidth;
1427
1428 pSplitWindow->SetItemSizeRange(
1429 nSetId,
1430 Range(nRequestedWidth, std::max(nRequestedWidth, getMaximumWidth())));
1431 }
1432}
1433
1435{
1436 if (mpParentWindow != nullptr)
1437 {
1438 SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent());
1439 if (pSplitWindow != mpSplitWindow)
1440 {
1441 if (mpSplitWindow != nullptr)
1442 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
1443
1444 mpSplitWindow = pSplitWindow;
1445
1446 if (mpSplitWindow != nullptr)
1447 mpSplitWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler));
1448 }
1449 return mpSplitWindow;
1450 }
1451 else
1452 return nullptr;
1453}
1454
1455void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag)
1456{
1457 if (mpParentWindow == nullptr)
1458 return;
1459
1460 if (bCloseAfterDrag)
1461 {
1462 // Make sure that the indicator exists.
1463 if (!mpCloseIndicator)
1465
1466 // Place and show the indicator.
1467 const Size aWindowSize (mpParentWindow->GetSizePixel());
1468 const Size aImageSize (mpCloseIndicator->GetSizePixel());
1469 mpCloseIndicator->SetPosPixel(
1470 Point(
1471 aWindowSize.Width() - TabBar::GetDefaultWidth() - aImageSize.Width(),
1472 (aWindowSize.Height() - aImageSize.Height())/2));
1473 mpCloseIndicator->Show();
1474 }
1475 else
1476 {
1477 // Hide but don't delete the indicator.
1478 if (mpCloseIndicator)
1479 mpCloseIndicator->Hide();
1480 }
1481}
1482
1484{
1485 if ( ! mpCurrentDeck)
1486 return;
1487
1488 const bool bIsHighContrastModeActive (Theme::IsHighContrastMode());
1489
1490 const ResourceManager& rResourceManager = *mpResourceManager;
1491
1492 // Update the deck icon.
1493 std::shared_ptr<DeckDescriptor> xDeckDescriptor = rResourceManager.GetDeckDescriptor(mpCurrentDeck->GetId());
1494 if (xDeckDescriptor && mpCurrentDeck->GetTitleBar())
1495 {
1496 const OUString sIconURL(
1497 bIsHighContrastModeActive
1498 ? xDeckDescriptor->msHighContrastTitleBarIconURL
1499 : xDeckDescriptor->msTitleBarIconURL);
1500 mpCurrentDeck->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
1501 }
1502
1503 // Update the panel icons.
1504 const SharedPanelContainer& rPanels (mpCurrentDeck->GetPanels());
1505 for (const auto& rxPanel : rPanels)
1506 {
1507 if ( ! rxPanel)
1508 continue;
1509 if (!rxPanel->GetTitleBar())
1510 continue;
1511 std::shared_ptr<PanelDescriptor> xPanelDescriptor = rResourceManager.GetPanelDescriptor(rxPanel->GetId());
1512 if (!xPanelDescriptor)
1513 continue;
1514 const OUString sIconURL (
1515 bIsHighContrastModeActive
1516 ? xPanelDescriptor->msHighContrastTitleBarIconURL
1517 : xPanelDescriptor->msTitleBarIconURL);
1518 rxPanel->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
1519 }
1520}
1521
1523{
1524 if (mpCurrentDeck)
1525 {
1526 if (!IsDeckOpen())
1528 mpCurrentDeck->ShowPanel(rPanel);
1529 }
1530}
1531
1533{
1535 mpResourceManager->GetMatchingDecks (aDecks,
1538 mxFrame->getController());
1539 return aDecks;
1540}
1541
1543{
1545
1546 mpResourceManager->GetMatchingPanels(aPanels,
1548 rDeckId,
1549 mxFrame->getController());
1550 return aPanels;
1551}
1552
1553void SidebarController::updateModel(const css::uno::Reference<css::frame::XModel>& xModel)
1554{
1555 mpResourceManager->UpdateModel(xModel);
1556}
1557
1559{
1560 if (mpSplitWindow)
1561 mpSplitWindow->FadeOut();
1562}
1563
1565{
1566 if (mpSplitWindow)
1567 mpSplitWindow->FadeIn();
1568}
1569
1571{
1572 tools::Rectangle aRect;
1573 if (mpCurrentDeck)
1574 {
1575 if (DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar())
1576 {
1577 aRect = pTitleBar->GetDragArea();
1578 }
1579 }
1580 return aRect;
1581}
1582
1583void SidebarController::frameAction(const css::frame::FrameActionEvent& rEvent)
1584{
1585 if (rEvent.Frame == mxFrame)
1586 {
1587 if (rEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING)
1588 unregisterSidebarForFrame(mxFrame->getController());
1589 else if (rEvent.Action == css::frame::FrameAction_COMPONENT_REATTACHED)
1590 registerSidebarForFrame(mxFrame->getController());
1591 }
1592}
1593
1595{
1596 // Impress shutdown : context (frame) is disposed before sidebar disposing
1597 // calc writer : context (frame) is disposed after sidebar disposing
1598 // so need to test if GetCurrentContext is still valid regarding msApplication
1599 if (GetCurrentContext().msApplication != "none")
1600 {
1601 mpResourceManager->SaveDecksSettings(GetCurrentContext());
1602 mpResourceManager->SaveLastActiveDeck(GetCurrentContext(), msCurrentDeckId);
1603 }
1604}
1605
1606static bool isChartOrMathContext(const Context& context)
1607{
1608 return context.msApplication == "com.sun.star.chart2.ChartDocument"
1609 || context.msApplication == "com.sun.star.formula.FormulaProperties";
1610}
1611
1613{
1615 return true; // We are not yet changed, but in the process
1616
1618}
1619
1621{
1622 if (!pViewShell)
1623 return nullptr;
1624
1625 Reference<css::frame::XController2> xController(pViewShell->GetController(), UNO_QUERY);
1626 if (!xController.is())
1627 return nullptr;
1628
1629 // Make sure there is a model behind the controller, otherwise getSidebar() can crash.
1630 if (!xController->getModel().is())
1631 return nullptr;
1632
1633 Reference<css::ui::XSidebarProvider> xSidebarProvider = xController->getSidebar();
1634 if (!xSidebarProvider.is())
1635 return nullptr;
1636
1637 Reference<css::ui::XSidebar> xSidebar = xSidebarProvider->getSidebar();
1638 if (!xSidebar.is())
1639 return nullptr;
1640
1641 return dynamic_cast<sfx2::sidebar::SidebarController*>(xSidebar.get());
1642}
1643
1644} // end of namespace sfx2::sidebar
1645
1646/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::unique_ptr< weld::Image > m_xWidget
virtual void dispose() override
void setX(tools::Long nX)
constexpr tools::Long X() const
SfxBindings & GetBindings() const
Definition: dockwin.hxx:96
const css::uno::Reference< css::frame::XFrame > & GetFrameInterface() const
Definition: frame.cxx:515
SfxViewShell * GetViewShell() const
Returns the SfxViewShell in which they are located in the subshells.
Definition: shell.cxx:129
bool GetWindowPos(const SfxDockingWindow *pWindow, sal_uInt16 &rLine, sal_uInt16 &rPos) const
Definition: splitwin.cxx:818
void MoveWindow(SfxDockingWindow *pDockWin, const Size &rSize, sal_uInt16 nLine, sal_uInt16 nPos, bool bNewLine)
Definition: splitwin.cxx:537
bool IsFadeIn() const
Definition: splitwin.cxx:1010
tools::Long GetLineSize(sal_uInt16) const
Definition: splitwin.cxx:868
virtual void FadeIn() override
Definition: splitwin.cxx:1142
sal_uInt16 GetWindowCount(sal_uInt16 nLine) const
Definition: splitwin.cxx:880
SfxFrame & GetFrame() const
Definition: viewfrm.cxx:2782
One SfxViewShell more or less represents one edit window for a document, there can be multiple ones f...
Definition: viewsh.hxx:165
bool isLOKMobilePhone() const
Check if the lok client is running on a mobile device.
Definition: viewsh.hxx:470
css::uno::Reference< css::frame::XController > GetController() const
Definition: viewsh.cxx:2662
virtual void libreOfficeKitViewCallback(int nType, const OString &pPayload) const override
Invokes the registered callback, if there are any.
Definition: viewsh.cxx:2244
static SAL_WARN_UNUSED_RESULT SfxViewShell * Current()
Definition: viewsh.cxx:1848
constexpr tools::Long Height() const
void setWidth(tools::Long nWidth)
constexpr tools::Long Width() const
WindowAlign GetAlign() const
sal_uInt16 GetItemId(vcl::Window *pWindow) const
void SetItemSizeRange(sal_uInt16 nId, const Range &rRange)
sal_uInt16 GetSet(sal_uInt16 nId) const
static UITestLogger & getInstance()
void logEvent(const EventDescription &rDescription)
static vcl::Window * GetWindow(const css::uno::Reference< css::awt::XWindow > &rxWindow)
void disposeAndClear()
void reset(reference_type *pBody)
static VclPtr< reference_type > Create(Arg &&... arg)
bool put(const OUString &_rValueName, const VALUE_TYPE &_rValue)
css::uno::Sequence< css::beans::PropertyValue > getPropertyValues() const
virtual void SAL_CALL dispose() noexcept final override
OUString msApplication
Definition: Context.hxx:30
void SetCloserVisible(const bool bIsCloserVisible)
virtual void SetTitle(const OUString &rsTitle) override
static void LOKSendSidebarFullUpdate()
Definition: Deck.cxx:40
void SetPanels(const SharedPanelContainer &rPanels)
void Clear()
Forget all panels and buttons.
void SetDeck(Deck *pDeck)
void SetMoreOptionsCommand(const OUString &rsCommandName, const css::uno::Reference< css::frame::XFrame > &rxFrame, const css::uno::Reference< css::frame::XController > &rxController)
Multiple panels form a single deck.
Definition: Panel.hxx:54
Read the content of the Sidebar.xcu file and provide access methods so that the sidebar can easily de...
std::vector< PanelContextDescriptor > PanelContextDescriptorContainer
std::shared_ptr< DeckDescriptor > GetDeckDescriptor(std::u16string_view rsDeckId) const
std::vector< DeckContextDescriptor > DeckContextDescriptorContainer
std::shared_ptr< PanelDescriptor > GetPanelDescriptor(std::u16string_view rsPanelId) const
static sal_Int32 GetDefaultWidth(vcl::Window const *pWindow)
static rtl::Reference< SidebarController > create(SidebarDockingWindow *pParentWindow, const SfxViewFrame *pViewFrame)
sal_Int32 SetChildWindowWidth(const sal_Int32 nNewWidth)
Set the child window container to a new width.
virtual void SAL_CALL notifyContextChangeEvent(const css::ui::ContextChangeEventObject &rEvent) override
::std::optional< bool > mbIsDeckRequestedOpen
Two flags control whether the deck is displayed or if only the tab bar remains visible.
bool CanModifyChildWindowWidth()
The close of the deck changes the width of the child window.
static const sal_Int32 SwitchFlag_ForceSwitch
void notifyDeckTitle(std::u16string_view targetDeckId)
tools::Rectangle GetDeckDragArea() const
virtual void SAL_CALL requestLayout() override
void ProcessNewWidth(const sal_Int32 nNewWidth)
css::uno::Reference< css::beans::XPropertySet > mxThemePropertySet
void SwitchToDeck(std::u16string_view rsDeckId)
css::uno::Reference< css::frame::XFrame > mxFrame
sal_Int32 mnRequestedForceFlags
Use a combination of SwitchFlag_* as value.
void unregisterSidebarForFrame(const css::uno::Reference< css::frame::XController > &xFrame)
void RequestCloseDeck()
Show only the tab bar, not the deck.
::std::optional< bool > mbIsDeckOpen
sal_Int32 mnSavedSidebarWidth
Before the deck is closed the sidebar width is saved into this variable, so that it can be restored w...
css::uno::Reference< css::frame::XDispatch > mxReadOnlyModeDispatch
static SidebarController * GetSidebarControllerForView(const SfxViewShell *pViewShell)
const Context & GetCurrentContext() const
void CreatePanels(std::u16string_view rDeckId, const Context &rContext)
void updateModel(const css::uno::Reference< css::frame::XModel > &xModel)
virtual void SAL_CALL frameAction(const css::frame::FrameActionEvent &rEvent) override
static SidebarController * GetSidebarControllerForFrame(const css::uno::Reference< css::frame::XFrame > &rxFrame)
Return the SidebarController object that is associated with the given XFrame.
static const sal_Int32 SwitchFlag_ForceNewDeck
sal_Int32 mnWidthOnSplitterButtonDown
When the user moves the splitter then we remember the width at that time.
void ShowPanel(const Panel &rPanel)
Typically called when a panel is focused via keyboard.
void UpdateCloseIndicator(const bool bIsIndicatorVisible)
void UpdateConfigurations()
Make maRequestedContext the current context.
css::uno::Reference< css::ui::XUIElement > CreateUIElement(const css::uno::Reference< css::awt::XWindow > &rxWindow, const OUString &rsImplementationURL, const bool bWantsCanvas, const Context &rContext)
ResourceManager::DeckContextDescriptorContainer GetMatchingDecks()
virtual void SAL_CALL disposing(const css::lang::EventObject &rEventObject) override
void registerSidebarForFrame(const css::uno::Reference< css::frame::XController > &xFrame)
static const sal_Int32 SwitchFlag_NoForce
In some situations it is necessary to force an update of the current deck and its panels.
SidebarController(const SidebarController &)=delete
void ShowPopupMenu(weld::Menu &rMainMenu, weld::Menu &rSubMenu, const ::std::vector< TabBar::DeckMenuData > &rMenuData) const
std::unique_ptr< ResourceManager > mpResourceManager
void UpdateTitleBarIcons()
Update the icons displayed in the title bars of the deck and the panels.
virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent &rEvent) override
VclPtr< vcl::Window > mpCloseIndicator
Control that is temporarily used as replacement for the deck to indicate that when the current mouse ...
void RequestOpenDeck()
Open the deck area and restore the parent window to its old width.
virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent &rEvent) override
void OpenThenToggleDeck(const OUString &rsDeckId)
VclPtr< SidebarDockingWindow > mpParentWindow
std::shared_ptr< Panel > CreatePanel(std::u16string_view rsPanelId, weld::Widget *pParentWindow, const bool bIsInitiallyExpanded, const Context &rContext, const VclPtr< Deck > &pDeck)
css::uno::Reference< css::frame::XController > mxCurrentController
bool IsDeckOpen(const sal_Int32 nIndex=-1)
VclPtr< SfxSplitWindow > mpSplitWindow
void PopulatePopupMenus(weld::Menu &rMainButton, weld::Menu &rSubMenu, const ::std::vector< TabBar::DeckMenuData > &rMenuData) const
void OpenThenSwitchToDeck(std::u16string_view rsDeckId)
bool IsDeckVisible(std::u16string_view rsDeckId)
Returns true when the given deck is the currently visible deck.
static const sal_Int32 SwitchFlag_ForceNewPanels
ResourceManager::PanelContextDescriptorContainer GetMatchingPanels(std::u16string_view rDeckId)
void CreateDeck(std::u16string_view rDeckId)
The tab bar is the container for the individual tabs.
Definition: TabBar.hxx:41
static sal_Int32 GetDefaultWidth()
Definition: TabBar.cxx:109
static Color GetColor(const ThemeItem eItem)
Definition: Theme.cxx:49
static css::uno::Reference< css::beans::XPropertySet > GetPropertySet()
Definition: Theme.cxx:168
static void HandleDataChange()
Definition: Theme.cxx:76
static bool IsHighContrastMode()
Definition: Theme.cxx:70
bool GetVisible() const
Definition: TitleBar.cxx:62
static css::uno::Reference< css::graphic::XGraphic > GetImage(const OUString &rsImageURL, const OUString &rsHighContrastImageURL, const css::uno::Reference< css::frame::XFrame > &rxFrame)
static OUString GetModuleName(const css::uno::Reference< css::frame::XController > &rxFrame)
Definition: Tools.cxx:92
static css::uno::Reference< css::frame::XDispatch > GetDispatch(const css::uno::Reference< css::frame::XFrame > &rxFrame, const css::util::URL &rURL)
Definition: Tools.cxx:83
static css::util::URL GetURL(const OUString &rsCommand)
Definition: Tools.cxx:71
void put(std::u16string_view pPropName, const OUString &rPropValue)
OString finishAndGetAsOString()
static Application GetApplicationEnum(const OUString &rsApplicationName)
static const OUString & GetContextName(const Context eContext)
static Context GetContextEnum(const OUString &rsContextName)
virtual Size GetSizePixel() const
void connect_activate(const Link< const OUString &, void > &rLink)
virtual void set_visible(const OUString &rIdent, bool bVisible)=0
virtual void insert(int pos, const OUString &rId, const OUString &rStr, const OUString *pIconName, VirtualDevice *pImageSurface, const css::uno::Reference< css::graphic::XGraphic > &rImage, TriState eCheckRadioFalse)=0
virtual void set_sensitive(const OUString &rIdent, bool bSensitive)=0
virtual void set_active(const OUString &rIdent, bool bActive)=0
#define TOOLS_WARN_EXCEPTION(area, stream)
Reference< XDispatch > xDispatch
URL aURL
float u
TRISTATE_FALSE
TRISTATE_TRUE
sal_Int32 nIndex
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
@ Exception
uno::Reference< ui::XContextChangeEventListener > GetFirstListenerWith(css::uno::Reference< css::uno::XComponentContext > const &xComponentContext, uno::Reference< uno::XInterface > const &xEventFocus, std::function< bool(uno::Reference< ui::XContextChangeEventListener > const &)> const &rPredicate)
void Create(SwFormatVertOrient &rItem, SvStream &rStrm, sal_uInt16 nVersionAbusedAsSize)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
static bool isChartOrMathContext(const Context &context)
std::vector< std::shared_ptr< Panel > > SharedPanelContainer
Definition: Panel.hxx:118
IMPL_LINK(FocusManager, KeyInputHdl, const KeyEvent &, rKeyEvent, bool)
void dispose()
long Long
sal_Int16 nId
std::map< OUString, OUString > aParameters
Reference< XController > xController
Reference< XModel > xModel
WinBits const WB_SIZEABLE
WindowAlign