diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sd/source/ui/slideshow | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sd/source/ui/slideshow')
-rw-r--r-- | sd/source/ui/slideshow/PaneHider.cxx | 99 | ||||
-rw-r--r-- | sd/source/ui/slideshow/PaneHider.hxx | 66 | ||||
-rw-r--r-- | sd/source/ui/slideshow/SlideShowRestarter.cxx | 157 | ||||
-rw-r--r-- | sd/source/ui/slideshow/SlideShowRestarter.hxx | 87 | ||||
-rw-r--r-- | sd/source/ui/slideshow/showwin.cxx | 633 | ||||
-rw-r--r-- | sd/source/ui/slideshow/showwindow.hxx | 110 | ||||
-rw-r--r-- | sd/source/ui/slideshow/slideshow.cxx | 1189 | ||||
-rw-r--r-- | sd/source/ui/slideshow/slideshowimpl.cxx | 3619 | ||||
-rw-r--r-- | sd/source/ui/slideshow/slideshowimpl.hxx | 355 | ||||
-rw-r--r-- | sd/source/ui/slideshow/slideshowviewimpl.cxx | 626 | ||||
-rw-r--r-- | sd/source/ui/slideshow/slideshowviewimpl.hxx | 182 |
11 files changed, 7123 insertions, 0 deletions
diff --git a/sd/source/ui/slideshow/PaneHider.cxx b/sd/source/ui/slideshow/PaneHider.cxx new file mode 100644 index 0000000000..6207bf156a --- /dev/null +++ b/sd/source/ui/slideshow/PaneHider.cxx @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "PaneHider.hxx" + +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include "slideshowimpl.hxx" +#include <framework/FrameworkHelper.hxx> + +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XConfigurationController.hpp> +#include <com/sun/star/drawing/framework/XConfiguration.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/lang/DisposedException.hpp> + +#include <comphelper/diagnose_ex.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::framework::FrameworkHelper; +using ::com::sun::star::lang::DisposedException; + +namespace sd +{ +PaneHider::PaneHider(const ViewShell& rViewShell, SlideshowImpl* pSlideShow) +{ + // Hide the left and right pane windows when a slideshow exists and is + // not full screen. + if (pSlideShow == nullptr || pSlideShow->isFullScreen()) + return; + + try + { + Reference<XControllerManager> xControllerManager( + rViewShell.GetViewShellBase().GetController(), UNO_QUERY_THROW); + mxConfigurationController = xControllerManager->getConfigurationController(); + if (mxConfigurationController.is()) + { + // Get and save the current configuration. + mxConfiguration = mxConfigurationController->getRequestedConfiguration(); + if (mxConfiguration.is()) + { + // Iterate over the resources and deactivate the panes. + const Sequence<Reference<XResourceId>> aResources(mxConfiguration->getResources( + nullptr, framework::FrameworkHelper::msPaneURLPrefix, + AnchorBindingMode_DIRECT)); + for (const Reference<XResourceId>& xPaneId : aResources) + { + if (xPaneId->getResourceURL() != FrameworkHelper::msCenterPaneURL) + { + mxConfigurationController->requestResourceDeactivation(xPaneId); + } + } + } + } + FrameworkHelper::Instance(rViewShell.GetViewShellBase())->WaitForUpdate(); + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +PaneHider::~PaneHider() +{ + if (mxConfiguration.is() && mxConfigurationController.is()) + { + try + { + mxConfigurationController->restoreConfiguration(mxConfiguration); + } + catch (DisposedException&) + { + // When the configuration controller is already disposed then + // there is no point in restoring the configuration. + } + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/PaneHider.hxx b/sd/source/ui/slideshow/PaneHider.hxx new file mode 100644 index 0000000000..a2d3cabb01 --- /dev/null +++ b/sd/source/ui/slideshow/PaneHider.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.hxx> + +namespace com::sun::star::drawing::framework +{ +class XConfiguration; +} +namespace com::sun::star::drawing::framework +{ +class XConfigurationController; +} + +namespace sd +{ +class ViewShell; +class SlideshowImpl; + +/** Hide the windows of the side panes and restore the original visibility + later. Used by the in-window slide show in order to use the whole frame + window for the show. +*/ +class PaneHider +{ +public: + /** The constructor hides all side panes that belong to the + ViewShellBase of the given view shell. + */ + PaneHider(const ViewShell& rViewShell, SlideshowImpl* pSlideShow); + + /** Restore the original visibility of the side panes. + */ + ~PaneHider(); + +private: + /** Remember whether the visibility states of the windows of the panes + has been modified and have to be restored. + */ + + css::uno::Reference<css::drawing::framework::XConfigurationController> + mxConfigurationController; + css::uno::Reference<css::drawing::framework::XConfiguration> mxConfiguration; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/SlideShowRestarter.cxx b/sd/source/ui/slideshow/SlideShowRestarter.cxx new file mode 100644 index 0000000000..a9b53acf11 --- /dev/null +++ b/sd/source/ui/slideshow/SlideShowRestarter.cxx @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <DrawController.hxx> +#include <ViewShellBase.hxx> +#include <slideshow.hxx> +#include "SlideShowRestarter.hxx" + +#include <comphelper/propertyvalue.hxx> +#include <framework/ConfigurationController.hxx> +#include <framework/FrameworkHelper.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svxids.hrc> +#include <utility> +#include <vcl/svapp.hxx> + +#include <functional> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using ::sd::framework::FrameworkHelper; + +namespace sd { + +SlideShowRestarter::SlideShowRestarter ( + ::rtl::Reference<SlideShow> pSlideShow, + ViewShellBase* pViewShellBase) + : mnEventId(nullptr), + mpSlideShow(std::move(pSlideShow)), + mpViewShellBase(pViewShellBase), + mnDisplayCount(Application::GetScreenCount()), + mpDispatcher(pViewShellBase->GetViewFrame().GetDispatcher()), + mnCurrentSlideNumber(0) +{ +} + +SlideShowRestarter::~SlideShowRestarter() +{ +} + +void SlideShowRestarter::Restart (bool bForce) +{ + // Prevent multiple and concurrently restarts. + if (mnEventId != nullptr) + return; + + if (bForce) + mnDisplayCount = 0; + + // Remember the current slide in order to restore it after the slide + // show has been restarted. + if (mpSlideShow.is()) + mnCurrentSlideNumber = mpSlideShow->getCurrentPageNumber(); + + // Remember a shared pointer to this object to prevent its destruction + // before the whole restarting process has finished. + mpSelf = shared_from_this(); + + // We do not know in what situation this method was called. So, in + // order to be able to cleanly stop the presentation, we do that + // asynchronously. + mnEventId = Application::PostUserEvent( + LINK(this, SlideShowRestarter, EndPresentation)); +} + +IMPL_LINK_NOARG(SlideShowRestarter, EndPresentation, void*, void) +{ + mnEventId = nullptr; + if (!mpSlideShow.is()) + return; + + if (mnDisplayCount == static_cast<sal_Int32>(Application::GetScreenCount())) + return; + + bool bIsExitAfterPresenting = mpSlideShow->IsExitAfterPresenting(); + mpSlideShow->SetExitAfterPresenting(false); + mpSlideShow->end(); + mpSlideShow->SetExitAfterPresenting(bIsExitAfterPresenting); + + // The following piece of code should not be here because the + // slide show should be aware of the existence of the presenter + // console (which is displayed in the FullScreenPane). But the + // timing has to be right and I did not find a better place for + // it. + + // Wait for the full screen pane, which displays the presenter + // console, to disappear. Only when it is gone, call + // InitiatePresenterStart(), in order to begin the asynchronous + // restart of the slide show. + if (mpViewShellBase == nullptr) + return; + + ::std::shared_ptr<FrameworkHelper> pHelper( + FrameworkHelper::Instance(*mpViewShellBase)); + if (pHelper->GetConfigurationController()->getResource( + FrameworkHelper::CreateResourceId(FrameworkHelper::msFullScreenPaneURL)).is()) + { + ::sd::framework::ConfigurationController::Lock aLock ( + pHelper->GetConfigurationController()); + + pHelper->RunOnConfigurationEvent( + FrameworkHelper::msConfigurationUpdateEndEvent, + ::std::bind(&SlideShowRestarter::StartPresentation, shared_from_this())); + pHelper->UpdateConfiguration(); + } + else + { + StartPresentation(); + } +} + +void SlideShowRestarter::StartPresentation() +{ + //rhbz#1091117 crash because we're exiting the application, and this is + //being called during the configuration update event on exit. At this point + //newly created objects won't get disposed called on them, because the + //disposer is doing its last execution of that now. + if (mpViewShellBase && mpViewShellBase->GetDrawController()->IsDisposing()) + return; + + if (mpDispatcher == nullptr && mpViewShellBase!=nullptr) + mpDispatcher = mpViewShellBase->GetViewFrame().GetDispatcher(); + + // Start the slide show on the saved current slide. + if (mpDispatcher != nullptr) + { + mpDispatcher->Execute(SID_PRESENTATION, SfxCallMode::ASYNCHRON); + if (mpSlideShow.is()) + { + Sequence aProperties{ comphelper::makePropertyValue("FirstPage", + "page" + OUString::number(mnCurrentSlideNumber+1)) }; + mpSlideShow->startWithArguments(aProperties); + } + mpSelf.reset(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/SlideShowRestarter.hxx b/sd/source/ui/slideshow/SlideShowRestarter.hxx new file mode 100644 index 0000000000..f8cf95c33a --- /dev/null +++ b/sd/source/ui/slideshow/SlideShowRestarter.hxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <rtl/ref.hxx> +#include <tools/link.hxx> +#include <memory> + +namespace sd +{ +class SlideShow; +} +namespace sd +{ +class ViewShellBase; +} +class SfxDispatcher; +struct ImplSVEvent; + +namespace sd +{ +/** This class is used when a display is removed or added to restart the + slide show. This is necessary at least with DirectX because + deactivating a display invalidates DirectX resources. Accessing those + leads to a crash. + + During a restart a possibly installed presenter extension is given the + opportunity to show or hide depending on the number of available displays. +*/ +class SlideShowRestarter : public std::enable_shared_from_this<SlideShowRestarter> +{ +public: + /** Create a new SlideShowRestarter object. + @param rpSlideShow + The slide show is used to determine the current slide, which is + restored after the restart, and of course to stop and start the + slide show. + @param pViewShellBase + Used to get access to a slot dispatcher. + */ + SlideShowRestarter(::rtl::Reference<SlideShow> pSlideShow, ViewShellBase* pViewShellBase); + virtual ~SlideShowRestarter(); + + /** Restarting the slide show is an asynchronous multi step process + which is started by calling this method. + @param bForce + Used to force a re-start, even if the display count is unchanged. + */ + void Restart(bool bForce); + +private: + ImplSVEvent* mnEventId; + ::rtl::Reference<SlideShow> mpSlideShow; + ViewShellBase* mpViewShellBase; + ::std::shared_ptr<SlideShowRestarter> mpSelf; + sal_Int32 mnDisplayCount; + SfxDispatcher* mpDispatcher; + sal_Int32 mnCurrentSlideNumber; + + DECL_LINK(EndPresentation, void*, void); + + /** Restart the presentation on the slide last shown before the restart + was initiated. + */ + void StartPresentation(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/showwin.cxx b/sd/source/ui/slideshow/showwin.cxx new file mode 100644 index 0000000000..d6ecc19a69 --- /dev/null +++ b/sd/source/ui/slideshow/showwin.cxx @@ -0,0 +1,633 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/awt/Key.hpp> + +#include "showwindow.hxx" +#include "slideshowimpl.hxx" + +#include <unotools/localedatawrapper.hxx> +#include <unotools/syslocale.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sfxsids.hrc> + + +#include <slideshow.hxx> +#include <ViewShell.hxx> +#include <sdresid.hxx> +#include <helpids.h> +#include <strings.hrc> + +#include <sal/log.hxx> +#include <utility> +#include <vcl/settings.hxx> +#include <vcl/virdev.hxx> +#include <tools/duration.hxx> + +using namespace ::com::sun::star; + +namespace sd { + +const sal_uInt64 HIDE_MOUSE_TIMEOUT = 10000; +const sal_uInt64 SHOW_MOUSE_TIMEOUT = 1000; + +ShowWindow::ShowWindow( ::rtl::Reference< SlideshowImpl > xController, vcl::Window* pParent ) +: ::sd::Window( pParent ) +, maPauseTimer("sd ShowWindow maPauseTimer") +, maMouseTimer("sd ShowWindow maMouseTimer") +, mnPauseTimeout( SLIDE_NO_TIMEOUT ) +, mnRestartPageIndex( PAGE_NO_END ) +, meShowWindowMode(SHOWWINDOWMODE_NORMAL) +, mbShowNavigatorAfterSpecialMode( false ) +, mbMouseAutoHide(true) +, mbMouseCursorHidden(false) +, mnFirstMouseMove(0) +, mxController(std::move( xController )) +{ + GetOutDev()->SetOutDevViewType( OutDevViewType::SlideShow ); + + // Do never mirror the preview window. This explicitly includes right + // to left writing environments. + EnableRTL (false); + + MapMode aMap(GetMapMode()); + aMap.SetMapUnit(MapUnit::Map100thMM); + SetMapMode(aMap); + + // set HelpId + SetHelpId( HID_SD_WIN_PRESENTATION ); + + maPauseTimer.SetInvokeHandler( LINK( this, ShowWindow, PauseTimeoutHdl ) ); + maPauseTimer.SetTimeout( 1000 ); + maMouseTimer.SetInvokeHandler( LINK( this, ShowWindow, MouseTimeoutHdl ) ); + maMouseTimer.SetTimeout( HIDE_MOUSE_TIMEOUT ); + + maShowBackground = Wallpaper( COL_BLACK ); + SetBackground(); // avoids that VCL paints any background! + GetParent()->Show(); + AddEventListener( LINK( this, ShowWindow, EventHdl ) ); +} + +ShowWindow::~ShowWindow() +{ + disposeOnce(); +} + +void ShowWindow::dispose() +{ + maPauseTimer.Stop(); + maMouseTimer.Stop(); + ::sd::Window::dispose(); +} + +void ShowWindow::KeyInput(const KeyEvent& rKEvt) +{ + // Ignore workaround of https://gitlab.gnome.org/GNOME/gtk/issues/1785 + // See calls to GtkSalFrame::makeFakeKeyPress (Fixed in GTK 3.24) + bool bFakeKeyPress = rKEvt.GetKeyCode().GetFullCode() == 0; + if (bFakeKeyPress) + return; + + bool bReturn = false; + + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode ) + { + TerminateShow(); + bReturn = true; + } + else if( SHOWWINDOWMODE_END == meShowWindowMode ) + { + const int nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch( nKeyCode ) + { + case KEY_PAGEUP: + case KEY_LEFT: + case KEY_UP: + case KEY_P: + case KEY_HOME: + case KEY_END: + case awt::Key::CONTEXTMENU: + // these keys will be handled by the slide show even + // while in end mode + break; + default: + TerminateShow(); + bReturn = true; + } + } + else if( SHOWWINDOWMODE_BLANK == meShowWindowMode ) + { + RestartShow(); + bReturn = true; + } + else if( SHOWWINDOWMODE_PAUSE == meShowWindowMode ) + { + const int nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch( nKeyCode ) + { + case KEY_ESCAPE: + TerminateShow(); + bReturn = true; + break; + case KEY_PAGEUP: + case KEY_RIGHT: + case KEY_UP: + case KEY_P: + case KEY_HOME: + case KEY_END: + case awt::Key::CONTEXTMENU: + // these keys will be handled by the slide show even + // while in end mode + break; + default: + RestartShow(); + bReturn = true; + break; + } + } + + if( !bReturn ) + { + if( mxController.is() ) + bReturn = mxController->keyInput(rKEvt); + + if( !bReturn ) + { + if( mpViewShell ) + { + mpViewShell->KeyInput(rKEvt,this); + } + else + { + Window::KeyInput(rKEvt); + } + } + } + + if( mpViewShell ) + mpViewShell->SetActiveWindow( this ); +} + +void ShowWindow::MouseButtonDown(const MouseEvent& /*rMEvt*/) +{ + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode ) + { + TerminateShow(); + } + else if( mpViewShell ) + { + mpViewShell->SetActiveWindow( this ); + } +} + +void ShowWindow::MouseMove(const MouseEvent& /*rMEvt*/) +{ + if( mbMouseAutoHide ) + { + if( mbMouseCursorHidden ) + { + if( mnFirstMouseMove ) + { + // if this is not the first mouse move while hidden, see if + // enough time has pasted to show mouse pointer again + sal_uInt64 nTime = ::tools::Time::GetSystemTicks(); + if( (nTime - mnFirstMouseMove) >= SHOW_MOUSE_TIMEOUT ) + { + ShowPointer( true ); + mnFirstMouseMove = 0; + mbMouseCursorHidden = false; + maMouseTimer.SetTimeout( HIDE_MOUSE_TIMEOUT ); + maMouseTimer.Start(); + } + } + else + { + // if this is the first mouse move, note current + // time and start idle timer to cancel show mouse pointer + // again if not enough mouse movement is measured + mnFirstMouseMove = ::tools::Time::GetSystemTicks(); + maMouseTimer.SetTimeout( 2*SHOW_MOUSE_TIMEOUT ); + maMouseTimer.Start(); + } + } + else + { + // current mousemove restarts the idle timer to hide the mouse + maMouseTimer.Start(); + } + } + + if( mpViewShell ) + mpViewShell->SetActiveWindow( this ); +} + +void ShowWindow::MouseButtonUp(const MouseEvent& rMEvt) +{ + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode ) + { + TerminateShow(); + } + else if( (SHOWWINDOWMODE_END == meShowWindowMode) && !rMEvt.IsRight() ) + { + TerminateShow(); + } + else if( (( SHOWWINDOWMODE_BLANK == meShowWindowMode ) || ( SHOWWINDOWMODE_PAUSE == meShowWindowMode )) + && !rMEvt.IsRight() ) + { + RestartShow(); + } + else + { + if( mxController.is() ) + mxController->mouseButtonUp( rMEvt ); + } +} + +/** + * if FuSlideShow is still available, forward it + */ +void ShowWindow::Paint(vcl::RenderContext& /*rRenderContext*/, const ::tools::Rectangle& rRect) +{ + if( (meShowWindowMode == SHOWWINDOWMODE_NORMAL) || (meShowWindowMode == SHOWWINDOWMODE_PREVIEW) ) + { + if( mxController.is() ) + { + mxController->paint(); + } + else if(mpViewShell ) + { + mpViewShell->Paint(rRect, this); + } + } + else + { + GetOutDev()->DrawWallpaper( rRect, maShowBackground ); + + if( SHOWWINDOWMODE_END == meShowWindowMode ) + { + DrawEndScene(); + } + else if( SHOWWINDOWMODE_PAUSE == meShowWindowMode ) + { + DrawPauseScene( false ); + } + else if( SHOWWINDOWMODE_BLANK == meShowWindowMode ) + { + // just blank through background color => nothing to be done here + } + } +} + +void ShowWindow::LoseFocus() +{ + Window::LoseFocus(); + + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode) + TerminateShow(); +} + +void ShowWindow::SetEndMode() +{ + if( !(( SHOWWINDOWMODE_NORMAL == meShowWindowMode ) && mpViewShell && mpViewShell->GetView()) ) + return; + + DeleteWindowFromPaintView(); + meShowWindowMode = SHOWWINDOWMODE_END; + maShowBackground = Wallpaper( COL_BLACK ); + + // hide navigator if it is visible + if( mpViewShell->GetViewFrame()->GetChildWindow( SID_NAVIGATOR ) ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR, false ); + mbShowNavigatorAfterSpecialMode = true; + } + + Invalidate(); +} + +bool ShowWindow::SetPauseMode( sal_Int32 nTimeout, Graphic const * pLogo ) +{ + rtl::Reference< SlideShow > xSlideShow; + + if( mpViewShell ) + xSlideShow = SlideShow::GetSlideShow( mpViewShell->GetViewShellBase() ); + + if( xSlideShow.is() && !nTimeout ) + { + xSlideShow->jumpToPageIndex( 0 ); + } + else if( ( SHOWWINDOWMODE_NORMAL == meShowWindowMode ) && mpViewShell && mpViewShell->GetView() ) + { + DeleteWindowFromPaintView(); + mnPauseTimeout = nTimeout; + mnRestartPageIndex = 0; + meShowWindowMode = SHOWWINDOWMODE_PAUSE; + maShowBackground = Wallpaper( COL_BLACK ); + + // hide navigator if it is visible + if( mpViewShell->GetViewFrame()->GetChildWindow( SID_NAVIGATOR ) ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR, false ); + mbShowNavigatorAfterSpecialMode = true; + } + + if( pLogo ) + maLogo = *pLogo; + + Invalidate(); + + if( SLIDE_NO_TIMEOUT != mnPauseTimeout ) + maPauseTimer.Start(); + } + + return( SHOWWINDOWMODE_PAUSE == meShowWindowMode ); +} + +bool ShowWindow::SetBlankMode( sal_Int32 nPageIndexToRestart, const Color& rBlankColor ) +{ + if( ( SHOWWINDOWMODE_NORMAL == meShowWindowMode ) && mpViewShell && mpViewShell->GetView() ) + { + DeleteWindowFromPaintView(); + mnRestartPageIndex = nPageIndexToRestart; + meShowWindowMode = SHOWWINDOWMODE_BLANK; + maShowBackground = Wallpaper( rBlankColor ); + + // hide navigator if it is visible + if( mpViewShell->GetViewFrame()->GetChildWindow( SID_NAVIGATOR ) ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR, false ); + mbShowNavigatorAfterSpecialMode = true; + } + + Invalidate(); + } + + return( SHOWWINDOWMODE_BLANK == meShowWindowMode ); +} + +void ShowWindow::SetPreviewMode() +{ + meShowWindowMode = SHOWWINDOWMODE_PREVIEW; +} + +void ShowWindow::TerminateShow() +{ + maLogo.Clear(); + maPauseTimer.Stop(); + maMouseTimer.Stop(); + GetOutDev()->Erase(); + maShowBackground = Wallpaper( COL_BLACK ); + meShowWindowMode = SHOWWINDOWMODE_NORMAL; + mnPauseTimeout = SLIDE_NO_TIMEOUT; + + if( mpViewShell ) + { + // show navigator? + if( mbShowNavigatorAfterSpecialMode ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR ); + mbShowNavigatorAfterSpecialMode = false; + } + } + + if( mxController.is() ) + mxController->endPresentation(); + + mnRestartPageIndex = PAGE_NO_END; +} + +void ShowWindow::RestartShow() +{ + RestartShow( mnRestartPageIndex ); +} + +void ShowWindow::RestartShow( sal_Int32 nPageIndexToRestart ) +{ + ShowWindowMode eOldShowWindowMode = meShowWindowMode; + + maLogo.Clear(); + maPauseTimer.Stop(); + GetOutDev()->Erase(); + maShowBackground = Wallpaper( COL_BLACK ); + meShowWindowMode = SHOWWINDOWMODE_NORMAL; + mnPauseTimeout = SLIDE_NO_TIMEOUT; + + if( mpViewShell ) + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( mpViewShell->GetViewShellBase() ) ); + + if( xSlideShow.is() ) + { + AddWindowToPaintView(); + + if( SHOWWINDOWMODE_BLANK == eOldShowWindowMode || SHOWWINDOWMODE_END == eOldShowWindowMode ) + { + xSlideShow->pause(false); + Invalidate(); + } + else + { + xSlideShow->jumpToPageIndex( nPageIndexToRestart ); + } + } + } + + mnRestartPageIndex = PAGE_NO_END; + + // show navigator? + if( mbShowNavigatorAfterSpecialMode ) + { + if (mpViewShell) + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR ); + mbShowNavigatorAfterSpecialMode = false; + } +} + +void ShowWindow::DrawPauseScene( bool bTimeoutOnly ) +{ + const MapMode& rMap = GetMapMode(); + const Point aOutOrg( PixelToLogic( Point() ) ); + const Size aOutSize( GetOutDev()->GetOutputSize() ); + const Size aTextSize(OutputDevice::LogicToLogic(Size(0, 14), MapMode(MapUnit::MapPoint), rMap)); + const Size aOffset(OutputDevice::LogicToLogic(Size(1000, 1000), MapMode(MapUnit::Map100thMM), rMap)); + OUString aText( SdResId( STR_PRES_PAUSE ) ); + bool bDrawn = false; + + vcl::Font aFont( GetSettings().GetStyleSettings().GetMenuFont() ); + const vcl::Font aOldFont( GetFont() ); + + aFont.SetFontSize( aTextSize ); + aFont.SetColor( COL_WHITE ); + aFont.SetCharSet( aOldFont.GetCharSet() ); + aFont.SetLanguage( aOldFont.GetLanguage() ); + + if( !bTimeoutOnly && ( maLogo.GetType() != GraphicType::NONE ) ) + { + Size aGrfSize; + + if (maLogo.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel) + aGrfSize = PixelToLogic( maLogo.GetPrefSize() ); + else + aGrfSize = OutputDevice::LogicToLogic( maLogo.GetPrefSize(), maLogo.GetPrefMapMode(), rMap ); + + const Point aGrfPos( std::max( aOutOrg.X() + aOutSize.Width() - aGrfSize.Width() - aOffset.Width(), aOutOrg.X() ), + std::max( aOutOrg.Y() + aOutSize.Height() - aGrfSize.Height() - aOffset.Height(), aOutOrg.Y() ) ); + + if( maLogo.IsAnimated() ) + maLogo.StartAnimation(*GetOutDev(), aGrfPos, aGrfSize, reinterpret_cast<sal_IntPtr>(this)); + else + maLogo.Draw(*GetOutDev(), aGrfPos, aGrfSize); + } + + if( SLIDE_NO_TIMEOUT != mnPauseTimeout ) + { + MapMode aVMap( rMap ); + ScopedVclPtrInstance< VirtualDevice > pVDev( *GetOutDev() ); + + aVMap.SetOrigin( Point() ); + pVDev->SetMapMode( aVMap ); + pVDev->SetBackground( Wallpaper( COL_BLACK ) ); + + // set font first, to determine real output height + pVDev->SetFont( aFont ); + + const Size aVDevSize( aOutSize.Width(), pVDev->GetTextHeight() ); + + if( pVDev->SetOutputSize( aVDevSize ) ) + { + // Note: if performance gets an issue here, we can use NumberFormatter directly + SvtSysLocale aSysLocale; + const LocaleDataWrapper& aLocaleData = aSysLocale.GetLocaleData(); + + aText += " ( " + aLocaleData.getDuration( ::tools::Duration( 0, 0, 0, mnPauseTimeout, 0 )) + " )"; + pVDev->DrawText( Point( aOffset.Width(), 0 ), aText ); + GetOutDev()->DrawOutDev( Point( aOutOrg.X(), aOffset.Height() ), aVDevSize, Point(), aVDevSize, *pVDev ); + bDrawn = true; + } + } + + if( !bDrawn ) + { + SetFont( aFont ); + GetOutDev()->DrawText( Point( aOutOrg.X() + aOffset.Width(), aOutOrg.Y() + aOffset.Height() ), aText ); + SetFont( aOldFont ); + } +} + +void ShowWindow::DrawEndScene() +{ + const vcl::Font aOldFont( GetFont() ); + vcl::Font aFont( GetSettings().GetStyleSettings().GetMenuFont() ); + + const Point aOutOrg( PixelToLogic( Point() ) ); + const Size aTextSize(OutputDevice::LogicToLogic(Size(0, 14), MapMode(MapUnit::MapPoint), GetMapMode())); + const OUString aText( SdResId( STR_PRES_SOFTEND ) ); + + aFont.SetFontSize( aTextSize ); + aFont.SetColor( COL_WHITE ); + aFont.SetCharSet( aOldFont.GetCharSet() ); + aFont.SetLanguage( aOldFont.GetLanguage() ); + SetFont( aFont ); + GetOutDev()->DrawText( Point( aOutOrg.X() + aTextSize.Height(), aOutOrg.Y() + aTextSize.Height() ), aText ); + SetFont( aOldFont ); +} + +IMPL_LINK( ShowWindow, PauseTimeoutHdl, Timer*, pTimer, void ) +{ + if( !( --mnPauseTimeout ) ) + RestartShow(); + else + { + DrawPauseScene( true ); + pTimer->Start(); + } +} + +IMPL_LINK_NOARG(ShowWindow, MouseTimeoutHdl, Timer *, void) +{ + if( mbMouseCursorHidden ) + { + // not enough mouse movements since first recording so + // cancel show mouse pointer for now + mnFirstMouseMove = 0; + } + else + { + // mouse has been idle too long, hide pointer + ShowPointer( false ); + mbMouseCursorHidden = true; + } +} + +IMPL_LINK( ShowWindow, EventHdl, VclWindowEvent&, rEvent, void ) +{ + if( mbMouseAutoHide ) + { + if (rEvent.GetId() == VclEventId::WindowShow) + { + maMouseTimer.SetTimeout( HIDE_MOUSE_TIMEOUT ); + maMouseTimer.Start(); + } + } +} + +void ShowWindow::DeleteWindowFromPaintView() +{ + if( mpViewShell->GetView() ) + mpViewShell->GetView()->DeleteDeviceFromPaintView( *GetOutDev() ); + + sal_uInt16 nChild = GetChildCount(); + while( nChild-- ) + GetChild( nChild )->Show( false ); +} + +void ShowWindow::AddWindowToPaintView() +{ + if( mpViewShell->GetView() ) + mpViewShell->GetView()->AddDeviceToPaintView( *GetOutDev(), nullptr ); + + sal_uInt16 nChild = GetChildCount(); + while( nChild-- ) + GetChild( nChild )->Show(); +} + +// Override the sd::Window's CreateAccessible to create a different accessible object +css::uno::Reference<css::accessibility::XAccessible> + ShowWindow::CreateAccessible() +{ + css::uno::Reference< css::accessibility::XAccessible > xAcc = GetAccessible(false); + if (xAcc) + { + return xAcc; + } + if (mpViewShell != nullptr) + { + xAcc = mpViewShell->CreateAccessibleDocumentView (this); + SetAccessible(xAcc); + return xAcc; + } + else + { + SAL_WARN("sd", "::sd::Window::CreateAccessible: no view shell"); + return vcl::Window::CreateAccessible (); + } +} +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/showwindow.hxx b/sd/source/ui/slideshow/showwindow.hxx new file mode 100644 index 0000000000..c7a1d9bf02 --- /dev/null +++ b/sd/source/ui/slideshow/showwindow.hxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <rtl/ref.hxx> +#include <sal/types.h> +#include <vcl/timer.hxx> +#include <vcl/graph.hxx> + +#include <Window.hxx> + +namespace sd { + +class SlideshowImpl; + +#define SLIDE_NO_TIMEOUT SAL_MAX_INT32 + +enum ShowWindowMode +{ + SHOWWINDOWMODE_NORMAL = 0, + SHOWWINDOWMODE_PAUSE = 1, + SHOWWINDOWMODE_END = 2, + SHOWWINDOWMODE_BLANK = 3, + SHOWWINDOWMODE_PREVIEW = 4 +}; + +class ShowWindow + : public ::sd::Window +{ + +public: + ShowWindow ( ::rtl::Reference< ::sd::SlideshowImpl > xController, vcl::Window* pParent ); + virtual ~ShowWindow() override; + virtual void dispose() override; + + void SetEndMode(); + bool SetPauseMode( sal_Int32 nTimeoutSec, Graphic const * pLogo = nullptr ); + bool SetBlankMode( sal_Int32 nPageIndexToRestart, const Color& rBlankColor ); + + const Color& GetBlankColor() const { return maShowBackground.GetColor(); } + + void SetPreviewMode(); + + void SetMouseAutoHide( bool bMouseAutoHide ) { mbMouseAutoHide = bMouseAutoHide; } + + ShowWindowMode GetShowWindowMode() const { return meShowWindowMode; } + + void RestartShow( sal_Int32 nPageIndexToRestart ); + + virtual void LoseFocus() override; + + virtual void KeyInput(const KeyEvent& rKEvt) override; + virtual void MouseMove(const MouseEvent& rMEvt) override; + virtual void MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) override; + /// Override the sd::Window's CreateAccessible to create a different accessible object + virtual css::uno::Reference<css::accessibility::XAccessible> + CreateAccessible() override; + + void TerminateShow(); + void RestartShow(); + +private: + void DrawPauseScene( bool bTimeoutOnly ); + void DrawEndScene(); + + void DeleteWindowFromPaintView(); + void AddWindowToPaintView(); + +private: + Timer maPauseTimer; + Timer maMouseTimer; + Wallpaper maShowBackground; + Graphic maLogo; + sal_uLong mnPauseTimeout; + sal_Int32 mnRestartPageIndex; + ShowWindowMode meShowWindowMode; + bool mbShowNavigatorAfterSpecialMode; + bool mbMouseAutoHide; + bool mbMouseCursorHidden; + sal_uInt64 mnFirstMouseMove; + + DECL_LINK( PauseTimeoutHdl, Timer*, void ); + DECL_LINK(MouseTimeoutHdl, Timer *, void); + DECL_LINK( EventHdl, VclWindowEvent&, void ); + + ::rtl::Reference< SlideshowImpl > mxController; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshow.cxx b/sd/source/ui/slideshow/slideshow.cxx new file mode 100644 index 0000000000..1c21ef7ee0 --- /dev/null +++ b/sd/source/ui/slideshow/slideshow.cxx @@ -0,0 +1,1189 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/util/URL.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <sal/log.hxx> +#include <vcl/svapp.hxx> +#include <vcl/wrkwin.hxx> +#include <svx/svdpool.hxx> +#include <svx/svdlayer.hxx> +#include <svl/itemprop.hxx> + +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sfxsids.hrc> + +#include <framework/FrameworkHelper.hxx> +#include <comphelper/extract.hxx> + +#include <FrameView.hxx> +#include <createpresentation.hxx> +#include <unomodel.hxx> +#include <slideshow.hxx> +#include "slideshowimpl.hxx" +#include <sdattr.hrc> +#include <sdmod.hxx> +#include <FactoryIds.hxx> +#include <DrawDocShell.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include "SlideShowRestarter.hxx" +#include <DrawController.hxx> +#include <PresentationViewShell.hxx> +#include <customshowlist.hxx> +#include <unopage.hxx> +#include <sdpage.hxx> +#include <cusshow.hxx> +#include <optsitem.hxx> +#include <strings.hrc> +#include <sdresid.hxx> + +using ::com::sun::star::presentation::XSlideShowController; +using ::sd::framework::FrameworkHelper; +using ::com::sun::star::awt::XWindow; +using namespace ::sd; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::drawing::framework; + +namespace { + /** This local version of the work window overrides DataChanged() so that it + can restart the slide show when a display is added or removed. + */ + class FullScreenWorkWindow : public WorkWindow + { + public: + FullScreenWorkWindow ( + const ::rtl::Reference<SlideShow>& rpSlideShow, + ViewShellBase* pViewShellBase) + : WorkWindow(nullptr, WB_HIDE | WB_CLIPCHILDREN), + mpRestarter(std::make_shared<SlideShowRestarter>(rpSlideShow, pViewShellBase)) + {} + + void Restart(bool bForce) + { + mpRestarter->Restart(bForce); + } + + virtual void DataChanged (const DataChangedEvent& rEvent) override + { + if (rEvent.GetType() == DataChangedEventType::DISPLAY) + Restart(false); + } + + private: + ::std::shared_ptr<SlideShowRestarter> mpRestarter; + }; +} + +static std::span<const SfxItemPropertyMapEntry> ImplGetPresentationPropertyMap() +{ + // NOTE: First member must be sorted + static const SfxItemPropertyMapEntry aPresentationPropertyMap_Impl[] = + { + { u"AllowAnimations"_ustr, ATTR_PRESENT_ANIMATION_ALLOWED, cppu::UnoType<bool>::get(), 0, 0 }, + { u"CustomShow"_ustr, ATTR_PRESENT_CUSTOMSHOW, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { u"Display"_ustr, ATTR_PRESENT_DISPLAY, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { u"FirstPage"_ustr, ATTR_PRESENT_DIANAME, ::cppu::UnoType<OUString>::get(), 0, 0 }, + { u"IsAlwaysOnTop"_ustr, ATTR_PRESENT_ALWAYS_ON_TOP, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsAutomatic"_ustr, ATTR_PRESENT_MANUEL, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsEndless"_ustr, ATTR_PRESENT_ENDLESS, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsFullScreen"_ustr, ATTR_PRESENT_FULLSCREEN, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsShowAll"_ustr, ATTR_PRESENT_ALL, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsMouseVisible"_ustr, ATTR_PRESENT_MOUSE, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsShowLogo"_ustr, ATTR_PRESENT_SHOW_PAUSELOGO, cppu::UnoType<bool>::get(), 0, 0 }, + { u"IsTransitionOnClick"_ustr, ATTR_PRESENT_CHANGE_PAGE, cppu::UnoType<bool>::get(), 0, 0 }, + { u"Pause"_ustr, ATTR_PRESENT_PAUSE_TIMEOUT, ::cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { u"StartWithNavigator"_ustr, ATTR_PRESENT_NAVIGATOR, cppu::UnoType<bool>::get(), 0, 0 }, + { u"UsePen"_ustr, ATTR_PRESENT_PEN, cppu::UnoType<bool>::get(), 0, 0 }, + }; + + return aPresentationPropertyMap_Impl; +} + + +SlideShow::SlideShow( SdDrawDocument* pDoc ) +: maPropSet(ImplGetPresentationPropertyMap(), SdrObject::GetGlobalDrawObjectItemPool()) +, mbIsInStartup(false) +, mpDoc( pDoc ) +, mpCurrentViewShellBase( nullptr ) +, mpFullScreenViewShellBase( nullptr ) +, mpFullScreenFrameView( nullptr ) +, mnInPlaceConfigEvent( nullptr ) +{ +} + +void SlideShow::ThrowIfDisposed() const +{ + if( mpDoc == nullptr ) + throw DisposedException(); +} + +/// used by the model to create a slideshow for it +rtl::Reference< SlideShow > SlideShow::Create( SdDrawDocument* pDoc ) +{ + return new SlideShow( pDoc ); +} + +rtl::Reference< SlideShow > SlideShow::GetSlideShow( SdDrawDocument const * pDocument ) +{ + rtl::Reference< SlideShow > xRet; + + if( pDocument ) + xRet = GetSlideShow( *pDocument ); + + return xRet; +} + +rtl::Reference< SlideShow > SlideShow::GetSlideShow( SdDrawDocument const & rDocument ) +{ + return rtl::Reference< SlideShow >( + dynamic_cast< SlideShow* >( rDocument.getPresentation().get() ) ); +} + +rtl::Reference< SlideShow > SlideShow::GetSlideShow( ViewShellBase const & rBase ) +{ + return GetSlideShow( rBase.GetDocument() ); +} + +css::uno::Reference< css::presentation::XSlideShowController > SlideShow::GetSlideShowController(ViewShellBase const & rBase ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + + Reference< XSlideShowController > xRet; + if( xSlideShow.is() ) + xRet = xSlideShow->getController(); + + return xRet; +} + +bool SlideShow::StartPreview( ViewShellBase const & rBase, + const css::uno::Reference< css::drawing::XDrawPage >& xDrawPage, + const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + if( !xSlideShow.is() ) + return false; + + xSlideShow->startPreview( xDrawPage, xAnimationNode ); + return true; +} + +void SlideShow::Stop( ViewShellBase const & rBase ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + if( xSlideShow.is() ) + xSlideShow->end(); +} + +bool SlideShow::IsRunning( ViewShellBase const & rBase ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + return xSlideShow.is() && xSlideShow->isRunning(); +} + +bool SlideShow::IsRunning( const ViewShell& rViewShell ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rViewShell.GetViewShellBase() ) ); + return xSlideShow.is() && xSlideShow->isRunning() && (xSlideShow->mxController->getViewShell() == &rViewShell); +} + +void SlideShow::CreateController( ViewShell* pViewSh, ::sd::View* pView, vcl::Window* pParentWindow ) +{ + SAL_INFO_IF( !mxController.is(), "sd.slideshow", "sd::SlideShow::CreateController(), clean up old controller first!" ); + + Reference< XPresentation2 > xThis( this ); + + rtl::Reference<SlideshowImpl> xController ( + new SlideshowImpl(xThis, pViewSh, pView, mpDoc, pParentWindow)); + + // Reset mbIsInStartup. From here mxController.is() is used to prevent + // multiple slide show instances for one document. + mxController = xController; + mbIsInStartup = false; + +} + +// XServiceInfo +OUString SAL_CALL SlideShow::getImplementationName( ) +{ + return "com.sun.star.comp.sd.SlideShow"; +} + +sal_Bool SAL_CALL SlideShow::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SlideShow::getSupportedServiceNames( ) +{ + return { "com.sun.star.presentation.Presentation" }; +} + +// XPropertySet +Reference< XPropertySetInfo > SAL_CALL SlideShow::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static Reference< XPropertySetInfo > xInfo = maPropSet.getPropertySetInfo(); + return xInfo; + } + +void SAL_CALL SlideShow::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + + sd::PresentationSettings& rPresSettings = mpDoc->getPresentationSettings(); + + const SfxItemPropertyMapEntry* pEntry = maPropSet.getPropertyMapEntry(aPropertyName); + + if( pEntry && ((pEntry->nFlags & PropertyAttribute::READONLY) != 0) ) + throw PropertyVetoException(); + + bool bValuesChanged = false; + bool bIllegalArgument = true; + + switch( pEntry ? pEntry->nWID : -1 ) + { + case ATTR_PRESENT_ALL: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbAll != bVal ) + { + rPresSettings.mbAll = bVal; + bValuesChanged = true; + if( bVal ) + rPresSettings.mbCustomShow = false; + } + } + break; + } + case ATTR_PRESENT_CHANGE_PAGE: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( bVal == rPresSettings.mbLockedPages ) + { + bValuesChanged = true; + rPresSettings.mbLockedPages = !bVal; + } + } + break; + } + + case ATTR_PRESENT_ANIMATION_ALLOWED: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if(rPresSettings.mbAnimationAllowed != bVal) + { + bValuesChanged = true; + rPresSettings.mbAnimationAllowed = bVal; + } + } + break; + } + case ATTR_PRESENT_CUSTOMSHOW: + { + OUString aShowName; + if( aValue >>= aShowName ) + { + bIllegalArgument = false; + + SdCustomShowList* pCustomShowList = mpDoc->GetCustomShowList(); + if(pCustomShowList) + { + SdCustomShow* pCustomShow; + for( pCustomShow = pCustomShowList->First(); pCustomShow != nullptr; pCustomShow = pCustomShowList->Next() ) + { + if( pCustomShow->GetName() == aShowName ) + break; + } + + rPresSettings.mbCustomShow = true; + bValuesChanged = true; + } + } + break; + } + case ATTR_PRESENT_ENDLESS: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbEndless != bVal) + { + bValuesChanged = true; + rPresSettings.mbEndless = bVal; + } + } + break; + } + case ATTR_PRESENT_FULLSCREEN: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + if( rPresSettings.mbFullScreen != bVal) + { + bValuesChanged = true; + rPresSettings.mbFullScreen = bVal; + } + } + break; + } + case ATTR_PRESENT_DIANAME: + { + OUString aPresPage; + aValue >>= aPresPage; + bIllegalArgument = false; + if( (rPresSettings.maPresPage != aPresPage) || !rPresSettings.mbCustomShow || !rPresSettings.mbAll ) + { + bValuesChanged = true; + rPresSettings.maPresPage = getUiNameFromPageApiNameImpl(aPresPage); + rPresSettings.mbCustomShow = false; + rPresSettings.mbAll = false; + } + break; + } + case ATTR_PRESENT_MANUEL: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbManual != bVal) + { + bValuesChanged = true; + rPresSettings.mbManual = bVal; + } + } + break; + } + case ATTR_PRESENT_MOUSE: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + if( rPresSettings.mbMouseVisible != bVal) + { + bValuesChanged = true; + rPresSettings.mbMouseVisible = bVal; + } + } + break; + } + case ATTR_PRESENT_ALWAYS_ON_TOP: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbAlwaysOnTop != bVal) + { + bValuesChanged = true; + rPresSettings.mbAlwaysOnTop = bVal; + } + } + break; + } + case ATTR_PRESENT_NAVIGATOR: + bIllegalArgument = false; + //ignored, but exists in some older documents + break; + case ATTR_PRESENT_PEN: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if(rPresSettings.mbMouseAsPen != bVal) + { + bValuesChanged = true; + rPresSettings.mbMouseAsPen = bVal; + } + } + break; + } + case ATTR_PRESENT_PAUSE_TIMEOUT: + { + sal_Int32 nValue = 0; + if( (aValue >>= nValue) && (nValue >= 0) ) + { + bIllegalArgument = false; + if( rPresSettings.mnPauseTimeout != nValue ) + { + bValuesChanged = true; + rPresSettings.mnPauseTimeout = nValue; + } + } + break; + } + case ATTR_PRESENT_SHOW_PAUSELOGO: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbShowPauseLogo != bVal ) + { + bValuesChanged = true; + rPresSettings.mbShowPauseLogo = bVal; + } + } + break; + } + case ATTR_PRESENT_DISPLAY: + { + sal_Int32 nDisplay = 0; + if( aValue >>= nDisplay ) + { + bIllegalArgument = false; + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + pOptions->SetDisplay( nDisplay ); + + FullScreenWorkWindow *pWin = dynamic_cast<FullScreenWorkWindow *>(GetWorkWindow()); + if( !pWin ) + return; + pWin->Restart(true); + } + break; + } + + default: + throw UnknownPropertyException( OUString::number(pEntry ? pEntry->nWID : -1), static_cast<cppu::OWeakObject*>(this)); + } + + if( bIllegalArgument ) + throw IllegalArgumentException(); + + if( bValuesChanged ) + mpDoc->SetChanged(); +} + +Any SAL_CALL SlideShow::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + + const sd::PresentationSettings& rPresSettings = mpDoc->getPresentationSettings(); + + const SfxItemPropertyMapEntry* pEntry = maPropSet.getPropertyMapEntry(PropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case ATTR_PRESENT_ALL: + return Any( !rPresSettings.mbCustomShow && rPresSettings.mbAll ); + case ATTR_PRESENT_CHANGE_PAGE: + return Any( !rPresSettings.mbLockedPages ); + case ATTR_PRESENT_ANIMATION_ALLOWED: + return Any( rPresSettings.mbAnimationAllowed ); + case ATTR_PRESENT_CUSTOMSHOW: + { + SdCustomShowList* pList = mpDoc->GetCustomShowList(); + SdCustomShow* pShow = (pList && rPresSettings.mbCustomShow) ? pList->GetCurObject() : nullptr; + OUString aShowName; + + if(pShow) + aShowName = pShow->GetName(); + + return Any( aShowName ); + } + case ATTR_PRESENT_ENDLESS: + return Any( rPresSettings.mbEndless ); + case ATTR_PRESENT_FULLSCREEN: + return Any( rPresSettings.mbFullScreen ); + case ATTR_PRESENT_DIANAME: + { + OUString aSlideName; + + if( !rPresSettings.mbCustomShow && !rPresSettings.mbAll ) + aSlideName = getPageApiNameFromUiName( rPresSettings.maPresPage ); + + return Any( aSlideName ); + } + case ATTR_PRESENT_MANUEL: + return Any( rPresSettings.mbManual ); + case ATTR_PRESENT_MOUSE: + return Any( rPresSettings.mbMouseVisible ); + case ATTR_PRESENT_ALWAYS_ON_TOP: + return Any( rPresSettings.mbAlwaysOnTop ); + case ATTR_PRESENT_NAVIGATOR: + return Any( false ); + case ATTR_PRESENT_PEN: + return Any( rPresSettings.mbMouseAsPen ); + case ATTR_PRESENT_PAUSE_TIMEOUT: + return Any( rPresSettings.mnPauseTimeout ); + case ATTR_PRESENT_SHOW_PAUSELOGO: + return Any( rPresSettings.mbShowPauseLogo ); + case ATTR_PRESENT_DISPLAY: + { + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + return Any(pOptions->GetDisplay()); + } + + default: + throw UnknownPropertyException( OUString::number(pEntry ? pEntry->nWID : -1), static_cast<cppu::OWeakObject*>(this)); + } +} + +void SAL_CALL SlideShow::addPropertyChangeListener( const OUString& , const Reference< XPropertyChangeListener >& ) +{ +} + +void SAL_CALL SlideShow::removePropertyChangeListener( const OUString& , const Reference< XPropertyChangeListener >& ) +{ +} + +void SAL_CALL SlideShow::addVetoableChangeListener( const OUString& , const Reference< XVetoableChangeListener >& ) +{ +} + +void SAL_CALL SlideShow::removeVetoableChangeListener( const OUString& , const Reference< XVetoableChangeListener >& ) +{ +} + +// XPresentation + +void SAL_CALL SlideShow::start() +{ + const Sequence< PropertyValue > aArguments; + startWithArguments( aArguments ); +} + +WorkWindow *SlideShow::GetWorkWindow() +{ + if( !mpFullScreenViewShellBase ) + return nullptr; + + PresentationViewShell* pShell = dynamic_cast<PresentationViewShell*>(mpFullScreenViewShellBase->GetMainViewShell().get()); + + if( !pShell || !pShell->GetViewFrame() ) + return nullptr; + + return dynamic_cast<WorkWindow*>(pShell->GetViewFrame()->GetFrame().GetWindow().GetParent()); +} + +bool SlideShow::IsExitAfterPresenting() const +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + return mpDoc->IsExitAfterPresenting(); +} + +void SlideShow::SetExitAfterPresenting(bool bExit) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + mpDoc->SetExitAfterPresenting(bExit); +} + +void SAL_CALL SlideShow::end() +{ + SolarMutexGuard aGuard; + + // The mbIsInStartup flag should have been reset during the start of the + // slide show. Reset it here just in case that something has horribly + // gone wrong. + assert(!mbIsInStartup); + + rtl::Reference< SlideshowImpl > xController( mxController ); + if( !xController.is() ) + return; + + mxController.clear(); + + if( mpFullScreenFrameView ) + { + delete mpFullScreenFrameView; + mpFullScreenFrameView = nullptr; + } + + ViewShellBase* pFullScreenViewShellBase = mpFullScreenViewShellBase; + mpFullScreenViewShellBase = nullptr; + + // dispose before fullscreen window changes screens + // (potentially). If this needs to be moved behind + // pWorkWindow->StartPresentationMode() again, read issue + // pWorkWindow->i94007 & implement the solution outlined + // there. + xController->dispose(); + + if( pFullScreenViewShellBase ) + { + PresentationViewShell* pShell = dynamic_cast<PresentationViewShell*>(pFullScreenViewShellBase->GetMainViewShell().get()); + + if( pShell && pShell->GetViewFrame() ) + { + WorkWindow* pWorkWindow = dynamic_cast<WorkWindow*>(pShell->GetViewFrame()->GetFrame().GetWindow().GetParent()); + if( pWorkWindow ) + { + pWorkWindow->StartPresentationMode( (mxController.is() && mxController->maPresSettings.mbAlwaysOnTop) + ? PresentationFlags::HideAllApps : PresentationFlags::NONE ); + } + } + } + + if( pFullScreenViewShellBase ) + { + PresentationViewShell* pShell = nullptr; + { + // Get the shell pointer in its own scope to be sure that + // the shared_ptr to the shell is released before DoClose() + // is called. + ::std::shared_ptr<ViewShell> pSharedView (pFullScreenViewShellBase->GetMainViewShell()); + pShell = dynamic_cast<PresentationViewShell*>(pSharedView.get()); + } + if( pShell && pShell->GetViewFrame() ) + pShell->GetViewFrame()->DoClose(); + } + else if( mpCurrentViewShellBase ) + { + ViewShell* pViewShell = mpCurrentViewShellBase->GetMainViewShell().get(); + + if( pViewShell ) + { + FrameView* pFrameView = pViewShell->GetFrameView(); + + if( pFrameView && (pFrameView->GetPresentationViewShellId() != SID_VIEWSHELL0) ) + { + ViewShell::ShellType ePreviousType (pFrameView->GetPreviousViewShellType()); + pFrameView->SetPreviousViewShellType(ViewShell::ST_NONE); + + pFrameView->SetPresentationViewShellId(SID_VIEWSHELL0); + pFrameView->SetPreviousViewShellType(pViewShell->GetShellType()); + + framework::FrameworkHelper::Instance(*mpCurrentViewShellBase)->RequestView( + framework::FrameworkHelper::GetViewURL(ePreviousType), + framework::FrameworkHelper::msCenterPaneURL); + + pViewShell->GetViewFrame()->GetBindings().InvalidateAll( true ); + } + } + } + + if( mpCurrentViewShellBase ) + { + if (ViewShell* const pViewShell = mpCurrentViewShellBase->GetMainViewShell().get()) + { + // invalidate the view shell so the presentation slot will be re-enabled + // and the rehearsing will be updated + pViewShell->Invalidate(); + + if( xController->meAnimationMode ==ANIMATIONMODE_SHOW ) + { + // switch to the previously visible Slide + DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>( pViewShell ); + if( pDrawViewShell ) + pDrawViewShell->SwitchPage( static_cast<sal_uInt16>(xController->getRestoreSlide()) ); + else + { + DrawController& rDrawController = + *mpCurrentViewShellBase->GetDrawController(); + rDrawController.setCurrentPage( + Reference<XDrawPage>( + mpDoc->GetSdPage(xController->getRestoreSlide(), PageKind::Standard)->getUnoPage(), + UNO_QUERY)); + } + } + + if( pViewShell->GetDoc()->IsExitAfterPresenting() ) + { + pViewShell->GetDoc()->SetExitAfterPresenting( false ); + + Reference<frame::XDispatchProvider> xProvider(pViewShell->GetViewShellBase().GetController()->getFrame(), + UNO_QUERY); + if( xProvider.is() ) + { + util::URL aURL; + aURL.Complete = ".uno:CloseFrame"; + + uno::Reference< frame::XDispatch > xDispatch( + xProvider->queryDispatch( + aURL, OUString(), 0)); + if( xDispatch.is() ) + { + xDispatch->dispatch(aURL, + uno::Sequence< beans::PropertyValue >()); + } + } + } + + // In case mbMouseAsPen was set, a new layer DrawnInSlideshow might have been generated + // during slideshow, which is not known to FrameView yet. + if (any2bool(getPropertyValue("UsePen")) + && pViewShell->GetDoc()->GetLayerAdmin().GetLayer("DrawnInSlideshow")) + { + SdrLayerIDSet aDocLayerIDSet; + pViewShell->GetDoc()->GetLayerAdmin().getVisibleLayersODF(aDocLayerIDSet); + if (pViewShell->GetFrameView()->GetVisibleLayers() != aDocLayerIDSet) + { + pViewShell->GetFrameView()->SetVisibleLayers(aDocLayerIDSet); + } + pViewShell->GetDoc()->GetLayerAdmin().getPrintableLayersODF(aDocLayerIDSet); + if (pViewShell->GetFrameView()->GetPrintableLayers() != aDocLayerIDSet) + { + pViewShell->GetFrameView()->SetPrintableLayers(aDocLayerIDSet); + } + pViewShell->GetDoc()->GetLayerAdmin().getLockedLayersODF(aDocLayerIDSet); + if (pViewShell->GetFrameView()->GetLockedLayers() != aDocLayerIDSet) + { + pViewShell->GetFrameView()->SetLockedLayers(aDocLayerIDSet); + } + pViewShell->InvalidateWindows(); + } + + // Fire the acc focus event when focus is switched back. The above method + // mpCurrentViewShellBase->GetWindow()->GrabFocus() will set focus to WorkWindow + // instead of the sd::window, so here call Shell's method to fire the focus event + pViewShell->SwitchActiveViewFireFocus(); + } + } + mpCurrentViewShellBase = nullptr; +} + +void SAL_CALL SlideShow::rehearseTimings() +{ + Sequence< PropertyValue > aArguments{ comphelper::makePropertyValue("RehearseTimings", true) }; + startWithArguments( aArguments ); +} + +// XPresentation2 + +void SAL_CALL SlideShow::startWithArguments(const Sequence< PropertyValue >& rArguments) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + + // Stop a running show before starting a new one. + if( mxController.is() ) + { + assert(!mbIsInStartup); + end(); + } + else if (mbIsInStartup) + { + // We are already somewhere in process of starting a slide show but + // have not yet got to the point where mxController is set. There + // is not yet a slide show to end so return silently. + return; + } + + // Prevent multiple instance of the SlideShow class for one document. + mbIsInStartup = true; + + mxCurrentSettings = std::make_shared<PresentationSettingsEx>( mpDoc->getPresentationSettings() ); + mxCurrentSettings->SetArguments( rArguments ); + + // if there is no view shell base set, use the current one or the first using this document + if( mpCurrentViewShellBase == nullptr ) + { + // first check current + ::sd::ViewShellBase* pBase = ::sd::ViewShellBase::GetViewShellBase( SfxViewFrame::Current() ); + if( pBase && pBase->GetDocument() == mpDoc ) + { + mpCurrentViewShellBase = pBase; + } + else + { + // current is not ours, so get first from ours + mpCurrentViewShellBase = ::sd::ViewShellBase::GetViewShellBase( SfxViewFrame::GetFirst( mpDoc->GetDocSh() ) ); + } + } + + // #i118456# make sure TextEdit changes get pushed to model. + // mpDrawView is tested against NULL above already. + if(mpCurrentViewShellBase) + { + ViewShell* pViewShell = mpCurrentViewShellBase->GetMainViewShell().get(); + + if(pViewShell && pViewShell->GetView()) + { + pViewShell->GetView()->SdrEndTextEdit(); + } + } + + // Start either a full-screen or an in-place show. + if(mxCurrentSettings->mbFullScreen && !mxCurrentSettings->mbPreview) + StartFullscreenPresentation(); + else + StartInPlacePresentation(); + +} + +sal_Bool SAL_CALL SlideShow::isRunning( ) +{ + SolarMutexGuard aGuard; + return mxController.is() && mxController->isRunning(); +} + +Reference< XSlideShowController > SAL_CALL SlideShow::getController( ) +{ + ThrowIfDisposed(); + + return mxController; +} + +// XComponent + +void SlideShow::disposing(std::unique_lock<std::mutex>&) +{ + SolarMutexGuard aGuard; + + if( mnInPlaceConfigEvent ) + { + Application::RemoveUserEvent( mnInPlaceConfigEvent ); + mnInPlaceConfigEvent = nullptr; + } + + if( mxController.is() ) + { + mxController->dispose(); + mxController.clear(); + } + + mpCurrentViewShellBase = nullptr; + mpFullScreenViewShellBase = nullptr; + mpDoc = nullptr; +} + +void SlideShow::startPreview( const Reference< XDrawPage >& xDrawPage, const Reference< XAnimationNode >& xAnimationNode ) +{ + Sequence< PropertyValue > aArguments{ + comphelper::makePropertyValue("Preview", true), + comphelper::makePropertyValue("FirstPage", xDrawPage), + comphelper::makePropertyValue("AnimationNode", xAnimationNode), + comphelper::makePropertyValue("ParentWindow", Reference< XWindow >()), + }; + + startWithArguments( aArguments ); +} + +OutputDevice* SlideShow::getShowWindow() +{ + return mxController.is() ? mxController->mpShowWindow->GetOutDev() : nullptr; +} + +int SlideShow::getAnimationMode() const +{ + return mxController.is() ? mxController->meAnimationMode : ANIMATIONMODE_SHOW; +} + +void SlideShow::jumpToPageIndex( sal_Int32 nPageIndex ) +{ + if( mxController.is() ) + mxController->displaySlideIndex( nPageIndex ); +} + +void SlideShow::jumpToPageNumber( sal_Int32 nPageNumber ) +{ + if( mxController.is() ) + mxController->displaySlideNumber( nPageNumber ); +} + +sal_Int32 SlideShow::getCurrentPageNumber() const +{ + return mxController.is() ? mxController->getCurrentSlideNumber() : 0; +} + +void SlideShow::jumpToBookmark( const OUString& sBookmark ) +{ + if( mxController.is() ) + mxController->jumpToBookmark( sBookmark ); +} + +bool SlideShow::isFullScreen() const +{ + return mxController.is() && mxController->maPresSettings.mbFullScreen; +} + +void SlideShow::resize( const Size &rSize ) +{ + if( mxController.is() ) + mxController->resize( rSize ); +} + +bool SlideShow::activate( ViewShellBase& rBase ) +{ + if( (mpFullScreenViewShellBase == &rBase) && !mxController.is() ) + { + ::std::shared_ptr<PresentationViewShell> pShell = std::dynamic_pointer_cast<PresentationViewShell>(rBase.GetMainViewShell()); + if (pShell != nullptr) + { + pShell->FinishInitialization( mpFullScreenFrameView ); + mpFullScreenFrameView = nullptr; + + CreateController( pShell.get(), pShell->GetView(), rBase.GetViewWindow() ); + + if (!mxController->startShow(mxCurrentSettings.get())) + return false; + + pShell->Resize(); + // Defer the sd::ShowWindow's GrabFocus to here. so that the accessible event can be fired correctly. + pShell->GetActiveWindow()->GrabFocus(); + } + } + + if( mxController.is() ) + mxController->activate(); + + return true; +} + +void SlideShow::deactivate() +{ + mxController->deactivate(); +} + +bool SlideShow::keyInput(const KeyEvent& rKEvt) +{ + return mxController.is() && mxController->keyInput(rKEvt); +} + +void SlideShow::paint() +{ + if( mxController.is() ) + mxController->paint(); +} + +void SlideShow::pause( bool bPause ) +{ + if( mxController.is() ) + { + if( bPause ) + mxController->pause(); + else + mxController->resume(); + } +} + +bool SlideShow::swipe(const CommandGestureSwipeData& rSwipeData) +{ + return mxController.is() && mxController->swipe(rSwipeData); +} + +bool SlideShow::longpress(const CommandGestureLongPressData& rLongPressData) +{ + return mxController.is() && mxController->longpress(rLongPressData); +} + +void SlideShow::StartInPlacePresentationConfigurationCallback() +{ + if( mnInPlaceConfigEvent != nullptr ) + Application::RemoveUserEvent( mnInPlaceConfigEvent ); + + mnInPlaceConfigEvent = Application::PostUserEvent( LINK( this, SlideShow, StartInPlacePresentationConfigurationHdl ) ); +} + +IMPL_LINK_NOARG(SlideShow, StartInPlacePresentationConfigurationHdl, void*, void) +{ + mnInPlaceConfigEvent = nullptr; + StartInPlacePresentation(); +} + +void SlideShow::StartInPlacePresentation() +{ + if( mpCurrentViewShellBase ) + { + // Save the current view shell type so that it can be restored after the + // show has ended. If there already is a saved shell type then that is + // not overwritten. + + ViewShell::ShellType eShell = ViewShell::ST_NONE; + + ::std::shared_ptr<FrameworkHelper> pHelper(FrameworkHelper::Instance(*mpCurrentViewShellBase)); + ::std::shared_ptr<ViewShell> pMainViewShell(pHelper->GetViewShell(FrameworkHelper::msCenterPaneURL)); + + if( pMainViewShell ) + eShell = pMainViewShell->GetShellType(); + + if( eShell != ViewShell::ST_IMPRESS ) + { + // Switch temporary to a DrawViewShell which supports the in-place presentation. + + if( pMainViewShell ) + { + FrameView* pFrameView = pMainViewShell->GetFrameView(); + pFrameView->SetPresentationViewShellId(SID_VIEWSHELL1); + pFrameView->SetPreviousViewShellType (pMainViewShell->GetShellType()); + pFrameView->SetPageKind (PageKind::Standard); + } + + pHelper->RequestView( FrameworkHelper::msImpressViewURL, FrameworkHelper::msCenterPaneURL ); + pHelper->RunOnConfigurationEvent( + FrameworkHelper::msConfigurationUpdateEndEvent, + [this] (bool const) { return this->StartInPlacePresentationConfigurationCallback(); } ); + return; + } + else + { + vcl::Window* pParentWindow = mxCurrentSettings->mpParentWindow; + if( pParentWindow == nullptr ) + pParentWindow = mpCurrentViewShellBase->GetViewWindow(); + + CreateController( pMainViewShell.get(), pMainViewShell->GetView(), pParentWindow ); + } + } + else if( mxCurrentSettings->mpParentWindow ) + { + // no current view shell, but parent window + CreateController( nullptr, nullptr, mxCurrentSettings->mpParentWindow ); + } + + if( !mxController.is() ) + return; + + bool bSuccess = false; + if( mxCurrentSettings && mxCurrentSettings->mbPreview ) + { + bSuccess = mxController->startPreview(mxCurrentSettings->mxStartPage, mxCurrentSettings->mxAnimationNode, mxCurrentSettings->mpParentWindow ); + } + else + { + bSuccess = mxController->startShow(mxCurrentSettings.get()); + } + + if( !bSuccess ) + end(); + else + { + if( mpCurrentViewShellBase && ( !mxCurrentSettings || ( mxCurrentSettings && !mxCurrentSettings->mbPreview ) ) ) + mpCurrentViewShellBase->GetWindow()->GrabFocus(); + } +} + +void SlideShow::StartFullscreenPresentation( ) +{ + // Create the top level window in which the PresentationViewShell(Base) + // will be created. This is done here explicitly so that we can make it + // fullscreen. + const sal_Int32 nDisplay (GetDisplay()); + VclPtr<WorkWindow> pWorkWindow = VclPtr<FullScreenWorkWindow>::Create(this, mpCurrentViewShellBase); + pWorkWindow->SetBackground(Wallpaper(COL_BLACK)); + OUString Title(SdResId(STR_FULLSCREEN_SLIDESHOW)); + Title = Title.replaceFirst("%s", + mpCurrentViewShellBase->GetDocShell()->GetTitle(SFX_TITLE_DETECT)); + pWorkWindow->SetText(Title); + pWorkWindow->StartPresentationMode( true, mpDoc->getPresentationSettings().mbAlwaysOnTop ? PresentationFlags::HideAllApps : PresentationFlags::NONE, nDisplay); + // pWorkWindow->ShowFullScreenMode(sal_False, nDisplay); + + if (!pWorkWindow->IsVisible()) + return; + + // Initialize the new presentation view shell with a copy of the + // frame view of the current view shell. This avoids that + // changes made by the presentation have an effect on the other + // view shells. + FrameView* pOriginalFrameView = nullptr; + ::std::shared_ptr<ViewShell> xShell(mpCurrentViewShellBase->GetMainViewShell()); + if (xShell) + pOriginalFrameView = xShell->GetFrameView(); + + delete mpFullScreenFrameView; + mpFullScreenFrameView = new FrameView(mpDoc, pOriginalFrameView); + + // The new frame is created hidden. To make it visible and activate the + // new view shell--a prerequisite to process slot calls and initialize + // its panes--a GrabFocus() has to be called later on. + SfxFrame* pNewFrame = SfxFrame::CreateHidden( *mpDoc->GetDocSh(), *pWorkWindow, PRESENTATION_FACTORY_ID ); + pNewFrame->SetPresentationMode(true); + + mpFullScreenViewShellBase = static_cast<ViewShellBase*>(pNewFrame->GetCurrentViewFrame()->GetViewShell()); + if(mpFullScreenViewShellBase != nullptr) + { + // The following GrabFocus() is responsible for activating the + // new view shell. Without it the screen remains blank (under + // Windows and some Linux variants.) + mpFullScreenViewShellBase->GetWindow()->GrabFocus(); + } +} + +/// convert configuration setting display concept to real screens +sal_Int32 SlideShow::GetDisplay() +{ + sal_Int32 nDisplay = 0; + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + if( pOptions ) + nDisplay = pOptions->GetDisplay(); + + if( nDisplay < 0 ) + nDisplay = -1; + else if( nDisplay == 0) + nDisplay = static_cast<sal_Int32>(Application::GetDisplayExternalScreen()); + else + nDisplay--; + + SAL_INFO("sd", "Presenting on real screen " << nDisplay); + + return nDisplay; +} + +bool SlideShow::dependsOn( ViewShellBase const * pViewShellBase ) +{ + return mxController.is() && (pViewShellBase == mpCurrentViewShellBase) && mpFullScreenViewShellBase; +} + +Reference< presentation::XPresentation2 > CreatePresentation( const SdDrawDocument& rDocument ) +{ + return Reference< presentation::XPresentation2 >( SlideShow::Create( const_cast< SdDrawDocument* >( &rDocument ) ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowimpl.cxx b/sd/source/ui/slideshow/slideshowimpl.cxx new file mode 100644 index 0000000000..4bf43fc3bd --- /dev/null +++ b/sd/source/ui/slideshow/slideshowimpl.cxx @@ -0,0 +1,3619 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <algorithm> + +#include <config_features.h> + +#include <com/sun/star/frame/theAutoRecovery.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/document/XEventsSupplier.hpp> +#include <com/sun/star/drawing/XMasterPageTarget.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/SystemPointer.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/presentation/SlideShow.hpp> +#include <com/sun/star/media/XPlayer.hpp> +#include <officecfg/Office/Impress.hxx> +#include <officecfg/Office/Recovery.hxx> +#include <svl/stritem.hxx> +#include <svl/urihelper.hxx> +#include <basic/sbstar.hxx> + +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/sequence.hxx> + +#include <sfx2/infobar.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/app.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdoole2.hxx> +#include <svx/f3dchild.hxx> +#include <svx/imapdlg.hxx> +#include <svx/fontwork.hxx> +#include <svx/SvxColorChildWindow.hxx> +#include <svx/bmpmask.hxx> +#include <svx/srchdlg.hxx> +#include <svx/hyperdlg.hxx> +#include <svx/svxids.hrc> +#include <svx/unoapi.hxx> +#include <AnimationChildWindow.hxx> +#include <notifydocumentevent.hxx> +#include "slideshowimpl.hxx" +#include "slideshowviewimpl.hxx" +#include "PaneHider.hxx" + +#include <bitmaps.hlst> +#include <strings.hrc> +#include <sdresid.hxx> +#include <utility> +#include <vcl/canvastools.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/weldutils.hxx> + +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/help.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <rtl/ref.hxx> +#include <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> +#include <avmedia/mediawindow.hxx> +#include <svtools/colrdlg.hxx> +#include <DrawDocShell.hxx> +#include <ViewShellBase.hxx> +#include <PresentationViewShell.hxx> +#include <RemoteServer.hxx> +#include <customshowlist.hxx> +#include <unopage.hxx> +#include <sdpage.hxx> +#include <sdmod.hxx> +#include <app.hrc> +#include <cusshow.hxx> +#include <optsitem.hxx> + +#define CM_SLIDES 21 + +using ::com::sun::star::animations::XAnimationNode; +using ::com::sun::star::animations::XAnimationListener; +using ::com::sun::star::awt::XWindow; +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::beans; + +namespace sd +{ +/** Slots, which will be disabled in the slide show and are managed by Sfx. + Have to be sorted in the order of the SIDs */ +sal_uInt16 const pAllowed[] = +{ + SID_OPENDOC , // 5501 ///< that internally jumps work + SID_JUMPTOMARK , // 5598 + SID_OPENHYPERLINK , // 6676 + SID_PRESENTATION_END // 27218 +}; + +class AnimationSlideController +{ +public: + enum Mode { ALL, FROM, CUSTOM, PREVIEW }; + +public: + AnimationSlideController( Reference< XIndexAccess > const & xSlides, Mode eMode ); + + void setStartSlideNumber( sal_Int32 nSlideNumber ) { mnStartSlideNumber = nSlideNumber; } + sal_Int32 getStartSlideIndex() const; + + sal_Int32 getCurrentSlideNumber() const; + sal_Int32 getCurrentSlideIndex() const; + + sal_Int32 getSlideIndexCount() const { return maSlideNumbers.size(); } + sal_Int32 getSlideNumberCount() const { return mnSlideCount; } + + sal_Int32 getSlideNumber( sal_Int32 nSlideIndex ) const; + + void insertSlideNumber( sal_Int32 nSlideNumber, bool bVisible = true ); + void setPreviewNode( const Reference< XAnimationNode >& xPreviewNode ); + + bool jumpToSlideIndex( sal_Int32 nNewSlideIndex ); + bool jumpToSlideNumber( sal_Int32 nNewSlideIndex ); + + bool nextSlide(); + bool previousSlide(); + + void displayCurrentSlide( const Reference< XSlideShow >& xShow, + const Reference< XDrawPagesSupplier>& xDrawPages, + const bool bSkipAllMainSequenceEffects ); + + sal_Int32 getNextSlideIndex() const; + sal_Int32 getPreviousSlideIndex() const; + + bool isVisibleSlideNumber( sal_Int32 nSlideNumber ) const; + + Reference< XDrawPage > getSlideByNumber( sal_Int32 nSlideNumber ) const; + + sal_Int32 getNextSlideNumber() const; + + bool hasSlides() const { return !maSlideNumbers.empty(); } + +private: + bool getSlideAPI( sal_Int32 nSlideNumber, Reference< XDrawPage >& xSlide, Reference< XAnimationNode >& xAnimNode ); + sal_Int32 findSlideIndex( sal_Int32 nSlideNumber ) const; + + bool isValidIndex( sal_Int32 nIndex ) const { return (nIndex >= 0) && (o3tl::make_unsigned(nIndex) < maSlideNumbers.size()); } + bool isValidSlideNumber( sal_Int32 nSlideNumber ) const { return (nSlideNumber >= 0) && (nSlideNumber < mnSlideCount); } + +private: + Mode meMode; + sal_Int32 mnStartSlideNumber; + std::vector< sal_Int32 > maSlideNumbers; + std::vector< bool > maSlideVisible; + std::vector< bool > maSlideVisited; + Reference< XAnimationNode > mxPreviewNode; + sal_Int32 mnSlideCount; + sal_Int32 mnCurrentSlideIndex; + sal_Int32 mnHiddenSlideNumber; + Reference< XIndexAccess > mxSlides; +}; + +Reference< XDrawPage > AnimationSlideController::getSlideByNumber( sal_Int32 nSlideNumber ) const +{ + Reference< XDrawPage > xSlide; + if( mxSlides.is() && (nSlideNumber >= 0) && (nSlideNumber < mxSlides->getCount()) ) + mxSlides->getByIndex( nSlideNumber ) >>= xSlide; + return xSlide; +} + +bool AnimationSlideController::isVisibleSlideNumber( sal_Int32 nSlideNumber ) const +{ + sal_Int32 nIndex = findSlideIndex( nSlideNumber ); + + if( nIndex != -1 ) + return maSlideVisible[ nIndex ]; + else + return false; +} + +void AnimationSlideController::setPreviewNode( const Reference< XAnimationNode >& xPreviewNode ) +{ + mxPreviewNode = xPreviewNode; +} + +AnimationSlideController::AnimationSlideController( Reference< XIndexAccess > const & xSlides, Mode eMode ) +: meMode( eMode ) +, mnStartSlideNumber(-1) +, mnSlideCount( 0 ) +, mnCurrentSlideIndex(0) +, mnHiddenSlideNumber( -1 ) +, mxSlides( xSlides ) +{ + if( mxSlides.is() ) + mnSlideCount = xSlides->getCount(); +} + +sal_Int32 AnimationSlideController::getStartSlideIndex() const +{ + if( mnStartSlideNumber >= 0 ) + { + sal_Int32 nIndex; + const sal_Int32 nCount = maSlideNumbers.size(); + + for( nIndex = 0; nIndex < nCount; nIndex++ ) + { + if( maSlideNumbers[nIndex] == mnStartSlideNumber ) + return nIndex; + } + } + + return 0; +} + +sal_Int32 AnimationSlideController::getCurrentSlideNumber() const +{ + if( mnHiddenSlideNumber != -1 ) + return mnHiddenSlideNumber; + else if( !maSlideNumbers.empty() ) + return maSlideNumbers[mnCurrentSlideIndex]; + else + return 0; +} + +sal_Int32 AnimationSlideController::getCurrentSlideIndex() const +{ + if( mnHiddenSlideNumber != -1 ) + return -1; + else + return mnCurrentSlideIndex; +} + +bool AnimationSlideController::jumpToSlideIndex( sal_Int32 nNewSlideIndex ) +{ + if( isValidIndex( nNewSlideIndex ) ) + { + mnCurrentSlideIndex = nNewSlideIndex; + mnHiddenSlideNumber = -1; + maSlideVisited[mnCurrentSlideIndex] = true; + return true; + } + else + { + return false; + } +} + +bool AnimationSlideController::jumpToSlideNumber( sal_Int32 nNewSlideNumber ) +{ + sal_Int32 nIndex = findSlideIndex( nNewSlideNumber ); + if( isValidIndex( nIndex ) ) + { + return jumpToSlideIndex( nIndex ); + } + else if( (nNewSlideNumber >= 0) && (nNewSlideNumber < mnSlideCount) ) + { + // jump to a hidden slide + mnHiddenSlideNumber = nNewSlideNumber; + return true; + } + else + { + return false; + } +} + +sal_Int32 AnimationSlideController::getSlideNumber( sal_Int32 nSlideIndex ) const +{ + if( isValidIndex( nSlideIndex ) ) + return maSlideNumbers[nSlideIndex]; + else + return -1; +} + +void AnimationSlideController::insertSlideNumber( sal_Int32 nSlideNumber, bool bVisible /* = true */ ) +{ + DBG_ASSERT( isValidSlideNumber( nSlideNumber ), "sd::AnimationSlideController::insertSlideNumber(), illegal index" ); + if( isValidSlideNumber( nSlideNumber ) ) + { + maSlideNumbers.push_back( nSlideNumber ); + maSlideVisible.push_back( bVisible ); + maSlideVisited.push_back( false ); + } +} + +bool AnimationSlideController::getSlideAPI( sal_Int32 nSlideNumber, Reference< XDrawPage >& xSlide, Reference< XAnimationNode >& xAnimNode ) +{ + if( isValidSlideNumber( nSlideNumber ) ) try + { + xSlide.set( mxSlides->getByIndex(nSlideNumber), UNO_QUERY_THROW ); + + if( meMode == PREVIEW ) + { + xAnimNode = mxPreviewNode; + } + else + { + Reference< animations::XAnimationNodeSupplier > xAnimNodeSupplier( xSlide, UNO_QUERY_THROW ); + xAnimNode = xAnimNodeSupplier->getAnimationNode(); + } + + return true; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::AnimationSlideController::getSlideAPI()" ); + } + + return false; +} + +sal_Int32 AnimationSlideController::findSlideIndex( sal_Int32 nSlideNumber ) const +{ + sal_Int32 nIndex; + const sal_Int32 nCount = maSlideNumbers.size(); + + for( nIndex = 0; nIndex < nCount; nIndex++ ) + { + if( maSlideNumbers[nIndex] == nSlideNumber ) + return nIndex; + } + + return -1; +} + +sal_Int32 AnimationSlideController::getNextSlideIndex() const +{ + switch( meMode ) + { + case ALL: + { + sal_Int32 nNewSlideIndex = mnCurrentSlideIndex + 1; + if( isValidIndex( nNewSlideIndex ) ) + { + // if the current slide is not excluded, make sure the + // next slide is also not excluded. + // if the current slide is excluded, we want to go + // to the next slide, even if this is also excluded. + if( maSlideVisible[mnCurrentSlideIndex] ) + { + while( isValidIndex( nNewSlideIndex ) ) + { + if( maSlideVisible[nNewSlideIndex] ) + break; + + nNewSlideIndex++; + } + } + } + return isValidIndex( nNewSlideIndex ) ? nNewSlideIndex : -1; + } + + case FROM: + case CUSTOM: + return mnHiddenSlideNumber == -1 ? mnCurrentSlideIndex + 1 : mnCurrentSlideIndex; + + default: + case PREVIEW: + return -1; + + } +} + +sal_Int32 AnimationSlideController::getNextSlideNumber() const +{ + sal_Int32 nNextSlideIndex = getNextSlideIndex(); + if( isValidIndex( nNextSlideIndex ) ) + { + return maSlideNumbers[nNextSlideIndex]; + } + else + { + return -1; + } +} + +bool AnimationSlideController::nextSlide() +{ + return jumpToSlideIndex( getNextSlideIndex() ); +} + +sal_Int32 AnimationSlideController::getPreviousSlideIndex() const +{ + sal_Int32 nNewSlideIndex = mnCurrentSlideIndex - 1; + + switch( meMode ) + { + case ALL: + { + // make sure the previous slide is visible + // or was already visited + while( isValidIndex( nNewSlideIndex ) ) + { + if( maSlideVisible[nNewSlideIndex] || maSlideVisited[nNewSlideIndex] ) + break; + + nNewSlideIndex--; + } + + break; + } + + case PREVIEW: + return -1; + + default: + break; + } + + return nNewSlideIndex; +} + +bool AnimationSlideController::previousSlide() +{ + return jumpToSlideIndex( getPreviousSlideIndex() ); +} + +void AnimationSlideController::displayCurrentSlide( const Reference< XSlideShow >& xShow, + const Reference< XDrawPagesSupplier>& xDrawPages, + const bool bSkipAllMainSequenceEffects ) +{ + const sal_Int32 nCurrentSlideNumber = getCurrentSlideNumber(); + + if( !(xShow.is() && (nCurrentSlideNumber != -1 )) ) + return; + + Reference< XDrawPage > xSlide; + Reference< XAnimationNode > xAnimNode; + ::std::vector<PropertyValue> aProperties; + + const sal_Int32 nNextSlideNumber = getNextSlideNumber(); + if( getSlideAPI( nNextSlideNumber, xSlide, xAnimNode ) ) + { + Sequence< Any > aValue{ Any(xSlide), Any(xAnimNode) }; + aProperties.emplace_back( "Prefetch" , + -1, + Any(aValue), + PropertyState_DIRECT_VALUE); + } + if (bSkipAllMainSequenceEffects) + { + // Add one property that prevents the slide transition from being + // shown (to speed up the transition to the previous slide) and + // one to show all main sequence effects so that the user can + // continue to undo effects. + aProperties.emplace_back( "SkipAllMainSequenceEffects", + -1, + Any(true), + PropertyState_DIRECT_VALUE); + aProperties.emplace_back("SkipSlideTransition", + -1, + Any(true), + PropertyState_DIRECT_VALUE); + } + + if( getSlideAPI( nCurrentSlideNumber, xSlide, xAnimNode ) ) + xShow->displaySlide( xSlide, xDrawPages, xAnimNode, comphelper::containerToSequence(aProperties) ); +} + +constexpr OUString gsOnClick( u"OnClick"_ustr ); +constexpr OUString gsBookmark( u"Bookmark"_ustr ); +constexpr OUString gsVerb( u"Verb"_ustr ); + +SlideshowImpl::SlideshowImpl( const Reference< XPresentation2 >& xPresentation, ViewShell* pViewSh, ::sd::View* pView, SdDrawDocument* pDoc, vcl::Window* pParentWindow ) +: mxModel(pDoc->getUnoModel()) +, maUpdateTimer("SlideShowImpl maUpdateTimer") +, maInputFreezeTimer("SlideShowImpl maInputFreezeTimer") +, maDeactivateTimer("SlideShowImpl maDeactivateTimer") +, mpView(pView) +, mpViewShell(pViewSh) +, mpDocSh(pDoc->GetDocSh()) +, mpDoc(pDoc) +, mpParentWindow(pParentWindow) +, mpShowWindow(nullptr) +, mnRestoreSlide(0) +, maPresSize( -1, -1 ) +, meAnimationMode(ANIMATIONMODE_SHOW) +, mpOldActiveWindow(nullptr) +, mnChildMask( 0 ) +, mbDisposed(false) +, mbAutoSaveWasOn(false) +, mbRehearseTimings(false) +, mbIsPaused(false) +, mbWasPaused(false) +, mbInputFreeze(false) +, mbActive(false) +, maPresSettings( pDoc->getPresentationSettings() ) +, mnUserPaintColor( 0x80ff0000L ) +, mbUsePen(false) +, mdUserPaintStrokeWidth ( 150.0 ) +, mnEndShowEvent(nullptr) +, mnContextMenuEvent(nullptr) +, mnEventObjectChange(nullptr) +, mnEventPageOrderChange(nullptr) +, mxPresentation( xPresentation ) +{ + if( mpViewShell ) + mpOldActiveWindow = mpViewShell->GetActiveWindow(); + + maUpdateTimer.SetInvokeHandler(LINK(this, SlideshowImpl, updateHdl)); + // Priority must not be too high or we'll starve input handling etc. + maUpdateTimer.SetPriority(TaskPriority::REPAINT); + + maDeactivateTimer.SetInvokeHandler(LINK(this, SlideshowImpl, deactivateHdl)); + maDeactivateTimer.SetTimeout( 20 ); + + maInputFreezeTimer.SetInvokeHandler( LINK( this, SlideshowImpl, ReadyForNextInputHdl ) ); + maInputFreezeTimer.SetTimeout( 20 ); + + // no autosave during show + if (officecfg::Office::Recovery::AutoSave::Enabled::get()) + mbAutoSaveWasOn = true; + + Application::AddEventListener( LINK( this, SlideshowImpl, EventListenerHdl ) ); + + mbUsePen = maPresSettings.mbMouseAsPen; + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + if( pOptions ) + { + mnUserPaintColor = pOptions->GetPresentationPenColor(); + mdUserPaintStrokeWidth = pOptions->GetPresentationPenWidth(); + } + + // to be able to react on various changes in the DrawModel, this class + // is now derived from SfxListener and registers itself at the DrawModel + if (nullptr != mpDoc) + StartListening(*mpDoc); +} + +SlideshowImpl::~SlideshowImpl() +{ + // stop listening to DrawModel (see above) + if (nullptr != mpDoc) + EndListening(*mpDoc); + + SdModule *pModule = SD_MOD(); + //rhbz#806663 SlideshowImpl can outlive SdModule + SdOptions* pOptions = pModule ? + pModule->GetSdOptions(DocumentType::Impress) : nullptr; + if( pOptions ) + { + pOptions->SetPresentationPenColor(mnUserPaintColor); + pOptions->SetPresentationPenWidth(mdUserPaintStrokeWidth); + } + + Application::RemoveEventListener( LINK( this, SlideshowImpl, EventListenerHdl ) ); + + maDeactivateTimer.Stop(); + + if( !mbDisposed ) + { + OSL_FAIL("SlideshowImpl::~SlideshowImpl(), component was not disposed!"); + std::unique_lock g(m_aMutex); + disposing(g); + } +} + +void SlideshowImpl::disposing(std::unique_lock<std::mutex>&) +{ +#ifdef ENABLE_SDREMOTE + RemoteServer::presentationStopped(); +#endif + if( mxShow.is() && mpDoc ) + NotifyDocumentEvent( + *mpDoc, + "OnEndPresentation" ); + + if( mbAutoSaveWasOn ) + setAutoSaveState( true ); + + if( mnEndShowEvent ) + Application::RemoveUserEvent( mnEndShowEvent ); + if( mnContextMenuEvent ) + Application::RemoveUserEvent( mnContextMenuEvent ); + if( mnEventObjectChange ) + Application::RemoveUserEvent( mnEventObjectChange ); + if( mnEventPageOrderChange ) + Application::RemoveUserEvent( mnEventPageOrderChange ); + + maInputFreezeTimer.Stop(); + + SolarMutexGuard aSolarGuard; + + if( !mxShow.is() ) + return; + + if( mxPresentation.is() ) + mxPresentation->end(); + + maUpdateTimer.Stop(); + + removeShapeEvents(); + + if( mxListenerProxy.is() ) + mxListenerProxy->removeAsSlideShowListener(); + + try + { + if( mxView.is() ) + mxShow->removeView( mxView ); + + Reference< XComponent > xComponent( mxShow, UNO_QUERY ); + if( xComponent.is() ) + xComponent->dispose(); + + if( mxView.is() ) + mxView->dispose(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::stop()" ); + } + + mxShow.clear(); + mxView.clear(); + mxListenerProxy.clear(); + mpSlideController.reset(); + + // take DrawView from presentation window, but give the old window back + if( mpShowWindow && mpView ) + mpView->DeleteDeviceFromPaintView( *mpShowWindow->GetOutDev() ); + + if( mpView ) + mpView->SetAnimationPause( false ); + + if( mpViewShell ) + { + mpViewShell->SetActiveWindow(mpOldActiveWindow); + if (mpShowWindow) + mpShowWindow->SetViewShell( nullptr ); + } + + if( mpView ) + mpView->InvalidateAllWin(); + + if( maPresSettings.mbFullScreen ) + { +#if HAVE_FEATURE_SCRIPTING + // restore StarBASICErrorHdl + StarBASIC::SetGlobalErrorHdl(maStarBASICGlobalErrorHdl); + maStarBASICGlobalErrorHdl = Link<StarBASIC*,bool>(); +#endif + } + else + { + if( mpShowWindow ) + mpShowWindow->Hide(); + } + + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + mpDocSh->SetSlotFilter(); + mpDocSh->ApplySlotFilter(); + + Help::EnableContextHelp(); + Help::EnableExtHelp(); + + showChildWindows(); + mnChildMask = 0; + } + + // show current window again + if( mpViewShell && dynamic_cast< PresentationViewShell *>( mpViewShell ) == nullptr) + { + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + mpViewShell->GetViewShellBase().ShowUIControls (true); + mpPaneHider.reset(); + } + else if( meAnimationMode == ANIMATIONMODE_PREVIEW ) + { + mpViewShell->ShowUIControls(true); + } + } + + if( mpShowWindow ) + mpShowWindow->Hide(); + mpShowWindow.disposeAndClear(); + + if ( mpViewShell ) + { + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + ::sd::Window* pActWin = mpViewShell->GetActiveWindow(); + + if (pActWin) + { + Size aVisSizePixel = pActWin->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = pActWin->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + mpViewShell->VisAreaChanged(aVisAreaWin); + if (mpView) + mpView->VisAreaChanged(pActWin->GetOutDev()); + pActWin->GrabFocus(); + } + } + + // restart the custom show dialog if he started us + if( mpViewShell->IsStartShowWithDialog() && getDispatcher() ) + { + mpViewShell->SetStartShowWithDialog( false ); + getDispatcher()->Execute( SID_CUSTOMSHOW_DLG, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + + mpViewShell->GetViewShellBase().UpdateBorder(true); + } + + if( mpShowWindow ) + { + mpShowWindow.disposeAndClear(); + } + + setActiveXToolbarsVisible( true ); + + mbDisposed = true; +} + +bool SlideshowImpl::startPreview( + const Reference< XDrawPage >& xDrawPage, + const Reference< XAnimationNode >& xAnimationNode, + vcl::Window * pParent ) +{ + bool bRet = false; + + try + { + const Reference<lang::XServiceInfo> xServiceInfo( xDrawPage, UNO_QUERY ); + if (xServiceInfo.is()) { + const Sequence<OUString> supportedServices( + xServiceInfo->getSupportedServiceNames() ); + if (comphelper::findValue(supportedServices, "com.sun.star.drawing.MasterPage") != -1) { + OSL_FAIL("sd::SlideshowImpl::startPreview() " + "not allowed on master page!"); + return false; + } + } + + mxPreviewDrawPage = xDrawPage; + mxPreviewAnimationNode = xAnimationNode; + meAnimationMode = ANIMATIONMODE_PREVIEW; + + maPresSettings.mbAll = false; + maPresSettings.mbEndless = false; + maPresSettings.mbCustomShow = false; + maPresSettings.mbManual = false; + maPresSettings.mbMouseVisible = false; + maPresSettings.mbMouseAsPen = false; + maPresSettings.mbLockedPages = false; + maPresSettings.mbAlwaysOnTop = false; + maPresSettings.mbFullScreen = false; + maPresSettings.mbAnimationAllowed = true; + maPresSettings.mnPauseTimeout = 0; + maPresSettings.mbShowPauseLogo = false; + + Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), UNO_QUERY_THROW ); + Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW ); + mpSlideController = std::make_shared<AnimationSlideController>( xSlides, AnimationSlideController::PREVIEW ); + + sal_Int32 nSlideNumber = 0; + Reference< XPropertySet > xSet( mxPreviewDrawPage, UNO_QUERY_THROW ); + xSet->getPropertyValue( "Number" ) >>= nSlideNumber; + mpSlideController->insertSlideNumber( nSlideNumber-1 ); + mpSlideController->setPreviewNode( xAnimationNode ); + + mpShowWindow = VclPtr<ShowWindow>::Create( this, ((pParent == nullptr) && mpViewShell) ? mpParentWindow.get() : pParent ); + if( mpViewShell ) + { + mpViewShell->SetActiveWindow( mpShowWindow ); + mpShowWindow->SetViewShell (mpViewShell); + mpViewShell->ShowUIControls (false); + } + + if( mpView ) + { + mpView->AddDeviceToPaintView( *mpShowWindow->GetOutDev(), nullptr ); + mpView->SetAnimationPause( true ); + } + + // call resize handler + if( pParent ) + { + maPresSize = pParent->GetSizePixel(); + } + else if( mpViewShell ) + { + ::tools::Rectangle aContentRect (mpViewShell->GetViewShellBase().getClientRectangle()); + if (AllSettings::GetLayoutRTL()) + { + aContentRect.SetLeft( aContentRect.Right() ); + aContentRect.AdjustRight(aContentRect.Right() ); + } + maPresSize = aContentRect.GetSize(); + mpShowWindow->SetPosPixel( aContentRect.TopLeft() ); + } + else + { + OSL_FAIL("sd::SlideshowImpl::startPreview(), I need either a parent window or a viewshell!"); + } + resize( maPresSize ); + + sal_Int32 nPropertyCount = 1; + if( mxPreviewAnimationNode.is() ) + nPropertyCount++; + + Sequence< beans::PropertyValue > aProperties(nPropertyCount); + auto pProperties = aProperties.getArray(); + pProperties[0].Name = "AutomaticAdvancement"; + pProperties[0].Value <<= 1.0; // one second timeout + + if( mxPreviewAnimationNode.is() ) + { + pProperties[1].Name = "NoSlideTransitions"; + pProperties[1].Value <<= true; + } + + bRet = startShowImpl( aProperties ); + + if( mpShowWindow != nullptr && meAnimationMode == ANIMATIONMODE_PREVIEW ) + mpShowWindow->SetPreviewMode(); + + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::startPreview()" ); + bRet = false; + } + + return bRet; +} + +bool SlideshowImpl::startShow( PresentationSettingsEx const * pPresSettings ) +{ + const rtl::Reference<SlideshowImpl> xKeepAlive(this); + + DBG_ASSERT( !mxShow.is(), "sd::SlideshowImpl::startShow(), called twice!" ); + if( mxShow.is() ) + return true; + DBG_ASSERT( mpParentWindow!=nullptr, "sd::SlideshowImpl::startShow() called without parent window" ); + if (mpParentWindow == nullptr) + return false; + + // Autoplay (pps/ppsx) + if (mpViewShell->GetDoc()->IsStartWithPresentation()){ + mpViewShell->GetDoc()->SetExitAfterPresenting(true); + } + + bool bRet = false; + + try + { + if( pPresSettings ) + { + maPresSettings = *pPresSettings; + mbRehearseTimings = pPresSettings->mbRehearseTimings; + } + + OUString aPresSlide( maPresSettings.maPresPage ); + SdPage* pStartPage = mpViewShell->GetActualPage(); + bool bStartWithActualSlide = pStartPage; + + // times should be measured? + if( mbRehearseTimings ) + { + maPresSettings.mbEndless = false; + maPresSettings.mbManual = true; + maPresSettings.mbMouseVisible = true; + maPresSettings.mbMouseAsPen = false; + maPresSettings.mnPauseTimeout = 0; + maPresSettings.mbShowPauseLogo = false; + } + + if( pStartPage ) + { + if( pStartPage->GetPageKind() == PageKind::Notes ) + { + // we are in notes page mode, so get + // the corresponding draw page + const sal_uInt16 nPgNum = ( pStartPage->GetPageNum() - 2 ) >> 1; + pStartPage = mpDoc->GetSdPage( nPgNum, PageKind::Standard ); + } + } + + if( bStartWithActualSlide ) + { + if ( aPresSlide.isEmpty()) + { + // no preset slide yet, so pick current on one + aPresSlide = pStartPage->GetName(); + // if the starting slide is hidden, we can't set slide controller to ALL mode + maPresSettings.mbAll = !pStartPage->IsExcluded(); + } + + if( meAnimationMode != ANIMATIONMODE_SHOW ) + { + if( pStartPage->GetPageKind() == PageKind::Standard ) + { + maPresSettings.mbAll = false; + } + } + } + + // build page list + createSlideList( maPresSettings.mbAll, aPresSlide ); + + // remember Slide number from where the show was started + if( pStartPage ) + mnRestoreSlide = ( pStartPage->GetPageNum() - 1 ) / 2; + + if( mpSlideController->hasSlides() ) + { + // hide child windows + hideChildWindows(); + + mpShowWindow = VclPtr<ShowWindow>::Create( this, mpParentWindow ); + mpShowWindow->SetMouseAutoHide( !maPresSettings.mbMouseVisible ); + mpViewShell->SetActiveWindow( mpShowWindow ); + mpShowWindow->SetViewShell (mpViewShell); + mpViewShell->GetViewShellBase().ShowUIControls (false); + // Hide the side panes for in-place presentations. + if ( ! maPresSettings.mbFullScreen) + mpPaneHider.reset(new PaneHider(*mpViewShell,this)); + + // these Slots are forbidden in other views for this document + if( mpDocSh ) + { + mpDocSh->SetSlotFilter( true, pAllowed ); + mpDocSh->ApplySlotFilter(); + } + + Help::DisableContextHelp(); + Help::DisableExtHelp(); + + if( maPresSettings.mbFullScreen ) + { +#if HAVE_FEATURE_SCRIPTING + // disable basic ide error handling + maStarBASICGlobalErrorHdl = StarBASIC::GetGlobalErrorHdl(); + StarBASIC::SetGlobalErrorHdl( Link<StarBASIC*,bool>() ); +#endif + } + + // call resize handler + maPresSize = mpParentWindow->GetSizePixel(); + if (!maPresSettings.mbFullScreen) + { + const ::tools::Rectangle& aClientRect = mpViewShell->GetViewShellBase().getClientRectangle(); + maPresSize = aClientRect.GetSize(); + mpShowWindow->SetPosPixel( aClientRect.TopLeft() ); + resize( maPresSize ); + } + + // #i41824# + // Note: In FullScreen Mode the OS (window manager) sends a resize to + // the WorkWindow once it actually resized it to full size. The + // WorkWindow propagates the resize to the DrawViewShell which calls + // resize() at the SlideShow (this). Calling resize here results in a + // temporary display of a black window in the window's default size + + if( mpView ) + { + mpView->AddDeviceToPaintView( *mpShowWindow->GetOutDev(), nullptr ); + mpView->SetAnimationPause( true ); + } + + SfxBindings* pBindings = getBindings(); + if( pBindings ) + { + pBindings->Invalidate( SID_PRESENTATION ); + pBindings->Invalidate( SID_REHEARSE_TIMINGS ); + } + + // Defer the sd::ShowWindow's GrabFocus to SlideShow::activate. so that the accessible event can be fired correctly. + //mpShowWindow->GrabFocus(); + + std::vector<beans::PropertyValue> aProperties; + aProperties.reserve( 4 ); + + aProperties.emplace_back( "AdvanceOnClick" , + -1, Any( !maPresSettings.mbLockedPages ), + beans::PropertyState_DIRECT_VALUE ); + + aProperties.emplace_back( "ImageAnimationsAllowed" , + -1, Any( maPresSettings.mbAnimationAllowed ), + beans::PropertyState_DIRECT_VALUE ); + + const bool bZOrderEnabled( + SD_MOD()->GetSdOptions( mpDoc->GetDocumentType() )->IsSlideshowRespectZOrder() ); + aProperties.emplace_back( "DisableAnimationZOrder" , + -1, Any( !bZOrderEnabled ), + beans::PropertyState_DIRECT_VALUE ); + + aProperties.emplace_back( "ForceManualAdvance" , + -1, Any( maPresSettings.mbManual ), + beans::PropertyState_DIRECT_VALUE ); + + if( mbUsePen ) + { + aProperties.emplace_back( "UserPaintColor" , + // User paint color is black by default. + -1, Any( mnUserPaintColor ), + beans::PropertyState_DIRECT_VALUE ); + + aProperties.emplace_back( "UserPaintStrokeWidth" , + // User paint color is black by default. + -1, Any( mdUserPaintStrokeWidth ), + beans::PropertyState_DIRECT_VALUE ); + } + + if (mbRehearseTimings) { + aProperties.emplace_back( "RehearseTimings" , + -1, Any(true), beans::PropertyState_DIRECT_VALUE ); + } + + bRet = startShowImpl( Sequence<beans::PropertyValue>( + aProperties.data(), aProperties.size() ) ); + + } + + setActiveXToolbarsVisible( false ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::startShow()" ); + bRet = false; + } + + return bRet; +} + +bool SlideshowImpl::startShowImpl( const Sequence< beans::PropertyValue >& aProperties ) +{ + try + { + mxShow.set( createSlideShow(), UNO_SET_THROW ); + + mxView = new SlideShowView( + *mpShowWindow, + mpDoc, + meAnimationMode, + this, + maPresSettings.mbFullScreen); + + // try add wait symbol to properties: + const Reference<rendering::XSpriteCanvas> xSpriteCanvas( + mxView->getCanvas() ); + if (xSpriteCanvas.is()) + { + BitmapEx waitSymbolBitmap(BMP_WAIT_ICON); + const Reference<rendering::XBitmap> xBitmap( + vcl::unotools::xBitmapFromBitmapEx( waitSymbolBitmap ) ); + if (xBitmap.is()) + { + mxShow->setProperty( + beans::PropertyValue( "WaitSymbolBitmap" , + -1, + Any( xBitmap ), + beans::PropertyState_DIRECT_VALUE ) ); + } + + BitmapEx pointerSymbolBitmap(BMP_POINTER_ICON); + const Reference<rendering::XBitmap> xPointerBitmap( + vcl::unotools::xBitmapFromBitmapEx( pointerSymbolBitmap ) ); + if (xPointerBitmap.is()) + { + mxShow->setProperty( + beans::PropertyValue( "PointerSymbolBitmap" , + -1, + Any( xPointerBitmap ), + beans::PropertyState_DIRECT_VALUE ) ); + } + if (officecfg::Office::Impress::Misc::Start::ShowNavigationPanel::get()) + { + NavbarButtonSize btnScale = static_cast<NavbarButtonSize>(officecfg::Office::Impress::Layout::Display::NavigationBtnScale::get()); + OUString prevSlidePath = ""; + OUString nextSlidePath = ""; + OUString menuPath = ""; + switch (btnScale) + { + case NavbarButtonSize::Large: + { + prevSlidePath = BMP_PREV_SLIDE_LARGE; + nextSlidePath = BMP_NEXT_SLIDE_LARGE; + menuPath = BMP_MENU_SLIDE_LARGE; + break; + } + case NavbarButtonSize::XLarge: + { + prevSlidePath = BMP_PREV_SLIDE_EXTRALARGE; + nextSlidePath = BMP_NEXT_SLIDE_EXTRALARGE; + menuPath = BMP_MENU_SLIDE_EXTRALARGE; + break; + } + case NavbarButtonSize::Auto: + case NavbarButtonSize::Small: + default: + { + prevSlidePath = BMP_PREV_SLIDE_SMALL; + nextSlidePath = BMP_NEXT_SLIDE_SMALL; + menuPath = BMP_MENU_SLIDE_SMALL; + break; + } + } + BitmapEx prevSlideBm(prevSlidePath); + const Reference<rendering::XBitmap> xPrevSBitmap( + vcl::unotools::xBitmapFromBitmapEx(prevSlideBm)); + if (xPrevSBitmap.is()) + { + mxShow->setProperty(beans::PropertyValue("NavigationSlidePrev", -1, + Any(xPrevSBitmap), + beans::PropertyState_DIRECT_VALUE)); + } + BitmapEx menuSlideBm(menuPath); + const Reference<rendering::XBitmap> xMenuSBitmap( + vcl::unotools::xBitmapFromBitmapEx(menuSlideBm)); + if (xMenuSBitmap.is()) + { + mxShow->setProperty(beans::PropertyValue("NavigationSlideMenu", -1, + Any(xMenuSBitmap), + beans::PropertyState_DIRECT_VALUE)); + } + BitmapEx nextSlideBm(nextSlidePath); + const Reference<rendering::XBitmap> xNextSBitmap( + vcl::unotools::xBitmapFromBitmapEx(nextSlideBm)); + if (xNextSBitmap.is()) + { + mxShow->setProperty(beans::PropertyValue("NavigationSlideNext", -1, + Any(xNextSBitmap), + beans::PropertyState_DIRECT_VALUE)); + } + } + } + + for( const auto& rProp : aProperties ) + mxShow->setProperty( rProp ); + + mxShow->addView( mxView ); + + mxListenerProxy.set( new SlideShowListenerProxy( this, mxShow ) ); + mxListenerProxy->addAsSlideShowListener(); + + NotifyDocumentEvent( + *mpDoc, + "OnStartPresentation"); + displaySlideIndex( mpSlideController->getStartSlideIndex() ); + + return true; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::startShowImpl()" ); + return false; + } +} + +/** called only by the slideshow view when the first paint event occurs. + This actually starts the slideshow. */ +void SlideshowImpl::onFirstPaint() +{ + if( mpShowWindow ) + { + /* + mpShowWindow->SetBackground( Wallpaper( COL_BLACK ) ); + mpShowWindow->Erase(); + mpShowWindow->SetBackground(); + */ + } + + SolarMutexGuard aSolarGuard; + maUpdateTimer.SetTimeout( sal_uLong(100) ); + maUpdateTimer.Start(); +} + +void SlideshowImpl::paint() +{ + if( mxView.is() ) try + { + awt::PaintEvent aEvt; + // aEvt.UpdateRect = TODO + mxView->paint( aEvt ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::paint()" ); + } +} + +void SAL_CALL SlideshowImpl::addSlideShowListener( const Reference< XSlideShowListener >& xListener ) +{ + if( mxListenerProxy.is() ) + mxListenerProxy->addSlideShowListener( xListener ); +} + +void SAL_CALL SlideshowImpl::removeSlideShowListener( const Reference< XSlideShowListener >& xListener ) +{ + if( mxListenerProxy.is() ) + mxListenerProxy->removeSlideShowListener( xListener ); +} + +void SlideshowImpl::slideEnded(const bool bReverse) +{ + if (bReverse) + gotoPreviousSlide(true); + else + gotoNextSlide(); +} + +bool SlideshowImpl::swipe(const CommandGestureSwipeData &rSwipeData) +{ + if (mbUsePen || mnContextMenuEvent) + return false; + double nVelocityX = rSwipeData.getVelocityX(); + // tdf#108475 make it swipe only if some reasonable movement was involved + if (fabs(nVelocityX) < 50) + return false; + if (nVelocityX > 0) + { + gotoPreviousSlide(); + } + else + { + gotoNextEffect(); + } + //a swipe is followed by a mouse up, tell the view to ignore that mouse up as we've reacted + //to the swipe instead + mxView->ignoreNextMouseReleased(); + return true; +} + +bool SlideshowImpl::longpress(const CommandGestureLongPressData &rLongPressData) +{ + if (mnContextMenuEvent) + return false; + + maPopupMousePos = Point(rLongPressData.getX(), rLongPressData.getY()); + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + + return true; +} + +void SlideshowImpl::removeShapeEvents() +{ + if( !(mxShow.is() && mxListenerProxy.is()) ) + return; + + try + { + for( const auto& rEntry : maShapeEventMap ) + { + mxListenerProxy->removeShapeEventListener( rEntry.first ); + mxShow->setShapeCursor( rEntry.first, awt::SystemPointer::ARROW ); + } + + maShapeEventMap.clear(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::removeShapeEvents()" ); + } +} + +void SlideshowImpl::registerShapeEvents(sal_Int32 nSlideNumber) +{ + if( nSlideNumber < 0 ) + return; + + try + { + Reference< XDrawPagesSupplier > xDrawPages( mxModel, UNO_QUERY_THROW ); + Reference< XIndexAccess > xPages( xDrawPages->getDrawPages(), UNO_QUERY_THROW ); + + Reference< XShapes > xDrawPage; + xPages->getByIndex(nSlideNumber) >>= xDrawPage; + + if( xDrawPage.is() ) + { + Reference< XMasterPageTarget > xMasterPageTarget( xDrawPage, UNO_QUERY ); + if( xMasterPageTarget.is() ) + { + Reference< XShapes > xMasterPage = xMasterPageTarget->getMasterPage(); + if( xMasterPage.is() ) + registerShapeEvents( xMasterPage ); + } + registerShapeEvents( xDrawPage ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::registerShapeEvents()" ); + } +} + +void SlideshowImpl::registerShapeEvents( Reference< XShapes > const & xShapes ) +{ + try + { + const sal_Int32 nShapeCount = xShapes->getCount(); + sal_Int32 nShape; + for( nShape = 0; nShape < nShapeCount; nShape++ ) + { + Reference< XShape > xShape; + xShapes->getByIndex( nShape ) >>= xShape; + + if( xShape.is() && xShape->getShapeType() == "com.sun.star.drawing.GroupShape" ) + { + Reference< XShapes > xSubShapes( xShape, UNO_QUERY ); + if( xSubShapes.is() ) + registerShapeEvents( xSubShapes ); + } + + Reference< XPropertySet > xSet( xShape, UNO_QUERY ); + if( !xSet.is() ) + continue; + + Reference< XPropertySetInfo > xSetInfo( xSet->getPropertySetInfo() ); + if( !xSetInfo.is() || !xSetInfo->hasPropertyByName( gsOnClick ) ) + continue; + + WrappedShapeEventImplPtr pEvent = std::make_shared<WrappedShapeEventImpl>(); + xSet->getPropertyValue( gsOnClick ) >>= pEvent->meClickAction; + + switch( pEvent->meClickAction ) + { + case ClickAction_PREVPAGE: + case ClickAction_NEXTPAGE: + case ClickAction_FIRSTPAGE: + case ClickAction_LASTPAGE: + case ClickAction_STOPPRESENTATION: + break; + case ClickAction_BOOKMARK: + if( xSetInfo->hasPropertyByName( gsBookmark ) ) + xSet->getPropertyValue( gsBookmark ) >>= pEvent->maStrBookmark; + if( getSlideNumberForBookmark( pEvent->maStrBookmark ) == -1 ) + continue; + break; + case ClickAction_DOCUMENT: + case ClickAction_SOUND: + case ClickAction_PROGRAM: + case ClickAction_MACRO: + if( xSetInfo->hasPropertyByName( gsBookmark ) ) + xSet->getPropertyValue( gsBookmark ) >>= pEvent->maStrBookmark; + break; + case ClickAction_VERB: + if( xSetInfo->hasPropertyByName( gsVerb ) ) + xSet->getPropertyValue( gsVerb ) >>= pEvent->mnVerb; + break; + default: + continue; // skip all others + } + + maShapeEventMap[ xShape ] = pEvent; + + if( mxListenerProxy.is() ) + mxListenerProxy->addShapeEventListener( xShape ); + mxShow->setShapeCursor( xShape, awt::SystemPointer::REFHAND ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::registerShapeEvents()" ); + } +} + +void SlideshowImpl::displayCurrentSlide (const bool bSkipAllMainSequenceEffects) +{ + stopSound(); + removeShapeEvents(); + + if( mpSlideController && mxShow.is() ) + { + Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), + UNO_QUERY_THROW ); + mpSlideController->displayCurrentSlide( mxShow, xDrawPages, bSkipAllMainSequenceEffects ); + registerShapeEvents(mpSlideController->getCurrentSlideNumber()); + update(); + + } + // send out page change event and notify to update all acc info for current page + if (mpViewShell) + { + sal_Int32 currentPageIndex = getCurrentSlideIndex(); + mpViewShell->fireSwitchCurrentPage(currentPageIndex); + mpViewShell->NotifyAccUpdate(); + } +} + +void SlideshowImpl::endPresentation() +{ + if( maPresSettings.mbMouseAsPen) + { + Reference< XMultiServiceFactory > xDocFactory(mpDoc->getUnoModel(), UNO_QUERY ); + if( xDocFactory.is() ) + mxShow->registerUserPaintPolygons(xDocFactory); + } + + if( !mnEndShowEvent ) + mnEndShowEvent = Application::PostUserEvent( LINK(this, SlideshowImpl, endPresentationHdl) ); +} + +IMPL_LINK_NOARG(SlideshowImpl, endPresentationHdl, void*, void) +{ + mnEndShowEvent = nullptr; + + stopSound(); + + if( mxPresentation.is() ) + mxPresentation->end(); +} + +void SAL_CALL SlideshowImpl::pause() +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + return; + + try + { + mbIsPaused = true; + if( mxShow.is() ) + { + mxShow->pause(true); + + if( mxListenerProxy.is() ) + mxListenerProxy->paused(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::pause()" ); + } +} + +void SAL_CALL SlideshowImpl::resume() +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) try + { + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK || mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END ) + { + mpShowWindow->RestartShow(); + } + else + { + mbIsPaused = false; + if( mxShow.is() ) + { + mxShow->pause(false); + update(); + + if( mxListenerProxy.is() ) + mxListenerProxy->resumed(); + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::resume()" ); + } +#ifdef ENABLE_SDREMOTE + RemoteServer::presentationStarted( this ); +#endif +} + +sal_Bool SAL_CALL SlideshowImpl::isPaused() +{ + SolarMutexGuard aSolarGuard; + return mbIsPaused; +} + +void SAL_CALL SlideshowImpl::blankScreen( sal_Int32 nColor ) +{ + SolarMutexGuard aSolarGuard; + + if( mpShowWindow && mpSlideController ) + { + if( mpShowWindow->SetBlankMode( mpSlideController->getCurrentSlideIndex(), Color(ColorTransparency, nColor) ) ) + { + pause(); + } + } +} + +// XShapeEventListener + +void SlideshowImpl::click( const Reference< XShape >& xShape ) +{ + SolarMutexGuard aSolarGuard; + + WrappedShapeEventImplPtr pEvent = maShapeEventMap[xShape]; + if( !pEvent ) + return; + + switch( pEvent->meClickAction ) + { + case ClickAction_PREVPAGE: gotoPreviousSlide(); break; + case ClickAction_NEXTPAGE: gotoNextSlide(); break; + case ClickAction_FIRSTPAGE: gotoFirstSlide(); break; + case ClickAction_LASTPAGE: gotoLastSlide(); break; + case ClickAction_STOPPRESENTATION: endPresentation(); break; + case ClickAction_BOOKMARK: + { + gotoBookmark( pEvent->maStrBookmark ); + } + break; + case ClickAction_SOUND: + { +#if HAVE_FEATURE_AVMEDIA + try + { + mxPlayer.set(avmedia::MediaWindow::createPlayer(pEvent->maStrBookmark, ""/*TODO?*/), uno::UNO_SET_THROW ); + mxPlayer->start(); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::click()" ); + } +#endif + } + break; + + case ClickAction_DOCUMENT: + { + OUString aBookmark( pEvent->maStrBookmark ); + + sal_Int32 nPos = aBookmark.indexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL( aBookmark.copy( 0, nPos+1 ) ); + OUString aName( aBookmark.copy( nPos+1 ) ); + aURL += getUiNameFromPageApiNameImpl( aName ); + aBookmark = aURL; + } + + mpDocSh->OpenBookmark( aBookmark ); + } + break; + + case ClickAction_PROGRAM: + { + INetURLObject aURL( + ::URIHelper::SmartRel2Abs( + INetURLObject(mpDocSh->GetMedium()->GetBaseURL()), + pEvent->maStrBookmark, ::URIHelper::GetMaybeFileHdl(), true, + false, INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous ) ); + + if( INetProtocol::File == aURL.GetProtocol() ) + { + SfxStringItem aUrl( SID_FILE_NAME, aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + SfxBoolItem aBrowsing( SID_BROWSE, true ); + + if (SfxViewFrame* pViewFrm = SfxViewFrame::Current()) + { + SfxUnoFrameItem aDocFrame(SID_FILLFRAME, pViewFrm->GetFrame().GetFrameInterface()); + pViewFrm->GetDispatcher()->ExecuteList( SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aUrl, &aBrowsing }, { &aDocFrame } ); + } + } + } + break; + +#if HAVE_FEATURE_SCRIPTING + case presentation::ClickAction_MACRO: + { + const OUString aMacro( pEvent->maStrBookmark ); + + if ( SfxApplication::IsXScriptURL( aMacro ) ) + { + Any aRet; + Sequence< sal_Int16 > aOutArgsIndex; + Sequence< Any > aOutArgs; + Sequence< Any >* pInArgs = new Sequence< Any >(0); + mpDocSh->CallXScript( aMacro, *pInArgs, aRet, aOutArgsIndex, aOutArgs); + } + else + { + // aMacro has the following syntax: + // "Macroname.Modulname.Libname.Documentname" or + // "Macroname.Modulname.Libname.Applicationname" + sal_Int32 nIdx{ 0 }; + const std::u16string_view aMacroName = o3tl::getToken(aMacro, 0, '.', nIdx); + const std::u16string_view aModulName = o3tl::getToken(aMacro, 0, '.', nIdx); + + // todo: is the limitation still given that only + // Modulname+Macroname can be used here? + OUString aExecMacro = OUString::Concat(aModulName) + "." + aMacroName; + mpDocSh->GetBasic()->Call(aExecMacro); + } + } + break; +#endif + + case ClickAction_VERB: + { + // todo, better do it async? + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape); + SdrOle2Obj* pOleObject = dynamic_cast< SdrOle2Obj* >(pObj); + if (pOleObject && mpViewShell ) + mpViewShell->ActivateObject(pOleObject, pEvent->mnVerb); + } + break; + default: + break; + } +} + +sal_Int32 SlideshowImpl::getSlideNumberForBookmark( const OUString& rStrBookmark ) +{ + bool bIsMasterPage; + OUString aBookmark = getUiNameFromPageApiNameImpl( rStrBookmark ); + sal_uInt16 nPgNum = mpDoc->GetPageByName( aBookmark, bIsMasterPage ); + + if( nPgNum == SDRPAGE_NOTFOUND ) + { + // Is the bookmark an object? + SdrObject* pObj = mpDoc->GetObj( aBookmark ); + + if( pObj ) + { + nPgNum = pObj->getSdrPageFromSdrObject()->GetPageNum(); + bIsMasterPage = pObj->getSdrPageFromSdrObject()->IsMasterPage(); + } + } + + if( (nPgNum == SDRPAGE_NOTFOUND) || bIsMasterPage || static_cast<SdPage*>(mpDoc->GetPage(nPgNum))->GetPageKind() != PageKind::Standard ) + return -1; + + return ( nPgNum - 1) >> 1; +} + +void SlideshowImpl::contextMenuShow(const css::awt::Point& point) +{ + maPopupMousePos = { point.X, point.Y }; + mnContextMenuEvent = Application::PostUserEvent(LINK(this, SlideshowImpl, ContextMenuHdl)); +} + +void SlideshowImpl::hyperLinkClicked( OUString const& aHyperLink ) +{ + OUString aBookmark( aHyperLink ); + + sal_Int32 nPos = aBookmark.indexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL( aBookmark.copy( 0, nPos+1 ) ); + OUString aName( aBookmark.copy( nPos+1 ) ); + aURL += getUiNameFromPageApiNameImpl( aName ); + aBookmark = aURL; + } + + mpDocSh->OpenBookmark( aBookmark ); +} + +void SlideshowImpl::displaySlideNumber( sal_Int32 nSlideNumber ) +{ + if( mpSlideController ) + { + if( mpSlideController->jumpToSlideNumber( nSlideNumber ) ) + { + displayCurrentSlide(); + } + } +} + +/** nSlideIndex == -1 displays current slide again */ +void SlideshowImpl::displaySlideIndex( sal_Int32 nSlideIndex ) +{ + if( mpSlideController ) + { + if( (nSlideIndex == -1) || mpSlideController->jumpToSlideIndex( nSlideIndex ) ) + { + displayCurrentSlide(); + } + } +} + +void SlideshowImpl::jumpToBookmark( const OUString& sBookmark ) +{ + sal_Int32 nSlideNumber = getSlideNumberForBookmark( sBookmark ); + if( nSlideNumber != -1 ) + displaySlideNumber( nSlideNumber ); +} + +sal_Int32 SlideshowImpl::getCurrentSlideNumber() const +{ + return mpSlideController ? mpSlideController->getCurrentSlideNumber() : -1; +} + +sal_Bool SAL_CALL SlideshowImpl::isEndless() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbEndless; +} + +void SlideshowImpl::update() +{ + startUpdateTimer(); +} + +void SlideshowImpl::startUpdateTimer() +{ + SolarMutexGuard aSolarGuard; + maUpdateTimer.SetTimeout( 0 ); + maUpdateTimer.Start(); +} + +/** this timer is called 20ms after a new slide was displayed. + This is used to unfreeze user input that was disabled after + slide change to skip input that was buffered during slide + transition preparation */ +IMPL_LINK_NOARG(SlideshowImpl, ReadyForNextInputHdl, Timer *, void) +{ + mbInputFreeze = false; +} + +/** if I catch someone someday who calls this method by hand + and not by using the timer, I will personally punish this + person seriously, even if this person is me. +*/ +IMPL_LINK_NOARG(SlideshowImpl, updateHdl, Timer *, void) +{ + updateSlideShow(); +} + +void SlideshowImpl::updateSlideShow() +{ + // prevent me from deletion when recursing (App::EnableYieldMode does) + const rtl::Reference<SlideshowImpl> xKeepAlive(this); + + Reference< XSlideShow > xShow( mxShow ); + if ( ! xShow.is()) + return; + + try + { + double fUpdate = 0.0; + if( !xShow->update(fUpdate) ) + fUpdate = -1.0; + + if (mxShow.is() && (fUpdate >= 0.0)) + { + if (::basegfx::fTools::equalZero(fUpdate)) + { + // Make sure idle tasks don't starve when we don't have to wait. + // Don't process any events generated after invoking the function. + Application::Reschedule(/*bHandleAllCurrentEvents=*/true); + } + else + { + // Avoid busy loop when the previous call to update() + // returns a small positive number but not 0 (which is + // handled above). Also, make sure that calls to update() + // have a minimum frequency. + // => Allow up to 60 frames per second. Call at least once + // every 4 seconds. + const static sal_Int32 nMaximumFrameCount (60); + const static double nMinimumTimeout (1.0 / nMaximumFrameCount); + const static double nMaximumTimeout (4.0); + fUpdate = std::clamp(fUpdate, nMinimumTimeout, nMaximumTimeout); + + // Make sure that the maximum frame count has not been set + // too high (only then conversion to milliseconds and long + // integer may lead to zero value.) + OSL_ASSERT(static_cast<sal_uLong>(fUpdate * 1000.0) > 0); + } + + // Use our high resolution timers for the asynchronous callback. + maUpdateTimer.SetTimeout(static_cast<sal_uLong>(fUpdate * 1000.0)); + maUpdateTimer.Start(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::updateSlideShow()" ); + } +} + +bool SlideshowImpl::keyInput(const KeyEvent& rKEvt) +{ + if( !mxShow.is() || mbInputFreeze ) + return false; + + bool bRet = true; + + try + { + const int nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch( nKeyCode ) + { + case awt::Key::CONTEXTMENU: + if( !mnContextMenuEvent ) + { + if( mpShowWindow ) + maPopupMousePos = mpShowWindow->GetPointerState().maPos; + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + } + break; + + // cancel show + case KEY_ESCAPE: + case KEY_SUBTRACT: + // in case the user cancels the presentation, switch to current slide + // in edit mode + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + endPresentation(); + break; + + // advance show + case KEY_PAGEDOWN: + if(rKEvt.GetKeyCode().IsMod2()) + { + gotoNextSlide(); + break; + } + [[fallthrough]]; + case KEY_SPACE: + case KEY_RIGHT: + case KEY_DOWN: + case KEY_XF86FORWARD: + gotoNextEffect(); + break; + + case KEY_RETURN: + { + if( !maCharBuffer.isEmpty() ) + { + if( mpSlideController ) + { + if( mpSlideController->jumpToSlideNumber( maCharBuffer.toInt32() - 1 ) ) + displayCurrentSlide(); + } + maCharBuffer.clear(); + } + else + { + gotoNextEffect(); + } + } + break; + + // numeric: add to buffer + case KEY_0: + case KEY_1: + case KEY_2: + case KEY_3: + case KEY_4: + case KEY_5: + case KEY_6: + case KEY_7: + case KEY_8: + case KEY_9: + maCharBuffer += OUStringChar( rKEvt.GetCharCode() ); + break; + + case KEY_PAGEUP: + if(rKEvt.GetKeyCode().IsMod2()) + { + gotoPreviousSlide(); + break; + } + [[fallthrough]]; + case KEY_LEFT: + case KEY_UP: + case KEY_BACKSPACE: + case KEY_XF86BACK: + gotoPreviousEffect(); + break; + + case KEY_P: + setUsePen( !mbUsePen ); + break; + + // tdf#149351 Ctrl+A disables pointer as pen mode + case KEY_A: + if(rKEvt.GetKeyCode().IsMod1()) + { + setUsePen( false ); + break; + } + break; + + case KEY_E: + setEraseAllInk( true ); + updateSlideShow(); + break; + + case KEY_HOME: + gotoFirstSlide(); + break; + + case KEY_END: + gotoLastSlide(); + break; + + case KEY_B: + case KEY_W: + case KEY_POINT: + case KEY_COMMA: + { + blankScreen( ((nKeyCode == KEY_W ) || (nKeyCode == KEY_COMMA)) ? 0x00ffffff : 0x00000000 ); + } + break; + + default: + bRet = false; + break; + } + } + catch( Exception& ) + { + bRet = false; + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::keyInput()" ); + } + + return bRet; +} + +IMPL_LINK( SlideshowImpl, EventListenerHdl, VclSimpleEvent&, rSimpleEvent, void ) +{ + if( !mxShow.is() || mbInputFreeze ) + return; + + if( !((rSimpleEvent.GetId() == VclEventId::WindowCommand) && static_cast<VclWindowEvent*>(&rSimpleEvent)->GetData()) ) + return; + + const CommandEvent& rEvent = *static_cast<const CommandEvent*>(static_cast<VclWindowEvent*>(&rSimpleEvent)->GetData()); + + if( rEvent.GetCommand() != CommandEventId::Media ) + return; + + CommandMediaData* pMediaData = rEvent.GetMediaData(); + pMediaData->SetPassThroughToOS(false); + switch (pMediaData->GetMediaId()) + { +#if defined( MACOSX ) + case MediaCommand::Menu: + if( !mnContextMenuEvent ) + { + if( mpShowWindow ) + maPopupMousePos = mpShowWindow->GetPointerState().maPos; + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + } + break; + case MediaCommand::VolumeDown: + gotoPreviousSlide(); + break; + case MediaCommand::VolumeUp: + gotoNextEffect(); + break; +#endif + case MediaCommand::NextTrack: + gotoNextEffect(); + break; + case MediaCommand::Pause: + if( !mbIsPaused ) + blankScreen(0); + break; + case MediaCommand::Play: + if( mbIsPaused ) + resume(); + break; + + case MediaCommand::PlayPause: + if( mbIsPaused ) + resume(); + else + blankScreen(0); + break; + case MediaCommand::PreviousTrack: + gotoPreviousSlide(); + break; + case MediaCommand::NextTrackHold: + gotoLastSlide(); + break; + + case MediaCommand::Rewind: + gotoFirstSlide(); + break; + case MediaCommand::Stop: + // in case the user cancels the presentation, switch to current slide + // in edit mode + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + endPresentation(); + break; + default: + pMediaData->SetPassThroughToOS(true); + break; + } +} + +void SlideshowImpl::mouseButtonUp(const MouseEvent& rMEvt) +{ + if( rMEvt.IsRight() && !mnContextMenuEvent ) + { + maPopupMousePos = rMEvt.GetPosPixel(); + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + } +} + +IMPL_LINK_NOARG(SlideshowImpl, ContextMenuHdl, void*, void) +{ + mnContextMenuEvent = nullptr; + + if (mpSlideController == nullptr) + return; + + mbWasPaused = mbIsPaused; + if( !mbWasPaused ) + pause(); + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/simpress/ui/slidecontextmenu.ui")); + std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu")); + OUString sNextImage(BMP_MENU_NEXT), sPrevImage(BMP_MENU_PREV); + xMenu->insert(0, "next", SdResId(RID_SVXSTR_MENU_NEXT), &sNextImage, nullptr, nullptr, TRISTATE_INDET); + xMenu->insert(1, "prev", SdResId(RID_SVXSTR_MENU_PREV), &sPrevImage, nullptr, nullptr, TRISTATE_INDET); + + // Adding button to display if in Pen mode + xMenu->set_active("pen", mbUsePen); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + xMenu->set_visible("next", mpSlideController->getNextSlideIndex() != -1); + xMenu->set_visible("prev", (mpSlideController->getPreviousSlideIndex() != -1 ) || (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK)); + xMenu->set_visible("edit", mpViewShell->GetDoc()->IsStartWithPresentation()); + + std::unique_ptr<weld::Menu> xPageMenu(xBuilder->weld_menu("gotomenu")); + OUString sFirstImage(BMP_MENU_FIRST), sLastImage(BMP_MENU_LAST); + xPageMenu->insert(0, "first", SdResId(RID_SVXSTR_MENU_FIRST), &sFirstImage, nullptr, nullptr, TRISTATE_INDET); + xPageMenu->insert(1, "last", SdResId(RID_SVXSTR_MENU_LAST), &sLastImage, nullptr, nullptr, TRISTATE_INDET); + + // populate slide goto list + const sal_Int32 nPageNumberCount = mpSlideController->getSlideNumberCount(); + if( nPageNumberCount <= 1 ) + { + xMenu->set_visible("goto", false); + } + else + { + sal_Int32 nCurrentSlideNumber = mpSlideController->getCurrentSlideNumber(); + if( (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + nCurrentSlideNumber = -1; + + xPageMenu->set_visible("first", mpSlideController->getSlideNumber(0) != nCurrentSlideNumber); + xPageMenu->set_visible("last", mpSlideController->getSlideNumber(mpSlideController->getSlideIndexCount() - 1) != nCurrentSlideNumber); + + sal_Int32 nPageNumber; + + for( nPageNumber = 0; nPageNumber < nPageNumberCount; nPageNumber++ ) + { + if( mpSlideController->isVisibleSlideNumber( nPageNumber ) ) + { + SdPage* pPage = mpDoc->GetSdPage(static_cast<sal_uInt16>(nPageNumber), PageKind::Standard); + if (pPage) + { + OUString sId(OUString::number(CM_SLIDES + nPageNumber)); + xPageMenu->append_check(sId, pPage->GetName()); + if (nPageNumber == nCurrentSlideNumber) + xPageMenu->set_active(sId, true); + } + } + } + } + + std::unique_ptr<weld::Menu> xBlankMenu(xBuilder->weld_menu("screenmenu")); + + if (mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK) + { + xBlankMenu->set_active((mpShowWindow->GetBlankColor() == COL_WHITE) ? "white" : "black", true); + } + + std::unique_ptr<weld::Menu> xWidthMenu(xBuilder->weld_menu("widthmenu")); + + // populate color width list + sal_Int32 nIterator; + double nWidth; + + nWidth = 4.0; + for( nIterator = 1; nIterator < 6; nIterator++) + { + switch(nIterator) + { + case 1: + nWidth = 4.0; + break; + case 2: + nWidth = 100.0; + break; + case 3: + nWidth = 150.0; + break; + case 4: + nWidth = 200.0; + break; + case 5: + nWidth = 400.0; + break; + default: + break; + } + + if (nWidth == mdUserPaintStrokeWidth) + xWidthMenu->set_active(OUString::number(nWidth), true); + } + + ::tools::Rectangle aRect(maPopupMousePos, Size(1,1)); + weld::Window* pParent = weld::GetPopupParent(*mpShowWindow, aRect); + ContextMenuSelectHdl(xMenu->popup_at_rect(pParent, aRect)); + + if( mxView.is() ) + mxView->ignoreNextMouseReleased(); + + if( !mbWasPaused ) + resume(); +} + +void SlideshowImpl::ContextMenuSelectHdl(std::u16string_view rMenuId) +{ + if (rMenuId == u"prev") + { + gotoPreviousSlide(); + mbWasPaused = false; + } + else if(rMenuId == u"next") + { + gotoNextSlide(); + mbWasPaused = false; + } + else if (rMenuId == u"first") + { + gotoFirstSlide(); + mbWasPaused = false; + } + else if (rMenuId == u"last") + { + gotoLastSlide(); + mbWasPaused = false; + } + else if (rMenuId == u"black" || rMenuId == u"white") + { + const Color aBlankColor(rMenuId == u"white" ? COL_WHITE : COL_BLACK); + if( mbWasPaused ) + { + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK ) + { + if( mpShowWindow->GetBlankColor() == aBlankColor ) + { + mbWasPaused = false; + mpShowWindow->RestartShow(); + return; + } + } + mpShowWindow->RestartShow(); + } + if( mpShowWindow->SetBlankMode( mpSlideController->getCurrentSlideIndex(), aBlankColor ) ) + { + pause(); + mbWasPaused = true; + } + } + else if (rMenuId == u"color") + { + //Open a color picker based on SvColorDialog + ::Color aColor( ColorTransparency, mnUserPaintColor ); + SvColorDialog aColorDlg; + aColorDlg.SetColor( aColor ); + + if (aColorDlg.Execute(mpShowWindow->GetFrameWeld())) + { + aColor = aColorDlg.GetColor(); + setPenColor(sal_Int32(aColor)); + } + mbWasPaused = false; + } + else if (rMenuId == u"4") + { + setPenWidth(4.0); + mbWasPaused = false; + } + else if (rMenuId == u"100") + { + setPenWidth(100.0); + mbWasPaused = false; + } + else if (rMenuId == u"150") + { + setPenWidth(150.0); + mbWasPaused = false; + } + else if (rMenuId == u"200") + { + setPenWidth(200.0); + mbWasPaused = false; + } + else if (rMenuId == u"400") + { + setPenWidth(400.0); + mbWasPaused = false; + } + else if (rMenuId == u"erase") + { + setEraseAllInk(true); + mbWasPaused = false; + } + else if (rMenuId == u"pen") + { + setUsePen(!mbUsePen); + mbWasPaused = false; + } + else if (rMenuId == u"edit") + { + // When in autoplay mode (pps/ppsx), offer editing of the presentation + // Turn autostart off, else Impress will close when exiting the Presentation + mpViewShell->GetDoc()->SetExitAfterPresenting(false); + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + { + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + } + endPresentation(); + } + else if (rMenuId == u"end") + { + // in case the user cancels the presentation, switch to current slide + // in edit mode + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + { + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + } + endPresentation(); + } + else if (!rMenuId.empty()) + { + sal_Int32 nPageNumber = o3tl::toInt32(rMenuId) - CM_SLIDES; + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow( nPageNumber ); + } + else if( nPageNumber != mpSlideController->getCurrentSlideNumber() ) + { + displaySlideNumber( nPageNumber ); + } + mbWasPaused = false; + } +} + +Reference< XSlideShow > SlideshowImpl::createSlideShow() +{ + Reference< XSlideShow > xShow; + + try + { + Reference< uno::XComponentContext > xContext = + ::comphelper::getProcessComponentContext(); + + xShow.set( presentation::SlideShow::create(xContext), UNO_SET_THROW ); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::createSlideShow()" ); + } + + return xShow; +} + +void SlideshowImpl::createSlideList( bool bAll, std::u16string_view rPresSlide ) +{ + const sal_uInt16 nSlideCount = mpDoc->GetSdPageCount( PageKind::Standard ); + + if( !nSlideCount ) + return; + + SdCustomShow* pCustomShow; + + if( mpDoc->GetCustomShowList() && maPresSettings.mbCustomShow ) + pCustomShow = mpDoc->GetCustomShowList()->GetCurObject(); + else + pCustomShow = nullptr; + + // create animation slide controller + AnimationSlideController::Mode eMode = + ( pCustomShow && !pCustomShow->PagesVector().empty() ) ? AnimationSlideController::CUSTOM : + (bAll ? AnimationSlideController::ALL : AnimationSlideController::FROM); + + Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), UNO_QUERY_THROW ); + Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW ); + mpSlideController = std::make_shared<AnimationSlideController>( xSlides, eMode ); + + if( eMode != AnimationSlideController::CUSTOM ) + { + sal_Int32 nFirstVisibleSlide = 0; + + // normal presentation + if( !rPresSlide.empty() ) + { + sal_Int32 nSlide; + bool bTakeNextAvailable = false; + + for( nSlide = 0, nFirstVisibleSlide = -1; + ( nSlide < nSlideCount ) && ( -1 == nFirstVisibleSlide ); nSlide++ ) + { + SdPage* pTestSlide = mpDoc->GetSdPage( static_cast<sal_uInt16>(nSlide), PageKind::Standard ); + + if( pTestSlide->GetName() == rPresSlide ) + { + if( pTestSlide->IsExcluded() ) + bTakeNextAvailable = true; + else + nFirstVisibleSlide = nSlide; + } + else if( bTakeNextAvailable && !pTestSlide->IsExcluded() ) + nFirstVisibleSlide = nSlide; + } + + if( -1 == nFirstVisibleSlide ) + nFirstVisibleSlide = 0; + } + + for( sal_Int32 i = 0; i < nSlideCount; i++ ) + { + bool bVisible = ! mpDoc->GetSdPage( static_cast<sal_uInt16>(i), PageKind::Standard )->IsExcluded(); + if( bVisible || (eMode == AnimationSlideController::ALL) ) + mpSlideController->insertSlideNumber( i, bVisible ); + } + + mpSlideController->setStartSlideNumber( nFirstVisibleSlide ); + } + else + { + if( meAnimationMode != ANIMATIONMODE_SHOW && !rPresSlide.empty() ) + { + sal_Int32 nSlide; + for( nSlide = 0; nSlide < nSlideCount; nSlide++ ) + if( rPresSlide == mpDoc->GetSdPage( static_cast<sal_uInt16>(nSlide), PageKind::Standard )->GetName() ) + break; + + if( nSlide < nSlideCount ) + mpSlideController->insertSlideNumber( static_cast<sal_uInt16>(nSlide) ); + } + + for( const auto& rpPage : pCustomShow->PagesVector() ) + { + const sal_uInt16 nSdSlide = ( rpPage->GetPageNum() - 1 ) / 2; + + if( ! mpDoc->GetSdPage( nSdSlide, PageKind::Standard )->IsExcluded()) + mpSlideController->insertSlideNumber( nSdSlide ); + } + } +} + +typedef sal_uInt16 (*FncGetChildWindowId)(); + +const FncGetChildWindowId aShowChildren[] = +{ + &AnimationChildWindow::GetChildWindowId, + &Svx3DChildWindow::GetChildWindowId, + &SvxFontWorkChildWindow::GetChildWindowId, + &SvxColorChildWindow::GetChildWindowId, + &SvxSearchDialogWrapper::GetChildWindowId, + &SvxBmpMaskChildWindow::GetChildWindowId, + &SvxIMapDlgChildWindow::GetChildWindowId, + &SvxHlinkDlgWrapper::GetChildWindowId, + &SfxInfoBarContainerChild::GetChildWindowId +}; + +void SlideshowImpl::hideChildWindows() +{ + mnChildMask = 0; + + if( ANIMATIONMODE_SHOW != meAnimationMode ) + return; + + SfxViewFrame* pViewFrame = getViewFrame(); + + if( !pViewFrame ) + return; + + for( sal_uLong i = 0; i < SAL_N_ELEMENTS( aShowChildren ); i++ ) + { + const sal_uInt16 nId = ( *aShowChildren[ i ] )(); + + if( pViewFrame->GetChildWindow( nId ) ) + { + pViewFrame->SetChildWindow( nId, false ); + mnChildMask |= ::tools::ULong(1) << i; + } + } +} + +void SlideshowImpl::showChildWindows() +{ + if( ANIMATIONMODE_SHOW == meAnimationMode ) + { + SfxViewFrame* pViewFrame = getViewFrame(); + if( pViewFrame ) + { + for( sal_uLong i = 0; i < SAL_N_ELEMENTS(aShowChildren); i++ ) + { + if( mnChildMask & ( ::tools::ULong(1) << i ) ) + pViewFrame->SetChildWindow( ( *aShowChildren[ i ] )(), true ); + } + } + } +} + +SfxViewFrame* SlideshowImpl::getViewFrame() const +{ + return mpViewShell ? mpViewShell->GetViewFrame() : nullptr; +} + +SfxDispatcher* SlideshowImpl::getDispatcher() const +{ + return (mpViewShell && mpViewShell->GetViewFrame()) ? mpViewShell->GetViewFrame()->GetDispatcher() : nullptr; +} + +SfxBindings* SlideshowImpl::getBindings() const +{ + return (mpViewShell && mpViewShell->GetViewFrame()) ? &mpViewShell->GetViewFrame()->GetBindings() : nullptr; +} + +void SlideshowImpl::resize( const Size& rSize ) +{ + maPresSize = rSize; + + if(mpShowWindow) + { + mpShowWindow->SetSizePixel( maPresSize ); + mpShowWindow->Show(); + } + + if( mxView.is() ) try + { + awt::WindowEvent aEvt; + mxView->windowResized(aEvt); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::resize()" ); + } +} + +void SlideshowImpl::setActiveXToolbarsVisible( bool bVisible ) +{ + // in case of ActiveX control the toolbars should not be visible if slide show runs in window mode + // actually it runs always in window mode in case of ActiveX control + if ( !(!maPresSettings.mbFullScreen && mpDocSh && mpDocSh->GetMedium()) ) + return; + + const SfxBoolItem* pItem = mpDocSh->GetMedium()->GetItemSet().GetItem(SID_VIEWONLY, false); + if ( !(pItem && pItem->GetValue()) ) + return; + + // this is a plugin/activex mode, no toolbars should be visible during slide show + // after the end of slide show they should be visible again + SfxViewFrame* pViewFrame = getViewFrame(); + if( !pViewFrame ) + return; + + try + { + Reference< frame::XLayoutManager > xLayoutManager; + Reference< beans::XPropertySet > xFrameProps( pViewFrame->GetFrame().GetFrameInterface(), UNO_QUERY_THROW ); + if ( ( xFrameProps->getPropertyValue( "LayoutManager" ) + >>= xLayoutManager ) + && xLayoutManager.is() ) + { + xLayoutManager->setVisible( bVisible ); + } + } + catch( uno::Exception& ) + {} +} + +void SAL_CALL SlideshowImpl::activate() +{ + SolarMutexGuard aSolarGuard; + + maDeactivateTimer.Stop(); + + if( mbActive || !mxShow.is() ) + return; + + mbActive = true; + + if( ANIMATIONMODE_SHOW == meAnimationMode ) + { + if( mbAutoSaveWasOn ) + setAutoSaveState( false ); + + if( mpShowWindow ) + { + SfxViewFrame* pViewFrame = getViewFrame(); + SfxDispatcher* pDispatcher = pViewFrame ? pViewFrame->GetDispatcher() : nullptr; + + hideChildWindows(); + + if( pDispatcher ) + { + // filter all forbidden slots + pDispatcher->SetSlotFilter( SfxSlotFilterState::ENABLED, pAllowed ); + } + + if( getBindings() ) + getBindings()->InvalidateAll(true); + + mpShowWindow->GrabFocus(); + } + } + + resume(); +} + +void SAL_CALL SlideshowImpl::deactivate() +{ + SolarMutexGuard aSolarGuard; + + if( mbActive && mxShow.is() ) + { + maDeactivateTimer.Start(); + } +} + +IMPL_LINK_NOARG(SlideshowImpl, deactivateHdl, Timer *, void) +{ + if( !(mbActive && mxShow.is()) ) + return; + + mbActive = false; + + pause(); + + if( ANIMATIONMODE_SHOW == meAnimationMode ) + { + if( mbAutoSaveWasOn ) + setAutoSaveState( true ); + + if( mpShowWindow ) + { + showChildWindows(); + } + } +} + +sal_Bool SAL_CALL SlideshowImpl::isActive() +{ + SolarMutexGuard aSolarGuard; + return mbActive; +} + +void SlideshowImpl::setAutoSaveState( bool bOn) +{ + try + { + uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() ); + + uno::Reference< util::XURLTransformer > xParser(util::URLTransformer::create(xContext)); + util::URL aURL; + aURL.Complete = "vnd.sun.star.autorecovery:/setAutoSaveState"; + xParser->parseStrict(aURL); + + Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue("AutoSaveState", bOn) }; + + uno::Reference< frame::XDispatch > xAutoSave = frame::theAutoRecovery::get(xContext); + xAutoSave->dispatch(aURL, aArgs); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::setAutoSaveState()"); + } +} + +Reference< XDrawPage > SAL_CALL SlideshowImpl::getCurrentSlide() +{ + SolarMutexGuard aSolarGuard; + + Reference< XDrawPage > xSlide; + if( mxShow.is() && mpSlideController ) + { + sal_Int32 nSlide = getCurrentSlideNumber(); + if( (nSlide >= 0) && (nSlide < mpSlideController->getSlideNumberCount() ) ) + xSlide = mpSlideController->getSlideByNumber( nSlide ); + } + + return xSlide; +} + +sal_Int32 SAL_CALL SlideshowImpl::getNextSlideIndex() +{ + SolarMutexGuard aSolarGuard; + + if( mxShow.is() ) + { + return mpSlideController->getNextSlideIndex(); + } + else + { + return -1; + } +} + +sal_Int32 SAL_CALL SlideshowImpl::getCurrentSlideIndex() +{ + return mpSlideController ? mpSlideController->getCurrentSlideIndex() : -1; +} + +// css::presentation::XSlideShowController: + +::sal_Int32 SAL_CALL SlideshowImpl::getSlideCount() +{ + return mpSlideController ? mpSlideController->getSlideIndexCount() : 0; +} + +Reference< XDrawPage > SAL_CALL SlideshowImpl::getSlideByIndex(::sal_Int32 Index) +{ + if ((mpSlideController == nullptr) || (Index < 0) + || (Index >= mpSlideController->getSlideIndexCount())) + throw IndexOutOfBoundsException(); + + return mpSlideController->getSlideByNumber( mpSlideController->getSlideNumber( Index ) ); +} + +sal_Bool SAL_CALL SlideshowImpl::getAlwaysOnTop() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbAlwaysOnTop; +} + +void SAL_CALL SlideshowImpl::setAlwaysOnTop( sal_Bool bAlways ) +{ + SolarMutexGuard aSolarGuard; + if( maPresSettings.mbAlwaysOnTop != bool(bAlways) ) + { + maPresSettings.mbAlwaysOnTop = bAlways; + // todo, can this be changed while running? + } +} + +sal_Bool SAL_CALL SlideshowImpl::isFullScreen() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbFullScreen; +} + +sal_Bool SAL_CALL SlideshowImpl::getMouseVisible() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbMouseVisible; +} + +void SAL_CALL SlideshowImpl::setMouseVisible( sal_Bool bVisible ) +{ + SolarMutexGuard aSolarGuard; + if( maPresSettings.mbMouseVisible != bool(bVisible) ) + { + maPresSettings.mbMouseVisible = bVisible; + if( mpShowWindow ) + mpShowWindow->SetMouseAutoHide( !maPresSettings.mbMouseVisible ); + } +} + +sal_Bool SAL_CALL SlideshowImpl::getUsePen() +{ + SolarMutexGuard aSolarGuard; + return mbUsePen; +} + +void SAL_CALL SlideshowImpl::setUsePen( sal_Bool bMouseAsPen ) +{ + SolarMutexGuard aSolarGuard; + mbUsePen = bMouseAsPen; + if( !mxShow.is() ) + return; + + try + { + // For Pencolor; + Any aValue; + if( mbUsePen ) + aValue <<= mnUserPaintColor; + beans::PropertyValue aPenProp; + aPenProp.Name = "UserPaintColor"; + aPenProp.Value = aValue; + mxShow->setProperty( aPenProp ); + + //for StrokeWidth : + if( mbUsePen ) + { + beans::PropertyValue aPenPropWidth; + aPenPropWidth.Name = "UserPaintStrokeWidth"; + aPenPropWidth.Value <<= mdUserPaintStrokeWidth; + mxShow->setProperty( aPenPropWidth ); + + // for Pen Mode + beans::PropertyValue aPenPropSwitchPenMode; + aPenPropSwitchPenMode.Name = "SwitchPenMode"; + aPenPropSwitchPenMode.Value <<= true; + mxShow->setProperty( aPenPropSwitchPenMode ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::setUsePen()" ); + } +} + +double SAL_CALL SlideshowImpl::getPenWidth() +{ + SolarMutexGuard aSolarGuard; + return mdUserPaintStrokeWidth; +} + +void SAL_CALL SlideshowImpl::setPenWidth( double dStrokeWidth ) +{ + SolarMutexGuard aSolarGuard; + mdUserPaintStrokeWidth = dStrokeWidth; + setUsePen( true ); // enable pen mode, update color and width +} + +sal_Int32 SAL_CALL SlideshowImpl::getPenColor() +{ + SolarMutexGuard aSolarGuard; + return mnUserPaintColor; +} + +void SAL_CALL SlideshowImpl::setPenColor( sal_Int32 nColor ) +{ + SolarMutexGuard aSolarGuard; + mnUserPaintColor = nColor; + setUsePen( true ); // enable pen mode, update color +} + +void SAL_CALL SlideshowImpl::setEraseAllInk(sal_Bool bEraseAllInk) +{ + if( !bEraseAllInk ) + return; + + SolarMutexGuard aSolarGuard; + if( !mxShow.is() ) + return; + + try + { + beans::PropertyValue aPenPropEraseAllInk; + aPenPropEraseAllInk.Name = "EraseAllInk"; + aPenPropEraseAllInk.Value <<= bEraseAllInk; + mxShow->setProperty( aPenPropEraseAllInk ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd.slideshow", "sd::SlideshowImpl::setEraseAllInk()" ); + } +} + +// XSlideShowController Methods +sal_Bool SAL_CALL SlideshowImpl::isRunning( ) +{ + SolarMutexGuard aSolarGuard; + return mxShow.is(); +} + +void SAL_CALL SlideshowImpl::gotoNextEffect( ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mxShow.is() && mpSlideController && mpShowWindow) ) + return; + + if( mbIsPaused && mpShowWindow->GetShowWindowMode() != SHOWWINDOWMODE_END ) + resume(); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( eMode == SHOWWINDOWMODE_END ) + { + endPresentation(); + } + else if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow(); + } + else + { + mxShow->nextEffect(); + update(); + } +} + +void SAL_CALL SlideshowImpl::gotoPreviousEffect( ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mxShow.is() && mpSlideController && mpShowWindow) ) + return; + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) || mbIsPaused ) + { + resume(); + } + else + { + mxShow->previousEffect(); + update(); + } +} + +void SAL_CALL SlideshowImpl::gotoFirstSlide( ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mpShowWindow && mpSlideController) ) + return; + + if( mbIsPaused ) + resume(); + + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END ) + { + if( mpSlideController->getSlideIndexCount() ) + mpShowWindow->RestartShow( 0); + } + else + { + displaySlideIndex( 0 ); + } +} + +void SAL_CALL SlideshowImpl::gotoNextSlide( ) +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + resume(); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow(); + } + else + { + // if this is a show, ignore user inputs and + // start 20ms timer to reenable inputs to filter + // buffered inputs during slide transition + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + mbInputFreeze = true; + maInputFreezeTimer.Start(); + } + + if( mpSlideController ) + { + if( mpSlideController->nextSlide() ) + { + displayCurrentSlide(); + } + else + { + stopSound(); + + if( meAnimationMode == ANIMATIONMODE_PREVIEW ) + { + endPresentation(); + } + else if( maPresSettings.mbEndless ) + { + if( maPresSettings.mnPauseTimeout ) + { + if( mpShowWindow ) + { + if ( maPresSettings.mbShowPauseLogo ) + { + Graphic aGraphic(SfxApplication::GetApplicationLogo(360)); + mpShowWindow->SetPauseMode( maPresSettings.mnPauseTimeout, &aGraphic ); + } + else + mpShowWindow->SetPauseMode( maPresSettings.mnPauseTimeout ); + } + } + else + { + displaySlideIndex( 0 ); + } + } + else + { + if( mpShowWindow ) + { + mpShowWindow->SetEndMode(); + if( !mpViewShell->GetDoc()->IsStartWithPresentation() ) + pause(); + } + } + } + } + } +} + +void SAL_CALL SlideshowImpl::gotoPreviousSlide( ) +{ + gotoPreviousSlide(false); +} + +void SlideshowImpl::gotoPreviousSlide (const bool bSkipAllMainSequenceEffects) +{ + SolarMutexGuard aSolarGuard; + + if( !(mxShow.is() && mpSlideController) ) + return; + + try + { + if( mbIsPaused ) + resume(); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( eMode == SHOWWINDOWMODE_END ) + { + mpShowWindow->RestartShow( mpSlideController->getCurrentSlideIndex() ); + } + else if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow(); + } + else + { + if( mpSlideController->previousSlide()) + displayCurrentSlide(bSkipAllMainSequenceEffects); + else if (bSkipAllMainSequenceEffects) + { + // We could not go to the previous slide (probably because + // the current slide is already the first one). We still + // have to call displayCurrentSlide because the calling + // slideshow can not determine whether there is a previous + // slide or not and has already prepared for a slide change. + // This slide change has to be completed now, even when + // changing to the same slide. + // Note that in this special case we do NOT pass + // bSkipAllMainSequenceEffects because we display the same + // slide as before and do not want to show all its effects. + displayCurrentSlide(); + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::gotoPreviousSlide()" ); + } +} + +void SAL_CALL SlideshowImpl::gotoLastSlide() +{ + SolarMutexGuard aSolarGuard; + + if( !mpSlideController ) + return; + + if( mbIsPaused ) + resume(); + + const sal_Int32 nLastSlideIndex = mpSlideController->getSlideIndexCount() - 1; + if( nLastSlideIndex >= 0 ) + { + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END ) + { + mpShowWindow->RestartShow( nLastSlideIndex ); + } + else + { + displaySlideIndex( nLastSlideIndex ); + } + } +} + +void SAL_CALL SlideshowImpl::gotoBookmark( const OUString& rBookmark ) +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + resume(); + + sal_Int32 nSlideNumber = getSlideNumberForBookmark( rBookmark ); + if( nSlideNumber != -1 ) + displaySlideNumber( nSlideNumber ); +} + +void SAL_CALL SlideshowImpl::gotoSlide( const Reference< XDrawPage >& xSlide ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mpSlideController && xSlide.is()) ) + return; + + if( mbIsPaused ) + resume(); + + const sal_Int32 nSlideCount = mpSlideController->getSlideNumberCount(); + for( sal_Int32 nSlide = 0; nSlide < nSlideCount; nSlide++ ) + { + if( mpSlideController->getSlideByNumber( nSlide ) == xSlide ) + { + displaySlideNumber( nSlide ); + } + } +} + +void SAL_CALL SlideshowImpl::gotoSlideIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + resume(); + + displaySlideIndex( nIndex ); +} + +void SAL_CALL SlideshowImpl::stopSound( ) +{ + SolarMutexGuard aSolarGuard; + + try + { + if( mxPlayer.is() ) + { + mxPlayer->stop(); + mxPlayer.clear(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::stopSound()" ); + } +} + +// XIndexAccess + +::sal_Int32 SAL_CALL SlideshowImpl::getCount( ) +{ + return getSlideCount(); +} + +css::uno::Any SAL_CALL SlideshowImpl::getByIndex( ::sal_Int32 Index ) +{ + return Any( getSlideByIndex( Index ) ); +} + +css::uno::Type SAL_CALL SlideshowImpl::getElementType( ) +{ + return cppu::UnoType<XDrawPage>::get(); +} + +sal_Bool SAL_CALL SlideshowImpl::hasElements( ) +{ + return getSlideCount() != 0; +} + +namespace +{ + class AsyncUpdateSlideshow_Impl + { + public: + struct AsyncUpdateSlideshowData + { + SlideshowImpl* pSlideshowImpl; + uno::Reference< css::drawing::XDrawPage > XCurrentSlide; + SdrHintKind eHintKind; + }; + + static ImplSVEvent* AsyncUpdateSlideshow( + SlideshowImpl* pSlideshowImpl, + uno::Reference< css::drawing::XDrawPage >& rXCurrentSlide, + SdrHintKind eHintKind) + { + AsyncUpdateSlideshowData* pNew(new AsyncUpdateSlideshowData); + pNew->pSlideshowImpl = pSlideshowImpl; + pNew->XCurrentSlide = rXCurrentSlide; + pNew->eHintKind = eHintKind; + return Application::PostUserEvent(LINK(nullptr, AsyncUpdateSlideshow_Impl, Update), pNew); + // coverity[leaked_storage] - pDisruptor takes care of its own destruction at idle time + } + + DECL_STATIC_LINK(AsyncUpdateSlideshow_Impl, Update, void*, void); + }; + + IMPL_STATIC_LINK(AsyncUpdateSlideshow_Impl, Update, void*, pData, void) + { + AsyncUpdateSlideshowData* pSlideData(static_cast<AsyncUpdateSlideshowData*>(pData)); + pSlideData->pSlideshowImpl->AsyncNotifyEvent(pSlideData->XCurrentSlide, pSlideData->eHintKind); + delete pSlideData; + } +} + +void SlideshowImpl::AsyncNotifyEvent( + const uno::Reference< css::drawing::XDrawPage >& rXCurrentSlide, + const SdrHintKind eHintKind) +{ + if (SdrHintKind::ObjectChange == eHintKind) + { + mnEventObjectChange = nullptr; + + // refresh single slide + gotoSlide(rXCurrentSlide); + } + else if (SdrHintKind::PageOrderChange == eHintKind) + { + mnEventPageOrderChange = nullptr; + + // order of pages (object pages or master pages) changed (Insert/Remove/ChangePos) + // rXCurrentSlide is the current slide before the change. + Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), UNO_QUERY_THROW ); + Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW ); + const sal_Int32 nNewSlideCount(xSlides.is() ? xSlides->getCount() : 0); + + if (nNewSlideCount != mpSlideController->getSlideNumberCount()) + { + // need to reinitialize AnimationSlideController + OUString aPresSlide( maPresSettings.maPresPage ); + createSlideList( maPresSettings.mbAll, aPresSlide ); + } + + // Check if current slide before change is still valid (maybe removed) + const sal_Int32 nSlideCount(mpSlideController->getSlideNumberCount()); + bool bSlideStillValid(false); + + for (sal_Int32 nSlide(0); !bSlideStillValid && nSlide < nSlideCount; nSlide++) + { + if (rXCurrentSlide == mpSlideController->getSlideByNumber(nSlide)) + { + bSlideStillValid = true; + } + } + + if(bSlideStillValid) + { + // stay on that slide + gotoSlide(rXCurrentSlide); + } + else + { + // not possible to stay on that slide, go to 1st slide (kinda restart) + gotoFirstSlide(); + } + } +} + +void SlideshowImpl::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if (SfxHintId::ThisIsAnSdrHint != rHint.GetId()) + // nothing to do for non-SdrHints + return; + + if (nullptr == mpDoc) + // better do nothing when no DrawModel (should not happen) + return; + + // tdf#158664 I am surprised, but the 'this' instance keeps incarnated + // when the slideshow was running once, so need to check for + // SlideShow instance/running to be safe. + // NOTE: isRunning() checks mxShow.is(), that is what we want + if (!isRunning()) + // no SlideShow instance or not running, nothing to do + return; + + const SdrHintKind eHintKind(static_cast<const SdrHint&>(rHint).GetKind()); + + if (SdrHintKind::ObjectChange == eHintKind) + { + if (nullptr != mnEventObjectChange) + // avoid multiple events + return; + + // Object changed, object & involved page included in rHint. + uno::Reference< css::drawing::XDrawPage > XCurrentSlide(getCurrentSlide()); + if (!XCurrentSlide.is()) + return; + + SdrPage* pCurrentSlide(GetSdrPageFromXDrawPage(XCurrentSlide)); + if (nullptr == pCurrentSlide) + return; + + const SdrPage* pHintPage(static_cast<const SdrHint&>(rHint).GetPage()); + if (nullptr == pHintPage) + return; + + bool bCurrentSlideIsInvolved(false); + + if (pHintPage->IsMasterPage()) + { + if (pCurrentSlide->TRG_HasMasterPage()) + { + // current slide uses MasterPage on which the change happened + bCurrentSlideIsInvolved = (pHintPage == &pCurrentSlide->TRG_GetMasterPage()); + } + } + else + { + // object on current slide was changed + bCurrentSlideIsInvolved = (pHintPage == pCurrentSlide); + } + + if (!bCurrentSlideIsInvolved) + // nothing to do when current slide is not involved + return; + + // Refresh current slide. Need to do that asynchronous, else e.g. + // text edit changes EditEngine/Outliner are not progressed far + // enough (ObjectChanged broadcast which we are in here seems + // to early for some cases) + mnEventObjectChange = AsyncUpdateSlideshow_Impl::AsyncUpdateSlideshow(this, XCurrentSlide, eHintKind); + } + else if (SdrHintKind::PageOrderChange == eHintKind) + { + // Unfortunately we get multiple events, e.g. when drag/drop position change in + // slide sorter on left side of EditView. This includes some with page number +1, + // then again -1 (it's a position change). Problem is that in-between already + // a re-schedule seems to happen, so indeed AsyncNotifyEvent will change to +1/-1 + // already. Since we get even more, at least try to take the last one. I found no + // good solution yet for this. + if (nullptr != mnEventPageOrderChange) + Application::RemoveUserEvent( mnEventPageOrderChange ); + + // order of pages (object pages or master pages) changed (Insert/Remove/ChangePos) + uno::Reference< css::drawing::XDrawPage > XCurrentSlide(getCurrentSlide()); + mnEventPageOrderChange = AsyncUpdateSlideshow_Impl::AsyncUpdateSlideshow(this, XCurrentSlide, eHintKind); + } + else if (SdrHintKind::ModelCleared == eHintKind) + { + // immediately end presentation + endPresentation(); + } + + // maybe need to add reactions here to other Hint-Types +} + +Reference< XSlideShow > SAL_CALL SlideshowImpl::getSlideShow() +{ + return mxShow; +} + +PresentationSettingsEx::PresentationSettingsEx( const PresentationSettingsEx& r ) +: PresentationSettings( r ) +, mbRehearseTimings(r.mbRehearseTimings) +, mbPreview(r.mbPreview) +, mpParentWindow( nullptr ) +{ +} + +PresentationSettingsEx::PresentationSettingsEx( PresentationSettings const & r ) +: PresentationSettings( r ) +, mbRehearseTimings(false) +, mbPreview(false) +, mpParentWindow(nullptr) +{ +} + +void PresentationSettingsEx::SetArguments( const Sequence< PropertyValue >& rArguments ) +{ + for( const PropertyValue& rValue : rArguments ) + { + SetPropertyValue( rValue.Name, rValue.Value ); + } +} + +void PresentationSettingsEx::SetPropertyValue( std::u16string_view rProperty, const Any& rValue ) +{ + if ( rProperty == u"RehearseTimings" ) + { + if( rValue >>= mbRehearseTimings ) + return; + } + else if ( rProperty == u"Preview" ) + { + if( rValue >>= mbPreview ) + return; + } + else if ( rProperty == u"AnimationNode" ) + { + if( rValue >>= mxAnimationNode ) + return; + } + else if ( rProperty == u"ParentWindow" ) + { + Reference< XWindow > xWindow; + if( rValue >>= xWindow ) + { + mpParentWindow = xWindow.is() ? VCLUnoHelper::GetWindow( xWindow ) + : nullptr; + return; + } + } + else if ( rProperty == u"AllowAnimations" ) + { + if( rValue >>= mbAnimationAllowed ) + return; + } + else if ( rProperty == u"FirstPage" ) + { + OUString aPresPage; + if( rValue >>= aPresPage ) + { + maPresPage = getUiNameFromPageApiNameImpl(aPresPage); + mbCustomShow = false; + mbAll = false; + return; + } + else + { + if( rValue >>= mxStartPage ) + return; + } + } + else if ( rProperty == u"IsAlwaysOnTop" ) + { + if( rValue >>= mbAlwaysOnTop ) + return; + } + else if ( rProperty == u"IsAutomatic" ) + { + if( rValue >>= mbManual ) + return; + } + else if ( rProperty == u"IsEndless" ) + { + if( rValue >>= mbEndless ) + return; + } + else if ( rProperty == u"IsFullScreen" ) + { + if( rValue >>= mbFullScreen ) + return; + } + else if ( rProperty == u"IsMouseVisible" ) + { + if( rValue >>= mbMouseVisible ) + return; + } + else if ( rProperty == u"Pause" ) + { + sal_Int32 nPause = -1; + if( (rValue >>= nPause) && (nPause >= 0) ) + { + mnPauseTimeout = nPause; + return; + } + } + else if ( rProperty == u"UsePen" ) + { + if( rValue >>= mbMouseAsPen ) + return; + } + throw IllegalArgumentException(); +} + +// XAnimationListener + +SlideShowListenerProxy::SlideShowListenerProxy( rtl::Reference< SlideshowImpl > xController, css::uno::Reference< css::presentation::XSlideShow > xSlideShow ) +: mxController(std::move( xController )) +, mxSlideShow(std::move( xSlideShow )) +{ +} + +SlideShowListenerProxy::~SlideShowListenerProxy() +{ +} + +void SlideShowListenerProxy::addAsSlideShowListener() +{ + if( mxSlideShow.is() ) + { + Reference< XSlideShowListener > xSlideShowListener( this ); + mxSlideShow->addSlideShowListener( xSlideShowListener ); + } +} + +void SlideShowListenerProxy::removeAsSlideShowListener() +{ + if( mxSlideShow.is() ) + { + Reference< XSlideShowListener > xSlideShowListener( this ); + mxSlideShow->removeSlideShowListener( xSlideShowListener ); + } +} + +void SlideShowListenerProxy::addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + if( mxSlideShow.is() ) + { + Reference< XShapeEventListener > xListener( this ); + mxSlideShow->addShapeEventListener( xListener, xShape ); + } +} + +void SlideShowListenerProxy::removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + if( mxSlideShow.is() ) + { + Reference< XShapeEventListener > xListener( this ); + mxSlideShow->removeShapeEventListener( xListener, xShape ); + } +} + +void SlideShowListenerProxy::addSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& xListener ) +{ + std::unique_lock g(m_aMutex); + maListeners.addInterface(g, xListener); +} + +void SlideShowListenerProxy::removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& xListener ) +{ + std::unique_lock g(m_aMutex); + maListeners.removeInterface(g, xListener); +} + +void SAL_CALL SlideShowListenerProxy::beginEvent( const Reference< XAnimationNode >& xNode ) +{ + std::unique_lock aGuard( m_aMutex ); + + if( maListeners.getLength(aGuard) >= 0 ) + { + maListeners.forEach(aGuard, + [&] (Reference<XAnimationListener> const& xListener) { + return xListener->beginEvent(xNode); + } ); + } +} + +void SAL_CALL SlideShowListenerProxy::endEvent( const Reference< XAnimationNode >& xNode ) +{ + std::unique_lock aGuard( m_aMutex ); + + if( maListeners.getLength(aGuard) >= 0 ) + { + maListeners.forEach(aGuard, + [&] (Reference<XAnimationListener> const& xListener) { + return xListener->endEvent(xNode); + } ); + } +} + +void SAL_CALL SlideShowListenerProxy::repeat( const Reference< XAnimationNode >& xNode, ::sal_Int32 nRepeat ) +{ + std::unique_lock aGuard( m_aMutex ); + + if( maListeners.getLength(aGuard) >= 0 ) + { + maListeners.forEach(aGuard, + [&] (Reference<XAnimationListener> const& xListener) { + return xListener->repeat(xNode, nRepeat); + } ); + } +} + +// css::presentation::XSlideShowListener: + +void SAL_CALL SlideShowListenerProxy::paused( ) +{ + std::unique_lock aGuard( m_aMutex ); + + maListeners.forEach(aGuard, + [](uno::Reference<presentation::XSlideShowListener> const& xListener) + { + xListener->paused(); + }); +} + +void SAL_CALL SlideShowListenerProxy::resumed( ) +{ + std::unique_lock aGuard( m_aMutex ); + + maListeners.forEach(aGuard, + [](uno::Reference<presentation::XSlideShowListener> const& xListener) + { + xListener->resumed(); + }); +} + +void SAL_CALL SlideShowListenerProxy::slideTransitionStarted( ) +{ + std::unique_lock aGuard( m_aMutex ); + + maListeners.forEach(aGuard, + [](uno::Reference<presentation::XSlideShowListener> const& xListener) + { + xListener->slideTransitionStarted(); + }); +} + +void SAL_CALL SlideShowListenerProxy::slideTransitionEnded( ) +{ + std::unique_lock aGuard( m_aMutex ); + + maListeners.forEach(aGuard, + [](uno::Reference<presentation::XSlideShowListener> const& xListener) + { + xListener->slideTransitionEnded (); + }); +} + +void SAL_CALL SlideShowListenerProxy::slideAnimationsEnded( ) +{ + std::unique_lock aGuard( m_aMutex ); + + maListeners.forEach(aGuard, + [](uno::Reference<presentation::XSlideShowListener> const& xListener) + { + xListener->slideAnimationsEnded (); + }); +} + +void SlideShowListenerProxy::slideEnded(sal_Bool bReverse) +{ + { + std::unique_lock aGuard( m_aMutex ); + + if( maListeners.getLength(aGuard) >= 0 ) + { + maListeners.forEach(aGuard, + [&] (Reference<XSlideShowListener> const& xListener) { + return xListener->slideEnded(bReverse); + } ); + } + } + + { + SolarMutexGuard aSolarGuard; + if( mxController.is() ) + mxController->slideEnded(bReverse); + } +} + +void SlideShowListenerProxy::hyperLinkClicked( OUString const& aHyperLink ) +{ + { + std::unique_lock aGuard( m_aMutex ); + + if( maListeners.getLength(aGuard) >= 0 ) + { + maListeners.forEach(aGuard, + [&] (Reference<XSlideShowListener> const& xListener) { + return xListener->hyperLinkClicked(aHyperLink); + } ); + } + } + + { + SolarMutexGuard aSolarGuard; + if( mxController.is() ) + mxController->hyperLinkClicked(aHyperLink); + } +} + +// XEventListener + +void SAL_CALL SlideShowListenerProxy::disposing( const css::lang::EventObject& aDisposeEvent ) +{ + std::unique_lock g(m_aMutex); + maListeners.disposeAndClear( g, aDisposeEvent ); + mxController.clear(); + mxSlideShow.clear(); +} + +// XShapeEventListener + +void SAL_CALL SlideShowListenerProxy::click( const Reference< XShape >& xShape, const css::awt::MouseEvent& /*aOriginalEvent*/ ) +{ + SolarMutexGuard aSolarGuard; + if( mxController.is() ) + mxController->click(xShape ); +} + +void SAL_CALL SlideShowListenerProxy::contextMenuShow(const css::awt::Point& point) +{ + SolarMutexGuard aSolarGuard; + if (mxController.is()) + mxController->contextMenuShow(point); +} + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowimpl.hxx b/sd/source/ui/slideshow/slideshowimpl.hxx new file mode 100644 index 0000000000..155cdc329f --- /dev/null +++ b/sd/source/ui/slideshow/slideshowimpl.hxx @@ -0,0 +1,355 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <memory> +#include <sal/config.h> +#include <comphelper/compbase.hxx> +#include <cppuhelper/implbase.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <com/sun/star/presentation/ClickAction.hpp> +#include <com/sun/star/presentation/XSlideShowNavigationListener.hpp> +#include <com/sun/star/presentation/XSlideShowController.hpp> +#include <com/sun/star/presentation/XShapeEventListener.hpp> + +#include <drawdoc.hxx> + +#include "showwindow.hxx" + +#include <slideshow.hxx> + +namespace com::sun::star::frame { class XModel; } +namespace com::sun::star::media { class XPlayer; } +namespace sd { class DrawDocShell; } +namespace sd { class ViewShell; } + +class SfxBindings; +class SfxDispatcher; +class SfxViewFrame; +class StarBASIC; + +namespace sd +{ +class SlideShowView; +class AnimationSlideController; +class PaneHider; + +struct PresentationSettingsEx : public PresentationSettings +{ + bool mbRehearseTimings; + bool mbPreview; + VclPtr<vcl::Window> mpParentWindow; + css::uno::Reference< css::drawing::XDrawPage > mxStartPage; + css::uno::Reference< css::animations::XAnimationNode > mxAnimationNode; + + PresentationSettingsEx( const PresentationSettingsEx& ); + explicit PresentationSettingsEx( PresentationSettings const & ); + + /// @throws css::lang::IllegalArgumentException + void SetArguments( const css::uno::Sequence< css::beans::PropertyValue >& rArguments ); + + /// @throws css::lang::IllegalArgumentException + void SetPropertyValue( std::u16string_view rProperty, const css::uno::Any& rValue ); +}; + +struct WrappedShapeEventImpl +{ + css::presentation::ClickAction meClickAction; + sal_Int32 mnVerb; + OUString maStrBookmark; + WrappedShapeEventImpl() : meClickAction( css::presentation::ClickAction_NONE ), mnVerb( 0 ) {}; +}; + +typedef std::shared_ptr< WrappedShapeEventImpl > WrappedShapeEventImplPtr; + +class SlideShowListenerProxy : + public ::cppu::WeakImplHelper< css::presentation::XSlideShowNavigationListener, css::presentation::XShapeEventListener > +{ +public: + SlideShowListenerProxy( rtl::Reference< SlideshowImpl > xController, css::uno::Reference< css::presentation::XSlideShow > xSlideShow ); + virtual ~SlideShowListenerProxy() override; + + void addAsSlideShowListener(); + void removeAsSlideShowListener(); + + void addSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ); + void removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ); + + void addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ); + void removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ); + + // css::animations::XAnimationListener + virtual void SAL_CALL beginEvent( const css::uno::Reference< css::animations::XAnimationNode >& Node ) override; + virtual void SAL_CALL endEvent( const css::uno::Reference< css::animations::XAnimationNode >& Node ) override; + virtual void SAL_CALL repeat( const css::uno::Reference< css::animations::XAnimationNode >& Node, ::sal_Int32 Repeat ) override; + + // css::presentation::XSlideShowListener: + virtual void SAL_CALL paused() override; + virtual void SAL_CALL resumed() override; + virtual void SAL_CALL slideTransitionStarted() override; + virtual void SAL_CALL slideTransitionEnded() override; + virtual void SAL_CALL slideAnimationsEnded() override; + virtual void SAL_CALL slideEnded(sal_Bool bReverse) override; + virtual void SAL_CALL hyperLinkClicked(const OUString & hyperLink) override; + + // css::presentation::XSlideShowNavigationListener: + virtual void SAL_CALL contextMenuShow(const css::awt::Point& point) override; + + // css::lang::XEventListener: + virtual void SAL_CALL disposing(const css::lang::EventObject & Source) override; + + // css::presentation::XShapeEventListener: + virtual void SAL_CALL click(const css::uno::Reference< css::drawing::XShape > & xShape, const css::awt::MouseEvent & aOriginalEvent) override; + +private: + std::mutex m_aMutex; + ::comphelper::OInterfaceContainerHelper4<css::presentation::XSlideShowListener> maListeners; + rtl::Reference< SlideshowImpl > mxController; + css::uno::Reference< css::presentation::XSlideShow > mxSlideShow; +}; + +typedef comphelper::WeakComponentImplHelper< css::presentation::XSlideShowController, css::container::XIndexAccess > SlideshowImplBase; + +class SlideshowImpl final : public SlideshowImplBase, public SfxListener +{ +friend class SlideShow; +friend class SlideShowView; + +public: + explicit SlideshowImpl( const css::uno::Reference< css::presentation::XPresentation2 >& xPresentation, ViewShell* pViewSh, ::sd::View* pView, SdDrawDocument* pDoc, vcl::Window* pParentWindow); + + // css::presentation::XSlideShowController: + virtual sal_Bool SAL_CALL getAlwaysOnTop() override; + virtual void SAL_CALL setAlwaysOnTop( sal_Bool _alwaysontop ) override; + virtual sal_Bool SAL_CALL getMouseVisible() override; + virtual void SAL_CALL setMouseVisible( sal_Bool _mousevisible ) override; + virtual sal_Bool SAL_CALL getUsePen() override; + virtual void SAL_CALL setUsePen( sal_Bool _usepen ) override; + virtual ::sal_Int32 SAL_CALL getPenColor() override; + virtual void SAL_CALL setPenColor( ::sal_Int32 _pencolor ) override; + virtual double SAL_CALL getPenWidth() override; + virtual void SAL_CALL setPenWidth( double dStrokeWidth ) override; + /// @throws css::uno::RuntimeException + virtual void SAL_CALL setEraseAllInk( sal_Bool bEraseAllInk ) override; + virtual sal_Bool SAL_CALL isRunning( ) override; + virtual ::sal_Int32 SAL_CALL getSlideCount( ) override; + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getSlideByIndex( ::sal_Int32 Index ) override; + virtual void SAL_CALL addSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ) override; + virtual void SAL_CALL removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ) override; + virtual void SAL_CALL gotoNextEffect( ) override; + virtual void SAL_CALL gotoPreviousEffect( ) override; + virtual void SAL_CALL gotoFirstSlide( ) override; + virtual void SAL_CALL gotoNextSlide( ) override; + virtual void SAL_CALL gotoPreviousSlide( ) override; + virtual void SAL_CALL gotoLastSlide( ) override; + virtual void SAL_CALL gotoBookmark( const OUString& Bookmark ) override; + virtual void SAL_CALL gotoSlide( const css::uno::Reference< css::drawing::XDrawPage >& Page ) override; + virtual void SAL_CALL gotoSlideIndex( ::sal_Int32 Index ) override; + virtual void SAL_CALL stopSound( ) override; + virtual void SAL_CALL pause( ) override; + virtual void SAL_CALL resume( ) override; + virtual sal_Bool SAL_CALL isPaused( ) override; + virtual void SAL_CALL blankScreen( ::sal_Int32 Color ) override; + virtual void SAL_CALL activate( ) override; + virtual void SAL_CALL deactivate( ) override; + virtual sal_Bool SAL_CALL isActive( ) override; + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getCurrentSlide( ) override; + virtual ::sal_Int32 SAL_CALL getCurrentSlideIndex( ) override; + virtual ::sal_Int32 SAL_CALL getNextSlideIndex( ) override; + virtual sal_Bool SAL_CALL isEndless( ) override; + virtual sal_Bool SAL_CALL isFullScreen( ) override; + virtual css::uno::Reference< css::presentation::XSlideShow > SAL_CALL getSlideShow( ) override; + + // XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount( ) override; + virtual css::uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override; + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + // SfxListener + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + + // will be called from the SlideShowListenerProxy when this event is fired from the XSlideShow + void slideEnded(const bool bReverse); + void contextMenuShow(const css::awt::Point& point); + /// @throws css::uno::RuntimeException + void hyperLinkClicked(const OUString & hyperLink); + void click(const css::uno::Reference< css::drawing::XShape > & xShape); + bool swipe(const CommandGestureSwipeData &rSwipeData); + bool longpress(const CommandGestureLongPressData& rLongPressData); + + /// ends the presentation async + void endPresentation(); + + // possibly triggered from events @SlideshowImpl::Notify if needed, but make it asynchronous to + // allow the noted event to completely finish in the core + void AsyncNotifyEvent(const css::uno::Reference< css::drawing::XDrawPage >&, const SdrHintKind); + + ViewShell* getViewShell() const { return mpViewShell; } + + void paint(); + bool keyInput(const KeyEvent& rKEvt); + void mouseButtonUp(const MouseEvent& rMEvt); + +private: + SlideshowImpl(SlideshowImpl const &) = delete; + void operator =(SlideshowImpl const &) = delete; + + virtual ~SlideshowImpl() override; + + // override WeakComponentImplHelperBase::disposing() + // This function is called upon disposing the component, + // if your component needs special work when it becomes + // disposed, do it here. + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // internal + bool startShow( PresentationSettingsEx const * pPresSettings ); + bool startPreview( + const css::uno::Reference< css::drawing::XDrawPage >& xDrawPage, + const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode, + vcl::Window* pParent ); + + /** forces an async call to update in the main thread */ + void startUpdateTimer(); + + void update(); + + void createSlideList( bool bAll, std::u16string_view rPresSlide ); + + void displayCurrentSlide (const bool bSkipAllMainSequenceEffects = false); + + void displaySlideNumber( sal_Int32 nSlide ); + void displaySlideIndex( sal_Int32 nIndex ); + sal_Int32 getCurrentSlideNumber() const; + bool isInputFreezed() const { return mbInputFreeze; } + + void jumpToBookmark( const OUString& sBookmark ); + + void hideChildWindows(); + void showChildWindows(); + + void resize( const Size& rSize ); + + void setActiveXToolbarsVisible( bool bVisible ); + + DECL_LINK( updateHdl, Timer *, void ); + DECL_LINK( ReadyForNextInputHdl, Timer *, void ); + DECL_LINK( endPresentationHdl, void*, void ); + void ContextMenuSelectHdl(std::u16string_view rIdent); + DECL_LINK( ContextMenuHdl, void*, void ); + DECL_LINK( deactivateHdl, Timer *, void ); + DECL_LINK( EventListenerHdl, VclSimpleEvent&, void ); + + /** called only by the slideshow view when the first paint event occurs. + This actually starts the slideshow. */ + void onFirstPaint(); + + ::tools::Long getRestoreSlide() const { return mnRestoreSlide; } + +private: + bool startShowImpl( + const css::uno::Sequence< css::beans::PropertyValue >& aProperties ); + + SfxViewFrame* getViewFrame() const; + SfxDispatcher* getDispatcher() const; + SfxBindings* getBindings() const; + + sal_Int32 getSlideNumberForBookmark( const OUString& rStrBookmark ); + + void removeShapeEvents(); + void registerShapeEvents( sal_Int32 nSlideNumber ); + /// @throws css::uno::Exception + void registerShapeEvents( css::uno::Reference< css::drawing::XShapes > const & xShapes ); + + static css::uno::Reference< css::presentation::XSlideShow > createSlideShow(); + + static void setAutoSaveState( bool bOn ); + void gotoPreviousSlide (const bool bSkipAllMainSequenceEffects); + + /** Called by our maUpdateTimer's updateHdl handler this method is + responsible to call the slideshow update() method and, depending on + its return value, wait for a certain amount of time before another + call to update() is scheduled. + */ + void updateSlideShow(); + + css::uno::Reference< css::presentation::XSlideShow > mxShow; + rtl::Reference<sd::SlideShowView> mxView; + css::uno::Reference< css::frame::XModel > mxModel; + + Timer maUpdateTimer; + Timer maInputFreezeTimer; + Timer maDeactivateTimer; + + ::sd::View* mpView; + ViewShell* mpViewShell; + DrawDocShell* mpDocSh; + SdDrawDocument* mpDoc; + + VclPtr<vcl::Window> mpParentWindow; + VclPtr<sd::ShowWindow> mpShowWindow; + + std::shared_ptr< AnimationSlideController > mpSlideController; + + ::tools::Long mnRestoreSlide; + Point maPopupMousePos; + Size maPresSize; + AnimationMode meAnimationMode; + OUString maCharBuffer; + VclPtr< ::sd::Window> mpOldActiveWindow; + Link<StarBASIC*,bool> maStarBASICGlobalErrorHdl; + ::tools::ULong mnChildMask; + bool mbDisposed; + bool mbAutoSaveWasOn; + bool mbRehearseTimings; + bool mbIsPaused; + bool mbWasPaused; // used to cache pause state during context menu + bool mbInputFreeze; + bool mbActive; + + PresentationSettings maPresSettings; + sal_Int32 mnUserPaintColor; + + bool mbUsePen; + double mdUserPaintStrokeWidth; + + std::map< css::uno::Reference< css::drawing::XShape >, WrappedShapeEventImplPtr > + maShapeEventMap; + + css::uno::Reference< css::drawing::XDrawPage > mxPreviewDrawPage; + css::uno::Reference< css::animations::XAnimationNode > mxPreviewAnimationNode; + + css::uno::Reference< css::media::XPlayer > mxPlayer; + + ::std::unique_ptr<PaneHider> mpPaneHider; + + ImplSVEvent * mnEndShowEvent; + ImplSVEvent * mnContextMenuEvent; + ImplSVEvent * mnEventObjectChange; + ImplSVEvent * mnEventPageOrderChange; + + css::uno::Reference< css::presentation::XPresentation2 > mxPresentation; + ::rtl::Reference< SlideShowListenerProxy > mxListenerProxy; +}; + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowviewimpl.cxx b/sd/source/ui/slideshow/slideshowviewimpl.cxx new file mode 100644 index 0000000000..d6addc3f87 --- /dev/null +++ b/sd/source/ui/slideshow/slideshowviewimpl.cxx @@ -0,0 +1,626 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "slideshowviewimpl.hxx" +#include "slideshowimpl.hxx" +#include <sdpage.hxx> + +#include <vcl/svapp.hxx> + +#include <com/sun/star/awt/Pointer.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> + +#include <cppcanvas/vclfactory.hxx> +#include <cppcanvas/basegfxfactory.hxx> +#include <basegfx/utils/canvastools.hxx> + +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/processfactory.hxx> + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::WeakReference; +using ::com::sun::star::uno::Exception; + +using namespace ::com::sun::star; + +namespace sd +{ + +void SlideShowViewMouseListeners::notify( std::unique_lock<std::mutex>& rGuard, const WrappedMouseEvent& rEvent ) +{ + forEach(rGuard, + [&rEvent] (const Reference<css::awt::XMouseListener>& rListener) + { + switch( rEvent.meType ) + { + case WrappedMouseEvent::PRESSED: + rListener->mousePressed( rEvent.maEvent ); + break; + + case WrappedMouseEvent::RELEASED: + rListener->mouseReleased( rEvent.maEvent ); + break; + + case WrappedMouseEvent::ENTERED: + rListener->mouseEntered( rEvent.maEvent ); + break; + + case WrappedMouseEvent::EXITED: + rListener->mouseExited( rEvent.maEvent ); + break; + } + }); +} + + +void SlideShowViewMouseMotionListeners::notify( std::unique_lock<std::mutex>& rGuard,const WrappedMouseMotionEvent& rEvent ) +{ + forEach(rGuard, + [&rEvent] (const Reference< awt::XMouseMotionListener >& rListener) + { + switch( rEvent.meType ) + { + case WrappedMouseMotionEvent::DRAGGED: + rListener->mouseDragged( rEvent.maEvent ); + break; + + case WrappedMouseMotionEvent::MOVED: + rListener->mouseMoved( rEvent.maEvent ); + break; + } + }); +} + +// SlideShowView +SlideShowView::SlideShowView( ShowWindow& rOutputWindow, + SdDrawDocument* pDoc, + AnimationMode eAnimationMode, + SlideshowImpl* pSlideShow, + bool bFullScreen ) +: mpCanvas( ::cppcanvas::VCLFactory::createSpriteCanvas( rOutputWindow ) ), + mxWindow( VCLUnoHelper::GetInterface( &rOutputWindow ), uno::UNO_SET_THROW ), + mxWindowPeer( mxWindow, uno::UNO_QUERY_THROW ), + mpSlideShow( pSlideShow ), + mrOutputWindow( rOutputWindow ), + mpDoc( pDoc ), + mbIsMouseMotionListener( false ), + meAnimationMode( eAnimationMode ), + mbFirstPaint( true ), + mbMousePressedEaten( false ) +{ + mxWindow->addWindowListener( this ); + mxWindow->addMouseListener( this ); + + mxPointer = awt::Pointer::create( ::comphelper::getProcessComponentContext() ); + + getTransformation(); + + // #i48939# only switch on kind of hacky scroll optimization, when + // running fullscreen. this minimizes the probability that other + // windows partially cover the show. + if( bFullScreen ) + { + try + { + Reference< beans::XPropertySet > xCanvasProps( getCanvas(), + uno::UNO_QUERY_THROW ); + xCanvasProps->setPropertyValue("UnsafeScrolling", + uno::Any( true ) ); + } + catch( uno::Exception& ) + { + } + } + + mTranslationOffset.Width = 0; + mTranslationOffset.Height = 0; +} + +// Dispose all internal references +void SlideShowView::disposing(std::unique_lock<std::mutex>& rGuard) +{ + mpSlideShow = nullptr; + + // deregister listeners + if( mxWindow.is() ) + { + mxWindow->removeWindowListener( this ); + mxWindow->removeMouseListener( this ); + + if( mbIsMouseMotionListener ) + mxWindow->removeMouseMotionListener( this ); + } + + mpCanvas.reset(); + mxWindow.clear(); + + // clear all listener containers + disposingImpl(rGuard); +} + +// Disposing our broadcaster +void SAL_CALL SlideShowView::disposing( const lang::EventObject& ) +{ + std::unique_lock aGuard( m_aMutex ); + + disposingImpl(aGuard); +} + +// Disposing our broadcaster +void SlideShowView::disposingImpl(std::unique_lock<std::mutex>& rGuard) +{ + // notify all listeners that _we_ are going down (send a disposing()), + // then delete listener containers: + lang::EventObject const evt( static_cast<OWeakObject *>(this) ); + if (!maViewListeners.empty()) + { + auto tmp = std::move(maViewListeners); + rGuard.unlock(); + for( const auto& rxListener : tmp ) + { + Reference< util::XModifyListener > xListener( rxListener ); + if( xListener.is() ) + xListener->disposing( evt ); + } + rGuard.lock(); + } + if (maPaintListeners.getLength(rGuard)) + { + maPaintListeners.disposeAndClear( rGuard, evt ); + rGuard.lock(); + } + if (maMouseListeners.getLength(rGuard)) + { + maMouseListeners.disposeAndClear( rGuard, evt ); + rGuard.lock(); + } + if (maMouseMotionListeners.getLength(rGuard)) + { + maMouseMotionListeners.disposeAndClear( rGuard, evt ); + rGuard.lock(); + } +} + +void SlideShowView::paint( const awt::PaintEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + + if( mbFirstPaint ) + { + mbFirstPaint = false; + SlideshowImpl* pSlideShow = mpSlideShow; + aGuard.unlock(); + if( pSlideShow ) + pSlideShow->onFirstPaint(); + } + else + { + // Change event source, to enable listeners to match event + // with view + awt::PaintEvent aEvent( e ); + aEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + maPaintListeners.notifyEach( aGuard, &css::awt::XPaintListener::windowPaint, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! + } +} + +// XSlideShowView methods +Reference< rendering::XSpriteCanvas > SAL_CALL SlideShowView::getCanvas( ) +{ + std::unique_lock aGuard( m_aMutex ); + + return mpCanvas ? mpCanvas->getUNOSpriteCanvas() : Reference< rendering::XSpriteCanvas >(); +} + +void SAL_CALL SlideShowView::clear() +{ + // paint background in black + std::unique_lock aGuard( m_aMutex ); + SolarMutexGuard aSolarGuard; + + // fill the bounds rectangle in black + + const Size aWindowSize( mrOutputWindow.GetSizePixel() ); + + ::basegfx::B2DPolygon aPoly( ::basegfx::utils::createPolygonFromRect( + ::basegfx::B2DRectangle(0.0,0.0, + aWindowSize.Width(), + aWindowSize.Height() ) ) ); + ::cppcanvas::PolyPolygonSharedPtr pPolyPoly( + ::cppcanvas::BaseGfxFactory::createPolyPolygon( mpCanvas, aPoly ) ); + + if( pPolyPoly ) + { + pPolyPoly->setRGBAFillColor( 0x000000FFU ); + pPolyPoly->draw(); + } +} + +geometry::IntegerSize2D SAL_CALL SlideShowView::getTranslationOffset( ) +{ + return mTranslationOffset; +} + +geometry::AffineMatrix2D SAL_CALL SlideShowView::getTransformation( ) +{ + std::unique_lock aGuard( m_aMutex ); + SolarMutexGuard aSolarGuard; + + const Size& rTmpSize( mrOutputWindow.GetSizePixel() ); + + if (rTmpSize.IsEmpty()) + { + return geometry::AffineMatrix2D (1,0,0,0,1,0); + } + + const Size aWindowSize( mrOutputWindow.GetSizePixel() ); + Size aOutputSize( aWindowSize ); + + if( meAnimationMode != ANIMATIONMODE_SHOW ) + { + aOutputSize.setWidth( static_cast<::tools::Long>( aOutputSize.Width() / 1.03 ) ); + aOutputSize.setHeight( static_cast<::tools::Long>( aOutputSize.Height() / 1.03 ) ); + } + + SdPage* pP = mpDoc->GetSdPage( 0, PageKind::Standard ); + Size aPageSize( pP->GetSize() ); + + const double page_ratio = static_cast<double>(aPageSize.Width()) / static_cast<double>(aPageSize.Height()); + const double output_ratio = static_cast<double>(aOutputSize.Width()) / static_cast<double>(aOutputSize.Height()); + + if( page_ratio > output_ratio ) + { + aOutputSize.setHeight( ( aOutputSize.Width() * aPageSize.Height() ) / aPageSize.Width() ); + } + else if( page_ratio < output_ratio ) + { + aOutputSize.setWidth( ( aOutputSize.Height() * aPageSize.Width() ) / aPageSize.Height() ); + } + + Point aOutputOffset( ( aWindowSize.Width() - aOutputSize.Width() ) >> 1, + ( aWindowSize.Height() - aOutputSize.Height() ) >> 1 ); + + // Reduce available width by one, as the slides might actually + // render one pixel wider and higher as aPageSize below specifies + // (when shapes of page size have visible border lines) + aOutputSize.AdjustWidth( -1 ); + aOutputSize.AdjustHeight( -1 ); + + // Record mTranslationOffset + mTranslationOffset.Height = aOutputOffset.Y(); + mTranslationOffset.Width = aOutputOffset.X(); + + // scale presentation into available window rect (minus 10%); center in the window + const basegfx::B2DHomMatrix aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix( + aOutputSize.Width(), aOutputSize.Height(), aOutputOffset.X(), aOutputOffset.Y())); + + geometry::AffineMatrix2D aRes; + + return ::basegfx::unotools::affineMatrixFromHomMatrix( aRes, aMatrix ); +} + +void SAL_CALL SlideShowView::addTransformationChangedListener( const Reference< util::XModifyListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + WeakReference< util::XModifyListener > xWeak( xListener ); + if( std::find( maViewListeners.begin(), maViewListeners.end(), xWeak ) == maViewListeners.end() ) + maViewListeners.push_back( xWeak ); +} + +void SAL_CALL SlideShowView::removeTransformationChangedListener( const Reference< util::XModifyListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + WeakReference< util::XModifyListener > xWeak( xListener ); + auto aIter( std::find( maViewListeners.begin(), maViewListeners.end(), xWeak ) ); + if( aIter != maViewListeners.end() ) + maViewListeners.erase( aIter ); +} + +void SAL_CALL SlideShowView::addPaintListener( const Reference< awt::XPaintListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maPaintListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::removePaintListener( const Reference< awt::XPaintListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maPaintListeners.removeInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::addMouseListener( const Reference< awt::XMouseListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maMouseListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::removeMouseListener( const Reference< awt::XMouseListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maMouseListeners.removeInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::addMouseMotionListener( const Reference< awt::XMouseMotionListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + + if( !mbIsMouseMotionListener && mxWindow.is() ) + { + // delay motion event registration, until we really + // need it + mbIsMouseMotionListener = true; + mxWindow->addMouseMotionListener( this ); + } + + maMouseMotionListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::removeMouseMotionListener( const Reference< awt::XMouseMotionListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maMouseMotionListeners.removeInterface( aGuard, xListener ); + + // TODO(P1): Might be nice to deregister for mouse motion + // events, when the last listener is gone. +} + +void SAL_CALL SlideShowView::setMouseCursor( sal_Int16 nPointerShape ) +{ + std::unique_lock aGuard( m_aMutex ); + + // forward to window + if( mxPointer.is() ) + mxPointer->setType( nPointerShape ); + + if( mxWindowPeer.is() ) + mxWindowPeer->setPointer( mxPointer ); +} + +awt::Rectangle SAL_CALL SlideShowView::getCanvasArea( ) +{ + awt::Rectangle aRectangle; + + if( mxWindow.is() ) + return mxWindow->getPosSize(); + + aRectangle.X = aRectangle.Y = aRectangle.Width = aRectangle.Height = 0; + + return aRectangle; +} + +void SlideShowView::updateimpl( std::unique_lock<std::mutex>& rGuard, SlideshowImpl* pSlideShow ) +{ + if( !pSlideShow ) + return; + + ::rtl::Reference< SlideshowImpl > xKeepAlive( pSlideShow ); + + if( mbFirstPaint ) + { + mbFirstPaint = false; + SlideshowImpl* pTmpSlideShow = mpSlideShow; + rGuard.unlock(); + if( pTmpSlideShow ) + pTmpSlideShow->onFirstPaint(); + } else + rGuard.unlock(); + + pSlideShow->startUpdateTimer(); +} + +// XWindowListener methods +void SAL_CALL SlideShowView::windowResized( const awt::WindowEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + + if (!maViewListeners.empty()) + { + // Change event source, to enable listeners to match event + // with view + awt::WindowEvent aEvent( e ); + aEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + auto aIter( maViewListeners.begin() ); + while( aIter != maViewListeners.end() ) + { + Reference< util::XModifyListener > xListener( *aIter ); + if( xListener.is() ) + { + aGuard.unlock(); + xListener->modified( aEvent ); + aGuard.lock(); + ++aIter; + } + else + { + aIter = maViewListeners.erase( aIter ); + } + } + } + + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +void SAL_CALL SlideShowView::windowMoved( const awt::WindowEvent& ) +{ + // ignored +} + +void SAL_CALL SlideShowView::windowShown( const lang::EventObject& ) +{ + // ignored +} + +void SAL_CALL SlideShowView::windowHidden( const lang::EventObject& ) +{ + // ignored +} + +// XMouseListener implementation +void SAL_CALL SlideShowView::mousePressed( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + if( mpSlideShow && mpSlideShow->isInputFreezed() ) + { + mbMousePressedEaten = true; + } + else + { + mbMousePressedEaten = false; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::PRESSED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! + } +} + +void SAL_CALL SlideShowView::mouseReleased( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + if( mbMousePressedEaten ) + { + // if mouse button down was ignored, also ignore mouse button up + mbMousePressedEaten = false; + } + else if( mpSlideShow && !mpSlideShow->isInputFreezed() ) + { + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::RELEASED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! + } +} + +void SAL_CALL SlideShowView::mouseEntered( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::ENTERED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +void SAL_CALL SlideShowView::mouseExited( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::EXITED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +// XMouseMotionListener implementation +void SAL_CALL SlideShowView::mouseDragged( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseMotionEvent aEvent; + aEvent.meType = WrappedMouseMotionEvent::DRAGGED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseMotionListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +void SAL_CALL SlideShowView::mouseMoved( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseMotionEvent aEvent; + aEvent.meType = WrappedMouseMotionEvent::MOVED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseMotionListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowviewimpl.hxx b/sd/source/ui/slideshow/slideshowviewimpl.hxx new file mode 100644 index 0000000000..3a5018be41 --- /dev/null +++ b/sd/source/ui/slideshow/slideshowviewimpl.hxx @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <comphelper/compbase.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <com/sun/star/awt/XWindowListener.hpp> +#include <com/sun/star/util/XModifyListener.hpp> +#include <com/sun/star/awt/XPaintListener.hpp> +#include <com/sun/star/presentation/XSlideShowView.hpp> +#include <cppcanvas/spritecanvas.hxx> +#include <cppuhelper/weakref.hxx> + +#include <slideshow.hxx> + +namespace com::sun::star::awt { class XPointer; } +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::awt { class XWindowPeer; } +namespace com::sun::star::awt { struct WindowEvent; } +namespace com::sun::star::rendering { class XSpriteCanvas; } +class SdDrawDocument; + +namespace sd +{ + +struct WrappedMouseEvent : public css::lang::EventObject +{ + enum EventType + { + PRESSED, + RELEASED, + ENTERED, + EXITED + }; + + EventType meType; + css::awt::MouseEvent maEvent; +}; + +struct WrappedMouseMotionEvent : public css::lang::EventObject +{ + enum EventType + { + DRAGGED, + MOVED + }; + + EventType meType; + css::awt::MouseEvent maEvent; +}; + +// SlideShowViewPaintListeners +typedef ::comphelper::OInterfaceContainerHelper4< css::awt::XPaintListener > SlideShowViewPaintListeners; + + +// SlideShowViewMouseListeners +typedef ::comphelper::OInterfaceContainerHelper4< css::awt::XMouseListener > SlideShowViewMouseListeners_Base; + +class SlideShowViewMouseListeners : public SlideShowViewMouseListeners_Base +{ +public: + void notify(std::unique_lock<std::mutex>& rGuard, const WrappedMouseEvent& rEvent); +}; + + +// SlideShowViewMouseMotionListeners +typedef ::comphelper::OInterfaceContainerHelper4< css::awt::XMouseMotionListener > SlideShowViewMouseMotionListeners_Base; + +class SlideShowViewMouseMotionListeners : public SlideShowViewMouseMotionListeners_Base +{ +public: + void notify( std::unique_lock<std::mutex>& rGuard, const WrappedMouseMotionEvent& rEvent ); +}; + +// SlideShowView +class ShowWindow; +class SlideshowImpl; + +typedef comphelper::WeakComponentImplHelper< css::presentation::XSlideShowView, + css::awt::XWindowListener, + css::awt::XMouseListener, + css::awt::XMouseMotionListener > SlideShowView_Base; + +class SlideShowView final : public SlideShowView_Base +{ +public: + SlideShowView( ShowWindow& rOutputWindow, + SdDrawDocument* pDoc, + AnimationMode eAnimationMode, + SlideshowImpl* pSlideShow, + bool bFullScreen ); + + void ignoreNextMouseReleased() { mbMousePressedEaten = true; } + + /// Dispose all internal references + virtual void disposing(std::unique_lock<std::mutex>&) override; + + /// Disposing our broadcaster + virtual void SAL_CALL disposing( const css::lang::EventObject& ) override; + + /// @throws css::uno::RuntimeException + void paint( const css::awt::PaintEvent& e ); + + // XSlideShowView methods + virtual css::uno::Reference< css::rendering::XSpriteCanvas > SAL_CALL getCanvas( ) override; + virtual void SAL_CALL clear( ) override; + virtual css::geometry::AffineMatrix2D SAL_CALL getTransformation( ) override; + virtual css::geometry::IntegerSize2D SAL_CALL getTranslationOffset( ) override; + virtual void SAL_CALL addTransformationChangedListener( const css::uno::Reference< css::util::XModifyListener >& xListener ) override; + virtual void SAL_CALL removeTransformationChangedListener( const css::uno::Reference< css::util::XModifyListener >& xListener ) override; + virtual void SAL_CALL addPaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override; + virtual void SAL_CALL removePaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override; + virtual void SAL_CALL addMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override; + virtual void SAL_CALL removeMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override; + virtual void SAL_CALL addMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override; + virtual void SAL_CALL removeMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override; + virtual void SAL_CALL setMouseCursor( sal_Int16 nPointerShape ) override; + virtual css::awt::Rectangle SAL_CALL getCanvasArea( ) override; + + // XWindowListener methods + virtual void SAL_CALL windowResized( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowMoved( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowShown( const css::lang::EventObject& e ) override; + virtual void SAL_CALL windowHidden( const css::lang::EventObject& e ) override; + + // XMouseListener implementation + virtual void SAL_CALL mousePressed( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseReleased( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseEntered( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseExited( const css::awt::MouseEvent& e ) override; + + // XMouseMotionListener implementation + virtual void SAL_CALL mouseDragged( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseMoved( const css::awt::MouseEvent& e ) override; + +protected: + virtual ~SlideShowView() override {} + +private: + void updateimpl( std::unique_lock<std::mutex>& rGuard, SlideshowImpl* pSlideShow ); + + void disposingImpl( std::unique_lock<std::mutex>& ); + + ::cppcanvas::SpriteCanvasSharedPtr mpCanvas; + css::uno::Reference< css::awt::XWindow > mxWindow; + css::uno::Reference< css::awt::XWindowPeer > mxWindowPeer; + css::uno::Reference< css::awt::XPointer > mxPointer; + SlideshowImpl* mpSlideShow; + ShowWindow& mrOutputWindow; + std::vector< css::uno::WeakReference< css::util::XModifyListener > > + maViewListeners; + SlideShowViewPaintListeners maPaintListeners; + SlideShowViewMouseListeners maMouseListeners; + SlideShowViewMouseMotionListeners maMouseMotionListeners; + SdDrawDocument* mpDoc; + bool mbIsMouseMotionListener; + AnimationMode meAnimationMode; + bool mbFirstPaint; + bool mbMousePressedEaten; + css::geometry::IntegerSize2D mTranslationOffset; +}; + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |