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