xref: /trunk/main/sfx2/source/sidebar/SidebarController.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956) !
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 
23 
24 #include "precompiled_sfx2.hxx"
25 
26 #include "SidebarController.hxx"
27 #include "Deck.hxx"
28 #include "DeckTitleBar.hxx"
29 #include "Panel.hxx"
30 #include "PanelTitleBar.hxx"
31 #include "SidebarPanel.hxx"
32 #include "SidebarResource.hxx"
33 #include "TabBar.hxx"
34 #include "sfx2/sidebar/Theme.hxx"
35 #include "sfx2/sidebar/SidebarChildWindow.hxx"
36 #include "sfx2/sidebar/Tools.hxx"
37 #include "SidebarDockingWindow.hxx"
38 #include "Context.hxx"
39 
40 #include "sfxresid.hxx"
41 #include "sfx2/sfxsids.hrc"
42 #include "sfx2/titledockwin.hxx"
43 #include "sfxlocal.hrc"
44 #include <vcl/floatwin.hxx>
45 #include <vcl/fixed.hxx>
46 #include "splitwin.hxx"
47 #include <svl/smplhint.hxx>
48 #include <tools/link.hxx>
49 #include <toolkit/helper/vclunohelper.hxx>
50 
51 #include <comphelper/componentfactory.hxx>
52 #include <comphelper/processfactory.hxx>
53 #include <comphelper/componentcontext.hxx>
54 #include <comphelper/namedvaluecollection.hxx>
55 
56 #include <com/sun/star/frame/XDispatchProvider.hpp>
57 #include <com/sun/star/lang/XInitialization.hpp>
58 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
59 #include <com/sun/star/ui/ContextChangeEventObject.hpp>
60 #include <com/sun/star/ui/XUIElementFactory.hpp>
61 #include <com/sun/star/util/XURLTransformer.hpp>
62 #include <com/sun/star/util/URL.hpp>
63 #include <com/sun/star/rendering/XSpriteCanvas.hpp>
64 
65 #include <boost/bind.hpp>
66 #include <boost/function.hpp>
67 #include <boost/scoped_array.hpp>
68 
69 
70 using namespace css;
71 using namespace cssu;
72 using ::rtl::OUString;
73 
74 
75 #undef VERBOSE
76 
77 namespace
78 {
79     const static OUString gsReadOnlyCommandName (A2S(".uno:EditDoc"));
80     const static sal_Int32 gnMaximumSidebarWidth (400);
81     const static sal_Int32 gnWidthCloseThreshold (70);
82     const static sal_Int32 gnWidthOpenThreshold (40);
83 }
84 
85 
86 namespace sfx2 { namespace sidebar {
87 
88 SidebarController::SidebarControllerContainer SidebarController::maSidebarControllerContainer;
89 
90 namespace {
91     enum MenuId
92     {
93         MID_UNLOCK_TASK_PANEL = 1,
94         MID_LOCK_TASK_PANEL,
95         MID_CUSTOMIZATION,
96         MID_RESTORE_DEFAULT,
97         MID_FIRST_PANEL,
98         MID_FIRST_HIDE = 1000
99     };
100 
101     /** When in doubt, show this deck.
102     */
103     static const ::rtl::OUString gsDefaultDeckId(A2S("PropertyDeck"));
104 }
105 
106 
SidebarController(SidebarDockingWindow * pParentWindow,const cssu::Reference<css::frame::XFrame> & rxFrame)107 SidebarController::SidebarController (
108     SidebarDockingWindow* pParentWindow,
109     const cssu::Reference<css::frame::XFrame>& rxFrame)
110     : SidebarControllerInterfaceBase(m_aMutex),
111       mpCurrentDeck(),
112       mpParentWindow(pParentWindow),
113       mpTabBar(new TabBar(
114               mpParentWindow,
115               rxFrame,
116               ::boost::bind(&SidebarController::OpenThenSwitchToDeck, this, _1),
117               ::boost::bind(&SidebarController::ShowPopupMenu, this, _1,_2))),
118       mxFrame(rxFrame),
119       maCurrentContext(OUString(), OUString()),
120       maRequestedContext(),
121       mnRequestedForceFlags(SwitchFlag_NoForce),
122       msCurrentDeckId(gsDefaultDeckId),
123       msCurrentDeckTitle(),
124       maPropertyChangeForwarder(::boost::bind(&SidebarController::BroadcastPropertyChange, this)),
125       maContextChangeUpdate(::boost::bind(&SidebarController::UpdateConfigurations, this)),
126       maAsynchronousDeckSwitch(),
127       mbIsDeckRequestedOpen(),
128       mbIsDeckOpen(),
129       mbCanDeckBeOpened(true),
130       mnSavedSidebarWidth(pParentWindow->GetSizePixel().Width()),
131       maFocusManager(::boost::bind(&SidebarController::ShowPanel, this, _1)),
132       mxReadOnlyModeDispatch(),
133       mbIsDocumentReadOnly(false),
134       mpSplitWindow(NULL),
135       mnWidthOnSplitterButtonDown(0),
136       mpCloseIndicator()
137 {
138     if (pParentWindow == NULL)
139     {
140         OSL_ASSERT(pParentWindow!=NULL);
141             return;
142     }
143 
144     // Listen for context change events.
145     cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
146         css::ui::ContextChangeEventMultiplexer::get(
147             ::comphelper::getProcessComponentContext()));
148     if (xMultiplexer.is())
149         xMultiplexer->addContextChangeEventListener(
150             static_cast<css::ui::XContextChangeEventListener*>(this),
151             mxFrame->getController());
152 
153     // Listen for window events.
154     mpParentWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler));
155 
156     // Listen for theme property changes.
157     Theme::GetPropertySet()->addPropertyChangeListener(
158         A2S(""),
159         static_cast<css::beans::XPropertyChangeListener*>(this));
160 
161     // Get the dispatch object as preparation to listen for changes of
162     // the read-only state.
163     const util::URL aURL (Tools::GetURL(gsReadOnlyCommandName));
164     mxReadOnlyModeDispatch = Tools::GetDispatch(mxFrame, aURL);
165     if (mxReadOnlyModeDispatch.is())
166         mxReadOnlyModeDispatch->addStatusListener(this, aURL);
167 
168     SwitchToDeck(A2S("default"));
169 
170     WeakReference<SidebarController> xWeakController (this);
171     maSidebarControllerContainer.insert(
172         SidebarControllerContainer::value_type(
173             rxFrame,
174             xWeakController));
175 }
176 
177 
178 
179 
~SidebarController(void)180 SidebarController::~SidebarController (void)
181 {
182 }
183 
184 
185 
186 
GetSidebarControllerForFrame(const cssu::Reference<css::frame::XFrame> & rxFrame)187 SidebarController* SidebarController::GetSidebarControllerForFrame (
188     const cssu::Reference<css::frame::XFrame>& rxFrame)
189 {
190     SidebarControllerContainer::iterator iEntry (maSidebarControllerContainer.find(rxFrame));
191     if (iEntry == maSidebarControllerContainer.end())
192         return NULL;
193 
194     cssu::Reference<XInterface> xController (iEntry->second.get());
195     if ( ! xController.is())
196         return NULL;
197 
198     return dynamic_cast<SidebarController*>(xController.get());
199 }
200 
201 
202 
203 
disposing(void)204 void SAL_CALL SidebarController::disposing (void)
205 {
206     SidebarControllerContainer::iterator iEntry (maSidebarControllerContainer.find(mxFrame));
207     if (iEntry != maSidebarControllerContainer.end())
208         maSidebarControllerContainer.erase(iEntry);
209 
210     maFocusManager.Clear();
211 
212     cssu::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
213         css::ui::ContextChangeEventMultiplexer::get(
214             ::comphelper::getProcessComponentContext()));
215     if (xMultiplexer.is())
216         xMultiplexer->removeAllContextChangeEventListeners(
217             static_cast<css::ui::XContextChangeEventListener*>(this));
218 
219     if (mxReadOnlyModeDispatch.is())
220         mxReadOnlyModeDispatch->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName));
221     if (mpSplitWindow != NULL)
222     {
223         mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
224         mpSplitWindow = NULL;
225     }
226 
227     if (mpParentWindow != NULL)
228     {
229         mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
230         mpParentWindow = NULL;
231     }
232 
233     if (mpCurrentDeck)
234     {
235         mpCurrentDeck->Dispose();
236         mpCurrentDeck->PrintWindowTree();
237         mpCurrentDeck.reset();
238     }
239 
240     mpTabBar.reset();
241 
242     Theme::GetPropertySet()->removePropertyChangeListener(
243         A2S(""),
244         static_cast<css::beans::XPropertyChangeListener*>(this));
245 
246     maContextChangeUpdate.CancelRequest();
247     maAsynchronousDeckSwitch.CancelRequest();
248 }
249 
250 
251 
252 
notifyContextChangeEvent(const css::ui::ContextChangeEventObject & rEvent)253 void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent)
254 {
255     // Update to the requested new context asynchronously to avoid
256     // subtle errors caused by SFX2 which in rare cases can not
257     // properly handle a synchronous update.
258     maRequestedContext = Context(
259         rEvent.ApplicationName,
260         rEvent.ContextName);
261     if (maRequestedContext != maCurrentContext)
262     {
263         maAsynchronousDeckSwitch.CancelRequest();
264         maContextChangeUpdate.RequestCall();
265     }
266 }
267 
268 
269 
270 
disposing(const css::lang::EventObject & rEventObject)271 void SAL_CALL SidebarController::disposing (const css::lang::EventObject& rEventObject)
272 {
273     (void)rEventObject;
274 
275     dispose();
276 }
277 
278 
279 
280 
propertyChange(const css::beans::PropertyChangeEvent & rEvent)281 void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& rEvent)
282 {
283     (void)rEvent;
284 
285     maPropertyChangeForwarder.RequestCall();
286 }
287 
288 
289 
290 
statusChanged(const css::frame::FeatureStateEvent & rEvent)291 void SAL_CALL SidebarController::statusChanged (const css::frame::FeatureStateEvent& rEvent)
292 {
293     bool bIsReadWrite (true);
294     if (rEvent.IsEnabled)
295         rEvent.State >>= bIsReadWrite;
296 
297     if (mbIsDocumentReadOnly != !bIsReadWrite)
298     {
299         mbIsDocumentReadOnly = !bIsReadWrite;
300 
301         // Force the current deck to update its panel list.
302         if ( ! mbIsDocumentReadOnly)
303             msCurrentDeckId = gsDefaultDeckId;
304         mnRequestedForceFlags |= SwitchFlag_ForceSwitch;
305         maAsynchronousDeckSwitch.CancelRequest();
306         maContextChangeUpdate.RequestCall();
307     }
308 }
309 
310 
311 
312 
requestLayout(void)313 void SAL_CALL SidebarController::requestLayout (void)
314 {
315     if (mpCurrentDeck)
316         mpCurrentDeck->RequestLayout();
317     RestrictWidth();
318 }
319 
320 
321 
322 
BroadcastPropertyChange(void)323 void SidebarController::BroadcastPropertyChange (void)
324 {
325     DataChangedEvent aEvent (DATACHANGED_USER);
326     mpParentWindow->NotifyAllChilds(aEvent);
327     mpParentWindow->Invalidate(INVALIDATE_CHILDREN);
328 }
329 
330 
331 
332 
NotifyResize(void)333 void SidebarController::NotifyResize (void)
334 {
335     if( !bool(mpTabBar))
336     {
337         OSL_ASSERT( bool(mpTabBar));
338         return;
339     }
340 
341     Window* pParentWindow = mpTabBar->GetParent();
342 
343     const sal_Int32 nWidth (pParentWindow->GetSizePixel().Width());
344     const sal_Int32 nHeight (pParentWindow->GetSizePixel().Height());
345 
346     mbIsDeckOpen = (nWidth > TabBar::GetDefaultWidth());
347 
348     if (mnSavedSidebarWidth <= 0)
349         mnSavedSidebarWidth = nWidth;
350 
351     bool bIsDeckVisible;
352     if (mbCanDeckBeOpened)
353     {
354         const bool bIsOpening (nWidth > mnWidthOnSplitterButtonDown);
355         if (bIsOpening)
356             bIsDeckVisible = nWidth >= TabBar::GetDefaultWidth() + gnWidthOpenThreshold;
357         else
358             bIsDeckVisible = nWidth >= TabBar::GetDefaultWidth() + gnWidthCloseThreshold;
359         mbIsDeckRequestedOpen = bIsDeckVisible;
360         UpdateCloseIndicator(!bIsDeckVisible);
361     }
362     else
363         bIsDeckVisible = false;
364 
365     // Place the deck.
366     if (mpCurrentDeck)
367     {
368         if (bIsDeckVisible)
369         {
370             mpCurrentDeck->SetPosSizePixel(0,0, nWidth-TabBar::GetDefaultWidth(), nHeight);
371             mpCurrentDeck->Show();
372             mpCurrentDeck->RequestLayout();
373         }
374         else
375             mpCurrentDeck->Hide();
376     }
377 
378     // Place the tab bar.
379     mpTabBar->SetPosSizePixel(nWidth-TabBar::GetDefaultWidth(),0,TabBar::GetDefaultWidth(),nHeight);
380     mpTabBar->Show();
381 
382     // Determine if the closer of the deck can be shown.
383     if (mpCurrentDeck)
384     {
385         DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar();
386         if (pTitleBar != NULL && pTitleBar->IsVisible())
387             pTitleBar->SetCloserVisible(CanModifyChildWindowWidth());
388     }
389 
390     RestrictWidth();
391 }
392 
393 
394 
395 
ProcessNewWidth(const sal_Int32 nNewWidth)396 void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth)
397 {
398     if ( ! mbIsDeckRequestedOpen)
399         return;
400 
401     if (mbIsDeckRequestedOpen.get())
402      {
403         // Deck became large enough to be shown. Show it.
404         mnSavedSidebarWidth = nNewWidth;
405         RequestOpenDeck();
406     }
407     else
408     {
409         // Deck became too small. Close it completely.
410         // If window is wider than the tab bar then mark the deck as being visible, even when it's not.
411         // This is to trigger an adjustment of the width to the width of the tab bar.
412         mbIsDeckOpen = true;
413         RequestCloseDeck();
414 
415         if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth())
416             mnSavedSidebarWidth = mnWidthOnSplitterButtonDown;
417     }
418 }
419 
420 
421 
422 
UpdateConfigurations(void)423 void SidebarController::UpdateConfigurations (void)
424 {
425     if (maCurrentContext != maRequestedContext
426         || mnRequestedForceFlags!=SwitchFlag_NoForce)
427     {
428         maCurrentContext = maRequestedContext;
429 
430         // Find the set of decks that could be displayed for the new context.
431         ResourceManager::DeckContextDescriptorContainer aDecks;
432         ResourceManager::Instance().GetMatchingDecks (
433             aDecks,
434             maCurrentContext,
435             mbIsDocumentReadOnly,
436             mxFrame);
437 
438         // Notify the tab bar about the updated set of decks.
439         mpTabBar->SetDecks(aDecks);
440 
441         // Find the new deck. By default that is the same as the old
442         // one. If that is not set or not enabled, then choose the
443         // first enabled deck.
444         OUString sNewDeckId;
445         for (ResourceManager::DeckContextDescriptorContainer::const_iterator
446                  iDeck(aDecks.begin()),
447                  iEnd(aDecks.end());
448              iDeck!=iEnd;
449              ++iDeck)
450         {
451             if (iDeck->mbIsEnabled)
452             {
453                 if (iDeck->msId.equals(msCurrentDeckId))
454                 {
455                     sNewDeckId = msCurrentDeckId;
456                     break;
457                 }
458                 else if (sNewDeckId.getLength() == 0)
459                     sNewDeckId = iDeck->msId;
460             }
461         }
462 
463         if (sNewDeckId.getLength() == 0)
464         {
465             // We did not find a valid deck.
466             RequestCloseDeck();
467             return;
468         }
469 
470         // Tell the tab bar to highlight the button associated
471         // with the deck.
472         mpTabBar->HighlightDeck(sNewDeckId);
473 
474         SwitchToDeck(
475             *ResourceManager::Instance().GetDeckDescriptor(sNewDeckId),
476             maCurrentContext);
477     }
478 }
479 
480 
481 
482 
OpenThenSwitchToDeck(const::rtl::OUString & rsDeckId)483 void SidebarController::OpenThenSwitchToDeck (
484     const ::rtl::OUString& rsDeckId)
485 {
486     RequestOpenDeck();
487     SwitchToDeck(rsDeckId);
488     mpTabBar->Invalidate();
489 }
490 
491 
492 
493 
RequestSwitchToDeck(const::rtl::OUString & rsDeckId)494 void SidebarController::RequestSwitchToDeck (
495     const ::rtl::OUString& rsDeckId)
496 {
497     maContextChangeUpdate.CancelRequest();
498     maAsynchronousDeckSwitch.RequestCall(
499         ::boost::bind(&SidebarController::OpenThenSwitchToDeck, this, rsDeckId));
500 }
501 
502 
503 
504 
SwitchToDeck(const::rtl::OUString & rsDeckId)505 void SidebarController::SwitchToDeck (
506     const ::rtl::OUString& rsDeckId)
507 {
508     if ( ! msCurrentDeckId.equals(rsDeckId)
509         || ! mbIsDeckOpen
510         || mnRequestedForceFlags!=SwitchFlag_NoForce)
511     {
512         const DeckDescriptor* pDeckDescriptor = ResourceManager::Instance().GetDeckDescriptor(rsDeckId);
513         if (pDeckDescriptor != NULL)
514             SwitchToDeck(*pDeckDescriptor, maCurrentContext);
515     }
516 }
517 
518 
519 
520 
SwitchToDeck(const DeckDescriptor & rDeckDescriptor,const Context & rContext)521 void SidebarController::SwitchToDeck (
522     const DeckDescriptor& rDeckDescriptor,
523     const Context& rContext)
524 {
525     maFocusManager.Clear();
526 
527     const bool bForceNewDeck ((mnRequestedForceFlags&SwitchFlag_ForceNewDeck)!=0);
528     const bool bForceNewPanels ((mnRequestedForceFlags&SwitchFlag_ForceNewPanels)!=0);
529     mnRequestedForceFlags = SwitchFlag_NoForce;
530 
531     if ( ! msCurrentDeckId.equals(rDeckDescriptor.msId)
532         || bForceNewDeck)
533     {
534         // When the deck changes then destroy the deck and all panels
535         // and create everything new.
536         if (mpCurrentDeck)
537         {
538             mpCurrentDeck->Dispose();
539             mpCurrentDeck.reset();
540         }
541 
542         msCurrentDeckId = rDeckDescriptor.msId;
543     }
544     mpTabBar->HighlightDeck(msCurrentDeckId);
545 
546     // Determine the panels to display in the deck.
547     ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors;
548     ResourceManager::Instance().GetMatchingPanels(
549         aPanelContextDescriptors,
550         rContext,
551         rDeckDescriptor.msId,
552         mxFrame);
553 
554     if (aPanelContextDescriptors.empty())
555     {
556         // There are no panels to be displayed in the current context.
557         if (EnumContext::GetContextEnum(rContext.msContext) != EnumContext::Context_Empty)
558         {
559             // Switch to the "empty" context and try again.
560             SwitchToDeck(
561                 rDeckDescriptor,
562                 Context(
563                     rContext.msApplication,
564                     EnumContext::GetContextName(EnumContext::Context_Empty)));
565             return;
566         }
567         else
568         {
569             // This is already the "empty" context. Looks like we have
570             // to live with an empty deck.
571         }
572     }
573 
574     // Provide a configuration and Deck object.
575     if ( ! mpCurrentDeck)
576     {
577         mpCurrentDeck.reset(
578             new Deck(
579                 rDeckDescriptor,
580                 mpParentWindow,
581                 ::boost::bind(&SidebarController::RequestCloseDeck, this)));
582         msCurrentDeckTitle = rDeckDescriptor.msTitle;
583 
584     }
585     if ( ! mpCurrentDeck)
586         return;
587 
588 #ifdef DEBUG
589     // Show the context name in the deck title bar.
590     DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar();
591     if (pTitleBar != NULL)
592         pTitleBar->SetTitle(rDeckDescriptor.msTitle+A2S(" (")+maCurrentContext.msContext+A2S(")"));
593 #endif
594 
595     // Update the panel list.
596     const sal_Int32 nNewPanelCount (aPanelContextDescriptors.size());
597     SharedPanelContainer aNewPanels;
598     const SharedPanelContainer& rCurrentPanels (mpCurrentDeck->GetPanels());
599     aNewPanels.resize(nNewPanelCount);
600     sal_Int32 nWriteIndex (0);
601     bool bHasPanelSetChanged (false);
602     for (sal_Int32 nReadIndex=0; nReadIndex<nNewPanelCount; ++nReadIndex)
603     {
604         const ResourceManager::PanelContextDescriptor& rPanelContexDescriptor (
605             aPanelContextDescriptors[nReadIndex]);
606 
607         // Determine if the panel can be displayed.
608         const bool bIsPanelVisible (!mbIsDocumentReadOnly || rPanelContexDescriptor.mbShowForReadOnlyDocuments);
609         if ( ! bIsPanelVisible)
610             continue;
611 
612         // Find the corresponding panel among the currently active
613         // panels.
614         SharedPanelContainer::const_iterator iPanel;
615         if (bForceNewPanels)
616         {
617             // All panels have to be created in any case. There is no
618             // point in searching already existing panels.
619             iPanel = rCurrentPanels.end();
620         }
621         else
622         {
623             iPanel = ::std::find_if(
624                 rCurrentPanels.begin(),
625                 rCurrentPanels.end(),
626                 ::boost::bind(&Panel::HasIdPredicate, _1, ::boost::cref(rPanelContexDescriptor.msId)));
627         }
628         if (iPanel != rCurrentPanels.end())
629         {
630             // Panel already exists in current deck. Reuse it.
631             aNewPanels[nWriteIndex] = *iPanel;
632             aNewPanels[nWriteIndex]->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible);
633         }
634         else
635         {
636             // Panel does not yet exist or creation of new panels is forced.
637             // Create it.
638             aNewPanels[nWriteIndex] = CreatePanel(
639                 rPanelContexDescriptor.msId,
640                 mpCurrentDeck->GetPanelParentWindow(),
641                 rPanelContexDescriptor.mbIsInitiallyVisible,
642                 rContext);
643             bHasPanelSetChanged = true;
644         }
645         if( bool(aNewPanels[nWriteIndex]))
646         {
647             // Depending on the context we have to change the command
648             // for the "more options" dialog.
649             PanelTitleBar* pTitleBar = aNewPanels[nWriteIndex]->GetTitleBar();
650             if (pTitleBar != NULL)
651             {
652                 pTitleBar->SetMoreOptionsCommand(
653                     rPanelContexDescriptor.msMenuCommand,
654                     mxFrame);
655             }
656 
657             ++nWriteIndex;
658         }
659 
660     }
661     aNewPanels.resize(nWriteIndex);
662 
663     // Activate the deck and the new set of panels.
664     mpCurrentDeck->SetPosSizePixel(
665         0,
666         0,
667         mpParentWindow->GetSizePixel().Width()-TabBar::GetDefaultWidth(),
668         mpParentWindow->GetSizePixel().Height());
669     mpCurrentDeck->SetPanels(aNewPanels);
670     mpCurrentDeck->Show();
671 
672     mpParentWindow->SetText(rDeckDescriptor.msTitle);
673 
674     if (bHasPanelSetChanged)
675         NotifyResize();
676 
677     // Tell the focus manager about the new panels and tab bar
678     // buttons.
679     maFocusManager.SetDeckTitle(mpCurrentDeck->GetTitleBar());
680     maFocusManager.SetPanels(aNewPanels);
681     mpTabBar->UpdateFocusManager(maFocusManager);
682     UpdateTitleBarIcons();
683 }
684 
685 
686 
687 
CreatePanel(const OUString & rsPanelId,::Window * pParentWindow,const bool bIsInitiallyExpanded,const Context & rContext)688 SharedPanel SidebarController::CreatePanel (
689     const OUString& rsPanelId,
690     ::Window* pParentWindow,
691     const bool bIsInitiallyExpanded,
692     const Context& rContext)
693 {
694     const PanelDescriptor* pPanelDescriptor = ResourceManager::Instance().GetPanelDescriptor(rsPanelId);
695     if (pPanelDescriptor == NULL)
696         return SharedPanel();
697 
698     // Create the panel which is the parent window of the UIElement.
699     SharedPanel pPanel (new Panel(
700         *pPanelDescriptor,
701         pParentWindow,
702         bIsInitiallyExpanded,
703         ::boost::bind(&Deck::RequestLayout, mpCurrentDeck.get()),
704         ::boost::bind(&SidebarController::GetCurrentContext, this)));
705 
706     // Create the XUIElement.
707     Reference<ui::XUIElement> xUIElement (CreateUIElement(
708             pPanel->GetComponentInterface(),
709             pPanelDescriptor->msImplementationURL,
710             pPanelDescriptor->mbWantsCanvas,
711             rContext));
712     if (xUIElement.is())
713     {
714         // Initialize the panel and add it to the active deck.
715         pPanel->SetUIElement(xUIElement);
716     }
717     else
718     {
719         pPanel.reset();
720     }
721 
722     return pPanel;
723 }
724 
725 
726 
727 
CreateUIElement(const Reference<awt::XWindowPeer> & rxWindow,const::rtl::OUString & rsImplementationURL,const bool bWantsCanvas,const Context & rContext)728 Reference<ui::XUIElement> SidebarController::CreateUIElement (
729     const Reference<awt::XWindowPeer>& rxWindow,
730     const ::rtl::OUString& rsImplementationURL,
731     const bool bWantsCanvas,
732     const Context& rContext)
733 {
734     try
735     {
736         const ::comphelper::ComponentContext aComponentContext (::comphelper::getProcessServiceFactory());
737         const Reference<ui::XUIElementFactory> xUIElementFactory (
738             aComponentContext.createComponent("com.sun.star.ui.UIElementFactoryManager"),
739             UNO_QUERY_THROW);
740 
741        // Create the XUIElement.
742         ::comphelper::NamedValueCollection aCreationArguments;
743         aCreationArguments.put("Frame", makeAny(mxFrame));
744         aCreationArguments.put("ParentWindow", makeAny(rxWindow));
745         SfxDockingWindow* pSfxDockingWindow = dynamic_cast<SfxDockingWindow*>(mpParentWindow);
746         if (pSfxDockingWindow != NULL)
747             aCreationArguments.put("SfxBindings", makeAny(sal_uInt64(&pSfxDockingWindow->GetBindings())));
748         aCreationArguments.put("Theme", Theme::GetPropertySet());
749         aCreationArguments.put("Sidebar", makeAny(Reference<ui::XSidebar>(static_cast<ui::XSidebar*>(this))));
750         if (bWantsCanvas)
751         {
752             Reference<rendering::XSpriteCanvas> xCanvas (VCLUnoHelper::GetWindow(rxWindow)->GetSpriteCanvas());
753             aCreationArguments.put("Canvas", makeAny(xCanvas));
754         }
755         aCreationArguments.put("ApplicationName", makeAny(rContext.msApplication));
756         aCreationArguments.put("ContextName", makeAny(rContext.msContext));
757 
758         Reference<ui::XUIElement> xUIElement(
759             xUIElementFactory->createUIElement(
760                 rsImplementationURL,
761                 Sequence<beans::PropertyValue>(aCreationArguments.getPropertyValues())),
762             UNO_QUERY_THROW);
763 
764         return xUIElement;
765     }
766     catch(Exception& rException)
767     {
768         OSL_TRACE("caught exception: %s",
769             OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr());
770         // For some reason we can not create the actual panel.
771         // Probably because its factory was not properly registered.
772         // TODO: provide feedback to developer to better pinpoint the
773         // source of the error.
774 
775         return NULL;
776     }
777 }
778 
779 
780 
781 
IMPL_LINK(SidebarController,WindowEventHandler,VclWindowEvent *,pEvent)782 IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent*, pEvent)
783 {
784     if (pEvent==NULL)
785         return sal_False;
786 
787     if (pEvent->GetWindow() == mpParentWindow)
788     {
789         switch (pEvent->GetId())
790         {
791             case VCLEVENT_WINDOW_SHOW:
792             case VCLEVENT_WINDOW_RESIZE:
793                 NotifyResize();
794                 break;
795 
796             case VCLEVENT_WINDOW_DATACHANGED:
797                 // Force an update of deck and tab bar to reflect
798                 // changes in theme (high contrast mode).
799                 Theme::HandleDataChange();
800                 UpdateTitleBarIcons();
801                 mpParentWindow->Invalidate();
802                 mnRequestedForceFlags |= SwitchFlag_ForceNewDeck | SwitchFlag_ForceNewPanels;
803                 maAsynchronousDeckSwitch.CancelRequest();
804                 maContextChangeUpdate.RequestCall();
805                 break;
806 
807             case SFX_HINT_DYING:
808                 dispose();
809                 break;
810 
811             case VCLEVENT_WINDOW_PAINT:
812                 OSL_TRACE("Paint");
813                 break;
814 
815             default:
816                 break;
817         }
818     }
819     else if (pEvent->GetWindow()==mpSplitWindow && mpSplitWindow!=NULL)
820     {
821         switch (pEvent->GetId())
822         {
823             case VCLEVENT_WINDOW_MOUSEBUTTONDOWN:
824                 mnWidthOnSplitterButtonDown = mpParentWindow->GetSizePixel().Width();
825                 break;
826 
827             case VCLEVENT_WINDOW_MOUSEBUTTONUP:
828             {
829                 ProcessNewWidth(mpParentWindow->GetSizePixel().Width());
830                 mnWidthOnSplitterButtonDown = 0;
831                 break;
832             }
833 
834             case SFX_HINT_DYING:
835                 dispose();
836                 break;
837          }
838     }
839 
840     return sal_True;
841 }
842 
843 
844 
845 
ShowPopupMenu(const Rectangle & rButtonBox,const::std::vector<TabBar::DeckMenuData> & rMenuData) const846 void SidebarController::ShowPopupMenu (
847     const Rectangle& rButtonBox,
848     const ::std::vector<TabBar::DeckMenuData>& rMenuData) const
849 {
850     ::boost::shared_ptr<PopupMenu> pMenu = CreatePopupMenu(rMenuData);
851     pMenu->SetSelectHdl(LINK(this, SidebarController, OnMenuItemSelected));
852 
853     // pass toolbox button rect so the menu can stay open on button up
854     Rectangle aBox (rButtonBox);
855     aBox.Move(mpTabBar->GetPosPixel().X(), 0);
856     pMenu->Execute(mpParentWindow, aBox, POPUPMENU_EXECUTE_DOWN);
857 }
858 
859 
860 
861 
ShowDetailMenu(const::rtl::OUString & rsMenuCommand) const862 void SidebarController::ShowDetailMenu (const ::rtl::OUString& rsMenuCommand) const
863 {
864     try
865     {
866         const util::URL aURL (Tools::GetURL(rsMenuCommand));
867         Reference<frame::XDispatch> xDispatch (Tools::GetDispatch(mxFrame, aURL));
868         if (xDispatch.is())
869             xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>());
870     }
871     catch(Exception& rException)
872     {
873         OSL_TRACE("caught exception: %s",
874             OUStringToOString(rException.Message, RTL_TEXTENCODING_ASCII_US).getStr());
875     }
876 }
877 
878 
879 
880 
CreatePopupMenu(const::std::vector<TabBar::DeckMenuData> & rMenuData) const881 ::boost::shared_ptr<PopupMenu> SidebarController::CreatePopupMenu (
882     const ::std::vector<TabBar::DeckMenuData>& rMenuData) const
883 {
884     // Create the top level popup menu.
885     ::boost::shared_ptr<PopupMenu> pMenu (new PopupMenu());
886     FloatingWindow* pMenuWindow = dynamic_cast<FloatingWindow*>(pMenu->GetWindow());
887     if (pMenuWindow != NULL)
888     {
889         pMenuWindow->SetPopupModeFlags(pMenuWindow->GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOMOUSEUPCLOSE);
890     }
891 
892     // Create sub menu for customization (hiding of deck tabs.)
893     PopupMenu* pCustomizationMenu = new PopupMenu();
894 
895     SidebarResource aLocalResource;
896 
897     // Add one entry for every tool panel element to individually make
898     // them visible or hide them.
899     sal_Int32 nIndex (0);
900     for(::std::vector<TabBar::DeckMenuData>::const_iterator
901             iItem(rMenuData.begin()),
902             iEnd(rMenuData.end());
903         iItem!=iEnd;
904         ++iItem,++nIndex)
905     {
906         const sal_Int32 nMenuIndex (nIndex+MID_FIRST_PANEL);
907         pMenu->InsertItem(nMenuIndex, iItem->msDisplayName, MIB_RADIOCHECK);
908         pMenu->CheckItem(nMenuIndex, iItem->mbIsCurrentDeck ? sal_True : sal_False);
909         pMenu->EnableItem(nMenuIndex, (iItem->mbIsEnabled&&iItem->mbIsActive) ? sal_True : sal_False);
910 
911         const sal_Int32 nSubMenuIndex (nIndex+MID_FIRST_HIDE);
912         if (iItem->mbIsCurrentDeck)
913         {
914             // Don't allow the currently visible deck to be disabled.
915             pCustomizationMenu->InsertItem(nSubMenuIndex, iItem->msDisplayName, MIB_RADIOCHECK);
916             pCustomizationMenu->CheckItem(nSubMenuIndex, sal_True);
917         }
918         else
919         {
920             pCustomizationMenu->InsertItem(nSubMenuIndex, iItem->msDisplayName, MIB_CHECKABLE);
921             pCustomizationMenu->CheckItem(nSubMenuIndex, iItem->mbIsActive ? sal_True : sal_False);
922         }
923     }
924 
925     pMenu->InsertSeparator();
926 
927     // Add entry for docking or un-docking the tool panel.
928     if (mpParentWindow->IsFloatingMode())
929         pMenu->InsertItem(MID_LOCK_TASK_PANEL, String(SfxResId(STR_SFX_DOCK)));
930     else
931         pMenu->InsertItem(MID_UNLOCK_TASK_PANEL, String(SfxResId(STR_SFX_UNDOCK)));
932 
933     pCustomizationMenu->InsertSeparator();
934     pCustomizationMenu->InsertItem(MID_RESTORE_DEFAULT, String(SfxResId(STRING_RESTORE)));
935 
936     pMenu->InsertItem(MID_CUSTOMIZATION, String(SfxResId(STRING_CUSTOMIZATION)));
937     pMenu->SetPopupMenu(MID_CUSTOMIZATION, pCustomizationMenu);
938 
939     pMenu->RemoveDisabledEntries(sal_False, sal_False);
940 
941     return pMenu;
942 }
943 
944 
945 
946 
IMPL_LINK(SidebarController,OnMenuItemSelected,Menu *,pMenu)947 IMPL_LINK(SidebarController, OnMenuItemSelected, Menu*, pMenu)
948 {
949     if (pMenu == NULL)
950     {
951         OSL_ENSURE(pMenu!=NULL, "sfx2::sidebar::SidebarController::OnMenuItemSelected: illegal menu!");
952         return 0;
953     }
954 
955     pMenu->Deactivate();
956     const sal_Int32 nIndex (pMenu->GetCurItemId());
957     switch (nIndex)
958     {
959         case MID_UNLOCK_TASK_PANEL:
960             mpParentWindow->SetFloatingMode(sal_True);
961             break;
962 
963         case MID_LOCK_TASK_PANEL:
964             mpParentWindow->SetFloatingMode(sal_False);
965             break;
966 
967         case MID_RESTORE_DEFAULT:
968             mpTabBar->RestoreHideFlags();
969             break;
970 
971         default:
972         {
973             try
974             {
975                 if (nIndex >= MID_FIRST_PANEL && nIndex<MID_FIRST_HIDE)
976                     SwitchToDeck(mpTabBar->GetDeckIdForIndex(nIndex - MID_FIRST_PANEL));
977                 else if (nIndex >=MID_FIRST_HIDE)
978                     if (pMenu->GetItemBits(nIndex) == MIB_CHECKABLE)
979                         mpTabBar->ToggleHideFlag(nIndex-MID_FIRST_HIDE);
980             }
981             catch (RuntimeException&)
982             {
983             }
984         }
985         break;
986     }
987 
988     return 1;
989 }
990 
991 
992 
993 
RequestCloseDeck(void)994 void SidebarController::RequestCloseDeck (void)
995 {
996     mbIsDeckRequestedOpen = false;
997     UpdateDeckOpenState();
998 }
999 
1000 
1001 
1002 
RequestOpenDeck(void)1003 void SidebarController::RequestOpenDeck (void)
1004 {
1005     mbIsDeckRequestedOpen = true;
1006     UpdateDeckOpenState();
1007 }
1008 
1009 
1010 
1011 
UpdateDeckOpenState(void)1012 void SidebarController::UpdateDeckOpenState (void)
1013 {
1014     if ( ! mbIsDeckRequestedOpen)
1015         // No state requested.
1016         return;
1017 
1018     // Update (change) the open state when it either has not yet been initialized
1019     // or when its value differs from the requested state.
1020     if ( ! mbIsDeckOpen
1021         || mbIsDeckOpen.get() != mbIsDeckRequestedOpen.get())
1022     {
1023         if (mbIsDeckRequestedOpen.get())
1024         {
1025             if (mnSavedSidebarWidth <= TabBar::GetDefaultWidth())
1026                 SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow));
1027             else
1028                 SetChildWindowWidth(mnSavedSidebarWidth);
1029         }
1030         else
1031         {
1032             if ( ! mpParentWindow->IsFloatingMode())
1033                 mnSavedSidebarWidth = SetChildWindowWidth(TabBar::GetDefaultWidth());
1034             if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth())
1035                 mnSavedSidebarWidth = mnWidthOnSplitterButtonDown;
1036             mpParentWindow->SetStyle(mpParentWindow->GetStyle() & ~WB_SIZEABLE);
1037         }
1038 
1039         mbIsDeckOpen = mbIsDeckRequestedOpen.get();
1040         if (mbIsDeckOpen.get() && mpCurrentDeck)
1041             mpCurrentDeck->Show(mbIsDeckOpen.get());
1042         NotifyResize();
1043     }
1044 }
1045 
1046 
1047 
1048 
GetFocusManager(void)1049 FocusManager& SidebarController::GetFocusManager (void)
1050 {
1051     return maFocusManager;
1052 }
1053 
1054 
1055 
1056 
CanModifyChildWindowWidth(void)1057 bool SidebarController::CanModifyChildWindowWidth (void)
1058 {
1059     SfxSplitWindow* pSplitWindow = GetSplitWindow();
1060     if (pSplitWindow == NULL)
1061         return false;
1062 
1063     sal_uInt16 nRow (0xffff);
1064     sal_uInt16 nColumn (0xffff);
1065     if (pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow))
1066     {
1067         sal_uInt16 nRowCount (pSplitWindow->GetWindowCount(nColumn));
1068         return nRowCount==1;
1069     }
1070     else
1071         return false;
1072 }
1073 
1074 
1075 
1076 
SetChildWindowWidth(const sal_Int32 nNewWidth)1077 sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth)
1078 {
1079     SfxSplitWindow* pSplitWindow = GetSplitWindow();
1080     if (pSplitWindow == NULL)
1081         return 0;
1082 
1083     sal_uInt16 nRow (0xffff);
1084     sal_uInt16 nColumn (0xffff);
1085     pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow);
1086     const long nColumnWidth (pSplitWindow->GetLineSize(nColumn));
1087 
1088     Window* pWindow = mpParentWindow;
1089     const Point aWindowPosition (pWindow->GetPosPixel());
1090     const Size aWindowSize (pWindow->GetSizePixel());
1091 
1092     pSplitWindow->MoveWindow(
1093         mpParentWindow,
1094         Size(nNewWidth, aWindowSize.Height()),
1095         nColumn,
1096         nRow);
1097     static_cast<SplitWindow*>(pSplitWindow)->Split();
1098 
1099     return static_cast<sal_Int32>(nColumnWidth);
1100 }
1101 
1102 
1103 
1104 
RestrictWidth(void)1105 void SidebarController::RestrictWidth (void)
1106 {
1107     SfxSplitWindow* pSplitWindow = GetSplitWindow();
1108     if (pSplitWindow != NULL)
1109     {
1110         const sal_uInt16 nId (pSplitWindow->GetItemId(mpParentWindow));
1111         const sal_uInt16 nSetId (pSplitWindow->GetSet(nId));
1112         pSplitWindow->SetItemSizeRange(
1113             nSetId,
1114             Range(TabBar::GetDefaultWidth(), gnMaximumSidebarWidth));
1115     }
1116 }
1117 
1118 
1119 
1120 
GetSplitWindow(void)1121 SfxSplitWindow* SidebarController::GetSplitWindow (void)
1122 {
1123     if (mpParentWindow != NULL)
1124     {
1125         SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent());
1126         if (pSplitWindow != mpSplitWindow)
1127         {
1128             if (mpSplitWindow != NULL)
1129                 mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler));
1130 
1131             mpSplitWindow = pSplitWindow;
1132 
1133             if (mpSplitWindow != NULL)
1134                 mpSplitWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler));
1135         }
1136         return mpSplitWindow;
1137     }
1138     else
1139         return NULL;
1140 }
1141 
1142 
1143 
1144 
UpdateCloseIndicator(const bool bCloseAfterDrag)1145 void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag)
1146 {
1147     if (mpParentWindow == NULL)
1148         return;
1149 
1150     if (bCloseAfterDrag)
1151     {
1152         // Make sure that the indicator exists.
1153         if ( ! mpCloseIndicator)
1154         {
1155             mpCloseIndicator.reset(new FixedImage(mpParentWindow));
1156             FixedImage* pFixedImage = static_cast<FixedImage*>(mpCloseIndicator.get());
1157             const Image aImage (Theme::GetImage(Theme::Image_CloseIndicator));
1158             pFixedImage->SetImage(aImage);
1159             pFixedImage->SetSizePixel(aImage.GetSizePixel());
1160             pFixedImage->SetBackground(Theme::GetWallpaper(Theme::Paint_DeckBackground));
1161         }
1162 
1163         // Place and show the indicator.
1164         const Size aWindowSize (mpParentWindow->GetSizePixel());
1165         const Size aImageSize (mpCloseIndicator->GetSizePixel());
1166         mpCloseIndicator->SetPosPixel(
1167             Point(
1168                 aWindowSize.Width() - TabBar::GetDefaultWidth() - aImageSize.Width(),
1169                 (aWindowSize.Height() - aImageSize.Height())/2));
1170         mpCloseIndicator->Show();
1171     }
1172     else
1173     {
1174         // Hide but don't delete the indicator.
1175         if (mpCloseIndicator)
1176             mpCloseIndicator->Hide();
1177     }
1178 }
1179 
1180 
1181 
1182 
UpdateTitleBarIcons(void)1183 void SidebarController::UpdateTitleBarIcons (void)
1184 {
1185     if ( ! mpCurrentDeck)
1186         return;
1187 
1188     const bool bIsHighContrastModeActive (Theme::IsHighContrastMode());
1189     const ResourceManager& rResourceManager (ResourceManager::Instance());
1190 
1191     // Update the deck icon.
1192     const DeckDescriptor* pDeckDescriptor = rResourceManager.GetDeckDescriptor(mpCurrentDeck->GetId());
1193     if (pDeckDescriptor != NULL && mpCurrentDeck->GetTitleBar())
1194     {
1195         const OUString sIconURL(
1196             bIsHighContrastModeActive
1197                 ? pDeckDescriptor->msHighContrastTitleBarIconURL
1198                 : pDeckDescriptor->msTitleBarIconURL);
1199         mpCurrentDeck->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
1200     }
1201 
1202     // Update the panel icons.
1203     const SharedPanelContainer& rPanels (mpCurrentDeck->GetPanels());
1204     for (SharedPanelContainer::const_iterator
1205              iPanel(rPanels.begin()), iEnd(rPanels.end());
1206              iPanel!=iEnd;
1207              ++iPanel)
1208     {
1209         if ( ! *iPanel)
1210             continue;
1211         if ((*iPanel)->GetTitleBar() == NULL)
1212             continue;
1213         const PanelDescriptor* pPanelDescriptor = rResourceManager.GetPanelDescriptor((*iPanel)->GetId());
1214         if (pPanelDescriptor == NULL)
1215             continue;
1216         const OUString sIconURL (
1217             bIsHighContrastModeActive
1218                ? pPanelDescriptor->msHighContrastTitleBarIconURL
1219                : pPanelDescriptor->msTitleBarIconURL);
1220         (*iPanel)->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame));
1221     }
1222 }
1223 
1224 
1225 
1226 
ShowPanel(const Panel & rPanel)1227 void SidebarController::ShowPanel (const Panel& rPanel)
1228 {
1229     if (mpCurrentDeck)
1230         mpCurrentDeck->ShowPanel(rPanel);
1231 }
1232 
1233 
1234 
1235 
GetCurrentContext(void) const1236 Context SidebarController::GetCurrentContext (void) const
1237 {
1238     return maCurrentContext;
1239 }
1240 
1241 
1242 } } // end of namespace sfx2::sidebar
1243