bump product version to 4.1.6.2
[LibreOffice.git] / sfx2 / source / sidebar / SidebarController.cxx
blobe49465e18253e9e5dda3d28764fd2d244ab7367f
1 /*
2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 #include "SidebarController.hxx"
19 #include "Deck.hxx"
20 #include "DeckTitleBar.hxx"
21 #include "Panel.hxx"
22 #include "PanelTitleBar.hxx"
23 #include "SidebarPanel.hxx"
24 #include "SidebarResource.hxx"
25 #include "TabBar.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 "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/componentcontext.hxx>
44 #include <comphelper/namedvaluecollection.hxx>
46 #include <com/sun/star/frame/XDispatchProvider.hpp>
47 #include <com/sun/star/lang/XInitialization.hpp>
48 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
49 #include <com/sun/star/ui/ContextChangeEventObject.hpp>
50 #include <com/sun/star/ui/XUIElementFactory.hpp>
51 #include <com/sun/star/util/XURLTransformer.hpp>
52 #include <com/sun/star/util/URL.hpp>
53 #include <com/sun/star/rendering/XSpriteCanvas.hpp>
55 #include <boost/bind.hpp>
56 #include <boost/function.hpp>
57 #include <boost/scoped_array.hpp>
60 using namespace css;
61 using namespace cssu;
62 using ::rtl::OUString;
65 #undef VERBOSE
67 namespace
69 const static char gsReadOnlyCommandName[] = ".uno:EditDoc";
70 const static sal_Int32 gnMaximumSidebarWidth (400);
71 const static sal_Int32 gnWidthCloseThreshold (70);
72 const static sal_Int32 gnWidthOpenThreshold (40);
76 namespace sfx2 { namespace sidebar {
78 namespace {
79 enum MenuId
81 MID_UNLOCK_TASK_PANEL = 1,
82 MID_LOCK_TASK_PANEL,
83 MID_CUSTOMIZATION,
84 MID_RESTORE_DEFAULT,
85 MID_FIRST_PANEL,
86 MID_FIRST_HIDE = 1000
89 /** When in doubt, show this deck.
91 static const char gsDefaultDeckId[] = "PropertyDeck";
95 SidebarController::SidebarController (
96 SidebarDockingWindow* pParentWindow,
97 const cssu::Reference<css::frame::XFrame>& rxFrame)
98 : SidebarControllerInterfaceBase(m_aMutex),
99 mpCurrentDeck(),
100 mpParentWindow(pParentWindow),
101 mpTabBar(new TabBar(
102 mpParentWindow,
103 rxFrame,
104 ::boost::bind(&SidebarController::OpenThenSwitchToDeck, this, _1),
105 ::boost::bind(&SidebarController::ShowPopupMenu, this, _1,_2))),
106 mxFrame(rxFrame),
107 maCurrentContext(OUString(), OUString()),
108 maRequestedContext(),
109 mnRequestedForceFlags(SwitchFlag_NoForce),
110 msCurrentDeckId(gsDefaultDeckId),
111 msCurrentDeckTitle(),
112 maPropertyChangeForwarder(::boost::bind(&SidebarController::BroadcastPropertyChange, this)),
113 maContextChangeUpdate(::boost::bind(&SidebarController::UpdateConfigurations, this)),
114 mbIsDeckRequestedOpen(),
115 mbIsDeckOpen(),
116 mbCanDeckBeOpened(true),
117 mnSavedSidebarWidth(pParentWindow->GetSizePixel().Width()),
118 maFocusManager(::boost::bind(&SidebarController::ShowPanel, this, _1)),
119 mxReadOnlyModeDispatch(),
120 mbIsDocumentReadOnly(false),
121 mpSplitWindow(NULL),
122 mnWidthOnSplitterButtonDown(0),
123 mpCloseIndicator()
125 if (pParentWindow == NULL)
127 OSL_ASSERT(pParentWindow!=NULL);
128 return;
131 // Listen for context change events.
132 cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
133 css::ui::ContextChangeEventMultiplexer::get(
134 ::comphelper::getProcessComponentContext()));
135 if (xMultiplexer.is())
136 xMultiplexer->addContextChangeEventListener(
137 static_cast<css::ui::XContextChangeEventListener*>(this),
138 mxFrame->getController());
140 // Listen for window events.
141 mpParentWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler));
143 // Listen for theme property changes.
144 Theme::GetPropertySet()->addPropertyChangeListener(
145 A2S(""),
146 static_cast<css::beans::XPropertyChangeListener*>(this));
148 // Get the dispatch object as preparation to listen for changes of
149 // the read-only state.
150 const util::URL aURL (Tools::GetURL(gsReadOnlyCommandName));
151 mxReadOnlyModeDispatch = Tools::GetDispatch(mxFrame, aURL);
152 if (mxReadOnlyModeDispatch.is())
153 mxReadOnlyModeDispatch->addStatusListener(this, aURL);
155 SwitchToDeck(A2S("default"));
161 SidebarController::~SidebarController (void)
168 void SAL_CALL SidebarController::disposing (void)
170 maFocusManager.Clear();
172 cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
173 css::ui::ContextChangeEventMultiplexer::get(
174 ::comphelper::getProcessComponentContext()));
175 if (xMultiplexer.is())
176 xMultiplexer->removeAllContextChangeEventListeners(
177 static_cast<css::ui::XContextChangeEventListener*>(this));
179 if (mxReadOnlyModeDispatch.is())
180 mxReadOnlyModeDispatch->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName));
181 if (mpSplitWindow != NULL)
183 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
184 mpSplitWindow = NULL;
187 if (mpParentWindow != NULL)
189 mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
190 mpParentWindow = NULL;
193 if (mpCurrentDeck)
195 mpCurrentDeck->Dispose();
196 mpCurrentDeck->PrintWindowTree();
197 mpCurrentDeck.reset();
200 mpTabBar.reset();
202 Theme::GetPropertySet()->removePropertyChangeListener(
203 A2S(""),
204 static_cast<css::beans::XPropertyChangeListener*>(this));
206 maContextChangeUpdate.CancelRequest();
212 void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent)
213 throw(cssu::RuntimeException)
215 // Update to the requested new context asynchronously to avoid
216 // subtle errors caused by SFX2 which in rare cases can not
217 // properly handle a synchronous update.
218 maRequestedContext = Context(
219 rEvent.ApplicationName,
220 rEvent.ContextName);
221 if (maRequestedContext != maCurrentContext)
222 maContextChangeUpdate.RequestCall();
228 void SAL_CALL SidebarController::disposing (const css::lang::EventObject& rEventObject)
229 throw(cssu::RuntimeException)
231 (void)rEventObject;
233 dispose();
239 void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& rEvent)
240 throw(cssu::RuntimeException)
242 (void)rEvent;
244 maPropertyChangeForwarder.RequestCall();
250 void SAL_CALL SidebarController::statusChanged (const css::frame::FeatureStateEvent& rEvent)
251 throw(cssu::RuntimeException)
253 bool bIsReadWrite (true);
254 if (rEvent.IsEnabled)
255 rEvent.State >>= bIsReadWrite;
257 if (mbIsDocumentReadOnly != !bIsReadWrite)
259 mbIsDocumentReadOnly = !bIsReadWrite;
261 // Force the current deck to update its panel list.
262 if ( ! mbIsDocumentReadOnly)
263 msCurrentDeckId = gsDefaultDeckId;
264 mnRequestedForceFlags |= SwitchFlag_ForceSwitch;
265 maContextChangeUpdate.RequestCall();
272 void SAL_CALL SidebarController::requestLayout (void)
273 throw(cssu::RuntimeException)
275 sal_Int32 nMinimalWidth = 0;
276 if (mpCurrentDeck)
278 mpCurrentDeck->RequestLayout();
279 nMinimalWidth = mpCurrentDeck->GetMinimalWidth();
281 RestrictWidth(nMinimalWidth);
287 void SidebarController::BroadcastPropertyChange (void)
289 DataChangedEvent aEvent (DATACHANGED_USER);
290 mpParentWindow->NotifyAllChildren(aEvent);
291 mpParentWindow->Invalidate(INVALIDATE_CHILDREN);
297 void SidebarController::NotifyResize (void)
299 if (mpTabBar == NULL)
301 OSL_ASSERT(mpTabBar!=NULL);
302 return;
305 Window* pParentWindow = mpTabBar->GetParent();
307 const sal_Int32 nWidth (pParentWindow->GetSizePixel().Width());
308 const sal_Int32 nHeight (pParentWindow->GetSizePixel().Height());
310 mbIsDeckOpen = (nWidth > TabBar::GetDefaultWidth());
312 if (mnSavedSidebarWidth <= 0)
313 mnSavedSidebarWidth = nWidth;
315 bool bIsDeckVisible;
316 if (mbCanDeckBeOpened)
318 const bool bIsOpening (nWidth > mnWidthOnSplitterButtonDown);
319 if (bIsOpening)
320 bIsDeckVisible = nWidth >= TabBar::GetDefaultWidth() + gnWidthOpenThreshold;
321 else
322 bIsDeckVisible = nWidth >= TabBar::GetDefaultWidth() + gnWidthCloseThreshold;
323 mbIsDeckRequestedOpen = bIsDeckVisible;
324 UpdateCloseIndicator(!bIsDeckVisible);
326 else
327 bIsDeckVisible = false;
329 // Place the deck.
330 if (mpCurrentDeck)
332 if (bIsDeckVisible)
334 mpCurrentDeck->setPosSizePixel(0,0, nWidth-TabBar::GetDefaultWidth(), nHeight);
335 mpCurrentDeck->Show();
336 mpCurrentDeck->RequestLayout();
338 else
339 mpCurrentDeck->Hide();
342 // Place the tab bar.
343 mpTabBar->setPosSizePixel(nWidth-TabBar::GetDefaultWidth(),0,TabBar::GetDefaultWidth(),nHeight);
344 mpTabBar->Show();
346 // Determine if the closer of the deck can be shown.
347 sal_Int32 nMinimalWidth = 0;
348 if (mpCurrentDeck)
350 DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar();
351 if (pTitleBar != NULL && pTitleBar->IsVisible())
352 pTitleBar->SetCloserVisible(CanModifyChildWindowWidth());
353 nMinimalWidth = mpCurrentDeck->GetMinimalWidth();
356 RestrictWidth(nMinimalWidth);
362 void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth)
364 if ( ! mbIsDeckRequestedOpen)
365 return;
367 if (mbIsDeckRequestedOpen.get())
369 // Deck became large enough to be shown. Show it.
370 mnSavedSidebarWidth = nNewWidth;
371 RequestOpenDeck();
373 else
375 // Deck became too small. Close it completely.
376 // If window is wider than the tab bar then mark the deck as being visible, even when it its not.
377 // This is to trigger an adjustment of the width to the width of the tab bar.
378 mbIsDeckOpen = true;
379 RequestCloseDeck();
381 if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth())
382 mnSavedSidebarWidth = mnWidthOnSplitterButtonDown;
389 void SidebarController::UpdateConfigurations (void)
391 if (maCurrentContext != maRequestedContext
392 || mnRequestedForceFlags!=SwitchFlag_NoForce)
394 maCurrentContext = maRequestedContext;
396 // Find the set of decks that could be displayed for the new context.
397 ResourceManager::DeckContextDescriptorContainer aDecks;
398 ResourceManager::Instance().GetMatchingDecks (
399 aDecks,
400 maCurrentContext,
401 mbIsDocumentReadOnly,
402 mxFrame);
404 // Notify the tab bar about the updated set of decks.
405 mpTabBar->SetDecks(aDecks);
407 // Find the new deck. By default that is the same as the old
408 // one. If that is not set or not enabled, then choose the
409 // first enabled deck.
410 OUString sNewDeckId;
411 for (ResourceManager::DeckContextDescriptorContainer::const_iterator
412 iDeck(aDecks.begin()),
413 iEnd(aDecks.end());
414 iDeck!=iEnd;
415 ++iDeck)
417 if (iDeck->mbIsEnabled)
419 if (iDeck->msId.equals(msCurrentDeckId))
421 sNewDeckId = msCurrentDeckId;
422 break;
424 else if (sNewDeckId.getLength() == 0)
425 sNewDeckId = iDeck->msId;
429 if (sNewDeckId.getLength() == 0)
431 // We did not find a valid deck.
432 RequestCloseDeck();
433 return;
436 // Tell the tab bar to highlight the button associated
437 // with the deck.
438 mpTabBar->HighlightDeck(sNewDeckId);
440 SwitchToDeck(
441 *ResourceManager::Instance().GetDeckDescriptor(sNewDeckId),
442 maCurrentContext);
444 #ifdef DEBUG
445 // Show the context name in the deck title bar.
446 if (mpCurrentDeck)
448 DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar();
449 if (pTitleBar != NULL)
450 pTitleBar->SetTitle(msCurrentDeckTitle+A2S(" (")+maCurrentContext.msContext+A2S(")"));
452 #endif
459 void SidebarController::OpenThenSwitchToDeck (
460 const ::rtl::OUString& rsDeckId)
462 RequestOpenDeck();
463 SwitchToDeck(rsDeckId);
469 void SidebarController::SwitchToDeck (
470 const ::rtl::OUString& rsDeckId)
472 if ( ! msCurrentDeckId.equals(rsDeckId)
473 || ! mbIsDeckOpen
474 || mnRequestedForceFlags!=SwitchFlag_NoForce)
476 const DeckDescriptor* pDeckDescriptor = ResourceManager::Instance().GetDeckDescriptor(rsDeckId);
477 if (pDeckDescriptor != NULL)
478 SwitchToDeck(*pDeckDescriptor, maCurrentContext);
485 void SidebarController::SwitchToDeck (
486 const DeckDescriptor& rDeckDescriptor,
487 const Context& rContext)
489 maFocusManager.Clear();
491 const bool bForceNewDeck ((mnRequestedForceFlags&SwitchFlag_ForceNewDeck)!=0);
492 const bool bForceNewPanels ((mnRequestedForceFlags&SwitchFlag_ForceNewPanels)!=0);
493 mnRequestedForceFlags = SwitchFlag_NoForce;
495 if ( ! msCurrentDeckId.equals(rDeckDescriptor.msId)
496 || bForceNewDeck)
498 // When the deck changes then destroy the deck and all panels
499 // and create everything new.
500 if (mpCurrentDeck)
502 mpCurrentDeck->Dispose();
503 mpCurrentDeck.reset();
506 msCurrentDeckId = rDeckDescriptor.msId;
508 mpTabBar->HighlightDeck(msCurrentDeckId);
510 // Determine the panels to display in the deck.
511 ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors;
512 ResourceManager::Instance().GetMatchingPanels(
513 aPanelContextDescriptors,
514 rContext,
515 rDeckDescriptor.msId,
516 mxFrame);
518 if (aPanelContextDescriptors.empty())
520 // There are no panels to be displayed in the current context.
521 if (EnumContext::GetContextEnum(rContext.msContext) != EnumContext::Context_Empty)
523 // Switch to the "empty" context and try again.
524 SwitchToDeck(
525 rDeckDescriptor,
526 Context(
527 rContext.msApplication,
528 EnumContext::GetContextName(EnumContext::Context_Empty)));
529 return;
531 else
533 // This is already the "empty" context. Looks like we have
534 // to live with an empty deck.
538 // Provide a configuration and Deck object.
539 if ( ! mpCurrentDeck)
541 mpCurrentDeck.reset(
542 new Deck(
543 rDeckDescriptor,
544 mpParentWindow,
545 ::boost::bind(&SidebarController::RequestCloseDeck, this)));
546 msCurrentDeckTitle = rDeckDescriptor.msTitle;
548 if ( ! mpCurrentDeck)
549 return;
551 // Update the panel list.
552 const sal_Int32 nNewPanelCount (aPanelContextDescriptors.size());
553 SharedPanelContainer aNewPanels;
554 const SharedPanelContainer& rCurrentPanels (mpCurrentDeck->GetPanels());
555 aNewPanels.resize(nNewPanelCount);
556 sal_Int32 nWriteIndex (0);
557 bool bHasPanelSetChanged (false);
558 for (sal_Int32 nReadIndex=0; nReadIndex<nNewPanelCount; ++nReadIndex)
560 const ResourceManager::PanelContextDescriptor& rPanelContexDescriptor (
561 aPanelContextDescriptors[nReadIndex]);
563 // Determine if the panel can be displayed.
564 const bool bIsPanelVisible (!mbIsDocumentReadOnly || rPanelContexDescriptor.mbShowForReadOnlyDocuments);
565 if ( ! bIsPanelVisible)
566 continue;
568 // Find the corresponding panel among the currently active
569 // panels.
570 SharedPanelContainer::const_iterator iPanel;
571 if (bForceNewPanels)
573 // All panels have to be created in any case. There is no
574 // point in searching already existing panels.
575 iPanel = rCurrentPanels.end();
577 else
579 iPanel = ::std::find_if(
580 rCurrentPanels.begin(),
581 rCurrentPanels.end(),
582 ::boost::bind(&Panel::HasIdPredicate, _1, ::boost::cref(rPanelContexDescriptor.msId)));
584 if (iPanel != rCurrentPanels.end())
586 // Panel already exists in current deck. Reuse it.
587 aNewPanels[nWriteIndex] = *iPanel;
588 aNewPanels[nWriteIndex]->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible);
590 else
592 // Panel does not yet exist or creation of new panels is forced.
593 // Create it.
594 aNewPanels[nWriteIndex] = CreatePanel(
595 rPanelContexDescriptor.msId,
596 mpCurrentDeck->GetPanelParentWindow(),
597 rPanelContexDescriptor.mbIsInitiallyVisible,
598 rContext);
599 bHasPanelSetChanged = true;
601 if (aNewPanels[nWriteIndex] != NULL)
603 // Depending on the context we have to change the command
604 // for the "more options" dialog.
605 PanelTitleBar* pTitleBar = aNewPanels[nWriteIndex]->GetTitleBar();
606 if (pTitleBar != NULL)
608 pTitleBar->SetMoreOptionsCommand(
609 rPanelContexDescriptor.msMenuCommand,
610 mxFrame);
613 ++nWriteIndex;
617 aNewPanels.resize(nWriteIndex);
619 // Activate the deck and the new set of panels.
620 mpCurrentDeck->setPosSizePixel(
623 mpParentWindow->GetSizePixel().Width()-TabBar::GetDefaultWidth(),
624 mpParentWindow->GetSizePixel().Height());
625 mpCurrentDeck->SetPanels(aNewPanels);
626 mpCurrentDeck->Show();
628 mpParentWindow->SetText(rDeckDescriptor.msTitle);
630 if (bHasPanelSetChanged)
631 NotifyResize();
633 // Tell the focus manager about the new panels and tab bar
634 // buttons.
635 maFocusManager.SetDeckTitle(mpCurrentDeck->GetTitleBar());
636 maFocusManager.SetPanels(aNewPanels);
637 mpTabBar->UpdateFocusManager(maFocusManager);
638 UpdateTitleBarIcons();
644 SharedPanel SidebarController::CreatePanel (
645 const OUString& rsPanelId,
646 ::Window* pParentWindow,
647 const bool bIsInitiallyExpanded,
648 const Context& rContext)
650 const PanelDescriptor* pPanelDescriptor = ResourceManager::Instance().GetPanelDescriptor(rsPanelId);
651 if (pPanelDescriptor == NULL)
652 return SharedPanel();
654 // Create the panel which is the parent window of the UIElement.
655 SharedPanel pPanel (new Panel(
656 *pPanelDescriptor,
657 pParentWindow,
658 bIsInitiallyExpanded,
659 ::boost::bind(&Deck::RequestLayout, mpCurrentDeck.get()),
660 ::boost::bind(&SidebarController::GetCurrentContext, this)));
662 // Create the XUIElement.
663 Reference<ui::XUIElement> xUIElement (CreateUIElement(
664 pPanel->GetComponentInterface(),
665 pPanelDescriptor->msImplementationURL,
666 pPanelDescriptor->mbWantsCanvas,
667 rContext));
668 if (xUIElement.is())
670 // Initialize the panel and add it to the active deck.
671 pPanel->SetUIElement(xUIElement);
673 else
675 pPanel.reset();
678 return pPanel;
684 Reference<ui::XUIElement> SidebarController::CreateUIElement (
685 const Reference<awt::XWindowPeer>& rxWindow,
686 const ::rtl::OUString& rsImplementationURL,
687 const bool bWantsCanvas,
688 const Context& rContext)
692 const ::comphelper::ComponentContext aComponentContext (::comphelper::getProcessServiceFactory());
693 const Reference<ui::XUIElementFactory> xUIElementFactory (
694 aComponentContext.createComponent("com.sun.star.ui.UIElementFactoryManager"),
695 UNO_QUERY_THROW);
697 // Create the XUIElement.
698 ::comphelper::NamedValueCollection aCreationArguments;
699 aCreationArguments.put("Frame", makeAny(mxFrame));
700 aCreationArguments.put("ParentWindow", makeAny(rxWindow));
701 SfxDockingWindow* pSfxDockingWindow = dynamic_cast<SfxDockingWindow*>(mpParentWindow);
702 if (pSfxDockingWindow != NULL)
703 aCreationArguments.put("SfxBindings", makeAny(sal_uInt64(&pSfxDockingWindow->GetBindings())));
704 aCreationArguments.put("Theme", Theme::GetPropertySet());
705 aCreationArguments.put("Sidebar", makeAny(Reference<ui::XSidebar>(static_cast<ui::XSidebar*>(this))));
706 if (bWantsCanvas)
708 Reference<rendering::XSpriteCanvas> xCanvas (VCLUnoHelper::GetWindow(rxWindow)->GetSpriteCanvas());
709 aCreationArguments.put("Canvas", makeAny(xCanvas));
711 aCreationArguments.put("ApplicationName", makeAny(rContext.msApplication));
712 aCreationArguments.put("ContextName", makeAny(rContext.msContext));
714 Reference<ui::XUIElement> xUIElement(
715 xUIElementFactory->createUIElement(
716 rsImplementationURL,
717 Sequence<beans::PropertyValue>(aCreationArguments.getPropertyValues())),
718 UNO_QUERY_THROW);
720 return xUIElement;
722 catch(Exception& rException)
724 OSL_TRACE("caught exception: %s",
725 OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr());
726 // For some reason we can not create the actual panel.
727 // Probably because its factory was not properly registered.
728 // TODO: provide feedback to developer to better pinpoint the
729 // source of the error.
731 return NULL;
738 IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent*, pEvent)
740 if (pEvent==NULL)
741 return sal_False;
743 if (pEvent->GetWindow() == mpParentWindow)
745 switch (pEvent->GetId())
747 case VCLEVENT_WINDOW_SHOW:
748 case VCLEVENT_WINDOW_RESIZE:
749 NotifyResize();
750 break;
752 case VCLEVENT_WINDOW_DATACHANGED:
753 // Force an update of deck and tab bar to reflect
754 // changes in theme (high contrast mode).
755 Theme::HandleDataChange();
756 UpdateTitleBarIcons();
757 mpParentWindow->Invalidate();
758 mnRequestedForceFlags |= SwitchFlag_ForceNewDeck | SwitchFlag_ForceNewPanels;
759 maContextChangeUpdate.RequestCall();
760 break;
762 case SFX_HINT_DYING:
763 dispose();
764 break;
766 case VCLEVENT_WINDOW_PAINT:
767 OSL_TRACE("Paint");
768 break;
770 default:
771 break;
774 else if (pEvent->GetWindow()==mpSplitWindow && mpSplitWindow!=NULL)
776 switch (pEvent->GetId())
778 case VCLEVENT_WINDOW_MOUSEBUTTONDOWN:
779 mnWidthOnSplitterButtonDown = mpParentWindow->GetSizePixel().Width();
780 break;
782 case VCLEVENT_WINDOW_MOUSEBUTTONUP:
784 ProcessNewWidth(mpParentWindow->GetSizePixel().Width());
785 mnWidthOnSplitterButtonDown = 0;
786 break;
789 case SFX_HINT_DYING:
790 dispose();
791 break;
795 return sal_True;
801 void SidebarController::ShowPopupMenu (
802 const Rectangle& rButtonBox,
803 const ::std::vector<TabBar::DeckMenuData>& rMenuData) const
805 ::boost::shared_ptr<PopupMenu> pMenu = CreatePopupMenu(rMenuData);
806 pMenu->SetSelectHdl(LINK(this, SidebarController, OnMenuItemSelected));
808 // pass toolbox button rect so the menu can stay open on button up
809 Rectangle aBox (rButtonBox);
810 aBox.Move(mpTabBar->GetPosPixel().X(), 0);
811 pMenu->Execute(mpParentWindow, aBox, POPUPMENU_EXECUTE_DOWN);
817 void SidebarController::ShowDetailMenu (const ::rtl::OUString& rsMenuCommand) const
821 const util::URL aURL (Tools::GetURL(rsMenuCommand));
822 Reference<frame::XDispatch> xDispatch (Tools::GetDispatch(mxFrame, aURL));
823 if (xDispatch.is())
824 xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>());
826 catch(Exception& rException)
828 OSL_TRACE("caught exception: %s",
829 OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr());
836 ::boost::shared_ptr<PopupMenu> SidebarController::CreatePopupMenu (
837 const ::std::vector<TabBar::DeckMenuData>& rMenuData) const
839 // Create the top level popup menu.
840 ::boost::shared_ptr<PopupMenu> pMenu (new PopupMenu());
841 FloatingWindow* pMenuWindow = dynamic_cast<FloatingWindow*>(pMenu->GetWindow());
842 if (pMenuWindow != NULL)
844 pMenuWindow->SetPopupModeFlags(pMenuWindow->GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOMOUSEUPCLOSE);
847 // Create sub menu for customization (hiding of deck tabs.)
848 PopupMenu* pCustomizationMenu = new PopupMenu();
850 SidebarResource aLocalResource;
852 // Add one entry for every tool panel element to individually make
853 // them visible or hide them.
854 sal_Int32 nIndex (0);
855 for(::std::vector<TabBar::DeckMenuData>::const_iterator
856 iItem(rMenuData.begin()),
857 iEnd(rMenuData.end());
858 iItem!=iEnd;
859 ++iItem,++nIndex)
861 const sal_Int32 nMenuIndex (nIndex+MID_FIRST_PANEL);
862 pMenu->InsertItem(nMenuIndex, iItem->msDisplayName, MIB_RADIOCHECK);
863 pMenu->CheckItem(nMenuIndex, iItem->mbIsCurrentDeck ? sal_True : sal_False);
864 pMenu->EnableItem(nMenuIndex, (iItem->mbIsEnabled&&iItem->mbIsActive) ? sal_True : sal_False);
866 const sal_Int32 nSubMenuIndex (nIndex+MID_FIRST_HIDE);
867 if (iItem->mbIsCurrentDeck)
869 // Don't allow the currently visible deck to be disabled.
870 pCustomizationMenu->InsertItem(nSubMenuIndex, iItem->msDisplayName, MIB_RADIOCHECK);
871 pCustomizationMenu->CheckItem(nSubMenuIndex, sal_True);
873 else
875 pCustomizationMenu->InsertItem(nSubMenuIndex, iItem->msDisplayName, MIB_CHECKABLE);
876 pCustomizationMenu->CheckItem(nSubMenuIndex, iItem->mbIsActive ? sal_True : sal_False);
880 pMenu->InsertSeparator();
882 // Add entry for docking or un-docking the tool panel.
883 if (mpParentWindow->IsFloatingMode())
884 pMenu->InsertItem(MID_LOCK_TASK_PANEL, String(SfxResId(STR_SFX_DOCK)));
885 else
886 pMenu->InsertItem(MID_UNLOCK_TASK_PANEL, String(SfxResId(STR_SFX_UNDOCK)));
888 pCustomizationMenu->InsertSeparator();
889 pCustomizationMenu->InsertItem(MID_RESTORE_DEFAULT, String(SfxResId(STRING_RESTORE)));
891 pMenu->InsertItem(MID_CUSTOMIZATION, String(SfxResId(STRING_CUSTOMIZATION)));
892 pMenu->SetPopupMenu(MID_CUSTOMIZATION, pCustomizationMenu);
894 pMenu->RemoveDisabledEntries(sal_False, sal_False);
896 return pMenu;
902 IMPL_LINK(SidebarController, OnMenuItemSelected, Menu*, pMenu)
904 if (pMenu == NULL)
906 OSL_ENSURE(pMenu!=NULL, "sfx2::sidebar::SidebarController::OnMenuItemSelected: illegal menu!");
907 return 0;
910 pMenu->Deactivate();
911 const sal_Int32 nIndex (pMenu->GetCurItemId());
912 switch (nIndex)
914 case MID_UNLOCK_TASK_PANEL:
915 mpParentWindow->SetFloatingMode(sal_True);
916 break;
918 case MID_LOCK_TASK_PANEL:
919 mpParentWindow->SetFloatingMode(sal_False);
920 break;
922 case MID_RESTORE_DEFAULT:
923 mpTabBar->RestoreHideFlags();
924 break;
926 default:
930 if (nIndex >= MID_FIRST_PANEL && nIndex<MID_FIRST_HIDE)
931 SwitchToDeck(mpTabBar->GetDeckIdForIndex(nIndex - MID_FIRST_PANEL));
932 else if (nIndex >=MID_FIRST_HIDE)
933 if (pMenu->GetItemBits(nIndex) == MIB_CHECKABLE)
934 mpTabBar->ToggleHideFlag(nIndex-MID_FIRST_HIDE);
936 catch (RuntimeException&)
940 break;
943 return 1;
949 void SidebarController::RequestCloseDeck (void)
951 mbIsDeckRequestedOpen = false;
952 UpdateDeckOpenState();
958 void SidebarController::RequestOpenDeck (void)
960 mbIsDeckRequestedOpen = true;
961 UpdateDeckOpenState();
967 void SidebarController::UpdateDeckOpenState (void)
969 if ( ! mbIsDeckRequestedOpen)
970 // No state requested.
971 return;
973 // Update (change) the open state when it either has not yet been initialized
974 // or when its value differs from the requested state.
975 if ( ! mbIsDeckOpen
976 || mbIsDeckOpen.get() != mbIsDeckRequestedOpen.get())
978 if (mbIsDeckRequestedOpen.get())
980 if (mnSavedSidebarWidth <= TabBar::GetDefaultWidth())
981 SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow));
982 else
983 SetChildWindowWidth(mnSavedSidebarWidth);
985 else
987 if ( ! mpParentWindow->IsFloatingMode())
988 mnSavedSidebarWidth = SetChildWindowWidth(TabBar::GetDefaultWidth());
989 if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth())
990 mnSavedSidebarWidth = mnWidthOnSplitterButtonDown;
991 mpParentWindow->SetStyle(mpParentWindow->GetStyle() & ~WB_SIZEABLE);
994 mbIsDeckOpen = mbIsDeckRequestedOpen.get();
995 if (mbIsDeckOpen.get() && mpCurrentDeck)
996 mpCurrentDeck->Show(mbIsDeckOpen.get());
997 NotifyResize();
1004 FocusManager& SidebarController::GetFocusManager (void)
1006 return maFocusManager;
1012 bool SidebarController::CanModifyChildWindowWidth (void)
1014 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1015 if (pSplitWindow == NULL)
1016 return false;
1018 sal_uInt16 nRow (0xffff);
1019 sal_uInt16 nColumn (0xffff);
1020 if (pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow))
1022 sal_uInt16 nRowCount (pSplitWindow->GetWindowCount(nColumn));
1023 return nRowCount==1;
1025 else
1026 return false;
1032 sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth)
1034 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1035 if (pSplitWindow == NULL)
1036 return 0;
1038 sal_uInt16 nRow (0xffff);
1039 sal_uInt16 nColumn (0xffff);
1040 pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow);
1041 const long nColumnWidth (pSplitWindow->GetLineSize(nColumn));
1043 Window* pWindow = mpParentWindow;
1044 const Size aWindowSize (pWindow->GetSizePixel());
1046 pSplitWindow->MoveWindow(
1047 mpParentWindow,
1048 Size(nNewWidth, aWindowSize.Height()),
1049 nColumn,
1050 nRow);
1051 static_cast<SplitWindow*>(pSplitWindow)->Split();
1053 return static_cast<sal_Int32>(nColumnWidth);
1059 void SidebarController::RestrictWidth (sal_Int32 nWidth)
1061 SfxSplitWindow* pSplitWindow = GetSplitWindow();
1062 if (pSplitWindow != NULL)
1064 const sal_uInt16 nId (pSplitWindow->GetItemId(mpParentWindow));
1065 const sal_uInt16 nSetId (pSplitWindow->GetSet(nId));
1066 pSplitWindow->SetItemSizeRange(
1067 nSetId,
1068 Range(TabBar::GetDefaultWidth() + nWidth, gnMaximumSidebarWidth));
1075 SfxSplitWindow* SidebarController::GetSplitWindow (void)
1077 if (mpParentWindow != NULL)
1079 SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent());
1080 if (pSplitWindow != mpSplitWindow)
1082 if (mpSplitWindow != NULL)
1083 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
1085 mpSplitWindow = pSplitWindow;
1087 if (mpSplitWindow != NULL)
1088 mpSplitWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler));
1090 return mpSplitWindow;
1092 else
1093 return NULL;
1099 void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag)
1101 if (mpParentWindow == NULL)
1102 return;
1104 if (bCloseAfterDrag)
1106 // Make sure that the indicator exists.
1107 if ( ! mpCloseIndicator)
1109 mpCloseIndicator.reset(new FixedImage(mpParentWindow));
1110 FixedImage* pFixedImage = static_cast<FixedImage*>(mpCloseIndicator.get());
1111 const Image aImage (Theme::GetImage(Theme::Image_CloseIndicator));
1112 pFixedImage->SetImage(aImage);
1113 pFixedImage->SetSizePixel(aImage.GetSizePixel());
1114 pFixedImage->SetBackground(Theme::GetWallpaper(Theme::Paint_DeckBackground));
1117 // Place and show the indicator.
1118 const Size aWindowSize (mpParentWindow->GetSizePixel());
1119 const Size aImageSize (mpCloseIndicator->GetSizePixel());
1120 mpCloseIndicator->SetPosPixel(
1121 Point(
1122 aWindowSize.Width() - TabBar::GetDefaultWidth() - aImageSize.Width(),
1123 (aWindowSize.Height() - aImageSize.Height())/2));
1124 mpCloseIndicator->Show();
1126 else
1128 // Hide but don't delete the indicator.
1129 if (mpCloseIndicator)
1130 mpCloseIndicator->Hide();
1137 void SidebarController::UpdateTitleBarIcons (void)
1139 if ( ! mpCurrentDeck)
1140 return;
1142 const bool bIsHighContrastModeActive (Theme::IsHighContrastMode());
1143 const ResourceManager& rResourceManager (ResourceManager::Instance());
1145 // Update the deck icon.
1146 const DeckDescriptor* pDeckDescriptor = rResourceManager.GetDeckDescriptor(mpCurrentDeck->GetId());
1147 if (pDeckDescriptor != NULL && mpCurrentDeck->GetTitleBar())
1149 const OUString sIconURL(
1150 bIsHighContrastModeActive
1151 ? pDeckDescriptor->msHighContrastTitleBarIconURL
1152 : pDeckDescriptor->msTitleBarIconURL);
1153 mpCurrentDeck->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
1156 // Update the panel icons.
1157 const SharedPanelContainer& rPanels (mpCurrentDeck->GetPanels());
1158 for (SharedPanelContainer::const_iterator
1159 iPanel(rPanels.begin()), iEnd(rPanels.end());
1160 iPanel!=iEnd;
1161 ++iPanel)
1163 if ( ! *iPanel)
1164 continue;
1165 if ((*iPanel)->GetTitleBar() == NULL)
1166 continue;
1167 const PanelDescriptor* pPanelDescriptor = rResourceManager.GetPanelDescriptor((*iPanel)->GetId());
1168 if (pPanelDescriptor == NULL)
1169 continue;
1170 const OUString sIconURL (
1171 bIsHighContrastModeActive
1172 ? pPanelDescriptor->msHighContrastTitleBarIconURL
1173 : pPanelDescriptor->msTitleBarIconURL);
1174 (*iPanel)->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
1181 void SidebarController::ShowPanel (const Panel& rPanel)
1183 if (mpCurrentDeck)
1184 mpCurrentDeck->ShowPanel(rPanel);
1190 Context SidebarController::GetCurrentContext (void) const
1192 return maCurrentContext;
1196 } } // end of namespace sfx2::sidebar