1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <string_view>
23 
24 #include "PresenterController.hxx"
25 
26 #include "PresenterAccessibility.hxx"
27 #include "PresenterCanvasHelper.hxx"
28 #include "PresenterCurrentSlideObserver.hxx"
29 #include "PresenterScreen.hxx"
30 #include "PresenterPaintManager.hxx"
31 #include "PresenterPaneBase.hxx"
32 #include "PresenterPaneContainer.hxx"
33 #include "PresenterPaneBorderPainter.hxx"
34 #include "PresenterTheme.hxx"
35 #include "PresenterViewFactory.hxx"
36 #include "PresenterWindowManager.hxx"
37 
38 #include <com/sun/star/awt/Key.hpp>
39 #include <com/sun/star/awt/KeyModifier.hpp>
40 #include <com/sun/star/awt/MouseButton.hpp>
41 #include <com/sun/star/container/XNamed.hpp>
42 #include <com/sun/star/drawing/XDrawView.hpp>
43 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
44 #include <com/sun/star/drawing/framework/ResourceActivationMode.hpp>
45 #include <com/sun/star/drawing/framework/ResourceId.hpp>
46 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
47 #include <com/sun/star/frame/FrameSearchFlag.hpp>
48 #include <com/sun/star/frame/XDispatchProvider.hpp>
49 #include <com/sun/star/presentation/AnimationEffect.hpp>
50 #include <com/sun/star/presentation/XPresentation.hpp>
51 #include <com/sun/star/presentation/XPresentationSupplier.hpp>
52 #include <com/sun/star/rendering/TextDirection.hpp>
53 #include <com/sun/star/util/URLTransformer.hpp>
54 
55 #include <rtl/ustrbuf.hxx>
56 
57 using namespace ::com::sun::star;
58 using namespace ::com::sun::star::uno;
59 using namespace ::com::sun::star::presentation;
60 using namespace ::com::sun::star::drawing::framework;
61 
62 namespace {
63     const sal_Int32 ResourceActivationEventType = 0;
64     const sal_Int32 ResourceDeactivationEventType = 1;
65     const sal_Int32 ConfigurationUpdateEndEventType = 2;
66 }
67 
68 namespace sdext::presenter {
69 
~IPresentationTime()70 IPresentationTime::~IPresentationTime()
71 {
72 }
73 
74 PresenterController::InstanceContainer PresenterController::maInstances;
75 
Instance(const css::uno::Reference<css::frame::XFrame> & rxFrame)76 ::rtl::Reference<PresenterController> PresenterController::Instance (
77     const css::uno::Reference<css::frame::XFrame>& rxFrame)
78 {
79     InstanceContainer::const_iterator iInstance (maInstances.find(rxFrame));
80     if (iInstance != maInstances.end())
81         return iInstance->second;
82     else
83         return ::rtl::Reference<PresenterController>();
84 }
85 
PresenterController(const css::uno::WeakReference<css::lang::XEventListener> & rxScreen,const Reference<XComponentContext> & rxContext,const Reference<frame::XController> & rxController,const Reference<presentation::XSlideShowController> & rxSlideShowController,const rtl::Reference<PresenterPaneContainer> & rpPaneContainer,const Reference<XResourceId> & rxMainPaneId)86 PresenterController::PresenterController (
87     const css::uno::WeakReference<css::lang::XEventListener> &rxScreen,
88     const Reference<XComponentContext>& rxContext,
89     const Reference<frame::XController>& rxController,
90     const Reference<presentation::XSlideShowController>& rxSlideShowController,
91     const rtl::Reference<PresenterPaneContainer>& rpPaneContainer,
92     const Reference<XResourceId>& rxMainPaneId)
93     : PresenterControllerInterfaceBase(m_aMutex),
94       mxScreen(rxScreen),
95       mxComponentContext(rxContext),
96       mxController(rxController),
97       mxConfigurationController(),
98       mxSlideShowController(rxSlideShowController),
99       mxMainPaneId(rxMainPaneId),
100       mpPaneContainer(rpPaneContainer),
101       mnCurrentSlideIndex(-1),
102       mxCurrentSlide(),
103       mxNextSlide(),
104       mpWindowManager(new PresenterWindowManager(rxContext,mpPaneContainer,this)),
105       mpTheme(),
106       mxMainWindow(),
107       mpPaneBorderPainter(),
108       mpCanvasHelper(std::make_shared<PresenterCanvasHelper>()),
109       mxPresenterHelper(),
110       mpPaintManager(),
111       mnPendingSlideNumber(-1),
112       mxUrlTransformer(),
113       mpAccessibleObject(),
114       mbIsAccessibilityActive(false)
115 {
116     OSL_ASSERT(mxController.is());
117 
118     if ( ! mxSlideShowController.is())
119         throw lang::IllegalArgumentException(
120             "missing slide show controller",
121             static_cast<XWeak*>(this),
122             2);
123 
124     new PresenterCurrentSlideObserver(this,rxSlideShowController);
125 
126     // Listen for configuration changes.
127     Reference<XControllerManager> xCM (mxController, UNO_QUERY_THROW);
128     mxConfigurationController = xCM->getConfigurationController();
129     if (mxConfigurationController.is())
130     {
131         mxConfigurationController->addConfigurationChangeListener(
132             this,
133             "ResourceActivation",
134             Any(ResourceActivationEventType));
135         mxConfigurationController->addConfigurationChangeListener(
136             this,
137             "ResourceDeactivation",
138             Any(ResourceDeactivationEventType));
139         mxConfigurationController->addConfigurationChangeListener(
140             this,
141             "ConfigurationUpdateEnd",
142             Any(ConfigurationUpdateEndEventType));
143     }
144 
145     // Listen for the frame being activated.
146     Reference<frame::XFrame> xFrame (mxController->getFrame());
147     if (xFrame.is())
148         xFrame->addFrameActionListener(this);
149 
150     // Create the border painter.
151     mpPaneBorderPainter = new PresenterPaneBorderPainter(rxContext);
152     mpWindowManager->SetPaneBorderPainter(mpPaneBorderPainter);
153 
154     // Create an object that is able to load the bitmaps in a format that is
155     // supported by the canvas.
156     Reference<lang::XMultiComponentFactory> xFactory =
157         rxContext->getServiceManager();
158     if ( ! xFactory.is())
159         return;
160     mxPresenterHelper.set(
161         xFactory->createInstanceWithContext(
162             "com.sun.star.drawing.PresenterHelper",
163             rxContext),
164         UNO_QUERY_THROW);
165 
166     if (mxSlideShowController.is())
167     {
168         mxSlideShowController->activate();
169         Reference<beans::XPropertySet> xProperties (mxSlideShowController, UNO_QUERY);
170         if (xProperties.is())
171         {
172             Reference<awt::XWindow> xWindow (
173                 xProperties->getPropertyValue("ParentWindow"), UNO_QUERY);
174             if (xWindow.is())
175                 xWindow->addKeyListener(this);
176         }
177     }
178 
179     UpdateCurrentSlide(0);
180 
181     maInstances[mxController->getFrame()] = this;
182 
183     // Create a URLTransformer.
184     if (xFactory.is())
185     {
186         mxUrlTransformer.set(util::URLTransformer::create(mxComponentContext));
187     }
188 }
189 
~PresenterController()190 PresenterController::~PresenterController()
191 {
192 }
193 
disposing()194 void PresenterController::disposing()
195 {
196     maInstances.erase(mxController->getFrame());
197 
198     if (mxMainWindow.is())
199     {
200         mxMainWindow->removeKeyListener(this);
201         mxMainWindow->removeMouseListener(this);
202         mxMainWindow = nullptr;
203     }
204     if (mxConfigurationController.is())
205         mxConfigurationController->removeConfigurationChangeListener(this);
206 
207     if (mxController.is())
208     {
209         Reference<frame::XFrame> xFrame (mxController->getFrame());
210         if (xFrame.is())
211             xFrame->removeFrameActionListener(this);
212         mxController = nullptr;
213     }
214 
215     Reference<XComponent> xWindowManagerComponent (
216         static_cast<XWeak*>(mpWindowManager.get()), UNO_QUERY);
217     mpWindowManager = nullptr;
218     if (xWindowManagerComponent.is())
219         xWindowManagerComponent->dispose();
220 
221     mxComponentContext = nullptr;
222     mxConfigurationController = nullptr;
223     mxSlideShowController = nullptr;
224     mxMainPaneId = nullptr;
225     mpPaneContainer = nullptr;
226     mnCurrentSlideIndex = -1;
227     mxCurrentSlide = nullptr;
228     mxNextSlide = nullptr;
229     mpTheme.reset();
230     {
231         Reference<lang::XComponent> xComponent (
232             static_cast<XWeak*>(mpPaneBorderPainter.get()), UNO_QUERY);
233         mpPaneBorderPainter = nullptr;
234         if (xComponent.is())
235             xComponent->dispose();
236     }
237     mpCanvasHelper.reset();
238     {
239         Reference<lang::XComponent> xComponent (mxPresenterHelper, UNO_QUERY);
240         mxPresenterHelper = nullptr;
241         if (xComponent.is())
242             xComponent->dispose();
243     }
244     mpPaintManager.reset();
245     mnPendingSlideNumber = -1;
246     {
247         Reference<lang::XComponent> xComponent (mxUrlTransformer, UNO_QUERY);
248         mxUrlTransformer = nullptr;
249         if (xComponent.is())
250             xComponent->dispose();
251     }
252 }
253 
UpdateCurrentSlide(const sal_Int32 nOffset)254 void PresenterController::UpdateCurrentSlide (const sal_Int32 nOffset)
255 {
256     // std::cerr << "Updating current Slide to " << nOffset << std::endl;
257     GetSlides(nOffset);
258     UpdatePaneTitles();
259     UpdateViews();
260 
261     // Update the accessibility object.
262     if (IsAccessibilityActive())
263     {
264         mpAccessibleObject->NotifyCurrentSlideChange();
265     }
266 }
267 
GetSlides(const sal_Int32 nOffset)268 void PresenterController::GetSlides (const sal_Int32 nOffset)
269 {
270     if ( ! mxSlideShowController.is())
271         return;
272 
273     // Get the current slide from the slide show controller.
274     mxCurrentSlide = nullptr;
275     Reference<container::XIndexAccess> xIndexAccess(mxSlideShowController, UNO_QUERY);
276     try
277     {
278         sal_Int32 nSlideIndex = mxSlideShowController->getCurrentSlideIndex() + nOffset;
279         if (mxSlideShowController->isPaused())
280             nSlideIndex = -1;
281 
282         if (xIndexAccess.is() && nSlideIndex>=0)
283         {
284             if (nSlideIndex < xIndexAccess->getCount())
285             {
286                 mnCurrentSlideIndex = nSlideIndex;
287                 mxCurrentSlide.set( xIndexAccess->getByIndex(nSlideIndex), UNO_QUERY);
288             }
289         }
290     }
291     catch (RuntimeException&)
292     {
293     }
294 
295     // Get the next slide.
296     mxNextSlide = nullptr;
297     try
298     {
299         const sal_Int32 nNextSlideIndex (mxSlideShowController->getNextSlideIndex()+nOffset);
300         if (nNextSlideIndex >= 0)
301         {
302             if (xIndexAccess.is())
303             {
304                 if (nNextSlideIndex < xIndexAccess->getCount())
305                     mxNextSlide.set( xIndexAccess->getByIndex(nNextSlideIndex), UNO_QUERY);
306             }
307         }
308     }
309     catch (RuntimeException&)
310     {
311     }
312 }
313 
UpdatePaneTitles()314 void PresenterController::UpdatePaneTitles()
315 {
316     if ( ! mxSlideShowController.is())
317         return;
318 
319     // Get placeholders and their values.
320     static const OUStringLiteral sCurrentSlideNumberPlaceholder (u"CURRENT_SLIDE_NUMBER");
321     static const OUStringLiteral sCurrentSlideNamePlaceholder (u"CURRENT_SLIDE_NAME");
322     static const OUStringLiteral sSlideCountPlaceholder (u"SLIDE_COUNT");
323 
324     // Get string for slide count.
325     OUString sSlideCount ("---");
326     Reference<container::XIndexAccess> xIndexAccess(mxSlideShowController, UNO_QUERY);
327     if (xIndexAccess.is())
328         sSlideCount = OUString::number(xIndexAccess->getCount());
329 
330     // Get string for current slide index.
331     OUString sCurrentSlideNumber (OUString::number(mnCurrentSlideIndex + 1));
332 
333     // Get name of the current slide.
334     OUString sCurrentSlideName;
335     Reference<container::XNamed> xNamedSlide (mxCurrentSlide, UNO_QUERY);
336     if (xNamedSlide.is())
337         sCurrentSlideName = xNamedSlide->getName();
338     Reference<beans::XPropertySet> xSlideProperties (mxCurrentSlide, UNO_QUERY);
339     if (xSlideProperties.is())
340     {
341         try
342         {
343             OUString sName;
344             if (xSlideProperties->getPropertyValue("LinkDisplayName") >>= sName)
345             {
346                 // Find out whether the name of the current slide has been
347                 // automatically created or has been set by the user.
348                 if (sName != sCurrentSlideName)
349                     sCurrentSlideName = sName;
350             }
351         }
352         catch (const beans::UnknownPropertyException&)
353         {
354         }
355     }
356 
357     // Replace the placeholders with their current values.
358     for (auto& rxPane : mpPaneContainer->maPanes)
359     {
360         OSL_ASSERT(rxPane != nullptr);
361 
362         OUString sTemplate (IsAccessibilityActive()
363             ? rxPane->msAccessibleTitleTemplate
364             : rxPane->msTitleTemplate);
365         if (sTemplate.isEmpty())
366             continue;
367 
368         OUStringBuffer sResult;
369         sResult.ensureCapacity(sTemplate.getLength());
370 
371         sal_Int32 nIndex (0);
372         while (true)
373         {
374             sal_Int32 nStartIndex = sTemplate.indexOf('%', nIndex);
375             if (nStartIndex < 0)
376             {
377                 // Add the remaining part of the string.
378                 sResult.append(sTemplate.subView(nIndex));
379                 break;
380             }
381             else
382             {
383                 // Add the part preceding the next %.
384                 sResult.append(sTemplate.subView(nIndex, nStartIndex-nIndex));
385 
386                 // Get the placeholder
387                 ++nStartIndex;
388                 const sal_Int32 nEndIndex (sTemplate.indexOf('%', nStartIndex+1));
389                 const OUString sPlaceholder (sTemplate.copy(nStartIndex, nEndIndex-nStartIndex));
390                 nIndex = nEndIndex+1;
391 
392                 // Replace the placeholder with its current value.
393                 if (sPlaceholder == sCurrentSlideNumberPlaceholder)
394                     sResult.append(sCurrentSlideNumber);
395                 else if (sPlaceholder == sCurrentSlideNamePlaceholder)
396                     sResult.append(sCurrentSlideName);
397                 else if (sPlaceholder == sSlideCountPlaceholder)
398                     sResult.append(sSlideCount);
399             }
400         }
401 
402         rxPane->msTitle = sResult.makeStringAndClear();
403         if (rxPane->mxPane.is())
404             rxPane->mxPane->SetTitle(rxPane->msTitle);
405     }
406 }
407 
UpdateViews()408 void PresenterController::UpdateViews()
409 {
410     // Tell all views about the slides they should display.
411     for (const auto& rxPane : mpPaneContainer->maPanes)
412     {
413         Reference<drawing::XDrawView> xDrawView (rxPane->mxView, UNO_QUERY);
414         if (xDrawView.is())
415             xDrawView->setCurrentPage(mxCurrentSlide);
416     }
417 }
418 
419 SharedBitmapDescriptor
GetViewBackground(const OUString & rsViewURL) const420     PresenterController::GetViewBackground (const OUString& rsViewURL) const
421 {
422     if (mpTheme != nullptr)
423     {
424         const OUString sStyleName (mpTheme->GetStyleName(rsViewURL));
425         return mpTheme->GetBitmap(sStyleName, "Background");
426     }
427     return SharedBitmapDescriptor();
428 }
429 
430 PresenterTheme::SharedFontDescriptor
GetViewFont(const OUString & rsViewURL) const431     PresenterController::GetViewFont (const OUString& rsViewURL) const
432 {
433     if (mpTheme != nullptr)
434     {
435         const OUString sStyleName (mpTheme->GetStyleName(rsViewURL));
436         return mpTheme->GetFont(sStyleName);
437     }
438     return PresenterTheme::SharedFontDescriptor();
439 }
440 
GetTheme() const441 const std::shared_ptr<PresenterTheme>& PresenterController::GetTheme() const
442 {
443     return mpTheme;
444 }
445 
GetWindowManager() const446 const ::rtl::Reference<PresenterWindowManager>& PresenterController::GetWindowManager() const
447 {
448     return mpWindowManager;
449 }
450 
451 const Reference<presentation::XSlideShowController>&
GetSlideShowController() const452     PresenterController::GetSlideShowController() const
453 {
454     return mxSlideShowController;
455 }
456 
GetPaneContainer() const457 const rtl::Reference<PresenterPaneContainer>& PresenterController::GetPaneContainer() const
458 {
459     return mpPaneContainer;
460 }
461 
GetPaneBorderPainter() const462 const ::rtl::Reference<PresenterPaneBorderPainter>& PresenterController::GetPaneBorderPainter() const
463 {
464     return mpPaneBorderPainter;
465 }
466 
GetCanvasHelper() const467 const std::shared_ptr<PresenterCanvasHelper>& PresenterController::GetCanvasHelper() const
468 {
469     return mpCanvasHelper;
470 }
471 
GetPresenterHelper() const472 const Reference<drawing::XPresenterHelper>& PresenterController::GetPresenterHelper() const
473 {
474     return mxPresenterHelper;
475 }
476 
GetPaintManager() const477 const std::shared_ptr<PresenterPaintManager>& PresenterController::GetPaintManager() const
478 {
479     return mpPaintManager;
480 }
481 
ShowView(const OUString & rsViewURL)482 void PresenterController::ShowView (const OUString& rsViewURL)
483 {
484     PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
485         mpPaneContainer->FindViewURL(rsViewURL));
486     if (!pDescriptor)
487         return;
488 
489     pDescriptor->mbIsActive = true;
490     mxConfigurationController->requestResourceActivation(
491         pDescriptor->mxPaneId,
492         ResourceActivationMode_ADD);
493     mxConfigurationController->requestResourceActivation(
494         ResourceId::createWithAnchor(
495             mxComponentContext,
496             rsViewURL,
497             pDescriptor->mxPaneId),
498         ResourceActivationMode_REPLACE);
499 }
500 
HideView(const OUString & rsViewURL)501 void PresenterController::HideView (const OUString& rsViewURL)
502 {
503     PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
504         mpPaneContainer->FindViewURL(rsViewURL));
505     if (pDescriptor)
506     {
507         mxConfigurationController->requestResourceDeactivation(
508             ResourceId::createWithAnchor(
509                 mxComponentContext,
510                 rsViewURL,
511                 pDescriptor->mxPaneId));
512     }
513 }
514 
DispatchUnoCommand(const OUString & rsCommand) const515 void PresenterController::DispatchUnoCommand (const OUString& rsCommand) const
516 {
517     if ( ! mxUrlTransformer.is())
518         return;
519 
520     util::URL aURL;
521     aURL.Complete = rsCommand;
522     mxUrlTransformer->parseStrict(aURL);
523 
524     Reference<frame::XDispatch> xDispatch (GetDispatch(aURL));
525     if ( ! xDispatch.is())
526         return;
527 
528     xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>());
529 }
530 
GetDispatch(const util::URL & rURL) const531 Reference<css::frame::XDispatch> PresenterController::GetDispatch (const util::URL& rURL) const
532 {
533     if ( ! mxController.is())
534         return nullptr;
535 
536     Reference<frame::XDispatchProvider> xDispatchProvider (mxController->getFrame(), UNO_QUERY);
537     if ( ! xDispatchProvider.is())
538         return nullptr;
539 
540     return xDispatchProvider->queryDispatch(
541         rURL,
542         OUString(),
543         frame::FrameSearchFlag::SELF);
544 }
545 
CreateURLFromString(const OUString & rsURL) const546 util::URL PresenterController::CreateURLFromString (const OUString& rsURL) const
547 {
548     util::URL aURL;
549 
550     if (mxUrlTransformer.is())
551     {
552         aURL.Complete = rsURL;
553         mxUrlTransformer->parseStrict(aURL);
554     }
555 
556     return aURL;
557 }
558 
559 const Reference<drawing::framework::XConfigurationController>&
GetConfigurationController() const560     PresenterController::GetConfigurationController() const
561 {
562     return mxConfigurationController;
563 }
564 
GetCurrentSlide() const565 const Reference<drawing::XDrawPage>& PresenterController::GetCurrentSlide() const
566 {
567     return mxCurrentSlide;
568 }
569 
HasTransition(Reference<drawing::XDrawPage> const & rxPage)570 bool PresenterController::HasTransition (Reference<drawing::XDrawPage> const & rxPage)
571 {
572     bool bTransition = false;
573     if( rxPage.is() )
574     {
575         Reference<beans::XPropertySet> xSlidePropertySet (rxPage, UNO_QUERY);
576         try
577         {
578             sal_uInt16 aTransitionType = 0;
579             xSlidePropertySet->getPropertyValue("TransitionType") >>= aTransitionType;
580             if (aTransitionType > 0)
581             {
582                 bTransition = true;
583             }
584         }
585         catch (const beans::UnknownPropertyException&)
586         {
587         }
588     }
589     return bTransition;
590 }
591 
HasCustomAnimation(Reference<drawing::XDrawPage> const & rxPage)592 bool PresenterController::HasCustomAnimation (Reference<drawing::XDrawPage> const & rxPage)
593 {
594     bool bCustomAnimation = false;
595     if( rxPage.is() )
596     {
597         sal_uInt32 i, nCount = rxPage->getCount();
598         for ( i = 0; i < nCount; i++ )
599         {
600             Reference<drawing::XShape> xShape(rxPage->getByIndex(i), UNO_QUERY);
601             Reference<beans::XPropertySet> xShapePropertySet(xShape, UNO_QUERY);
602             presentation::AnimationEffect aEffect = presentation::AnimationEffect_NONE;
603             presentation::AnimationEffect aTextEffect = presentation::AnimationEffect_NONE;
604             try
605             {
606                 xShapePropertySet->getPropertyValue("Effect") >>= aEffect;
607                 xShapePropertySet->getPropertyValue("TextEffect") >>= aTextEffect;
608             }
609             catch (const beans::UnknownPropertyException&)
610             {
611             }
612             if( aEffect != presentation::AnimationEffect_NONE ||
613                 aTextEffect != presentation::AnimationEffect_NONE )
614             {
615                 bCustomAnimation = true;
616                 break;
617             }
618         }
619     }
620     return bCustomAnimation;
621 }
622 
SetAccessibilityActiveState(const bool bIsActive)623 void PresenterController::SetAccessibilityActiveState (const bool bIsActive)
624 {
625     if ( mbIsAccessibilityActive != bIsActive)
626     {
627         mbIsAccessibilityActive = bIsActive;
628         UpdatePaneTitles();
629     }
630 }
631 
632 
HandleMouseClick(const awt::MouseEvent & rEvent)633 void PresenterController::HandleMouseClick (const awt::MouseEvent& rEvent)
634 {
635     if (!mxSlideShowController.is())
636         return;
637 
638     switch (rEvent.Buttons)
639     {
640         case awt::MouseButton::LEFT:
641             if (rEvent.Modifiers == awt::KeyModifier::MOD2)
642                 mxSlideShowController->gotoNextSlide();
643             else
644                 mxSlideShowController->gotoNextEffect();
645             break;
646 
647         case awt::MouseButton::RIGHT:
648             mxSlideShowController->gotoPreviousSlide();
649             break;
650 
651         default:
652             // Other or multiple buttons.
653             break;
654     }
655 }
656 
RequestViews(const bool bIsSlideSorterActive,const bool bIsNotesViewActive,const bool bIsHelpViewActive)657 void PresenterController::RequestViews (
658     const bool bIsSlideSorterActive,
659     const bool bIsNotesViewActive,
660     const bool bIsHelpViewActive)
661 {
662     for (const auto& rxPane : mpPaneContainer->maPanes)
663     {
664         bool bActivate (true);
665         const OUString sViewURL (rxPane->msViewURL);
666         if (sViewURL == PresenterViewFactory::msNotesViewURL)
667         {
668             bActivate = bIsNotesViewActive && !bIsSlideSorterActive && !bIsHelpViewActive;
669         }
670         else if (sViewURL == PresenterViewFactory::msSlideSorterURL)
671         {
672             bActivate = bIsSlideSorterActive;
673         }
674         else if (sViewURL == PresenterViewFactory::msCurrentSlidePreviewViewURL
675             || sViewURL == PresenterViewFactory::msNextSlidePreviewViewURL)
676         {
677             bActivate = !bIsSlideSorterActive && ! bIsHelpViewActive;
678         }
679         else if (sViewURL == PresenterViewFactory::msToolBarViewURL)
680         {
681             bActivate = true;
682         }
683         else if (sViewURL == PresenterViewFactory::msHelpViewURL)
684         {
685             bActivate = bIsHelpViewActive;
686         }
687 
688         if (bActivate)
689             ShowView(sViewURL);
690         else
691             HideView(sViewURL);
692     }
693 }
694 
SetPresentationTime(IPresentationTime * pPresentationTime)695 void PresenterController::SetPresentationTime(IPresentationTime* pPresentationTime)
696 {
697     mpPresentationTime = pPresentationTime;
698 }
699 
GetPresentationTime()700 IPresentationTime* PresenterController::GetPresentationTime()
701 {
702     return mpPresentationTime;
703 }
704 
705 //----- XConfigurationChangeListener ------------------------------------------
706 
notifyConfigurationChange(const ConfigurationChangeEvent & rEvent)707 void SAL_CALL PresenterController::notifyConfigurationChange (
708     const ConfigurationChangeEvent& rEvent)
709 {
710     if (rBHelper.bDisposed || rBHelper.bInDispose)
711     {
712         throw lang::DisposedException (
713             "PresenterController object has already been disposed",
714             static_cast<uno::XWeak*>(this));
715     }
716 
717     sal_Int32 nType (0);
718     if ( ! (rEvent.UserData >>= nType))
719         return;
720 
721     switch (nType)
722     {
723         case ResourceActivationEventType:
724             if (rEvent.ResourceId->compareTo(mxMainPaneId) == 0)
725             {
726                 InitializeMainPane(Reference<XPane>(rEvent.ResourceObject,UNO_QUERY));
727             }
728             else if (rEvent.ResourceId->isBoundTo(mxMainPaneId,AnchorBindingMode_DIRECT))
729             {
730                 // A pane bound to the main pane has been created and is
731                 // stored in the pane container.
732                 Reference<XPane> xPane (rEvent.ResourceObject,UNO_QUERY);
733                 if (xPane.is())
734                 {
735                     mpPaneContainer->FindPaneId(xPane->getResourceId());
736                 }
737             }
738             else if (rEvent.ResourceId->isBoundTo(mxMainPaneId,AnchorBindingMode_INDIRECT))
739             {
740                 // A view bound to one of the panes has been created and is
741                 // stored in the pane container along with its pane.
742                 Reference<XView> xView (rEvent.ResourceObject,UNO_QUERY);
743                 if (xView.is())
744                 {
745                     mpPaneContainer->StoreView(xView);
746                     UpdateViews();
747                     mpWindowManager->NotifyViewCreation(xView);
748                 }
749             }
750             break;
751 
752         case ResourceDeactivationEventType:
753             if (rEvent.ResourceId->isBoundTo(mxMainPaneId,AnchorBindingMode_INDIRECT))
754             {
755                 // If this is a view then remove it from the pane container.
756                 Reference<XView> xView (rEvent.ResourceObject,UNO_QUERY);
757                 if (xView.is())
758                 {
759                     PresenterPaneContainer::SharedPaneDescriptor pDescriptor(
760                         mpPaneContainer->RemoveView(xView));
761 
762                     // A possibly opaque view has been removed.  Update()
763                     // updates the clip polygon.
764                     mpWindowManager->Update();
765                     // Request the repainting of the area previously
766                     // occupied by the view.
767                     if (pDescriptor)
768                         GetPaintManager()->Invalidate(pDescriptor->mxBorderWindow);
769                 }
770             }
771             break;
772 
773         case ConfigurationUpdateEndEventType:
774             if (IsAccessibilityActive())
775             {
776                 mpAccessibleObject->UpdateAccessibilityHierarchy();
777                 UpdateCurrentSlide(0);
778             }
779             break;
780     }
781 }
782 
783 //----- XEventListener --------------------------------------------------------
784 
disposing(const lang::EventObject & rEvent)785 void SAL_CALL PresenterController::disposing (
786     const lang::EventObject& rEvent)
787 {
788     if (rEvent.Source == mxController)
789         mxController = nullptr;
790     else if (rEvent.Source == mxConfigurationController)
791         mxConfigurationController = nullptr;
792     else if (rEvent.Source == mxSlideShowController)
793         mxSlideShowController = nullptr;
794     else if (rEvent.Source == mxMainWindow)
795         mxMainWindow = nullptr;
796 }
797 
798 //----- XFrameActionListener --------------------------------------------------
799 
frameAction(const frame::FrameActionEvent & rEvent)800 void SAL_CALL PresenterController::frameAction (
801     const frame::FrameActionEvent& rEvent)
802 {
803     if (rEvent.Action == frame::FrameAction_FRAME_ACTIVATED)
804     {
805         if (mxSlideShowController.is())
806             mxSlideShowController->activate();
807     }
808 }
809 
810 //----- XKeyListener ----------------------------------------------------------
811 
keyPressed(const awt::KeyEvent & rEvent)812 void SAL_CALL PresenterController::keyPressed (const awt::KeyEvent& rEvent)
813 {
814     // Tell all views about the unhandled key event.
815     for (const auto& rxPane : mpPaneContainer->maPanes)
816     {
817         if ( ! rxPane->mbIsActive)
818             continue;
819 
820         Reference<awt::XKeyListener> xKeyListener (rxPane->mxView, UNO_QUERY);
821         if (xKeyListener.is())
822             xKeyListener->keyPressed(rEvent);
823     }
824 }
825 
keyReleased(const awt::KeyEvent & rEvent)826 void SAL_CALL PresenterController::keyReleased (const awt::KeyEvent& rEvent)
827 {
828     if (rEvent.Source != mxMainWindow)
829         return;
830 
831     switch (rEvent.KeyCode)
832     {
833         case awt::Key::ESCAPE:
834         case awt::Key::SUBTRACT:
835         {
836             if( mxController.is() )
837             {
838                 Reference< XPresentationSupplier > xPS( mxController->getModel(), UNO_QUERY );
839                 if( xPS.is() )
840                 {
841                     Reference< XPresentation > xP( xPS->getPresentation() );
842                     if( xP.is() )
843                         xP->end();
844                 }
845             }
846         }
847         break;
848 
849         case awt::Key::PAGEDOWN:
850             if (mxSlideShowController.is())
851             {
852                 if (rEvent.Modifiers == awt::KeyModifier::MOD2)
853                     mxSlideShowController->gotoNextSlide();
854                 else
855                     mxSlideShowController->gotoNextEffect();
856             }
857             break;
858 
859         case awt::Key::RIGHT:
860         case awt::Key::SPACE:
861         case awt::Key::DOWN:
862             if (mxSlideShowController.is())
863             {
864                 mxSlideShowController->gotoNextEffect();
865             }
866             break;
867 
868         case awt::Key::PAGEUP:
869             if (mxSlideShowController.is())
870             {
871                 if (rEvent.Modifiers == awt::KeyModifier::MOD2)
872                     mxSlideShowController->gotoPreviousSlide();
873                 else
874                     mxSlideShowController->gotoPreviousEffect();
875             }
876             break;
877 
878         case awt::Key::LEFT:
879         case awt::Key::UP:
880         case awt::Key::BACKSPACE:
881             if (mxSlideShowController.is())
882             {
883                 mxSlideShowController->gotoPreviousEffect();
884             }
885             break;
886 
887         case awt::Key::P:
888             if (mxSlideShowController.is())
889             {
890                 bool bPenEnabled = mxSlideShowController->getUsePen();
891                 mxSlideShowController->setUsePen( !bPenEnabled );
892             }
893             break;
894 
895         case awt::Key::E:
896             if (mxSlideShowController.is())
897             {
898                 mxSlideShowController->setEraseAllInk( true );
899             }
900             break;
901 
902         case awt::Key::HOME:
903             if (mxSlideShowController.is())
904             {
905                 mxSlideShowController->gotoFirstSlide();
906             }
907             break;
908 
909         case awt::Key::END:
910             if (mxSlideShowController.is())
911             {
912                 mxSlideShowController->gotoLastSlide();
913             }
914             break;
915 
916         case awt::Key::W:
917         case awt::Key::COMMA:
918             if (mxSlideShowController.is())
919             {
920                 if (mxSlideShowController->isPaused())
921                     mxSlideShowController->resume();
922                 else
923                     mxSlideShowController->blankScreen(0x00ffffff);
924             }
925             break;
926 
927         case awt::Key::B:
928         case awt::Key::POINT:
929             if (mxSlideShowController.is())
930             {
931                 if (mxSlideShowController->isPaused())
932                     mxSlideShowController->resume();
933                 else
934                     mxSlideShowController->blankScreen(0x00000000);
935             }
936             break;
937 
938         case awt::Key::NUM0:
939         case awt::Key::NUM1:
940         case awt::Key::NUM2:
941         case awt::Key::NUM3:
942         case awt::Key::NUM4:
943         case awt::Key::NUM5:
944         case awt::Key::NUM6:
945         case awt::Key::NUM7:
946         case awt::Key::NUM8:
947         case awt::Key::NUM9:
948             HandleNumericKeyPress(rEvent.KeyCode-awt::Key::NUM0, rEvent.Modifiers);
949             break;
950 
951         case awt::Key::RETURN:
952             if (mnPendingSlideNumber > 0)
953             {
954                 if (mxSlideShowController.is())
955                     mxSlideShowController->gotoSlideIndex(mnPendingSlideNumber - 1);
956                 mnPendingSlideNumber = -1;
957             }
958             else
959             {
960                 if (mxSlideShowController.is())
961                     mxSlideShowController->gotoNextEffect();
962             }
963 
964             break;
965 
966         case awt::Key::F1:
967             // Toggle the help view.
968             if (mpWindowManager)
969             {
970                 if (mpWindowManager->GetViewMode() != PresenterWindowManager::VM_Help)
971                     mpWindowManager->SetViewMode(PresenterWindowManager::VM_Help);
972                 else
973                     mpWindowManager->SetHelpViewState(false);
974             }
975 
976             break;
977 
978         default:
979             // Tell all views about the unhandled key event.
980             for (const auto& rxPane : mpPaneContainer->maPanes)
981             {
982                 if ( ! rxPane->mbIsActive)
983                     continue;
984 
985                 Reference<awt::XKeyListener> xKeyListener (rxPane->mxView, UNO_QUERY);
986                 if (xKeyListener.is())
987                     xKeyListener->keyReleased(rEvent);
988             }
989             break;
990     }
991 }
992 
HandleNumericKeyPress(const sal_Int32 nKey,const sal_Int32 nModifiers)993 void PresenterController::HandleNumericKeyPress (
994     const sal_Int32 nKey,
995     const sal_Int32 nModifiers)
996 {
997     switch (nModifiers)
998     {
999         case 0:
1000             if (mnPendingSlideNumber == -1)
1001                 mnPendingSlideNumber = 0;
1002             UpdatePendingSlideNumber(mnPendingSlideNumber * 10 + nKey);
1003             break;
1004 
1005         case awt::KeyModifier::MOD1:
1006             // Ctrl-1, Ctrl-2, and Ctrl-3 are used to switch between views
1007             // (slide view, notes view, normal). Ctrl-4 switches monitors
1008             mnPendingSlideNumber = -1;
1009             if (!mpWindowManager)
1010                 return;
1011             switch(nKey)
1012             {
1013                 case 1:
1014                     mpWindowManager->SetViewMode(PresenterWindowManager::VM_Standard);
1015                     break;
1016                 case 2:
1017                     mpWindowManager->SetViewMode(PresenterWindowManager::VM_Notes);
1018                     break;
1019                 case 3:
1020                     mpWindowManager->SetViewMode(PresenterWindowManager::VM_SlideOverview);
1021                     break;
1022                 case 4:
1023                     SwitchMonitors();
1024                     break;
1025                 default:
1026                     // Ignore unsupported key.
1027                     break;
1028             }
1029             break;
1030 
1031         default:
1032             // Ignore unsupported modifiers.
1033             break;
1034     }
1035 }
1036 
1037 //----- XMouseListener --------------------------------------------------------
1038 
mousePressed(const css::awt::MouseEvent &)1039 void SAL_CALL PresenterController::mousePressed (const css::awt::MouseEvent&)
1040 {
1041     if (mxMainWindow.is())
1042         mxMainWindow->setFocus();
1043 }
1044 
mouseReleased(const css::awt::MouseEvent &)1045 void SAL_CALL PresenterController::mouseReleased (const css::awt::MouseEvent&) {}
1046 
mouseEntered(const css::awt::MouseEvent &)1047 void SAL_CALL PresenterController::mouseEntered (const css::awt::MouseEvent&) {}
1048 
mouseExited(const css::awt::MouseEvent &)1049 void SAL_CALL PresenterController::mouseExited (const css::awt::MouseEvent&) {}
1050 
InitializeMainPane(const Reference<XPane> & rxPane)1051 void PresenterController::InitializeMainPane (const Reference<XPane>& rxPane)
1052 {
1053     if ( ! rxPane.is())
1054         return;
1055 
1056     mpAccessibleObject = new PresenterAccessible(
1057         mxComponentContext,
1058         this,
1059         rxPane);
1060 
1061     LoadTheme(rxPane);
1062 
1063     // Main pane has been created and is now observed by the window
1064     // manager.
1065     mpWindowManager->SetParentPane(rxPane);
1066     mpWindowManager->SetTheme(mpTheme);
1067 
1068     if (mpPaneBorderPainter)
1069         mpPaneBorderPainter->SetTheme(mpTheme);
1070 
1071     // Add key listener
1072     mxMainWindow = rxPane->getWindow();
1073     if (mxMainWindow.is())
1074     {
1075         mxMainWindow->addKeyListener(this);
1076         mxMainWindow->addMouseListener(this);
1077     }
1078     Reference<XPane2> xPane2 (rxPane, UNO_QUERY);
1079     if (xPane2.is())
1080         xPane2->setVisible(true);
1081 
1082     mpPaintManager = std::make_shared<PresenterPaintManager>(mxMainWindow, mxPresenterHelper, mpPaneContainer);
1083 
1084     mxCanvas.set(rxPane->getCanvas(), UNO_QUERY);
1085 
1086     if (mxSlideShowController.is())
1087         mxSlideShowController->activate();
1088 
1089     UpdateCurrentSlide(0);
1090 }
1091 
LoadTheme(const Reference<XPane> & rxPane)1092 void PresenterController::LoadTheme (const Reference<XPane>& rxPane)
1093 {
1094     // Create (load) the current theme.
1095     if (rxPane.is())
1096         mpTheme = std::make_shared<PresenterTheme>(mxComponentContext, rxPane->getCanvas());
1097 }
1098 
GetSlideAspectRatio() const1099 double PresenterController::GetSlideAspectRatio() const
1100 {
1101     double nSlideAspectRatio (28.0/21.0);
1102 
1103     try
1104     {
1105         if (mxController.is())
1106         {
1107             Reference<drawing::XDrawPagesSupplier> xSlideSupplier (
1108                 mxController->getModel(), UNO_QUERY_THROW);
1109             Reference<drawing::XDrawPages> xSlides (xSlideSupplier->getDrawPages());
1110             if (xSlides.is() && xSlides->getCount()>0)
1111             {
1112                 Reference<beans::XPropertySet> xProperties(xSlides->getByIndex(0),UNO_QUERY_THROW);
1113                 sal_Int32 nWidth (28000);
1114                 sal_Int32 nHeight (21000);
1115                 if ((xProperties->getPropertyValue("Width") >>= nWidth)
1116                     && (xProperties->getPropertyValue("Height") >>= nHeight)
1117                     && nHeight > 0)
1118                 {
1119                     nSlideAspectRatio = double(nWidth) / double(nHeight);
1120                 }
1121             }
1122         }
1123     }
1124     catch (RuntimeException&)
1125     {
1126         OSL_ASSERT(false);
1127     }
1128 
1129     return nSlideAspectRatio;
1130 }
1131 
UpdatePendingSlideNumber(const sal_Int32 nPendingSlideNumber)1132 void PresenterController::UpdatePendingSlideNumber (const sal_Int32 nPendingSlideNumber)
1133 {
1134     mnPendingSlideNumber = nPendingSlideNumber;
1135 
1136     if (mpTheme == nullptr)
1137         return;
1138 
1139     if ( ! mxMainWindow.is())
1140         return;
1141 
1142     PresenterTheme::SharedFontDescriptor pFont (
1143         mpTheme->GetFont("PendingSlideNumberFont"));
1144     if (!pFont)
1145         return;
1146 
1147     pFont->PrepareFont(mxCanvas);
1148     if ( ! pFont->mxFont.is())
1149         return;
1150 
1151     const OUString sText (OUString::number(mnPendingSlideNumber));
1152     rendering::StringContext aContext (sText, 0, sText.getLength());
1153     pFont->mxFont->createTextLayout(
1154             aContext,
1155             rendering::TextDirection::WEAK_LEFT_TO_RIGHT,
1156             0);
1157 }
1158 
SwitchMonitors()1159 void PresenterController::SwitchMonitors()
1160 {
1161     Reference<lang::XEventListener> xScreen( mxScreen );
1162     if (!xScreen.is())
1163         return;
1164 
1165     PresenterScreen *pScreen = dynamic_cast<PresenterScreen *>(xScreen.get());
1166     if (!pScreen)
1167         return;
1168 
1169     pScreen->SwitchMonitors();
1170 }
1171 
ExitPresenter()1172 void PresenterController::ExitPresenter()
1173 {
1174     if( mxController.is() )
1175         {
1176             Reference< XPresentationSupplier > xPS( mxController->getModel(), UNO_QUERY );
1177             if( xPS.is() )
1178             {
1179                 Reference< XPresentation > xP( xPS->getPresentation() );
1180                 if( xP.is() )
1181                     xP->end();
1182             }
1183         }
1184 }
1185 
1186 } // end of namespace ::sdext::presenter
1187 
1188 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1189