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