1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 #include "precompiled_sfx2.hxx"
23 
24 #include "SidebarController.hxx"
25 #include "Deck.hxx"
26 #include "DeckTitleBar.hxx"
27 #include "Panel.hxx"
28 #include "SidebarPanel.hxx"
29 #include "SidebarResource.hxx"
30 #include "TabBar.hxx"
31 #include "sfx2/sidebar/Theme.hxx"
32 #include "SidebarDockingWindow.hxx"
33 #include "Context.hxx"
34 #include "Tools.hxx"
35 
36 #include "sfxresid.hxx"
37 #include "sfx2/sfxsids.hrc"
38 #include "sfx2/titledockwin.hxx"
39 #include "sfxlocal.hrc"
40 #include <vcl/floatwin.hxx>
41 #include "splitwin.hxx"
42 #include <svl/smplhint.hxx>
43 #include <tools/link.hxx>
44 #include <comphelper/componentfactory.hxx>
45 #include <comphelper/processfactory.hxx>
46 #include <comphelper/componentcontext.hxx>
47 #include <comphelper/namedvaluecollection.hxx>
48 
49 #include <com/sun/star/frame/XDispatchProvider.hpp>
50 #include <com/sun/star/lang/XInitialization.hpp>
51 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
52 #include <com/sun/star/ui/ContextChangeEventObject.hpp>
53 #include <com/sun/star/ui/XUIElementFactory.hpp>
54 #include <com/sun/star/util/XURLTransformer.hpp>
55 #include <com/sun/star/util/URL.hpp>
56 
57 #include <boost/bind.hpp>
58 #include <boost/function.hpp>
59 #include <boost/scoped_array.hpp>
60 
61 
62 using namespace css;
63 using namespace cssu;
64 using ::rtl::OUString;
65 
66 
67 
68 namespace sfx2 { namespace sidebar {
69 
70 namespace {
71     enum MenuId
72     {
73         MID_UNLOCK_TASK_PANEL = 1,
74         MID_LOCK_TASK_PANEL,
75         MID_CUSTOMIZATION,
76         MID_RESTORE_DEFAULT,
77         MID_FIRST_PANEL,
78         MID_FIRST_HIDE = 1000
79     };
80 }
81 
82 
83 SidebarController::SidebarController (
84     SidebarDockingWindow* pParentWindow,
85     const cssu::Reference<css::frame::XFrame>& rxFrame)
86     : SidebarControllerInterfaceBase(m_aMutex),
87       mpCurrentDeck(),
88       mpParentWindow(pParentWindow),
89       mpTabBar(new TabBar(
90               mpParentWindow,
91               rxFrame,
92               ::boost::bind(&SidebarController::SwitchToDeck, this, _1),
93               ::boost::bind(&SidebarController::ShowPopupMenu, this, _1,_2,_3))),
94       mxFrame(rxFrame),
95       maCurrentContext(OUString(), OUString()),
96       msCurrentDeckId(A2S("PropertyDeck")),
97       maPropertyChangeForwarder(::boost::bind(&SidebarController::BroadcastPropertyChange, this)),
98       mbIsDeckClosed(false),
99       mnSavedSidebarWidth(pParentWindow->GetSizePixel().Width())
100 {
101     if (pParentWindow == NULL)
102     {
103         OSL_ASSERT(pParentWindow!=NULL);
104             return;
105     }
106 
107     // Listen for context change events.
108     cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
109         css::ui::ContextChangeEventMultiplexer::get(
110             ::comphelper::getProcessComponentContext()));
111     if (xMultiplexer.is())
112         xMultiplexer->addContextChangeEventListener(
113             static_cast<css::ui::XContextChangeEventListener*>(this),
114             mxFrame->getController());
115 
116     // Listen for window events.
117     mpParentWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler));
118 
119     // Listen for theme property changes.
120     Theme::GetPropertySet()->addPropertyChangeListener(
121         A2S(""),
122         static_cast<css::beans::XPropertyChangeListener*>(this));
123 
124     SwitchToDeck(A2S("default"));
125 }
126 
127 
128 
129 
130 SidebarController::~SidebarController (void)
131 {
132 }
133 
134 
135 
136 
137 void SAL_CALL SidebarController::disposing (void)
138 {
139     cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
140         css::ui::ContextChangeEventMultiplexer::get(
141             ::comphelper::getProcessComponentContext()));
142     if (xMultiplexer.is())
143         xMultiplexer->removeAllContextChangeEventListeners(
144             static_cast<css::ui::XContextChangeEventListener*>(this));
145 
146     if (mpParentWindow != NULL)
147     {
148         mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
149         mpParentWindow = NULL;
150     }
151 
152     if (mpCurrentDeck)
153     {
154         mpCurrentDeck->Dispose();
155         OSL_TRACE("deleting deck window subtree");
156         mpCurrentDeck->PrintWindowTree();
157         mpCurrentDeck.reset();
158     }
159 
160     mpTabBar.reset();
161 
162     Theme::GetPropertySet()->removePropertyChangeListener(
163         A2S(""),
164         static_cast<css::beans::XPropertyChangeListener*>(this));
165 }
166 
167 
168 
169 
170 void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent)
171     throw(cssu::RuntimeException)
172 {
173     UpdateConfigurations(
174         Context(
175             rEvent.ApplicationName,
176             rEvent.ContextName));
177 }
178 
179 
180 
181 
182 void SAL_CALL SidebarController::disposing (const css::lang::EventObject& rEventObject)
183     throw(cssu::RuntimeException)
184 {
185     (void)rEventObject;
186 
187     dispose();
188 }
189 
190 
191 
192 
193 void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& rEvent)
194     throw(cssu::RuntimeException)
195 {
196     (void)rEvent;
197 
198     maPropertyChangeForwarder.RequestCall();
199 }
200 
201 
202 
203 
204 void SAL_CALL SidebarController::requestLayout (void)
205     throw(cssu::RuntimeException)
206 {
207     if (mpCurrentDeck)
208         mpCurrentDeck->RequestLayout();
209     RestrictWidth();
210 }
211 
212 
213 
214 
215 void SidebarController::BroadcastPropertyChange (void)
216 {
217     DataChangedEvent aEvent (DATACHANGED_USER);
218     mpParentWindow->NotifyAllChilds(aEvent);
219     mpParentWindow->Invalidate(INVALIDATE_CHILDREN);
220 }
221 
222 
223 
224 
225 void SidebarController::NotifyResize (void)
226 {
227     if (mpTabBar == NULL)
228     {
229         OSL_ASSERT(mpTabBar!=NULL);
230         return;
231     }
232 
233     Window* pParentWindow = mpTabBar->GetParent();
234 
235     const sal_Int32 nWidth (pParentWindow->GetSizePixel().Width());
236     const sal_Int32 nHeight (pParentWindow->GetSizePixel().Height());
237 
238     // Place the deck.
239     if (mpCurrentDeck)
240     {
241         mpCurrentDeck->SetPosSizePixel(0,0, nWidth-TabBar::GetDefaultWidth(), nHeight);
242         mpCurrentDeck->Show();
243         mpCurrentDeck->RequestLayout();
244     }
245 
246     // Place the tab bar.
247     mpTabBar->SetPosSizePixel(nWidth-TabBar::GetDefaultWidth(),0,TabBar::GetDefaultWidth(),nHeight);
248     mpTabBar->Show();
249 
250     // Determine if the closer of the deck can be shown.
251     if (mpCurrentDeck)
252     {
253         DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar();
254         if (pTitleBar != NULL && pTitleBar->IsVisible())
255             pTitleBar->SetCloserVisible(CanModifyChildWindowWidth());
256     }
257 
258     if (nWidth > TabBar::GetDefaultWidth())
259         mnSavedSidebarWidth = nWidth;
260 
261     RestrictWidth();
262 #ifdef DEBUG
263     if (mpCurrentDeck)
264     {
265         mpCurrentDeck->PrintWindowTree();
266         sal_Int32 nPanelIndex (0);
267         for (SharedPanelContainer::const_iterator
268                  iPanel(mpCurrentDeck->GetPanels().begin()),
269                  iEnd(mpCurrentDeck->GetPanels().end());
270              iPanel!=iEnd;
271              ++iPanel,++nPanelIndex)
272         {
273             OSL_TRACE("panel %d:", nPanelIndex);
274             (*iPanel)->PrintWindowTree();
275         }
276     }
277 #endif
278 }
279 
280 
281 
282 
283 void SidebarController::UpdateConfigurations (const Context& rContext)
284 {
285     if (maCurrentContext != rContext)
286     {
287         maCurrentContext = rContext;
288 
289         // Notify the tab bar about the updated set of decks.
290         ResourceManager::IdContainer aDeckIds;
291         ResourceManager::Instance().GetMatchingDecks (
292             aDeckIds,
293             rContext,
294             mxFrame);
295         mpTabBar->SetDecks(aDeckIds);
296 
297         // Check if the current deck is among the matching decks.
298         bool bCurrentDeckMatches (false);
299         for (ResourceManager::IdContainer::const_iterator
300                  iDeck(aDeckIds.begin()),
301                  iEnd(aDeckIds.end());
302              iDeck!=iEnd;
303              ++iDeck)
304         {
305             if (iDeck->equals(msCurrentDeckId))
306             {
307                 bCurrentDeckMatches = true;
308                 break;
309             }
310         }
311 
312         DeckDescriptor const* pDeckDescriptor = NULL;
313         if ( ! bCurrentDeckMatches)
314             pDeckDescriptor = ResourceManager::Instance().GetBestMatchingDeck(rContext, mxFrame);
315         else
316             pDeckDescriptor = ResourceManager::Instance().GetDeckDescriptor(msCurrentDeckId);
317         if (pDeckDescriptor != NULL)
318         {
319             msCurrentDeckId = pDeckDescriptor->msId;
320             SwitchToDeck(*pDeckDescriptor, rContext);
321         }
322 
323 #ifdef DEBUG
324         // Show the context name in the deck title bar.
325         if (mpCurrentDeck)
326         {
327             DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar();
328             if (pTitleBar != NULL)
329                 pTitleBar->SetTitle(msCurrentDeckTitle+A2S(" (")+rContext.msContext+A2S(")"));
330         }
331 #endif
332     }
333 }
334 
335 
336 
337 
338 void SidebarController::SwitchToDeck (
339     const ::rtl::OUString& rsDeckId)
340 {
341     if ( ! msCurrentDeckId.equals(rsDeckId) || mbIsDeckClosed)
342     {
343         const DeckDescriptor* pDeckDescriptor = ResourceManager::Instance().GetDeckDescriptor(rsDeckId);
344         if (pDeckDescriptor != NULL)
345             SwitchToDeck(*pDeckDescriptor, maCurrentContext);
346     }
347 }
348 
349 
350 
351 
352 void SidebarController::SwitchToDeck (
353     const DeckDescriptor& rDeckDescriptor,
354     const Context& rContext)
355 {
356     if ( ! msCurrentDeckId.equals(rDeckDescriptor.msId))
357     {
358         // When the deck changes then destroy the deck and all panels
359         // and create everything new.
360         if (mpCurrentDeck)
361         {
362             mpCurrentDeck->Dispose();
363             mpCurrentDeck.reset();
364         }
365 
366         msCurrentDeckId = rDeckDescriptor.msId;
367     }
368 
369     // Reopen the deck when necessary.
370     OpenDeck();
371 
372     // Determine the panels to display in the deck.
373     ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors;
374     ResourceManager::Instance().GetMatchingPanels(
375         aPanelContextDescriptors,
376         rContext,
377         rDeckDescriptor.msId,
378         mxFrame);
379 
380     if (aPanelContextDescriptors.empty())
381     {
382         // There are no panels to be displayed in the current context.
383         if (EnumContext::GetContextEnum(rContext.msContext) != EnumContext::Context_Empty)
384         {
385             // Switch to the "empty" context and try again.
386             SwitchToDeck(
387                 rDeckDescriptor,
388                 Context(
389                     rContext.msApplication,
390                     EnumContext::GetContextName(EnumContext::Context_Empty)));
391             return;
392         }
393         else
394         {
395             // This is already the "empty" context. Looks like we have
396             // to live with an empty deck.
397         }
398     }
399 
400     if (mpCurrentDeck
401         && ArePanelSetsEqual(mpCurrentDeck->GetPanels(), aPanelContextDescriptors))
402     {
403         // Requested set of panels is identical to the current set of
404         // panels => Nothing to do.
405         return;
406     }
407 
408     // Provide a configuration and Deck object.
409     if ( ! mpCurrentDeck)
410     {
411         mpCurrentDeck.reset(
412             new Deck(
413                 rDeckDescriptor,
414                 mpParentWindow,
415                 ::boost::bind(&SidebarController::CloseDeck, this)));
416         msCurrentDeckTitle = rDeckDescriptor.msTitle;
417     }
418     if ( ! mpCurrentDeck)
419         return;
420 
421     // Update the panel list.
422     const sal_Int32 nNewPanelCount (aPanelContextDescriptors.size());
423     SharedPanelContainer aNewPanels;
424     const SharedPanelContainer& rCurrentPanels (mpCurrentDeck->GetPanels());
425     aNewPanels.resize(nNewPanelCount);
426     sal_Int32 nWriteIndex (0);
427     bool bHasPanelSetChanged (false);
428     for (sal_Int32 nReadIndex=0; nReadIndex<nNewPanelCount; ++nReadIndex)
429     {
430         const ResourceManager::PanelContextDescriptor& rPanelContexDescriptor (
431             aPanelContextDescriptors[nReadIndex]);
432 
433         // Find the corresponding panel among the currently active
434         // panels.
435         SharedPanelContainer::const_iterator iPanel (::std::find_if(
436                 rCurrentPanels.begin(),
437                 rCurrentPanels.end(),
438                 ::boost::bind(&Panel::HasIdPredicate, _1, ::boost::cref(rPanelContexDescriptor.msId))));
439         if (iPanel != rCurrentPanels.end())
440         {
441             // Panel already exists in current deck.  Reuse it.
442             aNewPanels[nWriteIndex] = *iPanel;
443             OSL_TRACE("    reusing panel %s", S2A(rPanelContexDescriptor.msId));
444         }
445         else
446         {
447             // Panel does not yet exist.  Create it.
448             aNewPanels[nWriteIndex] = CreatePanel(
449                 rPanelContexDescriptor.msId,
450                 mpCurrentDeck->GetPanelParentWindow(),
451                 rPanelContexDescriptor.msMenuCommand);
452             OSL_TRACE("    creating panel %s", S2A(rPanelContexDescriptor.msId));
453             bHasPanelSetChanged = true;
454         }
455         if (aNewPanels[nWriteIndex] != NULL)
456         {
457             // Depending on the context we have to collapse the panel.
458             aNewPanels[nWriteIndex]->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible);
459 
460             ++nWriteIndex;
461         }
462 
463     }
464     aNewPanels.resize(nWriteIndex);
465 
466     // Activate the deck and the new set of panels.
467     mpCurrentDeck->SetPosSizePixel(
468         0,
469         0,
470         mpParentWindow->GetSizePixel().Width()-TabBar::GetDefaultWidth(),
471         mpParentWindow->GetSizePixel().Height());
472     mpCurrentDeck->SetPanels(aNewPanels);
473     mpCurrentDeck->Show();
474 
475     // Tell the tab bar to highlight the button associated with the
476     // deck.
477     mpTabBar->HighlightDeck(rDeckDescriptor.msId);
478 
479     mpParentWindow->SetText(rDeckDescriptor.msTitle);
480 
481     if (bHasPanelSetChanged)
482         NotifyResize();
483 }
484 
485 
486 
487 
488 bool SidebarController::ArePanelSetsEqual (
489     const SharedPanelContainer& rCurrentPanels,
490     const ResourceManager::PanelContextDescriptorContainer& rRequestedPanels)
491 {
492     OSL_TRACE("current panel list:");
493     for (SharedPanelContainer::const_iterator
494              iPanel(rCurrentPanels.begin()),
495              iEnd(rCurrentPanels.end());
496          iPanel!=iEnd;
497          ++iPanel)
498     {
499         OSL_TRACE("    panel %s", S2A((*iPanel)->GetId()));
500     }
501 
502     OSL_TRACE("requested panels: ");
503     for (ResourceManager::PanelContextDescriptorContainer::const_iterator
504              iId(rRequestedPanels.begin()),
505              iEnd(rRequestedPanels.end());
506          iId!=iEnd;
507          ++iId)
508     {
509         OSL_TRACE("    panel %s", S2A(iId->msId));
510     }
511 
512     if (rCurrentPanels.size() != rRequestedPanels.size())
513         return false;
514     for (sal_Int32 nIndex=0,nCount=rCurrentPanels.size(); nIndex<nCount; ++nIndex)
515     {
516         if (rCurrentPanels[nIndex] == NULL)
517             return false;
518         if ( ! rCurrentPanels[nIndex]->GetId().equals(rRequestedPanels[nIndex].msId))
519             return false;
520     }
521     return true;
522 }
523 
524 
525 
526 
527 SharedPanel SidebarController::CreatePanel (
528     const OUString& rsPanelId,
529     ::Window* pParentWindow,
530     const OUString& rsMenuCommand)
531 {
532     const PanelDescriptor* pPanelDescriptor = ResourceManager::Instance().GetPanelDescriptor(rsPanelId);
533     if (pPanelDescriptor == NULL)
534         return SharedPanel();
535 
536 #ifdef DEBUG
537     // Prevent the panel not being created in the same memory of an old panel.
538     ::boost::scoped_array<char> pUnused (new char[sizeof(Panel)]);
539     OSL_TRACE("allocated memory at %x", pUnused.get());
540 #endif
541 
542     // Create the panel which is the parent window of the UIElement.
543     SharedPanel pPanel (new Panel(
544         *pPanelDescriptor,
545         pParentWindow,
546         ::boost::bind(&Deck::RequestLayout, mpCurrentDeck.get()),
547         rsMenuCommand.getLength()>0
548             ? ::boost::bind(&SidebarController::ShowDetailMenu,this,rsMenuCommand)
549             : ::boost::function<void(void)>()));
550 
551     // Create the XUIElement.
552     Reference<ui::XUIElement> xUIElement (CreateUIElement(
553             pPanel->GetComponentInterface(),
554             pPanelDescriptor->msImplementationURL));
555     if (xUIElement.is())
556     {
557         // Initialize the panel and add it to the active deck.
558         pPanel->SetUIElement(xUIElement);
559     }
560     else
561     {
562         pPanel.reset();
563     }
564 
565     return pPanel;
566 }
567 
568 
569 
570 
571 Reference<ui::XUIElement> SidebarController::CreateUIElement (
572     const Reference<awt::XWindowPeer>& rxWindow,
573     const ::rtl::OUString& rsImplementationURL)
574 {
575     try
576     {
577         const ::comphelper::ComponentContext aComponentContext (::comphelper::getProcessServiceFactory());
578         const Reference<ui::XUIElementFactory> xUIElementFactory (
579             aComponentContext.createComponent("com.sun.star.ui.UIElementFactoryManager"),
580             UNO_QUERY_THROW);
581 
582        // Create the XUIElement.
583         ::comphelper::NamedValueCollection aCreationArguments;
584         aCreationArguments.put("Frame", makeAny(mxFrame));
585         aCreationArguments.put("ParentWindow", makeAny(rxWindow));
586         SfxDockingWindow* pSfxDockingWindow = dynamic_cast<SfxDockingWindow*>(mpParentWindow);
587         if (pSfxDockingWindow != NULL)
588             aCreationArguments.put("SfxBindings", makeAny(sal_uInt64(&pSfxDockingWindow->GetBindings())));
589         aCreationArguments.put("Theme", Theme::GetPropertySet());
590         aCreationArguments.put("Sidebar", makeAny(Reference<ui::XSidebar>(static_cast<ui::XSidebar*>(this))));
591 
592         Reference<ui::XUIElement> xUIElement(
593             xUIElementFactory->createUIElement(
594                 rsImplementationURL,
595                 Sequence<beans::PropertyValue>(aCreationArguments.getPropertyValues())),
596             UNO_QUERY_THROW);
597 
598         return xUIElement;
599     }
600     catch(Exception& rException)
601     {
602         OSL_TRACE("caught exception: %s",
603             OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr());
604         // For some reason we can not create the actual panel.
605         // Probably because its factory was not properly registered.
606         // TODO: provide feedback to developer to better pinpoint the
607         // source of the error.
608 
609         return NULL;
610     }
611 }
612 
613 
614 
615 
616 IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent*, pEvent)
617 {
618     if (pEvent != NULL)
619     {
620         switch (pEvent->GetId())
621         {
622             case VCLEVENT_WINDOW_GETFOCUS:
623             case VCLEVENT_WINDOW_LOSEFOCUS:
624                 break;
625 
626             case VCLEVENT_WINDOW_SHOW:
627             case VCLEVENT_WINDOW_RESIZE:
628                 NotifyResize();
629                 break;
630 
631             case VCLEVENT_WINDOW_DATACHANGED:
632                 // Force an update of deck and tab bar to reflect
633                 // changes in theme (high contrast mode).
634                 Theme::HandleDataChange();
635                 mpParentWindow->Invalidate();
636                 break;
637 
638             case SFX_HINT_DYING:
639                 dispose();
640                 break;
641 
642             default:
643                 break;
644         }
645     }
646 
647     return sal_True;
648 }
649 
650 
651 
652 
653 void SidebarController::ShowPopupMenu (
654     const Rectangle& rButtonBox,
655     const ::std::vector<TabBar::DeckMenuData>& rDeckSelectionData,
656     const ::std::vector<TabBar::DeckMenuData>& rDeckShowData) const
657 {
658     ::boost::shared_ptr<PopupMenu> pMenu = CreatePopupMenu(rDeckSelectionData, rDeckShowData);
659     pMenu->SetSelectHdl(LINK(this, SidebarController, OnMenuItemSelected));
660 
661     // pass toolbox button rect so the menu can stay open on button up
662     Rectangle aBox (rButtonBox);
663     aBox.Move(mpTabBar->GetPosPixel().X(), 0);
664     pMenu->Execute(mpParentWindow, aBox, POPUPMENU_EXECUTE_DOWN);
665 }
666 
667 
668 
669 
670 void SidebarController::ShowDetailMenu (const ::rtl::OUString& rsMenuCommand) const
671 {
672     try
673     {
674         util::URL aURL;
675         aURL.Complete = rsMenuCommand;
676 
677         const ::comphelper::ComponentContext aComponentContext (::comphelper::getProcessServiceFactory());
678         const Reference<util::XURLTransformer> xParser (
679             aComponentContext.createComponent("com.sun.star.util.URLTransformer"),
680             UNO_QUERY_THROW);
681         xParser->parseStrict(aURL);
682         Reference<frame::XDispatchProvider> xProvider (mxFrame, UNO_QUERY_THROW);
683         Reference<frame::XDispatch> xDispatch (xProvider->queryDispatch(aURL, OUString(), 0));
684         if (xDispatch.is())
685             xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>());
686     }
687     catch(Exception& rException)
688     {
689         OSL_TRACE("caught exception: %s",
690             OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr());
691     }
692 }
693 
694 
695 
696 
697 ::boost::shared_ptr<PopupMenu> SidebarController::CreatePopupMenu (
698     const ::std::vector<TabBar::DeckMenuData>& rDeckSelectionData,
699     const ::std::vector<TabBar::DeckMenuData>& rDeckShowData) const
700 {
701     ::boost::shared_ptr<PopupMenu> pMenu (new PopupMenu());
702     FloatingWindow* pMenuWindow = dynamic_cast<FloatingWindow*>(pMenu->GetWindow());
703     if (pMenuWindow != NULL)
704     {
705         pMenuWindow->SetPopupModeFlags(pMenuWindow->GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOMOUSEUPCLOSE);
706     }
707 
708     SidebarResource aLocalResource;
709 
710     // Add one entry for every tool panel element to individually make
711     // them visible or hide them.
712     {
713         sal_Int32 nIndex (MID_FIRST_PANEL);
714         for(::std::vector<TabBar::DeckMenuData>::const_iterator
715                 iItem(rDeckSelectionData.begin()),
716                 iEnd(rDeckSelectionData.end());
717             iItem!=iEnd;
718             ++iItem)
719         {
720             pMenu->InsertItem(nIndex, iItem->get<0>(), MIB_RADIOCHECK);
721             pMenu->CheckItem(nIndex, iItem->get<2>());
722             ++nIndex;
723         }
724     }
725 
726     pMenu->InsertSeparator();
727 
728     // Add entry for docking or un-docking the tool panel.
729     if (mpParentWindow->IsFloatingMode())
730         pMenu->InsertItem(MID_LOCK_TASK_PANEL, String(SfxResId(STR_SFX_DOCK)));
731     else
732         pMenu->InsertItem(MID_UNLOCK_TASK_PANEL, String(SfxResId(STR_SFX_UNDOCK)));
733 
734     // Add sub menu for customization (hiding of deck tabs.)
735     PopupMenu* pCustomizationMenu = new PopupMenu();
736     {
737         sal_Int32 nIndex (MID_FIRST_HIDE);
738         for(::std::vector<TabBar::DeckMenuData>::const_iterator
739                 iItem(rDeckShowData.begin()),
740                 iEnd(rDeckShowData.end());
741             iItem!=iEnd;
742             ++iItem)
743         {
744             pCustomizationMenu->InsertItem(nIndex, iItem->get<0>(), MIB_CHECKABLE);
745             pCustomizationMenu->CheckItem(nIndex, iItem->get<2>());
746             ++nIndex;
747         }
748     }
749 
750     pCustomizationMenu->InsertSeparator();
751     pCustomizationMenu->InsertItem(MID_RESTORE_DEFAULT, String(SfxResId(STRING_RESTORE)));
752 
753     pMenu->InsertItem(MID_CUSTOMIZATION, String(SfxResId(STRING_CUSTOMIZATION)));
754     pMenu->SetPopupMenu(MID_CUSTOMIZATION, pCustomizationMenu);
755 
756     pMenu->RemoveDisabledEntries(sal_False, sal_False);
757 
758     return pMenu;
759 }
760 
761 
762 
763 
764 IMPL_LINK(SidebarController, OnMenuItemSelected, Menu*, pMenu)
765 {
766     if (pMenu == NULL)
767     {
768         OSL_ENSURE(pMenu!=NULL, "sfx2::sidebar::SidebarController::OnMenuItemSelected: illegal menu!");
769         return 0;
770     }
771 
772     pMenu->Deactivate();
773     const sal_Int32 nIndex (pMenu->GetCurItemId());
774     switch (nIndex)
775     {
776         case MID_UNLOCK_TASK_PANEL:
777             mpParentWindow->SetFloatingMode(sal_True);
778             break;
779 
780         case MID_LOCK_TASK_PANEL:
781             mpParentWindow->SetFloatingMode(sal_False);
782             break;
783 
784         case MID_RESTORE_DEFAULT:
785             mpTabBar->RestoreHideFlags();
786             break;
787 
788         default:
789         {
790             try
791             {
792                 if (nIndex >= MID_FIRST_PANEL && nIndex<MID_FIRST_HIDE)
793                     SwitchToDeck(mpTabBar->GetDeckIdForIndex(nIndex - MID_FIRST_PANEL));
794                 else if (nIndex >=MID_FIRST_HIDE)
795                     mpTabBar->ToggleHideFlag(nIndex-MID_FIRST_HIDE);
796             }
797             catch (RuntimeException&)
798             {
799             }
800         }
801         break;
802     }
803 
804     return 1;
805 }
806 
807 
808 
809 
810 void SidebarController::CloseDeck (void)
811 {
812     if ( ! mbIsDeckClosed)
813     {
814         mbIsDeckClosed = true;
815         if ( ! mpParentWindow->IsFloatingMode())
816             mnSavedSidebarWidth = SetChildWindowWidth(TabBar::GetDefaultWidth());
817         mpParentWindow->SetStyle(mpParentWindow->GetStyle() & ~WB_SIZEABLE);
818 
819         if (mpCurrentDeck)
820             mpCurrentDeck->Hide();
821 
822         NotifyResize();
823     }
824 }
825 
826 
827 
828 
829 void SidebarController::OpenDeck (void)
830 {
831     if (mbIsDeckClosed)
832     {
833         mbIsDeckClosed = false;
834         SetChildWindowWidth(mnSavedSidebarWidth);
835 
836         if (mpCurrentDeck)
837             mpCurrentDeck->Show();
838 
839         NotifyResize();
840     }
841 }
842 
843 
844 
845 
846 bool SidebarController::CanModifyChildWindowWidth (void) const
847 {
848     SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent());
849     if (pSplitWindow == NULL)
850     {
851         OSL_ASSERT(pSplitWindow!=NULL);
852         return 0;
853     }
854 
855     sal_uInt16 nRow (0xffff);
856     sal_uInt16 nColumn (0xffff);
857     pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow);
858 
859     sal_uInt16 nRowCount (pSplitWindow->GetWindowCount(nColumn));
860 
861     return nRowCount == 1;
862 }
863 
864 
865 
866 
867 sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth)
868 {
869     SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent());
870     if (pSplitWindow == NULL)
871         return 0;
872 
873     sal_uInt16 nRow (0xffff);
874     sal_uInt16 nColumn (0xffff);
875     pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow);
876     const long nColumnWidth (pSplitWindow->GetLineSize(nColumn));
877 
878     Window* pWindow = mpParentWindow;
879     const Point aWindowPosition (pWindow->GetPosPixel());
880     const Size aWindowSize (pWindow->GetSizePixel());
881 
882     pSplitWindow->MoveWindow(
883         mpParentWindow,
884         Size(nNewWidth, aWindowSize.Height()),
885         nColumn,
886         nRow);
887 
888     return static_cast<sal_Int32>(nColumnWidth);
889 }
890 
891 
892 
893 
894 void SidebarController::RestrictWidth (void)
895 {
896     SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent());
897     if (pSplitWindow != NULL)
898     {
899         const sal_uInt16 nId (pSplitWindow->GetItemId(mpParentWindow));
900         const sal_uInt16 nSetId (pSplitWindow->GetSet(nId));
901         // Minimum width is always that of the tabbar.
902         const sal_Int32 nMinimumWidth (TabBar::GetDefaultWidth());
903         // Maximum width depends on whether the deck is open or closed.
904         const sal_Int32 nMaximumWidth (
905             mbIsDeckClosed
906                 ? TabBar::GetDefaultWidth()
907                 : 400);
908         pSplitWindow->SetItemSizeRange(
909             nSetId,
910             Range(nMinimumWidth, nMaximumWidth));
911         if (nMinimumWidth == nMaximumWidth)
912             pSplitWindow->SetItemSize(nSetId, nMinimumWidth);
913     }
914 }
915 
916 
917 } } // end of namespace sfx2::sidebar
918