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 "SidebarController.hxx"
21 #include "DeckTitleBar.hxx"
23 #include "PanelTitleBar.hxx"
24 #include "SidebarResource.hxx"
26 #include <sfx2/sidebar/Theme.hxx>
27 #include <sfx2/sidebar/SidebarChildWindow.hxx>
28 #include <sfx2/sidebar/Tools.hxx>
29 #include "SidebarDockingWindow.hxx"
30 #include "Context.hxx"
32 #include <sfx2/sfxresid.hxx>
33 #include <sfx2/sfxsids.hrc>
34 #include <sfx2/titledockwin.hxx>
35 #include "sfxlocal.hrc"
36 #include <vcl/floatwin.hxx>
37 #include <vcl/fixed.hxx>
38 #include "splitwin.hxx"
39 #include <svl/smplhint.hxx>
40 #include <tools/link.hxx>
41 #include <toolkit/helper/vclunohelper.hxx>
42 #include <comphelper/processfactory.hxx>
43 #include <comphelper/namedvaluecollection.hxx>
45 #include <com/sun/star/frame/XDispatchProvider.hpp>
46 #include <com/sun/star/lang/XInitialization.hpp>
47 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
48 #include <com/sun/star/ui/ContextChangeEventObject.hpp>
49 #include <com/sun/star/ui/theUIElementFactoryManager.hpp>
50 #include <com/sun/star/util/XURLTransformer.hpp>
51 #include <com/sun/star/util/URL.hpp>
52 #include <com/sun/star/rendering/XSpriteCanvas.hpp>
54 #include <boost/bind.hpp>
55 #include <boost/function.hpp>
56 #include <boost/scoped_array.hpp>
59 using namespace css::uno
;
60 using ::rtl::OUString
;
64 const static char gsReadOnlyCommandName
[] = ".uno:EditDoc";
65 const static char gsHideSidebarCommandName
[] = ".uno:Sidebar";
66 const static sal_Int32
gnMaximumSidebarWidth (400);
67 const static sal_Int32
gnWidthCloseThreshold (70);
68 const static sal_Int32
gnWidthOpenThreshold (40);
71 namespace sfx2
{ namespace sidebar
{
73 SidebarController::SidebarControllerContainer
SidebarController::maSidebarControllerContainer
;
78 MID_UNLOCK_TASK_PANEL
= 1,
87 /** When in doubt, show this deck.
89 static const char gsDefaultDeckId
[] = "PropertyDeck";
92 SidebarController::SidebarController (
93 SidebarDockingWindow
* pParentWindow
,
94 const css::uno::Reference
<css::frame::XFrame
>& rxFrame
)
95 : SidebarControllerInterfaceBase(m_aMutex
),
97 mpParentWindow(pParentWindow
),
98 mpTabBar(VclPtr
<TabBar
>::Create(
101 ::boost::bind(&SidebarController::OpenThenSwitchToDeck
, this, _1
),
102 ::boost::bind(&SidebarController::ShowPopupMenu
, this, _1
,_2
))),
104 maCurrentContext(OUString(), OUString()),
105 maRequestedContext(),
106 mnRequestedForceFlags(SwitchFlag_NoForce
),
107 msCurrentDeckId(gsDefaultDeckId
),
108 msCurrentDeckTitle(),
109 maPropertyChangeForwarder(::boost::bind(&SidebarController::BroadcastPropertyChange
, this)),
110 maContextChangeUpdate(::boost::bind(&SidebarController::UpdateConfigurations
, this)),
111 maAsynchronousDeckSwitch(),
112 mbIsDeckRequestedOpen(),
114 mbCanDeckBeOpened(true),
115 mnSavedSidebarWidth(pParentWindow
->GetSizePixel().Width()),
116 maFocusManager(::boost::bind(&SidebarController::ShowPanel
, this, _1
)),
117 mxReadOnlyModeDispatch(),
118 mbIsDocumentReadOnly(false),
120 mnWidthOnSplitterButtonDown(0)
122 // Listen for context change events.
123 css::uno::Reference
<css::ui::XContextChangeEventMultiplexer
> xMultiplexer (
124 css::ui::ContextChangeEventMultiplexer::get(
125 ::comphelper::getProcessComponentContext()));
126 if (xMultiplexer
.is())
127 xMultiplexer
->addContextChangeEventListener(
128 static_cast<css::ui::XContextChangeEventListener
*>(this),
129 mxFrame
->getController());
131 // Listen for window events.
132 mpParentWindow
->AddEventListener(LINK(this, SidebarController
, WindowEventHandler
));
134 // Listen for theme property changes.
135 Theme::GetPropertySet()->addPropertyChangeListener(
137 static_cast<css::beans::XPropertyChangeListener
*>(this));
139 // Get the dispatch object as preparation to listen for changes of
140 // the read-only state.
141 const util::URL
aURL (Tools::GetURL(gsReadOnlyCommandName
));
142 mxReadOnlyModeDispatch
= Tools::GetDispatch(mxFrame
, aURL
);
143 if (mxReadOnlyModeDispatch
.is())
144 mxReadOnlyModeDispatch
->addStatusListener(this, aURL
);
146 SwitchToDeck(gsDefaultDeckId
);
148 WeakReference
<SidebarController
> xWeakController (this);
149 maSidebarControllerContainer
.insert(
150 SidebarControllerContainer::value_type(
155 SidebarController::~SidebarController()
159 SidebarController
* SidebarController::GetSidebarControllerForFrame (
160 const css::uno::Reference
<css::frame::XFrame
>& rxFrame
)
162 SidebarControllerContainer::iterator
iEntry (maSidebarControllerContainer
.find(rxFrame
));
163 if (iEntry
== maSidebarControllerContainer
.end())
166 css::uno::Reference
<XInterface
> xController (iEntry
->second
.get());
167 if ( ! xController
.is())
170 return dynamic_cast<SidebarController
*>(xController
.get());
173 void SAL_CALL
SidebarController::disposing()
175 mpCloseIndicator
.disposeAndClear();
177 SidebarControllerContainer::iterator
iEntry (maSidebarControllerContainer
.find(mxFrame
));
178 if (iEntry
!= maSidebarControllerContainer
.end())
179 maSidebarControllerContainer
.erase(iEntry
);
181 maFocusManager
.Clear();
183 css::uno::Reference
<css::ui::XContextChangeEventMultiplexer
> xMultiplexer (
184 css::ui::ContextChangeEventMultiplexer::get(
185 ::comphelper::getProcessComponentContext()));
186 if (xMultiplexer
.is())
187 xMultiplexer
->removeAllContextChangeEventListeners(
188 static_cast<css::ui::XContextChangeEventListener
*>(this));
190 if (mxReadOnlyModeDispatch
.is())
191 mxReadOnlyModeDispatch
->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName
));
192 if (mpSplitWindow
!= nullptr)
194 mpSplitWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
195 mpSplitWindow
= NULL
;
198 if (mpParentWindow
!= nullptr)
200 mpParentWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
201 mpParentWindow
= NULL
;
206 mpCurrentDeck
.disposeAndClear();
209 mpTabBar
.disposeAndClear();
211 Theme::GetPropertySet()->removePropertyChangeListener(
213 static_cast<css::beans::XPropertyChangeListener
*>(this));
215 maContextChangeUpdate
.CancelRequest();
216 maAsynchronousDeckSwitch
.CancelRequest();
219 void SAL_CALL
SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject
& rEvent
)
220 throw(css::uno::RuntimeException
, std::exception
)
222 // Update to the requested new context asynchronously to avoid
223 // subtle errors caused by SFX2 which in rare cases can not
224 // properly handle a synchronous update.
225 maRequestedContext
= Context(
226 rEvent
.ApplicationName
,
228 if (maRequestedContext
!= maCurrentContext
)
230 maAsynchronousDeckSwitch
.CancelRequest();
231 maContextChangeUpdate
.RequestCall();
235 void SAL_CALL
SidebarController::disposing (const css::lang::EventObject
& rEventObject
)
236 throw(css::uno::RuntimeException
, std::exception
)
243 void SAL_CALL
SidebarController::propertyChange (const css::beans::PropertyChangeEvent
& rEvent
)
244 throw(css::uno::RuntimeException
, std::exception
)
248 maPropertyChangeForwarder
.RequestCall();
251 void SAL_CALL
SidebarController::statusChanged (const css::frame::FeatureStateEvent
& rEvent
)
252 throw(css::uno::RuntimeException
, std::exception
)
254 bool bIsReadWrite (true);
255 if (rEvent
.IsEnabled
)
256 rEvent
.State
>>= bIsReadWrite
;
258 if (mbIsDocumentReadOnly
!= !bIsReadWrite
)
260 mbIsDocumentReadOnly
= !bIsReadWrite
;
262 // Force the current deck to update its panel list.
263 if ( ! mbIsDocumentReadOnly
)
264 msCurrentDeckId
= gsDefaultDeckId
;
265 mnRequestedForceFlags
|= SwitchFlag_ForceSwitch
;
266 maAsynchronousDeckSwitch
.CancelRequest();
267 maContextChangeUpdate
.RequestCall();
271 void SAL_CALL
SidebarController::requestLayout()
272 throw(css::uno::RuntimeException
, std::exception
)
274 sal_Int32 nMinimalWidth
= 0;
277 mpCurrentDeck
->RequestLayout();
278 nMinimalWidth
= mpCurrentDeck
->GetMinimalWidth();
280 RestrictWidth(nMinimalWidth
);
283 void SidebarController::BroadcastPropertyChange()
285 DataChangedEvent
aEvent (DataChangedEventType::USER
);
286 mpParentWindow
->NotifyAllChildren(aEvent
);
287 mpParentWindow
->Invalidate(INVALIDATE_CHILDREN
);
290 void SidebarController::NotifyResize()
294 OSL_ASSERT(mpTabBar
!=nullptr);
298 vcl::Window
* pParentWindow
= mpTabBar
->GetParent();
299 sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor();
301 const sal_Int32
nWidth (pParentWindow
->GetSizePixel().Width());
302 const sal_Int32
nHeight (pParentWindow
->GetSizePixel().Height());
304 mbIsDeckOpen
= (nWidth
> nTabBarDefaultWidth
);
306 if (mnSavedSidebarWidth
<= 0)
307 mnSavedSidebarWidth
= nWidth
;
310 if (mbCanDeckBeOpened
)
312 const bool bIsOpening (nWidth
> mnWidthOnSplitterButtonDown
);
314 bIsDeckVisible
= nWidth
>= nTabBarDefaultWidth
+ gnWidthOpenThreshold
;
316 bIsDeckVisible
= nWidth
>= nTabBarDefaultWidth
+ gnWidthCloseThreshold
;
317 mbIsDeckRequestedOpen
= bIsDeckVisible
;
318 UpdateCloseIndicator(!bIsDeckVisible
);
321 bIsDeckVisible
= false;
325 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
326 WindowAlign eAlign
= pSplitWindow
? pSplitWindow
->GetAlign() : WINDOWALIGN_RIGHT
;
328 if (eAlign
== WINDOWALIGN_LEFT
) // attach the Sidebar towards the left-side of screen
330 nDeckX
= nTabBarDefaultWidth
;
333 else // attach the Sidebar towards the right-side of screen
336 nTabX
= nWidth
-nTabBarDefaultWidth
;
339 // Place the deck first.
342 mpCurrentDeck
->setPosSizePixel(nDeckX
, 0, nWidth
- nTabBarDefaultWidth
, nHeight
);
343 mpCurrentDeck
->Show();
344 mpCurrentDeck
->RequestLayout();
347 mpCurrentDeck
->Hide();
349 // Now place the tab bar.
350 mpTabBar
->setPosSizePixel(nTabX
, 0, nTabBarDefaultWidth
, nHeight
);
355 // Determine if the closer of the deck can be shown.
356 sal_Int32 nMinimalWidth
= 0;
359 DeckTitleBar
* pTitleBar
= mpCurrentDeck
->GetTitleBar();
360 if (pTitleBar
!= NULL
&& pTitleBar
->IsVisible())
361 pTitleBar
->SetCloserVisible(CanModifyChildWindowWidth());
362 nMinimalWidth
= mpCurrentDeck
->GetMinimalWidth();
365 RestrictWidth(nMinimalWidth
);
368 void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth
)
370 if ( ! mbIsDeckRequestedOpen
)
373 if (mbIsDeckRequestedOpen
.get())
375 // Deck became large enough to be shown. Show it.
376 mnSavedSidebarWidth
= nNewWidth
;
381 // Deck became too small. Close it completely.
382 // If window is wider than the tab bar then mark the deck as being visible, even when it its not.
383 // This is to trigger an adjustment of the width to the width of the tab bar.
387 if (mnWidthOnSplitterButtonDown
> TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor())
388 mnSavedSidebarWidth
= mnWidthOnSplitterButtonDown
;
392 void SidebarController::UpdateConfigurations()
394 if (maCurrentContext
!= maRequestedContext
395 || mnRequestedForceFlags
!=SwitchFlag_NoForce
)
397 maCurrentContext
= maRequestedContext
;
399 // Find the set of decks that could be displayed for the new context.
400 ResourceManager::DeckContextDescriptorContainer aDecks
;
401 ResourceManager::Instance().GetMatchingDecks (
404 mbIsDocumentReadOnly
,
407 // Notify the tab bar about the updated set of decks.
408 mpTabBar
->SetDecks(aDecks
);
410 // Find the new deck. By default that is the same as the old
411 // one. If that is not set or not enabled, then choose the
412 // first enabled deck (which is PropertyDeck).
414 for (ResourceManager::DeckContextDescriptorContainer::const_iterator
415 iDeck(aDecks
.begin()),
420 if (iDeck
->mbIsEnabled
)
422 if (iDeck
->msId
.equals(msCurrentDeckId
))
424 sNewDeckId
= msCurrentDeckId
;
427 else if (sNewDeckId
.getLength() == 0)
428 sNewDeckId
= iDeck
->msId
;
432 if (sNewDeckId
.getLength() == 0)
434 // We did not find a valid deck.
439 // Tell the tab bar to highlight the button associated
441 mpTabBar
->HighlightDeck(sNewDeckId
);
443 const DeckDescriptor
* pDescriptor
=
444 ResourceManager::Instance().GetDeckDescriptor(sNewDeckId
);
455 void SidebarController::OpenThenSwitchToDeck (
456 const ::rtl::OUString
& rsDeckId
)
458 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
459 if ( pSplitWindow
&& !pSplitWindow
->IsFadeIn() )
460 // tdf#83546 Collapsed sidebar should expand first
461 pSplitWindow
->FadeIn();
462 else if ( IsDeckVisible( rsDeckId
) )
465 // tdf#67627 Clicking a second time on a Deck icon will close the Deck
468 // tdf#88241 Summoning an undocked sidebar a second time should close sidebar
469 mpParentWindow
->Close();
473 SwitchToDeck(rsDeckId
);
474 mpTabBar
->Invalidate();
475 mpTabBar
->HighlightDeck(rsDeckId
);
478 void SidebarController::SwitchToDeck (
479 const ::rtl::OUString
& rsDeckId
)
481 if ( ! msCurrentDeckId
.equals(rsDeckId
)
483 || mnRequestedForceFlags
!=SwitchFlag_NoForce
)
485 const DeckDescriptor
* pDeckDescriptor
= ResourceManager::Instance().GetDeckDescriptor(rsDeckId
);
486 if (pDeckDescriptor
!= NULL
)
487 SwitchToDeck(*pDeckDescriptor
, maCurrentContext
);
491 void SidebarController::SwitchToDeck (
492 const DeckDescriptor
& rDeckDescriptor
,
493 const Context
& rContext
)
495 maFocusManager
.Clear();
497 const bool bForceNewDeck ((mnRequestedForceFlags
&SwitchFlag_ForceNewDeck
)!=0);
498 const bool bForceNewPanels ((mnRequestedForceFlags
&SwitchFlag_ForceNewPanels
)!=0);
500 if ( ! msCurrentDeckId
.equals(rDeckDescriptor
.msId
)
503 // When the deck changes then destroy the deck and all panels
504 // and create everything new.
505 mpCurrentDeck
.disposeAndClear();
507 msCurrentDeckId
= rDeckDescriptor
.msId
;
509 mpTabBar
->HighlightDeck(msCurrentDeckId
);
511 // Determine the panels to display in the deck.
512 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors
;
513 ResourceManager::Instance().GetMatchingPanels(
514 aPanelContextDescriptors
,
516 rDeckDescriptor
.msId
,
519 if (aPanelContextDescriptors
.empty())
521 // There are no panels to be displayed in the current context.
522 if (EnumContext::GetContextEnum(rContext
.msContext
) != EnumContext::Context_Empty
)
524 // Switch to the "empty" context and try again.
528 rContext
.msApplication
,
529 EnumContext::GetContextName(EnumContext::Context_Empty
)));
534 // This is already the "empty" context. Looks like we have
535 // to live with an empty deck.
539 // Provide a configuration and Deck object.
540 if ( ! mpCurrentDeck
)
543 VclPtr
<Deck
>::Create(
546 ::boost::bind(&SidebarController::RequestCloseDeck
, this)));
547 msCurrentDeckTitle
= rDeckDescriptor
.msTitle
;
550 if ( ! mpCurrentDeck
)
554 // Show the context name in the deck title bar.
555 DeckTitleBar
* pDebugTitleBar
= mpCurrentDeck
->GetTitleBar();
556 if (pDebugTitleBar
!= NULL
)
557 pDebugTitleBar
->SetTitle(rDeckDescriptor
.msTitle
+ " (" + maCurrentContext
.msContext
+ ")");
560 // Update the panel list.
561 const sal_Int32
nNewPanelCount (aPanelContextDescriptors
.size());
562 SharedPanelContainer aNewPanels
;
563 const SharedPanelContainer
& rCurrentPanels (mpCurrentDeck
->GetPanels());
565 aNewPanels
.resize(nNewPanelCount
);
566 sal_Int32
nWriteIndex (0);
567 bool bHasPanelSetChanged (false);
568 for (sal_Int32 nReadIndex
=0; nReadIndex
<nNewPanelCount
; ++nReadIndex
)
570 const ResourceManager::PanelContextDescriptor
& rPanelContexDescriptor (
571 aPanelContextDescriptors
[nReadIndex
]);
573 // Determine if the panel can be displayed.
574 const bool bIsPanelVisible (!mbIsDocumentReadOnly
|| rPanelContexDescriptor
.mbShowForReadOnlyDocuments
);
575 if ( ! bIsPanelVisible
)
578 // Find the corresponding panel among the currently active
580 SharedPanelContainer::const_iterator iPanel
= rCurrentPanels
.end();
582 if (!bForceNewPanels
)
584 iPanel
= rCurrentPanels
.end();
585 for (auto a
= rCurrentPanels
.begin(); a
!= rCurrentPanels
.end(); ++a
)
587 if ((*a
)->HasIdPredicate(rPanelContexDescriptor
.msId
))
594 if (iPanel
!= rCurrentPanels
.end())
596 // Panel already exists in current deck. Reuse it.
597 aNewPanels
[nWriteIndex
] = *iPanel
;
598 aNewPanels
[nWriteIndex
]->SetExpanded(rPanelContexDescriptor
.mbIsInitiallyVisible
);
602 // Panel does not yet exist or creation of new panels is forced.
604 aNewPanels
[nWriteIndex
] = CreatePanel(
605 rPanelContexDescriptor
.msId
,
606 mpCurrentDeck
->GetPanelParentWindow(),
607 rPanelContexDescriptor
.mbIsInitiallyVisible
,
609 bHasPanelSetChanged
= true;
611 if (aNewPanels
[nWriteIndex
] != nullptr)
613 // Depending on the context we have to change the command
614 // for the "more options" dialog.
615 PanelTitleBar
* pTitleBar
= aNewPanels
[nWriteIndex
]->GetTitleBar();
616 if (pTitleBar
!= NULL
)
618 pTitleBar
->SetMoreOptionsCommand(
619 rPanelContexDescriptor
.msMenuCommand
,
627 // mpCurrentPanels - may miss stuff (?)
628 aNewPanels
.resize(nWriteIndex
);
630 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
631 sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor();
632 WindowAlign eAlign
= pSplitWindow
? pSplitWindow
->GetAlign() : WINDOWALIGN_RIGHT
;
634 if (eAlign
== WINDOWALIGN_LEFT
) // attach the Sidebar towards the left-side of screen
636 nDeckX
= nTabBarDefaultWidth
;
638 else // attach the Sidebar towards the right-side of screen
643 // Activate the deck and the new set of panels.
644 mpCurrentDeck
->setPosSizePixel(
647 mpParentWindow
->GetSizePixel().Width() - nTabBarDefaultWidth
,
648 mpParentWindow
->GetSizePixel().Height());
650 mpCurrentDeck
->ResetPanels(aNewPanels
);
651 mpCurrentDeck
->Show();
653 mpParentWindow
->SetText(rDeckDescriptor
.msTitle
);
655 if (bHasPanelSetChanged
)
658 // Tell the focus manager about the new panels and tab bar
660 maFocusManager
.SetDeckTitle(mpCurrentDeck
->GetTitleBar());
661 maFocusManager
.SetPanels(aNewPanels
);
662 mpTabBar
->UpdateFocusManager(maFocusManager
);
663 UpdateTitleBarIcons();
666 VclPtr
<Panel
> SidebarController::CreatePanel (
667 const OUString
& rsPanelId
,
668 vcl::Window
* pParentWindow
,
669 const bool bIsInitiallyExpanded
,
670 const Context
& rContext
)
672 const PanelDescriptor
* pPanelDescriptor
= ResourceManager::Instance().GetPanelDescriptor(rsPanelId
);
673 if (pPanelDescriptor
== NULL
)
676 // Create the panel which is the parent window of the UIElement.
677 VclPtr
<Panel
> pPanel
= VclPtr
<Panel
>::Create(
680 bIsInitiallyExpanded
,
681 ::boost::bind(&Deck::RequestLayout
, mpCurrentDeck
.get()),
682 ::boost::bind(&SidebarController::GetCurrentContext
, this));
684 // Create the XUIElement.
685 Reference
<ui::XUIElement
> xUIElement (CreateUIElement(
686 pPanel
->GetComponentInterface(),
687 pPanelDescriptor
->msImplementationURL
,
688 pPanelDescriptor
->mbWantsCanvas
,
692 // Initialize the panel and add it to the active deck.
693 pPanel
->SetUIElement(xUIElement
);
697 pPanel
.disposeAndClear();
703 Reference
<ui::XUIElement
> SidebarController::CreateUIElement (
704 const Reference
<awt::XWindowPeer
>& rxWindow
,
705 const ::rtl::OUString
& rsImplementationURL
,
706 const bool bWantsCanvas
,
707 const Context
& rContext
)
711 const Reference
<XComponentContext
> xComponentContext (::comphelper::getProcessComponentContext() );
712 const Reference
<ui::XUIElementFactory
> xUIElementFactory
=
713 ui::theUIElementFactoryManager::get( xComponentContext
);
715 // Create the XUIElement.
716 ::comphelper::NamedValueCollection aCreationArguments
;
717 aCreationArguments
.put("Frame", makeAny(mxFrame
));
718 aCreationArguments
.put("ParentWindow", makeAny(rxWindow
));
719 SfxDockingWindow
* pSfxDockingWindow
= dynamic_cast<SfxDockingWindow
*>(mpParentWindow
.get());
720 if (pSfxDockingWindow
!= NULL
)
721 aCreationArguments
.put("SfxBindings", makeAny(sal_uInt64(&pSfxDockingWindow
->GetBindings())));
722 aCreationArguments
.put("Theme", Theme::GetPropertySet());
723 aCreationArguments
.put("Sidebar", makeAny(Reference
<ui::XSidebar
>(static_cast<ui::XSidebar
*>(this))));
726 Reference
<rendering::XSpriteCanvas
> xCanvas (VCLUnoHelper::GetWindow(rxWindow
)->GetSpriteCanvas());
727 aCreationArguments
.put("Canvas", makeAny(xCanvas
));
729 aCreationArguments
.put("ApplicationName", makeAny(rContext
.msApplication
));
730 aCreationArguments
.put("ContextName", makeAny(rContext
.msContext
));
732 Reference
<ui::XUIElement
> xUIElement(
733 xUIElementFactory
->createUIElement(
735 Sequence
<beans::PropertyValue
>(aCreationArguments
.getPropertyValues())),
740 catch(const Exception
& rException
)
742 SAL_WARN("sfx.sidebar", "Cannot create panel: " << rException
.Message
);
747 IMPL_LINK(SidebarController
, WindowEventHandler
, VclWindowEvent
*, pEvent
)
750 return sal_IntPtr(false);
752 if (pEvent
->GetWindow() == mpParentWindow
)
754 switch (pEvent
->GetId())
756 case VCLEVENT_WINDOW_SHOW
:
757 case VCLEVENT_WINDOW_RESIZE
:
761 case VCLEVENT_WINDOW_DATACHANGED
:
762 // Force an update of deck and tab bar to reflect
763 // changes in theme (high contrast mode).
764 Theme::HandleDataChange();
765 UpdateTitleBarIcons();
766 mpParentWindow
->Invalidate();
767 mnRequestedForceFlags
|= SwitchFlag_ForceNewDeck
| SwitchFlag_ForceNewPanels
;
768 maAsynchronousDeckSwitch
.CancelRequest();
769 maContextChangeUpdate
.RequestCall();
776 case VCLEVENT_WINDOW_PAINT
:
784 else if (pEvent
->GetWindow()==mpSplitWindow
&& mpSplitWindow
!=nullptr)
786 switch (pEvent
->GetId())
788 case VCLEVENT_WINDOW_MOUSEBUTTONDOWN
:
789 mnWidthOnSplitterButtonDown
= mpParentWindow
->GetSizePixel().Width();
792 case VCLEVENT_WINDOW_MOUSEBUTTONUP
:
794 ProcessNewWidth(mpParentWindow
->GetSizePixel().Width());
795 mnWidthOnSplitterButtonDown
= 0;
805 return sal_IntPtr(true);
808 void SidebarController::ShowPopupMenu (
809 const Rectangle
& rButtonBox
,
810 const ::std::vector
<TabBar::DeckMenuData
>& rMenuData
) const
812 ::boost::shared_ptr
<PopupMenu
> pMenu
= CreatePopupMenu(rMenuData
);
813 pMenu
->SetSelectHdl(LINK(const_cast<SidebarController
*>(this), SidebarController
, OnMenuItemSelected
));
815 // pass toolbox button rect so the menu can stay open on button up
816 Rectangle
aBox (rButtonBox
);
817 aBox
.Move(mpTabBar
->GetPosPixel().X(), 0);
818 pMenu
->Execute(mpParentWindow
, aBox
, PopupMenuFlags::ExecuteDown
);
821 ::boost::shared_ptr
<PopupMenu
> SidebarController::CreatePopupMenu (
822 const ::std::vector
<TabBar::DeckMenuData
>& rMenuData
) const
824 // Create the top level popup menu.
825 ::boost::shared_ptr
<PopupMenu
> pMenu (new PopupMenu());
826 FloatingWindow
* pMenuWindow
= dynamic_cast<FloatingWindow
*>(pMenu
->GetWindow());
827 if (pMenuWindow
!= NULL
)
829 pMenuWindow
->SetPopupModeFlags(pMenuWindow
->GetPopupModeFlags() | FloatWinPopupFlags::NoMouseUpClose
);
832 // Create sub menu for customization (hiding of deck tabs.)
833 PopupMenu
* pCustomizationMenu
= new PopupMenu();
835 SidebarResource aLocalResource
;
837 // Add one entry for every tool panel element to individually make
838 // them visible or hide them.
839 sal_Int32
nIndex (0);
840 for(::std::vector
<TabBar::DeckMenuData
>::const_iterator
841 iItem(rMenuData
.begin()),
842 iEnd(rMenuData
.end());
846 const sal_Int32
nMenuIndex (nIndex
+MID_FIRST_PANEL
);
847 pMenu
->InsertItem(nMenuIndex
, iItem
->msDisplayName
, MenuItemBits::RADIOCHECK
);
848 pMenu
->CheckItem(nMenuIndex
, iItem
->mbIsCurrentDeck
);
849 pMenu
->EnableItem(nMenuIndex
, iItem
->mbIsEnabled
&&iItem
->mbIsActive
);
851 const sal_Int32
nSubMenuIndex (nIndex
+MID_FIRST_HIDE
);
852 if (iItem
->mbIsCurrentDeck
)
854 // Don't allow the currently visible deck to be disabled.
855 pCustomizationMenu
->InsertItem(nSubMenuIndex
, iItem
->msDisplayName
, MenuItemBits::RADIOCHECK
);
856 pCustomizationMenu
->CheckItem(nSubMenuIndex
, true);
860 pCustomizationMenu
->InsertItem(nSubMenuIndex
, iItem
->msDisplayName
, MenuItemBits::CHECKABLE
);
861 pCustomizationMenu
->CheckItem(nSubMenuIndex
, iItem
->mbIsActive
);
865 pMenu
->InsertSeparator();
867 // Add entry for docking or un-docking the tool panel.
868 if (mpParentWindow
->IsFloatingMode())
869 pMenu
->InsertItem(MID_LOCK_TASK_PANEL
, SFX2_RESSTR(STR_SFX_DOCK
));
871 pMenu
->InsertItem(MID_UNLOCK_TASK_PANEL
, SFX2_RESSTR(STR_SFX_UNDOCK
));
873 pMenu
->InsertItem(MID_HIDE_SIDEBAR
, SFX2_RESSTR(STRING_HIDE_SIDEBAR
));
874 pCustomizationMenu
->InsertSeparator();
875 pCustomizationMenu
->InsertItem(MID_RESTORE_DEFAULT
, SFX2_RESSTR(STRING_RESTORE
));
877 pMenu
->InsertItem(MID_CUSTOMIZATION
, SFX2_RESSTR(STRING_CUSTOMIZATION
));
878 pMenu
->SetPopupMenu(MID_CUSTOMIZATION
, pCustomizationMenu
);
880 pMenu
->RemoveDisabledEntries(false, false);
885 IMPL_LINK(SidebarController
, OnMenuItemSelected
, Menu
*, pMenu
)
889 OSL_ENSURE(pMenu
!=NULL
, "sfx2::sidebar::SidebarController::OnMenuItemSelected: illegal menu!");
894 const sal_Int32
nIndex (pMenu
->GetCurItemId());
897 case MID_UNLOCK_TASK_PANEL
:
898 mpParentWindow
->SetFloatingMode(true);
901 case MID_LOCK_TASK_PANEL
:
902 mpParentWindow
->SetFloatingMode(false);
905 case MID_RESTORE_DEFAULT
:
906 mpTabBar
->RestoreHideFlags();
909 case MID_HIDE_SIDEBAR
:
911 const util::URL
aURL (Tools::GetURL(gsHideSidebarCommandName
));
912 Reference
<frame::XDispatch
> mxDispatch (Tools::GetDispatch(mxFrame
, aURL
));
914 mxDispatch
->dispatch(aURL
, Sequence
<beans::PropertyValue
>());
921 if (nIndex
>= MID_FIRST_PANEL
&& nIndex
<MID_FIRST_HIDE
)
924 SwitchToDeck(mpTabBar
->GetDeckIdForIndex(nIndex
- MID_FIRST_PANEL
));
926 else if (nIndex
>=MID_FIRST_HIDE
)
927 if (pMenu
->GetItemBits(nIndex
) == MenuItemBits::CHECKABLE
)
928 mpTabBar
->ToggleHideFlag(nIndex
-MID_FIRST_HIDE
);
930 catch (RuntimeException
&)
940 void SidebarController::RequestCloseDeck()
942 mbIsDeckRequestedOpen
= false;
943 UpdateDeckOpenState();
945 // remove highlight from TabBar, because Deck will be closed
946 mpTabBar
->RemoveDeckHighlight();
949 void SidebarController::RequestOpenDeck()
951 mbIsDeckRequestedOpen
= true;
952 UpdateDeckOpenState();
955 bool SidebarController::IsDeckVisible(const OUString
& rsDeckId
)
957 return mbIsDeckOpen
&& mbIsDeckOpen
.get() && msCurrentDeckId
== rsDeckId
;
960 void SidebarController::UpdateDeckOpenState()
962 if ( ! mbIsDeckRequestedOpen
)
963 // No state requested.
966 sal_Int32 nTabBarDefaultWidth
= TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor();
968 // Update (change) the open state when it either has not yet been initialized
969 // or when its value differs from the requested state.
971 || mbIsDeckOpen
.get() != mbIsDeckRequestedOpen
.get())
973 if (mbIsDeckRequestedOpen
.get())
975 if (mnSavedSidebarWidth
<= nTabBarDefaultWidth
)
976 SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow
));
978 SetChildWindowWidth(mnSavedSidebarWidth
);
982 if ( ! mpParentWindow
->IsFloatingMode())
983 mnSavedSidebarWidth
= SetChildWindowWidth(nTabBarDefaultWidth
);
984 if (mnWidthOnSplitterButtonDown
> nTabBarDefaultWidth
)
985 mnSavedSidebarWidth
= mnWidthOnSplitterButtonDown
;
986 mpParentWindow
->SetStyle(mpParentWindow
->GetStyle() & ~WB_SIZEABLE
);
989 mbIsDeckOpen
= mbIsDeckRequestedOpen
.get();
990 if (mbIsDeckOpen
.get() && mpCurrentDeck
)
991 mpCurrentDeck
->Show(mbIsDeckOpen
.get());
996 bool SidebarController::CanModifyChildWindowWidth()
998 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
999 if (pSplitWindow
== NULL
)
1002 sal_uInt16
nRow (0xffff);
1003 sal_uInt16
nColumn (0xffff);
1004 if (pSplitWindow
->GetWindowPos(mpParentWindow
, nColumn
, nRow
))
1006 sal_uInt16
nRowCount (pSplitWindow
->GetWindowCount(nColumn
));
1007 return nRowCount
==1;
1013 sal_Int32
SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth
)
1015 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1016 if (pSplitWindow
== NULL
)
1019 sal_uInt16
nRow (0xffff);
1020 sal_uInt16
nColumn (0xffff);
1021 pSplitWindow
->GetWindowPos(mpParentWindow
, nColumn
, nRow
);
1022 const long nColumnWidth (pSplitWindow
->GetLineSize(nColumn
));
1024 vcl::Window
* pWindow
= mpParentWindow
;
1025 const Size
aWindowSize (pWindow
->GetSizePixel());
1027 pSplitWindow
->MoveWindow(
1029 Size(nNewWidth
, aWindowSize
.Height()),
1032 static_cast<SplitWindow
*>(pSplitWindow
)->Split();
1034 return static_cast<sal_Int32
>(nColumnWidth
);
1037 void SidebarController::RestrictWidth (sal_Int32 nWidth
)
1039 SfxSplitWindow
* pSplitWindow
= GetSplitWindow();
1040 if (pSplitWindow
!= NULL
)
1042 const sal_uInt16
nId (pSplitWindow
->GetItemId(mpParentWindow
.get()));
1043 const sal_uInt16
nSetId (pSplitWindow
->GetSet(nId
));
1044 pSplitWindow
->SetItemSizeRange(
1046 Range(TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor() + nWidth
,
1047 gnMaximumSidebarWidth
* mpTabBar
->GetDPIScaleFactor()));
1051 SfxSplitWindow
* SidebarController::GetSplitWindow()
1053 if (mpParentWindow
!= nullptr)
1055 SfxSplitWindow
* pSplitWindow
= dynamic_cast<SfxSplitWindow
*>(mpParentWindow
->GetParent());
1056 if (pSplitWindow
!= mpSplitWindow
)
1058 if (mpSplitWindow
!= nullptr)
1059 mpSplitWindow
->RemoveEventListener(LINK(this, SidebarController
, WindowEventHandler
));
1061 mpSplitWindow
= pSplitWindow
;
1063 if (mpSplitWindow
!= nullptr)
1064 mpSplitWindow
->AddEventListener(LINK(this, SidebarController
, WindowEventHandler
));
1066 return mpSplitWindow
;
1072 void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag
)
1074 if (mpParentWindow
== nullptr)
1077 if (bCloseAfterDrag
)
1079 // Make sure that the indicator exists.
1080 if ( ! mpCloseIndicator
)
1082 mpCloseIndicator
.reset(VclPtr
<FixedImage
>::Create(mpParentWindow
));
1083 FixedImage
* pFixedImage
= static_cast<FixedImage
*>(mpCloseIndicator
.get());
1084 const Image
aImage (Theme::GetImage(Theme::Image_CloseIndicator
));
1085 pFixedImage
->SetImage(aImage
);
1086 pFixedImage
->SetSizePixel(aImage
.GetSizePixel());
1087 pFixedImage
->SetBackground(Theme::GetWallpaper(Theme::Paint_DeckBackground
));
1090 // Place and show the indicator.
1091 const Size
aWindowSize (mpParentWindow
->GetSizePixel());
1092 const Size
aImageSize (mpCloseIndicator
->GetSizePixel());
1093 mpCloseIndicator
->SetPosPixel(
1095 aWindowSize
.Width() - TabBar::GetDefaultWidth() * mpTabBar
->GetDPIScaleFactor() - aImageSize
.Width(),
1096 (aWindowSize
.Height() - aImageSize
.Height())/2));
1097 mpCloseIndicator
->Show();
1101 // Hide but don't delete the indicator.
1102 if (mpCloseIndicator
)
1103 mpCloseIndicator
->Hide();
1107 void SidebarController::UpdateTitleBarIcons()
1109 if ( ! mpCurrentDeck
)
1112 const bool bIsHighContrastModeActive (Theme::IsHighContrastMode());
1113 const ResourceManager
& rResourceManager (ResourceManager::Instance());
1115 // Update the deck icon.
1116 const DeckDescriptor
* pDeckDescriptor
= rResourceManager
.GetDeckDescriptor(mpCurrentDeck
->GetId());
1117 if (pDeckDescriptor
!= NULL
&& mpCurrentDeck
->GetTitleBar())
1119 const OUString
sIconURL(
1120 bIsHighContrastModeActive
1121 ? pDeckDescriptor
->msHighContrastTitleBarIconURL
1122 : pDeckDescriptor
->msTitleBarIconURL
);
1123 mpCurrentDeck
->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL
, mxFrame
));
1126 // Update the panel icons.
1127 const SharedPanelContainer
& rPanels (mpCurrentDeck
->GetPanels());
1128 for (SharedPanelContainer::const_iterator
1129 iPanel(rPanels
.begin()), iEnd(rPanels
.end());
1135 if ((*iPanel
)->GetTitleBar() == NULL
)
1137 const PanelDescriptor
* pPanelDescriptor
= rResourceManager
.GetPanelDescriptor((*iPanel
)->GetId());
1138 if (pPanelDescriptor
== NULL
)
1140 const OUString
sIconURL (
1141 bIsHighContrastModeActive
1142 ? pPanelDescriptor
->msHighContrastTitleBarIconURL
1143 : pPanelDescriptor
->msTitleBarIconURL
);
1144 (*iPanel
)->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL
, mxFrame
));
1148 void SidebarController::ShowPanel (const Panel
& rPanel
)
1151 mpCurrentDeck
->ShowPanel(rPanel
);
1154 } } // end of namespace sfx2::sidebar
1156 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */