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