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