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