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