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