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_sd.hxx"
25
26 #include "SlsListener.hxx"
27
28 #include "SlideSorter.hxx"
29 #include "SlideSorterViewShell.hxx"
30 #include "ViewShellHint.hxx"
31 #include "controller/SlideSorterController.hxx"
32 #include "controller/SlsPageSelector.hxx"
33 #include "controller/SlsCurrentSlideManager.hxx"
34 #include "controller/SlsSelectionManager.hxx"
35 #include "controller/SlsSelectionObserver.hxx"
36 #include "model/SlideSorterModel.hxx"
37 #include "model/SlsPageEnumerationProvider.hxx"
38 #include "view/SlideSorterView.hxx"
39 #include "cache/SlsPageCache.hxx"
40 #include "cache/SlsPageCacheManager.hxx"
41 #include "drawdoc.hxx"
42 #include "DrawDocShell.hxx"
43
44 #include "glob.hrc"
45 #include "ViewShellBase.hxx"
46 #include "ViewShellManager.hxx"
47 #include "FrameView.hxx"
48 #include "EventMultiplexer.hxx"
49 #include <com/sun/star/document/XEventBroadcaster.hpp>
50 #include <com/sun/star/beans/XPropertySet.hpp>
51 #include <com/sun/star/frame/FrameActionEvent.hpp>
52 #include <com/sun/star/frame/FrameAction.hpp>
53 #include <sfx2/viewfrm.hxx>
54 #include <tools/diagnose_ex.h>
55
56
57 using namespace ::com::sun::star::accessibility;
58 using namespace ::com::sun::star::beans;
59 using namespace ::com::sun::star::uno;
60 using namespace ::com::sun::star;
61
62 namespace sd { namespace slidesorter { namespace controller {
63
64
Listener(SlideSorter & rSlideSorter)65 Listener::Listener (
66 SlideSorter& rSlideSorter)
67 : ListenerInterfaceBase(maMutex),
68 mrSlideSorter(rSlideSorter),
69 mrController(mrSlideSorter.GetController()),
70 mpBase(mrSlideSorter.GetViewShellBase()),
71 mbListeningToDocument (false),
72 mbListeningToUNODocument (false),
73 mbListeningToController (false),
74 mbListeningToFrame (false),
75 mbIsMainViewChangePending(false),
76 mxControllerWeak(),
77 mxFrameWeak(),
78 mpModelChangeLock()
79 {
80 StartListening(*mrSlideSorter.GetModel().GetDocument());
81 StartListening(*mrSlideSorter.GetModel().GetDocument()->GetDocSh());
82 mbListeningToDocument = true;
83
84 // Connect to the UNO document.
85 Reference<document::XEventBroadcaster> xBroadcaster (
86 mrSlideSorter.GetModel().GetDocument()->getUnoModel(), uno::UNO_QUERY);
87 if (xBroadcaster.is())
88 {
89 xBroadcaster->addEventListener (this);
90 mbListeningToUNODocument = true;
91 }
92
93 // Listen for disposing events from the document.
94 Reference<XComponent> xComponent (xBroadcaster, UNO_QUERY);
95 if (xComponent.is())
96 xComponent->addEventListener (
97 Reference<lang::XEventListener>(
98 static_cast<XWeak*>(this), UNO_QUERY));
99
100 // Connect to the frame to listen for controllers being exchanged.
101 bool bIsMainViewShell (false);
102 ViewShell* pViewShell = mrSlideSorter.GetViewShell();
103 if (pViewShell != NULL)
104 bIsMainViewShell = pViewShell->IsMainViewShell();
105 if ( ! bIsMainViewShell)
106 {
107 // Listen to changes of certain properties.
108 Reference<frame::XFrame> xFrame;
109 Reference<frame::XController> xController (mrSlideSorter.GetXController());
110 if (xController.is())
111 xFrame = xController->getFrame();
112 mxFrameWeak = xFrame;
113 if (xFrame.is())
114 {
115 xFrame->addFrameActionListener (
116 Reference<frame::XFrameActionListener>(
117 static_cast<XWeak*>(this), UNO_QUERY));
118 mbListeningToFrame = true;
119 }
120
121 // Connect to the current controller.
122 ConnectToController ();
123 }
124
125 // Listen for hints of the MainViewShell as well. If that is not yet
126 // present then the EventMultiplexer will tell us when it is available.
127 if (mpBase != NULL)
128 {
129 ViewShell* pMainViewShell = mpBase->GetMainViewShell().get();
130 if (pMainViewShell != NULL
131 && pMainViewShell!=pViewShell)
132 {
133 StartListening(*pMainViewShell);
134 }
135
136 Link aLink (LINK(this, Listener, EventMultiplexerCallback));
137 mpBase->GetEventMultiplexer()->AddEventListener(
138 aLink,
139 tools::EventMultiplexerEvent::EID_MAIN_VIEW_REMOVED
140 | tools::EventMultiplexerEvent::EID_MAIN_VIEW_ADDED
141 | tools::EventMultiplexerEvent::EID_CONTROLLER_ATTACHED
142 | tools::EventMultiplexerEvent::EID_CONTROLLER_DETACHED
143 | tools::EventMultiplexerEvent::EID_CONFIGURATION_UPDATED);
144 }
145 }
146
147
148
149
~Listener(void)150 Listener::~Listener (void)
151 {
152 DBG_ASSERT( !mbListeningToDocument && !mbListeningToUNODocument && !mbListeningToFrame,
153 "sd::Listener::~Listener(), disposing() was not called, ask DBO!" );
154 }
155
156
157
158
ReleaseListeners(void)159 void Listener::ReleaseListeners (void)
160 {
161 if (mbListeningToDocument)
162 {
163 EndListening(*mrSlideSorter.GetModel().GetDocument()->GetDocSh());
164 EndListening(*mrSlideSorter.GetModel().GetDocument());
165 mbListeningToDocument = false;
166 }
167
168 if (mbListeningToUNODocument)
169 {
170 Reference<document::XEventBroadcaster> xBroadcaster (
171 mrSlideSorter.GetModel().GetDocument()->getUnoModel(), UNO_QUERY);
172 if (xBroadcaster.is())
173 xBroadcaster->removeEventListener (this);
174
175 // Remove the dispose listener.
176 Reference<XComponent> xComponent (xBroadcaster, UNO_QUERY);
177 if (xComponent.is())
178 xComponent->removeEventListener (
179 Reference<lang::XEventListener>(
180 static_cast<XWeak*>(this), UNO_QUERY));
181
182 mbListeningToUNODocument = false;
183 }
184
185 if (mbListeningToFrame)
186 {
187 // Listen to changes of certain properties.
188 Reference<frame::XFrame> xFrame (mxFrameWeak);
189 if (xFrame.is())
190 {
191 xFrame->removeFrameActionListener (
192 Reference<frame::XFrameActionListener>(
193 static_cast<XWeak*>(this), UNO_QUERY));
194 mbListeningToFrame = false;
195 }
196 }
197
198 DisconnectFromController ();
199
200 if (mpBase != NULL)
201 {
202 Link aLink (LINK(this, Listener, EventMultiplexerCallback));
203 mpBase->GetEventMultiplexer()->RemoveEventListener(
204 aLink,
205 tools::EventMultiplexerEvent::EID_MAIN_VIEW_REMOVED
206 | tools::EventMultiplexerEvent::EID_MAIN_VIEW_ADDED
207 | tools::EventMultiplexerEvent::EID_CONTROLLER_ATTACHED
208 | tools::EventMultiplexerEvent::EID_CONTROLLER_DETACHED
209 | tools::EventMultiplexerEvent::EID_CONFIGURATION_UPDATED);
210 }
211 }
212
213
214
215
ConnectToController(void)216 void Listener::ConnectToController (void)
217 {
218 ViewShell* pShell = mrSlideSorter.GetViewShell();
219
220 // Register at the controller of the main view shell (if we are that not
221 // ourself).
222 if (pShell==NULL || ! pShell->IsMainViewShell())
223 {
224 Reference<frame::XController> xController (mrSlideSorter.GetXController());
225
226 // Listen to changes of certain properties.
227 Reference<beans::XPropertySet> xSet (xController, UNO_QUERY);
228 if (xSet.is())
229 {
230 try
231 {
232 xSet->addPropertyChangeListener(String::CreateFromAscii("CurrentPage"), this);
233 }
234 catch (beans::UnknownPropertyException aEvent)
235 {
236 DBG_UNHANDLED_EXCEPTION();
237 }
238 try
239 {
240 xSet->addPropertyChangeListener(String::CreateFromAscii("IsMasterPageMode"), this);
241 }
242 catch (beans::UnknownPropertyException aEvent)
243 {
244 DBG_UNHANDLED_EXCEPTION();
245 }
246 }
247
248 // Listen for disposing events.
249 Reference<XComponent> xComponent (xController, UNO_QUERY);
250 if (xComponent.is())
251 {
252 xComponent->addEventListener (
253 Reference<lang::XEventListener>(static_cast<XWeak*>(this), UNO_QUERY));
254
255 mxControllerWeak = xController;
256 mbListeningToController = true;
257 }
258 }
259 }
260
261
262
263
DisconnectFromController(void)264 void Listener::DisconnectFromController (void)
265 {
266 if (mbListeningToController)
267 {
268 Reference<frame::XController> xController = mxControllerWeak;
269 Reference<beans::XPropertySet> xSet (xController, UNO_QUERY);
270 try
271 {
272 // Remove the property listener.
273 if (xSet.is())
274 {
275 xSet->removePropertyChangeListener (
276 String::CreateFromAscii("CurrentPage"),
277 this);
278 xSet->removePropertyChangeListener (
279 String::CreateFromAscii("IsMasterPageMode"),
280 this);
281 }
282
283 // Remove the dispose listener.
284 Reference<XComponent> xComponent (xController, UNO_QUERY);
285 if (xComponent.is())
286 xComponent->removeEventListener (
287 Reference<lang::XEventListener>(
288 static_cast<XWeak*>(this), UNO_QUERY));
289 }
290 catch (beans::UnknownPropertyException aEvent)
291 {
292 DBG_UNHANDLED_EXCEPTION();
293 }
294
295 mbListeningToController = false;
296 mxControllerWeak = Reference<frame::XController>();
297 }
298 }
299
300
301
302
Notify(SfxBroadcaster & rBroadcaster,const SfxHint & rHint)303 void Listener::Notify (
304 SfxBroadcaster& rBroadcaster,
305 const SfxHint& rHint)
306 {
307 if (rHint.ISA(SdrHint))
308 {
309 SdrHint& rSdrHint (*PTR_CAST(SdrHint,&rHint));
310 switch (rSdrHint.GetKind())
311 {
312 case HINT_PAGEORDERCHG:
313 if (&rBroadcaster == mrSlideSorter.GetModel().GetDocument())
314 HandleModelChange(rSdrHint.GetPage());
315 break;
316
317 default:
318 break;
319 }
320 }
321 else if (rHint.ISA(ViewShellHint))
322 {
323 ViewShellHint& rViewShellHint (*PTR_CAST(ViewShellHint,&rHint));
324 switch (rViewShellHint.GetHintId())
325 {
326 case ViewShellHint::HINT_PAGE_RESIZE_START:
327 // Initiate a model change but do nothing (well, not much)
328 // until we are told that all slides have been resized.
329 mpModelChangeLock.reset(new SlideSorterController::ModelChangeLock(mrController));
330 mrController.HandleModelChange();
331 break;
332
333 case ViewShellHint::HINT_PAGE_RESIZE_END:
334 // All slides have been resized. The model has to be updated.
335 mpModelChangeLock.reset();
336 break;
337
338 case ViewShellHint::HINT_CHANGE_EDIT_MODE_START:
339 mrController.PrepareEditModeChange();
340 break;
341
342 case ViewShellHint::HINT_CHANGE_EDIT_MODE_END:
343 mrController.FinishEditModeChange();
344 break;
345
346 case ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START:
347 mpModelChangeLock.reset(new SlideSorterController::ModelChangeLock(mrController));
348 break;
349
350 case ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END:
351 mpModelChangeLock.reset();
352 break;
353 }
354 }
355 else if (rHint.ISA(SfxSimpleHint))
356 {
357 SfxSimpleHint& rSfxSimpleHint (*PTR_CAST(SfxSimpleHint,&rHint));
358 switch (rSfxSimpleHint.GetId())
359 {
360 case SFX_HINT_DOCCHANGED:
361 mrController.CheckForMasterPageAssignment();
362 mrController.CheckForSlideTransitionAssignment();
363 break;
364 }
365 }
366 }
367
368
369
370
IMPL_LINK(Listener,EventMultiplexerCallback,::sd::tools::EventMultiplexerEvent *,pEvent)371 IMPL_LINK(Listener, EventMultiplexerCallback, ::sd::tools::EventMultiplexerEvent*, pEvent)
372 {
373 switch (pEvent->meEventId)
374 {
375 case tools::EventMultiplexerEvent::EID_MAIN_VIEW_REMOVED:
376 {
377 if (mpBase != NULL)
378 {
379 ViewShell* pMainViewShell = mpBase->GetMainViewShell().get();
380 if (pMainViewShell != NULL)
381 EndListening(*pMainViewShell);
382 }
383 }
384 break;
385
386
387 case tools::EventMultiplexerEvent::EID_MAIN_VIEW_ADDED:
388 mbIsMainViewChangePending = true;
389 break;
390
391 case tools::EventMultiplexerEvent::EID_CONFIGURATION_UPDATED:
392 if (mbIsMainViewChangePending && mpBase != NULL)
393 {
394 mbIsMainViewChangePending = false;
395 ViewShell* pMainViewShell = mpBase->GetMainViewShell().get();
396 if (pMainViewShell != NULL
397 && pMainViewShell!=mrSlideSorter.GetViewShell())
398 {
399 StartListening (*pMainViewShell);
400 }
401 }
402 break;
403
404 case tools::EventMultiplexerEvent::EID_CONTROLLER_ATTACHED:
405 {
406 ConnectToController();
407 // mrController.GetPageSelector().GetCoreSelection();
408 UpdateEditMode();
409 }
410 break;
411
412
413 case tools::EventMultiplexerEvent::EID_CONTROLLER_DETACHED:
414 DisconnectFromController();
415 break;
416
417 case tools::EventMultiplexerEvent::EID_SHAPE_CHANGED:
418 case tools::EventMultiplexerEvent::EID_SHAPE_INSERTED:
419 case tools::EventMultiplexerEvent::EID_SHAPE_REMOVED:
420 HandleShapeModification(static_cast<const SdrPage*>(pEvent->mpUserData));
421 break;
422
423 case tools::EventMultiplexerEvent::EID_END_TEXT_EDIT:
424 if (pEvent->mpUserData != NULL)
425 {
426 const SdrObject* pObject = static_cast<const SdrObject*>(pEvent->mpUserData);
427 HandleShapeModification(pObject->GetPage());
428 }
429 break;
430
431 default:
432 break;
433 }
434
435 return 0;
436 }
437
438
439
440
441 //===== lang::XEventListener ================================================
442
disposing(const lang::EventObject & rEventObject)443 void SAL_CALL Listener::disposing (
444 const lang::EventObject& rEventObject)
445 throw (RuntimeException)
446 {
447 if ((mbListeningToDocument || mbListeningToUNODocument)
448 && mrSlideSorter.GetModel().GetDocument()!=NULL
449 && rEventObject.Source
450 == mrSlideSorter.GetModel().GetDocument()->getUnoModel())
451 {
452 mbListeningToDocument = false;
453 mbListeningToUNODocument = false;
454 }
455 else if (mbListeningToController)
456 {
457 Reference<frame::XController> xController (mxControllerWeak);
458 if (rEventObject.Source == xController)
459 {
460 mbListeningToController = false;
461 }
462 }
463 }
464
465
466
467
468 //===== document::XEventListener ============================================
469
notifyEvent(const document::EventObject &)470 void SAL_CALL Listener::notifyEvent (
471 const document::EventObject& )
472 throw (RuntimeException)
473 {
474 }
475
476
477
478
479 //===== beans::XPropertySetListener =========================================
480
propertyChange(const PropertyChangeEvent & rEvent)481 void SAL_CALL Listener::propertyChange (
482 const PropertyChangeEvent& rEvent)
483 throw (RuntimeException)
484 {
485 ThrowIfDisposed();
486
487 static const ::rtl::OUString sCurrentPagePropertyName (
488 RTL_CONSTASCII_USTRINGPARAM("CurrentPage"));
489 static const ::rtl::OUString sEditModePropertyName (
490 RTL_CONSTASCII_USTRINGPARAM("IsMasterPageMode"));
491
492 if (rEvent.PropertyName.equals(sCurrentPagePropertyName))
493 {
494 Any aCurrentPage = rEvent.NewValue;
495 Reference<beans::XPropertySet> xPageSet (aCurrentPage, UNO_QUERY);
496 if (xPageSet.is())
497 {
498 try
499 {
500 Any aPageNumber = xPageSet->getPropertyValue (
501 String(RTL_CONSTASCII_USTRINGPARAM("Number")));
502 sal_Int32 nCurrentPage = 0;
503 aPageNumber >>= nCurrentPage;
504 mrController.GetCurrentSlideManager()->NotifyCurrentSlideChange(nCurrentPage-1);
505 }
506 catch (beans::UnknownPropertyException aEvent)
507 {
508 DBG_UNHANDLED_EXCEPTION();
509 }
510 catch (lang::DisposedException&)
511 {
512 // Something is already disposed. There is not much we can
513 // do, except not to crash.
514 }
515 }
516 }
517 else if (rEvent.PropertyName.equals (sEditModePropertyName))
518 {
519 sal_Bool bIsMasterPageMode = sal_False;
520 rEvent.NewValue >>= bIsMasterPageMode;
521 mrController.ChangeEditMode (
522 bIsMasterPageMode ? EM_MASTERPAGE : EM_PAGE);
523 }
524 }
525
526
527
528
529 //===== frame::XFrameActionListener ==========================================
530
frameAction(const frame::FrameActionEvent & rEvent)531 void SAL_CALL Listener::frameAction (const frame::FrameActionEvent& rEvent)
532 throw (::com::sun::star::uno::RuntimeException)
533 {
534 switch (rEvent.Action)
535 {
536 case frame::FrameAction_COMPONENT_DETACHING:
537 DisconnectFromController();
538 break;
539
540 case frame::FrameAction_COMPONENT_REATTACHED:
541 {
542 ConnectToController();
543 mrController.GetPageSelector().GetCoreSelection();
544 UpdateEditMode();
545 }
546 break;
547
548 default:
549 break;
550 }
551 }
552
553
554
555
556 //===== accessibility::XAccessibleEventListener ==============================
557
notifyEvent(const AccessibleEventObject &)558 void SAL_CALL Listener::notifyEvent (
559 const AccessibleEventObject& )
560 throw (RuntimeException)
561 {
562 }
563
564
565
566
disposing(void)567 void SAL_CALL Listener::disposing (void)
568 {
569 ReleaseListeners();
570 }
571
572
573
574
UpdateEditMode(void)575 void Listener::UpdateEditMode (void)
576 {
577 // When there is a new controller then the edit mode may have changed at
578 // the same time.
579 Reference<frame::XController> xController (mxControllerWeak);
580 Reference<beans::XPropertySet> xSet (xController, UNO_QUERY);
581 bool bIsMasterPageMode = false;
582 if (xSet != NULL)
583 {
584 try
585 {
586 Any aValue (xSet->getPropertyValue(
587 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("IsMasterPageMode"))));
588 aValue >>= bIsMasterPageMode;
589 }
590 catch (beans::UnknownPropertyException e)
591 {
592 // When the property is not supported then the master page mode
593 // is not supported, too.
594 bIsMasterPageMode = false;
595 }
596 }
597 mrController.ChangeEditMode (
598 bIsMasterPageMode ? EM_MASTERPAGE : EM_PAGE);
599 }
600
601
602
HandleModelChange(const SdrPage * pPage)603 void Listener::HandleModelChange (const SdrPage* pPage)
604 {
605 // Notify model and selection observer about the page. The return value
606 // of the model call acts as filter as to which events to pass to the
607 // selection observer.
608 if (mrSlideSorter.GetModel().NotifyPageEvent(pPage))
609 {
610 // The page of the hint belongs (or belonged) to the model.
611
612 // Tell the cache manager that the preview bitmaps for a deleted
613 // page can be removed from all caches.
614 if (pPage!=NULL && ! pPage->IsInserted())
615 cache::PageCacheManager::Instance()->ReleasePreviewBitmap(pPage);
616
617 mrController.GetSelectionManager()->GetSelectionObserver()->NotifyPageEvent(pPage);
618 }
619
620 // Tell the controller about the model change only when the document is
621 // in a sane state, not just in the middle of a larger change.
622 SdDrawDocument* pDocument (mrSlideSorter.GetModel().GetDocument());
623 if (pDocument != NULL
624 && pDocument->GetMasterSdPageCount(PK_STANDARD) == pDocument->GetMasterSdPageCount(PK_NOTES))
625 {
626 // A model change can make updates of some text fields necessary
627 // (like page numbers and page count.) Invalidate all previews in
628 // the cache to cope with this. Doing this on demand would be a
629 // nice optimization.
630 cache::PageCacheManager::Instance()->InvalidateAllPreviewBitmaps(pDocument->getUnoModel());
631
632 mrController.HandleModelChange();
633 }
634 }
635
636
637
HandleShapeModification(const SdrPage * pPage)638 void Listener::HandleShapeModification (const SdrPage* pPage)
639 {
640 if (pPage == NULL)
641 return;
642
643 // Invalidate the preview of the page (in all slide sorters that display
644 // it.)
645 ::boost::shared_ptr<cache::PageCacheManager> pCacheManager (cache::PageCacheManager::Instance());
646 if ( ! pCacheManager)
647 return;
648 SdDrawDocument* pDocument = mrSlideSorter.GetModel().GetDocument();
649 if (pDocument == NULL)
650 {
651 OSL_ASSERT(pDocument!=NULL);
652 return;
653 }
654 pCacheManager->InvalidatePreviewBitmap(pDocument->getUnoModel(), pPage);
655 mrSlideSorter.GetView().GetPreviewCache()->RequestPreviewBitmap(pPage);
656
657 // When the page is a master page then invalidate the previews of all
658 // pages that are linked to this master page.
659 if (pPage->IsMasterPage())
660 {
661 for (sal_uInt16 nIndex=0,nCount=pDocument->GetSdPageCount(PK_STANDARD);
662 nIndex<nCount;
663 ++nIndex)
664 {
665 const SdPage* pCandidate = pDocument->GetSdPage(nIndex, PK_STANDARD);
666 if (pCandidate!=NULL && pCandidate->TRG_HasMasterPage())
667 {
668 if (&pCandidate->TRG_GetMasterPage() == pPage)
669 pCacheManager->InvalidatePreviewBitmap(pDocument->getUnoModel(), pCandidate);
670 }
671 else
672 {
673 OSL_ASSERT(pCandidate!=NULL && pCandidate->TRG_HasMasterPage());
674 }
675 }
676 }
677 }
678
679
680
681
ThrowIfDisposed(void)682 void Listener::ThrowIfDisposed (void)
683 throw (::com::sun::star::lang::DisposedException)
684 {
685 if (rBHelper.bDisposed || rBHelper.bInDispose)
686 {
687 throw lang::DisposedException (
688 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
689 "SlideSorterController object has already been disposed")),
690 static_cast<uno::XWeak*>(this));
691 }
692 }
693
694
695
696
697 } } } // end of namespace ::sd::slidesorter::controller
698