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>
21 #include <boost/property_tree/json_parser.hpp>
23 #include <sfx2/sidebar/Deck.hxx>
24 #include <sidebar/DeckDescriptor.hxx>
25 #include <sidebar/DeckTitleBar.hxx>
26 #include <sfx2/sidebar/Panel.hxx>
27 #include <sidebar/PanelDescriptor.hxx>
28 #include <sidebar/PanelTitleBar.hxx>
29 #include <sfx2/sidebar/TabBar.hxx>
30 #include <sfx2/sidebar/Theme.hxx>
31 #include <sfx2/sidebar/SidebarChildWindow.hxx>
32 #include <sidebar/Tools.hxx>
33 #include <sfx2/sidebar/SidebarDockingWindow.hxx>
34 #include <com/sun/star/ui/XSidebarProvider.hpp>
35 #include <com/sun/star/frame/XController2.hpp>
36 #include <sfx2/sidebar/Context.hxx>
37 #include <sfx2/viewsh.hxx>
40 #include <framework/ContextChangeEventMultiplexerTunnel.hxx>
41 #include <vcl/EnumContext.hxx>
42 #include <vcl/uitest/logger.hxx>
43 #include <vcl/uitest/eventdescription.hxx>
44 #include <vcl/svapp.hxx>
45 #include <splitwin.hxx>
46 #include <comphelper/diagnose_ex.hxx>
47 #include <tools/json_writer.hxx>
48 #include <tools/link.hxx>
49 #include <toolkit/helper/vclunohelper.hxx>
50 #include <comphelper/processfactory.hxx>
51 #include <comphelper/namedvaluecollection.hxx>
52 #include <comphelper/lok.hxx>
53 #include <sal/log.hxx>
54 #include <officecfg/Office/UI/Sidebar.hxx>
55 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
56 #include <o3tl/string_view.hxx>
58 #include <com/sun/star/awt/XWindowPeer.hpp>
59 #include <com/sun/star/frame/XDispatch.hpp>
60 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
61 #include <com/sun/star/ui/ContextChangeEventObject.hpp>
62 #include <com/sun/star/ui/theUIElementFactoryManager.hpp>
63 #include <com/sun/star/util/URL.hpp>
64 #include <com/sun/star/rendering/XSpriteCanvas.hpp>
66 #include <bitmaps.hlst>
69 using namespace css::uno
;
73 constexpr OUString gsReadOnlyCommandName
= u
".uno:EditDoc"_ustr
;
74 const sal_Int32
gnWidthCloseThreshold (70);
75 const sal_Int32
gnWidthOpenThreshold (40);
77 std::string
UnoNameFromDeckId(std::u16string_view rsDeckId
, const sfx2::sidebar::Context
& context
)
79 if (rsDeckId
== u
"SdCustomAnimationDeck")
80 return ".uno:CustomAnimation";
82 if (rsDeckId
== u
"PropertyDeck")
83 return vcl::EnumContext::Application::Impress
== vcl::EnumContext::GetApplicationEnum(context
.msApplication
) ? ".uno:ModifyPage" : ".uno:Sidebar";
85 if (rsDeckId
== u
"SdLayoutsDeck")
86 return ".uno:ModifyPage";
88 if (rsDeckId
== u
"SdSlideTransitionDeck")
89 return ".uno:SlideChangeWindow";
91 if (rsDeckId
== u
"SdAllMasterPagesDeck")
92 return ".uno:MasterSlidesPanel";
94 if (rsDeckId
== u
"SdMasterPagesDeck")
95 return ".uno:MasterSlidesPanel";
97 if (rsDeckId
== u
"GalleryDeck")
98 return ".uno:Gallery";
100 OString sUno
= ".uno:SidebarDeck." + OUStringToOString(rsDeckId
, RTL_TEXTENCODING_ASCII_US
);
101 return std::string(sUno
);
105 namespace sfx2::sidebar
{
109 /** When in doubt, show this deck.
111 constexpr OUString
gsDefaultDeckId(u
"PropertyDeck"_ustr
);
114 SidebarController::SidebarController (
115 SidebarDockingWindow
* pParentWindow
,
116 const SfxViewFrame
* pViewFrame
)
117 : mpParentWindow(pParentWindow
),
118 mpViewFrame(pViewFrame
),
119 mxFrame(pViewFrame
->GetFrame().GetFrameInterface()),
120 mpTabBar(VclPtr
<TabBar
>::Create(
123 [this](const OUString
& rsDeckId
) { return this->OpenThenToggleDeck(rsDeckId
); },
124 [this](weld::Menu
& rMainMenu
, weld::Menu
& rSubMenu
) { return this->ConnectMenuActivateHandlers(rMainMenu
, rSubMenu
); },
126 maCurrentContext(OUString(), OUString()),
127 maRequestedContext(OUString(), OUString()),
128 mnRequestedForceFlags(SwitchFlag_NoForce
),
129 mbMinimumSidebarWidth(officecfg::Office::UI::Sidebar::General::MinimumWidth::get()),
130 msCurrentDeckId(gsDefaultDeckId
),
131 maPropertyChangeForwarder(mpViewFrame
, [this](){ return this->BroadcastPropertyChange(); }),
132 maContextChangeUpdate(mpViewFrame
, [this](){ return this->UpdateConfigurations(); }),
133 mbFloatingDeckClosed(!pParentWindow
->IsFloatingMode()),
134 mnSavedSidebarWidth(pParentWindow
->GetSizePixel().Width()),
135 maFocusManager([this](const Panel
& rPanel
){ return this->ShowPanel(rPanel
); }),
136 mbIsDocumentReadOnly(false),
137 mpSplitWindow(nullptr),
138 mnWidthOnSplitterButtonDown(0)
140 mnMaximumSidebarWidth
= officecfg::Office::UI::Sidebar::General::MaximumWidth::get() * mpTabBar
->GetDPIScaleFactor();
141 // Decks and panel collections for this sidebar
142 mpResourceManager
= std::make_unique
<ResourceManager
>();
145 rtl::Reference
<SidebarController
> SidebarController::create(SidebarDockingWindow
* pParentWindow
,
146 const SfxViewFrame
* pViewFrame
)
148 rtl::Reference
<SidebarController
> instance(new SidebarController(pParentWindow
, pViewFrame
));
150 const css::uno::Reference
<css::frame::XFrame
>& rxFrame
= pViewFrame
->GetFrame().GetFrameInterface();
151 instance
->registerSidebarForFrame(rxFrame
->getController());
152 rxFrame
->addFrameActionListener(instance
);
153 // Listen for window events.
154 instance
->mpParentWindow
->AddEventListener(LINK(instance
.get(), SidebarController
, WindowEventHandler
));
156 // Listen for theme property changes.
157 instance
->mxThemePropertySet
= Theme::GetPropertySet();
158 instance
->mxThemePropertySet
->addPropertyChangeListener(
160 static_cast<css::beans::XPropertyChangeListener
*>(instance
.get()));
162 // Get the dispatch object as preparation to listen for changes of
163 // the read-only state.
164 const util::URL
aURL (Tools::GetURL(gsReadOnlyCommandName
));
165 instance
->mxReadOnlyModeDispatch
= Tools::GetDispatch(rxFrame
, aURL
);
166 if (instance
->mxReadOnlyModeDispatch
.is())
167 instance
->mxReadOnlyModeDispatch
->addStatusListener(instance
, aURL
);
169 //first UpdateConfigurations call will SwitchToDeck
174 SidebarController::~SidebarController()
178 SidebarController
* SidebarController::GetSidebarControllerForFrame (
179 const css::uno::Reference
<css::frame::XFrame
>& rxFrame
)
181 uno::Reference
<frame::XController
> const xController(rxFrame
->getController());
182 if (!xController
.is()) // this may happen during dispose of Draw controller but perhaps it's a bug
184 SAL_WARN("sfx.sidebar", "GetSidebarControllerForFrame: frame has no XController");
187 uno::Reference
<ui::XContextChangeEventListener
> const xListener(
188 framework::GetFirstListenerWith(
189 ::comphelper::getProcessComponentContext(),
191 [] (uno::Reference
<uno::XInterface
> const& xRef
)
192 { return nullptr != dynamic_cast<SidebarController
*>(xRef
.get()); }
195 return dynamic_cast<SidebarController
*>(xListener
.get());
198 void SidebarController::registerSidebarForFrame(const css::uno::Reference
<css::frame::XController
>& xController
)
200 // Listen for context change events.
201 css::uno::Reference
<css::ui::XContextChangeEventMultiplexer
> xMultiplexer (
202 css::ui::ContextChangeEventMultiplexer::get(
203 ::comphelper::getProcessComponentContext()));
204 xMultiplexer
->addContextChangeEventListener(
205 static_cast<css::ui::XContextChangeEventListener
*>(this),
209 void SidebarController::unregisterSidebarForFrame(const css::uno::Reference
<css::frame::XController
>& xController
)
214 css::uno::Reference
<css::ui::XContextChangeEventMultiplexer
> xMultiplexer (
215 css::ui::ContextChangeEventMultiplexer::get(
216 ::comphelper::getProcessComponentContext()));
217 xMultiplexer
->removeContextChangeEventListener(
218 static_cast<css::ui::XContextChangeEventListener
*>(this),
222 void SidebarController::disposeDecks()
224 SolarMutexGuard aSolarMutexGuard
;
226 if (comphelper::LibreOfficeKit::isActive())
228 if (const SfxViewShell
* pViewShell
= mpViewFrame
->GetViewShell())
230 const std::string hide
= UnoNameFromDeckId(msCurrentDeckId
, GetCurrentContext());
233 // Be consistent with SwitchToDeck(), so both places emit JSON.
234 boost::property_tree::ptree aTree
;
235 aTree
.put("commandName", hide
);
236 aTree
.put("state", "false");
237 std::stringstream aStream
;
238 boost::property_tree::write_json(aStream
, aTree
);
239 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
240 OString(aStream
.str()));
245 mpParentWindow
->ReleaseLOKNotifier();
248 mpCurrentDeck
.clear();
249 maFocusManager
.Clear();
250 mpResourceManager
->disposeDecks();
255 class CloseIndicator final
: public InterimItemWindow
258 CloseIndicator(vcl::Window
* pParent
)
259 : InterimItemWindow(pParent
, u
"svt/ui/fixedimagecontrol.ui"_ustr
, u
"FixedImageControl"_ustr
)
260 , m_xWidget(m_xBuilder
->weld_image(u
"image"_ustr
))
262 InitControlBase(m_xWidget
.get());
264 m_xWidget
->set_from_icon_name(SIDEBAR_CLOSE_INDICATOR
);
266 SetSizePixel(get_preferred_size());
268 SetBackground(Theme::GetColor(Theme::Color_DeckBackground
));
271 virtual ~CloseIndicator() override
276 virtual void dispose() override
279 InterimItemWindow::dispose();
283 std::unique_ptr
<weld::Image
> m_xWidget
;
287 void SidebarController::disposing(std::unique_lock
<std::mutex
>&)
289 SolarMutexGuard aSolarMutexGuard
;
291 mpCloseIndicator
.disposeAndClear();
293 maFocusManager
.Clear();
294 mpTabBar
.disposeAndClear();
299 ResourceManager::DeckContextDescriptorContainer aDecks
;
301 mpResourceManager
->GetMatchingDecks (
304 IsDocumentReadOnly(),
305 mxFrame
->getController());
307 for (const auto& rDeck
: aDecks
)
309 std::shared_ptr
<DeckDescriptor
> deckDesc
= mpResourceManager
->GetDeckDescriptor(rDeck
.msId
);
311 VclPtr
<Deck
> aDeck
= deckDesc
->mpDeck
;
313 aDeck
.disposeAndClear();
316 maContextChangeUpdate
.CancelRequest();
318 if (mxReadOnlyModeDispatch
.is())
319 mxReadOnlyModeDispatch
->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName
));
321 if (mxThemePropertySet
.is())
322 mxThemePropertySet
->removePropertyChangeListener(
324 static_cast<css::beans::XPropertyChangeListener
*>(this));
326 if (mpParentWindow
!= nullptr)
328 mpParentWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
329 mpParentWindow
= nullptr;
332 if (mpSplitWindow
!= nullptr)
334 mpSplitWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
335 mpSplitWindow
= nullptr;
338 mxFrame
->removeFrameActionListener(this);
340 uno::Reference
<css::frame::XController
> xController
= mxFrame
->getController();
341 if (!xController
.is())
342 xController
= mxCurrentController
;
344 unregisterSidebarForFrame(xController
);
347 void SAL_CALL
SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject
& rEvent
)
349 SolarMutexGuard aSolarMutexGuard
;
351 // Update to the requested new context asynchronously to avoid
352 // subtle errors caused by SFX2 which in rare cases can not
353 // properly handle a synchronous update.
355 maRequestedContext
= Context(
356 rEvent
.ApplicationName
,
359 if (maRequestedContext
!= maCurrentContext
)
361 mxCurrentController
.set(rEvent
.Source
, css::uno::UNO_QUERY
);
362 maContextChangeUpdate
.RequestCall(); // async call, not a prob
365 // TODO: this call is redundant but mandatory for unit test to update context on document loading
366 if (!comphelper::LibreOfficeKit::isActive())
367 UpdateConfigurations();
371 void SAL_CALL
SidebarController::disposing (const css::lang::EventObject
& )
376 void SAL_CALL
SidebarController::propertyChange (const css::beans::PropertyChangeEvent
& )
378 SolarMutexGuard aSolarMutexGuard
;
380 maPropertyChangeForwarder
.RequestCall(); // async call, not a prob
385 void SAL_CALL
SidebarController::statusChanged (const css::frame::FeatureStateEvent
& rEvent
)
387 SolarMutexGuard aSolarMutexGuard
;
389 bool bIsReadWrite (true);
390 if (rEvent
.IsEnabled
)
391 rEvent
.State
>>= bIsReadWrite
;
393 if (mbIsDocumentReadOnly
!= !bIsReadWrite
)
395 mbIsDocumentReadOnly
= !bIsReadWrite
;
397 // Force the current deck to update its panel list.
398 if ( ! mbIsDocumentReadOnly
)
399 SwitchToDefaultDeck();
401 mnRequestedForceFlags
|= SwitchFlag_ForceSwitch
;
402 maContextChangeUpdate
.RequestCall(); // async call, ok to call
403 // with held solarmutex
407 void SAL_CALL
SidebarController::requestLayout()
409 SolarMutexGuard aSolarMutexGuard
;
411 sal_Int32 nMinimalWidth
= 0;
412 if (mpCurrentDeck
&& !mpCurrentDeck
->isDisposed())
414 mpCurrentDeck
->RequestLayout();
415 nMinimalWidth
= mbMinimumSidebarWidth
? mpCurrentDeck
->GetMinimalWidth() : 0;
417 RestrictWidth(nMinimalWidth
);
420 void SidebarController::BroadcastPropertyChange()
422 mpParentWindow
->Invalidate(InvalidateFlags::Children
);
425 void SidebarController::NotifyResize()
429 OSL_ASSERT(mpTabBar
!=nullptr);
433 const sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth();
435 const sal_Int32
nWidth(mpParentWindow
->GetSizePixel().Width());
436 const sal_Int32
nHeight(mpParentWindow
->GetSizePixel().Height());
438 mbIsDeckOpen
= (nWidth
> nTabBarDefaultWidth
);
440 if (mnSavedSidebarWidth
<= 0)
441 mnSavedSidebarWidth
= nWidth
;
444 const bool bIsOpening (nWidth
> mnWidthOnSplitterButtonDown
);
446 bIsDeckVisible
= nWidth
>= nTabBarDefaultWidth
+ gnWidthOpenThreshold
;
448 bIsDeckVisible
= nWidth
>= nTabBarDefaultWidth
+ gnWidthCloseThreshold
;
449 mbIsDeckRequestedOpen
= bIsDeckVisible
;
450 UpdateCloseIndicator(!bIsDeckVisible
);
452 if (mpCurrentDeck
&& !mpCurrentDeck
->isDisposed())
454 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
455 WindowAlign eAlign
= pSplitWindow
? pSplitWindow
->GetAlign() : WindowAlign::Right
;
456 tools::Long nDeckX
, nTabX
;
457 if (eAlign
== WindowAlign::Left
) // attach the Sidebar towards the left-side of screen
459 nDeckX
= nTabBarDefaultWidth
;
462 else // attach the Sidebar towards the right-side of screen
465 nTabX
= nWidth
- nTabBarDefaultWidth
;
468 // Place the deck first.
471 if (comphelper::LibreOfficeKit::isActive())
473 // We want to let the layouter use up as much of the
474 // height as necessary to make sure no scrollbar is
475 // visible. This only works when there are no greedy
476 // panes that fill up all available area. So we only
477 // use this for the PropertyDeck, which has no such
478 // panes, while most other do. This is fine, since
479 // it's the PropertyDeck that really has many panes
480 // that can collapse or expand. For others, limit
481 // the height to something sensible.
482 const sal_Int32 nExtHeight
= (msCurrentDeckId
== "PropertyDeck" ? 2000 : 600);
483 // No TabBar in LOK (use nWidth in full).
484 mpCurrentDeck
->setPosSizePixel(nDeckX
, 0, nWidth
, nExtHeight
);
487 mpCurrentDeck
->setPosSizePixel(nDeckX
, 0, nWidth
- nTabBarDefaultWidth
, nHeight
);
488 mpCurrentDeck
->Show();
489 mpCurrentDeck
->RequestLayout();
490 mpTabBar
->HighlightDeck(mpCurrentDeck
->GetId());
493 mpCurrentDeck
->Hide();
495 // Now place the tab bar.
496 mpTabBar
->setPosSizePixel(nTabX
, 0, nTabBarDefaultWidth
, nHeight
);
497 if (!comphelper::LibreOfficeKit::isActive())
498 mpTabBar
->Show(); // Don't show TabBar in LOK.
501 // Determine if the closer of the deck can be shown.
502 sal_Int32 nMinimalWidth
= 0;
503 if (mpCurrentDeck
&& !mpCurrentDeck
->isDisposed())
505 DeckTitleBar
* pTitleBar
= mpCurrentDeck
->GetTitleBar();
506 if (pTitleBar
&& pTitleBar
->GetVisible())
507 pTitleBar
->SetCloserVisible(CanModifyChildWindowWidth());
508 nMinimalWidth
= mbMinimumSidebarWidth
? mpCurrentDeck
->GetMinimalWidth() : 0;
511 RestrictWidth(nMinimalWidth
);
514 void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth
)
516 if ( ! mbIsDeckRequestedOpen
.has_value())
519 if (*mbIsDeckRequestedOpen
)
521 // Deck became large enough to be shown. Show it.
522 mnSavedSidebarWidth
= nNewWidth
;
523 // Store nNewWidth to mnWidthOnSplitterButtonDown when dragging sidebar Splitter
524 mnWidthOnSplitterButtonDown
= nNewWidth
;
530 // Deck became too small. Close it completely.
531 // If window is wider than the tab bar then mark the deck as being visible, even when it is not.
532 // This is to trigger an adjustment of the width to the width of the tab bar.
536 if (mnWidthOnSplitterButtonDown
> TabBar::GetDefaultWidth())
537 mnSavedSidebarWidth
= mnWidthOnSplitterButtonDown
;
541 void SidebarController::SyncUpdate()
543 maPropertyChangeForwarder
.Sync();
544 maContextChangeUpdate
.Sync();
547 void SidebarController::UpdateConfigurations()
549 if (maCurrentContext
== maRequestedContext
550 && mnRequestedForceFlags
== SwitchFlag_NoForce
)
553 if ((maCurrentContext
.msApplication
!= "none") &&
554 !maCurrentContext
.msApplication
.isEmpty())
556 mpResourceManager
->SaveDecksSettings(maCurrentContext
);
557 mpResourceManager
->SetLastActiveDeck(maCurrentContext
, msCurrentDeckId
);
560 // get last active deck for this application on first update
561 if (!maRequestedContext
.msApplication
.isEmpty() &&
562 (maCurrentContext
.msApplication
!= maRequestedContext
.msApplication
))
564 OUString sLastActiveDeck
= mpResourceManager
->GetLastActiveDeck( maRequestedContext
);
565 if (!sLastActiveDeck
.isEmpty())
566 msCurrentDeckId
= sLastActiveDeck
;
569 maCurrentContext
= maRequestedContext
;
571 mpResourceManager
->InitDeckContext(GetCurrentContext());
573 // Find the set of decks that could be displayed for the new context.
574 ResourceManager::DeckContextDescriptorContainer aDecks
;
576 css::uno::Reference
<css::frame::XController
> xController
= mxCurrentController
.is() ? mxCurrentController
: mxFrame
->getController();
578 mpResourceManager
->GetMatchingDecks (
581 mbIsDocumentReadOnly
,
584 maFocusManager
.Clear();
586 // Notify the tab bar about the updated set of decks.
587 mpTabBar
->SetDecks(aDecks
);
589 // Find the new deck. By default that is the same as the old
590 // one. If that is not set or not enabled, then choose the
591 // first enabled deck (which is PropertyDeck).
593 for (const auto& rDeck
: aDecks
)
595 if (rDeck
.mbIsEnabled
)
597 if (rDeck
.msId
== msCurrentDeckId
)
599 sNewDeckId
= msCurrentDeckId
;
602 else if (sNewDeckId
.getLength() == 0)
603 sNewDeckId
= rDeck
.msId
;
607 if (sNewDeckId
.getLength() == 0)
609 // We did not find a valid deck.
614 std::shared_ptr
<DeckDescriptor
> xDescriptor
= mpResourceManager
->GetDeckDescriptor(sNewDeckId
);
618 SwitchToDeck(*xDescriptor
, maCurrentContext
);
624 void collectUIInformation(const OUString
& rDeckId
)
626 EventDescription aDescription
;
627 aDescription
.aAction
= "SIDEBAR";
628 aDescription
.aParent
= "MainWindow";
629 aDescription
.aParameters
= {{"PANEL", rDeckId
}};
630 aDescription
.aKeyWord
= "CurrentApp";
632 UITestLogger::getInstance().logEvent(aDescription
);
637 bool SidebarController::IsDocked() const { return !mpParentWindow
->IsFloatingMode(); }
639 void SidebarController::OpenThenToggleDeck (
640 const OUString
& rsDeckId
)
642 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
643 if ( pSplitWindow
&& !pSplitWindow
->IsFadeIn() )
644 // tdf#83546 Collapsed sidebar should expand first
645 pSplitWindow
->FadeIn();
646 else if ( IsDeckVisible( rsDeckId
) )
648 if( !WasFloatingDeckClosed() )
650 // tdf#88241 Summoning an undocked sidebar a second time should close sidebar
651 mpParentWindow
->Close();
656 // tdf#67627 Clicking a second time on a Deck icon will close the Deck
662 // before SwitchToDeck which may cause the rsDeckId string to be released
663 collectUIInformation(rsDeckId
);
664 SwitchToDeck(rsDeckId
);
666 // Make sure the sidebar is wide enough to fit the requested content
667 if (mpCurrentDeck
&& mpTabBar
)
669 sal_Int32 nRequestedWidth
= mpCurrentDeck
->GetMinimalWidth() + TabBar::GetDefaultWidth();
670 // if sidebar was dragged
671 if(mnWidthOnSplitterButtonDown
> 0 && mnWidthOnSplitterButtonDown
> nRequestedWidth
){
672 SetChildWindowWidth(mnWidthOnSplitterButtonDown
);
674 // tdf#150639 The mnWidthOnSplitterButtonDown is initialized to 0 at program start.
675 // This makes every call to take the else case until the user manually changes the
676 // width, but some decks such as Master Slides have the mnMinimalWidth too low which
677 // makes them too narrow for the content they should display to the user.
678 SetChildWindowWidth(nRequestedWidth
> mnSavedSidebarWidth
? nRequestedWidth
679 : mnSavedSidebarWidth
);
684 void SidebarController::OpenThenSwitchToDeck (
685 std::u16string_view rsDeckId
)
688 SwitchToDeck(rsDeckId
);
692 void SidebarController::SwitchToDefaultDeck()
694 SwitchToDeck(gsDefaultDeckId
);
697 void SidebarController::SwitchToDeck (
698 std::u16string_view rsDeckId
)
700 if ( msCurrentDeckId
!= rsDeckId
701 || ! mbIsDeckOpen
.has_value()
702 || mnRequestedForceFlags
!=SwitchFlag_NoForce
)
704 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= mpResourceManager
->GetDeckDescriptor(rsDeckId
);
708 SwitchToDeck(*xDeckDescriptor
, maCurrentContext
);
709 Deck::LOKSendSidebarFullUpdate();
714 void SidebarController::CreateDeck(std::u16string_view rDeckId
) {
715 CreateDeck(rDeckId
, maCurrentContext
);
718 void SidebarController::CreateDeck(std::u16string_view rDeckId
, const Context
& rContext
, bool bForceCreate
)
720 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= mpResourceManager
->GetDeckDescriptor(rDeckId
);
722 if (!xDeckDescriptor
)
725 VclPtr
<Deck
> aDeck
= xDeckDescriptor
->mpDeck
;
726 if (!aDeck
|| bForceCreate
)
729 aDeck
.disposeAndClear();
731 aDeck
= VclPtr
<Deck
>::Create(
734 [this]() { return this->RequestCloseDeck(); });
736 xDeckDescriptor
->mpDeck
= std::move(aDeck
);
737 CreatePanels(rDeckId
, rContext
);
740 void SidebarController::CreatePanels(std::u16string_view rDeckId
, const Context
& rContext
)
742 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= mpResourceManager
->GetDeckDescriptor(rDeckId
);
744 // init panels bounded to that deck, do not wait them being displayed as may be accessed through API
746 VclPtr
<Deck
> pDeck
= xDeckDescriptor
->mpDeck
;
748 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors
;
750 css::uno::Reference
<css::frame::XController
> xController
= mxCurrentController
.is() ? mxCurrentController
: mxFrame
->getController();
752 mpResourceManager
->GetMatchingPanels(
753 aPanelContextDescriptors
,
758 // Update the panel list.
759 const sal_Int32
nNewPanelCount (aPanelContextDescriptors
.size());
760 SharedPanelContainer aNewPanels
;
761 sal_Int32
nWriteIndex (0);
763 aNewPanels
.resize(nNewPanelCount
);
765 for (sal_Int32 nReadIndex
=0; nReadIndex
<nNewPanelCount
; ++nReadIndex
)
767 const ResourceManager::PanelContextDescriptor
& rPanelContexDescriptor (
768 aPanelContextDescriptors
[nReadIndex
]);
770 // Determine if the panel can be displayed.
771 const bool bIsPanelVisible (!mbIsDocumentReadOnly
|| rPanelContexDescriptor
.mbShowForReadOnlyDocuments
);
772 if ( ! bIsPanelVisible
)
775 auto xOldPanel(pDeck
->GetPanel(rPanelContexDescriptor
.msId
));
778 xOldPanel
->SetLurkMode(false);
779 aNewPanels
[nWriteIndex
] = xOldPanel
;
780 xOldPanel
->SetExpanded(rPanelContexDescriptor
.mbIsInitiallyVisible
);
785 auto aPanel
= CreatePanel(rPanelContexDescriptor
.msId
,
786 pDeck
->GetPanelParentWindow(),
787 rPanelContexDescriptor
.mbIsInitiallyVisible
,
792 aNewPanels
[nWriteIndex
] = std::move(aPanel
);
794 // Depending on the context we have to change the command
795 // for the "more options" dialog.
796 PanelTitleBar
* pTitleBar
= aNewPanels
[nWriteIndex
]->GetTitleBar();
799 pTitleBar
->SetMoreOptionsCommand(
800 rPanelContexDescriptor
.msMenuCommand
,
801 mxFrame
, xController
);
808 // mpCurrentPanels - may miss stuff (?)
809 aNewPanels
.resize(nWriteIndex
);
810 pDeck
->ResetPanels(std::move(aNewPanels
));
813 void SidebarController::SwitchToDeck (
814 const DeckDescriptor
& rDeckDescriptor
,
815 const Context
& rContext
)
817 if (comphelper::LibreOfficeKit::isActive())
819 if (const SfxViewShell
* pViewShell
= mpViewFrame
->GetViewShell())
821 std::vector
<std::pair
<std::string
, std::string
>> aStateChanges
;
822 if (msCurrentDeckId
!= rDeckDescriptor
.msId
)
824 const std::string hide
= UnoNameFromDeckId(msCurrentDeckId
, GetCurrentContext());
827 aStateChanges
.push_back({hide
, std::string("false")});
831 const std::string show
= UnoNameFromDeckId(rDeckDescriptor
.msId
, GetCurrentContext());
834 aStateChanges
.push_back({show
, std::string("true")});
837 for (const auto& rStateChange
: aStateChanges
)
839 boost::property_tree::ptree aTree
;
840 aTree
.put("locale", comphelper::LibreOfficeKit::getLocale().getBcp47());
841 aTree
.put("commandName", rStateChange
.first
);
842 aTree
.put("state", rStateChange
.second
);
843 std::stringstream aStream
;
844 boost::property_tree::write_json(aStream
, aTree
);
845 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
846 OString(aStream
.str()));
851 maFocusManager
.Clear();
853 const bool bForceNewDeck ((mnRequestedForceFlags
&SwitchFlag_ForceNewDeck
)!=0);
854 const bool bForceNewPanels ((mnRequestedForceFlags
&SwitchFlag_ForceNewPanels
)!=0);
855 mnRequestedForceFlags
= SwitchFlag_NoForce
;
857 if ( msCurrentDeckId
!= rDeckDescriptor
.msId
861 mpCurrentDeck
->Hide();
863 msCurrentDeckId
= rDeckDescriptor
.msId
;
866 // Determine the panels to display in the deck.
867 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors
;
869 css::uno::Reference
<css::frame::XController
> xController
= mxCurrentController
.is() ? mxCurrentController
: mxFrame
->getController();
871 mpResourceManager
->GetMatchingPanels(
872 aPanelContextDescriptors
,
874 rDeckDescriptor
.msId
,
877 if (aPanelContextDescriptors
.empty())
879 // There are no panels to be displayed in the current context.
880 if (vcl::EnumContext::GetContextEnum(rContext
.msContext
) != vcl::EnumContext::Context::Empty
)
882 // Switch to the "empty" context and try again.
886 rContext
.msApplication
,
887 vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Empty
)));
892 // This is already the "empty" context. Looks like we have
893 // to live with an empty deck.
897 // Provide a configuration and Deck object.
899 CreateDeck(rDeckDescriptor
.msId
, rContext
, bForceNewDeck
);
901 if (bForceNewPanels
&& !bForceNewDeck
) // already forced if bForceNewDeck
902 CreatePanels(rDeckDescriptor
.msId
, rContext
);
904 if (mpCurrentDeck
&& mpCurrentDeck
!= rDeckDescriptor
.mpDeck
)
905 mpCurrentDeck
->Hide();
906 mpCurrentDeck
.reset(rDeckDescriptor
.mpDeck
);
908 if ( ! mpCurrentDeck
)
911 #if OSL_DEBUG_LEVEL >= 2
912 // Show the context name in the deck title bar.
913 DeckTitleBar
* pDebugTitleBar
= mpCurrentDeck
->GetTitleBar();
915 pDebugTitleBar
->SetTitle(rDeckDescriptor
.msTitle
+ " (" + maCurrentContext
.msContext
+ ")");
918 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
919 sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth();
920 WindowAlign eAlign
= pSplitWindow
? pSplitWindow
->GetAlign() : WindowAlign::Right
;
922 if (eAlign
== WindowAlign::Left
) // attach the Sidebar towards the left-side of screen
924 nDeckX
= nTabBarDefaultWidth
;
926 else // attach the Sidebar towards the right-side of screen
931 // Activate the deck and the new set of panels.
932 mpCurrentDeck
->setPosSizePixel(
935 mpParentWindow
->GetSizePixel().Width() - nTabBarDefaultWidth
,
936 mpParentWindow
->GetSizePixel().Height());
938 mpCurrentDeck
->Show();
940 mpParentWindow
->SetText(rDeckDescriptor
.msTitle
);
944 // Tell the focus manager about the new panels and tab bar
946 maFocusManager
.SetDeck(mpCurrentDeck
);
947 maFocusManager
.SetPanels(mpCurrentDeck
->GetPanels());
949 mpTabBar
->UpdateFocusManager(maFocusManager
);
950 UpdateTitleBarIcons();
953 void SidebarController::notifyDeckTitle(std::u16string_view targetDeckId
)
955 if (msCurrentDeckId
== targetDeckId
)
957 maFocusManager
.SetDeck(mpCurrentDeck
);
958 mpTabBar
->UpdateFocusManager(maFocusManager
);
959 UpdateTitleBarIcons();
963 std::shared_ptr
<Panel
> SidebarController::CreatePanel (
964 std::u16string_view rsPanelId
,
965 weld::Widget
* pParentWindow
,
966 const bool bIsInitiallyExpanded
,
967 const Context
& rContext
,
968 const VclPtr
<Deck
>& pDeck
)
970 std::shared_ptr
<PanelDescriptor
> xPanelDescriptor
= mpResourceManager
->GetPanelDescriptor(rsPanelId
);
972 if (!xPanelDescriptor
)
975 // Create the panel which is the parent window of the UIElement.
976 auto xPanel
= std::make_shared
<Panel
>(
979 bIsInitiallyExpanded
,
981 [this]() { return this->GetCurrentContext(); },
984 // Create the XUIElement.
985 Reference
<ui::XUIElement
> xUIElement (CreateUIElement(
986 xPanel
->GetElementParentWindow(),
987 xPanelDescriptor
->msImplementationURL
,
988 xPanelDescriptor
->mbWantsCanvas
,
992 // Initialize the panel and add it to the active deck.
993 xPanel
->SetUIElement(xUIElement
);
1003 Reference
<ui::XUIElement
> SidebarController::CreateUIElement (
1004 const Reference
<awt::XWindow
>& rxWindow
,
1005 const OUString
& rsImplementationURL
,
1006 const bool bWantsCanvas
,
1007 const Context
& rContext
)
1011 const Reference
<XComponentContext
>& xComponentContext (::comphelper::getProcessComponentContext() );
1012 const Reference
<ui::XUIElementFactory
> xUIElementFactory
=
1013 ui::theUIElementFactoryManager::get( xComponentContext
);
1015 // Create the XUIElement.
1016 ::comphelper::NamedValueCollection aCreationArguments
;
1017 aCreationArguments
.put(u
"Frame"_ustr
, Any(mxFrame
));
1018 aCreationArguments
.put(u
"ParentWindow"_ustr
, Any(rxWindow
));
1019 SidebarDockingWindow
* pSfxDockingWindow
= mpParentWindow
.get();
1020 if (pSfxDockingWindow
!= nullptr)
1021 aCreationArguments
.put(u
"SfxBindings"_ustr
, Any(reinterpret_cast<sal_uInt64
>(&pSfxDockingWindow
->GetBindings())));
1022 aCreationArguments
.put(u
"Theme"_ustr
, Theme::GetPropertySet());
1023 aCreationArguments
.put(u
"Sidebar"_ustr
, Any(Reference
<ui::XSidebar
>(static_cast<ui::XSidebar
*>(this))));
1026 Reference
<rendering::XSpriteCanvas
> xCanvas (VCLUnoHelper::GetWindow(rxWindow
)->GetOutDev()->GetSpriteCanvas());
1027 aCreationArguments
.put(u
"Canvas"_ustr
, Any(xCanvas
));
1030 if (mxCurrentController
.is())
1032 OUString aModule
= Tools::GetModuleName(mxCurrentController
);
1033 if (!aModule
.isEmpty())
1035 aCreationArguments
.put(u
"Module"_ustr
, Any(aModule
));
1037 aCreationArguments
.put(u
"Controller"_ustr
, Any(mxCurrentController
));
1040 aCreationArguments
.put(u
"ApplicationName"_ustr
, Any(rContext
.msApplication
));
1041 aCreationArguments
.put(u
"ContextName"_ustr
, Any(rContext
.msContext
));
1043 Reference
<ui::XUIElement
> xUIElement(
1044 xUIElementFactory
->createUIElement(
1045 rsImplementationURL
,
1046 aCreationArguments
.getPropertyValues()),
1051 catch(const Exception
&)
1053 TOOLS_WARN_EXCEPTION("sfx.sidebar", "Cannot create panel " << rsImplementationURL
);
1058 IMPL_LINK(SidebarController
, WindowEventHandler
, VclWindowEvent
&, rEvent
, void)
1060 if (rEvent
.GetWindow() == mpParentWindow
)
1062 switch (rEvent
.GetId())
1064 case VclEventId::WindowShow
:
1065 case VclEventId::WindowResize
:
1069 case VclEventId::WindowDataChanged
:
1070 // Force an update of deck and tab bar to reflect
1071 // changes in theme (high contrast mode).
1072 Theme::HandleDataChange();
1073 UpdateTitleBarIcons();
1074 mpParentWindow
->Invalidate();
1075 mnRequestedForceFlags
|= SwitchFlag_ForceNewDeck
| SwitchFlag_ForceNewPanels
;
1076 maContextChangeUpdate
.RequestCall();
1079 case VclEventId::WindowToggleFloating
:
1080 // make sure the appropriate "Dock" or "Undock" menu entry is shown
1081 mpTabBar
->UpdateMenus();
1084 case VclEventId::ObjectDying
:
1088 case VclEventId::WindowPaint
:
1089 SAL_INFO("sfx.sidebar", "Paint");
1096 else if (rEvent
.GetWindow()==mpSplitWindow
&& mpSplitWindow
!=nullptr)
1098 switch (rEvent
.GetId())
1100 case VclEventId::WindowMouseButtonDown
:
1101 mnWidthOnSplitterButtonDown
= mpParentWindow
->GetSizePixel().Width();
1104 case VclEventId::WindowMouseButtonUp
:
1106 ProcessNewWidth(mpParentWindow
->GetSizePixel().Width());
1110 case VclEventId::ObjectDying
:
1119 void SidebarController::ConnectMenuActivateHandlers(weld::Menu
& rMainMenu
, weld::Menu
& rSubMenu
) const
1121 rMainMenu
.connect_activate(LINK(const_cast<SidebarController
*>(this), SidebarController
, OnMenuItemSelected
));
1122 rSubMenu
.connect_activate(LINK(const_cast<SidebarController
*>(this), SidebarController
, OnSubMenuItemSelected
));
1125 IMPL_LINK(SidebarController
, OnMenuItemSelected
, const OUString
&, rCurItemId
, void)
1127 if (rCurItemId
== "unlocktaskpanel")
1129 mpParentWindow
->SetFloatingMode(true);
1130 if (mpParentWindow
->IsFloatingMode())
1131 mpParentWindow
->ToTop(ToTopFlags::GrabFocusOnly
);
1133 else if (rCurItemId
== "locktaskpanel")
1135 mpParentWindow
->SetFloatingMode(false);
1137 else if (rCurItemId
== "hidesidebar")
1139 if (!comphelper::LibreOfficeKit::isActive())
1141 const util::URL
aURL(Tools::GetURL(u
".uno:Sidebar"_ustr
));
1142 Reference
<frame::XDispatch
> xDispatch(Tools::GetDispatch(mxFrame
, aURL
));
1144 xDispatch
->dispatch(aURL
, Sequence
<beans::PropertyValue
>());
1148 // In LOK we don't really destroy the sidebar when "closing";
1149 // we simply hide it. This is because recreating it is problematic
1150 // See notes in SidebarDockingWindow::NotifyResize().
1158 std::u16string_view sNumber
;
1159 if (rCurItemId
.startsWith("select", &sNumber
))
1162 SwitchToDeck(mpTabBar
->GetDeckIdForIndex(o3tl::toInt32(sNumber
)));
1164 mpParentWindow
->GrabFocusToDocument();
1166 catch (RuntimeException
&)
1172 IMPL_LINK(SidebarController
, OnSubMenuItemSelected
, const OUString
&, rCurItemId
, void)
1176 std::u16string_view sNumber
;
1177 if (rCurItemId
.startsWith("customize", &sNumber
))
1179 mpTabBar
->ToggleHideFlag(o3tl::toInt32(sNumber
));
1181 // Find the set of decks that could be displayed for the new context.
1182 ResourceManager::DeckContextDescriptorContainer aDecks
;
1183 mpResourceManager
->GetMatchingDecks (
1185 GetCurrentContext(),
1186 IsDocumentReadOnly(),
1187 mxFrame
->getController());
1188 // Notify the tab bar about the updated set of decks.
1189 maFocusManager
.Clear();
1190 mpTabBar
->SetDecks(aDecks
);
1191 mpTabBar
->HighlightDeck(mpCurrentDeck
->GetId());
1192 mpTabBar
->UpdateFocusManager(maFocusManager
);
1194 mpParentWindow
->GrabFocusToDocument();
1196 catch (RuntimeException
&)
1202 void SidebarController::RequestCloseDeck()
1204 if (comphelper::LibreOfficeKit::isActive() && mpCurrentDeck
)
1206 const SfxViewShell
* pViewShell
= SfxViewShell::Current();
1207 if (pViewShell
&& pViewShell
->isLOKMobilePhone())
1209 // Mobile phone - TODO: unify with desktop
1210 tools::JsonWriter aJsonWriter
;
1211 aJsonWriter
.put("id", mpParentWindow
->get_id());
1212 aJsonWriter
.put("type", "dockingwindow");
1213 aJsonWriter
.put("text", mpParentWindow
->GetText());
1214 aJsonWriter
.put("enabled", false);
1215 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_JSDIALOG
, aJsonWriter
.finishAndGetAsOString());
1217 else if (pViewShell
)
1219 tools::JsonWriter aJsonWriter
;
1220 aJsonWriter
.put("id", mpParentWindow
->get_id());
1221 aJsonWriter
.put("action", "close");
1222 aJsonWriter
.put("jsontype", "sidebar");
1223 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_JSDIALOG
, aJsonWriter
.finishAndGetAsOString());
1227 mbIsDeckRequestedOpen
= false;
1228 UpdateDeckOpenState();
1230 mpTabBar
->RemoveDeckHighlight();
1233 void SidebarController::RequestOpenDeck()
1235 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1236 if ( pSplitWindow
&& !pSplitWindow
->IsFadeIn() )
1237 // tdf#83546 Collapsed sidebar should expand first
1238 pSplitWindow
->FadeIn();
1240 mbIsDeckRequestedOpen
= true;
1241 UpdateDeckOpenState();
1244 bool SidebarController::IsDeckOpen(const sal_Int32 nIndex
)
1248 OUString
asDeckId(mpTabBar
->GetDeckIdForIndex(nIndex
));
1249 return IsDeckVisible(asDeckId
);
1251 return mbIsDeckOpen
.has_value() && *mbIsDeckOpen
;
1254 bool SidebarController::IsDeckVisible(std::u16string_view rsDeckId
)
1256 return mbIsDeckOpen
.has_value() && *mbIsDeckOpen
&& msCurrentDeckId
== rsDeckId
;
1259 void SidebarController::UpdateDeckOpenState()
1261 if ( ! mbIsDeckRequestedOpen
.has_value() )
1262 // No state requested.
1265 const sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth();
1267 // Update (change) the open state when it either has not yet been initialized
1268 // or when its value differs from the requested state.
1269 if ( mbIsDeckOpen
.has_value() && *mbIsDeckOpen
== *mbIsDeckRequestedOpen
)
1272 if (*mbIsDeckRequestedOpen
)
1274 if (!mpParentWindow
->IsFloatingMode())
1276 if (mnSavedSidebarWidth
<= nTabBarDefaultWidth
)
1277 SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow
));
1279 SetChildWindowWidth(mnSavedSidebarWidth
);
1283 // Show the Deck by resizing back to the original size (before hiding).
1284 Size
aNewSize(mpParentWindow
->GetFloatingWindow()->GetSizePixel());
1285 Point
aNewPos(mpParentWindow
->GetFloatingWindow()->GetPosPixel());
1287 aNewPos
.setX(aNewPos
.X() - mnSavedSidebarWidth
+ nTabBarDefaultWidth
);
1288 aNewSize
.setWidth(mnSavedSidebarWidth
);
1290 mpParentWindow
->GetFloatingWindow()->SetPosSizePixel(aNewPos
, aNewSize
);
1292 if (comphelper::LibreOfficeKit::isActive())
1294 // Sidebar wide enough to render the menu; enable it.
1295 mpTabBar
->EnableMenuButton(true);
1297 if (const SfxViewShell
* pViewShell
= mpViewFrame
->GetViewShell())
1299 const std::string uno
= UnoNameFromDeckId(msCurrentDeckId
, GetCurrentContext());
1301 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
1302 OString(uno
+ "=true"));
1309 if ( ! mpParentWindow
->IsFloatingMode())
1310 mnSavedSidebarWidth
= SetChildWindowWidth(nTabBarDefaultWidth
);
1313 // Hide the Deck by resizing to the width of the TabBar.
1314 Size
aNewSize(mpParentWindow
->GetFloatingWindow()->GetSizePixel());
1315 Point
aNewPos(mpParentWindow
->GetFloatingWindow()->GetPosPixel());
1316 mnSavedSidebarWidth
= aNewSize
.Width(); // Save the current width to restore.
1318 aNewPos
.setX(aNewPos
.X() + mnSavedSidebarWidth
- nTabBarDefaultWidth
);
1319 if (comphelper::LibreOfficeKit::isActive())
1321 // Hide by collapsing, otherwise with 0x0 the client might expect
1322 // to get valid dimensions on rendering and not collapse the sidebar.
1323 aNewSize
.setWidth(1);
1326 aNewSize
.setWidth(nTabBarDefaultWidth
);
1328 mpParentWindow
->GetFloatingWindow()->SetPosSizePixel(aNewPos
, aNewSize
);
1330 if (comphelper::LibreOfficeKit::isActive())
1332 // Sidebar too narrow to render the menu; disable it.
1333 mpTabBar
->EnableMenuButton(false);
1335 if (const SfxViewShell
* pViewShell
= mpViewFrame
->GetViewShell())
1337 const std::string uno
= UnoNameFromDeckId(msCurrentDeckId
, GetCurrentContext());
1339 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED
,
1340 OString(uno
+ "=false"));
1345 if (mnWidthOnSplitterButtonDown
> nTabBarDefaultWidth
)
1346 mnSavedSidebarWidth
= mnWidthOnSplitterButtonDown
;
1347 mpParentWindow
->SetStyle(mpParentWindow
->GetStyle() & ~WB_SIZEABLE
);
1353 bool SidebarController::CanModifyChildWindowWidth()
1355 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1356 if (pSplitWindow
== nullptr)
1359 sal_uInt16
nRow (0xffff);
1360 sal_uInt16
nColumn (0xffff);
1361 if (pSplitWindow
->GetWindowPos(mpParentWindow
, nColumn
, nRow
))
1363 sal_uInt16
nRowCount (pSplitWindow
->GetWindowCount(nColumn
));
1364 return nRowCount
==1;
1370 sal_Int32
SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth
)
1372 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1373 if (pSplitWindow
== nullptr)
1376 sal_uInt16
nRow (0xffff);
1377 sal_uInt16
nColumn (0xffff);
1378 pSplitWindow
->GetWindowPos(mpParentWindow
, nColumn
, nRow
);
1379 const tools::Long
nColumnWidth (pSplitWindow
->GetLineSize(nColumn
));
1381 vcl::Window
* pWindow
= mpParentWindow
;
1382 const Size
aWindowSize (pWindow
->GetSizePixel());
1384 pSplitWindow
->MoveWindow(
1386 Size(nNewWidth
, aWindowSize
.Height()),
1390 static_cast<SplitWindow
*>(pSplitWindow
)->Split();
1392 return static_cast<sal_Int32
>(nColumnWidth
);
1395 void SidebarController::RestrictWidth (sal_Int32 nWidth
)
1397 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1398 if (pSplitWindow
!= nullptr)
1400 const sal_uInt16
nId (pSplitWindow
->GetItemId(mpParentWindow
.get()));
1401 const sal_uInt16
nSetId (pSplitWindow
->GetSet(nId
));
1402 const sal_Int32 nRequestedWidth
= TabBar::GetDefaultWidth() + nWidth
;
1404 pSplitWindow
->SetItemSizeRange(
1406 Range(nRequestedWidth
, std::max(nRequestedWidth
, getMaximumWidth())));
1410 SfxSplitWindow
* SidebarController::GetSplitWindow()
1412 if (mpParentWindow
!= nullptr)
1414 SfxSplitWindow
* pSplitWindow
= dynamic_cast<SfxSplitWindow
*>(mpParentWindow
->GetParent());
1415 if (pSplitWindow
!= mpSplitWindow
)
1417 if (mpSplitWindow
!= nullptr)
1418 mpSplitWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
1420 mpSplitWindow
= pSplitWindow
;
1422 if (mpSplitWindow
!= nullptr)
1423 mpSplitWindow
->AddEventListener(LINK(this, SidebarController
, WindowEventHandler
));
1425 return mpSplitWindow
;
1431 void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag
)
1433 if (mpParentWindow
== nullptr)
1436 if (bCloseAfterDrag
)
1438 // Make sure that the indicator exists.
1439 if (!mpCloseIndicator
)
1440 mpCloseIndicator
.reset(VclPtr
<CloseIndicator
>::Create(mpParentWindow
));
1442 // Place and show the indicator.
1443 const Size
aWindowSize (mpParentWindow
->GetSizePixel());
1444 const Size
aImageSize (mpCloseIndicator
->GetSizePixel());
1445 mpCloseIndicator
->SetPosPixel(
1447 aWindowSize
.Width() - TabBar::GetDefaultWidth() - aImageSize
.Width(),
1448 (aWindowSize
.Height() - aImageSize
.Height())/2));
1449 mpCloseIndicator
->Show();
1453 // Hide but don't delete the indicator.
1454 if (mpCloseIndicator
)
1455 mpCloseIndicator
->Hide();
1459 void SidebarController::UpdateTitleBarIcons()
1461 if ( ! mpCurrentDeck
)
1464 const bool bIsHighContrastModeActive (Theme::IsHighContrastMode());
1466 const ResourceManager
& rResourceManager
= *mpResourceManager
;
1468 // Update the deck icon.
1469 std::shared_ptr
<DeckDescriptor
> xDeckDescriptor
= rResourceManager
.GetDeckDescriptor(mpCurrentDeck
->GetId());
1470 if (xDeckDescriptor
&& mpCurrentDeck
->GetTitleBar())
1472 const OUString
sIconURL(
1473 bIsHighContrastModeActive
1474 ? xDeckDescriptor
->msHighContrastTitleBarIconURL
1475 : xDeckDescriptor
->msTitleBarIconURL
);
1476 mpCurrentDeck
->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL
, mxFrame
));
1479 // Update the panel icons.
1480 const SharedPanelContainer
& rPanels (mpCurrentDeck
->GetPanels());
1481 for (const auto& rxPanel
: rPanels
)
1485 if (!rxPanel
->GetTitleBar())
1487 std::shared_ptr
<PanelDescriptor
> xPanelDescriptor
= rResourceManager
.GetPanelDescriptor(rxPanel
->GetId());
1488 if (!xPanelDescriptor
)
1490 const OUString
sIconURL (
1491 bIsHighContrastModeActive
1492 ? xPanelDescriptor
->msHighContrastTitleBarIconURL
1493 : xPanelDescriptor
->msTitleBarIconURL
);
1494 rxPanel
->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL
, mxFrame
));
1498 void SidebarController::ShowPanel (const Panel
& rPanel
)
1504 mpCurrentDeck
->ShowPanel(rPanel
);
1508 ResourceManager::DeckContextDescriptorContainer
SidebarController::GetMatchingDecks()
1510 ResourceManager::DeckContextDescriptorContainer aDecks
;
1511 mpResourceManager
->GetMatchingDecks (aDecks
,
1512 GetCurrentContext(),
1513 IsDocumentReadOnly(),
1514 mxFrame
->getController());
1518 ResourceManager::PanelContextDescriptorContainer
SidebarController::GetMatchingPanels(std::u16string_view rDeckId
)
1520 ResourceManager::PanelContextDescriptorContainer aPanels
;
1522 mpResourceManager
->GetMatchingPanels(aPanels
,
1523 GetCurrentContext(),
1525 mxFrame
->getController());
1529 void SidebarController::updateModel(const css::uno::Reference
<css::frame::XModel
>& xModel
)
1531 mpResourceManager
->UpdateModel(xModel
);
1534 void SidebarController::FadeOut()
1537 mpSplitWindow
->FadeOut();
1540 void SidebarController::FadeIn()
1543 mpSplitWindow
->FadeIn();
1546 tools::Rectangle
SidebarController::GetDeckDragArea() const
1548 tools::Rectangle aRect
;
1551 if (DeckTitleBar
* pTitleBar
= mpCurrentDeck
->GetTitleBar())
1553 aRect
= pTitleBar
->GetDragArea();
1559 void SidebarController::frameAction(const css::frame::FrameActionEvent
& rEvent
)
1561 if (rEvent
.Frame
== mxFrame
)
1563 if (rEvent
.Action
== css::frame::FrameAction_COMPONENT_DETACHING
)
1564 unregisterSidebarForFrame(mxFrame
->getController());
1565 else if (rEvent
.Action
== css::frame::FrameAction_COMPONENT_REATTACHED
)
1566 registerSidebarForFrame(mxFrame
->getController());
1570 void SidebarController::saveDeckState()
1572 // Impress shutdown : context (frame) is disposed before sidebar disposing
1573 // calc writer : context (frame) is disposed after sidebar disposing
1574 // so need to test if GetCurrentContext is still valid regarding msApplication
1575 if (GetCurrentContext().msApplication
!= "none")
1577 mpResourceManager
->SaveDecksSettings(GetCurrentContext());
1578 mpResourceManager
->SaveLastActiveDeck(GetCurrentContext(), msCurrentDeckId
);
1582 static bool isChartOrMathContext(const Context
& context
)
1584 return context
.msApplication
== "com.sun.star.chart2.ChartDocument"
1585 || context
.msApplication
== "com.sun.star.formula.FormulaProperties";
1588 bool SidebarController::hasChartOrMathContextCurrently() const
1590 if ((maRequestedContext
!= maCurrentContext
) && isChartOrMathContext(maRequestedContext
))
1591 return true; // We are not yet changed, but in the process
1593 return isChartOrMathContext(maCurrentContext
);
1596 sfx2::sidebar::SidebarController
* SidebarController::GetSidebarControllerForView(const SfxViewShell
* pViewShell
)
1601 Reference
<css::frame::XController2
> xController(pViewShell
->GetController(), UNO_QUERY
);
1602 if (!xController
.is())
1605 // Make sure there is a model behind the controller, otherwise getSidebar() can crash.
1606 if (!xController
->getModel().is())
1609 Reference
<css::ui::XSidebarProvider
> xSidebarProvider
= xController
->getSidebar();
1610 if (!xSidebarProvider
.is())
1613 Reference
<css::ui::XSidebar
> xSidebar
= xSidebarProvider
->getSidebar();
1617 return dynamic_cast<sfx2::sidebar::SidebarController
*>(xSidebar
.get());
1620 } // end of namespace sfx2::sidebar
1622 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */