summaryrefslogtreecommitdiffstats
path: root/sdext/source/presenter
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sdext/source/presenter/PresenterAccessibility.cxx1849
-rw-r--r--sdext/source/presenter/PresenterAccessibility.hxx115
-rw-r--r--sdext/source/presenter/PresenterBitmapContainer.cxx399
-rw-r--r--sdext/source/presenter/PresenterBitmapContainer.hxx146
-rw-r--r--sdext/source/presenter/PresenterButton.cxx447
-rw-r--r--sdext/source/presenter/PresenterButton.hxx138
-rw-r--r--sdext/source/presenter/PresenterCanvasHelper.cxx289
-rw-r--r--sdext/source/presenter/PresenterCanvasHelper.hxx107
-rw-r--r--sdext/source/presenter/PresenterConfigurationAccess.cxx274
-rw-r--r--sdext/source/presenter/PresenterConfigurationAccess.hxx178
-rw-r--r--sdext/source/presenter/PresenterController.cxx1185
-rw-r--r--sdext/source/presenter/PresenterController.hxx222
-rw-r--r--sdext/source/presenter/PresenterCurrentSlideObserver.cxx130
-rw-r--r--sdext/source/presenter/PresenterCurrentSlideObserver.hxx81
-rw-r--r--sdext/source/presenter/PresenterFrameworkObserver.cxx109
-rw-r--r--sdext/source/presenter/PresenterFrameworkObserver.hxx81
-rw-r--r--sdext/source/presenter/PresenterGeometryHelper.cxx262
-rw-r--r--sdext/source/presenter/PresenterGeometryHelper.hxx117
-rw-r--r--sdext/source/presenter/PresenterHelpView.cxx747
-rw-r--r--sdext/source/presenter/PresenterHelpView.hxx121
-rw-r--r--sdext/source/presenter/PresenterHelper.cxx56
-rw-r--r--sdext/source/presenter/PresenterHelper.hxx48
-rw-r--r--sdext/source/presenter/PresenterNotesView.cxx650
-rw-r--r--sdext/source/presenter/PresenterNotesView.hxx156
-rw-r--r--sdext/source/presenter/PresenterPaintManager.cxx141
-rw-r--r--sdext/source/presenter/PresenterPaintManager.hxx89
-rw-r--r--sdext/source/presenter/PresenterPane.cxx169
-rw-r--r--sdext/source/presenter/PresenterPane.hxx80
-rw-r--r--sdext/source/presenter/PresenterPaneBase.cxx342
-rw-r--r--sdext/source/presenter/PresenterPaneBase.hxx128
-rw-r--r--sdext/source/presenter/PresenterPaneBorderPainter.cxx882
-rw-r--r--sdext/source/presenter/PresenterPaneBorderPainter.hxx138
-rw-r--r--sdext/source/presenter/PresenterPaneContainer.cxx331
-rw-r--r--sdext/source/presenter/PresenterPaneContainer.hxx161
-rw-r--r--sdext/source/presenter/PresenterPaneFactory.cxx283
-rw-r--r--sdext/source/presenter/PresenterPaneFactory.hxx117
-rw-r--r--sdext/source/presenter/PresenterProtocolHandler.cxx829
-rw-r--r--sdext/source/presenter/PresenterProtocolHandler.hxx91
-rw-r--r--sdext/source/presenter/PresenterScreen.cxx801
-rw-r--r--sdext/source/presenter/PresenterScreen.hxx227
-rw-r--r--sdext/source/presenter/PresenterScrollBar.cxx824
-rw-r--r--sdext/source/presenter/PresenterScrollBar.hxx257
-rw-r--r--sdext/source/presenter/PresenterSlidePreview.cxx352
-rw-r--r--sdext/source/presenter/PresenterSlidePreview.hxx145
-rw-r--r--sdext/source/presenter/PresenterSlideShowView.cxx953
-rw-r--r--sdext/source/presenter/PresenterSlideShowView.hxx241
-rw-r--r--sdext/source/presenter/PresenterSlideSorter.cxx1929
-rw-r--r--sdext/source/presenter/PresenterSlideSorter.hxx189
-rw-r--r--sdext/source/presenter/PresenterSprite.cxx163
-rw-r--r--sdext/source/presenter/PresenterSprite.hxx73
-rw-r--r--sdext/source/presenter/PresenterSpritePane.cxx172
-rw-r--r--sdext/source/presenter/PresenterSpritePane.hxx79
-rw-r--r--sdext/source/presenter/PresenterTextView.cxx1192
-rw-r--r--sdext/source/presenter/PresenterTextView.hxx279
-rw-r--r--sdext/source/presenter/PresenterTheme.cxx1060
-rw-r--r--sdext/source/presenter/PresenterTheme.hxx134
-rw-r--r--sdext/source/presenter/PresenterTimer.cxx571
-rw-r--r--sdext/source/presenter/PresenterTimer.hxx122
-rw-r--r--sdext/source/presenter/PresenterToolBar.cxx2015
-rw-r--r--sdext/source/presenter/PresenterToolBar.hxx250
-rw-r--r--sdext/source/presenter/PresenterUIPainter.cxx241
-rw-r--r--sdext/source/presenter/PresenterUIPainter.hxx56
-rw-r--r--sdext/source/presenter/PresenterViewFactory.cxx503
-rw-r--r--sdext/source/presenter/PresenterViewFactory.hxx164
-rw-r--r--sdext/source/presenter/PresenterWindowManager.cxx1043
-rw-r--r--sdext/source/presenter/PresenterWindowManager.hxx208
-rw-r--r--sdext/source/presenter/presenter.component18
67 files changed, 25949 insertions, 0 deletions
diff --git a/sdext/source/presenter/PresenterAccessibility.cxx b/sdext/source/presenter/PresenterAccessibility.cxx
new file mode 100644
index 000000000..e3f49ed1d
--- /dev/null
+++ b/sdext/source/presenter/PresenterAccessibility.cxx
@@ -0,0 +1,1849 @@
+/* -*- 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 "PresenterAccessibility.hxx"
+#include "PresenterTextView.hxx"
+#include "PresenterConfigurationAccess.hxx"
+#include "PresenterNotesView.hxx"
+#include "PresenterPaneBase.hxx"
+#include "PresenterPaneContainer.hxx"
+#include "PresenterPaneFactory.hxx"
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/accessibility/XAccessibleText.hpp>
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/drawing/framework/XPane.hpp>
+#include <com/sun/star/drawing/framework/XView.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+//===== PresenterAccessibleObject =============================================
+
+namespace sdext::presenter {
+
+namespace {
+ typedef ::cppu::WeakComponentImplHelper <
+ css::accessibility::XAccessible,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleComponent,
+ css::accessibility::XAccessibleEventBroadcaster,
+ css::awt::XWindowListener
+ > PresenterAccessibleObjectInterfaceBase;
+}
+
+class PresenterAccessible::AccessibleObject
+ : public ::cppu::BaseMutex,
+ public PresenterAccessibleObjectInterfaceBase
+{
+public:
+ AccessibleObject (
+ const css::lang::Locale& rLocale,
+ const sal_Int16 nRole,
+ const OUString& rsName);
+ void LateInitialization();
+
+ virtual void SetWindow (
+ const css::uno::Reference<css::awt::XWindow>& rxContentWindow,
+ const css::uno::Reference<css::awt::XWindow>& rxBorderWindow);
+ void SetAccessibleParent (const css::uno::Reference<css::accessibility::XAccessible>& rxAccessibleParent);
+
+ virtual void SAL_CALL disposing() override;
+
+ void AddChild (const ::rtl::Reference<AccessibleObject>& rpChild);
+ void RemoveChild (const ::rtl::Reference<AccessibleObject>& rpChild);
+
+ void SetIsFocused (const bool bIsFocused);
+ void SetAccessibleName (const OUString& rsName);
+
+ void FireAccessibleEvent (
+ const sal_Int16 nEventId,
+ const css::uno::Any& rOldValue,
+ const css::uno::Any& rNewValue);
+
+ void UpdateStateSet();
+
+ //----- XAccessible -------------------------------------------------------
+
+ virtual css::uno::Reference<css::accessibility::XAccessibleContext> SAL_CALL
+ getAccessibleContext() override;
+
+ //----- XAccessibleContext ----------------------------------------------
+
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount() override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild (sal_Int32 nIndex) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL getAccessibleParent() override;
+
+ virtual sal_Int32 SAL_CALL getAccessibleIndexInParent() override;
+
+ virtual sal_Int16 SAL_CALL getAccessibleRole() override;
+
+ virtual OUString SAL_CALL getAccessibleDescription() override;
+
+ virtual OUString SAL_CALL getAccessibleName() override;
+
+ virtual css::uno::Reference<css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+
+ virtual css::uno::Reference<css::accessibility::XAccessibleStateSet> SAL_CALL
+ getAccessibleStateSet() override;
+
+ virtual css::lang::Locale SAL_CALL getLocale() override;
+
+ //----- XAccessibleComponent --------------------------------------------
+
+ virtual sal_Bool SAL_CALL containsPoint (
+ const css::awt::Point& aPoint) override;
+
+ virtual css::uno::Reference<css::accessibility::XAccessible> SAL_CALL
+ getAccessibleAtPoint (
+ const css::awt::Point& aPoint) override;
+
+ virtual css::awt::Rectangle SAL_CALL getBounds() override;
+
+ virtual css::awt::Point SAL_CALL getLocation() override;
+
+ virtual css::awt::Point SAL_CALL getLocationOnScreen() override;
+
+ virtual css::awt::Size SAL_CALL getSize() override;
+
+ virtual void SAL_CALL grabFocus() override;
+
+ virtual sal_Int32 SAL_CALL getForeground() override;
+
+ virtual sal_Int32 SAL_CALL getBackground() override;
+
+ //----- XAccessibleEventBroadcaster --------------------------------------
+
+ virtual void SAL_CALL addAccessibleEventListener (
+ const css::uno::Reference<css::accessibility::XAccessibleEventListener>& rxListener) override;
+
+ virtual void SAL_CALL removeAccessibleEventListener (
+ const css::uno::Reference<css::accessibility::XAccessibleEventListener>& rxListener) override;
+
+ //----- XWindowListener ---------------------------------------------------
+
+ virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override;
+
+ //----- XEventListener ----------------------------------------------------
+
+ virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override;
+
+protected:
+ OUString msName;
+ css::uno::Reference<css::awt::XWindow2> mxContentWindow;
+ css::uno::Reference<css::awt::XWindow2> mxBorderWindow;
+ const css::lang::Locale maLocale;
+ const sal_Int16 mnRole;
+ sal_uInt32 mnStateSet;
+ bool mbIsFocused;
+ css::uno::Reference<css::accessibility::XAccessible> mxParentAccessible;
+ ::std::vector<rtl::Reference<AccessibleObject> > maChildren;
+ ::std::vector<Reference<XAccessibleEventListener> > maListeners;
+
+ virtual awt::Point GetRelativeLocation();
+ virtual awt::Size GetSize();
+ virtual awt::Point GetAbsoluteParentLocation();
+
+ virtual bool GetWindowState (const sal_Int16 nType) const;
+
+ void UpdateState (const sal_Int16 aState, const bool bValue);
+
+ /// @throws css::lang::DisposedException
+ void ThrowIfDisposed() const;
+};
+
+//===== AccessibleStateSet ====================================================
+
+namespace {
+typedef ::cppu::WeakComponentImplHelper <
+ css::accessibility::XAccessibleStateSet
+ > AccessibleStateSetInterfaceBase;
+
+class AccessibleStateSet
+ : public ::cppu::BaseMutex,
+ public AccessibleStateSetInterfaceBase
+{
+public:
+ explicit AccessibleStateSet (const sal_Int32 nStateSet);
+
+ static sal_uInt32 GetStateMask (const sal_Int16 nType);
+
+ //----- XAccessibleStateSet -----------------------------------------------
+
+ virtual sal_Bool SAL_CALL isEmpty() override;
+
+ virtual sal_Bool SAL_CALL contains (sal_Int16 nState) override;
+
+ virtual sal_Bool SAL_CALL containsAll (const css::uno::Sequence<sal_Int16>& rStateSet) override;
+
+ virtual css::uno::Sequence<sal_Int16> SAL_CALL getStates() override;
+
+private:
+ const sal_Int32 mnStateSet;
+};
+
+//===== AccessibleRelationSet =================================================
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::accessibility::XAccessibleRelationSet
+ > AccessibleRelationSetInterfaceBase;
+
+class AccessibleRelationSet
+ : public ::cppu::BaseMutex,
+ public AccessibleRelationSetInterfaceBase
+{
+public:
+ AccessibleRelationSet();
+
+ void AddRelation (
+ const sal_Int16 nRelationType,
+ const Reference<XInterface>& rxObject);
+
+ //----- XAccessibleRelationSet --------------------------------------------
+
+ virtual sal_Int32 SAL_CALL getRelationCount() override;
+
+ virtual AccessibleRelation SAL_CALL getRelation (sal_Int32 nIndex) override;
+
+ virtual sal_Bool SAL_CALL containsRelation (sal_Int16 nRelationType) override;
+
+ virtual AccessibleRelation SAL_CALL getRelationByType (sal_Int16 nRelationType) override;
+
+private:
+ ::std::vector<AccessibleRelation> maRelations;
+};
+
+//===== PresenterAccessibleParagraph ==========================================
+
+typedef ::cppu::ImplInheritanceHelper <
+ PresenterAccessible::AccessibleObject,
+ css::accessibility::XAccessibleText
+ > PresenterAccessibleParagraphInterfaceBase;
+}
+
+class PresenterAccessible::AccessibleParagraph
+ : public PresenterAccessibleParagraphInterfaceBase
+{
+public:
+ AccessibleParagraph (
+ const css::lang::Locale& rLocale,
+ const OUString& rsName,
+ const SharedPresenterTextParagraph& rpParagraph,
+ const sal_Int32 nParagraphIndex);
+
+ //----- XAccessibleContext ------------------------------------------------
+
+ virtual css::uno::Reference<css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+
+ //----- XAccessibleText ---------------------------------------------------
+
+ virtual sal_Int32 SAL_CALL getCaretPosition() override;
+
+ virtual sal_Bool SAL_CALL setCaretPosition (sal_Int32 nIndex) override;
+
+ virtual sal_Unicode SAL_CALL getCharacter (sal_Int32 nIndex) override;
+
+ virtual css::uno::Sequence<css::beans::PropertyValue> SAL_CALL
+ getCharacterAttributes (
+ ::sal_Int32 nIndex,
+ const css::uno::Sequence<OUString>& rRequestedAttributes) override;
+
+ virtual css::awt::Rectangle SAL_CALL getCharacterBounds (sal_Int32 nIndex) override;
+
+ virtual sal_Int32 SAL_CALL getCharacterCount() override;
+
+ virtual sal_Int32 SAL_CALL getIndexAtPoint (const css::awt::Point& rPoint) override;
+
+ virtual OUString SAL_CALL getSelectedText() override;
+
+ virtual sal_Int32 SAL_CALL getSelectionStart() override;
+
+ virtual sal_Int32 SAL_CALL getSelectionEnd() override;
+
+ virtual sal_Bool SAL_CALL setSelection (sal_Int32 nStartIndex, sal_Int32 nEndIndex) override;
+
+ virtual OUString SAL_CALL getText() override;
+
+ virtual OUString SAL_CALL getTextRange (
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex) override;
+
+ virtual css::accessibility::TextSegment SAL_CALL getTextAtIndex (
+ sal_Int32 nIndex,
+ sal_Int16 nTextType) override;
+
+ virtual css::accessibility::TextSegment SAL_CALL getTextBeforeIndex (
+ sal_Int32 nIndex,
+ sal_Int16 nTextType) override;
+
+ virtual css::accessibility::TextSegment SAL_CALL getTextBehindIndex (
+ sal_Int32 nIndex,
+ sal_Int16 nTextType) override;
+
+ virtual sal_Bool SAL_CALL copyText (sal_Int32 nStartIndex, sal_Int32 nEndIndex) override;
+
+ virtual sal_Bool SAL_CALL scrollSubstringTo(
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex,
+ css::accessibility::AccessibleScrollType aScrollType) override;
+
+protected:
+ virtual awt::Point GetRelativeLocation() override;
+ virtual awt::Size GetSize() override;
+ virtual awt::Point GetAbsoluteParentLocation() override;
+ virtual bool GetWindowState (const sal_Int16 nType) const override;
+
+private:
+ SharedPresenterTextParagraph mpParagraph;
+ const sal_Int32 mnParagraphIndex;
+};
+
+//===== AccessibleConsole =====================================================
+
+namespace {
+
+class AccessibleConsole
+{
+public:
+ static rtl::Reference<PresenterAccessible::AccessibleObject> Create (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const lang::Locale& rLocale)
+ {
+ OUString sName ("Presenter Console");
+ PresenterConfigurationAccess aConfiguration (
+ rxContext,
+ "/org.openoffice.Office.PresenterScreen/",
+ PresenterConfigurationAccess::READ_ONLY);
+ aConfiguration.GetConfigurationNode("Presenter/Accessibility/Console/String")
+ >>= sName;
+
+ rtl::Reference<PresenterAccessible::AccessibleObject> pObject (
+ new PresenterAccessible::AccessibleObject(
+ rLocale, AccessibleRole::PANEL, sName));
+ pObject->LateInitialization();
+ pObject->UpdateStateSet();
+
+ return pObject;
+ }
+};
+
+//===== AccessiblePreview =====================================================
+
+class AccessiblePreview
+{
+public:
+ static rtl::Reference<PresenterAccessible::AccessibleObject> Create (
+ const Reference<css::uno::XComponentContext>& rxContext,
+ const lang::Locale& rLocale,
+ const Reference<awt::XWindow>& rxContentWindow,
+ const Reference<awt::XWindow>& rxBorderWindow)
+ {
+ OUString sName ("Presenter Notes Window");
+ {
+ PresenterConfigurationAccess aConfiguration (
+ rxContext,
+ "/org.openoffice.Office.PresenterScreen/",
+ PresenterConfigurationAccess::READ_ONLY);
+ aConfiguration.GetConfigurationNode("Presenter/Accessibility/Preview/String")
+ >>= sName;
+ }
+
+ rtl::Reference<PresenterAccessible::AccessibleObject> pObject (
+ new PresenterAccessible::AccessibleObject(
+ rLocale,
+ AccessibleRole::LABEL,
+ sName));
+ pObject->LateInitialization();
+ pObject->UpdateStateSet();
+ pObject->SetWindow(rxContentWindow, rxBorderWindow);
+
+ return pObject;
+ }
+};
+
+//===== AccessibleNotes =======================================================
+
+class AccessibleNotes : public PresenterAccessible::AccessibleObject
+{
+public:
+ AccessibleNotes (
+ const css::lang::Locale& rLocale,
+ const OUString& rsName);
+
+ static rtl::Reference<PresenterAccessible::AccessibleObject> Create (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const lang::Locale& rLocale,
+ const Reference<awt::XWindow>& rxContentWindow,
+ const Reference<awt::XWindow>& rxBorderWindow,
+ const std::shared_ptr<PresenterTextView>& rpTextView);
+
+ void SetTextView (const std::shared_ptr<PresenterTextView>& rpTextView);
+
+ virtual void SetWindow (
+ const css::uno::Reference<css::awt::XWindow>& rxContentWindow,
+ const css::uno::Reference<css::awt::XWindow>& rxBorderWindow) override;
+
+private:
+ std::shared_ptr<PresenterTextView> mpTextView;
+
+ void NotifyCaretChange (
+ const sal_Int32 nOldParagraphIndex,
+ const sal_Int32 nOldCharacterIndex,
+ const sal_Int32 nNewParagraphIndex,
+ const sal_Int32 nNewCharacterIndex);
+};
+
+//===== AccessibleFocusManager ================================================
+
+/** A singleton class that makes sure that only one accessibility object in
+ the PresenterConsole hierarchy has the focus.
+*/
+class AccessibleFocusManager
+{
+public:
+ static std::shared_ptr<AccessibleFocusManager> const & Instance();
+
+ void AddFocusableObject (const ::rtl::Reference<PresenterAccessible::AccessibleObject>& rpObject);
+ void RemoveFocusableObject (const ::rtl::Reference<PresenterAccessible::AccessibleObject>& rpObject);
+
+ void FocusObject (const ::rtl::Reference<PresenterAccessible::AccessibleObject>& rpObject);
+
+ ~AccessibleFocusManager();
+
+private:
+ static std::shared_ptr<AccessibleFocusManager> mpInstance;
+ ::std::vector<rtl::Reference<PresenterAccessible::AccessibleObject> > maFocusableObjects;
+ bool m_isInDtor = false;
+
+ AccessibleFocusManager();
+};
+
+}
+
+//===== PresenterAccessible ===================================================
+
+PresenterAccessible::PresenterAccessible (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController,
+ const Reference<drawing::framework::XPane>& rxMainPane)
+ : PresenterAccessibleInterfaceBase(m_aMutex),
+ mxComponentContext(rxContext),
+ mpPresenterController(rpPresenterController),
+ mxMainPane(rxMainPane, UNO_QUERY)
+{
+ if (mxMainPane.is())
+ mxMainPane->setAccessible(this);
+}
+
+PresenterAccessible::~PresenterAccessible()
+{
+}
+
+PresenterPaneContainer::SharedPaneDescriptor PresenterAccessible::GetPreviewPane() const
+{
+ PresenterPaneContainer::SharedPaneDescriptor pPreviewPane;
+
+ if ( ! mpPresenterController.is())
+ return pPreviewPane;
+
+ rtl::Reference<PresenterPaneContainer> pContainer (mpPresenterController->GetPaneContainer());
+ if ( ! pContainer.is())
+ return pPreviewPane;
+
+ pPreviewPane = pContainer->FindPaneURL(PresenterPaneFactory::msCurrentSlidePreviewPaneURL);
+ Reference<drawing::framework::XPane> xPreviewPane;
+ if (pPreviewPane)
+ xPreviewPane = pPreviewPane->mxPane.get();
+ if ( ! xPreviewPane.is())
+ {
+ pPreviewPane = pContainer->FindPaneURL(PresenterPaneFactory::msSlideSorterPaneURL);
+ }
+ return pPreviewPane;
+}
+
+void PresenterAccessible::UpdateAccessibilityHierarchy()
+{
+ if ( ! mpPresenterController.is())
+ return;
+
+ Reference<drawing::framework::XConfigurationController> xConfigurationController(
+ mpPresenterController->GetConfigurationController());
+ if ( ! xConfigurationController.is())
+ return;
+
+ rtl::Reference<PresenterPaneContainer> pPaneContainer (
+ mpPresenterController->GetPaneContainer());
+ if ( ! pPaneContainer.is())
+ return;
+
+ if ( ! mpAccessibleConsole.is())
+ return;
+
+ // Get the preview pane (standard or notes view) or the slide overview
+ // pane.
+ PresenterPaneContainer::SharedPaneDescriptor pPreviewPane(GetPreviewPane());
+ Reference<drawing::framework::XPane> xPreviewPane;
+ if (pPreviewPane)
+ xPreviewPane = pPreviewPane->mxPane.get();
+
+ // Get the notes pane.
+ PresenterPaneContainer::SharedPaneDescriptor pNotesPane(
+ pPaneContainer->FindPaneURL(PresenterPaneFactory::msNotesPaneURL));
+ Reference<drawing::framework::XPane> xNotesPane;
+ if (pNotesPane)
+ xNotesPane = pNotesPane->mxPane.get();
+
+ // Get the notes view.
+ Reference<drawing::framework::XView> xNotesView;
+ if (pNotesPane)
+ xNotesView = pNotesPane->mxView;
+ rtl::Reference<PresenterNotesView> pNotesView (
+ dynamic_cast<PresenterNotesView*>(xNotesView.get()));
+
+ UpdateAccessibilityHierarchy(
+ pPreviewPane ? pPreviewPane->mxContentWindow : Reference<awt::XWindow>(),
+ pPreviewPane ? pPreviewPane->mxBorderWindow : Reference<awt::XWindow>(),
+ (pPreviewPane&&pPreviewPane->mxPane.is()) ? pPreviewPane->mxPane->GetTitle() : OUString(),
+ pNotesPane ? pNotesPane->mxContentWindow : Reference<awt::XWindow>(),
+ pNotesPane ? pNotesPane->mxBorderWindow : Reference<awt::XWindow>(),
+ pNotesView.is()
+ ? pNotesView->GetTextView()
+ : std::shared_ptr<PresenterTextView>());
+}
+
+void PresenterAccessible::UpdateAccessibilityHierarchy (
+ const Reference<awt::XWindow>& rxPreviewContentWindow,
+ const Reference<awt::XWindow>& rxPreviewBorderWindow,
+ const OUString& rsTitle,
+ const Reference<awt::XWindow>& rxNotesContentWindow,
+ const Reference<awt::XWindow>& rxNotesBorderWindow,
+ const std::shared_ptr<PresenterTextView>& rpNotesTextView)
+{
+ if ( ! mpAccessibleConsole.is())
+ return;
+
+ if (mxPreviewContentWindow != rxPreviewContentWindow)
+ {
+ if (mpAccessiblePreview.is())
+ {
+ mpAccessibleConsole->RemoveChild(mpAccessiblePreview);
+ mpAccessiblePreview = nullptr;
+ }
+
+ mxPreviewContentWindow = rxPreviewContentWindow;
+ mxPreviewBorderWindow = rxPreviewBorderWindow;
+
+ if (mxPreviewContentWindow.is())
+ {
+ mpAccessiblePreview = AccessiblePreview::Create(
+ mxComponentContext,
+ lang::Locale(),
+ mxPreviewContentWindow,
+ mxPreviewBorderWindow);
+ mpAccessibleConsole->AddChild(mpAccessiblePreview);
+ mpAccessiblePreview->SetAccessibleName(rsTitle);
+ }
+ }
+
+ if (mxNotesContentWindow == rxNotesContentWindow)
+ return;
+
+ if (mpAccessibleNotes.is())
+ {
+ mpAccessibleConsole->RemoveChild(mpAccessibleNotes);
+ mpAccessibleNotes = nullptr;
+ }
+
+ mxNotesContentWindow = rxNotesContentWindow;
+ mxNotesBorderWindow = rxNotesBorderWindow;
+
+ if (mxNotesContentWindow.is())
+ {
+ mpAccessibleNotes = AccessibleNotes::Create(
+ mxComponentContext,
+ lang::Locale(),
+ mxNotesContentWindow,
+ mxNotesBorderWindow,
+ rpNotesTextView);
+ mpAccessibleConsole->AddChild(mpAccessibleNotes);
+ }
+}
+
+void PresenterAccessible::NotifyCurrentSlideChange ()
+{
+ if (mpAccessiblePreview.is())
+ {
+ PresenterPaneContainer::SharedPaneDescriptor pPreviewPane (GetPreviewPane());
+ mpAccessiblePreview->SetAccessibleName(
+ pPreviewPane&&pPreviewPane->mxPane.is()
+ ? pPreviewPane->mxPane->GetTitle()
+ : OUString());
+ }
+
+ // Play some focus ping-pong to trigger AT tools.
+ //AccessibleFocusManager::Instance()->FocusObject(mpAccessibleConsole);
+ AccessibleFocusManager::Instance()->FocusObject(mpAccessiblePreview);
+}
+
+void SAL_CALL PresenterAccessible::disposing()
+{
+ UpdateAccessibilityHierarchy(
+ nullptr,
+ nullptr,
+ OUString(),
+ nullptr,
+ nullptr,
+ std::shared_ptr<PresenterTextView>());
+
+ if (mxMainWindow.is())
+ {
+ mxMainWindow->removeFocusListener(this);
+
+ if (mxMainPane.is())
+ mxMainPane->setAccessible(nullptr);
+ }
+
+ mpAccessiblePreview = nullptr;
+ mpAccessibleNotes = nullptr;
+ mpAccessibleConsole = nullptr;
+}
+
+//----- XAccessible -----------------------------------------------------------
+
+Reference<XAccessibleContext> SAL_CALL PresenterAccessible::getAccessibleContext()
+{
+ if ( ! mpAccessibleConsole.is())
+ {
+ Reference<XPane> xMainPane (mxMainPane, UNO_QUERY);
+ if (xMainPane.is())
+ {
+ mxMainWindow = xMainPane->getWindow();
+ mxMainWindow->addFocusListener(this);
+ }
+ mpAccessibleConsole = AccessibleConsole::Create(
+ mxComponentContext, css::lang::Locale());
+ mpAccessibleConsole->SetWindow(mxMainWindow, nullptr);
+ mpAccessibleConsole->SetAccessibleParent(mxAccessibleParent);
+ UpdateAccessibilityHierarchy();
+ if (mpPresenterController.is())
+ mpPresenterController->SetAccessibilityActiveState(true);
+ }
+ return mpAccessibleConsole->getAccessibleContext();
+}
+
+//----- XFocusListener ----------------------------------------------------
+
+void SAL_CALL PresenterAccessible::focusGained (const css::awt::FocusEvent&)
+{
+ SAL_INFO("sdext.presenter", __func__ << ": PresenterAccessible::focusGained at " << this
+ << " and window " << mxMainWindow.get());
+ AccessibleFocusManager::Instance()->FocusObject(mpAccessibleConsole);
+}
+
+void SAL_CALL PresenterAccessible::focusLost (const css::awt::FocusEvent&)
+{
+ SAL_INFO("sdext.presenter", __func__ << ": PresenterAccessible::focusLost at " << this);
+ AccessibleFocusManager::Instance()->FocusObject(nullptr);
+}
+
+//----- XEventListener ----------------------------------------------------
+
+void SAL_CALL PresenterAccessible::disposing (const css::lang::EventObject& rEvent)
+{
+ if (rEvent.Source == mxMainWindow)
+ mxMainWindow = nullptr;
+}
+
+//----- XInitialize -----------------------------------------------------------
+
+void SAL_CALL PresenterAccessible::initialize (const css::uno::Sequence<css::uno::Any>& rArguments)
+{
+ if (rArguments.hasElements())
+ {
+ mxAccessibleParent.set(rArguments[0], UNO_QUERY);
+ if (mpAccessibleConsole.is())
+ mpAccessibleConsole->SetAccessibleParent(mxAccessibleParent);
+ }
+}
+
+//===== PresenterAccessible::AccessibleObject =========================================
+
+PresenterAccessible::AccessibleObject::AccessibleObject (
+ const lang::Locale& rLocale,
+ const sal_Int16 nRole,
+ const OUString& rsName)
+ : PresenterAccessibleObjectInterfaceBase(m_aMutex),
+ msName(rsName),
+ maLocale(rLocale),
+ mnRole(nRole),
+ mnStateSet(0),
+ mbIsFocused(false)
+{
+}
+
+void PresenterAccessible::AccessibleObject::LateInitialization()
+{
+ AccessibleFocusManager::Instance()->AddFocusableObject(this);
+}
+
+void PresenterAccessible::AccessibleObject::SetWindow (
+ const Reference<awt::XWindow>& rxContentWindow,
+ const Reference<awt::XWindow>& rxBorderWindow)
+{
+ Reference<awt::XWindow2> xContentWindow (rxContentWindow, UNO_QUERY);
+
+ if (mxContentWindow.get() == xContentWindow.get())
+ return;
+
+ if (mxContentWindow.is())
+ {
+ mxContentWindow->removeWindowListener(this);
+ }
+
+ mxContentWindow = xContentWindow;
+ mxBorderWindow.set(rxBorderWindow, UNO_QUERY);
+
+ if (mxContentWindow.is())
+ {
+ mxContentWindow->addWindowListener(this);
+ }
+
+ UpdateStateSet();
+}
+
+void PresenterAccessible::AccessibleObject::SetAccessibleParent (
+ const Reference<XAccessible>& rxAccessibleParent)
+{
+ mxParentAccessible = rxAccessibleParent;
+}
+
+void SAL_CALL PresenterAccessible::AccessibleObject::disposing()
+{
+ AccessibleFocusManager::Instance()->RemoveFocusableObject(this);
+ SetWindow(nullptr, nullptr);
+}
+
+//----- XAccessible -------------------------------------------------------
+
+Reference<XAccessibleContext> SAL_CALL
+ PresenterAccessible::AccessibleObject::getAccessibleContext()
+{
+ ThrowIfDisposed();
+
+ return this;
+}
+
+//----- XAccessibleContext ----------------------------------------------
+
+sal_Int32 SAL_CALL PresenterAccessible::AccessibleObject::getAccessibleChildCount()
+{
+ ThrowIfDisposed();
+
+ const sal_Int32 nChildCount (maChildren.size());
+
+ return nChildCount;
+}
+
+Reference<XAccessible> SAL_CALL
+ PresenterAccessible::AccessibleObject::getAccessibleChild (sal_Int32 nIndex)
+{
+ ThrowIfDisposed();
+
+ if (nIndex<0 || o3tl::make_unsigned(nIndex)>=maChildren.size())
+ throw lang::IndexOutOfBoundsException("invalid child index", static_cast<uno::XWeak*>(this));
+
+ return maChildren[nIndex];
+}
+
+Reference<XAccessible> SAL_CALL
+ PresenterAccessible::AccessibleObject::getAccessibleParent()
+{
+ ThrowIfDisposed();
+
+ return mxParentAccessible;
+}
+
+sal_Int32 SAL_CALL
+ PresenterAccessible::AccessibleObject::getAccessibleIndexInParent()
+{
+ ThrowIfDisposed();
+
+ const Reference<XAccessible> xThis (this);
+ if (mxParentAccessible.is())
+ {
+ const Reference<XAccessibleContext> xContext (mxParentAccessible->getAccessibleContext());
+ for (sal_Int32 nIndex=0,nCount=xContext->getAccessibleChildCount();
+ nIndex<nCount;
+ ++nIndex)
+ {
+ if (xContext->getAccessibleChild(nIndex) == xThis)
+ return nIndex;
+ }
+ }
+
+ return 0;
+}
+
+sal_Int16 SAL_CALL
+ PresenterAccessible::AccessibleObject::getAccessibleRole()
+{
+ ThrowIfDisposed();
+
+ return mnRole;
+}
+
+OUString SAL_CALL
+ PresenterAccessible::AccessibleObject::getAccessibleDescription()
+{
+ ThrowIfDisposed();
+
+ return msName;
+}
+
+OUString SAL_CALL
+ PresenterAccessible::AccessibleObject::getAccessibleName()
+{
+ ThrowIfDisposed();
+
+ return msName;
+}
+
+Reference<XAccessibleRelationSet> SAL_CALL
+ PresenterAccessible::AccessibleObject::getAccessibleRelationSet()
+{
+ ThrowIfDisposed();
+
+ return nullptr;
+}
+
+Reference<XAccessibleStateSet> SAL_CALL
+ PresenterAccessible::AccessibleObject::getAccessibleStateSet()
+{
+ ThrowIfDisposed();
+
+ return Reference<XAccessibleStateSet>(new AccessibleStateSet(mnStateSet));
+}
+
+lang::Locale SAL_CALL
+ PresenterAccessible::AccessibleObject::getLocale()
+{
+ ThrowIfDisposed();
+
+ if (mxParentAccessible.is())
+ {
+ Reference<XAccessibleContext> xParentContext (mxParentAccessible->getAccessibleContext());
+ if (xParentContext.is())
+ return xParentContext->getLocale();
+ }
+ return maLocale;
+}
+
+//----- XAccessibleComponent ------------------------------------------------
+
+sal_Bool SAL_CALL PresenterAccessible::AccessibleObject::containsPoint (
+ const awt::Point& rPoint)
+{
+ ThrowIfDisposed();
+
+ if (mxContentWindow.is())
+ {
+ const awt::Rectangle aBox (getBounds());
+ return rPoint.X>=aBox.X
+ && rPoint.Y>=aBox.Y
+ && rPoint.X<aBox.X+aBox.Width
+ && rPoint.Y<aBox.Y+aBox.Height;
+ }
+ else
+ return false;
+}
+
+Reference<XAccessible> SAL_CALL
+ PresenterAccessible::AccessibleObject::getAccessibleAtPoint (const awt::Point&)
+{
+ ThrowIfDisposed();
+
+ return Reference<XAccessible>();
+}
+
+awt::Rectangle SAL_CALL PresenterAccessible::AccessibleObject::getBounds()
+{
+ ThrowIfDisposed();
+
+ const awt::Point aLocation (GetRelativeLocation());
+ const awt::Size aSize (GetSize());
+
+ return awt::Rectangle (aLocation.X, aLocation.Y, aSize.Width, aSize.Height);
+}
+
+awt::Point SAL_CALL PresenterAccessible::AccessibleObject::getLocation()
+{
+ ThrowIfDisposed();
+
+ const awt::Point aLocation (GetRelativeLocation());
+
+ return aLocation;
+}
+
+awt::Point SAL_CALL PresenterAccessible::AccessibleObject::getLocationOnScreen()
+{
+ ThrowIfDisposed();
+
+ awt::Point aRelativeLocation (GetRelativeLocation());
+ awt::Point aParentLocationOnScreen (GetAbsoluteParentLocation());
+
+ return awt::Point(
+ aRelativeLocation.X + aParentLocationOnScreen.X,
+ aRelativeLocation.Y + aParentLocationOnScreen.Y);
+}
+
+awt::Size SAL_CALL PresenterAccessible::AccessibleObject::getSize()
+{
+ ThrowIfDisposed();
+
+ const awt::Size aSize (GetSize());
+
+ return aSize;
+}
+
+void SAL_CALL PresenterAccessible::AccessibleObject::grabFocus()
+{
+ ThrowIfDisposed();
+ if (mxBorderWindow.is())
+ mxBorderWindow->setFocus();
+ else if (mxContentWindow.is())
+ mxContentWindow->setFocus();
+}
+
+sal_Int32 SAL_CALL PresenterAccessible::AccessibleObject::getForeground()
+{
+ ThrowIfDisposed();
+
+ return 0x00ffffff;
+}
+
+sal_Int32 SAL_CALL PresenterAccessible::AccessibleObject::getBackground()
+{
+ ThrowIfDisposed();
+
+ return 0x00000000;
+}
+
+//----- XAccessibleEventBroadcaster -------------------------------------------
+
+void SAL_CALL PresenterAccessible::AccessibleObject::addAccessibleEventListener (
+ const Reference<XAccessibleEventListener>& rxListener)
+{
+ if (!rxListener.is())
+ return;
+
+ const osl::MutexGuard aGuard(m_aMutex);
+
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ uno::Reference<uno::XInterface> xThis (static_cast<XWeak*>(this), UNO_QUERY);
+ rxListener->disposing (lang::EventObject(xThis));
+ }
+ else
+ {
+ maListeners.push_back(rxListener);
+ }
+}
+
+void SAL_CALL PresenterAccessible::AccessibleObject::removeAccessibleEventListener (
+ const Reference<XAccessibleEventListener>& rxListener)
+{
+ ThrowIfDisposed();
+ if (rxListener.is())
+ {
+ const osl::MutexGuard aGuard(m_aMutex);
+
+ auto const it(std::remove(maListeners.begin(), maListeners.end(), rxListener));
+ if (it != maListeners.end())
+ {
+ maListeners.erase(it);
+ }
+ }
+}
+
+//----- XWindowListener ---------------------------------------------------
+
+void SAL_CALL PresenterAccessible::AccessibleObject::windowResized (
+ const css::awt::WindowEvent&)
+{
+ FireAccessibleEvent(AccessibleEventId::BOUNDRECT_CHANGED, Any(), Any());
+}
+
+void SAL_CALL PresenterAccessible::AccessibleObject::windowMoved (
+ const css::awt::WindowEvent&)
+{
+ FireAccessibleEvent(AccessibleEventId::BOUNDRECT_CHANGED, Any(), Any());
+}
+
+void SAL_CALL PresenterAccessible::AccessibleObject::windowShown (
+ const css::lang::EventObject&)
+{
+ UpdateStateSet();
+}
+
+void SAL_CALL PresenterAccessible::AccessibleObject::windowHidden (
+ const css::lang::EventObject&)
+{
+ UpdateStateSet();
+}
+
+//----- XEventListener --------------------------------------------------------
+
+void SAL_CALL PresenterAccessible::AccessibleObject::disposing (const css::lang::EventObject& rEvent)
+{
+ if (rEvent.Source == mxContentWindow)
+ {
+ mxContentWindow = nullptr;
+ mxBorderWindow = nullptr;
+ }
+ else
+ {
+ SetWindow(nullptr, nullptr);
+ }
+}
+
+//----- private ---------------------------------------------------------------
+
+bool PresenterAccessible::AccessibleObject::GetWindowState (const sal_Int16 nType) const
+{
+ switch (nType)
+ {
+ case AccessibleStateType::ENABLED:
+ return mxContentWindow.is() && mxContentWindow->isEnabled();
+
+ case AccessibleStateType::FOCUSABLE:
+ return true;
+
+ case AccessibleStateType::FOCUSED:
+ return mbIsFocused;
+
+ case AccessibleStateType::SHOWING:
+ return mxContentWindow.is() && mxContentWindow->isVisible();
+
+ default:
+ return false;
+ }
+}
+
+void PresenterAccessible::AccessibleObject::UpdateStateSet()
+{
+ UpdateState(AccessibleStateType::FOCUSABLE, true);
+ UpdateState(AccessibleStateType::VISIBLE, true);
+ UpdateState(AccessibleStateType::ENABLED, true);
+ UpdateState(AccessibleStateType::MULTI_LINE, true);
+ UpdateState(AccessibleStateType::SENSITIVE, true);
+
+ UpdateState(AccessibleStateType::ENABLED, GetWindowState(AccessibleStateType::ENABLED));
+ UpdateState(AccessibleStateType::FOCUSED, GetWindowState(AccessibleStateType::FOCUSED));
+ UpdateState(AccessibleStateType::SHOWING, GetWindowState(AccessibleStateType::SHOWING));
+ // UpdateState(AccessibleStateType::ACTIVE, GetWindowState(AccessibleStateType::ACTIVE));
+}
+
+void PresenterAccessible::AccessibleObject::UpdateState(
+ const sal_Int16 nState,
+ const bool bValue)
+{
+ const sal_uInt32 nStateMask (AccessibleStateSet::GetStateMask(nState));
+ if (((mnStateSet & nStateMask) != 0) == bValue)
+ return;
+ if (bValue)
+ {
+ mnStateSet |= nStateMask;
+ FireAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(), Any(nState));
+ }
+ else
+ {
+ mnStateSet &= ~nStateMask;
+ FireAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(nState), Any());
+ }
+}
+
+void PresenterAccessible::AccessibleObject::AddChild (
+ const ::rtl::Reference<AccessibleObject>& rpChild)
+{
+ maChildren.push_back(rpChild);
+ rpChild->SetAccessibleParent(this);
+ FireAccessibleEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN, Any(), Any());
+}
+
+void PresenterAccessible::AccessibleObject::RemoveChild (
+ const ::rtl::Reference<AccessibleObject>& rpChild)
+{
+ rpChild->SetAccessibleParent(Reference<XAccessible>());
+ maChildren.erase(::std::find(maChildren.begin(), maChildren.end(), rpChild));
+ FireAccessibleEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN, Any(), Any());
+}
+
+void PresenterAccessible::AccessibleObject::SetIsFocused (const bool bIsFocused)
+{
+ if (mbIsFocused != bIsFocused)
+ {
+ mbIsFocused = bIsFocused;
+ UpdateStateSet();
+ }
+}
+
+void PresenterAccessible::AccessibleObject::SetAccessibleName (const OUString& rsName)
+{
+ if (msName != rsName)
+ {
+ const OUString sOldName(msName);
+ msName = rsName;
+ FireAccessibleEvent(AccessibleEventId::NAME_CHANGED, Any(sOldName), Any(msName));
+ }
+}
+
+void PresenterAccessible::AccessibleObject::FireAccessibleEvent (
+ const sal_Int16 nEventId,
+ const uno::Any& rOldValue,
+ const uno::Any& rNewValue )
+{
+ AccessibleEventObject aEventObject;
+
+ aEventObject.Source = Reference<XWeak>(this);
+ aEventObject.EventId = nEventId;
+ aEventObject.NewValue = rNewValue;
+ aEventObject.OldValue = rOldValue;
+
+ ::std::vector<Reference<XAccessibleEventListener> > aListenerCopy(maListeners);
+ for (const auto& rxListener : aListenerCopy)
+ {
+ try
+ {
+ rxListener->notifyEvent(aEventObject);
+ }
+ catch (const lang::DisposedException&)
+ {
+ // Listener has been disposed and should have been removed
+ // already.
+ removeAccessibleEventListener(rxListener);
+ }
+ catch (const Exception&)
+ {
+ // Ignore all other exceptions and assume that they are
+ // caused by a temporary problem.
+ }
+ }
+}
+
+awt::Point PresenterAccessible::AccessibleObject::GetRelativeLocation()
+{
+ awt::Point aLocation;
+ if (mxContentWindow.is())
+ {
+ const awt::Rectangle aContentBox (mxContentWindow->getPosSize());
+ aLocation.X = aContentBox.X;
+ aLocation.Y = aContentBox.Y;
+ if (mxBorderWindow.is())
+ {
+ const awt::Rectangle aBorderBox (mxBorderWindow->getPosSize());
+ aLocation.X += aBorderBox.X;
+ aLocation.Y += aBorderBox.Y;
+ }
+ }
+ return aLocation;
+}
+
+awt::Size PresenterAccessible::AccessibleObject::GetSize()
+{
+ if (mxContentWindow.is())
+ {
+ const awt::Rectangle aBox (mxContentWindow->getPosSize());
+ return awt::Size(aBox.Width, aBox.Height);
+ }
+ else
+ return awt::Size();
+}
+
+awt::Point PresenterAccessible::AccessibleObject::GetAbsoluteParentLocation()
+{
+ Reference<XAccessibleComponent> xParentComponent;
+ if (mxParentAccessible.is())
+ xParentComponent.set( mxParentAccessible->getAccessibleContext(), UNO_QUERY);
+ if (xParentComponent.is())
+ return xParentComponent->getLocationOnScreen();
+ else
+ return awt::Point();
+}
+
+void PresenterAccessible::AccessibleObject::ThrowIfDisposed() const
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ throw lang::DisposedException("object has already been disposed", uno::Reference<uno::XInterface>(const_cast<uno::XWeak*>(static_cast<uno::XWeak const *>(this))));
+}
+
+//===== AccessibleStateSet ====================================================
+
+AccessibleStateSet::AccessibleStateSet (const sal_Int32 nStateSet)
+ : AccessibleStateSetInterfaceBase(m_aMutex),
+ mnStateSet (nStateSet)
+{
+}
+
+sal_uInt32 AccessibleStateSet::GetStateMask (const sal_Int16 nState)
+{
+ if (nState<0 || o3tl::make_unsigned(nState)>=sizeof(sal_uInt32)*8)
+ {
+ throw RuntimeException("AccessibleStateSet::GetStateMask: invalid state");
+ }
+
+ return 1<<nState;
+}
+
+//----- XAccessibleStateSet ---------------------------------------------------
+
+sal_Bool SAL_CALL AccessibleStateSet::isEmpty()
+{
+ return mnStateSet==0;
+}
+
+sal_Bool SAL_CALL AccessibleStateSet::contains (sal_Int16 nState)
+{
+ return (mnStateSet & GetStateMask(nState)) != 0;
+}
+
+sal_Bool SAL_CALL AccessibleStateSet::containsAll (const css::uno::Sequence<sal_Int16>& rStateSet)
+{
+ return std::none_of(rStateSet.begin(), rStateSet.end(),
+ [this](const sal_Int16 nState) { return (mnStateSet & GetStateMask(nState)) == 0; });
+}
+
+css::uno::Sequence<sal_Int16> SAL_CALL AccessibleStateSet::getStates()
+{
+ ::std::vector<sal_Int16> aStates;
+ aStates.reserve(sizeof(mnStateSet)*8);
+ for (sal_uInt16 nIndex=0; nIndex<sizeof(mnStateSet)*8; ++nIndex)
+ if ((mnStateSet & GetStateMask(nIndex)) != 0)
+ aStates.push_back(nIndex);
+ return Sequence<sal_Int16>(aStates.data(), aStates.size());
+}
+
+//===== AccessibleRelationSet =================================================
+
+AccessibleRelationSet::AccessibleRelationSet()
+ : AccessibleRelationSetInterfaceBase(m_aMutex)
+{
+}
+
+void AccessibleRelationSet::AddRelation (
+ const sal_Int16 nRelationType,
+ const Reference<XInterface>& rxObject)
+{
+ maRelations.emplace_back();
+ maRelations.back().RelationType = nRelationType;
+ maRelations.back().TargetSet = { rxObject };
+}
+
+//----- XAccessibleRelationSet ------------------------------------------------
+
+sal_Int32 SAL_CALL AccessibleRelationSet::getRelationCount()
+{
+ return maRelations.size();
+}
+
+AccessibleRelation SAL_CALL AccessibleRelationSet::getRelation (sal_Int32 nIndex)
+{
+ if (nIndex<0 && o3tl::make_unsigned(nIndex)>=maRelations.size())
+ return AccessibleRelation();
+ else
+ return maRelations[nIndex];
+}
+
+sal_Bool SAL_CALL AccessibleRelationSet::containsRelation (sal_Int16 nRelationType)
+{
+ return std::any_of(maRelations.begin(), maRelations.end(),
+ [nRelationType](const AccessibleRelation& rRelation) { return rRelation.RelationType == nRelationType; });
+}
+
+AccessibleRelation SAL_CALL AccessibleRelationSet::getRelationByType (sal_Int16 nRelationType)
+{
+ auto iRelation = std::find_if(maRelations.begin(), maRelations.end(),
+ [nRelationType](const AccessibleRelation& rRelation) { return rRelation.RelationType == nRelationType; });
+ if (iRelation != maRelations.end())
+ return *iRelation;
+ return AccessibleRelation();
+}
+
+//===== PresenterAccessible::AccessibleParagraph ==============================
+
+PresenterAccessible::AccessibleParagraph::AccessibleParagraph (
+ const lang::Locale& rLocale,
+ const OUString& rsName,
+ const SharedPresenterTextParagraph& rpParagraph,
+ const sal_Int32 nParagraphIndex)
+ : PresenterAccessibleParagraphInterfaceBase(rLocale, AccessibleRole::PARAGRAPH, rsName),
+ mpParagraph(rpParagraph),
+ mnParagraphIndex(nParagraphIndex)
+{
+}
+
+//----- XAccessibleContext ----------------------------------------------------
+
+Reference<XAccessibleRelationSet> SAL_CALL
+ PresenterAccessible::AccessibleParagraph::getAccessibleRelationSet()
+{
+ ThrowIfDisposed();
+
+ rtl::Reference<AccessibleRelationSet> pSet (new AccessibleRelationSet);
+
+ if (mxParentAccessible.is())
+ {
+ Reference<XAccessibleContext> xParentContext (mxParentAccessible->getAccessibleContext());
+ if (xParentContext.is())
+ {
+ if (mnParagraphIndex>0)
+ pSet->AddRelation(
+ AccessibleRelationType::CONTENT_FLOWS_FROM,
+ xParentContext->getAccessibleChild(mnParagraphIndex-1));
+
+ if (mnParagraphIndex<xParentContext->getAccessibleChildCount()-1)
+ pSet->AddRelation(
+ AccessibleRelationType::CONTENT_FLOWS_TO,
+ xParentContext->getAccessibleChild(mnParagraphIndex+1));
+ }
+ }
+
+ return pSet;
+}
+
+//----- XAccessibleText -------------------------------------------------------
+
+sal_Int32 SAL_CALL PresenterAccessible::AccessibleParagraph::getCaretPosition()
+{
+ ThrowIfDisposed();
+
+ sal_Int32 nPosition (-1);
+ if (mpParagraph)
+ nPosition = mpParagraph->GetCaretPosition();
+
+ return nPosition;
+}
+
+sal_Bool SAL_CALL PresenterAccessible::AccessibleParagraph::setCaretPosition (sal_Int32 nIndex)
+{
+ ThrowIfDisposed();
+
+ if (mpParagraph)
+ {
+ mpParagraph->SetCaretPosition(nIndex);
+ return true;
+ }
+ else
+ return false;
+}
+
+sal_Unicode SAL_CALL PresenterAccessible::AccessibleParagraph::getCharacter (sal_Int32 nIndex)
+{
+ ThrowIfDisposed();
+
+ if (!mpParagraph)
+ throw lang::IndexOutOfBoundsException("no text support in current mode", static_cast<uno::XWeak*>(this));
+ return mpParagraph->GetCharacter(nIndex);
+}
+
+Sequence<css::beans::PropertyValue> SAL_CALL
+ PresenterAccessible::AccessibleParagraph::getCharacterAttributes (
+ ::sal_Int32 nIndex,
+ const css::uno::Sequence<OUString>& rRequestedAttributes)
+{
+ ThrowIfDisposed();
+
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO( "sdext.presenter", __func__ << " at " << this << ", " << nIndex << " returns empty set" );
+ for (sal_Int32 nAttributeIndex(0), nAttributeCount(rRequestedAttributes.getLength());
+ nAttributeIndex < nAttributeCount;
+ ++nAttributeIndex)
+ {
+ SAL_INFO( "sdext.presenter",
+ " requested attribute " << nAttributeIndex << " is " << rRequestedAttributes[nAttributeIndex] );
+ }
+#else
+ (void)nIndex;
+ (void)rRequestedAttributes;
+#endif
+
+ // Character properties are not supported.
+ return Sequence<css::beans::PropertyValue>();
+}
+
+awt::Rectangle SAL_CALL PresenterAccessible::AccessibleParagraph::getCharacterBounds (
+ sal_Int32 nIndex)
+{
+ ThrowIfDisposed();
+
+ awt::Rectangle aCharacterBox;
+ if (nIndex < 0)
+ {
+ throw lang::IndexOutOfBoundsException("invalid text index", static_cast<uno::XWeak*>(this));
+ }
+ else if (mpParagraph)
+ {
+ aCharacterBox = mpParagraph->GetCharacterBounds(nIndex, false);
+ // Convert coordinates relative to the window origin into absolute
+ // screen coordinates.
+ const awt::Point aWindowLocationOnScreen (getLocationOnScreen());
+ aCharacterBox.X += aWindowLocationOnScreen.X;
+ aCharacterBox.Y += aWindowLocationOnScreen.Y;
+ }
+ else
+ {
+ throw lang::IndexOutOfBoundsException("no text support in current mode", static_cast<uno::XWeak*>(this));
+ }
+
+ return aCharacterBox;
+}
+
+sal_Int32 SAL_CALL PresenterAccessible::AccessibleParagraph::getCharacterCount()
+{
+ ThrowIfDisposed();
+
+ sal_Int32 nCount (0);
+ if (mpParagraph)
+ nCount = mpParagraph->GetCharacterCount();
+
+ return nCount;
+}
+
+sal_Int32 SAL_CALL PresenterAccessible::AccessibleParagraph::getIndexAtPoint (
+ const css::awt::Point& )
+{
+ ThrowIfDisposed();
+ return -1;
+}
+
+OUString SAL_CALL PresenterAccessible::AccessibleParagraph::getSelectedText()
+{
+ ThrowIfDisposed();
+
+ return getTextRange(getSelectionStart(), getSelectionEnd());
+}
+
+sal_Int32 SAL_CALL PresenterAccessible::AccessibleParagraph::getSelectionStart()
+{
+ ThrowIfDisposed();
+
+ return getCaretPosition();
+}
+
+sal_Int32 SAL_CALL PresenterAccessible::AccessibleParagraph::getSelectionEnd()
+{
+ ThrowIfDisposed();
+
+ return getCaretPosition();
+}
+
+sal_Bool SAL_CALL PresenterAccessible::AccessibleParagraph::setSelection (
+ sal_Int32 nStartIndex,
+ sal_Int32)
+{
+ ThrowIfDisposed();
+
+ return setCaretPosition(nStartIndex);
+}
+
+OUString SAL_CALL PresenterAccessible::AccessibleParagraph::getText()
+{
+ ThrowIfDisposed();
+
+ OUString sText;
+ if (mpParagraph)
+ sText = mpParagraph->GetText();
+
+ return sText;
+}
+
+OUString SAL_CALL PresenterAccessible::AccessibleParagraph::getTextRange (
+ sal_Int32 nLocalStartIndex,
+ sal_Int32 nLocalEndIndex)
+{
+ ThrowIfDisposed();
+
+ OUString sText;
+ if (mpParagraph)
+ {
+ const TextSegment aSegment (
+ mpParagraph->CreateTextSegment(nLocalStartIndex, nLocalEndIndex));
+ sText = aSegment.SegmentText;
+ }
+
+ return sText;
+}
+
+TextSegment SAL_CALL PresenterAccessible::AccessibleParagraph::getTextAtIndex (
+ sal_Int32 nLocalCharacterIndex,
+ sal_Int16 nTextType)
+{
+ ThrowIfDisposed();
+
+ TextSegment aSegment;
+ if (mpParagraph)
+ aSegment = mpParagraph->GetTextSegment(0, nLocalCharacterIndex, nTextType);
+
+ return aSegment;
+}
+
+TextSegment SAL_CALL PresenterAccessible::AccessibleParagraph::getTextBeforeIndex (
+ sal_Int32 nLocalCharacterIndex,
+ sal_Int16 nTextType)
+{
+ ThrowIfDisposed();
+
+ TextSegment aSegment;
+ if (mpParagraph)
+ aSegment = mpParagraph->GetTextSegment(-1, nLocalCharacterIndex, nTextType);
+
+ return aSegment;
+}
+
+TextSegment SAL_CALL PresenterAccessible::AccessibleParagraph::getTextBehindIndex (
+ sal_Int32 nLocalCharacterIndex,
+ sal_Int16 nTextType)
+{
+ ThrowIfDisposed();
+
+ TextSegment aSegment;
+ if (mpParagraph)
+ aSegment = mpParagraph->GetTextSegment(+1, nLocalCharacterIndex, nTextType);
+
+ return aSegment;
+}
+
+sal_Bool SAL_CALL PresenterAccessible::AccessibleParagraph::copyText (
+ sal_Int32,
+ sal_Int32)
+{
+ ThrowIfDisposed();
+
+ // Return false because copying to clipboard is not supported.
+ // It IS supported in the notes view. There is no need to duplicate
+ // this here.
+ return false;
+}
+
+sal_Bool SAL_CALL PresenterAccessible::AccessibleParagraph::scrollSubstringTo(
+ sal_Int32,
+ sal_Int32,
+ AccessibleScrollType)
+{
+ return false;
+}
+
+//----- protected -------------------------------------------------------------
+
+awt::Point PresenterAccessible::AccessibleParagraph::GetRelativeLocation()
+{
+ awt::Point aLocation (AccessibleObject::GetRelativeLocation());
+ if (mpParagraph)
+ {
+ const awt::Point aParagraphLocation (mpParagraph->GetRelativeLocation());
+ aLocation.X += aParagraphLocation.X;
+ aLocation.Y += aParagraphLocation.Y;
+ }
+
+ return aLocation;
+}
+
+awt::Size PresenterAccessible::AccessibleParagraph::GetSize()
+{
+ if (mpParagraph)
+ return mpParagraph->GetSize();
+ else
+ return AccessibleObject::GetSize();
+}
+
+awt::Point PresenterAccessible::AccessibleParagraph::GetAbsoluteParentLocation()
+{
+ if (mxParentAccessible.is())
+ {
+ Reference<XAccessibleContext> xParentContext =
+ mxParentAccessible->getAccessibleContext();
+ if (xParentContext.is())
+ {
+ Reference<XAccessibleComponent> xGrandParentComponent(
+ xParentContext->getAccessibleParent(), UNO_QUERY);
+ if (xGrandParentComponent.is())
+ return xGrandParentComponent->getLocationOnScreen();
+ }
+ }
+
+ return awt::Point();
+}
+
+bool PresenterAccessible::AccessibleParagraph::GetWindowState (const sal_Int16 nType) const
+{
+ switch (nType)
+ {
+ case AccessibleStateType::EDITABLE:
+ return bool(mpParagraph);
+
+ case AccessibleStateType::ACTIVE:
+ return true;
+
+ default:
+ return AccessibleObject::GetWindowState(nType);
+ }
+}
+
+//===== AccessibleNotes =======================================================
+
+AccessibleNotes::AccessibleNotes (
+ const css::lang::Locale& rLocale,
+ const OUString& rsName)
+ : AccessibleObject(rLocale,AccessibleRole::PANEL,rsName)
+{
+}
+
+rtl::Reference<PresenterAccessible::AccessibleObject> AccessibleNotes::Create (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const lang::Locale& rLocale,
+ const Reference<awt::XWindow>& rxContentWindow,
+ const Reference<awt::XWindow>& rxBorderWindow,
+ const std::shared_ptr<PresenterTextView>& rpTextView)
+{
+ OUString sName ("Presenter Notes Text");
+ {
+ PresenterConfigurationAccess aConfiguration (
+ rxContext,
+ "/org.openoffice.Office.PresenterScreen/",
+ PresenterConfigurationAccess::READ_ONLY);
+ aConfiguration.GetConfigurationNode("Presenter/Accessibility/Notes/String")
+ >>= sName;
+ }
+
+ rtl::Reference<AccessibleNotes> pObject (
+ new AccessibleNotes(
+ rLocale,
+ sName));
+ pObject->LateInitialization();
+ pObject->SetTextView(rpTextView);
+ pObject->UpdateStateSet();
+ pObject->SetWindow(rxContentWindow, rxBorderWindow);
+
+ return pObject;
+}
+
+void AccessibleNotes::SetTextView (
+ const std::shared_ptr<PresenterTextView>& rpTextView)
+{
+ ::std::vector<rtl::Reference<PresenterAccessible::AccessibleObject> > aChildren;
+
+ // Release any listeners to the current text view.
+ if (mpTextView)
+ {
+ mpTextView->GetCaret()->SetCaretMotionBroadcaster(
+ ::std::function<void (sal_Int32,sal_Int32,sal_Int32,sal_Int32)>());
+ mpTextView->SetTextChangeBroadcaster(
+ ::std::function<void ()>());
+ }
+
+ mpTextView = rpTextView;
+
+ if (!mpTextView)
+ return;
+
+ // Create a new set of children, one for each paragraph.
+ const sal_Int32 nParagraphCount (mpTextView->GetParagraphCount());
+ for (sal_Int32 nIndex=0; nIndex<nParagraphCount; ++nIndex)
+ {
+ rtl::Reference<PresenterAccessible::AccessibleParagraph> pParagraph (
+ new PresenterAccessible::AccessibleParagraph(
+ css::lang::Locale(),
+ "Paragraph"+OUString::number(nIndex),
+ rpTextView->GetParagraph(nIndex),
+ nIndex));
+ pParagraph->LateInitialization();
+ pParagraph->SetWindow(mxContentWindow, mxBorderWindow);
+ pParagraph->SetAccessibleParent(this);
+ aChildren.emplace_back(pParagraph.get());
+ }
+ maChildren.swap(aChildren);
+ FireAccessibleEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN, Any(), Any());
+
+ // Dispose the old children. (This will remove them from the focus
+ // manager).
+ for (const auto& rxChild : aChildren)
+ {
+ Reference<lang::XComponent> xComponent = rxChild;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ // This class acts as a controller of who broadcasts caret motion
+ // events and handles text changes. Register the corresponding
+ // listeners here.
+ mpTextView->GetCaret()->SetCaretMotionBroadcaster(
+ [this](sal_Int32 a, sal_Int32 b, sal_Int32 c, sal_Int32 d)
+ { return this->NotifyCaretChange(a, b, c, d); });
+ mpTextView->SetTextChangeBroadcaster(
+ [this]() { return SetTextView(mpTextView); });
+}
+
+void AccessibleNotes::SetWindow (
+ const css::uno::Reference<css::awt::XWindow>& rxContentWindow,
+ const css::uno::Reference<css::awt::XWindow>& rxBorderWindow)
+{
+ AccessibleObject::SetWindow(rxContentWindow, rxBorderWindow);
+
+ // Set the windows at the children as well, so that every paragraph can
+ // setup its geometry.
+ for (auto& rxChild : maChildren)
+ {
+ rxChild->SetWindow(rxContentWindow, rxBorderWindow);
+ }
+}
+
+void AccessibleNotes::NotifyCaretChange (
+ const sal_Int32 nOldParagraphIndex,
+ const sal_Int32 nOldCharacterIndex,
+ const sal_Int32 nNewParagraphIndex,
+ const sal_Int32 nNewCharacterIndex)
+{
+ AccessibleFocusManager::Instance()->FocusObject(
+ nNewParagraphIndex >= 0
+ ? maChildren[nNewParagraphIndex]
+ : this);
+
+ if (nOldParagraphIndex != nNewParagraphIndex)
+ {
+ // Moved caret from one paragraph to another (or showed or
+ // hid the caret). Move focus from one accessible
+ // paragraph to another.
+ if (nOldParagraphIndex >= 0)
+ {
+ maChildren[nOldParagraphIndex]->FireAccessibleEvent(
+ AccessibleEventId::CARET_CHANGED,
+ Any(nOldCharacterIndex),
+ Any(sal_Int32(-1)));
+ }
+ if (nNewParagraphIndex >= 0)
+ {
+ maChildren[nNewParagraphIndex]->FireAccessibleEvent(
+ AccessibleEventId::CARET_CHANGED,
+ Any(sal_Int32(-1)),
+ Any(nNewCharacterIndex));
+ }
+ }
+ else if (nNewParagraphIndex >= 0)
+ {
+ // Caret moved inside one paragraph.
+ maChildren[nNewParagraphIndex]->FireAccessibleEvent(
+ AccessibleEventId::CARET_CHANGED,
+ Any(nOldCharacterIndex),
+ Any(nNewCharacterIndex));
+ }
+}
+
+
+//===== AccessibleFocusManager ================================================
+
+std::shared_ptr<AccessibleFocusManager> AccessibleFocusManager::mpInstance;
+
+std::shared_ptr<AccessibleFocusManager> const & AccessibleFocusManager::Instance()
+{
+ if ( ! mpInstance)
+ {
+ mpInstance.reset(new AccessibleFocusManager());
+ }
+ return mpInstance;
+}
+
+AccessibleFocusManager::AccessibleFocusManager()
+{
+}
+
+AccessibleFocusManager::~AccessibleFocusManager()
+{
+ // copy member to stack, then drop it - otherwise will get use-after-free
+ // from AccessibleObject::disposing(), it will call ~Reference *twice*
+ auto const temp(std::move(maFocusableObjects));
+ (void) temp;
+ m_isInDtor = true;
+}
+
+void AccessibleFocusManager::AddFocusableObject (
+ const ::rtl::Reference<PresenterAccessible::AccessibleObject>& rpObject)
+{
+ OSL_ASSERT(rpObject.is());
+ OSL_ASSERT(::std::find(maFocusableObjects.begin(),maFocusableObjects.end(), rpObject)==maFocusableObjects.end());
+
+ maFocusableObjects.push_back(rpObject);
+}
+
+void AccessibleFocusManager::RemoveFocusableObject (
+ const ::rtl::Reference<PresenterAccessible::AccessibleObject>& rpObject)
+{
+ ::std::vector<rtl::Reference<PresenterAccessible::AccessibleObject> >::iterator iObject (
+ ::std::find(maFocusableObjects.begin(),maFocusableObjects.end(), rpObject));
+
+ if (iObject != maFocusableObjects.end())
+ maFocusableObjects.erase(iObject);
+ else
+ {
+ OSL_ASSERT(m_isInDtor); // in dtor, was removed already
+ }
+}
+
+void AccessibleFocusManager::FocusObject (
+ const ::rtl::Reference<PresenterAccessible::AccessibleObject>& rpObject)
+{
+ // Remove the focus of any of the other focusable objects.
+ for (auto& rxObject : maFocusableObjects)
+ {
+ if (rxObject!=rpObject)
+ rxObject->SetIsFocused(false);
+ }
+
+ if (rpObject.is())
+ rpObject->SetIsFocused(true);
+}
+
+} // end of namespace ::sd::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterAccessibility.hxx b/sdext/source/presenter/PresenterAccessibility.hxx
new file mode 100644
index 000000000..9789db525
--- /dev/null
+++ b/sdext/source/presenter/PresenterAccessibility.hxx
@@ -0,0 +1,115 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERACCESSIBILITY_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERACCESSIBILITY_HXX
+
+#include "PresenterPaneContainer.hxx"
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/awt/XFocusListener.hpp>
+#include <com/sun/star/drawing/framework/XPane.hpp>
+#include <com/sun/star/drawing/framework/XPane2.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <rtl/ref.hxx>
+#include <memory>
+
+
+namespace sdext::presenter {
+
+class PresenterController;
+class PresenterTextView;
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::accessibility::XAccessible,
+ css::lang::XInitialization,
+ css::awt::XFocusListener
+> PresenterAccessibleInterfaceBase;
+
+class PresenterAccessible
+ : public ::cppu::BaseMutex,
+ public PresenterAccessibleInterfaceBase
+{
+public:
+ PresenterAccessible (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController,
+ const css::uno::Reference<css::drawing::framework::XPane>& rxMainPane);
+ virtual ~PresenterAccessible() override;
+
+ void UpdateAccessibilityHierarchy();
+
+ void NotifyCurrentSlideChange ();
+
+ virtual void SAL_CALL disposing() override;
+
+ //----- XAccessible -------------------------------------------------------
+
+ virtual css::uno::Reference<css::accessibility::XAccessibleContext> SAL_CALL
+ getAccessibleContext() override;
+
+ //----- XFocusListener ----------------------------------------------------
+
+ virtual void SAL_CALL focusGained (const css::awt::FocusEvent& rEvent) override;
+
+ virtual void SAL_CALL focusLost (const css::awt::FocusEvent& rEvent) override;
+
+ //----- XEventListener ----------------------------------------------------
+
+ virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override;
+
+ //----- XInitialization ---------------------------------------------------
+
+ virtual void SAL_CALL initialize (const css::uno::Sequence<css::uno::Any>& rArguments) override;
+
+ class AccessibleObject;
+ class AccessibleParagraph;
+
+private:
+ const css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ css::uno::Reference<css::drawing::framework::XPane2> mxMainPane;
+ css::uno::Reference<css::awt::XWindow> mxMainWindow;
+ css::uno::Reference<css::awt::XWindow> mxPreviewContentWindow;
+ css::uno::Reference<css::awt::XWindow> mxPreviewBorderWindow;
+ css::uno::Reference<css::awt::XWindow> mxNotesContentWindow;
+ css::uno::Reference<css::awt::XWindow> mxNotesBorderWindow;
+ ::rtl::Reference<AccessibleObject> mpAccessibleConsole;
+ ::rtl::Reference<AccessibleObject> mpAccessiblePreview;
+ ::rtl::Reference<AccessibleObject> mpAccessibleNotes;
+ css::uno::Reference<css::accessibility::XAccessible> mxAccessibleParent;
+
+ void UpdateAccessibilityHierarchy (
+ const css::uno::Reference<css::awt::XWindow>& rxPreviewContentWindow,
+ const css::uno::Reference<css::awt::XWindow>& rxPreviewBorderWindow,
+ const OUString& rsTitle,
+ const css::uno::Reference<css::awt::XWindow>& rxNotesContentWindow,
+ const css::uno::Reference<css::awt::XWindow>& rxNotesBorderWindow,
+ const std::shared_ptr<PresenterTextView>& rpNotesTextView);
+ PresenterPaneContainer::SharedPaneDescriptor GetPreviewPane() const;
+};
+
+} // end of namespace ::sd::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterBitmapContainer.cxx b/sdext/source/presenter/PresenterBitmapContainer.cxx
new file mode 100644
index 000000000..17609da85
--- /dev/null
+++ b/sdext/source/presenter/PresenterBitmapContainer.cxx
@@ -0,0 +1,399 @@
+/* -*- 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 "PresenterBitmapContainer.hxx"
+#include "PresenterConfigurationAccess.hxx"
+
+#include <com/sun/star/drawing/XPresenterHelper.hpp>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::std;
+
+namespace sdext::presenter {
+
+//===== PresenterBitmapContainer ==============================================
+
+PresenterBitmapContainer::PresenterBitmapContainer (
+ const OUString& rsConfigurationBase,
+ const std::shared_ptr<PresenterBitmapContainer>& rpParentContainer,
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::uno::Reference<css::drawing::XPresenterHelper>& rxPresenterHelper)
+ : mpParentContainer(rpParentContainer),
+ mxCanvas(rxCanvas),
+ mxPresenterHelper(rxPresenterHelper)
+{
+ Initialize(rxComponentContext);
+
+ // Get access to the configuration.
+ PresenterConfigurationAccess aConfiguration (
+ rxComponentContext,
+ "org.openoffice.Office.PresenterScreen",
+ PresenterConfigurationAccess::READ_ONLY);
+ Reference<container::XNameAccess> xBitmapList (
+ aConfiguration.GetConfigurationNode(rsConfigurationBase),
+ UNO_QUERY_THROW);
+
+ LoadBitmaps(xBitmapList);
+}
+
+PresenterBitmapContainer::PresenterBitmapContainer (
+ const css::uno::Reference<css::container::XNameAccess>& rxRootNode,
+ const std::shared_ptr<PresenterBitmapContainer>& rpParentContainer,
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::uno::Reference<css::drawing::XPresenterHelper>& rxPresenterHelper)
+ : mpParentContainer(rpParentContainer),
+ mxCanvas(rxCanvas),
+ mxPresenterHelper(rxPresenterHelper)
+{
+ Initialize(rxComponentContext);
+
+ LoadBitmaps(rxRootNode);
+}
+
+void PresenterBitmapContainer::Initialize (
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext)
+{
+ if ( mxPresenterHelper.is())
+ return;
+
+ // Create an object that is able to load the bitmaps in a format that is
+ // supported by the canvas.
+ Reference<lang::XMultiComponentFactory> xFactory =
+ rxComponentContext->getServiceManager();
+ if ( ! xFactory.is())
+ return;
+ mxPresenterHelper.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.drawing.PresenterHelper",
+ rxComponentContext),
+ UNO_QUERY_THROW);
+}
+
+PresenterBitmapContainer::~PresenterBitmapContainer()
+{
+ maIconContainer.clear();
+}
+
+std::shared_ptr<PresenterBitmapContainer::BitmapDescriptor> PresenterBitmapContainer::GetBitmap (
+ const OUString& rsName) const
+{
+ BitmapContainer::const_iterator iSet (maIconContainer.find(rsName));
+ if (iSet != maIconContainer.end())
+ return iSet->second;
+ else if (mpParentContainer != nullptr)
+ return mpParentContainer->GetBitmap(rsName);
+ else
+ return SharedBitmapDescriptor();
+}
+
+void PresenterBitmapContainer::LoadBitmaps (
+ const css::uno::Reference<css::container::XNameAccess>& rxBitmapList)
+{
+ if ( ! mxCanvas.is())
+ return;
+
+ if ( ! rxBitmapList.is())
+ return;
+
+ try
+ {
+ // Load all button bitmaps.
+ if (rxBitmapList.is())
+ {
+ PresenterConfigurationAccess::ForAll(
+ rxBitmapList,
+ [this](OUString const& rKey, Reference<beans::XPropertySet> const& xProps)
+ {
+ this->ProcessBitmap(rKey, xProps);
+ });
+ }
+ }
+ catch (Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+}
+
+std::shared_ptr<PresenterBitmapContainer::BitmapDescriptor> PresenterBitmapContainer::LoadBitmap (
+ const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxNode,
+ const OUString& rsPath,
+ const css::uno::Reference<css::drawing::XPresenterHelper>& rxPresenterHelper,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const std::shared_ptr<BitmapDescriptor>& rpDefault)
+{
+ SharedBitmapDescriptor pBitmap;
+
+ if (rxNode.is())
+ {
+ try
+ {
+ Reference<beans::XPropertySet> xBitmapProperties (
+ PresenterConfigurationAccess::GetConfigurationNode(rxNode, rsPath),
+ UNO_QUERY);
+ if (xBitmapProperties.is())
+ pBitmap = LoadBitmap(
+ xBitmapProperties,
+ rxPresenterHelper,
+ rxCanvas,
+ rpDefault);
+ }
+ catch (Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+ }
+
+ return pBitmap;
+}
+
+void PresenterBitmapContainer::ProcessBitmap (
+ const OUString& rsKey,
+ const Reference<beans::XPropertySet>& rxProperties)
+{
+ OUString sName;
+ if ( ! (PresenterConfigurationAccess::GetProperty(rxProperties, "Name") >>= sName))
+ sName = rsKey;
+
+ maIconContainer[sName] = LoadBitmap(
+ rxProperties,
+ mxPresenterHelper,
+ mxCanvas,
+ SharedBitmapDescriptor());
+}
+
+std::shared_ptr<PresenterBitmapContainer::BitmapDescriptor> PresenterBitmapContainer::LoadBitmap (
+ const Reference<beans::XPropertySet>& rxProperties,
+ const css::uno::Reference<css::drawing::XPresenterHelper>& rxPresenterHelper,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const std::shared_ptr<BitmapDescriptor>& rpDefault)
+{
+ OSL_ASSERT(rxCanvas.is());
+ OSL_ASSERT(rxPresenterHelper.is());
+
+ SharedBitmapDescriptor pBitmap = std::make_shared<BitmapDescriptor>(rpDefault);
+
+ if ( ! rxProperties.is())
+ return pBitmap;
+
+ OUString sFileName;
+
+ // Load bitmaps.
+ if (PresenterConfigurationAccess::GetProperty(rxProperties, "NormalFileName") >>= sFileName)
+ try
+ {
+ pBitmap->SetBitmap(
+ BitmapDescriptor::Normal,
+ rxPresenterHelper->loadBitmap(sFileName, rxCanvas));
+ }
+ catch (Exception&)
+ {}
+ if (PresenterConfigurationAccess::GetProperty(rxProperties, "MouseOverFileName") >>= sFileName)
+ try
+ {
+ pBitmap->SetBitmap(
+ BitmapDescriptor::MouseOver,
+ rxPresenterHelper->loadBitmap(sFileName, rxCanvas));
+ }
+ catch (Exception&)
+ {}
+ if (PresenterConfigurationAccess::GetProperty(rxProperties, "ButtonDownFileName") >>= sFileName)
+ try
+ {
+ pBitmap->SetBitmap(
+ BitmapDescriptor::ButtonDown,
+ rxPresenterHelper->loadBitmap(sFileName, rxCanvas));
+ }
+ catch (Exception&)
+ {}
+ if (PresenterConfigurationAccess::GetProperty(rxProperties, "DisabledFileName") >>= sFileName)
+ try
+ {
+ pBitmap->SetBitmap(
+ BitmapDescriptor::Disabled,
+ rxPresenterHelper->loadBitmap(sFileName, rxCanvas));
+ }
+ catch (Exception&)
+ {}
+ if (PresenterConfigurationAccess::GetProperty(rxProperties, "MaskFileName") >>= sFileName)
+ try
+ {
+ pBitmap->SetBitmap(
+ BitmapDescriptor::Mask,
+ rxPresenterHelper->loadBitmap(sFileName, rxCanvas));
+ }
+ catch (Exception&)
+ {}
+
+ PresenterConfigurationAccess::GetProperty(rxProperties, "XOffset") >>= pBitmap->mnXOffset;
+ PresenterConfigurationAccess::GetProperty(rxProperties, "YOffset") >>= pBitmap->mnYOffset;
+
+ PresenterConfigurationAccess::GetProperty(rxProperties, "XHotSpot") >>= pBitmap->mnXHotSpot;
+ PresenterConfigurationAccess::GetProperty(rxProperties, "YHotSpot") >>= pBitmap->mnYHotSpot;
+
+ PresenterConfigurationAccess::GetProperty(rxProperties, "ReplacementColor") >>= pBitmap->maReplacementColor;
+
+ OUString sTexturingMode;
+ if (PresenterConfigurationAccess::GetProperty(rxProperties, "HorizontalTexturingMode") >>= sTexturingMode)
+ pBitmap->meHorizontalTexturingMode = StringToTexturingMode(sTexturingMode);
+ if (PresenterConfigurationAccess::GetProperty(rxProperties, "VerticalTexturingMode") >>= sTexturingMode)
+ pBitmap->meVerticalTexturingMode = StringToTexturingMode(sTexturingMode);
+
+ return pBitmap;
+}
+
+PresenterBitmapContainer::BitmapDescriptor::TexturingMode
+ PresenterBitmapContainer::StringToTexturingMode (std::u16string_view rsTexturingMode)
+{
+ if (rsTexturingMode == u"Once")
+ return PresenterBitmapContainer::BitmapDescriptor::Once;
+ else if (rsTexturingMode == u"Repeat")
+ return PresenterBitmapContainer::BitmapDescriptor::Repeat;
+ else if (rsTexturingMode == u"Stretch")
+ return PresenterBitmapContainer::BitmapDescriptor::Stretch;
+ else
+ return PresenterBitmapContainer::BitmapDescriptor::Once;
+}
+
+//===== PresenterBitmapContainer::BitmapSet ===================================
+
+PresenterBitmapContainer::BitmapDescriptor::BitmapDescriptor()
+ : mnWidth(0),
+ mnHeight(0),
+ mnXOffset(0),
+ mnYOffset(0),
+ mnXHotSpot(0),
+ mnYHotSpot(0),
+ maReplacementColor(0x00000000),
+ meHorizontalTexturingMode(Once),
+ meVerticalTexturingMode(Once)
+{
+}
+
+PresenterBitmapContainer::BitmapDescriptor::BitmapDescriptor (
+ const std::shared_ptr<PresenterBitmapContainer::BitmapDescriptor>& rpDefault)
+ : mnWidth(0),
+ mnHeight(0),
+ mnXOffset(0),
+ mnYOffset(0),
+ mnXHotSpot(0),
+ mnYHotSpot(0),
+ maReplacementColor(0x00000000),
+ meHorizontalTexturingMode(Once),
+ meVerticalTexturingMode(Once)
+{
+ if (rpDefault == nullptr)
+ return;
+
+ mnWidth = rpDefault->mnWidth;
+ mnHeight = rpDefault->mnHeight;
+ mnXOffset = rpDefault->mnXOffset;
+ mnYOffset = rpDefault->mnYOffset;
+ mnXHotSpot = rpDefault->mnXHotSpot;
+ mnYHotSpot = rpDefault->mnYHotSpot;
+ maReplacementColor = rpDefault->maReplacementColor;
+ meHorizontalTexturingMode = rpDefault->meHorizontalTexturingMode;
+ meVerticalTexturingMode = rpDefault->meVerticalTexturingMode;
+ mxNormalBitmap = rpDefault->mxNormalBitmap;
+ mxMouseOverBitmap = rpDefault->mxMouseOverBitmap;
+ mxButtonDownBitmap = rpDefault->mxButtonDownBitmap;
+ mxDisabledBitmap = rpDefault->mxDisabledBitmap;
+ mxMaskBitmap = rpDefault->mxMaskBitmap;
+}
+
+const css::uno::Reference<css::rendering::XBitmap>&
+ PresenterBitmapContainer::BitmapDescriptor::GetNormalBitmap() const
+{
+ return mxNormalBitmap;
+}
+
+css::uno::Reference<css::rendering::XBitmap> const &
+ PresenterBitmapContainer::BitmapDescriptor::GetBitmap(const Mode eMode) const
+{
+ switch (eMode)
+ {
+ case Normal:
+ default:
+ return mxNormalBitmap;
+
+ case MouseOver:
+ if (mxMouseOverBitmap.is())
+ return mxMouseOverBitmap;
+ else
+ return mxNormalBitmap;
+
+ case ButtonDown:
+ if (mxButtonDownBitmap.is())
+ return mxButtonDownBitmap;
+ else
+ return mxNormalBitmap;
+
+ case Disabled:
+ if (mxDisabledBitmap.is())
+ return mxDisabledBitmap;
+ else
+ return mxNormalBitmap;
+
+ case Mask:
+ return mxMaskBitmap;
+ }
+}
+
+void PresenterBitmapContainer::BitmapDescriptor::SetBitmap (
+ const Mode eMode,
+ const css::uno::Reference<css::rendering::XBitmap>& rxBitmap)
+{
+ switch (eMode)
+ {
+ case Normal:
+ default:
+ mxNormalBitmap = rxBitmap;
+ if (mxNormalBitmap.is())
+ {
+ const geometry::IntegerSize2D aSize (mxNormalBitmap->getSize());
+ mnWidth = aSize.Width;
+ mnHeight = aSize.Height;
+ }
+ break;
+
+ case MouseOver:
+ mxMouseOverBitmap = rxBitmap;
+ break;
+
+ case ButtonDown:
+ mxButtonDownBitmap = rxBitmap;
+ break;
+
+ case Disabled:
+ mxDisabledBitmap = rxBitmap;
+ break;
+
+ case Mask:
+ mxMaskBitmap = rxBitmap;
+ break;
+ }
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterBitmapContainer.hxx b/sdext/source/presenter/PresenterBitmapContainer.hxx
new file mode 100644
index 000000000..65f385b2b
--- /dev/null
+++ b/sdext/source/presenter/PresenterBitmapContainer.hxx
@@ -0,0 +1,146 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERBITMAPCONTAINER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERBITMAPCONTAINER_HXX
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/drawing/XPresenterHelper.hpp>
+#include <com/sun/star/rendering/XBitmap.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/Color.hpp>
+#include <map>
+#include <memory>
+
+namespace sdext::presenter {
+
+/** Manage a set of bitmap groups as they are used for buttons: three
+ bitmaps, one for the normal state, one for a mouse over effect and one
+ to show that the button has been pressed.
+ A bitmap group is defined by some entries in the configuration.
+*/
+class PresenterBitmapContainer
+{
+public:
+ /** There is one bitmap for the normal state, one for a mouse over effect and one
+ to show that a button has been pressed.
+ */
+ class BitmapDescriptor
+ {
+ public:
+ BitmapDescriptor();
+ explicit BitmapDescriptor (const std::shared_ptr<BitmapDescriptor>& rpDefault);
+
+ enum Mode {Normal, MouseOver, ButtonDown, Disabled, Mask};
+ const css::uno::Reference<css::rendering::XBitmap>& GetNormalBitmap() const;
+ css::uno::Reference<css::rendering::XBitmap> const & GetBitmap(const Mode eMode) const;
+ void SetBitmap (
+ const Mode eMode,
+ const css::uno::Reference<css::rendering::XBitmap>& rxBitmap);
+
+ sal_Int32 mnWidth;
+ sal_Int32 mnHeight;
+ sal_Int32 mnXOffset;
+ sal_Int32 mnYOffset;
+ sal_Int32 mnXHotSpot;
+ sal_Int32 mnYHotSpot;
+ css::util::Color maReplacementColor;
+ enum TexturingMode { Once, Repeat, Stretch };
+ TexturingMode meHorizontalTexturingMode;
+ TexturingMode meVerticalTexturingMode;
+
+ private:
+ css::uno::Reference<css::rendering::XBitmap> mxNormalBitmap;
+ css::uno::Reference<css::rendering::XBitmap> mxMouseOverBitmap;
+ css::uno::Reference<css::rendering::XBitmap> mxButtonDownBitmap;
+ css::uno::Reference<css::rendering::XBitmap> mxDisabledBitmap;
+ css::uno::Reference<css::rendering::XBitmap> mxMaskBitmap;
+ };
+
+ /** Create a new bitmap container from a section of the configuration.
+ @param rxComponentContext
+ The component context is used to create new API objects.
+ @param rxCanvas
+ Bitmaps are created specifically for this canvas.
+ @param rsConfigurationBase
+ The name of a configuration node whose sub-tree defines the
+ bitmap sets.
+ */
+ PresenterBitmapContainer (
+ const OUString& rsConfigurationBase,
+ const std::shared_ptr<PresenterBitmapContainer>& rpParentContainer,
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::uno::Reference<css::drawing::XPresenterHelper>& rxPresenterHelper = nullptr);
+ PresenterBitmapContainer (
+ const css::uno::Reference<css::container::XNameAccess>& rsRootNode,
+ const std::shared_ptr<PresenterBitmapContainer>& rpParentContainer,
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::uno::Reference<css::drawing::XPresenterHelper>& rxPresenterHelper = nullptr);
+ ~PresenterBitmapContainer();
+ PresenterBitmapContainer(const PresenterBitmapContainer&) = delete;
+ PresenterBitmapContainer& operator=(const PresenterBitmapContainer&) = delete;
+
+ void Initialize (
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext);
+
+ /** Return the bitmap set that is associated with the given name.
+ */
+ std::shared_ptr<BitmapDescriptor> GetBitmap (const OUString& rsName) const;
+
+ static std::shared_ptr<BitmapDescriptor> LoadBitmap (
+ const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxNode,
+ const OUString& rsPathToBitmapNode,
+ const css::uno::Reference<css::drawing::XPresenterHelper>& rxPresenterHelper,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const std::shared_ptr<BitmapDescriptor>& rpDefaultBitmap);
+
+private:
+ std::shared_ptr<PresenterBitmapContainer> mpParentContainer;
+ typedef ::std::map<OUString, std::shared_ptr<BitmapDescriptor> > BitmapContainer;
+ BitmapContainer maIconContainer;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+ css::uno::Reference<css::drawing::XPresenterHelper> mxPresenterHelper;
+
+ void LoadBitmaps (
+ const css::uno::Reference<css::container::XNameAccess>& rsRootNode);
+ void ProcessBitmap (
+ const OUString& rsKey,
+ const css::uno::Reference<css::beans::XPropertySet>& rProperties);
+ static std::shared_ptr<BitmapDescriptor> LoadBitmap (
+ const css::uno::Reference<css::beans::XPropertySet>& rxProperties,
+ const css::uno::Reference<css::drawing::XPresenterHelper>& rxPresenterHelper,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const std::shared_ptr<PresenterBitmapContainer::BitmapDescriptor>& rpDefault);
+ static BitmapDescriptor::TexturingMode
+ StringToTexturingMode (std::u16string_view rsTexturingMode);
+};
+
+typedef PresenterBitmapContainer::BitmapDescriptor PresenterBitmapDescriptor;
+typedef std::shared_ptr<PresenterBitmapContainer::BitmapDescriptor> SharedBitmapDescriptor;
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterButton.cxx b/sdext/source/presenter/PresenterButton.cxx
new file mode 100644
index 000000000..61de170c0
--- /dev/null
+++ b/sdext/source/presenter/PresenterButton.cxx
@@ -0,0 +1,447 @@
+/* -*- 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 "PresenterButton.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterController.hxx"
+#include "PresenterGeometryHelper.hxx"
+#include "PresenterPaintManager.hxx"
+#include "PresenterUIPainter.hxx"
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/drawing/XPresenterHelper.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/TextDirection.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sdext::presenter {
+
+const double gnHorizontalBorder (15);
+const double gnVerticalBorder (5);
+
+::rtl::Reference<PresenterButton> PresenterButton::Create (
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController,
+ const std::shared_ptr<PresenterTheme>& rpTheme,
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow,
+ const css::uno::Reference<css::rendering::XCanvas>& rxParentCanvas,
+ const OUString& rsConfigurationName)
+{
+ Reference<beans::XPropertySet> xProperties (GetConfigurationProperties(
+ rxComponentContext,
+ rsConfigurationName));
+ if (xProperties.is())
+ {
+ OUString sText;
+ OUString sAction;
+ PresenterConfigurationAccess::GetProperty(xProperties, "Text") >>= sText;
+ PresenterConfigurationAccess::GetProperty(xProperties, "Action") >>= sAction;
+
+ PresenterTheme::SharedFontDescriptor pFont;
+ if (rpTheme != nullptr)
+ pFont = rpTheme->GetFont("ButtonFont");
+
+ PresenterTheme::SharedFontDescriptor pMouseOverFont;
+ if (rpTheme != nullptr)
+ pMouseOverFont = rpTheme->GetFont("ButtonMouseOverFont");
+
+ rtl::Reference<PresenterButton> pButton (
+ new PresenterButton(
+ rxComponentContext,
+ rpPresenterController,
+ rpTheme,
+ rxParentWindow,
+ pFont,
+ pMouseOverFont,
+ sText,
+ sAction));
+ pButton->SetCanvas(rxParentCanvas, rxParentWindow);
+ return pButton;
+ }
+ else
+ return nullptr;
+}
+
+PresenterButton::PresenterButton (
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController,
+ const std::shared_ptr<PresenterTheme>& rpTheme,
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow,
+ const PresenterTheme::SharedFontDescriptor& rpFont,
+ const PresenterTheme::SharedFontDescriptor& rpMouseOverFont,
+ const OUString& rsText,
+ const OUString& rsAction)
+ : PresenterButtonInterfaceBase(m_aMutex),
+ mpPresenterController(rpPresenterController),
+ mpTheme(rpTheme),
+ msText(rsText),
+ mpFont(rpFont),
+ mpMouseOverFont(rpMouseOverFont),
+ msAction(rsAction),
+ maButtonSize(-1,-1),
+ meState(PresenterBitmapDescriptor::Normal)
+{
+ try
+ {
+ Reference<lang::XMultiComponentFactory> xFactory (rxComponentContext->getServiceManager());
+ if ( ! xFactory.is())
+ throw RuntimeException();
+
+ mxPresenterHelper.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.comp.Draw.PresenterHelper",
+ rxComponentContext),
+ UNO_QUERY_THROW);
+
+ if (mxPresenterHelper.is())
+ mxWindow = mxPresenterHelper->createWindow(rxParentWindow,
+ false,
+ false,
+ false,
+ false);
+
+ // Make the background transparent.
+ Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY_THROW);
+ xPeer->setBackground(0xff000000);
+
+ mxWindow->setVisible(true);
+ mxWindow->addPaintListener(this);
+ mxWindow->addMouseListener(this);
+ }
+ catch (RuntimeException&)
+ {
+ }
+}
+
+PresenterButton::~PresenterButton()
+{
+}
+
+void SAL_CALL PresenterButton::disposing()
+{
+ if (mxCanvas.is())
+ {
+ Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
+ mxCanvas = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ if (mxWindow.is())
+ {
+ mxWindow->removePaintListener(this);
+ mxWindow->removeMouseListener(this);
+ Reference<lang::XComponent> xComponent = mxWindow;
+ mxWindow = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+}
+
+void PresenterButton::SetCenter (const css::geometry::RealPoint2D& rLocation)
+{
+ if (mxCanvas.is())
+ {
+ Invalidate();
+
+ maCenter = rLocation;
+ mxWindow->setPosSize(
+ sal_Int32(0.5 + maCenter.X - maButtonSize.Width/2),
+ sal_Int32(0.5 + maCenter.Y - maButtonSize.Height/2),
+ maButtonSize.Width,
+ maButtonSize.Height,
+ awt::PosSize::POSSIZE);
+
+ Invalidate();
+ }
+ else
+ {
+ // The button can not be painted but we can at least store the new center.
+ maCenter = rLocation;
+ }
+}
+
+void PresenterButton::SetCanvas (
+ const css::uno::Reference<css::rendering::XCanvas>& rxParentCanvas,
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow)
+{
+ if (mxCanvas.is())
+ {
+ Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
+ mxCanvas = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ if (!(mxPresenterHelper.is() && rxParentCanvas.is() && rxParentWindow.is()))
+ return;
+
+ mxCanvas = mxPresenterHelper->createSharedCanvas (
+ Reference<rendering::XSpriteCanvas>(rxParentCanvas, UNO_QUERY),
+ rxParentWindow,
+ rxParentCanvas,
+ rxParentWindow,
+ mxWindow);
+ if (mxCanvas.is())
+ {
+ SetupButtonBitmaps();
+ SetCenter(maCenter);
+ }
+}
+
+css::geometry::IntegerSize2D const & PresenterButton::GetSize()
+{
+ if (maButtonSize.Width < 0)
+ CalculateButtonSize();
+ return maButtonSize;
+}
+
+//----- XPaintListener --------------------------------------------------------
+
+void SAL_CALL PresenterButton::windowPaint (const css::awt::PaintEvent& rEvent)
+{
+ ThrowIfDisposed();
+ if (!(mxWindow.is() && mxCanvas.is()))
+ return;
+
+ Reference<rendering::XBitmap> xBitmap;
+ if (meState == PresenterBitmapDescriptor::MouseOver)
+ xBitmap = mxMouseOverBitmap;
+ else
+ xBitmap = mxNormalBitmap;
+ if ( ! xBitmap.is())
+ return;
+
+ rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr);
+ rendering::RenderState aRenderState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ PresenterGeometryHelper::CreatePolygon(rEvent.UpdateRect, mxCanvas->getDevice()),
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ mxCanvas->drawBitmap(xBitmap, aViewState, aRenderState);
+
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(false);
+}
+
+//----- XMouseListener --------------------------------------------------------
+
+void SAL_CALL PresenterButton::mousePressed (const css::awt::MouseEvent&)
+{
+ ThrowIfDisposed();
+ meState = PresenterBitmapDescriptor::ButtonDown;
+}
+
+void SAL_CALL PresenterButton::mouseReleased (const css::awt::MouseEvent&)
+{
+ ThrowIfDisposed();
+
+ if (meState == PresenterBitmapDescriptor::ButtonDown)
+ {
+ OSL_ASSERT(mpPresenterController);
+ mpPresenterController->DispatchUnoCommand(msAction);
+
+ meState = PresenterBitmapDescriptor::Normal;
+ Invalidate();
+ }
+}
+
+void SAL_CALL PresenterButton::mouseEntered (const css::awt::MouseEvent&)
+{
+ ThrowIfDisposed();
+ meState = PresenterBitmapDescriptor::MouseOver;
+ Invalidate();
+}
+
+void SAL_CALL PresenterButton::mouseExited (const css::awt::MouseEvent&)
+{
+ ThrowIfDisposed();
+ meState = PresenterBitmapDescriptor::Normal;
+ Invalidate();
+}
+
+//----- lang::XEventListener --------------------------------------------------
+
+void SAL_CALL PresenterButton::disposing (const css::lang::EventObject& rEvent)
+{
+ if (rEvent.Source == mxWindow)
+ mxWindow = nullptr;
+}
+
+
+css::geometry::IntegerSize2D PresenterButton::CalculateButtonSize()
+{
+ if (mpFont && !mpFont->mxFont.is() && mxCanvas.is())
+ mpFont->PrepareFont(mxCanvas);
+ if (!mpFont || !mpFont->mxFont.is())
+ return geometry::IntegerSize2D(-1,-1);
+
+ geometry::RealSize2D aTextSize (PresenterCanvasHelper::GetTextSize(mpFont->mxFont,msText));
+
+ return geometry::IntegerSize2D (
+ sal_Int32(0.5 + aTextSize.Width + 2*gnHorizontalBorder),
+ sal_Int32(0.5 + aTextSize.Height + 2*gnVerticalBorder));
+}
+
+void PresenterButton::RenderButton (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const geometry::IntegerSize2D& rSize,
+ const PresenterTheme::SharedFontDescriptor& rpFont,
+ const PresenterBitmapDescriptor::Mode eMode,
+ const SharedBitmapDescriptor& rpLeft,
+ const SharedBitmapDescriptor& rpCenter,
+ const SharedBitmapDescriptor& rpRight)
+{
+ if ( ! rxCanvas.is())
+ return;
+
+ const awt::Rectangle aBox(0,0, rSize.Width, rSize.Height);
+
+ PresenterUIPainter::PaintHorizontalBitmapComposite (
+ rxCanvas,
+ aBox,
+ aBox,
+ GetBitmap(rpLeft, eMode),
+ GetBitmap(rpCenter, eMode),
+ GetBitmap(rpRight, eMode));
+
+ if (!rpFont || ! rpFont->mxFont.is())
+ return;
+
+ const rendering::StringContext aContext (msText, 0, msText.getLength());
+ const Reference<rendering::XTextLayout> xLayout (
+ rpFont->mxFont->createTextLayout(aContext,rendering::TextDirection::WEAK_LEFT_TO_RIGHT,0));
+ const geometry::RealRectangle2D aTextBBox (xLayout->queryTextBounds());
+
+ rendering::RenderState aRenderState (geometry::AffineMatrix2D(1,0,0, 0,1,0), nullptr,
+ Sequence<double>(4), rendering::CompositeOperation::SOURCE);
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, rpFont->mnColor);
+
+ aRenderState.AffineTransform.m02 = (rSize.Width - aTextBBox.X2 + aTextBBox.X1)/2;
+ aRenderState.AffineTransform.m12 = (rSize.Height - aTextBBox.Y2 + aTextBBox.Y1)/2 - aTextBBox.Y1;
+
+ /// this is responsible of the close button
+ rxCanvas->drawTextLayout(
+ xLayout,
+ rendering::ViewState(geometry::AffineMatrix2D(1,0,0, 0,1,0), nullptr),
+ aRenderState);
+}
+
+void PresenterButton::Invalidate()
+{
+ mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
+}
+
+Reference<rendering::XBitmap> PresenterButton::GetBitmap (
+ const SharedBitmapDescriptor& mpIcon,
+ const PresenterBitmapDescriptor::Mode eMode)
+{
+ if (mpIcon)
+ return mpIcon->GetBitmap(eMode);
+ else
+ {
+ OSL_ASSERT(mpIcon);
+ return nullptr;
+ }
+}
+
+void PresenterButton::SetupButtonBitmaps()
+{
+ if ( ! mxCanvas.is())
+ return;
+ if ( ! mxCanvas->getDevice().is())
+ return;
+
+ // Get the bitmaps for the button border.
+ SharedBitmapDescriptor pLeftBitmap (mpTheme->GetBitmap("ButtonFrameLeft"));
+ SharedBitmapDescriptor pCenterBitmap(mpTheme->GetBitmap("ButtonFrameCenter"));
+ SharedBitmapDescriptor pRightBitmap(mpTheme->GetBitmap("ButtonFrameRight"));
+
+ maButtonSize = CalculateButtonSize();
+
+ if (maButtonSize.Height<=0 && maButtonSize.Width<= 0)
+ return;
+
+ mxNormalBitmap = mxCanvas->getDevice()->createCompatibleAlphaBitmap(maButtonSize);
+ Reference<rendering::XCanvas> xCanvas (mxNormalBitmap, UNO_QUERY);
+ if (xCanvas.is())
+ RenderButton(
+ xCanvas,
+ maButtonSize,
+ mpFont,
+ PresenterBitmapDescriptor::Normal,
+ pLeftBitmap,
+ pCenterBitmap,
+ pRightBitmap);
+
+ mxMouseOverBitmap = mxCanvas->getDevice()->createCompatibleAlphaBitmap(maButtonSize);
+ xCanvas.set(mxMouseOverBitmap, UNO_QUERY);
+ if (mpMouseOverFont && !mpMouseOverFont->mxFont.is() && mxCanvas.is())
+ mpMouseOverFont->PrepareFont(mxCanvas);
+ if (xCanvas.is())
+ RenderButton(
+ xCanvas,
+ maButtonSize,
+ mpMouseOverFont,
+ PresenterBitmapDescriptor::MouseOver,
+ pLeftBitmap,
+ pCenterBitmap,
+ pRightBitmap);
+}
+
+Reference<beans::XPropertySet> PresenterButton::GetConfigurationProperties (
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const OUString& rsConfigurationName)
+{
+ PresenterConfigurationAccess aConfiguration (
+ rxComponentContext,
+ PresenterConfigurationAccess::msPresenterScreenRootName,
+ PresenterConfigurationAccess::READ_ONLY);
+ return Reference<beans::XPropertySet>(
+ PresenterConfigurationAccess::Find (
+ Reference<container::XNameAccess>(
+ aConfiguration.GetConfigurationNode("PresenterScreenSettings/Buttons"),
+ UNO_QUERY),
+ [&rsConfigurationName](OUString const&, uno::Reference<beans::XPropertySet> const& xProps) -> bool
+ {
+ return PresenterConfigurationAccess::IsStringPropertyEqual(
+ rsConfigurationName, "Name", xProps);
+ }),
+ UNO_QUERY);
+}
+
+void PresenterButton::ThrowIfDisposed() const
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterButton object has already been disposed",
+ const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
+ }
+}
+
+} // end of namespace sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterButton.hxx b/sdext/source/presenter/PresenterButton.hxx
new file mode 100644
index 000000000..f722e7da1
--- /dev/null
+++ b/sdext/source/presenter/PresenterButton.hxx
@@ -0,0 +1,138 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERBUTTON_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERBUTTON_HXX
+
+#include "PresenterBitmapContainer.hxx"
+#include "PresenterTheme.hxx"
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XPaintListener.hpp>
+#include <com/sun/star/awt/XMouseListener.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XBitmap.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <rtl/ref.hxx>
+
+namespace sdext::presenter {
+
+class PresenterController;
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::awt::XPaintListener,
+ css::awt::XMouseListener
+> PresenterButtonInterfaceBase;
+
+/** Button for the presenter screen. It displays a text surrounded by a
+ frame.
+*/
+class PresenterButton
+ : private ::cppu::BaseMutex,
+ public PresenterButtonInterfaceBase
+{
+public:
+ static ::rtl::Reference<PresenterButton> Create (
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController,
+ const std::shared_ptr<PresenterTheme>& rpTheme,
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow,
+ const css::uno::Reference<css::rendering::XCanvas>& rxParentCanvas,
+ const OUString& rsConfigurationName);
+ virtual ~PresenterButton() override;
+ PresenterButton(const PresenterButton&) = delete;
+ PresenterButton& operator=(const PresenterButton&) = delete;
+
+ virtual void SAL_CALL disposing() override;
+
+ void SetCenter (const css::geometry::RealPoint2D& rLocation);
+ void SetCanvas (
+ const css::uno::Reference<css::rendering::XCanvas>& rxParentCanvas,
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow);
+ css::geometry::IntegerSize2D const & GetSize();
+
+ // XPaintListener
+
+ virtual void SAL_CALL windowPaint (const css::awt::PaintEvent& rEvent) override;
+
+ // XMouseListener
+
+ virtual void SAL_CALL mousePressed (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseReleased (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseEntered (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseExited (const css::awt::MouseEvent& rEvent) override;
+
+ // lang::XEventListener
+ virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override;
+
+private:
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ std::shared_ptr<PresenterTheme> mpTheme;
+ css::uno::Reference<css::awt::XWindow> mxWindow;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+ css::uno::Reference<css::drawing::XPresenterHelper> mxPresenterHelper;
+ const OUString msText;
+ const PresenterTheme::SharedFontDescriptor mpFont;
+ const PresenterTheme::SharedFontDescriptor mpMouseOverFont;
+ const OUString msAction;
+ css::geometry::RealPoint2D maCenter;
+ css::geometry::IntegerSize2D maButtonSize;
+ PresenterBitmapDescriptor::Mode meState;
+ css::uno::Reference<css::rendering::XBitmap> mxNormalBitmap;
+ css::uno::Reference<css::rendering::XBitmap> mxMouseOverBitmap;
+
+ PresenterButton (
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController,
+ const std::shared_ptr<PresenterTheme>& rpTheme,
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow,
+ const PresenterTheme::SharedFontDescriptor& rFont,
+ const PresenterTheme::SharedFontDescriptor& rMouseOverFont,
+ const OUString& rxText,
+ const OUString& rxAction);
+ void RenderButton (
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::geometry::IntegerSize2D& rSize,
+ const PresenterTheme::SharedFontDescriptor& rFont,
+ const PresenterBitmapDescriptor::Mode eMode,
+ const SharedBitmapDescriptor& rpLeft,
+ const SharedBitmapDescriptor& rpCenter,
+ const SharedBitmapDescriptor& rpRight);
+ css::geometry::IntegerSize2D CalculateButtonSize();
+ void Invalidate();
+ static css::uno::Reference<css::rendering::XBitmap> GetBitmap (
+ const SharedBitmapDescriptor& mpIcon,
+ const PresenterBitmapDescriptor::Mode eMode);
+ void SetupButtonBitmaps();
+ static css::uno::Reference<css::beans::XPropertySet> GetConfigurationProperties (
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const OUString& rsConfigurationName);
+
+ /// @throws css::lang::DisposedException
+ void ThrowIfDisposed() const;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterCanvasHelper.cxx b/sdext/source/presenter/PresenterCanvasHelper.cxx
new file mode 100644
index 000000000..4ff103958
--- /dev/null
+++ b/sdext/source/presenter/PresenterCanvasHelper.cxx
@@ -0,0 +1,289 @@
+/* -*- 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 "PresenterCanvasHelper.hxx"
+
+#include "PresenterGeometryHelper.hxx"
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sdext::presenter {
+
+PresenterCanvasHelper::PresenterCanvasHelper()
+ : maDefaultViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr),
+ maDefaultRenderState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE)
+{
+}
+
+PresenterCanvasHelper::~PresenterCanvasHelper()
+{
+}
+
+void PresenterCanvasHelper::Paint (
+ const SharedBitmapDescriptor& rpBitmap,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::awt::Rectangle& rOuterBoundingBox,
+ const css::awt::Rectangle& rContentBoundingBox) const
+{
+ PaintRectangle(rpBitmap,rxCanvas,rRepaintBox,rOuterBoundingBox,rContentBoundingBox,
+ maDefaultViewState, maDefaultRenderState);
+}
+
+void PresenterCanvasHelper::PaintRectangle (
+ const SharedBitmapDescriptor& rpBitmap,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::awt::Rectangle& rOuterBoundingBox,
+ const css::awt::Rectangle& rContentBoundingBox,
+ const css::rendering::ViewState& rDefaultViewState,
+ const css::rendering::RenderState& rDefaultRenderState)
+{
+ if (!rpBitmap)
+ return;
+
+ if ( ! rxCanvas.is() || ! rxCanvas->getDevice().is())
+ return;
+
+ // Create a clip polypolygon that has the content box as hole.
+ ::std::vector<awt::Rectangle> aRectangles;
+ aRectangles.reserve(2);
+ aRectangles.push_back(
+ PresenterGeometryHelper::Intersection(rRepaintBox, rOuterBoundingBox));
+ if (rContentBoundingBox.Width > 0 && rContentBoundingBox.Height > 0)
+ aRectangles.push_back(
+ PresenterGeometryHelper::Intersection(rRepaintBox, rContentBoundingBox));
+ Reference<rendering::XPolyPolygon2D> xPolyPolygon (
+ PresenterGeometryHelper::CreatePolygon(
+ aRectangles,
+ rxCanvas->getDevice()));
+ if ( ! xPolyPolygon.is())
+ return;
+ xPolyPolygon->setFillRule(rendering::FillRule_EVEN_ODD);
+
+ if (rpBitmap->GetNormalBitmap().is())
+ {
+ if (rpBitmap->meHorizontalTexturingMode == PresenterBitmapDescriptor::Repeat
+ || rpBitmap->meVerticalTexturingMode == PresenterBitmapDescriptor::Repeat)
+ {
+ PaintTiledBitmap(
+ rpBitmap->GetNormalBitmap(),
+ rxCanvas,
+ rRepaintBox,
+ xPolyPolygon,
+ rContentBoundingBox,
+ rDefaultViewState,
+ rDefaultRenderState);
+ }
+ else
+ {
+ PaintBitmap(
+ rpBitmap->GetNormalBitmap(),
+ awt::Point(rOuterBoundingBox.X, rOuterBoundingBox.Y),
+ rxCanvas,
+ rRepaintBox,
+ xPolyPolygon,
+ rDefaultViewState,
+ rDefaultRenderState);
+ }
+ }
+ else
+ {
+ PaintColor(
+ rpBitmap->maReplacementColor,
+ rxCanvas,
+ rRepaintBox,
+ xPolyPolygon,
+ rDefaultViewState,
+ rDefaultRenderState);
+ }
+}
+
+void PresenterCanvasHelper::PaintTiledBitmap (
+ const css::uno::Reference<css::rendering::XBitmap>& rxTexture,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::uno::Reference<css::rendering::XPolyPolygon2D>& rxPolygon,
+ const css::awt::Rectangle& rHole,
+ const css::rendering::ViewState& rDefaultViewState,
+ const css::rendering::RenderState& rDefaultRenderState)
+{
+ if ( ! rxCanvas.is() || ! rxCanvas->getDevice().is())
+ return;
+
+ if ( ! rxTexture.is())
+ return;
+
+ if ( ! rxPolygon.is())
+ return;
+
+ rendering::ViewState aViewState (rDefaultViewState);
+ aViewState.Clip = rxPolygon;
+
+ // Create a local render state at which the location of the bitmap is
+ // set.
+ rendering::RenderState aRenderState (rDefaultRenderState);
+
+ // Tile the bitmap over the repaint box.
+ const geometry::IntegerSize2D aBitmapSize (rxTexture->getSize());
+ if( aBitmapSize.Width < 1 || aBitmapSize.Height < 1)
+ return;
+
+ const sal_Int32 nLeft = (rRepaintBox.X / aBitmapSize.Width) * aBitmapSize.Width;
+ const sal_Int32 nTop = (rRepaintBox.Y / aBitmapSize.Height) * aBitmapSize.Height;
+ const sal_Int32 nRight = ((rRepaintBox.X + rRepaintBox.Width - 1 + aBitmapSize.Width - 1)
+ / aBitmapSize.Width) * aBitmapSize.Width;
+ const sal_Int32 nBottom = ((rRepaintBox.Y + rRepaintBox.Height - 1 + aBitmapSize.Height - 1)
+ / aBitmapSize.Height) * aBitmapSize.Height;
+
+ for (sal_Int32 nY=nTop; nY<=nBottom; nY+=aBitmapSize.Height)
+ for (sal_Int32 nX=nLeft; nX<=nRight; nX+=aBitmapSize.Width)
+ {
+ if (PresenterGeometryHelper::IsInside(
+ awt::Rectangle(nX,nY,aBitmapSize.Width,aBitmapSize.Height),
+ rHole))
+ {
+ continue;
+ }
+ aRenderState.AffineTransform.m02 = nX;
+ aRenderState.AffineTransform.m12 = nY;
+ rxCanvas->drawBitmap(
+ rxTexture,
+ aViewState,
+ aRenderState);
+ }
+}
+
+void PresenterCanvasHelper::PaintBitmap (
+ const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
+ const awt::Point& rLocation,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::uno::Reference<css::rendering::XPolyPolygon2D>& rxPolygon,
+ const css::rendering::ViewState& rDefaultViewState,
+ const css::rendering::RenderState& rDefaultRenderState)
+{
+ if ( ! rxCanvas.is() || ! rxCanvas->getDevice().is())
+ return;
+
+ if ( ! rxBitmap.is())
+ return;
+
+ if ( ! rxPolygon.is())
+ return;
+
+ // Set the repaint box as clip rectangle at the view state.
+ rendering::ViewState aViewState (rDefaultViewState);
+ aViewState.Clip = PresenterGeometryHelper::CreatePolygon(rRepaintBox, rxCanvas->getDevice());
+
+ // Setup the rendering state so that the bitmap is painted top left in
+ // the polygon bounding box.
+ rendering::RenderState aRenderState (rDefaultRenderState);
+ aRenderState.AffineTransform = geometry::AffineMatrix2D(1,0, rLocation.X, 0,1,rLocation.Y);
+ aRenderState.Clip = rxPolygon;
+
+ rxCanvas->drawBitmap(
+ rxBitmap,
+ aViewState,
+ aRenderState);
+}
+
+void PresenterCanvasHelper::PaintColor (
+ const css::util::Color nColor,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::uno::Reference<css::rendering::XPolyPolygon2D>& rxPolygon,
+ const css::rendering::ViewState& rDefaultViewState,
+ const css::rendering::RenderState& rDefaultRenderState)
+{
+ if ( ! rxCanvas.is() || ! rxCanvas->getDevice().is())
+ return;
+
+ if ( ! rxPolygon.is())
+ return;
+
+ // Set the repaint box as clip rectangle at the view state.
+ rendering::ViewState aViewState (rDefaultViewState);
+ aViewState.Clip = PresenterGeometryHelper::CreatePolygon(rRepaintBox, rxCanvas->getDevice());
+
+ // Setup the rendering state to use the given color.
+ rendering::RenderState aRenderState (rDefaultRenderState);
+ SetDeviceColor(aRenderState, nColor);
+
+ rxCanvas->fillPolyPolygon(
+ rxPolygon,
+ aViewState,
+ aRenderState);
+}
+
+void PresenterCanvasHelper::SetDeviceColor(
+ rendering::RenderState& rRenderState,
+ const util::Color aColor)
+{
+ // Other component counts then 4 (RGBA) are not accepted (anymore).
+
+ OSL_ASSERT(rRenderState.DeviceColor.getLength() == 4);
+ if (rRenderState.DeviceColor.getLength() == 4)
+ {
+ auto pDeviceColor = rRenderState.DeviceColor.getArray();
+ pDeviceColor[0] = ((aColor >> 16) & 0x0ff) / 255.0;
+ pDeviceColor[1] = ((aColor >> 8) & 0x0ff) / 255.0;
+ pDeviceColor[2] = ((aColor >> 0) & 0x0ff) / 255.0;
+ pDeviceColor[3] = 1.0 - ((aColor >> 24) & 0x0ff) / 255.0;
+ }
+}
+
+css::geometry::RealRectangle2D PresenterCanvasHelper::GetTextBoundingBox (
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const OUString& rsText,
+ const sal_Int8 nTextDirection)
+{
+ if (rxFont.is() && !rsText.isEmpty())
+ {
+ rendering::StringContext aContext (rsText, 0, rsText.getLength());
+ Reference<rendering::XTextLayout> xLayout (
+ rxFont->createTextLayout(aContext, nTextDirection, 0));
+ return xLayout->queryTextBounds();
+ }
+ else
+ {
+ return geometry::RealRectangle2D(0,0,0,0);
+ }
+}
+
+css::geometry::RealSize2D PresenterCanvasHelper::GetTextSize (
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const OUString& rsText)
+{
+ const geometry::RealRectangle2D aTextBBox (GetTextBoundingBox(rxFont, rsText));
+ return css::geometry::RealSize2D(aTextBBox.X2 - aTextBBox.X1, aTextBBox.Y2 - aTextBBox.Y1);
+}
+
+} // end of namespace sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterCanvasHelper.hxx b/sdext/source/presenter/PresenterCanvasHelper.hxx
new file mode 100644
index 000000000..8902a9712
--- /dev/null
+++ b/sdext/source/presenter/PresenterCanvasHelper.hxx
@@ -0,0 +1,107 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERCANVASHELPER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERCANVASHELPER_HXX
+
+#include "PresenterBitmapContainer.hxx"
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/rendering/TextDirection.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XCanvasFont.hpp>
+#include <com/sun/star/rendering/XPolyPolygon2D.hpp>
+
+namespace sdext::presenter {
+
+/** Collection of functions to ease the life of a canvas user.
+*/
+class PresenterCanvasHelper
+{
+public:
+ PresenterCanvasHelper();
+ ~PresenterCanvasHelper();
+ PresenterCanvasHelper(const PresenterCanvasHelper&) = delete;
+ PresenterCanvasHelper& operator=(const PresenterCanvasHelper&) = delete;
+
+ void Paint (
+ const SharedBitmapDescriptor& rpBitmap,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::awt::Rectangle& rBackgroundBoundingBox,
+ const css::awt::Rectangle& rContentBoundingBox) const;
+
+ static void PaintRectangle (
+ const SharedBitmapDescriptor& rpBitmap,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::awt::Rectangle& rBackgroundBoundingBox,
+ const css::awt::Rectangle& rContentBoundingBox,
+ const css::rendering::ViewState& rDefaultViewState,
+ const css::rendering::RenderState& rDefaultRenderState);
+
+ static void SetDeviceColor(
+ css::rendering::RenderState& rRenderState,
+ const css::util::Color aColor);
+
+ static css::geometry::RealRectangle2D GetTextBoundingBox (
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const OUString& rsText,
+ const sal_Int8 = css::rendering::TextDirection::WEAK_LEFT_TO_RIGHT);
+
+ static css::geometry::RealSize2D GetTextSize (
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const OUString& rsText );
+
+private:
+ const css::rendering::ViewState maDefaultViewState;
+ const css::rendering::RenderState maDefaultRenderState;
+
+ static void PaintTiledBitmap (
+ const css::uno::Reference<css::rendering::XBitmap>& rxTexture,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::uno::Reference<css::rendering::XPolyPolygon2D>& rxPolygon,
+ const css::awt::Rectangle& rHole,
+ const css::rendering::ViewState& rDefaultViewState,
+ const css::rendering::RenderState& rDefaultRenderState);
+
+ static void PaintBitmap (
+ const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
+ const css::awt::Point& rLocation,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::uno::Reference<css::rendering::XPolyPolygon2D>& rxPolygon,
+ const css::rendering::ViewState& rDefaultViewState,
+ const css::rendering::RenderState& rDefaultRenderState);
+
+ static void PaintColor (
+ const css::util::Color nColor,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::uno::Reference<css::rendering::XPolyPolygon2D>& rxPolygon,
+ const css::rendering::ViewState& rDefaultViewState,
+ const css::rendering::RenderState& rDefaultRenderState);
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterConfigurationAccess.cxx b/sdext/source/presenter/PresenterConfigurationAccess.cxx
new file mode 100644
index 000000000..e7bd4524a
--- /dev/null
+++ b/sdext/source/presenter/PresenterConfigurationAccess.cxx
@@ -0,0 +1,274 @@
+/* -*- 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 "PresenterConfigurationAccess.hxx"
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sdext::presenter {
+
+PresenterConfigurationAccess::PresenterConfigurationAccess (
+ const Reference<XComponentContext>& rxContext,
+ const OUString& rsRootName,
+ WriteMode eMode)
+{
+ try
+ {
+ if (rxContext.is())
+ {
+ uno::Sequence<uno::Any> aCreationArguments(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(rsRootName)},
+ {"depth", uno::Any(sal_Int32(-1))}
+ }));
+
+ OUString sAccessService;
+ if (eMode == READ_ONLY)
+ sAccessService = "com.sun.star.configuration.ConfigurationAccess";
+ else
+ sAccessService = "com.sun.star.configuration.ConfigurationUpdateAccess";
+
+ Reference<lang::XMultiServiceFactory> xProvider =
+ configuration::theDefaultProvider::get( rxContext );
+ mxRoot = xProvider->createInstanceWithArguments(
+ sAccessService, aCreationArguments);
+ maNode <<= mxRoot;
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sdext.presenter", "caught exception while opening configuration");
+ }
+}
+
+PresenterConfigurationAccess::~PresenterConfigurationAccess()
+{
+}
+
+bool PresenterConfigurationAccess::IsValid() const
+{
+ return mxRoot.is();
+}
+
+Any PresenterConfigurationAccess::GetConfigurationNode (const OUString& sPathToNode)
+{
+ return GetConfigurationNode(
+ Reference<container::XHierarchicalNameAccess>(mxRoot, UNO_QUERY),
+ sPathToNode);
+}
+
+bool PresenterConfigurationAccess::GoToChild (const OUString& rsPathToNode)
+{
+ if ( ! IsValid())
+ return false;
+
+ Reference<container::XHierarchicalNameAccess> xNode (maNode, UNO_QUERY);
+ if (xNode.is())
+ {
+ maNode = GetConfigurationNode(
+ Reference<container::XHierarchicalNameAccess>(maNode, UNO_QUERY),
+ rsPathToNode);
+ if (Reference<XInterface>(maNode, UNO_QUERY).is())
+ return true;
+ }
+
+ mxRoot = nullptr;
+ return false;
+}
+
+bool PresenterConfigurationAccess::GoToChild (const Predicate& rPredicate)
+{
+ if ( ! IsValid())
+ return false;
+
+ maNode = Find(Reference<container::XNameAccess>(maNode,UNO_QUERY), rPredicate);
+ if (Reference<XInterface>(maNode, UNO_QUERY).is())
+ return true;
+
+ mxRoot = nullptr;
+ return false;
+}
+
+bool PresenterConfigurationAccess::SetProperty (
+ const OUString& rsPropertyName,
+ const Any& rValue)
+{
+ Reference<beans::XPropertySet> xProperties (maNode, UNO_QUERY);
+ if (xProperties.is())
+ {
+ xProperties->setPropertyValue(rsPropertyName, rValue);
+ return true;
+ }
+ else
+ return false;
+}
+
+Any PresenterConfigurationAccess::GetConfigurationNode (
+ const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxNode,
+ const OUString& sPathToNode)
+{
+ if (sPathToNode.isEmpty())
+ return Any(rxNode);
+
+ try
+ {
+ if (rxNode.is())
+ {
+ return rxNode->getByHierarchicalName(sPathToNode);
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sdext.presenter", "caught exception while getting configuration node " << sPathToNode);
+ }
+
+ return Any();
+}
+
+Reference<beans::XPropertySet> PresenterConfigurationAccess::GetNodeProperties (
+ const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxNode,
+ const OUString& rsPathToNode)
+{
+ return Reference<beans::XPropertySet>(GetConfigurationNode(rxNode, rsPathToNode), UNO_QUERY);
+}
+
+void PresenterConfigurationAccess::CommitChanges()
+{
+ Reference<util::XChangesBatch> xConfiguration (mxRoot, UNO_QUERY);
+ if (xConfiguration.is())
+ xConfiguration->commitChanges();
+}
+
+void PresenterConfigurationAccess::ForAll (
+ const Reference<container::XNameAccess>& rxContainer,
+ const ::std::vector<OUString>& rArguments,
+ const ItemProcessor& rProcessor)
+{
+ if (!rxContainer.is())
+ return;
+
+ ::std::vector<Any> aValues(rArguments.size());
+ const Sequence<OUString> aKeys (rxContainer->getElementNames());
+ for (const OUString& rsKey : aKeys)
+ {
+ bool bHasAllValues (true);
+ Reference<container::XNameAccess> xSetItem (rxContainer->getByName(rsKey), UNO_QUERY);
+ Reference<beans::XPropertySet> xSet (xSetItem, UNO_QUERY);
+ OSL_ASSERT(xSet.is());
+ if (xSetItem.is())
+ {
+ // Get from the current item of the container the children
+ // that match the names in the rArguments list.
+ for (size_t nValueIndex=0; nValueIndex<aValues.size(); ++nValueIndex)
+ {
+ if ( ! xSetItem->hasByName(rArguments[nValueIndex]))
+ bHasAllValues = false;
+ else
+ aValues[nValueIndex] = xSetItem->getByName(rArguments[nValueIndex]);
+ }
+ }
+ else
+ bHasAllValues = false;
+ if (bHasAllValues)
+ rProcessor(aValues);
+ }
+}
+
+void PresenterConfigurationAccess::ForAll (
+ const Reference<container::XNameAccess>& rxContainer,
+ const PropertySetProcessor& rProcessor)
+{
+ if (rxContainer.is())
+ {
+ const Sequence<OUString> aKeys (rxContainer->getElementNames());
+ for (const OUString& rsKey : aKeys)
+ {
+ Reference<beans::XPropertySet> xSet (rxContainer->getByName(rsKey), UNO_QUERY);
+ if (xSet.is())
+ rProcessor(rsKey, xSet);
+ }
+ }
+}
+
+Any PresenterConfigurationAccess::Find (
+ const Reference<container::XNameAccess>& rxContainer,
+ const Predicate& rPredicate)
+{
+ if (rxContainer.is())
+ {
+ const Sequence<OUString> aKeys (rxContainer->getElementNames());
+ for (const auto& rKey : aKeys)
+ {
+ Reference<beans::XPropertySet> xProperties (
+ rxContainer->getByName(rKey),
+ UNO_QUERY);
+ if (xProperties.is())
+ if (rPredicate(rKey, xProperties))
+ return Any(xProperties);
+ }
+ }
+ return Any();
+}
+
+bool PresenterConfigurationAccess::IsStringPropertyEqual (
+ std::u16string_view rsValue,
+ const OUString& rsPropertyName,
+ const css::uno::Reference<css::beans::XPropertySet>& rxNode)
+{
+ OUString sValue;
+ if (GetProperty(rxNode, rsPropertyName) >>= sValue)
+ return sValue == rsValue;
+ else
+ return false;
+}
+
+Any PresenterConfigurationAccess::GetProperty (
+ const Reference<beans::XPropertySet>& rxProperties,
+ const OUString& rsKey)
+{
+ OSL_ASSERT(rxProperties.is());
+ if ( ! rxProperties.is())
+ return Any();
+ try
+ {
+ Reference<beans::XPropertySetInfo> xInfo (rxProperties->getPropertySetInfo());
+ if (xInfo.is())
+ if ( ! xInfo->hasPropertyByName(rsKey))
+ return Any();
+ return rxProperties->getPropertyValue(rsKey);
+ }
+ catch (beans::UnknownPropertyException&)
+ {
+ }
+ return Any();
+}
+
+} // end of namespace sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterConfigurationAccess.hxx b/sdext/source/presenter/PresenterConfigurationAccess.hxx
new file mode 100644
index 000000000..afd1b9aa4
--- /dev/null
+++ b/sdext/source/presenter/PresenterConfigurationAccess.hxx
@@ -0,0 +1,178 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERCONFIGURATIONACCESS_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERCONFIGURATIONACCESS_HXX
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <vector>
+#include <functional>
+
+namespace sdext::presenter {
+
+/** This class gives access to the configuration. Create an object of this
+ class for one node of the configuration. This will be the root node.
+ From this one you can use this class in two ways.
+
+ <p>In a stateless mode (with exception of the root node) you can use static
+ methods for obtaining child nodes, get values from properties at leaf
+ nodes and iterate over children of inner nodes.</p>
+
+ <p>In a stateful mode use non-static methods like GoToChild() to
+ navigate to children.</p>
+
+ <p>Note to call CommitChanges() after making changes to
+ PresenterConfigurationAccess object that was opened in READ_WRITE mode.</p>
+*/
+class PresenterConfigurationAccess
+{
+public:
+ enum WriteMode { READ_WRITE, READ_ONLY };
+ typedef ::std::function<bool (
+ const OUString&,
+ const css::uno::Reference<css::beans::XPropertySet>&)> Predicate;
+ static constexpr OUStringLiteral msPresenterScreenRootName =
+ u"/org.openoffice.Office.PresenterScreen/";
+
+ /** Create a new object to access the configuration entries below the
+ given root.
+ @param rsRootName
+ Name of the root. You can use msPresenterScreenRootName to
+ access the configuration of the presenter screen.
+ @param eMode
+ This flag specifies whether to give read-write or read-only
+ access.
+ */
+ PresenterConfigurationAccess(
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const OUString& rsRootName,
+ WriteMode eMode);
+
+ ~PresenterConfigurationAccess();
+
+ /** Return a configuration node below the root of the called object.
+ @param rsPathToNode
+ The relative path from the root (as given the constructor) to the node.
+ */
+ css::uno::Any GetConfigurationNode (
+ const OUString& rsPathToNode);
+
+ /** Return <TRUE/> when opening the configuration (via creating a new
+ PresenterConfigurationAccess object) or previous calls to
+ GoToChild() left the called PresenterConfigurationAccess object in a
+ valid state.
+ */
+ bool IsValid() const;
+
+ /** Move the focused node to the (possibly indirect) child specified by the given path.
+ */
+ bool GoToChild (const OUString& rsPathToNode);
+
+ /** Move the focused node to the first direct child that fulfills the given predicate.
+ */
+ bool GoToChild (const Predicate& rPredicate);
+
+ /** Modify the property child of the currently focused node. Keep in
+ mind to call CommitChanges() to write the change back to the
+ configuration.
+ */
+ bool SetProperty (const OUString& rsPropertyName, const css::uno::Any& rValue);
+
+ /** Return a configuration node below the given node.
+ @param rxNode
+ The node that acts as root to the given relative path.
+ @param rsPathToNode
+ The relative path from the given node to the requested node.
+ When this string is empty then rxNode is returned.
+ @return
+ The type of the returned node varies with the requested node.
+ It is empty when the node was not found.
+ */
+ static css::uno::Any GetConfigurationNode (
+ const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxNode,
+ const OUString& rsPathToNode);
+
+ static css::uno::Reference<css::beans::XPropertySet> GetNodeProperties (
+ const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxNode,
+ const OUString& rsPathToNode);
+
+ /** Write any changes that have been made back to the configuration.
+ This call is ignored when the called ConfigurationAccess object was
+ not create with read-write mode.
+ */
+ void CommitChanges();
+
+ typedef ::std::function<void (
+ const ::std::vector<css::uno::Any>&) > ItemProcessor;
+ typedef ::std::function<void (
+ const OUString&,
+ const css::uno::Reference<css::beans::XPropertySet>&) > PropertySetProcessor;
+
+ /** Execute a functor for all elements of the given container.
+ @param rxContainer
+ The container is a XNameAccess to a list of the configuration.
+ This can be a node returned by GetConfigurationNode().
+ @param rArguments
+ The functor is called with arguments that are children of each
+ element of the container. The set of children is specified this
+ list.
+ @param rFunctor
+ The functor to be executed for some or all of the elements in
+ the given container.
+ */
+ static void ForAll (
+ const css::uno::Reference<css::container::XNameAccess>& rxContainer,
+ const ::std::vector<OUString>& rArguments,
+ const ItemProcessor& rProcessor);
+ static void ForAll (
+ const css::uno::Reference<css::container::XNameAccess>& rxContainer,
+ const PropertySetProcessor& rProcessor);
+
+ static css::uno::Any Find (
+ const css::uno::Reference<css::container::XNameAccess>& rxContainer,
+ const Predicate& rPredicate);
+
+ static bool IsStringPropertyEqual (
+ std::u16string_view rsValue,
+ const OUString& rsPropertyName,
+ const css::uno::Reference<css::beans::XPropertySet>& rxNode);
+
+ /** This method wraps a call to getPropertyValue() and returns an empty
+ Any instead of throwing an exception when the property does not
+ exist.
+ */
+ static css::uno::Any GetProperty (
+ const css::uno::Reference<css::beans::XPropertySet>& rxProperties,
+ const OUString& rsKey);
+
+private:
+ css::uno::Reference<css::uno::XInterface> mxRoot;
+ css::uno::Any maNode;
+};
+
+} // end of namespace sdext::tools
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterController.cxx b/sdext/source/presenter/PresenterController.cxx
new file mode 100644
index 000000000..1062d892d
--- /dev/null
+++ b/sdext/source/presenter/PresenterController.cxx
@@ -0,0 +1,1185 @@
+/* -*- 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 "PresenterController.hxx"
+
+#include "PresenterAccessibility.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterCurrentSlideObserver.hxx"
+#include "PresenterScreen.hxx"
+#include "PresenterPaintManager.hxx"
+#include "PresenterPaneBase.hxx"
+#include "PresenterPaneContainer.hxx"
+#include "PresenterPaneBorderPainter.hxx"
+#include "PresenterTheme.hxx"
+#include "PresenterViewFactory.hxx"
+#include "PresenterWindowManager.hxx"
+
+#include <com/sun/star/awt/Key.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/framework/ResourceActivationMode.hpp>
+#include <com/sun/star/drawing/framework/ResourceId.hpp>
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/presentation/AnimationEffect.hpp>
+#include <com/sun/star/presentation/XPresentation.hpp>
+#include <com/sun/star/presentation/XPresentationSupplier.hpp>
+#include <com/sun/star/rendering/TextDirection.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+#include <rtl/ustrbuf.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::presentation;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace {
+ const sal_Int32 ResourceActivationEventType = 0;
+ const sal_Int32 ResourceDeactivationEventType = 1;
+ const sal_Int32 ConfigurationUpdateEndEventType = 2;
+}
+
+namespace sdext::presenter {
+
+IPresentationTime::~IPresentationTime()
+{
+}
+
+PresenterController::InstanceContainer PresenterController::maInstances;
+
+::rtl::Reference<PresenterController> PresenterController::Instance (
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+{
+ InstanceContainer::const_iterator iInstance (maInstances.find(rxFrame));
+ if (iInstance != maInstances.end())
+ return iInstance->second;
+ else
+ return ::rtl::Reference<PresenterController>();
+}
+
+PresenterController::PresenterController (
+ const css::uno::WeakReference<css::lang::XEventListener> &rxScreen,
+ const Reference<XComponentContext>& rxContext,
+ const Reference<frame::XController>& rxController,
+ const Reference<presentation::XSlideShowController>& rxSlideShowController,
+ const rtl::Reference<PresenterPaneContainer>& rpPaneContainer,
+ const Reference<XResourceId>& rxMainPaneId)
+ : PresenterControllerInterfaceBase(m_aMutex),
+ mxScreen(rxScreen),
+ mxComponentContext(rxContext),
+ mxController(rxController),
+ mxSlideShowController(rxSlideShowController),
+ mxMainPaneId(rxMainPaneId),
+ mpPaneContainer(rpPaneContainer),
+ mnCurrentSlideIndex(-1),
+ mpWindowManager(new PresenterWindowManager(rxContext,mpPaneContainer,this)),
+ mpCanvasHelper(std::make_shared<PresenterCanvasHelper>()),
+ mnPendingSlideNumber(-1),
+ mbIsAccessibilityActive(false)
+{
+ OSL_ASSERT(mxController.is());
+
+ if ( ! mxSlideShowController.is())
+ throw lang::IllegalArgumentException(
+ "missing slide show controller",
+ static_cast<XWeak*>(this),
+ 2);
+
+ new PresenterCurrentSlideObserver(this,rxSlideShowController);
+
+ // Listen for configuration changes.
+ Reference<XControllerManager> xCM (mxController, UNO_QUERY_THROW);
+ mxConfigurationController = xCM->getConfigurationController();
+ if (mxConfigurationController.is())
+ {
+ mxConfigurationController->addConfigurationChangeListener(
+ this,
+ "ResourceActivation",
+ Any(ResourceActivationEventType));
+ mxConfigurationController->addConfigurationChangeListener(
+ this,
+ "ResourceDeactivation",
+ Any(ResourceDeactivationEventType));
+ mxConfigurationController->addConfigurationChangeListener(
+ this,
+ "ConfigurationUpdateEnd",
+ Any(ConfigurationUpdateEndEventType));
+ }
+
+ // Listen for the frame being activated.
+ Reference<frame::XFrame> xFrame (mxController->getFrame());
+ if (xFrame.is())
+ xFrame->addFrameActionListener(this);
+
+ // Create the border painter.
+ mpPaneBorderPainter = new PresenterPaneBorderPainter(rxContext);
+ mpWindowManager->SetPaneBorderPainter(mpPaneBorderPainter);
+
+ // Create an object that is able to load the bitmaps in a format that is
+ // supported by the canvas.
+ Reference<lang::XMultiComponentFactory> xFactory =
+ rxContext->getServiceManager();
+ if ( ! xFactory.is())
+ return;
+ mxPresenterHelper.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.drawing.PresenterHelper",
+ rxContext),
+ UNO_QUERY_THROW);
+
+ if (mxSlideShowController.is())
+ {
+ mxSlideShowController->activate();
+ Reference<beans::XPropertySet> xProperties (mxSlideShowController, UNO_QUERY);
+ if (xProperties.is())
+ {
+ Reference<awt::XWindow> xWindow (
+ xProperties->getPropertyValue("ParentWindow"), UNO_QUERY);
+ if (xWindow.is())
+ xWindow->addKeyListener(this);
+ }
+ }
+
+ UpdateCurrentSlide(0);
+
+ maInstances[mxController->getFrame()] = this;
+
+ // Create a URLTransformer.
+ if (xFactory.is())
+ {
+ mxUrlTransformer.set(util::URLTransformer::create(mxComponentContext));
+ }
+}
+
+PresenterController::~PresenterController()
+{
+}
+
+void PresenterController::disposing()
+{
+ maInstances.erase(mxController->getFrame());
+
+ if (mxMainWindow.is())
+ {
+ mxMainWindow->removeKeyListener(this);
+ mxMainWindow->removeMouseListener(this);
+ mxMainWindow = nullptr;
+ }
+ if (mxConfigurationController.is())
+ mxConfigurationController->removeConfigurationChangeListener(this);
+
+ if (mxController.is())
+ {
+ Reference<frame::XFrame> xFrame (mxController->getFrame());
+ if (xFrame.is())
+ xFrame->removeFrameActionListener(this);
+ mxController = nullptr;
+ }
+
+ Reference<XComponent> xWindowManagerComponent = mpWindowManager;
+ mpWindowManager = nullptr;
+ if (xWindowManagerComponent.is())
+ xWindowManagerComponent->dispose();
+
+ mxComponentContext = nullptr;
+ mxConfigurationController = nullptr;
+ mxSlideShowController = nullptr;
+ mxMainPaneId = nullptr;
+ mpPaneContainer = nullptr;
+ mnCurrentSlideIndex = -1;
+ mxCurrentSlide = nullptr;
+ mxNextSlide = nullptr;
+ mpTheme.reset();
+ {
+ Reference<lang::XComponent> xComponent = mpPaneBorderPainter;
+ mpPaneBorderPainter = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ mpCanvasHelper.reset();
+ {
+ Reference<lang::XComponent> xComponent (mxPresenterHelper, UNO_QUERY);
+ mxPresenterHelper = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ mpPaintManager.reset();
+ mnPendingSlideNumber = -1;
+ {
+ Reference<lang::XComponent> xComponent (mxUrlTransformer, UNO_QUERY);
+ mxUrlTransformer = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+}
+
+void PresenterController::UpdateCurrentSlide (const sal_Int32 nOffset)
+{
+ // std::cerr << "Updating current Slide to " << nOffset << std::endl;
+ GetSlides(nOffset);
+ UpdatePaneTitles();
+ UpdateViews();
+
+ // Update the accessibility object.
+ if (IsAccessibilityActive())
+ {
+ mpAccessibleObject->NotifyCurrentSlideChange();
+ }
+}
+
+void PresenterController::GetSlides (const sal_Int32 nOffset)
+{
+ if ( ! mxSlideShowController.is())
+ return;
+
+ // Get the current slide from the slide show controller.
+ mxCurrentSlide = nullptr;
+ Reference<container::XIndexAccess> xIndexAccess(mxSlideShowController, UNO_QUERY);
+ try
+ {
+ sal_Int32 nSlideIndex = mxSlideShowController->getCurrentSlideIndex() + nOffset;
+ if (mxSlideShowController->isPaused())
+ nSlideIndex = -1;
+
+ if (xIndexAccess.is() && nSlideIndex>=0)
+ {
+ if (nSlideIndex < xIndexAccess->getCount())
+ {
+ mnCurrentSlideIndex = nSlideIndex;
+ mxCurrentSlide.set( xIndexAccess->getByIndex(nSlideIndex), UNO_QUERY);
+ }
+ }
+ }
+ catch (RuntimeException&)
+ {
+ }
+
+ // Get the next slide.
+ mxNextSlide = nullptr;
+ try
+ {
+ const sal_Int32 nNextSlideIndex (mxSlideShowController->getNextSlideIndex()+nOffset);
+ if (nNextSlideIndex >= 0)
+ {
+ if (xIndexAccess.is())
+ {
+ if (nNextSlideIndex < xIndexAccess->getCount())
+ mxNextSlide.set( xIndexAccess->getByIndex(nNextSlideIndex), UNO_QUERY);
+ }
+ }
+ }
+ catch (RuntimeException&)
+ {
+ }
+}
+
+void PresenterController::UpdatePaneTitles()
+{
+ if ( ! mxSlideShowController.is())
+ return;
+
+ // Get placeholders and their values.
+ static const OUStringLiteral sCurrentSlideNumberPlaceholder (u"CURRENT_SLIDE_NUMBER");
+ static const OUStringLiteral sCurrentSlideNamePlaceholder (u"CURRENT_SLIDE_NAME");
+ static const OUStringLiteral sSlideCountPlaceholder (u"SLIDE_COUNT");
+
+ // Get string for slide count.
+ OUString sSlideCount ("---");
+ Reference<container::XIndexAccess> xIndexAccess(mxSlideShowController, UNO_QUERY);
+ if (xIndexAccess.is())
+ sSlideCount = OUString::number(xIndexAccess->getCount());
+
+ // Get string for current slide index.
+ OUString sCurrentSlideNumber (OUString::number(mnCurrentSlideIndex + 1));
+
+ // Get name of the current slide.
+ OUString sCurrentSlideName;
+ Reference<container::XNamed> xNamedSlide (mxCurrentSlide, UNO_QUERY);
+ if (xNamedSlide.is())
+ sCurrentSlideName = xNamedSlide->getName();
+ Reference<beans::XPropertySet> xSlideProperties (mxCurrentSlide, UNO_QUERY);
+ if (xSlideProperties.is())
+ {
+ try
+ {
+ OUString sName;
+ if (xSlideProperties->getPropertyValue("LinkDisplayName") >>= sName)
+ {
+ // Find out whether the name of the current slide has been
+ // automatically created or has been set by the user.
+ if (sName != sCurrentSlideName)
+ sCurrentSlideName = sName;
+ }
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ }
+ }
+
+ // Replace the placeholders with their current values.
+ for (auto& rxPane : mpPaneContainer->maPanes)
+ {
+ OSL_ASSERT(rxPane != nullptr);
+
+ OUString sTemplate (IsAccessibilityActive()
+ ? rxPane->msAccessibleTitleTemplate
+ : rxPane->msTitleTemplate);
+ if (sTemplate.isEmpty())
+ continue;
+
+ OUStringBuffer sResult;
+ sResult.ensureCapacity(sTemplate.getLength());
+
+ sal_Int32 nIndex (0);
+ while (true)
+ {
+ sal_Int32 nStartIndex = sTemplate.indexOf('%', nIndex);
+ if (nStartIndex < 0)
+ {
+ // Add the remaining part of the string.
+ sResult.append(sTemplate.subView(nIndex));
+ break;
+ }
+ else
+ {
+ // Add the part preceding the next %.
+ sResult.append(sTemplate.subView(nIndex, nStartIndex-nIndex));
+
+ // Get the placeholder
+ ++nStartIndex;
+ const sal_Int32 nEndIndex (sTemplate.indexOf('%', nStartIndex+1));
+ const std::u16string_view sPlaceholder (sTemplate.subView(nStartIndex, nEndIndex-nStartIndex));
+ nIndex = nEndIndex+1;
+
+ // Replace the placeholder with its current value.
+ if (sPlaceholder == sCurrentSlideNumberPlaceholder)
+ sResult.append(sCurrentSlideNumber);
+ else if (sPlaceholder == sCurrentSlideNamePlaceholder)
+ sResult.append(sCurrentSlideName);
+ else if (sPlaceholder == sSlideCountPlaceholder)
+ sResult.append(sSlideCount);
+ }
+ }
+
+ rxPane->msTitle = sResult.makeStringAndClear();
+ if (rxPane->mxPane.is())
+ rxPane->mxPane->SetTitle(rxPane->msTitle);
+ }
+}
+
+void PresenterController::UpdateViews()
+{
+ // Tell all views about the slides they should display.
+ for (const auto& rxPane : mpPaneContainer->maPanes)
+ {
+ Reference<drawing::XDrawView> xDrawView (rxPane->mxView, UNO_QUERY);
+ if (xDrawView.is())
+ xDrawView->setCurrentPage(mxCurrentSlide);
+ }
+}
+
+SharedBitmapDescriptor
+ PresenterController::GetViewBackground (const OUString& rsViewURL) const
+{
+ if (mpTheme != nullptr)
+ {
+ const OUString sStyleName (mpTheme->GetStyleName(rsViewURL));
+ return mpTheme->GetBitmap(sStyleName, "Background");
+ }
+ return SharedBitmapDescriptor();
+}
+
+PresenterTheme::SharedFontDescriptor
+ PresenterController::GetViewFont (const OUString& rsViewURL) const
+{
+ if (mpTheme != nullptr)
+ {
+ const OUString sStyleName (mpTheme->GetStyleName(rsViewURL));
+ return mpTheme->GetFont(sStyleName);
+ }
+ return PresenterTheme::SharedFontDescriptor();
+}
+
+const std::shared_ptr<PresenterTheme>& PresenterController::GetTheme() const
+{
+ return mpTheme;
+}
+
+const ::rtl::Reference<PresenterWindowManager>& PresenterController::GetWindowManager() const
+{
+ return mpWindowManager;
+}
+
+const Reference<presentation::XSlideShowController>&
+ PresenterController::GetSlideShowController() const
+{
+ return mxSlideShowController;
+}
+
+const rtl::Reference<PresenterPaneContainer>& PresenterController::GetPaneContainer() const
+{
+ return mpPaneContainer;
+}
+
+const ::rtl::Reference<PresenterPaneBorderPainter>& PresenterController::GetPaneBorderPainter() const
+{
+ return mpPaneBorderPainter;
+}
+
+const std::shared_ptr<PresenterCanvasHelper>& PresenterController::GetCanvasHelper() const
+{
+ return mpCanvasHelper;
+}
+
+const Reference<drawing::XPresenterHelper>& PresenterController::GetPresenterHelper() const
+{
+ return mxPresenterHelper;
+}
+
+const std::shared_ptr<PresenterPaintManager>& PresenterController::GetPaintManager() const
+{
+ return mpPaintManager;
+}
+
+void PresenterController::ShowView (const OUString& rsViewURL)
+{
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ mpPaneContainer->FindViewURL(rsViewURL));
+ if (!pDescriptor)
+ return;
+
+ pDescriptor->mbIsActive = true;
+ mxConfigurationController->requestResourceActivation(
+ pDescriptor->mxPaneId,
+ ResourceActivationMode_ADD);
+ mxConfigurationController->requestResourceActivation(
+ ResourceId::createWithAnchor(
+ mxComponentContext,
+ rsViewURL,
+ pDescriptor->mxPaneId),
+ ResourceActivationMode_REPLACE);
+}
+
+void PresenterController::HideView (const OUString& rsViewURL)
+{
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ mpPaneContainer->FindViewURL(rsViewURL));
+ if (pDescriptor)
+ {
+ mxConfigurationController->requestResourceDeactivation(
+ ResourceId::createWithAnchor(
+ mxComponentContext,
+ rsViewURL,
+ pDescriptor->mxPaneId));
+ }
+}
+
+void PresenterController::DispatchUnoCommand (const OUString& rsCommand) const
+{
+ if ( ! mxUrlTransformer.is())
+ return;
+
+ util::URL aURL;
+ aURL.Complete = rsCommand;
+ mxUrlTransformer->parseStrict(aURL);
+
+ Reference<frame::XDispatch> xDispatch (GetDispatch(aURL));
+ if ( ! xDispatch.is())
+ return;
+
+ xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>());
+}
+
+Reference<css::frame::XDispatch> PresenterController::GetDispatch (const util::URL& rURL) const
+{
+ if ( ! mxController.is())
+ return nullptr;
+
+ Reference<frame::XDispatchProvider> xDispatchProvider (mxController->getFrame(), UNO_QUERY);
+ if ( ! xDispatchProvider.is())
+ return nullptr;
+
+ return xDispatchProvider->queryDispatch(
+ rURL,
+ OUString(),
+ frame::FrameSearchFlag::SELF);
+}
+
+util::URL PresenterController::CreateURLFromString (const OUString& rsURL) const
+{
+ util::URL aURL;
+
+ if (mxUrlTransformer.is())
+ {
+ aURL.Complete = rsURL;
+ mxUrlTransformer->parseStrict(aURL);
+ }
+
+ return aURL;
+}
+
+const Reference<drawing::framework::XConfigurationController>&
+ PresenterController::GetConfigurationController() const
+{
+ return mxConfigurationController;
+}
+
+const Reference<drawing::XDrawPage>& PresenterController::GetCurrentSlide() const
+{
+ return mxCurrentSlide;
+}
+
+bool PresenterController::HasTransition (Reference<drawing::XDrawPage> const & rxPage)
+{
+ bool bTransition = false;
+ if( rxPage.is() )
+ {
+ Reference<beans::XPropertySet> xSlidePropertySet (rxPage, UNO_QUERY);
+ try
+ {
+ sal_uInt16 aTransitionType = 0;
+ xSlidePropertySet->getPropertyValue("TransitionType") >>= aTransitionType;
+ if (aTransitionType > 0)
+ {
+ bTransition = true;
+ }
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ }
+ }
+ return bTransition;
+}
+
+bool PresenterController::HasCustomAnimation (Reference<drawing::XDrawPage> const & rxPage)
+{
+ bool bCustomAnimation = false;
+ if( rxPage.is() )
+ {
+ sal_uInt32 i, nCount = rxPage->getCount();
+ for ( i = 0; i < nCount; i++ )
+ {
+ Reference<drawing::XShape> xShape(rxPage->getByIndex(i), UNO_QUERY);
+ Reference<beans::XPropertySet> xShapePropertySet(xShape, UNO_QUERY);
+ presentation::AnimationEffect aEffect = presentation::AnimationEffect_NONE;
+ presentation::AnimationEffect aTextEffect = presentation::AnimationEffect_NONE;
+ try
+ {
+ xShapePropertySet->getPropertyValue("Effect") >>= aEffect;
+ xShapePropertySet->getPropertyValue("TextEffect") >>= aTextEffect;
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ }
+ if( aEffect != presentation::AnimationEffect_NONE ||
+ aTextEffect != presentation::AnimationEffect_NONE )
+ {
+ bCustomAnimation = true;
+ break;
+ }
+ }
+ }
+ return bCustomAnimation;
+}
+
+void PresenterController::SetAccessibilityActiveState (const bool bIsActive)
+{
+ if ( mbIsAccessibilityActive != bIsActive)
+ {
+ mbIsAccessibilityActive = bIsActive;
+ UpdatePaneTitles();
+ }
+}
+
+
+void PresenterController::HandleMouseClick (const awt::MouseEvent& rEvent)
+{
+ if (!mxSlideShowController.is())
+ return;
+
+ switch (rEvent.Buttons)
+ {
+ case awt::MouseButton::LEFT:
+ if (rEvent.Modifiers == awt::KeyModifier::MOD2)
+ mxSlideShowController->gotoNextSlide();
+ else
+ mxSlideShowController->gotoNextEffect();
+ break;
+
+ case awt::MouseButton::RIGHT:
+ mxSlideShowController->gotoPreviousSlide();
+ break;
+
+ default:
+ // Other or multiple buttons.
+ break;
+ }
+}
+
+void PresenterController::RequestViews (
+ const bool bIsSlideSorterActive,
+ const bool bIsNotesViewActive,
+ const bool bIsHelpViewActive)
+{
+ for (const auto& rxPane : mpPaneContainer->maPanes)
+ {
+ bool bActivate (true);
+ const OUString sViewURL (rxPane->msViewURL);
+ if (sViewURL == PresenterViewFactory::msNotesViewURL)
+ {
+ bActivate = bIsNotesViewActive && !bIsSlideSorterActive && !bIsHelpViewActive;
+ }
+ else if (sViewURL == PresenterViewFactory::msSlideSorterURL)
+ {
+ bActivate = bIsSlideSorterActive;
+ }
+ else if (sViewURL == PresenterViewFactory::msCurrentSlidePreviewViewURL
+ || sViewURL == PresenterViewFactory::msNextSlidePreviewViewURL)
+ {
+ bActivate = !bIsSlideSorterActive && ! bIsHelpViewActive;
+ }
+ else if (sViewURL == PresenterViewFactory::msToolBarViewURL)
+ {
+ bActivate = true;
+ }
+ else if (sViewURL == PresenterViewFactory::msHelpViewURL)
+ {
+ bActivate = bIsHelpViewActive;
+ }
+
+ if (bActivate)
+ ShowView(sViewURL);
+ else
+ HideView(sViewURL);
+ }
+}
+
+void PresenterController::SetPresentationTime(IPresentationTime* pPresentationTime)
+{
+ mpPresentationTime = pPresentationTime;
+}
+
+IPresentationTime* PresenterController::GetPresentationTime()
+{
+ return mpPresentationTime;
+}
+
+//----- XConfigurationChangeListener ------------------------------------------
+
+void SAL_CALL PresenterController::notifyConfigurationChange (
+ const ConfigurationChangeEvent& rEvent)
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterController object has already been disposed",
+ static_cast<uno::XWeak*>(this));
+ }
+
+ sal_Int32 nType (0);
+ if ( ! (rEvent.UserData >>= nType))
+ return;
+
+ switch (nType)
+ {
+ case ResourceActivationEventType:
+ if (rEvent.ResourceId->compareTo(mxMainPaneId) == 0)
+ {
+ InitializeMainPane(Reference<XPane>(rEvent.ResourceObject,UNO_QUERY));
+ }
+ else if (rEvent.ResourceId->isBoundTo(mxMainPaneId,AnchorBindingMode_DIRECT))
+ {
+ // A pane bound to the main pane has been created and is
+ // stored in the pane container.
+ Reference<XPane> xPane (rEvent.ResourceObject,UNO_QUERY);
+ if (xPane.is())
+ {
+ mpPaneContainer->FindPaneId(xPane->getResourceId());
+ }
+ }
+ else if (rEvent.ResourceId->isBoundTo(mxMainPaneId,AnchorBindingMode_INDIRECT))
+ {
+ // A view bound to one of the panes has been created and is
+ // stored in the pane container along with its pane.
+ Reference<XView> xView (rEvent.ResourceObject,UNO_QUERY);
+ if (xView.is())
+ {
+ mpPaneContainer->StoreView(xView);
+ UpdateViews();
+ mpWindowManager->NotifyViewCreation(xView);
+ }
+ }
+ break;
+
+ case ResourceDeactivationEventType:
+ if (rEvent.ResourceId->isBoundTo(mxMainPaneId,AnchorBindingMode_INDIRECT))
+ {
+ // If this is a view then remove it from the pane container.
+ Reference<XView> xView (rEvent.ResourceObject,UNO_QUERY);
+ if (xView.is())
+ {
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor(
+ mpPaneContainer->RemoveView(xView));
+
+ // A possibly opaque view has been removed. Update()
+ // updates the clip polygon.
+ mpWindowManager->Update();
+ // Request the repainting of the area previously
+ // occupied by the view.
+ if (pDescriptor)
+ GetPaintManager()->Invalidate(pDescriptor->mxBorderWindow);
+ }
+ }
+ break;
+
+ case ConfigurationUpdateEndEventType:
+ if (IsAccessibilityActive())
+ {
+ mpAccessibleObject->UpdateAccessibilityHierarchy();
+ UpdateCurrentSlide(0);
+ }
+ break;
+ }
+}
+
+//----- XEventListener --------------------------------------------------------
+
+void SAL_CALL PresenterController::disposing (
+ const lang::EventObject& rEvent)
+{
+ if (rEvent.Source == mxController)
+ mxController = nullptr;
+ else if (rEvent.Source == mxConfigurationController)
+ mxConfigurationController = nullptr;
+ else if (rEvent.Source == mxSlideShowController)
+ mxSlideShowController = nullptr;
+ else if (rEvent.Source == mxMainWindow)
+ mxMainWindow = nullptr;
+}
+
+//----- XFrameActionListener --------------------------------------------------
+
+void SAL_CALL PresenterController::frameAction (
+ const frame::FrameActionEvent& rEvent)
+{
+ if (rEvent.Action == frame::FrameAction_FRAME_ACTIVATED)
+ {
+ if (mxSlideShowController.is())
+ mxSlideShowController->activate();
+ }
+}
+
+//----- XKeyListener ----------------------------------------------------------
+
+void SAL_CALL PresenterController::keyPressed (const awt::KeyEvent& rEvent)
+{
+ // Tell all views about the unhandled key event.
+ for (const auto& rxPane : mpPaneContainer->maPanes)
+ {
+ if ( ! rxPane->mbIsActive)
+ continue;
+
+ Reference<awt::XKeyListener> xKeyListener (rxPane->mxView, UNO_QUERY);
+ if (xKeyListener.is())
+ xKeyListener->keyPressed(rEvent);
+ }
+}
+
+void SAL_CALL PresenterController::keyReleased (const awt::KeyEvent& rEvent)
+{
+ if (rEvent.Source != mxMainWindow)
+ return;
+
+ switch (rEvent.KeyCode)
+ {
+ case awt::Key::ESCAPE:
+ case awt::Key::SUBTRACT:
+ {
+ if( mxController.is() )
+ {
+ Reference< XPresentationSupplier > xPS( mxController->getModel(), UNO_QUERY );
+ if( xPS.is() )
+ {
+ Reference< XPresentation > xP( xPS->getPresentation() );
+ if( xP.is() )
+ xP->end();
+ }
+ }
+ }
+ break;
+
+ case awt::Key::PAGEDOWN:
+ if (mxSlideShowController.is())
+ {
+ if (rEvent.Modifiers == awt::KeyModifier::MOD2)
+ mxSlideShowController->gotoNextSlide();
+ else
+ mxSlideShowController->gotoNextEffect();
+ }
+ break;
+
+ case awt::Key::RIGHT:
+ case awt::Key::SPACE:
+ case awt::Key::DOWN:
+ if (mxSlideShowController.is())
+ {
+ mxSlideShowController->gotoNextEffect();
+ }
+ break;
+
+ case awt::Key::PAGEUP:
+ if (mxSlideShowController.is())
+ {
+ if (rEvent.Modifiers == awt::KeyModifier::MOD2)
+ mxSlideShowController->gotoPreviousSlide();
+ else
+ mxSlideShowController->gotoPreviousEffect();
+ }
+ break;
+
+ case awt::Key::LEFT:
+ case awt::Key::UP:
+ case awt::Key::BACKSPACE:
+ if (mxSlideShowController.is())
+ {
+ mxSlideShowController->gotoPreviousEffect();
+ }
+ break;
+
+ case awt::Key::P:
+ if (mxSlideShowController.is())
+ {
+ bool bPenEnabled = mxSlideShowController->getUsePen();
+ mxSlideShowController->setUsePen( !bPenEnabled );
+ }
+ break;
+
+ // tdf#149351 Ctrl+A disables pointer as pen mode
+ case awt::Key::A:
+ if (mxSlideShowController.is())
+ {
+ if (rEvent.Modifiers == awt::KeyModifier::MOD1)
+ {
+ mxSlideShowController->setUsePen( false );
+ }
+ }
+ break;
+
+ case awt::Key::E:
+ if (mxSlideShowController.is())
+ {
+ mxSlideShowController->setEraseAllInk( true );
+ }
+ break;
+
+ case awt::Key::HOME:
+ if (mxSlideShowController.is())
+ {
+ mxSlideShowController->gotoFirstSlide();
+ }
+ break;
+
+ case awt::Key::END:
+ if (mxSlideShowController.is())
+ {
+ mxSlideShowController->gotoLastSlide();
+ }
+ break;
+
+ case awt::Key::W:
+ case awt::Key::COMMA:
+ if (mxSlideShowController.is())
+ {
+ if (mxSlideShowController->isPaused())
+ mxSlideShowController->resume();
+ else
+ mxSlideShowController->blankScreen(0x00ffffff);
+ }
+ break;
+
+ case awt::Key::B:
+ case awt::Key::POINT:
+ if (mxSlideShowController.is())
+ {
+ if (mxSlideShowController->isPaused())
+ mxSlideShowController->resume();
+ else
+ mxSlideShowController->blankScreen(0x00000000);
+ }
+ break;
+
+ case awt::Key::NUM0:
+ case awt::Key::NUM1:
+ case awt::Key::NUM2:
+ case awt::Key::NUM3:
+ case awt::Key::NUM4:
+ case awt::Key::NUM5:
+ case awt::Key::NUM6:
+ case awt::Key::NUM7:
+ case awt::Key::NUM8:
+ case awt::Key::NUM9:
+ HandleNumericKeyPress(rEvent.KeyCode-awt::Key::NUM0, rEvent.Modifiers);
+ break;
+
+ case awt::Key::RETURN:
+ if (mnPendingSlideNumber > 0)
+ {
+ if (mxSlideShowController.is())
+ mxSlideShowController->gotoSlideIndex(mnPendingSlideNumber - 1);
+ mnPendingSlideNumber = -1;
+ }
+ else
+ {
+ if (mxSlideShowController.is())
+ mxSlideShowController->gotoNextEffect();
+ }
+
+ break;
+
+ case awt::Key::F1:
+ // Toggle the help view.
+ if (mpWindowManager)
+ {
+ if (mpWindowManager->GetViewMode() != PresenterWindowManager::VM_Help)
+ mpWindowManager->SetViewMode(PresenterWindowManager::VM_Help);
+ else
+ mpWindowManager->SetHelpViewState(false);
+ }
+
+ break;
+
+ default:
+ // Tell all views about the unhandled key event.
+ for (const auto& rxPane : mpPaneContainer->maPanes)
+ {
+ if ( ! rxPane->mbIsActive)
+ continue;
+
+ Reference<awt::XKeyListener> xKeyListener (rxPane->mxView, UNO_QUERY);
+ if (xKeyListener.is())
+ xKeyListener->keyReleased(rEvent);
+ }
+ break;
+ }
+}
+
+void PresenterController::HandleNumericKeyPress (
+ const sal_Int32 nKey,
+ const sal_Int32 nModifiers)
+{
+ switch (nModifiers)
+ {
+ case 0:
+ if (mnPendingSlideNumber == -1)
+ mnPendingSlideNumber = 0;
+ UpdatePendingSlideNumber(mnPendingSlideNumber * 10 + nKey);
+ break;
+
+ case awt::KeyModifier::MOD1:
+ // Ctrl-1, Ctrl-2, and Ctrl-3 are used to switch between views
+ // (slide view, notes view, normal). Ctrl-4 switches monitors
+ mnPendingSlideNumber = -1;
+ if (!mpWindowManager)
+ return;
+ switch(nKey)
+ {
+ case 1:
+ mpWindowManager->SetViewMode(PresenterWindowManager::VM_Standard);
+ break;
+ case 2:
+ mpWindowManager->SetViewMode(PresenterWindowManager::VM_Notes);
+ break;
+ case 3:
+ mpWindowManager->SetViewMode(PresenterWindowManager::VM_SlideOverview);
+ break;
+ case 4:
+ SwitchMonitors();
+ break;
+ default:
+ // Ignore unsupported key.
+ break;
+ }
+ break;
+
+ default:
+ // Ignore unsupported modifiers.
+ break;
+ }
+}
+
+//----- XMouseListener --------------------------------------------------------
+
+void SAL_CALL PresenterController::mousePressed (const css::awt::MouseEvent&)
+{
+ if (mxMainWindow.is())
+ mxMainWindow->setFocus();
+}
+
+void SAL_CALL PresenterController::mouseReleased (const css::awt::MouseEvent&) {}
+
+void SAL_CALL PresenterController::mouseEntered (const css::awt::MouseEvent&) {}
+
+void SAL_CALL PresenterController::mouseExited (const css::awt::MouseEvent&) {}
+
+void PresenterController::InitializeMainPane (const Reference<XPane>& rxPane)
+{
+ if ( ! rxPane.is())
+ return;
+
+ mpAccessibleObject = new PresenterAccessible(
+ mxComponentContext,
+ this,
+ rxPane);
+
+ LoadTheme(rxPane);
+
+ // Main pane has been created and is now observed by the window
+ // manager.
+ mpWindowManager->SetParentPane(rxPane);
+ mpWindowManager->SetTheme(mpTheme);
+
+ if (mpPaneBorderPainter)
+ mpPaneBorderPainter->SetTheme(mpTheme);
+
+ // Add key listener
+ mxMainWindow = rxPane->getWindow();
+ if (mxMainWindow.is())
+ {
+ mxMainWindow->addKeyListener(this);
+ mxMainWindow->addMouseListener(this);
+ }
+ Reference<XPane2> xPane2 (rxPane, UNO_QUERY);
+ if (xPane2.is())
+ xPane2->setVisible(true);
+
+ mpPaintManager = std::make_shared<PresenterPaintManager>(mxMainWindow, mxPresenterHelper, mpPaneContainer);
+
+ mxCanvas.set(rxPane->getCanvas(), UNO_QUERY);
+
+ if (mxSlideShowController.is())
+ mxSlideShowController->activate();
+
+ UpdateCurrentSlide(0);
+}
+
+void PresenterController::LoadTheme (const Reference<XPane>& rxPane)
+{
+ // Create (load) the current theme.
+ if (rxPane.is())
+ mpTheme = std::make_shared<PresenterTheme>(mxComponentContext, rxPane->getCanvas());
+}
+
+double PresenterController::GetSlideAspectRatio() const
+{
+ double nSlideAspectRatio (28.0/21.0);
+
+ try
+ {
+ if (mxController.is())
+ {
+ Reference<drawing::XDrawPagesSupplier> xSlideSupplier (
+ mxController->getModel(), UNO_QUERY_THROW);
+ Reference<drawing::XDrawPages> xSlides (xSlideSupplier->getDrawPages());
+ if (xSlides.is() && xSlides->getCount()>0)
+ {
+ Reference<beans::XPropertySet> xProperties(xSlides->getByIndex(0),UNO_QUERY_THROW);
+ sal_Int32 nWidth (28000);
+ sal_Int32 nHeight (21000);
+ if ((xProperties->getPropertyValue("Width") >>= nWidth)
+ && (xProperties->getPropertyValue("Height") >>= nHeight)
+ && nHeight > 0)
+ {
+ nSlideAspectRatio = double(nWidth) / double(nHeight);
+ }
+ }
+ }
+ }
+ catch (RuntimeException&)
+ {
+ OSL_ASSERT(false);
+ }
+
+ return nSlideAspectRatio;
+}
+
+void PresenterController::UpdatePendingSlideNumber (const sal_Int32 nPendingSlideNumber)
+{
+ mnPendingSlideNumber = nPendingSlideNumber;
+
+ if (mpTheme == nullptr)
+ return;
+
+ if ( ! mxMainWindow.is())
+ return;
+
+ PresenterTheme::SharedFontDescriptor pFont (
+ mpTheme->GetFont("PendingSlideNumberFont"));
+ if (!pFont)
+ return;
+
+ pFont->PrepareFont(mxCanvas);
+ if ( ! pFont->mxFont.is())
+ return;
+
+ const OUString sText (OUString::number(mnPendingSlideNumber));
+ rendering::StringContext aContext (sText, 0, sText.getLength());
+ pFont->mxFont->createTextLayout(
+ aContext,
+ rendering::TextDirection::WEAK_LEFT_TO_RIGHT,
+ 0);
+}
+
+void PresenterController::SwitchMonitors()
+{
+ Reference<lang::XEventListener> xScreen( mxScreen );
+ if (!xScreen.is())
+ return;
+
+ PresenterScreen *pScreen = dynamic_cast<PresenterScreen *>(xScreen.get());
+ if (!pScreen)
+ return;
+
+ pScreen->SwitchMonitors();
+}
+
+void PresenterController::ExitPresenter()
+{
+ if( mxController.is() )
+ {
+ Reference< XPresentationSupplier > xPS( mxController->getModel(), UNO_QUERY );
+ if( xPS.is() )
+ {
+ Reference< XPresentation > xP( xPS->getPresentation() );
+ if( xP.is() )
+ xP->end();
+ }
+ }
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterController.hxx b/sdext/source/presenter/PresenterController.hxx
new file mode 100644
index 000000000..1a9d8a1aa
--- /dev/null
+++ b/sdext/source/presenter/PresenterController.hxx
@@ -0,0 +1,222 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERCONTROLLER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERCONTROLLER_HXX
+
+#include "PresenterAccessibility.hxx"
+#include "PresenterPaneContainer.hxx"
+#include "PresenterTheme.hxx"
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <com/sun/star/awt/XKeyListener.hpp>
+#include <com/sun/star/awt/XMouseListener.hpp>
+#include <com/sun/star/drawing/XPresenterHelper.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/presentation/XSlideShowController.hpp>
+#include <com/sun/star/frame/XFrameActionListener.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <com/sun/star/drawing/framework/XPane.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <rtl/ref.hxx>
+#include <map>
+#include <memory>
+
+namespace sdext::presenter {
+
+class PresenterCanvasHelper;
+class PresenterPaintManager;
+class PresenterPaneAnimator;
+class PresenterPaneContainer;
+class PresenterPaneBorderPainter;
+class PresenterTheme;
+class PresenterWindowManager;
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::drawing::framework::XConfigurationChangeListener,
+ css::frame::XFrameActionListener,
+ css::awt::XKeyListener,
+ css::awt::XMouseListener
+> PresenterControllerInterfaceBase;
+
+/// Represents an element in the toolbar that shows the time elapsed since the presentation started.
+class IPresentationTime
+{
+public:
+ virtual void restart() = 0;
+ virtual bool isPaused() = 0;
+ virtual void setPauseStatus(const bool pauseStatus) = 0;
+ virtual ~IPresentationTime();
+};
+
+/** The controller of the presenter screen is responsible for telling the
+ individual views which slides to show. Additionally it provides access
+ to frequently used values of the current theme.
+*/
+class PresenterController
+ : protected ::cppu::BaseMutex,
+ public PresenterControllerInterfaceBase
+{
+public:
+ static ::rtl::Reference<PresenterController> Instance (
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+
+ PresenterController (
+ const css::uno::WeakReference<css::lang::XEventListener> &rxScreen,
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const css::uno::Reference<css::presentation::XSlideShowController>& rxSlideShowController,
+ const rtl::Reference<PresenterPaneContainer>& rpPaneContainer,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxMainPaneId);
+ virtual ~PresenterController() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ void UpdateCurrentSlide (const sal_Int32 nOffset);
+
+ SharedBitmapDescriptor
+ GetViewBackground (const OUString& rsViewURL) const;
+ PresenterTheme::SharedFontDescriptor
+ GetViewFont (const OUString& rsViewURL) const;
+ const std::shared_ptr<PresenterTheme>& GetTheme() const;
+ const ::rtl::Reference<PresenterWindowManager>& GetWindowManager() const;
+ const css::uno::Reference<css::presentation::XSlideShowController>&
+ GetSlideShowController() const;
+ const rtl::Reference<PresenterPaneContainer>& GetPaneContainer() const;
+ const ::rtl::Reference<PresenterPaneBorderPainter>& GetPaneBorderPainter() const;
+ const std::shared_ptr<PresenterCanvasHelper>& GetCanvasHelper() const;
+ const css::uno::Reference<css::drawing::XPresenterHelper>& GetPresenterHelper() const;
+ const std::shared_ptr<PresenterPaintManager>& GetPaintManager() const;
+ double GetSlideAspectRatio() const;
+ void ShowView (const OUString& rsViewURL);
+ void HideView (const OUString& rsViewURL);
+ void SwitchMonitors();
+ void ExitPresenter();
+ void DispatchUnoCommand (const OUString& rsCommand) const;
+ css::uno::Reference<css::frame::XDispatch> GetDispatch (
+ const css::util::URL& rURL) const;
+ css::util::URL CreateURLFromString (const OUString& rsURL) const;
+ const css::uno::Reference<css::drawing::framework::XConfigurationController>&
+ GetConfigurationController() const;
+ const css::uno::Reference<css::drawing::XDrawPage>& GetCurrentSlide() const;
+ static bool HasTransition (css::uno::Reference<css::drawing::XDrawPage> const & rxPage);
+ static bool HasCustomAnimation (css::uno::Reference<css::drawing::XDrawPage> const & rxPage);
+ void SetAccessibilityActiveState (const bool bIsActive);
+ bool IsAccessibilityActive() const { return mbIsAccessibilityActive;}
+
+ void HandleMouseClick (const css::awt::MouseEvent& rEvent);
+ void UpdatePaneTitles();
+
+ /** Request activation or deactivation of (some of) the views according
+ to the given parameters.
+ */
+ void RequestViews (
+ const bool bIsSlideSorterActive,
+ const bool bIsNotesViewActive,
+ const bool bIsHelpViewActive);
+
+ void SetPresentationTime(IPresentationTime* pPresentationTime);
+ IPresentationTime* GetPresentationTime();
+
+ // XConfigurationChangeListener
+
+ virtual void SAL_CALL notifyConfigurationChange (
+ const css::drawing::framework::ConfigurationChangeEvent& rEvent) override;
+
+ // XEventListener
+
+ virtual void SAL_CALL disposing (
+ const css::lang::EventObject& rEvent) override;
+
+ // XFrameActionListener
+
+ virtual void SAL_CALL frameAction (
+ const css::frame::FrameActionEvent& rEvent) override;
+
+ // XKeyListener
+
+ virtual void SAL_CALL keyPressed (const css::awt::KeyEvent& rEvent) override;
+ virtual void SAL_CALL keyReleased (const css::awt::KeyEvent& rEvent) override;
+
+ // XMouseListener
+
+ virtual void SAL_CALL mousePressed (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseReleased (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseEntered (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseExited (const css::awt::MouseEvent& rEvent) override;
+
+private:
+ typedef ::std::map<css::uno::Reference<css::frame::XFrame>,rtl::Reference<PresenterController> > InstanceContainer;
+ static InstanceContainer maInstances;
+
+ css::uno::WeakReference<css::lang::XEventListener> mxScreen;
+ css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+ css::uno::Reference<css::rendering::XSpriteCanvas> mxCanvas;
+ css::uno::Reference<css::frame::XController> mxController;
+ css::uno::Reference<css::drawing::framework::XConfigurationController>
+ mxConfigurationController;
+ css::uno::Reference<css::presentation::XSlideShowController> mxSlideShowController;
+ css::uno::Reference<css::drawing::framework::XResourceId> mxMainPaneId;
+ rtl::Reference<PresenterPaneContainer> mpPaneContainer;
+ sal_Int32 mnCurrentSlideIndex;
+ css::uno::Reference<css::drawing::XDrawPage> mxCurrentSlide;
+ css::uno::Reference<css::drawing::XDrawPage> mxNextSlide;
+ ::rtl::Reference<PresenterWindowManager> mpWindowManager;
+ std::shared_ptr<PresenterTheme> mpTheme;
+ css::uno::Reference<css::awt::XWindow> mxMainWindow;
+ ::rtl::Reference<PresenterPaneBorderPainter> mpPaneBorderPainter;
+ std::shared_ptr<PresenterCanvasHelper> mpCanvasHelper;
+ css::uno::Reference<css::drawing::XPresenterHelper> mxPresenterHelper;
+ std::shared_ptr<PresenterPaintManager> mpPaintManager;
+ sal_Int32 mnPendingSlideNumber;
+ css::uno::Reference<css::util::XURLTransformer> mxUrlTransformer;
+ ::rtl::Reference<PresenterAccessible> mpAccessibleObject;
+ bool mbIsAccessibilityActive;
+ IPresentationTime* mpPresentationTime;
+
+ void GetSlides (const sal_Int32 nOffset);
+ void UpdateViews();
+ void InitializeMainPane (const css::uno::Reference<css::drawing::framework::XPane>& rxPane);
+ void LoadTheme (const css::uno::Reference<css::drawing::framework::XPane>& rxPane);
+ void UpdatePendingSlideNumber (const sal_Int32 nPendingSlideNumber);
+
+ /** This method is called when the user pressed one of the numerical
+ keys. Depending on the modifier, numeric keys switch to another
+ slide (no modifier), or change to another view (Ctrl modifier).
+ @param nKey
+ Numeric value that is printed on the pressed key. For example
+ pressing the key '4' will result in the value 4, not the ASCII
+ code (0x34?).
+ @param nModifiers
+ The modifier bit field as provided by the key up event.
+ */
+ void HandleNumericKeyPress (const sal_Int32 nKey, const sal_Int32 nModifiers);
+};
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterCurrentSlideObserver.cxx b/sdext/source/presenter/PresenterCurrentSlideObserver.cxx
new file mode 100644
index 000000000..0cd33d9c0
--- /dev/null
+++ b/sdext/source/presenter/PresenterCurrentSlideObserver.cxx
@@ -0,0 +1,130 @@
+/* -*- 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 "PresenterCurrentSlideObserver.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sdext::presenter {
+
+//===== PresenterCurrentSlideObserver =========================================
+
+PresenterCurrentSlideObserver::PresenterCurrentSlideObserver (
+ const ::rtl::Reference<PresenterController>& rxPresenterController,
+ const Reference<presentation::XSlideShowController>& rxSlideShowController)
+ : PresenterCurrentSlideObserverInterfaceBase(m_aMutex),
+ mpPresenterController(rxPresenterController),
+ mxSlideShowController(rxSlideShowController)
+{
+ if( mpPresenterController.is() )
+ {
+ mpPresenterController->addEventListener(this);
+ }
+
+ if( mxSlideShowController.is() )
+ {
+ // Listen for events from the slide show controller.
+ mxSlideShowController->addSlideShowListener(static_cast<XSlideShowListener*>(this));
+ }
+}
+
+PresenterCurrentSlideObserver::~PresenterCurrentSlideObserver()
+{
+}
+
+void SAL_CALL PresenterCurrentSlideObserver::disposing()
+{
+ // Disconnect form the slide show controller.
+ if(mxSlideShowController.is())
+ {
+ mxSlideShowController->removeSlideShowListener(static_cast<XSlideShowListener*>(this));
+ mxSlideShowController = nullptr;
+ }
+
+ if (mpPresenterController.is())
+ mpPresenterController->removeEventListener(this);
+}
+
+//----- XSlideShowListener ----------------------------------------------------
+
+void SAL_CALL PresenterCurrentSlideObserver::beginEvent (
+ const Reference<animations::XAnimationNode>&)
+{}
+
+void SAL_CALL PresenterCurrentSlideObserver::endEvent (
+ const Reference<animations::XAnimationNode>&)
+{}
+
+void SAL_CALL PresenterCurrentSlideObserver::repeat (
+ const css::uno::Reference<css::animations::XAnimationNode>&,
+ sal_Int32)
+{}
+
+void SAL_CALL PresenterCurrentSlideObserver::paused()
+{
+}
+
+void SAL_CALL PresenterCurrentSlideObserver::resumed()
+{
+}
+
+void SAL_CALL PresenterCurrentSlideObserver::slideEnded (sal_Bool bReverse)
+{
+ // Determine whether the new current slide (the one after the one that
+ // just ended) is the slide past the last slide in the presentation,
+ // i.e. the one that says something like "click to end presentation...".
+ if (mxSlideShowController.is() && !bReverse)
+ if (mxSlideShowController->getNextSlideIndex() < 0)
+ if( mpPresenterController.is() )
+ mpPresenterController->UpdateCurrentSlide(+1);
+}
+
+void SAL_CALL PresenterCurrentSlideObserver::hyperLinkClicked (const OUString &)
+{
+}
+
+void SAL_CALL PresenterCurrentSlideObserver::slideTransitionStarted()
+{
+ if( mpPresenterController.is() )
+ mpPresenterController->UpdateCurrentSlide(0);
+}
+
+void SAL_CALL PresenterCurrentSlideObserver::slideTransitionEnded()
+{
+}
+
+void SAL_CALL PresenterCurrentSlideObserver::slideAnimationsEnded()
+{
+}
+
+//----- XEventListener --------------------------------------------------------
+
+void SAL_CALL PresenterCurrentSlideObserver::disposing (
+ const lang::EventObject& rEvent)
+{
+ if (rEvent.Source == Reference<XInterface>(static_cast<XWeak*>(mpPresenterController.get())))
+ dispose();
+ else if (rEvent.Source == mxSlideShowController)
+ mxSlideShowController = nullptr;
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterCurrentSlideObserver.hxx b/sdext/source/presenter/PresenterCurrentSlideObserver.hxx
new file mode 100644
index 000000000..786744bfb
--- /dev/null
+++ b/sdext/source/presenter/PresenterCurrentSlideObserver.hxx
@@ -0,0 +1,81 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERCURRENTSLIDEOBSERVER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERCURRENTSLIDEOBSERVER_HXX
+
+#include "PresenterController.hxx"
+#include <com/sun/star/presentation/XSlideShowController.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <rtl/ref.hxx>
+
+namespace sdext::presenter {
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::presentation::XSlideShowListener
+> PresenterCurrentSlideObserverInterfaceBase;
+
+/** Check periodically the slide show controller and the
+ frame::XController whether the current slide has changed. If so,
+ then inform the presenter controller about it.
+
+ Objects of this class have their own lifetime control and destroy
+ themselves when the presenter controller is disposed.
+*/
+class PresenterCurrentSlideObserver
+ : protected ::cppu::BaseMutex,
+ public PresenterCurrentSlideObserverInterfaceBase
+{
+public:
+ PresenterCurrentSlideObserver (
+ const ::rtl::Reference<PresenterController>& rxPresenterController,
+ const css::uno::Reference<css::presentation::XSlideShowController>& rxSlideShowController);
+ virtual ~PresenterCurrentSlideObserver() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ // 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;
+
+ // 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;
+
+ // XEventListener
+ virtual void SAL_CALL disposing (
+ const css::lang::EventObject& rEvent) override;
+
+private:
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ css::uno::Reference<css::presentation::XSlideShowController> mxSlideShowController;
+};
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterFrameworkObserver.cxx b/sdext/source/presenter/PresenterFrameworkObserver.cxx
new file mode 100644
index 000000000..0f56da0b0
--- /dev/null
+++ b/sdext/source/presenter/PresenterFrameworkObserver.cxx
@@ -0,0 +1,109 @@
+/* -*- 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 "PresenterFrameworkObserver.hxx"
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+
+namespace sdext::presenter {
+
+PresenterFrameworkObserver::PresenterFrameworkObserver (
+ const css::uno::Reference<css::drawing::framework::XConfigurationController>&rxController,
+ const Action& rAction)
+ : PresenterFrameworkObserverInterfaceBase(m_aMutex),
+ mxConfigurationController(rxController),
+ maAction(rAction)
+{
+ if ( ! mxConfigurationController.is())
+ throw lang::IllegalArgumentException();
+
+ if (mxConfigurationController->hasPendingRequests())
+ {
+ mxConfigurationController->addConfigurationChangeListener(
+ this,
+ "ConfigurationUpdateEnd",
+ Any());
+ }
+ else
+ {
+ rAction(true);
+ }
+}
+
+PresenterFrameworkObserver::~PresenterFrameworkObserver()
+{
+}
+
+void PresenterFrameworkObserver::RunOnUpdateEnd (
+ const css::uno::Reference<css::drawing::framework::XConfigurationController>&rxController,
+ const Action& rAction)
+{
+ new PresenterFrameworkObserver(
+ rxController,
+ rAction);
+}
+
+void SAL_CALL PresenterFrameworkObserver::disposing()
+{
+ if (maAction)
+ maAction(false);
+ Shutdown();
+}
+
+void PresenterFrameworkObserver::Shutdown()
+{
+ maAction = Action();
+ if (mxConfigurationController != nullptr)
+ {
+ mxConfigurationController->removeConfigurationChangeListener(this);
+ mxConfigurationController = nullptr;
+ }
+}
+
+void SAL_CALL PresenterFrameworkObserver::disposing (const lang::EventObject& rEvent)
+{
+ if ( ! rEvent.Source.is())
+ return;
+
+ if (rEvent.Source == mxConfigurationController)
+ {
+ mxConfigurationController = nullptr;
+ if (maAction)
+ maAction(false);
+ }
+}
+
+void SAL_CALL PresenterFrameworkObserver::notifyConfigurationChange (
+ const ConfigurationChangeEvent& /*rEvent*/)
+{
+ Action aAction(maAction);
+ Shutdown();
+ aAction(true);
+
+ maAction = nullptr;
+ dispose();
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterFrameworkObserver.hxx b/sdext/source/presenter/PresenterFrameworkObserver.hxx
new file mode 100644
index 000000000..bc4b4bda1
--- /dev/null
+++ b/sdext/source/presenter/PresenterFrameworkObserver.hxx
@@ -0,0 +1,81 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERFRAMEWORKOBSERVER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERFRAMEWORKOBSERVER_HXX
+
+#include <com/sun/star/drawing/framework/XConfigurationChangeListener.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+
+#include <functional>
+
+namespace sdext::presenter {
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::drawing::framework::XConfigurationChangeListener
+ > PresenterFrameworkObserverInterfaceBase;
+
+/** Watch the drawing framework for changes and run callbacks when a certain
+ change takes place.
+*/
+class PresenterFrameworkObserver
+ : private ::cppu::BaseMutex,
+ public PresenterFrameworkObserverInterfaceBase
+{
+public:
+ typedef ::std::function<void (bool)> Action;
+
+ PresenterFrameworkObserver(const PresenterFrameworkObserver&) = delete;
+ PresenterFrameworkObserver& operator=(const PresenterFrameworkObserver&) = delete;
+
+ static void RunOnUpdateEnd (
+ const css::uno::Reference<css::drawing::framework::XConfigurationController>&rxController,
+ const Action& rAction);
+
+ virtual void SAL_CALL disposing() override;
+ virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override;
+ virtual void SAL_CALL notifyConfigurationChange (
+ const css::drawing::framework::ConfigurationChangeEvent& rEvent) override;
+
+private:
+ css::uno::Reference<css::drawing::framework::XConfigurationController> mxConfigurationController;
+ Action maAction;
+
+ /** Create a new PresenterFrameworkObserver object.
+ @param rPredicate
+ This functor tests whether the action is to be executed or not.
+ @param rAction
+ The functor to execute when the predicate returns true,
+ e.g. when some resource has been created.
+ */
+ PresenterFrameworkObserver (
+ const css::uno::Reference<css::drawing::framework::XConfigurationController>&rxController,
+ const Action& rAction);
+ virtual ~PresenterFrameworkObserver() override;
+
+ void Shutdown();
+};
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterGeometryHelper.cxx b/sdext/source/presenter/PresenterGeometryHelper.cxx
new file mode 100644
index 000000000..b2ad35c63
--- /dev/null
+++ b/sdext/source/presenter/PresenterGeometryHelper.cxx
@@ -0,0 +1,262 @@
+/* -*- 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 "PresenterGeometryHelper.hxx"
+
+#include <math.h>
+#include <algorithm>
+#include <o3tl/safeint.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+sal_Int32 Right (const awt::Rectangle& rBox)
+{
+ return rBox.X + rBox.Width - 1;
+}
+
+sal_Int32 Bottom (const awt::Rectangle& rBox)
+{
+ return rBox.Y + rBox.Height - 1;
+}
+
+sal_Int32 Width (const sal_Int32 nLeft, const sal_Int32 nRight)
+{
+ return nRight - nLeft + 1;
+}
+
+sal_Int32 Height (const sal_Int32 nTop, const sal_Int32 nBottom)
+{
+ return nBottom - nTop + 1;
+}
+
+} // end of anonymous namespace
+
+namespace sdext::presenter {
+
+sal_Int32 PresenterGeometryHelper::Floor (const double nValue)
+{
+ return sal::static_int_cast<sal_Int32>(floor(nValue));
+}
+
+sal_Int32 PresenterGeometryHelper::Ceil (const double nValue)
+{
+ return sal::static_int_cast<sal_Int32>(ceil(nValue));
+}
+
+sal_Int32 PresenterGeometryHelper::Round (const double nValue)
+{
+ return sal::static_int_cast<sal_Int32>(floor(0.5 + nValue));
+}
+
+awt::Rectangle PresenterGeometryHelper::ConvertRectangle (
+ const geometry::RealRectangle2D& rBox)
+{
+ const sal_Int32 nLeft (Floor(rBox.X1));
+ const sal_Int32 nTop (Floor(rBox.Y1));
+ const sal_Int32 nRight (Ceil(rBox.X2));
+ const sal_Int32 nBottom (Ceil(rBox.Y2));
+ return awt::Rectangle (nLeft,nTop,nRight-nLeft,nBottom-nTop);
+}
+
+awt::Rectangle PresenterGeometryHelper::ConvertRectangleWithConstantSize (
+ const geometry::RealRectangle2D& rBox)
+{
+ return awt::Rectangle (
+ Round(rBox.X1),
+ Round(rBox.Y1),
+ Round(rBox.X2 - rBox.X1),
+ Round(rBox.Y2 - rBox.Y1));
+}
+
+geometry::RealRectangle2D PresenterGeometryHelper::ConvertRectangle (
+ const css::awt::Rectangle& rBox)
+{
+ return geometry::RealRectangle2D(
+ rBox.X,
+ rBox.Y,
+ rBox.X + rBox.Width,
+ rBox.Y + rBox.Height);
+}
+
+awt::Rectangle PresenterGeometryHelper::TranslateRectangle (
+ const css::awt::Rectangle& rBox,
+ const sal_Int32 nXOffset,
+ const sal_Int32 nYOffset)
+{
+ return awt::Rectangle(rBox.X + nXOffset, rBox.Y + nYOffset, rBox.Width, rBox.Height);
+}
+
+awt::Rectangle PresenterGeometryHelper::Intersection (
+ const css::awt::Rectangle& rBox1,
+ const css::awt::Rectangle& rBox2)
+{
+ const sal_Int32 nLeft (::std::max(rBox1.X, rBox2.X));
+ const sal_Int32 nTop (::std::max(rBox1.Y, rBox2.Y));
+ const sal_Int32 nRight (::std::min(Right(rBox1), Right(rBox2)));
+ const sal_Int32 nBottom (::std::min(Bottom(rBox1), Bottom(rBox2)));
+ if (nLeft >= nRight || nTop >= nBottom)
+ return awt::Rectangle();
+ else
+ return awt::Rectangle(nLeft,nTop, Width(nLeft,nRight), Height(nTop,nBottom));
+}
+
+geometry::RealRectangle2D PresenterGeometryHelper::Intersection (
+ const geometry::RealRectangle2D& rBox1,
+ const geometry::RealRectangle2D& rBox2)
+{
+ const double nLeft (::std::max(rBox1.X1, rBox2.X1));
+ const double nTop (::std::max(rBox1.Y1, rBox2.Y1));
+ const double nRight (::std::min(rBox1.X2, rBox2.X2));
+ const double nBottom (::std::min(rBox1.Y2, rBox2.Y2));
+ if (nLeft >= nRight || nTop >= nBottom)
+ return geometry::RealRectangle2D(0,0,0,0);
+ else
+ return geometry::RealRectangle2D(nLeft,nTop, nRight, nBottom);
+}
+
+bool PresenterGeometryHelper::IsInside (
+ const css::geometry::RealRectangle2D& rBox,
+ const css::geometry::RealPoint2D& rPoint)
+{
+ return rBox.X1 <= rPoint.X
+ && rBox.Y1 <= rPoint.Y
+ && rBox.X2 >= rPoint.X
+ && rBox.Y2 >= rPoint.Y;
+}
+
+bool PresenterGeometryHelper::IsInside (
+ const css::awt::Rectangle& rBox1,
+ const css::awt::Rectangle& rBox2)
+{
+ return rBox1.X >= rBox2.X
+ && rBox1.Y >= rBox2.Y
+ && rBox1.X+rBox1.Width <= rBox2.X+rBox2.Width
+ && rBox1.Y+rBox1.Height <= rBox2.Y+rBox2.Height;
+}
+
+geometry::RealRectangle2D PresenterGeometryHelper::Union (
+ const geometry::RealRectangle2D& rBox1,
+ const geometry::RealRectangle2D& rBox2)
+{
+ const double nLeft (::std::min(rBox1.X1, rBox2.X1));
+ const double nTop (::std::min(rBox1.Y1, rBox2.Y1));
+ const double nRight (::std::max(rBox1.X2, rBox2.X2));
+ const double nBottom (::std::max(rBox1.Y2, rBox2.Y2));
+ if (nLeft >= nRight || nTop >= nBottom)
+ return geometry::RealRectangle2D(0,0,0,0);
+ else
+ return geometry::RealRectangle2D(nLeft,nTop, nRight, nBottom);
+}
+
+bool PresenterGeometryHelper::AreRectanglesDisjoint (
+ const css::awt::Rectangle& rBox1,
+ const css::awt::Rectangle& rBox2)
+{
+ return rBox1.X+rBox1.Width <= rBox2.X
+ || rBox1.Y+rBox1.Height <= rBox2.Y
+ || rBox1.X >= rBox2.X+rBox2.Width
+ || rBox1.Y >= rBox2.Y+rBox2.Height;
+}
+
+Reference<rendering::XPolyPolygon2D> PresenterGeometryHelper::CreatePolygon(
+ const awt::Rectangle& rBox,
+ const Reference<rendering::XGraphicDevice>& rxDevice)
+{
+ if ( ! rxDevice.is())
+ return nullptr;
+
+ Sequence<Sequence<geometry::RealPoint2D> > aPoints
+ {
+ {
+ { o3tl::narrowing<double>(rBox.X), o3tl::narrowing<double>(rBox.Y) },
+ { o3tl::narrowing<double>(rBox.X), o3tl::narrowing<double>(rBox.Y+rBox.Height) },
+ { o3tl::narrowing<double>(rBox.X+rBox.Width), o3tl::narrowing<double>(rBox.Y+rBox.Height) },
+ { o3tl::narrowing<double>(rBox.X+rBox.Width), o3tl::narrowing<double>(rBox.Y) }
+ }
+ };
+ Reference<rendering::XLinePolyPolygon2D> xPolygon (
+ rxDevice->createCompatibleLinePolyPolygon(aPoints));
+ if (xPolygon.is())
+ xPolygon->setClosed(0, true);
+
+ return xPolygon;
+}
+
+Reference<rendering::XPolyPolygon2D> PresenterGeometryHelper::CreatePolygon(
+ const geometry::RealRectangle2D& rBox,
+ const Reference<rendering::XGraphicDevice>& rxDevice)
+{
+ if ( ! rxDevice.is())
+ return nullptr;
+
+ Sequence<Sequence<geometry::RealPoint2D> > aPoints
+ {
+ {
+ { rBox.X1, rBox.Y1 },
+ { rBox.X1, rBox.Y2 },
+ { rBox.X2, rBox.Y2 },
+ { rBox.X2, rBox.Y1 }
+ }
+ };
+ Reference<rendering::XLinePolyPolygon2D> xPolygon (
+ rxDevice->createCompatibleLinePolyPolygon(aPoints));
+ if (xPolygon.is())
+ xPolygon->setClosed(0, true);
+
+ return xPolygon;
+}
+
+Reference<rendering::XPolyPolygon2D> PresenterGeometryHelper::CreatePolygon(
+ const ::std::vector<css::awt::Rectangle>& rBoxes,
+ const Reference<rendering::XGraphicDevice>& rxDevice)
+{
+ if ( ! rxDevice.is())
+ return nullptr;
+
+ const sal_Int32 nCount (rBoxes.size());
+ Sequence<Sequence<geometry::RealPoint2D> > aPoints(nCount);
+ auto aPointsRange = asNonConstRange(aPoints);
+ for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
+ {
+ const awt::Rectangle& rBox (rBoxes[nIndex]);
+ aPointsRange[nIndex] = Sequence<geometry::RealPoint2D>
+ {
+ { o3tl::narrowing<double>(rBox.X), o3tl::narrowing<double>(rBox.Y) },
+ { o3tl::narrowing<double>(rBox.X), o3tl::narrowing<double>(rBox.Y+rBox.Height) },
+ { o3tl::narrowing<double>(rBox.X+rBox.Width), o3tl::narrowing<double>(rBox.Y+rBox.Height) },
+ { o3tl::narrowing<double>(rBox.X+rBox.Width), o3tl::narrowing<double>(rBox.Y) }
+ };
+ }
+
+ Reference<rendering::XLinePolyPolygon2D> xPolygon (
+ rxDevice->createCompatibleLinePolyPolygon(aPoints));
+ if (xPolygon.is())
+ for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
+ xPolygon->setClosed(nIndex, true);
+
+ return xPolygon;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterGeometryHelper.hxx b/sdext/source/presenter/PresenterGeometryHelper.hxx
new file mode 100644
index 000000000..c2f55757e
--- /dev/null
+++ b/sdext/source/presenter/PresenterGeometryHelper.hxx
@@ -0,0 +1,117 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERGEOMETRYHELPER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERGEOMETRYHELPER_HXX
+
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/rendering/XGraphicDevice.hpp>
+#include <com/sun/star/rendering/XPolyPolygon2D.hpp>
+#include <com/sun/star/geometry/RealRectangle2D.hpp>
+#include <vector>
+
+namespace sdext::presenter {
+
+/** Collection of geometry related convenience functions.
+*/
+class PresenterGeometryHelper
+{
+public:
+ static sal_Int32 Round (const double nValue);
+ static sal_Int32 Floor (const double nValue);
+ static sal_Int32 Ceil (const double nValue);
+
+ /** Return the bounding box with integer coordinates of the given
+ rectangle. Note that due to different rounding of the left/top and
+ the right/bottom border the width of the resulting rectangle may
+ differ for different positions but constant width and height.
+ */
+ static css::awt::Rectangle ConvertRectangle (
+ const css::geometry::RealRectangle2D& rBox);
+
+ /** Convert the given rectangle to integer coordinates so that width and
+ height remain constant when only the position changes.
+ */
+ static css::awt::Rectangle ConvertRectangleWithConstantSize (
+ const css::geometry::RealRectangle2D& rBox);
+
+ static css::geometry::RealRectangle2D ConvertRectangle (
+ const css::awt::Rectangle& rBox);
+
+ // static css::awt::Size ConvertSize (
+ // const css::geometry::RealSize2D& rSize);
+
+ static css::awt::Rectangle TranslateRectangle (
+ const css::awt::Rectangle& rBox,
+ const sal_Int32 nXOffset,
+ const sal_Int32 nYOffset);
+
+ static css::awt::Rectangle Intersection (
+ const css::awt::Rectangle& rBox1,
+ const css::awt::Rectangle& rBox2);
+
+ static css::geometry::RealRectangle2D Intersection (
+ const css::geometry::RealRectangle2D& rBox1,
+ const css::geometry::RealRectangle2D& rBox2);
+
+ static bool IsInside (
+ const css::geometry::RealRectangle2D& rBox,
+ const css::geometry::RealPoint2D& rPoint);
+
+ /** Return whether rBox1 is completely inside rBox2.
+ */
+ static bool IsInside (
+ const css::awt::Rectangle& rBox1,
+ const css::awt::Rectangle& rBox2);
+
+ static css::geometry::RealRectangle2D Union (
+ const css::geometry::RealRectangle2D& rBox1,
+ const css::geometry::RealRectangle2D& rBox2);
+
+ static bool AreRectanglesDisjoint (
+ const css::awt::Rectangle& rBox1,
+ const css::awt::Rectangle& rBox2);
+
+ static css::uno::Reference<css::rendering::XPolyPolygon2D> CreatePolygon(
+ const css::awt::Rectangle& rBox,
+ const css::uno::Reference<css::rendering::XGraphicDevice>& rxDevice);
+
+ static css::uno::Reference<css::rendering::XPolyPolygon2D> CreatePolygon(
+ const css::geometry::RealRectangle2D& rBox,
+ const css::uno::Reference<css::rendering::XGraphicDevice>& rxDevice);
+
+ static css::uno::Reference<css::rendering::XPolyPolygon2D> CreatePolygon(
+ const ::std::vector<css::awt::Rectangle>& rBoxes,
+ const css::uno::Reference<css::rendering::XGraphicDevice>& rxDevice);
+
+ /** Create a polygon for a rounded rectangle.
+ */
+ /* static css::uno::Reference<css::rendering::XPolyPolygon2D> CreatePolygon(
+ const css::awt::Rectangle& rBox,
+ const double nRadius,
+ const css::uno::Reference<css::rendering::XGraphicDevice>&
+ rxDevice);
+ */
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterHelpView.cxx b/sdext/source/presenter/PresenterHelpView.cxx
new file mode 100644
index 000000000..74adeedd1
--- /dev/null
+++ b/sdext/source/presenter/PresenterHelpView.cxx
@@ -0,0 +1,747 @@
+/* -*- 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 <vcl/settings.hxx>
+#include "PresenterHelpView.hxx"
+#include "PresenterButton.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterGeometryHelper.hxx"
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/TextDirection.hpp>
+#include <com/sun/star/util/Color.hpp>
+#include <algorithm>
+#include <numeric>
+#include <string_view>
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+using ::std::vector;
+
+namespace sdext::presenter {
+
+namespace {
+ const sal_Int32 gnHorizontalGap (20);
+ const sal_Int32 gnVerticalBorder (30);
+ const sal_Int32 gnVerticalButtonPadding (12);
+
+ class LineDescriptor
+ {
+ public:
+ LineDescriptor();
+ void AddPart (
+ std::u16string_view rsLine,
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont);
+ bool IsEmpty() const;
+
+ OUString msLine;
+ geometry::RealSize2D maSize;
+ double mnVerticalOffset;
+
+ void CalculateSize (const css::uno::Reference<css::rendering::XCanvasFont>& rxFont);
+ };
+
+ class LineDescriptorList
+ {
+ public:
+ LineDescriptorList (
+ const OUString& rsText,
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const sal_Int32 nMaximalWidth);
+
+ void Update (
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const sal_Int32 nMaximalWidth);
+
+ double Paint(
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const geometry::RealRectangle2D& rBBox,
+ const bool bFlushLeft,
+ const rendering::ViewState& rViewState,
+ rendering::RenderState& rRenderState,
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont) const;
+ double GetHeight() const;
+
+ private:
+ const OUString msText;
+ std::shared_ptr<vector<LineDescriptor> > mpLineDescriptors;
+
+ static void SplitText (const OUString& rsText, vector<OUString>& rTextParts);
+ void FormatText (
+ const vector<OUString>& rTextParts,
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const sal_Int32 nMaximalWidth);
+ };
+
+ class Block
+ {
+ public:
+ Block (
+ const OUString& rsLeftText,
+ const OUString& rsRightText,
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const sal_Int32 nMaximalWidth);
+ Block(const Block&) = delete;
+ Block& operator=(const Block&) = delete;
+ void Update (
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const sal_Int32 nMaximalWidth);
+
+ LineDescriptorList maLeft;
+ LineDescriptorList maRight;
+ };
+} // end of anonymous namespace
+
+class PresenterHelpView::TextContainer : public vector<std::shared_ptr<Block> >
+{
+};
+
+PresenterHelpView::PresenterHelpView (
+ const Reference<uno::XComponentContext>& rxContext,
+ const Reference<XResourceId>& rxViewId,
+ const Reference<frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterHelpViewInterfaceBase(m_aMutex),
+ mxComponentContext(rxContext),
+ mxViewId(rxViewId),
+ mpPresenterController(rpPresenterController),
+ mnSeparatorY(0),
+ mnMaximalWidth(0)
+{
+ try
+ {
+ // Get the content window via the pane anchor.
+ Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW);
+ Reference<XConfigurationController> xCC (
+ xCM->getConfigurationController(), UNO_SET_THROW);
+ mxPane.set(xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW);
+
+ mxWindow = mxPane->getWindow();
+ ProvideCanvas();
+
+ mxWindow->addWindowListener(this);
+ mxWindow->addPaintListener(this);
+ Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY);
+ if (xPeer.is())
+ xPeer->setBackground(util::Color(0xff000000));
+ mxWindow->setVisible(true);
+
+ if (mpPresenterController.is())
+ {
+ mpFont = mpPresenterController->GetViewFont(mxViewId->getResourceURL());
+ if (mpFont)
+ {
+ mpFont->PrepareFont(mxCanvas);
+ }
+ }
+
+ // Create the close button.
+ mpCloseButton = PresenterButton::Create(
+ mxComponentContext,
+ mpPresenterController,
+ mpPresenterController->GetTheme(),
+ mxWindow,
+ mxCanvas,
+ "HelpViewCloser");
+
+ ReadHelpStrings();
+ Resize();
+ }
+ catch (RuntimeException&)
+ {
+ mxViewId = nullptr;
+ mxWindow = nullptr;
+ throw;
+ }
+}
+
+PresenterHelpView::~PresenterHelpView()
+{
+}
+
+void SAL_CALL PresenterHelpView::disposing()
+{
+ mxViewId = nullptr;
+
+ if (mpCloseButton.is())
+ {
+ Reference<lang::XComponent> xComponent = mpCloseButton;
+ mpCloseButton = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ if (mxWindow.is())
+ {
+ mxWindow->removeWindowListener(this);
+ mxWindow->removePaintListener(this);
+ }
+}
+
+//----- lang::XEventListener --------------------------------------------------
+
+void SAL_CALL PresenterHelpView::disposing (const lang::EventObject& rEventObject)
+{
+ if (rEventObject.Source == mxCanvas)
+ {
+ mxCanvas = nullptr;
+ }
+ else if (rEventObject.Source == mxWindow)
+ {
+ mxWindow = nullptr;
+ dispose();
+ }
+}
+
+//----- XWindowListener -------------------------------------------------------
+
+void SAL_CALL PresenterHelpView::windowResized (const awt::WindowEvent&)
+{
+ ThrowIfDisposed();
+ Resize();
+}
+
+void SAL_CALL PresenterHelpView::windowMoved (const awt::WindowEvent&)
+{
+ ThrowIfDisposed();
+}
+
+void SAL_CALL PresenterHelpView::windowShown (const lang::EventObject&)
+{
+ ThrowIfDisposed();
+ Resize();
+}
+
+void SAL_CALL PresenterHelpView::windowHidden (const lang::EventObject&)
+{
+ ThrowIfDisposed();
+}
+
+//----- XPaintListener --------------------------------------------------------
+
+void SAL_CALL PresenterHelpView::windowPaint (const css::awt::PaintEvent& rEvent)
+{
+ Paint(rEvent.UpdateRect);
+}
+
+void PresenterHelpView::Paint (const awt::Rectangle& rUpdateBox)
+{
+ ProvideCanvas();
+ if ( ! mxCanvas.is())
+ return;
+
+ // Clear background.
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ mpPresenterController->GetCanvasHelper()->Paint(
+ mpPresenterController->GetViewBackground(mxViewId->getResourceURL()),
+ mxCanvas,
+ rUpdateBox,
+ awt::Rectangle(0,0,aWindowBox.Width,aWindowBox.Height),
+ awt::Rectangle());
+
+ // Paint vertical divider.
+
+ rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ PresenterGeometryHelper::CreatePolygon(rUpdateBox, mxCanvas->getDevice()));
+
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
+
+ mxCanvas->drawLine(
+ geometry::RealPoint2D((aWindowBox.Width/2.0), gnVerticalBorder),
+ geometry::RealPoint2D((aWindowBox.Width/2.0), mnSeparatorY - gnVerticalBorder),
+ aViewState,
+ aRenderState);
+
+ // Paint the horizontal separator.
+ mxCanvas->drawLine(
+ geometry::RealPoint2D(0, mnSeparatorY),
+ geometry::RealPoint2D(aWindowBox.Width, mnSeparatorY),
+ aViewState,
+ aRenderState);
+
+ // Paint text.
+ double nY (gnVerticalBorder);
+ for (const auto& rxBlock : *mpTextContainer)
+ {
+ sal_Int32 LeftX1 = gnHorizontalGap;
+ sal_Int32 LeftX2 = aWindowBox.Width/2 - gnHorizontalGap;
+ sal_Int32 RightX1 = aWindowBox.Width/2 + gnHorizontalGap;
+ sal_Int32 RightX2 = aWindowBox.Width - gnHorizontalGap;
+ /* check whether RTL interface or not
+ then replace the windowbox position */
+ if(AllSettings::GetLayoutRTL())
+ {
+ LeftX1 = aWindowBox.Width/2 + gnHorizontalGap;
+ LeftX2 = aWindowBox.Width - gnHorizontalGap;
+ RightX1 = gnHorizontalGap;
+ RightX2 = aWindowBox.Width/2 - gnHorizontalGap;
+ }
+ const double nLeftHeight (
+ rxBlock->maLeft.Paint(mxCanvas,
+ geometry::RealRectangle2D(
+ LeftX1,
+ nY,
+ LeftX2,
+ aWindowBox.Height - gnVerticalBorder),
+ false,
+ aViewState,
+ aRenderState,
+ mpFont->mxFont));
+ const double nRightHeight (
+ rxBlock->maRight.Paint(mxCanvas,
+ geometry::RealRectangle2D(
+ RightX1,
+ nY,
+ RightX2,
+ aWindowBox.Height - gnVerticalBorder),
+ true,
+ aViewState,
+ aRenderState,
+ mpFont->mxFont));
+
+ nY += ::std::max(nLeftHeight,nRightHeight);
+ }
+
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(false);
+}
+
+void PresenterHelpView::ReadHelpStrings()
+{
+ mpTextContainer.reset(new TextContainer);
+ PresenterConfigurationAccess aConfiguration (
+ mxComponentContext,
+ "/org.openoffice.Office.PresenterScreen/",
+ PresenterConfigurationAccess::READ_ONLY);
+ Reference<container::XNameAccess> xStrings (
+ aConfiguration.GetConfigurationNode("PresenterScreenSettings/HelpView/HelpStrings"),
+ UNO_QUERY);
+ PresenterConfigurationAccess::ForAll(
+ xStrings,
+ [this](OUString const&, uno::Reference<beans::XPropertySet> const& xProps)
+ {
+ return this->ProcessString(xProps);
+ });
+}
+
+void PresenterHelpView::ProcessString (
+ const Reference<beans::XPropertySet>& rsProperties)
+{
+ if ( ! rsProperties.is())
+ return;
+
+ OUString sLeftText;
+ PresenterConfigurationAccess::GetProperty(rsProperties, "Left") >>= sLeftText;
+ OUString sRightText;
+ PresenterConfigurationAccess::GetProperty(rsProperties, "Right") >>= sRightText;
+ mpTextContainer->push_back(
+ std::make_shared<Block>(
+ sLeftText, sRightText, mpFont->mxFont, mnMaximalWidth));
+}
+
+void PresenterHelpView::CheckFontSize()
+{
+ if (!mpFont)
+ return;
+
+ sal_Int32 nBestSize (6);
+
+ // Scaling down and then reformatting can cause the text to be too large
+ // still. So do this again and again until the text size is
+ // small enough. Restrict the number of loops.
+ for (int nLoopCount=0; nLoopCount<5; ++nLoopCount)
+ {
+ double nY = std::accumulate(mpTextContainer->begin(), mpTextContainer->end(), double(0),
+ [](const double& sum, const std::shared_ptr<Block>& rxBlock) {
+ return sum + std::max(
+ rxBlock->maLeft.GetHeight(),
+ rxBlock->maRight.GetHeight());
+ });
+
+ const double nHeightDifference (nY - (mnSeparatorY-gnVerticalBorder));
+ if (nHeightDifference <= 0 && nHeightDifference > -50)
+ {
+ // We have found a good font size that is large and leaves not
+ // too much space below the help text.
+ return;
+ }
+
+ // Use a simple linear transformation to calculate initial guess of
+ // a size that lets all help text be shown inside the window.
+ const double nScale (double(mnSeparatorY-gnVerticalBorder) / nY);
+ if (nScale > 1.0 && nScale < 1.05)
+ break;
+
+ sal_Int32 nFontSizeGuess (sal_Int32(mpFont->mnSize * nScale));
+ if (nHeightDifference<=0 && mpFont->mnSize>nBestSize)
+ nBestSize = mpFont->mnSize;
+ mpFont->mnSize = nFontSizeGuess;
+ mpFont->mxFont = nullptr;
+ mpFont->PrepareFont(mxCanvas);
+
+ // Reformat blocks.
+ for (auto& rxBlock : *mpTextContainer)
+ rxBlock->Update(mpFont->mxFont, mnMaximalWidth);
+ }
+
+ if (nBestSize != mpFont->mnSize)
+ {
+ mpFont->mnSize = nBestSize;
+ mpFont->mxFont = nullptr;
+ mpFont->PrepareFont(mxCanvas);
+
+ // Reformat blocks.
+ for (auto& rxBlock : *mpTextContainer)
+ {
+ rxBlock->Update(mpFont->mxFont, mnMaximalWidth);
+ }
+ }
+}
+
+//----- XResourceId -----------------------------------------------------------
+
+Reference<XResourceId> SAL_CALL PresenterHelpView::getResourceId()
+{
+ ThrowIfDisposed();
+ return mxViewId;
+}
+
+sal_Bool SAL_CALL PresenterHelpView::isAnchorOnly()
+{
+ return false;
+}
+
+
+void PresenterHelpView::ProvideCanvas()
+{
+ if ( ! mxCanvas.is() && mxPane.is())
+ {
+ mxCanvas = mxPane->getCanvas();
+ if ( ! mxCanvas.is())
+ return;
+ Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->addEventListener(static_cast<awt::XPaintListener*>(this));
+
+ if (mpCloseButton.is())
+ mpCloseButton->SetCanvas(mxCanvas, mxWindow);
+ }
+}
+
+void PresenterHelpView::Resize()
+{
+ if (!(mpCloseButton && mxWindow.is()))
+ return;
+
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ mnMaximalWidth = (mxWindow->getPosSize().Width - 4*gnHorizontalGap) / 2;
+
+ // Place vertical separator.
+ mnSeparatorY = aWindowBox.Height
+ - mpCloseButton->GetSize().Height - gnVerticalButtonPadding;
+
+ mpCloseButton->SetCenter(geometry::RealPoint2D(
+ aWindowBox.Width/2.0,
+ aWindowBox.Height - mpCloseButton->GetSize().Height/2.0));
+
+ CheckFontSize();
+}
+
+void PresenterHelpView::ThrowIfDisposed()
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterHelpView has been already disposed",
+ static_cast<uno::XWeak*>(this));
+ }
+}
+
+//===== LineDescriptor =========================================================
+
+namespace {
+
+LineDescriptor::LineDescriptor()
+ : maSize(0,0),
+ mnVerticalOffset(0)
+{
+}
+
+void LineDescriptor::AddPart (
+ std::u16string_view rsLine,
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont)
+{
+ msLine += rsLine;
+
+ CalculateSize(rxFont);
+}
+
+bool LineDescriptor::IsEmpty() const
+{
+ return msLine.isEmpty();
+}
+
+void LineDescriptor::CalculateSize (
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont)
+{
+ OSL_ASSERT(rxFont.is());
+
+ rendering::StringContext aContext (msLine, 0, msLine.getLength());
+ Reference<rendering::XTextLayout> xLayout (
+ rxFont->createTextLayout(aContext, rendering::TextDirection::WEAK_LEFT_TO_RIGHT, 0));
+ const geometry::RealRectangle2D aTextBBox (xLayout->queryTextBounds());
+ maSize = css::geometry::RealSize2D(aTextBBox.X2 - aTextBBox.X1, aTextBBox.Y2 - aTextBBox.Y1);
+ mnVerticalOffset = aTextBBox.Y2;
+}
+
+} // end of anonymous namespace
+
+//===== LineDescriptorList ====================================================
+
+namespace {
+
+LineDescriptorList::LineDescriptorList (
+ const OUString& rsText,
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const sal_Int32 nMaximalWidth)
+ : msText(rsText)
+{
+ Update(rxFont, nMaximalWidth);
+}
+
+double LineDescriptorList::Paint(
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const geometry::RealRectangle2D& rBBox,
+ const bool bFlushLeft,
+ const rendering::ViewState& rViewState,
+ rendering::RenderState& rRenderState,
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont) const
+{
+ if ( ! rxCanvas.is())
+ return 0;
+
+ double nY (rBBox.Y1);
+ for (const auto& rLine : *mpLineDescriptors)
+ {
+ double nX;
+ /// check whether RTL interface or not
+ if(!AllSettings::GetLayoutRTL())
+ {
+ nX = rBBox.X1;
+ if ( ! bFlushLeft)
+ nX = rBBox.X2 - rLine.maSize.Width;
+ }
+ else
+ {
+ nX=rBBox.X2 - rLine.maSize.Width;
+ if ( ! bFlushLeft)
+ nX = rBBox.X1;
+ }
+ rRenderState.AffineTransform.m02 = nX;
+ rRenderState.AffineTransform.m12 = nY + rLine.maSize.Height - rLine.mnVerticalOffset;
+
+ const rendering::StringContext aContext (rLine.msLine, 0, rLine.msLine.getLength());
+ Reference<rendering::XTextLayout> xLayout (
+ rxFont->createTextLayout(aContext, rendering::TextDirection::WEAK_LEFT_TO_RIGHT, 0));
+ rxCanvas->drawTextLayout (
+ xLayout,
+ rViewState,
+ rRenderState);
+
+ nY += rLine.maSize.Height * 1.2;
+ }
+
+ return nY - rBBox.Y1;
+}
+
+double LineDescriptorList::GetHeight() const
+{
+ return std::accumulate(mpLineDescriptors->begin(), mpLineDescriptors->end(), double(0),
+ [](const double& nHeight, const LineDescriptor& rLine) {
+ return nHeight + rLine.maSize.Height * 1.2;
+ });
+}
+
+void LineDescriptorList::Update (
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const sal_Int32 nMaximalWidth)
+{
+ vector<OUString> aTextParts;
+ SplitText(msText, aTextParts);
+ FormatText(aTextParts, rxFont, nMaximalWidth);
+}
+
+void LineDescriptorList::SplitText (
+ const OUString& rsText,
+ vector<OUString>& rTextParts)
+{
+ const char cQuote ('\'');
+ const char cSeparator (',');
+
+ sal_Int32 nIndex (0);
+ sal_Int32 nStart (0);
+ sal_Int32 nLength (rsText.getLength());
+ bool bIsQuoted (false);
+ while (nIndex < nLength)
+ {
+ const sal_Int32 nQuoteIndex (rsText.indexOf(cQuote, nIndex));
+ const sal_Int32 nSeparatorIndex (rsText.indexOf(cSeparator, nIndex));
+ if (nQuoteIndex>=0 && (nSeparatorIndex==-1 || nQuoteIndex<nSeparatorIndex))
+ {
+ bIsQuoted = !bIsQuoted;
+ nIndex = nQuoteIndex+1;
+ continue;
+ }
+
+ const sal_Int32 nNextIndex = nSeparatorIndex;
+ if (nNextIndex < 0)
+ {
+ break;
+ }
+ else if ( ! bIsQuoted)
+ {
+ rTextParts.push_back(rsText.copy(nStart, nNextIndex-nStart));
+ nStart = nNextIndex + 1;
+ }
+ nIndex = nNextIndex+1;
+ }
+ if (nStart < nLength)
+ rTextParts.push_back(rsText.copy(nStart, nLength-nStart));
+}
+
+void LineDescriptorList::FormatText (
+ const vector<OUString>& rTextParts,
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const sal_Int32 nMaximalWidth)
+{
+ LineDescriptor aLineDescriptor;
+
+ mpLineDescriptors = std::make_shared<vector<LineDescriptor>>();
+
+ vector<OUString>::const_iterator iPart (rTextParts.begin());
+ vector<OUString>::const_iterator iEnd (rTextParts.end());
+ while (iPart!=iEnd)
+ {
+ if (aLineDescriptor.IsEmpty())
+ {
+ // Avoid empty lines.
+ if (PresenterCanvasHelper::GetTextSize(
+ rxFont, *iPart).Width > nMaximalWidth)
+ {
+ const char cSpace (' ');
+
+ sal_Int32 nIndex (0);
+ sal_Int32 nStart (0);
+ sal_Int32 nLength (iPart->getLength());
+ while (nIndex < nLength)
+ {
+ sal_Int32 nSpaceIndex (iPart->indexOf(cSpace, nIndex));
+ while (nSpaceIndex >= 0 && PresenterCanvasHelper::GetTextSize(
+ rxFont, iPart->copy(nStart, nSpaceIndex-nStart)).Width <= nMaximalWidth)
+ {
+ nIndex = nSpaceIndex;
+ nSpaceIndex = iPart->indexOf(cSpace, nIndex+1);
+ }
+
+ if (nSpaceIndex < 0 && PresenterCanvasHelper::GetTextSize(
+ rxFont, iPart->copy(nStart, nLength-nStart)).Width <= nMaximalWidth)
+ {
+ nIndex = nLength;
+ }
+
+ if (nIndex == nStart)
+ {
+ nIndex = nLength;
+ }
+
+ aLineDescriptor.AddPart(iPart->subView(nStart, nIndex-nStart), rxFont);
+ if (nIndex != nLength)
+ {
+ mpLineDescriptors->push_back(aLineDescriptor);
+ aLineDescriptor = LineDescriptor();
+ }
+ nStart = nIndex;
+ }
+ }
+ else
+ {
+ aLineDescriptor.AddPart(*iPart, rxFont);
+ }
+ }
+ else if (PresenterCanvasHelper::GetTextSize(
+ rxFont, aLineDescriptor.msLine+", "+*iPart).Width > nMaximalWidth)
+ {
+ aLineDescriptor.AddPart(u",", rxFont);
+ mpLineDescriptors->push_back(aLineDescriptor);
+ aLineDescriptor = LineDescriptor();
+ continue;
+ }
+ else
+ {
+ aLineDescriptor.AddPart(OUStringConcatenation(", "+*iPart), rxFont);
+ }
+ ++iPart;
+ }
+ if ( ! aLineDescriptor.IsEmpty())
+ {
+ mpLineDescriptors->push_back(aLineDescriptor);
+ }
+}
+
+} // end of anonymous namespace
+
+//===== Block =================================================================
+
+namespace {
+
+Block::Block (
+ const OUString& rsLeftText,
+ const OUString& rsRightText,
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const sal_Int32 nMaximalWidth)
+ : maLeft(rsLeftText, rxFont, nMaximalWidth),
+ maRight(rsRightText, rxFont, nMaximalWidth)
+{
+}
+
+void Block::Update (
+ const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
+ const sal_Int32 nMaximalWidth)
+{
+ maLeft.Update(rxFont, nMaximalWidth);
+ maRight.Update(rxFont, nMaximalWidth);
+}
+
+} // end of anonymous namespace
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterHelpView.hxx b/sdext/source/presenter/PresenterHelpView.hxx
new file mode 100644
index 000000000..58f629a36
--- /dev/null
+++ b/sdext/source/presenter/PresenterHelpView.hxx
@@ -0,0 +1,121 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERHELPVIEW_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERHELPVIEW_HXX
+
+#include "PresenterController.hxx"
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/awt/XPaintListener.hpp>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/drawing/framework/XView.hpp>
+#include <com/sun/star/drawing/framework/XResourceId.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <memory>
+
+namespace sdext::presenter {
+
+class PresenterButton;
+
+typedef cppu::WeakComponentImplHelper<
+ css::drawing::framework::XView,
+ css::awt::XWindowListener,
+ css::awt::XPaintListener
+ > PresenterHelpViewInterfaceBase;
+
+/** Show help text that describes the defined keys.
+*/
+class PresenterHelpView
+ : private ::cppu::BaseMutex,
+ public PresenterHelpViewInterfaceBase
+{
+public:
+ explicit PresenterHelpView (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterHelpView() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ // lang::XEventListener
+
+ virtual void SAL_CALL
+ disposing (const css::lang::EventObject& rEventObject) override;
+
+ // XWindowListener
+
+ virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override;
+
+ // XPaintListener
+
+ virtual void SAL_CALL windowPaint (const css::awt::PaintEvent& rEvent) override;
+
+ // XResourceId
+
+ virtual css::uno::Reference<css::drawing::framework::XResourceId> SAL_CALL getResourceId() override;
+
+ virtual sal_Bool SAL_CALL isAnchorOnly() override;
+
+private:
+ class TextContainer;
+
+ css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+ css::uno::Reference<css::drawing::framework::XResourceId> mxViewId;
+ css::uno::Reference<css::drawing::framework::XPane> mxPane;
+ css::uno::Reference<css::awt::XWindow> mxWindow;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ PresenterTheme::SharedFontDescriptor mpFont;
+ std::unique_ptr<TextContainer> mpTextContainer;
+ ::rtl::Reference<PresenterButton> mpCloseButton;
+ sal_Int32 mnSeparatorY;
+ sal_Int32 mnMaximalWidth;
+
+ void ProvideCanvas();
+ void Resize();
+ void Paint (const css::awt::Rectangle& rRedrawArea);
+ void ReadHelpStrings();
+ void ProcessString (
+ const css::uno::Reference<css::beans::XPropertySet>& rsProperties);
+
+ /** Find a font size, so that all text can be displayed at the same
+ time.
+ */
+ void CheckFontSize();
+
+ /** @throws css::lang::DisposedException when the object has already been
+ disposed.
+ */
+ void ThrowIfDisposed();
+};
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterHelper.cxx b/sdext/source/presenter/PresenterHelper.cxx
new file mode 100644
index 000000000..76bec0ece
--- /dev/null
+++ b/sdext/source/presenter/PresenterHelper.cxx
@@ -0,0 +1,56 @@
+/* -*- 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 "PresenterHelper.hxx"
+
+#include <com/sun/star/presentation/XPresentationSupplier.hpp>
+#include <com/sun/star/presentation/XPresentation2.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::presentation;
+
+namespace sdext::presenter {
+
+constexpr OUStringLiteral msPaneURLPrefix( u"private:resource/pane/");
+const OUString PresenterHelper::msFullScreenPaneURL( msPaneURLPrefix + "FullScreenPane");
+
+Reference<presentation::XSlideShowController> PresenterHelper::GetSlideShowController (
+ const Reference<frame::XController>& rxController)
+{
+ Reference<presentation::XSlideShowController> xSlideShowController;
+
+ if( rxController.is() ) try
+ {
+ Reference<XPresentationSupplier> xPS ( rxController->getModel(), UNO_QUERY_THROW);
+
+ Reference<XPresentation2> xPresentation(xPS->getPresentation(), UNO_QUERY_THROW);
+
+ xSlideShowController = xPresentation->getController();
+ }
+ catch(RuntimeException&)
+ {
+ }
+
+ return xSlideShowController;
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterHelper.hxx b/sdext/source/presenter/PresenterHelper.hxx
new file mode 100644
index 000000000..69fc57333
--- /dev/null
+++ b/sdext/source/presenter/PresenterHelper.hxx
@@ -0,0 +1,48 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERHELPER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERHELPER_HXX
+
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/presentation/XSlideShowController.hpp>
+
+namespace sdext::presenter
+{
+/** Collection of helper functions that do not fit in anywhere else.
+ Provide access to frequently used strings of the drawing framework.
+*/
+namespace PresenterHelper
+{
+extern const OUString msFullScreenPaneURL;
+
+/** Return the slide show controller of a running presentation that has
+ the same document as the given framework controller.
+ @return
+ When no presentation is running this method returns an empty reference.
+*/
+css::uno::Reference<css::presentation::XSlideShowController>
+GetSlideShowController(const css::uno::Reference<css::frame::XController>& rxController);
+}
+
+} // end of namespace presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterNotesView.cxx b/sdext/source/presenter/PresenterNotesView.cxx
new file mode 100644
index 000000000..457be1f61
--- /dev/null
+++ b/sdext/source/presenter/PresenterNotesView.cxx
@@ -0,0 +1,650 @@
+/* -*- 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 <vcl/settings.hxx>
+#include "PresenterNotesView.hxx"
+#include "PresenterButton.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterGeometryHelper.hxx"
+#include "PresenterPaintManager.hxx"
+#include "PresenterScrollBar.hxx"
+#include "PresenterTextView.hxx"
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/awt/Key.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <com/sun/star/drawing/framework/XPane.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/presentation/XPresentationPage.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/XSpriteCanvas.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+const sal_Int32 gnSpaceBelowSeparator (10);
+const sal_Int32 gnSpaceAboveSeparator (10);
+const double gnLineScrollFactor (1.2);
+
+namespace sdext::presenter {
+
+//===== PresenterNotesView ====================================================
+
+PresenterNotesView::PresenterNotesView (
+ const Reference<XComponentContext>& rxComponentContext,
+ const Reference<XResourceId>& rxViewId,
+ const Reference<frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterNotesViewInterfaceBase(m_aMutex),
+ mxViewId(rxViewId),
+ mpPresenterController(rpPresenterController),
+ maSeparatorColor(0xffffff),
+ mnSeparatorYLocation(0),
+ mnTop(0)
+{
+ try
+ {
+ Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW);
+ Reference<XConfigurationController> xCC (xCM->getConfigurationController(), UNO_SET_THROW);
+ Reference<XPane> xPane (xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW);
+
+ mxParentWindow = xPane->getWindow();
+ mxCanvas = xPane->getCanvas();
+ mpTextView = std::make_shared<PresenterTextView>(
+ rxComponentContext,
+ mxCanvas,
+ mpPresenterController->GetPaintManager()->GetInvalidator(mxParentWindow));
+
+ const OUString sResourceURL (mxViewId->getResourceURL());
+ mpFont = std::make_shared<PresenterTheme::FontDescriptor>(
+ rpPresenterController->GetViewFont(sResourceURL));
+ maSeparatorColor = mpFont->mnColor;
+ mpTextView->SetFont(mpFont);
+
+ CreateToolBar(rxComponentContext, rpPresenterController);
+
+ mpCloseButton = PresenterButton::Create(
+ rxComponentContext,
+ mpPresenterController,
+ mpPresenterController->GetTheme(),
+ mxParentWindow,
+ mxCanvas,
+ "NotesViewCloser");
+
+ if (mxParentWindow.is())
+ {
+ mxParentWindow->addWindowListener(this);
+ mxParentWindow->addPaintListener(this);
+ mxParentWindow->addKeyListener(this);
+ mxParentWindow->setVisible(true);
+ }
+
+ mpScrollBar = new PresenterVerticalScrollBar(
+ rxComponentContext,
+ mxParentWindow,
+ mpPresenterController->GetPaintManager(),
+ [this](double f) { return this->SetTop(f); });
+ mpScrollBar->SetBackground(
+ mpPresenterController->GetViewBackground(mxViewId->getResourceURL()));
+
+ mpScrollBar->SetCanvas(mxCanvas);
+
+ Layout();
+ }
+ catch (RuntimeException&)
+ {
+ PresenterNotesView::disposing();
+ throw;
+ }
+}
+
+PresenterNotesView::~PresenterNotesView()
+{
+}
+
+void SAL_CALL PresenterNotesView::disposing()
+{
+ if (mxParentWindow.is())
+ {
+ mxParentWindow->removeWindowListener(this);
+ mxParentWindow->removePaintListener(this);
+ mxParentWindow->removeKeyListener(this);
+ mxParentWindow = nullptr;
+ }
+
+ // Dispose tool bar.
+ {
+ Reference<XComponent> xComponent = mpToolBar;
+ mpToolBar = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ {
+ Reference<XComponent> xComponent (mxToolBarCanvas, UNO_QUERY);
+ mxToolBarCanvas = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ {
+ Reference<XComponent> xComponent = mxToolBarWindow;
+ mxToolBarWindow = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ // Dispose close button
+ {
+ Reference<XComponent> xComponent = mpCloseButton;
+ mpCloseButton = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ // Create the tool bar.
+
+ mpScrollBar = nullptr;
+
+ mxViewId = nullptr;
+}
+
+void PresenterNotesView::CreateToolBar (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+{
+ if (!rpPresenterController)
+ return;
+
+ Reference<drawing::XPresenterHelper> xPresenterHelper (
+ rpPresenterController->GetPresenterHelper());
+ if ( ! xPresenterHelper.is())
+ return;
+
+ // Create a new window as container of the tool bar.
+ mxToolBarWindow = xPresenterHelper->createWindow(
+ mxParentWindow,
+ false,
+ true,
+ false,
+ false);
+ mxToolBarCanvas = xPresenterHelper->createSharedCanvas (
+ Reference<rendering::XSpriteCanvas>(mxCanvas, UNO_QUERY),
+ mxParentWindow,
+ mxCanvas,
+ mxParentWindow,
+ mxToolBarWindow);
+
+ // Create the tool bar.
+ mpToolBar = new PresenterToolBar(
+ rxContext,
+ mxToolBarWindow,
+ mxToolBarCanvas,
+ rpPresenterController,
+ PresenterToolBar::Left);
+ mpToolBar->Initialize(
+ "PresenterScreenSettings/ToolBars/NotesToolBar");
+}
+
+void PresenterNotesView::SetSlide (const Reference<drawing::XDrawPage>& rxNotesPage)
+{
+ static constexpr OUStringLiteral sNotesShapeName (
+ u"com.sun.star.presentation.NotesShape");
+ static constexpr OUStringLiteral sTextShapeName (
+ u"com.sun.star.drawing.TextShape");
+
+ if (!rxNotesPage.is())
+ return;
+
+ // Iterate over all shapes and find the one that holds the text.
+ sal_Int32 nCount (rxNotesPage->getCount());
+ for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
+ {
+
+ Reference<lang::XServiceName> xServiceName (
+ rxNotesPage->getByIndex(nIndex), UNO_QUERY);
+ if (xServiceName.is()
+ && xServiceName->getServiceName() == sNotesShapeName)
+ {
+ }
+ else
+ {
+ Reference<drawing::XShapeDescriptor> xShapeDescriptor (
+ rxNotesPage->getByIndex(nIndex), UNO_QUERY);
+ if (xShapeDescriptor.is())
+ {
+ OUString sType (xShapeDescriptor->getShapeType());
+ if (sType == sNotesShapeName || sType == sTextShapeName)
+ {
+ Reference<text::XTextRange> xText (
+ rxNotesPage->getByIndex(nIndex), UNO_QUERY);
+ if (xText.is())
+ {
+ mpTextView->SetText(Reference<text::XText>(xText, UNO_QUERY));
+ }
+ }
+ }
+ }
+ }
+
+ Layout();
+
+ if (mpScrollBar)
+ {
+ mpScrollBar->SetThumbPosition(0, false);
+ UpdateScrollBar();
+ }
+
+ Invalidate();
+}
+
+//----- lang::XEventListener -------------------------------------------------
+
+void SAL_CALL PresenterNotesView::disposing (const lang::EventObject& rEventObject)
+{
+ if (rEventObject.Source == mxParentWindow)
+ mxParentWindow = nullptr;
+}
+
+//----- XWindowListener -------------------------------------------------------
+
+void SAL_CALL PresenterNotesView::windowResized (const awt::WindowEvent&)
+{
+ Layout();
+}
+
+void SAL_CALL PresenterNotesView::windowMoved (const awt::WindowEvent&) {}
+
+void SAL_CALL PresenterNotesView::windowShown (const lang::EventObject&) {}
+
+void SAL_CALL PresenterNotesView::windowHidden (const lang::EventObject&) {}
+
+//----- XPaintListener --------------------------------------------------------
+
+void SAL_CALL PresenterNotesView::windowPaint (const awt::PaintEvent& rEvent)
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterNotesView object has already been disposed",
+ static_cast<uno::XWeak*>(this));
+ }
+
+ if ( ! mbIsPresenterViewActive)
+ return;
+
+ ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex());
+ Paint(rEvent.UpdateRect);
+}
+
+//----- XResourceId -----------------------------------------------------------
+
+Reference<XResourceId> SAL_CALL PresenterNotesView::getResourceId()
+{
+ return mxViewId;
+}
+
+sal_Bool SAL_CALL PresenterNotesView::isAnchorOnly()
+{
+ return false;
+}
+
+//----- XDrawView -------------------------------------------------------------
+
+void SAL_CALL PresenterNotesView::setCurrentPage (const Reference<drawing::XDrawPage>& rxSlide)
+{
+ // Get the associated notes page.
+ mxCurrentNotesPage = nullptr;
+ try
+ {
+ Reference<presentation::XPresentationPage> xPresentationPage(rxSlide, UNO_QUERY);
+ if (xPresentationPage.is())
+ mxCurrentNotesPage = xPresentationPage->getNotesPage();
+ }
+ catch (RuntimeException&)
+ {
+ }
+
+ SetSlide(mxCurrentNotesPage);
+}
+
+Reference<drawing::XDrawPage> SAL_CALL PresenterNotesView::getCurrentPage()
+{
+ return nullptr;
+}
+
+//----- XKeyListener ----------------------------------------------------------
+
+void SAL_CALL PresenterNotesView::keyPressed (const awt::KeyEvent& rEvent)
+{
+ switch (rEvent.KeyCode)
+ {
+ case awt::Key::A:
+ Scroll(-gnLineScrollFactor * mpFont->mnSize);
+ break;
+
+ case awt::Key::Y:
+ case awt::Key::Z:
+ Scroll(+gnLineScrollFactor * mpFont->mnSize);
+ break;
+
+ case awt::Key::S:
+ ChangeFontSize(-1);
+ break;
+
+ case awt::Key::G:
+ ChangeFontSize(+1);
+ break;
+
+ case awt::Key::H:
+ if (mpTextView)
+ mpTextView->MoveCaret(
+ -1,
+ (rEvent.Modifiers == awt::KeyModifier::SHIFT)
+ ? css::accessibility::AccessibleTextType::CHARACTER
+ : css::accessibility::AccessibleTextType::WORD);
+ break;
+
+ case awt::Key::L:
+ if (mpTextView)
+ mpTextView->MoveCaret(
+ +1,
+ (rEvent.Modifiers == awt::KeyModifier::SHIFT)
+ ? css::accessibility::AccessibleTextType::CHARACTER
+ : css::accessibility::AccessibleTextType::WORD);
+ break;
+ }
+}
+
+void SAL_CALL PresenterNotesView::keyReleased (const awt::KeyEvent&) {}
+
+
+void PresenterNotesView::Layout()
+{
+ if ( ! mxParentWindow.is())
+ return;
+ awt::Rectangle aWindowBox (mxParentWindow->getPosSize());
+ geometry::RealRectangle2D aNewTextBoundingBox (0,0,aWindowBox.Width, aWindowBox.Height);
+ // Size the tool bar and the horizontal separator above it.
+ if (mxToolBarWindow.is())
+ {
+ const geometry::RealSize2D aToolBarSize (mpToolBar->GetMinimalSize());
+ const sal_Int32 nToolBarHeight = sal_Int32(aToolBarSize.Height + 0.5);
+ mxToolBarWindow->setPosSize(0, aWindowBox.Height - nToolBarHeight,
+ sal_Int32(aToolBarSize.Width + 0.5), nToolBarHeight,
+ awt::PosSize::POSSIZE);
+ mnSeparatorYLocation = aWindowBox.Height - nToolBarHeight - gnSpaceBelowSeparator;
+ aNewTextBoundingBox.Y2 = mnSeparatorYLocation - gnSpaceAboveSeparator;
+ // Place the close button.
+ if (mpCloseButton)
+ mpCloseButton->SetCenter(geometry::RealPoint2D(
+ (aWindowBox.Width + aToolBarSize.Width) / 2,
+ aWindowBox.Height - aToolBarSize.Height/2));
+ }
+ // Check whether the vertical scroll bar is necessary.
+ if (mpScrollBar)
+ {
+ bool bShowVerticalScrollbar (false);
+ try
+ {
+ const double nTextBoxHeight (aNewTextBoundingBox.Y2 - aNewTextBoundingBox.Y1);
+ const double nHeight (mpTextView->GetTotalTextHeight());
+ if (nHeight > nTextBoxHeight)
+ {
+ bShowVerticalScrollbar = true;
+ if(!AllSettings::GetLayoutRTL())
+ aNewTextBoundingBox.X2 -= mpScrollBar->GetSize();
+ else
+ aNewTextBoundingBox.X1 += mpScrollBar->GetSize();
+ }
+ mpScrollBar->SetTotalSize(nHeight);
+ }
+ catch(beans::UnknownPropertyException&)
+ {
+ OSL_ASSERT(false);
+ }
+ if(AllSettings::GetLayoutRTL())
+ {
+ mpScrollBar->SetVisible(bShowVerticalScrollbar);
+ mpScrollBar->SetPosSize(
+ geometry::RealRectangle2D(
+ aNewTextBoundingBox.X1 - mpScrollBar->GetSize(),
+ aNewTextBoundingBox.Y1,
+ aNewTextBoundingBox.X1,
+ aNewTextBoundingBox.Y2));
+ if( ! bShowVerticalScrollbar)
+ mpScrollBar->SetThumbPosition(0, false);
+ UpdateScrollBar();
+ }
+ else
+ {
+ mpScrollBar->SetVisible(bShowVerticalScrollbar);
+ mpScrollBar->SetPosSize(
+ geometry::RealRectangle2D(
+ aWindowBox.Width - mpScrollBar->GetSize(),
+ aNewTextBoundingBox.Y1,
+ aNewTextBoundingBox.X2 + mpScrollBar->GetSize(),
+ aNewTextBoundingBox.Y2));
+ if( ! bShowVerticalScrollbar)
+ mpScrollBar->SetThumbPosition(0, false);
+ UpdateScrollBar();
+ }
+ }
+ // Has the text area has changed it position or size?
+ if (aNewTextBoundingBox.X1 != maTextBoundingBox.X1
+ || aNewTextBoundingBox.Y1 != maTextBoundingBox.Y1
+ || aNewTextBoundingBox.X2 != maTextBoundingBox.X2
+ || aNewTextBoundingBox.Y2 != maTextBoundingBox.Y2)
+ {
+ maTextBoundingBox = aNewTextBoundingBox;
+ mpTextView->SetLocation(
+ geometry::RealPoint2D(
+ aNewTextBoundingBox.X1,
+ aNewTextBoundingBox.Y1));
+ mpTextView->SetSize(
+ geometry::RealSize2D(
+ aNewTextBoundingBox.X2 - aNewTextBoundingBox.X1,
+ aNewTextBoundingBox.Y2 - aNewTextBoundingBox.Y1));
+ }
+}
+
+void PresenterNotesView::Paint (const awt::Rectangle& rUpdateBox)
+{
+ if ( ! mxParentWindow.is())
+ return;
+ if ( ! mxCanvas.is())
+ return;
+
+ if (!mpBackground)
+ mpBackground = mpPresenterController->GetViewBackground(mxViewId->getResourceURL());
+
+ if (rUpdateBox.Y < maTextBoundingBox.Y2
+ && rUpdateBox.X < maTextBoundingBox.X2)
+ {
+ PaintText(rUpdateBox);
+ }
+
+ mpTextView->Paint(rUpdateBox);
+
+ if (rUpdateBox.Y + rUpdateBox.Height > maTextBoundingBox.Y2)
+ {
+ PaintToolBar(rUpdateBox);
+ }
+}
+
+void PresenterNotesView::PaintToolBar (const awt::Rectangle& rUpdateBox)
+{
+ awt::Rectangle aWindowBox (mxParentWindow->getPosSize());
+
+ rendering::ViewState aViewState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr);
+ rendering::RenderState aRenderState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ if (mpBackground)
+ {
+ // Paint the background.
+ mpPresenterController->GetCanvasHelper()->Paint(
+ mpBackground,
+ mxCanvas,
+ rUpdateBox,
+ awt::Rectangle(0,sal_Int32(maTextBoundingBox.Y2),aWindowBox.Width,aWindowBox.Height),
+ awt::Rectangle());
+ }
+
+ // Paint the horizontal separator.
+ OSL_ASSERT(mxViewId.is());
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, maSeparatorColor);
+
+ mxCanvas->drawLine(
+ geometry::RealPoint2D(0,mnSeparatorYLocation),
+ geometry::RealPoint2D(aWindowBox.Width,mnSeparatorYLocation),
+ aViewState,
+ aRenderState);
+}
+
+void PresenterNotesView::PaintText (const awt::Rectangle& rUpdateBox)
+{
+ const awt::Rectangle aBox (PresenterGeometryHelper::Intersection(rUpdateBox,
+ PresenterGeometryHelper::ConvertRectangle(maTextBoundingBox)));
+
+ if (aBox.Width <= 0 || aBox.Height <= 0)
+ return;
+
+ if (mpBackground)
+ {
+ // Paint the background.
+ mpPresenterController->GetCanvasHelper()->Paint(
+ mpBackground,
+ mxCanvas,
+ rUpdateBox,
+ aBox,
+ awt::Rectangle());
+ }
+
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(false);
+}
+
+void PresenterNotesView::Invalidate()
+{
+ mpPresenterController->GetPaintManager()->Invalidate(
+ mxParentWindow,
+ PresenterGeometryHelper::ConvertRectangle(maTextBoundingBox));
+}
+
+void PresenterNotesView::Scroll (const double rnDistance)
+{
+ try
+ {
+ mnTop += rnDistance;
+ mpTextView->SetOffset(0, mnTop);
+
+ UpdateScrollBar();
+ Invalidate();
+ }
+ catch (beans::UnknownPropertyException&)
+ {}
+}
+
+void PresenterNotesView::SetTop (const double nTop)
+{
+ try
+ {
+ mnTop = nTop;
+ mpTextView->SetOffset(0, mnTop);
+
+ UpdateScrollBar();
+ Invalidate();
+ }
+ catch (beans::UnknownPropertyException&)
+ {}
+}
+
+void PresenterNotesView::ChangeFontSize (const sal_Int32 nSizeChange)
+{
+ const sal_Int32 nNewSize (mpFont->mnSize + nSizeChange);
+ if (nNewSize <= 5)
+ return;
+
+ mpFont->mnSize = nNewSize;
+ mpFont->mxFont = nullptr;
+ mpTextView->SetFont(mpFont);
+
+ Layout();
+ UpdateScrollBar();
+ Invalidate();
+
+ // Write the new font size to the configuration to make it persistent.
+ try
+ {
+ const OUString sStyleName (mpPresenterController->GetTheme()->GetStyleName(
+ mxViewId->getResourceURL()));
+ std::shared_ptr<PresenterConfigurationAccess> pConfiguration (
+ mpPresenterController->GetTheme()->GetNodeForViewStyle(
+ sStyleName));
+ if (pConfiguration == nullptr || !pConfiguration->IsValid())
+ return;
+
+ pConfiguration->GoToChild("Font");
+ pConfiguration->SetProperty("Size", Any(static_cast<sal_Int32>(nNewSize+0.5)));
+ pConfiguration->CommitChanges();
+ }
+ catch (Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+}
+
+const std::shared_ptr<PresenterTextView>& PresenterNotesView::GetTextView() const
+{
+ return mpTextView;
+}
+
+void PresenterNotesView::UpdateScrollBar()
+{
+ if (!mpScrollBar)
+ return;
+
+ try
+ {
+ mpScrollBar->SetTotalSize(mpTextView->GetTotalTextHeight());
+ }
+ catch(beans::UnknownPropertyException&)
+ {
+ OSL_ASSERT(false);
+ }
+
+ mpScrollBar->SetLineHeight(mpFont->mnSize*1.2);
+ mpScrollBar->SetThumbPosition(mnTop, false);
+
+ mpScrollBar->SetThumbSize(maTextBoundingBox.Y2 - maTextBoundingBox.Y1);
+ mpScrollBar->CheckValues();
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterNotesView.hxx b/sdext/source/presenter/PresenterNotesView.hxx
new file mode 100644
index 000000000..1af3f241f
--- /dev/null
+++ b/sdext/source/presenter/PresenterNotesView.hxx
@@ -0,0 +1,156 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERNOTESVIEW_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERNOTESVIEW_HXX
+
+#include "PresenterController.hxx"
+#include "PresenterToolBar.hxx"
+#include "PresenterViewFactory.hxx"
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/drawing/framework/XView.hpp>
+#include <com/sun/star/drawing/framework/XResourceId.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <rtl/ref.hxx>
+#include <memory>
+
+namespace sdext::presenter {
+
+class PresenterButton;
+class PresenterScrollBar;
+class PresenterTextView;
+
+typedef cppu::WeakComponentImplHelper<
+ css::awt::XWindowListener,
+ css::awt::XPaintListener,
+ css::drawing::framework::XView,
+ css::drawing::XDrawView,
+ css::awt::XKeyListener
+ > PresenterNotesViewInterfaceBase;
+
+/** A drawing framework view of the notes of a slide. At the moment this is
+ a simple text view that does not show the original formatting of the
+ notes text.
+*/
+class PresenterNotesView
+ : private ::cppu::BaseMutex,
+ public PresenterNotesViewInterfaceBase,
+ public CachablePresenterView
+{
+public:
+ explicit PresenterNotesView (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterNotesView() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ /** Typically called from setCurrentSlide() with the notes page that is
+ associated with the slide given to setCurrentSlide().
+
+ Iterates over all text shapes on the given notes page and displays
+ the concatenated text of these.
+ */
+ void SetSlide (
+ const css::uno::Reference<css::drawing::XDrawPage>& rxNotesPage);
+
+ void ChangeFontSize (const sal_Int32 nSizeChange);
+
+ const std::shared_ptr<PresenterTextView>& GetTextView() const;
+
+ // lang::XEventListener
+
+ virtual void SAL_CALL
+ disposing (const css::lang::EventObject& rEventObject) override;
+
+ // XWindowListener
+
+ virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override;
+
+ // XPaintListener
+
+ virtual void SAL_CALL windowPaint (const css::awt::PaintEvent& rEvent) override;
+
+ // XResourceId
+
+ virtual css::uno::Reference<css::drawing::framework::XResourceId> SAL_CALL getResourceId() override;
+
+ virtual sal_Bool SAL_CALL isAnchorOnly() override;
+
+ // XDrawView
+
+ virtual void SAL_CALL setCurrentPage (
+ const css::uno::Reference<css::drawing::XDrawPage>& rxSlide) override;
+
+ virtual css::uno::Reference<css::drawing::XDrawPage> SAL_CALL getCurrentPage() override;
+
+ // XKeyListener
+
+ virtual void SAL_CALL keyPressed (const css::awt::KeyEvent& rEvent) override;
+ virtual void SAL_CALL keyReleased (const css::awt::KeyEvent& rEvent) override;
+
+private:
+ css::uno::Reference<css::drawing::framework::XResourceId> mxViewId;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ css::uno::Reference<css::awt::XWindow> mxParentWindow;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+ css::uno::Reference<css::drawing::XDrawPage> mxCurrentNotesPage;
+ ::rtl::Reference<PresenterScrollBar> mpScrollBar;
+ css::uno::Reference<css::awt::XWindow> mxToolBarWindow;
+ css::uno::Reference<css::rendering::XCanvas> mxToolBarCanvas;
+ ::rtl::Reference<PresenterToolBar> mpToolBar;
+ ::rtl::Reference<PresenterButton> mpCloseButton;
+ css::util::Color maSeparatorColor;
+ sal_Int32 mnSeparatorYLocation;
+ css::geometry::RealRectangle2D maTextBoundingBox;
+ SharedBitmapDescriptor mpBackground;
+ double mnTop;
+ PresenterTheme::SharedFontDescriptor mpFont;
+ std::shared_ptr<PresenterTextView> mpTextView;
+
+ void CreateToolBar (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ void Layout();
+ void Paint (const css::awt::Rectangle& rUpdateBox);
+ void PaintToolBar (const css::awt::Rectangle& rUpdateBox);
+ void PaintText (const css::awt::Rectangle& rUpdateBox);
+ void Invalidate();
+ void Scroll (const double nDistance);
+ void SetTop (const double nTop);
+ void UpdateScrollBar();
+};
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPaintManager.cxx b/sdext/source/presenter/PresenterPaintManager.cxx
new file mode 100644
index 000000000..ba1bc48de
--- /dev/null
+++ b/sdext/source/presenter/PresenterPaintManager.cxx
@@ -0,0 +1,141 @@
+/* -*- 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 "PresenterPaintManager.hxx"
+
+#include "PresenterPaneContainer.hxx"
+#include <com/sun/star/awt/InvalidateStyle.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sdext::presenter {
+
+PresenterPaintManager::PresenterPaintManager (
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow,
+ const css::uno::Reference<css::drawing::XPresenterHelper>& rxPresenterHelper,
+ const rtl::Reference<PresenterPaneContainer>& rpPaneContainer)
+ : mxParentWindow(rxParentWindow),
+ mxParentWindowPeer(rxParentWindow, UNO_QUERY),
+ mxPresenterHelper(rxPresenterHelper),
+ mpPaneContainer(rpPaneContainer)
+{
+}
+
+::std::function<void (const css::awt::Rectangle& rRepaintBox)>
+ PresenterPaintManager::GetInvalidator (
+ const css::uno::Reference<css::awt::XWindow>& rxWindow)
+{
+ return [this, rxWindow] (css::awt::Rectangle const& rRepaintBox)
+ {
+ return this->Invalidate(rxWindow, rRepaintBox /* , bSynchronous=false */);
+ };
+}
+
+void PresenterPaintManager::Invalidate (
+ const css::uno::Reference<css::awt::XWindow>& rxWindow)
+{
+ sal_Int16 nInvalidateMode (awt::InvalidateStyle::CHILDREN);
+
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor(
+ mpPaneContainer->FindContentWindow(rxWindow));
+ if (!pDescriptor || ! pDescriptor->mbIsOpaque)
+ nInvalidateMode |= awt::InvalidateStyle::TRANSPARENT;
+ else
+ nInvalidateMode |= awt::InvalidateStyle::NOTRANSPARENT;
+
+ Invalidate(rxWindow, nInvalidateMode);
+}
+
+void PresenterPaintManager::Invalidate (
+ const css::uno::Reference<css::awt::XWindow>& rxWindow,
+ const sal_Int16 nInvalidateFlags)
+{
+ if ((nInvalidateFlags & awt::InvalidateStyle::TRANSPARENT) != 0)
+ {
+ // Window is transparent and parent window(s) have to be painted as
+ // well. Invalidate the parent explicitly.
+ if (mxPresenterHelper.is() && mxParentWindowPeer.is())
+ {
+ const awt::Rectangle aBBox (
+ mxPresenterHelper->getWindowExtentsRelative(rxWindow, mxParentWindow));
+ mxParentWindowPeer->invalidateRect(aBBox, nInvalidateFlags);
+ }
+ }
+ else
+ {
+ Reference<awt::XWindowPeer> xPeer (rxWindow, UNO_QUERY);
+ if (xPeer.is())
+ xPeer->invalidate(nInvalidateFlags);
+ }
+}
+
+void PresenterPaintManager::Invalidate (
+ const css::uno::Reference<css::awt::XWindow>& rxWindow,
+ const css::awt::Rectangle& rRepaintBox,
+ const bool bSynchronous)
+{
+ sal_Int16 nInvalidateMode (awt::InvalidateStyle::CHILDREN);
+ if (bSynchronous)
+ nInvalidateMode |= awt::InvalidateStyle::UPDATE;
+
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor(
+ mpPaneContainer->FindContentWindow(rxWindow));
+ if (!pDescriptor || ! pDescriptor->mbIsOpaque)
+ nInvalidateMode |= awt::InvalidateStyle::TRANSPARENT;
+ else
+ nInvalidateMode |= awt::InvalidateStyle::NOTRANSPARENT;
+
+ Invalidate(rxWindow, rRepaintBox, nInvalidateMode);
+}
+
+void PresenterPaintManager::Invalidate (
+ const css::uno::Reference<css::awt::XWindow>& rxWindow,
+ const css::awt::Rectangle& rRepaintBox,
+ const sal_Int16 nInvalidateFlags)
+{
+ if ((nInvalidateFlags & awt::InvalidateStyle::TRANSPARENT) != 0)
+ {
+ // Window is transparent and parent window(s) have to be painted as
+ // well. Invalidate the parent explicitly.
+ if (mxPresenterHelper.is() && mxParentWindowPeer.is())
+ {
+ const awt::Rectangle aBBox (
+ mxPresenterHelper->getWindowExtentsRelative(rxWindow, mxParentWindow));
+ mxParentWindowPeer->invalidateRect(
+ awt::Rectangle(
+ rRepaintBox.X + aBBox.X,
+ rRepaintBox.Y + aBBox.Y,
+ rRepaintBox.Width,
+ rRepaintBox.Height),
+ nInvalidateFlags);
+ }
+ }
+ else
+ {
+ Reference<awt::XWindowPeer> xPeer (rxWindow, UNO_QUERY);
+ if (xPeer.is())
+ xPeer->invalidateRect(rRepaintBox, nInvalidateFlags);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPaintManager.hxx b/sdext/source/presenter/PresenterPaintManager.hxx
new file mode 100644
index 000000000..d3013209a
--- /dev/null
+++ b/sdext/source/presenter/PresenterPaintManager.hxx
@@ -0,0 +1,89 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPAINTMANAGER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPAINTMANAGER_HXX
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/drawing/XPresenterHelper.hpp>
+#include <rtl/ref.hxx>
+
+#include <functional>
+
+#include "PresenterPaneContainer.hxx"
+
+namespace sdext::presenter {
+
+class PresenterPaneContainer;
+
+/** Synchronize painting of windows and canvases. At the moment there is
+ just some processing of invalidate calls.
+ This could be extended to process incoming windowPaint() calls.
+*/
+class PresenterPaintManager
+{
+public:
+ /** Create paint manager with the window that is the top node in the
+ local window hierarchy.
+ */
+ PresenterPaintManager (
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow,
+ const css::uno::Reference<css::drawing::XPresenterHelper>& rxPresenterHelper,
+ const rtl::Reference<PresenterPaneContainer>& rpPaneContainer);
+
+ ::std::function<void (const css::awt::Rectangle& rRepaintBox)>
+ GetInvalidator (
+ const css::uno::Reference<css::awt::XWindow>& rxWindow);
+
+ /** Request a repaint of the whole window.
+ @param rxWindow
+ May be the parent window or one of its descendents.
+ */
+ void Invalidate (
+ const css::uno::Reference<css::awt::XWindow>& rxWindow);
+ void Invalidate (
+ const css::uno::Reference<css::awt::XWindow>& rxWindow,
+ const sal_Int16 nInvalidateFlags);
+
+ /** Request a repaint of a part of a window.
+ @param rxWindow
+ May be the parent window or one of its descendents.
+ */
+ void Invalidate (
+ const css::uno::Reference<css::awt::XWindow>& rxWindow,
+ const css::awt::Rectangle& rRepaintBox,
+ const bool bSynchronous = false);
+ void Invalidate (
+ const css::uno::Reference<css::awt::XWindow>& rxWindow,
+ const css::awt::Rectangle& rRepaintBox,
+ const sal_Int16 nInvalidateFlags);
+
+private:
+ css::uno::Reference<css::awt::XWindow> mxParentWindow;
+ css::uno::Reference<css::awt::XWindowPeer> mxParentWindowPeer;
+ css::uno::Reference<css::drawing::XPresenterHelper> mxPresenterHelper;
+ ::rtl::Reference<PresenterPaneContainer> mpPaneContainer;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPane.cxx b/sdext/source/presenter/PresenterPane.cxx
new file mode 100644
index 000000000..ad3531543
--- /dev/null
+++ b/sdext/source/presenter/PresenterPane.cxx
@@ -0,0 +1,169 @@
+/* -*- 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 "PresenterPane.hxx"
+#include "PresenterController.hxx"
+#include "PresenterPaintManager.hxx"
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sdext::presenter {
+
+//===== PresenterPane =========================================================
+
+PresenterPane::PresenterPane (
+ const Reference<XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterPaneBase(rxContext, rpPresenterController)
+{
+ Reference<lang::XMultiComponentFactory> xFactory (
+ mxComponentContext->getServiceManager(), UNO_SET_THROW);
+ mxPresenterHelper.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.comp.Draw.PresenterHelper",
+ mxComponentContext),
+ UNO_QUERY_THROW);
+}
+
+PresenterPane::~PresenterPane()
+{
+}
+
+//----- XPane -----------------------------------------------------------------
+
+Reference<awt::XWindow> SAL_CALL PresenterPane::getWindow()
+{
+ ThrowIfDisposed();
+ return mxContentWindow;
+}
+
+Reference<rendering::XCanvas> SAL_CALL PresenterPane::getCanvas()
+{
+ ThrowIfDisposed();
+ return mxContentCanvas;
+}
+
+//----- XWindowListener -------------------------------------------------------
+
+void SAL_CALL PresenterPane::windowResized (const awt::WindowEvent& rEvent)
+{
+ PresenterPaneBase::windowResized(rEvent);
+
+ Invalidate(maBoundingBox);
+
+ LayoutContextWindow();
+ ToTop();
+
+ UpdateBoundingBox();
+ Invalidate(maBoundingBox);
+}
+
+void SAL_CALL PresenterPane::windowMoved (const awt::WindowEvent& rEvent)
+{
+ PresenterPaneBase::windowMoved(rEvent);
+
+ Invalidate(maBoundingBox);
+
+ ToTop();
+
+ UpdateBoundingBox();
+ Invalidate(maBoundingBox);
+}
+
+void SAL_CALL PresenterPane::windowShown (const lang::EventObject& rEvent)
+{
+ PresenterPaneBase::windowShown(rEvent);
+
+ ToTop();
+
+ if (mxContentWindow.is())
+ {
+ LayoutContextWindow();
+ mxContentWindow->setVisible(true);
+ }
+
+ UpdateBoundingBox();
+ Invalidate(maBoundingBox);
+}
+
+void SAL_CALL PresenterPane::windowHidden (const lang::EventObject& rEvent)
+{
+ PresenterPaneBase::windowHidden(rEvent);
+
+ if (mxContentWindow.is())
+ mxContentWindow->setVisible(false);
+}
+
+//----- XPaintListener --------------------------------------------------------
+
+void SAL_CALL PresenterPane::windowPaint (const awt::PaintEvent& rEvent)
+{
+ ThrowIfDisposed();
+
+ PaintBorder(rEvent.UpdateRect);
+}
+
+
+void PresenterPane::CreateCanvases (
+ const Reference<rendering::XSpriteCanvas>& rxParentCanvas)
+{
+ if ( ! mxPresenterHelper.is())
+ return;
+ if ( ! mxParentWindow.is())
+ return;
+ if ( ! rxParentCanvas.is())
+ return;
+
+ mxBorderCanvas = mxPresenterHelper->createSharedCanvas(
+ rxParentCanvas,
+ mxParentWindow,
+ rxParentCanvas,
+ mxParentWindow,
+ mxBorderWindow);
+ mxContentCanvas = mxPresenterHelper->createSharedCanvas(
+ rxParentCanvas,
+ mxParentWindow,
+ rxParentCanvas,
+ mxParentWindow,
+ mxContentWindow);
+
+ PaintBorder(mxBorderWindow->getPosSize());
+}
+
+void PresenterPane::Invalidate (const css::awt::Rectangle& rRepaintBox)
+{
+ // Invalidate the parent window to be able to invalidate an area outside
+ // the current window area.
+ mpPresenterController->GetPaintManager()->Invalidate(mxParentWindow, rRepaintBox);
+}
+
+void PresenterPane::UpdateBoundingBox()
+{
+ if (mxBorderWindow.is() && IsVisible())
+ maBoundingBox = mxBorderWindow->getPosSize();
+ else
+ maBoundingBox = awt::Rectangle();
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPane.hxx b/sdext/source/presenter/PresenterPane.hxx
new file mode 100644
index 000000000..2a057229f
--- /dev/null
+++ b/sdext/source/presenter/PresenterPane.hxx
@@ -0,0 +1,80 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPANE_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPANE_HXX
+
+#include "PresenterPaneBase.hxx"
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <rtl/ref.hxx>
+
+namespace sdext::presenter
+{
+/** Pane used by the presenter screen. Pane objects are stored in the
+ PresenterPaneContainer. Sizes and positions are controlled
+ by the PresenterWindowManager. Interactive positioning and resizing is
+ managed by the PresenterPaneBorderManager. Borders around panes are
+ painted by the PresenterPaneBorderPainter.
+*/
+class PresenterPane : public PresenterPaneBase
+{
+public:
+ PresenterPane(const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterPane() override;
+
+ // XPane
+
+ css::uno::Reference<css::awt::XWindow> SAL_CALL getWindow() override;
+
+ css::uno::Reference<css::rendering::XCanvas> SAL_CALL getCanvas() override;
+
+ // XWindowListener
+
+ virtual void SAL_CALL windowResized(const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved(const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown(const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden(const css::lang::EventObject& rEvent) override;
+
+ // XPaintListener
+
+ virtual void SAL_CALL windowPaint(const css::awt::PaintEvent& rEvent) override;
+
+private:
+ /** Store the bounding box so that when the window is resized or moved
+ we still know the old position and size.
+ */
+ css::awt::Rectangle maBoundingBox;
+
+ virtual void CreateCanvases(
+ const css::uno::Reference<css::rendering::XSpriteCanvas>& rxParentCanvas) override;
+
+ void Invalidate(const css::awt::Rectangle& rRepaintBox);
+ void UpdateBoundingBox();
+};
+
+} // end of namespace ::sd::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPaneBase.cxx b/sdext/source/presenter/PresenterPaneBase.cxx
new file mode 100644
index 000000000..aac8d082a
--- /dev/null
+++ b/sdext/source/presenter/PresenterPaneBase.cxx
@@ -0,0 +1,342 @@
+/* -*- 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 "PresenterPaneBase.hxx"
+#include "PresenterController.hxx"
+#include "PresenterPaintManager.hxx"
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/awt/XWindow2.hpp>
+
+using namespace css;
+using namespace css::uno;
+using namespace css::drawing::framework;
+
+namespace sdext::presenter {
+
+//===== PresenterPaneBase =====================================================
+
+PresenterPaneBase::PresenterPaneBase (
+ const Reference<XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterPaneBaseInterfaceBase(m_aMutex),
+ mpPresenterController(rpPresenterController),
+ mxComponentContext(rxContext)
+{
+ if (mpPresenterController)
+ mxPresenterHelper = mpPresenterController->GetPresenterHelper();
+}
+
+PresenterPaneBase::~PresenterPaneBase()
+{
+}
+
+void PresenterPaneBase::disposing()
+{
+ if (mxBorderWindow.is())
+ {
+ mxBorderWindow->removeWindowListener(this);
+ mxBorderWindow->removePaintListener(this);
+ }
+
+ {
+ Reference<XComponent> xComponent (mxContentCanvas, UNO_QUERY);
+ mxContentCanvas = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ {
+ Reference<XComponent> xComponent = mxContentWindow;
+ mxContentWindow = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ {
+ Reference<XComponent> xComponent (mxBorderCanvas, UNO_QUERY);
+ mxBorderCanvas = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ {
+ Reference<XComponent> xComponent = mxBorderWindow;
+ mxBorderWindow = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ mxComponentContext = nullptr;
+}
+
+void PresenterPaneBase::SetTitle (const OUString& rsTitle)
+{
+ msTitle = rsTitle;
+
+ OSL_ASSERT(mpPresenterController);
+ OSL_ASSERT(mpPresenterController->GetPaintManager() != nullptr);
+
+ mpPresenterController->GetPaintManager()->Invalidate(mxBorderWindow);
+}
+
+const OUString& PresenterPaneBase::GetTitle() const
+{
+ return msTitle;
+}
+
+const Reference<drawing::framework::XPaneBorderPainter>&
+ PresenterPaneBase::GetPaneBorderPainter() const
+{
+ return mxBorderPainter;
+}
+
+//----- XInitialization -------------------------------------------------------
+
+void SAL_CALL PresenterPaneBase::initialize (const Sequence<Any>& rArguments)
+{
+ ThrowIfDisposed();
+
+ if ( ! mxComponentContext.is())
+ {
+ throw RuntimeException(
+ "PresenterSpritePane: missing component context",
+ static_cast<XWeak*>(this));
+ }
+
+ if (rArguments.getLength() != 5 && rArguments.getLength() != 6)
+ {
+ throw RuntimeException(
+ "PresenterSpritePane: invalid number of arguments",
+ static_cast<XWeak*>(this));
+ }
+
+ try
+ {
+ // Get the resource id from the first argument.
+ if ( ! (rArguments[0] >>= mxPaneId))
+ {
+ throw lang::IllegalArgumentException(
+ "PresenterPane: invalid pane id",
+ static_cast<XWeak*>(this),
+ 0);
+ }
+
+ if ( ! (rArguments[1] >>= mxParentWindow))
+ {
+ throw lang::IllegalArgumentException(
+ "PresenterPane: invalid parent window",
+ static_cast<XWeak*>(this),
+ 1);
+ }
+
+ Reference<rendering::XSpriteCanvas> xParentCanvas;
+ if ( ! (rArguments[2] >>= xParentCanvas))
+ {
+ throw lang::IllegalArgumentException(
+ "PresenterPane: invalid parent canvas",
+ static_cast<XWeak*>(this),
+ 2);
+ }
+
+ if ( ! (rArguments[3] >>= msTitle))
+ {
+ throw lang::IllegalArgumentException(
+ "PresenterPane: invalid title",
+ static_cast<XWeak*>(this),
+ 3);
+ }
+
+ if ( ! (rArguments[4] >>= mxBorderPainter))
+ {
+ throw lang::IllegalArgumentException(
+ "PresenterPane: invalid border painter",
+ static_cast<XWeak*>(this),
+ 4);
+ }
+
+ bool bIsWindowVisibleOnCreation (true);
+ if (rArguments.getLength()>5 && ! (rArguments[5] >>= bIsWindowVisibleOnCreation))
+ {
+ throw lang::IllegalArgumentException(
+ "PresenterPane: invalid window visibility flag",
+ static_cast<XWeak*>(this),
+ 5);
+ }
+
+ CreateWindows(bIsWindowVisibleOnCreation);
+
+ if (mxBorderWindow.is())
+ {
+ mxBorderWindow->addWindowListener(this);
+ mxBorderWindow->addPaintListener(this);
+ }
+
+ CreateCanvases(xParentCanvas);
+
+ // Raise new windows.
+ ToTop();
+ }
+ catch (Exception&)
+ {
+ mxContentWindow = nullptr;
+ mxComponentContext = nullptr;
+ throw;
+ }
+}
+
+//----- XResourceId -----------------------------------------------------------
+
+Reference<XResourceId> SAL_CALL PresenterPaneBase::getResourceId()
+{
+ ThrowIfDisposed();
+ return mxPaneId;
+}
+
+sal_Bool SAL_CALL PresenterPaneBase::isAnchorOnly()
+{
+ return true;
+}
+
+//----- XWindowListener -------------------------------------------------------
+
+void SAL_CALL PresenterPaneBase::windowResized (const awt::WindowEvent&)
+{
+ ThrowIfDisposed();
+}
+
+void SAL_CALL PresenterPaneBase::windowMoved (const awt::WindowEvent&)
+{
+ ThrowIfDisposed();
+}
+
+void SAL_CALL PresenterPaneBase::windowShown (const lang::EventObject&)
+{
+ ThrowIfDisposed();
+}
+
+void SAL_CALL PresenterPaneBase::windowHidden (const lang::EventObject&)
+{
+ ThrowIfDisposed();
+}
+
+//----- lang::XEventListener --------------------------------------------------
+
+void SAL_CALL PresenterPaneBase::disposing (const lang::EventObject& rEvent)
+{
+ if (rEvent.Source == mxBorderWindow)
+ {
+ mxBorderWindow = nullptr;
+ }
+}
+
+
+void PresenterPaneBase::CreateWindows (
+ const bool bIsWindowVisibleOnCreation)
+{
+ if (!(mxPresenterHelper.is() && mxParentWindow.is()))
+ return;
+
+ mxBorderWindow = mxPresenterHelper->createWindow(
+ mxParentWindow,
+ false,
+ bIsWindowVisibleOnCreation,
+ false,
+ false);
+ mxContentWindow = mxPresenterHelper->createWindow(
+ mxBorderWindow,
+ false,
+ bIsWindowVisibleOnCreation,
+ false,
+ false);
+}
+
+const Reference<awt::XWindow>& PresenterPaneBase::GetBorderWindow() const
+{
+ return mxBorderWindow;
+}
+
+void PresenterPaneBase::ToTop()
+{
+ if (mxPresenterHelper.is())
+ mxPresenterHelper->toTop(mxContentWindow);
+}
+
+void PresenterPaneBase::PaintBorder (const awt::Rectangle& rUpdateBox)
+{
+ OSL_ASSERT(mxPaneId.is());
+
+ if (!(mxBorderPainter.is() && mxBorderWindow.is() && mxBorderCanvas.is()))
+ return;
+
+ awt::Rectangle aBorderBox (mxBorderWindow->getPosSize());
+ awt::Rectangle aLocalBorderBox (0,0, aBorderBox.Width, aBorderBox.Height);
+
+ //TODO: paint border background?
+
+ mxBorderPainter->paintBorder(
+ mxPaneId->getResourceURL(),
+ mxBorderCanvas,
+ aLocalBorderBox,
+ rUpdateBox,
+ msTitle);
+}
+
+void PresenterPaneBase::LayoutContextWindow()
+{
+ OSL_ASSERT(mxPaneId.is());
+ OSL_ASSERT(mxBorderWindow.is());
+ OSL_ASSERT(mxContentWindow.is());
+ if (!(mxBorderPainter.is() && mxPaneId.is() && mxBorderWindow.is() && mxContentWindow.is()))
+ return;
+
+ const awt::Rectangle aBorderBox (mxBorderWindow->getPosSize());
+ const awt::Rectangle aInnerBox (mxBorderPainter->removeBorder(
+ mxPaneId->getResourceURL(),
+ aBorderBox,
+ drawing::framework::BorderType_TOTAL_BORDER));
+ mxContentWindow->setPosSize(
+ aInnerBox.X - aBorderBox.X,
+ aInnerBox.Y - aBorderBox.Y,
+ aInnerBox.Width,
+ aInnerBox.Height,
+ awt::PosSize::POSSIZE);
+}
+
+bool PresenterPaneBase::IsVisible() const
+{
+ Reference<awt::XWindow2> xWindow2 (mxBorderPainter, UNO_QUERY);
+ if (xWindow2.is())
+ return xWindow2->isVisible();
+
+ return false;
+}
+
+void PresenterPaneBase::ThrowIfDisposed()
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterPane object has already been disposed",
+ static_cast<uno::XWeak*>(this));
+ }
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPaneBase.hxx b/sdext/source/presenter/PresenterPaneBase.hxx
new file mode 100644
index 000000000..6df93af3e
--- /dev/null
+++ b/sdext/source/presenter/PresenterPaneBase.hxx
@@ -0,0 +1,128 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPANEBASE_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPANEBASE_HXX
+
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/drawing/XPresenterHelper.hpp>
+#include <com/sun/star/drawing/framework/XPane.hpp>
+#include <com/sun/star/drawing/framework/XPaneBorderPainter.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <rtl/ref.hxx>
+
+
+namespace sdext::presenter {
+
+class PresenterController;
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::drawing::framework::XPane,
+ css::lang::XInitialization,
+ css::awt::XWindowListener,
+ css::awt::XPaintListener
+> PresenterPaneBaseInterfaceBase;
+
+/** Base class of the panes used by the presenter screen. Pane objects are
+ stored in the PresenterPaneContainer. Sizes and positions are
+ controlled by the PresenterWindowManager. Interactive positioning and
+ resizing is managed by the PresenterPaneBorderManager. Borders around
+ panes are painted by the PresenterPaneBorderPainter.
+*/
+class PresenterPaneBase
+ : protected ::cppu::BaseMutex,
+ public PresenterPaneBaseInterfaceBase
+{
+public:
+ PresenterPaneBase (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterPaneBase() override;
+ PresenterPaneBase(const PresenterPaneBase&) = delete;
+ PresenterPaneBase& operator=(const PresenterPaneBase&) = delete;
+
+ virtual void SAL_CALL disposing() override;
+
+ const css::uno::Reference<css::awt::XWindow>& GetBorderWindow() const;
+ void SetTitle (const OUString& rsTitle);
+ const OUString& GetTitle() const;
+ const css::uno::Reference<css::drawing::framework::XPaneBorderPainter>& GetPaneBorderPainter() const;
+
+ // XInitialization
+
+ virtual void SAL_CALL initialize (const css::uno::Sequence<css::uno::Any>& rArguments) override;
+
+ // XResourceId
+
+ virtual css::uno::Reference<css::drawing::framework::XResourceId> SAL_CALL getResourceId() override;
+
+ virtual sal_Bool SAL_CALL isAnchorOnly() override;
+
+ // XWindowListener
+
+ virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override;
+
+ // lang::XEventListener
+
+ virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override;
+
+protected:
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ css::uno::Reference<css::awt::XWindow> mxParentWindow;
+ css::uno::Reference<css::awt::XWindow> mxBorderWindow;
+ css::uno::Reference<css::rendering::XCanvas> mxBorderCanvas;
+ css::uno::Reference<css::awt::XWindow> mxContentWindow;
+ css::uno::Reference<css::rendering::XCanvas> mxContentCanvas;
+ css::uno::Reference<css::drawing::framework::XResourceId> mxPaneId;
+ css::uno::Reference<css::drawing::framework::XPaneBorderPainter> mxBorderPainter;
+ css::uno::Reference<css::drawing::XPresenterHelper> mxPresenterHelper;
+ OUString msTitle;
+ css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+
+ virtual void CreateCanvases (
+ const css::uno::Reference<css::rendering::XSpriteCanvas>& rxParentCanvas) = 0;
+
+ void CreateWindows (
+ const bool bIsWindowVisibleOnCreation);
+ void PaintBorder (const css::awt::Rectangle& rUpdateRectangle);
+ void ToTop();
+ void LayoutContextWindow();
+ bool IsVisible() const;
+
+ /** @throws css::lang::DisposedException when the object has already been
+ disposed.
+ */
+ void ThrowIfDisposed();
+};
+
+} // end of namespace ::sd::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPaneBorderPainter.cxx b/sdext/source/presenter/PresenterPaneBorderPainter.cxx
new file mode 100644
index 000000000..6c0198c7e
--- /dev/null
+++ b/sdext/source/presenter/PresenterPaneBorderPainter.cxx
@@ -0,0 +1,882 @@
+/* -*- 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 "PresenterPaneBorderPainter.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterGeometryHelper.hxx"
+#include "PresenterTheme.hxx"
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/drawing/XPresenterHelper.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/FillRule.hpp>
+#include <com/sun/star/rendering/TextDirection.hpp>
+#include <com/sun/star/rendering/XSpriteCanvas.hpp>
+#include <map>
+#include <memory>
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sdext::presenter {
+
+namespace {
+ class BorderSize
+ {
+ public:
+ BorderSize();
+ sal_Int32 mnLeft;
+ sal_Int32 mnTop;
+ sal_Int32 mnRight;
+ sal_Int32 mnBottom;
+ };
+
+ class RendererPaneStyle
+ {
+ public:
+ RendererPaneStyle (
+ const std::shared_ptr<PresenterTheme>& rpTheme,
+ const OUString& rsStyleName);
+
+ awt::Rectangle AddBorder (
+ const awt::Rectangle& rBox,
+ drawing::framework::BorderType eBorderType) const;
+ awt::Rectangle RemoveBorder (
+ const awt::Rectangle& rBox,
+ drawing::framework::BorderType eBorderType) const;
+ Reference<rendering::XCanvasFont> GetFont (
+ const Reference<rendering::XCanvas>& rxCanvas) const;
+
+ SharedBitmapDescriptor mpTopLeft;
+ SharedBitmapDescriptor mpTop;
+ SharedBitmapDescriptor mpTopRight;
+ SharedBitmapDescriptor mpLeft;
+ SharedBitmapDescriptor mpRight;
+ SharedBitmapDescriptor mpBottomLeft;
+ SharedBitmapDescriptor mpBottom;
+ SharedBitmapDescriptor mpBottomRight;
+ SharedBitmapDescriptor mpBottomCallout;
+ SharedBitmapDescriptor mpEmpty;
+ PresenterTheme::SharedFontDescriptor mpFont;
+ sal_Int32 mnFontXOffset;
+ sal_Int32 mnFontYOffset;
+ enum class Anchor { Left, Right, Center };
+ Anchor meFontAnchor;
+ BorderSize maInnerBorderSize;
+ BorderSize maOuterBorderSize;
+ BorderSize maTotalBorderSize;
+ private:
+ void UpdateBorderSizes();
+ SharedBitmapDescriptor GetBitmap(
+ const std::shared_ptr<PresenterTheme>& rpTheme,
+ const OUString& rsStyleName,
+ const OUString& rsBitmapName);
+ };
+}
+
+class PresenterPaneBorderPainter::Renderer
+{
+public:
+ Renderer (
+ const Reference<XComponentContext>& rxContext,
+ const std::shared_ptr<PresenterTheme>& rpTheme);
+
+ void SetCanvas (const Reference<rendering::XCanvas>& rxCanvas);
+ void PaintBorder (
+ const OUString& rsTitle,
+ const awt::Rectangle& rBBox,
+ const awt::Rectangle& rUpdateBox,
+ const OUString& rsPaneURL);
+ void PaintTitle (
+ const OUString& rsTitle,
+ const std::shared_ptr<RendererPaneStyle>& rpStyle,
+ const awt::Rectangle& rUpdateBox,
+ const awt::Rectangle& rOuterBox,
+ const awt::Rectangle& rInnerBox);
+ void SetupClipping (
+ const awt::Rectangle& rUpdateBox,
+ const awt::Rectangle& rOuterBox,
+ const OUString& rsPaneStyleName);
+ std::shared_ptr<RendererPaneStyle> GetRendererPaneStyle (const OUString& rsResourceURL);
+ void SetCalloutAnchor (
+ const awt::Point& rCalloutAnchor);
+
+private:
+ std::shared_ptr<PresenterTheme> mpTheme;
+ typedef ::std::map<OUString, std::shared_ptr<RendererPaneStyle> > RendererPaneStyleContainer;
+ RendererPaneStyleContainer maRendererPaneStyles;
+ Reference<rendering::XCanvas> mxCanvas;
+ Reference<drawing::XPresenterHelper> mxPresenterHelper;
+ css::rendering::ViewState maViewState;
+ Reference<rendering::XPolyPolygon2D> mxViewStateClip;
+ bool mbHasCallout;
+ awt::Point maCalloutAnchor;
+
+ void PaintBitmap(
+ const awt::Rectangle& rBox,
+ const awt::Rectangle& rUpdateBox,
+ const sal_Int32 nXPosition,
+ const sal_Int32 nYPosition,
+ const sal_Int32 nStartOffset,
+ const sal_Int32 nEndOffset,
+ const bool bExpand,
+ const SharedBitmapDescriptor& rpBitmap);
+};
+
+// ===== PresenterPaneBorderPainter ===========================================
+
+PresenterPaneBorderPainter::PresenterPaneBorderPainter (
+ const Reference<XComponentContext>& rxContext)
+ : PresenterPaneBorderPainterInterfaceBase(m_aMutex),
+ mxContext(rxContext)
+{
+}
+
+PresenterPaneBorderPainter::~PresenterPaneBorderPainter()
+{
+}
+
+//----- XPaneBorderPainter ----------------------------------------------------
+
+awt::Rectangle SAL_CALL PresenterPaneBorderPainter::addBorder (
+ const OUString& rsPaneBorderStyleName,
+ const css::awt::Rectangle& rRectangle,
+ drawing::framework::BorderType eBorderType)
+{
+ ThrowIfDisposed();
+
+ ProvideTheme();
+
+ return AddBorder(rsPaneBorderStyleName, rRectangle, eBorderType);
+}
+
+awt::Rectangle SAL_CALL PresenterPaneBorderPainter::removeBorder (
+ const OUString& rsPaneBorderStyleName,
+ const css::awt::Rectangle& rRectangle,
+ drawing::framework::BorderType eBorderType)
+{
+ ThrowIfDisposed();
+
+ ProvideTheme();
+
+ return RemoveBorder(rsPaneBorderStyleName, rRectangle, eBorderType);
+}
+
+void SAL_CALL PresenterPaneBorderPainter::paintBorder (
+ const OUString& rsPaneBorderStyleName,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rOuterBorderRectangle,
+ const css::awt::Rectangle& rRepaintArea,
+ const OUString& rsTitle)
+{
+ ThrowIfDisposed();
+
+ // Early reject paints completely outside the repaint area.
+ if (rRepaintArea.X >= rOuterBorderRectangle.X+rOuterBorderRectangle.Width
+ || rRepaintArea.Y >= rOuterBorderRectangle.Y+rOuterBorderRectangle.Height
+ || rRepaintArea.X+rRepaintArea.Width <= rOuterBorderRectangle.X
+ || rRepaintArea.Y+rRepaintArea.Height <= rOuterBorderRectangle.Y)
+ {
+ return;
+ }
+ ProvideTheme(rxCanvas);
+
+ if (mpRenderer == nullptr)
+ return;
+
+ mpRenderer->SetCanvas(rxCanvas);
+ mpRenderer->SetupClipping(
+ rRepaintArea,
+ rOuterBorderRectangle,
+ rsPaneBorderStyleName);
+ mpRenderer->PaintBorder(
+ rsTitle,
+ rOuterBorderRectangle,
+ rRepaintArea,
+ rsPaneBorderStyleName);
+}
+
+void SAL_CALL PresenterPaneBorderPainter::paintBorderWithCallout (
+ const OUString& rsPaneBorderStyleName,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rOuterBorderRectangle,
+ const css::awt::Rectangle& rRepaintArea,
+ const OUString& rsTitle,
+ const css::awt::Point& rCalloutAnchor)
+{
+ ThrowIfDisposed();
+
+ // Early reject paints completely outside the repaint area.
+ if (rRepaintArea.X >= rOuterBorderRectangle.X+rOuterBorderRectangle.Width
+ || rRepaintArea.Y >= rOuterBorderRectangle.Y+rOuterBorderRectangle.Height
+ || rRepaintArea.X+rRepaintArea.Width <= rOuterBorderRectangle.X
+ || rRepaintArea.Y+rRepaintArea.Height <= rOuterBorderRectangle.Y)
+ {
+ return;
+ }
+ ProvideTheme(rxCanvas);
+
+ if (mpRenderer == nullptr)
+ return;
+
+ mpRenderer->SetCanvas(rxCanvas);
+ mpRenderer->SetupClipping(
+ rRepaintArea,
+ rOuterBorderRectangle,
+ rsPaneBorderStyleName);
+ mpRenderer->SetCalloutAnchor(rCalloutAnchor);
+ mpRenderer->PaintBorder(
+ rsTitle,
+ rOuterBorderRectangle,
+ rRepaintArea,
+ rsPaneBorderStyleName);
+}
+
+awt::Point SAL_CALL PresenterPaneBorderPainter::getCalloutOffset (
+ const OUString& rsPaneBorderStyleName)
+{
+ ThrowIfDisposed();
+ ProvideTheme();
+ if (mpRenderer != nullptr)
+ {
+ const std::shared_ptr<RendererPaneStyle> pRendererPaneStyle(
+ mpRenderer->GetRendererPaneStyle(rsPaneBorderStyleName));
+ if (pRendererPaneStyle != nullptr && pRendererPaneStyle->mpBottomCallout)
+ {
+ return awt::Point (
+ 0,
+ pRendererPaneStyle->mpBottomCallout->mnHeight
+ - pRendererPaneStyle->mpBottomCallout->mnYHotSpot);
+ }
+ }
+
+ return awt::Point(0,0);
+}
+
+
+bool PresenterPaneBorderPainter::ProvideTheme (const Reference<rendering::XCanvas>& rxCanvas)
+{
+ bool bModified (false);
+
+ if ( ! mxContext.is())
+ return false;
+
+ if (mpTheme != nullptr)
+ {
+ // Check if the theme already has a canvas.
+ if ( ! mpTheme->HasCanvas())
+ {
+ mpTheme->ProvideCanvas(rxCanvas);
+ bModified = true;
+ }
+ }
+ else
+ {
+ mpTheme = std::make_shared<PresenterTheme>(mxContext, rxCanvas);
+ bModified = true;
+ }
+
+ if (bModified)
+ {
+ if (mpRenderer == nullptr)
+ mpRenderer.reset(new Renderer(mxContext, mpTheme));
+ else
+ mpRenderer->SetCanvas(rxCanvas);
+ }
+
+ return bModified;
+}
+
+void PresenterPaneBorderPainter::ProvideTheme()
+{
+ if (mpTheme == nullptr)
+ {
+ // Create a theme without bitmaps (no canvas => no bitmaps).
+ ProvideTheme(nullptr);
+ }
+ // When there already is a theme then without a canvas we can not
+ // add anything new.
+}
+
+void PresenterPaneBorderPainter::SetTheme (const std::shared_ptr<PresenterTheme>& rpTheme)
+{
+ mpTheme = rpTheme;
+ if (mpRenderer == nullptr)
+ mpRenderer.reset(new Renderer(mxContext, mpTheme));
+}
+
+awt::Rectangle PresenterPaneBorderPainter::AddBorder (
+ const OUString& rsPaneURL,
+ const awt::Rectangle& rInnerBox,
+ const css::drawing::framework::BorderType eBorderType) const
+{
+ if (mpRenderer != nullptr)
+ {
+ const std::shared_ptr<RendererPaneStyle> pRendererPaneStyle(mpRenderer->GetRendererPaneStyle(rsPaneURL));
+ if (pRendererPaneStyle != nullptr)
+ return pRendererPaneStyle->AddBorder(rInnerBox, eBorderType);
+ }
+ return rInnerBox;
+}
+
+awt::Rectangle PresenterPaneBorderPainter::RemoveBorder (
+ const OUString& rsPaneURL,
+ const css::awt::Rectangle& rOuterBox,
+ const css::drawing::framework::BorderType eBorderType) const
+{
+ if (mpRenderer != nullptr)
+ {
+ const std::shared_ptr<RendererPaneStyle> pRendererPaneStyle(mpRenderer->GetRendererPaneStyle(rsPaneURL));
+ if (pRendererPaneStyle != nullptr)
+ return pRendererPaneStyle->RemoveBorder(rOuterBox, eBorderType);
+ }
+ return rOuterBox;
+}
+
+void PresenterPaneBorderPainter::ThrowIfDisposed() const
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterPaneBorderPainter object has already been disposed",
+ const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
+ }
+}
+
+//===== PresenterPaneBorderPainter::Renderer =====================================
+
+PresenterPaneBorderPainter::Renderer::Renderer (
+ const Reference<XComponentContext>& rxContext,
+ const std::shared_ptr<PresenterTheme>& rpTheme)
+ : mpTheme(rpTheme),
+ maViewState(geometry::AffineMatrix2D(1,0,0, 0,1,0), nullptr),
+ mbHasCallout(false)
+{
+ Reference<lang::XMultiComponentFactory> xFactory (rxContext->getServiceManager());
+ if (xFactory.is())
+ {
+ mxPresenterHelper.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.comp.Draw.PresenterHelper",
+ rxContext),
+ UNO_QUERY_THROW);
+ }
+}
+
+void PresenterPaneBorderPainter::Renderer::SetCanvas (const Reference<rendering::XCanvas>& rxCanvas)
+{
+ if (mxCanvas != rxCanvas)
+ {
+ mxCanvas = rxCanvas;
+ }
+}
+
+void PresenterPaneBorderPainter::Renderer::PaintBorder (
+ const OUString& rsTitle,
+ const awt::Rectangle& rBBox,
+ const awt::Rectangle& rUpdateBox,
+ const OUString& rsPaneURL)
+{
+ if ( ! mxCanvas.is())
+ return;
+
+ // Create the outer and inner border of the, ahm, border.
+ std::shared_ptr<RendererPaneStyle> pStyle (GetRendererPaneStyle(rsPaneURL));
+ if (pStyle == nullptr)
+ return;
+
+ awt::Rectangle aOuterBox (rBBox);
+ awt::Rectangle aCenterBox (
+ pStyle->RemoveBorder(aOuterBox, drawing::framework::BorderType_OUTER_BORDER));
+ awt::Rectangle aInnerBox (
+ pStyle->RemoveBorder(aOuterBox, drawing::framework::BorderType_TOTAL_BORDER));
+
+ // Prepare references for all used bitmaps.
+ SharedBitmapDescriptor pTop (pStyle->mpTop);
+ SharedBitmapDescriptor pTopLeft (pStyle->mpTopLeft);
+ SharedBitmapDescriptor pTopRight (pStyle->mpTopRight);
+ SharedBitmapDescriptor pLeft (pStyle->mpLeft);
+ SharedBitmapDescriptor pRight (pStyle->mpRight);
+ SharedBitmapDescriptor pBottomLeft (pStyle->mpBottomLeft);
+ SharedBitmapDescriptor pBottomRight (pStyle->mpBottomRight);
+ SharedBitmapDescriptor pBottom (pStyle->mpBottom);
+
+ // Paint the sides.
+ PaintBitmap(aCenterBox, rUpdateBox, 0,-1,
+ pTopLeft->mnXOffset, pTopRight->mnXOffset, true, pTop);
+ PaintBitmap(aCenterBox, rUpdateBox, -1,0,
+ pTopLeft->mnYOffset, pBottomLeft->mnYOffset, true, pLeft);
+ PaintBitmap(aCenterBox, rUpdateBox, +1,0,
+ pTopRight->mnYOffset, pBottomRight->mnYOffset, true, pRight);
+ if (mbHasCallout && pStyle->mpBottomCallout->GetNormalBitmap().is())
+ {
+ const sal_Int32 nCalloutWidth (pStyle->mpBottomCallout->mnWidth);
+ sal_Int32 nCalloutX (maCalloutAnchor.X - pStyle->mpBottomCallout->mnXHotSpot
+ - (aCenterBox.X - aOuterBox.X));
+ if (nCalloutX < pBottomLeft->mnXOffset + aCenterBox.X)
+ nCalloutX = pBottomLeft->mnXOffset + aCenterBox.X;
+ if (nCalloutX > pBottomRight->mnXOffset + aCenterBox.X + aCenterBox.Width)
+ nCalloutX = pBottomRight->mnXOffset + aCenterBox.X + aCenterBox.Width;
+ // Paint bottom callout.
+ PaintBitmap(aCenterBox, rUpdateBox, 0,+1, nCalloutX,0, false, pStyle->mpBottomCallout);
+ // Paint regular bottom bitmap left and right.
+ PaintBitmap(aCenterBox, rUpdateBox, 0,+1,
+ pBottomLeft->mnXOffset, nCalloutX-aCenterBox.Width, true, pBottom);
+ PaintBitmap(aCenterBox, rUpdateBox, 0,+1,
+ nCalloutX+nCalloutWidth, pBottomRight->mnXOffset, true, pBottom);
+ }
+ else
+ {
+ // Stretch the bottom bitmap over the full width.
+ PaintBitmap(aCenterBox, rUpdateBox, 0,+1,
+ pBottomLeft->mnXOffset, pBottomRight->mnXOffset, true, pBottom);
+ }
+
+ // Paint the corners.
+ PaintBitmap(aCenterBox, rUpdateBox, -1,-1, 0,0, false, pTopLeft);
+ PaintBitmap(aCenterBox, rUpdateBox, +1,-1, 0,0, false, pTopRight);
+ PaintBitmap(aCenterBox, rUpdateBox, -1,+1, 0,0, false, pBottomLeft);
+ PaintBitmap(aCenterBox, rUpdateBox, +1,+1, 0,0, false, pBottomRight);
+
+ // Paint the title.
+ PaintTitle(rsTitle, pStyle, rUpdateBox, aOuterBox, aInnerBox);
+
+ // In a double buffering environment request to make the changes visible.
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(false);
+}
+
+void PresenterPaneBorderPainter::Renderer::PaintTitle (
+ const OUString& rsTitle,
+ const std::shared_ptr<RendererPaneStyle>& rpStyle,
+ const awt::Rectangle& rUpdateBox,
+ const awt::Rectangle& rOuterBox,
+ const awt::Rectangle& rInnerBox)
+{
+ if ( ! mxCanvas.is())
+ return;
+
+ if (rsTitle.isEmpty())
+ return;
+
+ Reference<rendering::XCanvasFont> xFont (rpStyle->GetFont(mxCanvas));
+ if ( ! xFont.is())
+ return;
+
+ rendering::StringContext aContext (
+ rsTitle,
+ 0,
+ rsTitle.getLength());
+ Reference<rendering::XTextLayout> xLayout (xFont->createTextLayout(
+ aContext,
+ rendering::TextDirection::WEAK_LEFT_TO_RIGHT,
+ 0));
+ if ( ! xLayout.is())
+ return;
+
+ /// this is responsible of the texts above the slide windows
+ geometry::RealRectangle2D aBox (xLayout->queryTextBounds());
+ const double nTextHeight = aBox.Y2 - aBox.Y1;
+ const double nTextWidth = aBox.X1 + aBox.X2;
+ const sal_Int32 nTitleBarHeight = rInnerBox.Y - rOuterBox.Y - 1;
+ double nY = rOuterBox.Y + (nTitleBarHeight - nTextHeight) / 2 - aBox.Y1;
+ if (nY >= rInnerBox.Y)
+ nY = rInnerBox.Y - 1;
+ double nX;
+ switch (rpStyle->meFontAnchor)
+ {
+ case RendererPaneStyle::Anchor::Left:
+ nX = rInnerBox.X;
+ break;
+ case RendererPaneStyle::Anchor::Right:
+ nX = rInnerBox.X + rInnerBox.Width - nTextWidth;
+ break;
+ default: // RendererPaneStyle::Anchor::Center
+ nX = rInnerBox.X + (rInnerBox.Width - nTextWidth)/2;
+ break;
+ }
+ nX += rpStyle->mnFontXOffset;
+ nY += rpStyle->mnFontYOffset;
+
+ if (rUpdateBox.X >= nX+nTextWidth
+ || rUpdateBox.Y >= nY+nTextHeight
+ || rUpdateBox.X+rUpdateBox.Width <= nX
+ || rUpdateBox.Y+rUpdateBox.Height <= nY)
+ {
+ return;
+ }
+
+ rendering::RenderState aRenderState(
+ geometry::AffineMatrix2D(1,0,nX, 0,1,nY),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ PresenterCanvasHelper::SetDeviceColor(
+ aRenderState,
+ rpStyle->mpFont->mnColor);
+
+ mxCanvas->drawTextLayout (
+ xLayout,
+ maViewState,
+ aRenderState);
+}
+
+std::shared_ptr<RendererPaneStyle>
+ PresenterPaneBorderPainter::Renderer::GetRendererPaneStyle (const OUString& rsResourceURL)
+{
+ OSL_ASSERT(mpTheme != nullptr);
+
+ RendererPaneStyleContainer::const_iterator iStyle (maRendererPaneStyles.find(rsResourceURL));
+ if (iStyle == maRendererPaneStyles.end())
+ {
+ OUString sPaneStyleName ("DefaultRendererPaneStyle");
+
+ // Get pane layout name for resource URL.
+ const OUString sStyleName (mpTheme->GetStyleName(rsResourceURL));
+ if (!sStyleName.isEmpty())
+ sPaneStyleName = sStyleName;
+
+ // Create a new pane style object and initialize it with bitmaps.
+ auto pStyle = std::make_shared<RendererPaneStyle>(mpTheme,sPaneStyleName);
+ iStyle = maRendererPaneStyles.emplace(rsResourceURL, pStyle).first;
+ }
+ if (iStyle != maRendererPaneStyles.end())
+ return iStyle->second;
+ else
+ return std::shared_ptr<RendererPaneStyle>();
+}
+
+void PresenterPaneBorderPainter::Renderer::SetCalloutAnchor (
+ const awt::Point& rCalloutAnchor)
+{
+ mbHasCallout = true;
+ maCalloutAnchor = rCalloutAnchor;
+}
+
+void PresenterPaneBorderPainter::Renderer::PaintBitmap(
+ const awt::Rectangle& rBox,
+ const awt::Rectangle& rUpdateBox,
+ const sal_Int32 nXPosition,
+ const sal_Int32 nYPosition,
+ const sal_Int32 nStartOffset,
+ const sal_Int32 nEndOffset,
+ const bool bExpand,
+ const SharedBitmapDescriptor& rpBitmap)
+{
+ bool bUseCanvas (mxCanvas.is());
+ if ( ! bUseCanvas)
+ return;
+
+ if (rpBitmap->mnWidth<=0 || rpBitmap->mnHeight<=0)
+ return;
+
+ Reference<rendering::XBitmap> xBitmap = rpBitmap->GetNormalBitmap();
+ if ( ! xBitmap.is())
+ return;
+
+ // Calculate position, and for side bitmaps, the size.
+ sal_Int32 nX = 0;
+ sal_Int32 nY = 0;
+ sal_Int32 nW = rpBitmap->mnWidth;
+ sal_Int32 nH = rpBitmap->mnHeight;
+ if (nXPosition < 0)
+ {
+ nX = rBox.X - rpBitmap->mnWidth + rpBitmap->mnXOffset;
+ }
+ else if (nXPosition > 0)
+ {
+ nX = rBox.X + rBox.Width + rpBitmap->mnXOffset;
+ }
+ else
+ {
+ nX = rBox.X + nStartOffset;
+ if (bExpand)
+ nW = rBox.Width - nStartOffset + nEndOffset;
+ }
+
+ if (nYPosition < 0)
+ {
+ nY = rBox.Y - rpBitmap->mnHeight + rpBitmap->mnYOffset;
+ }
+ else if (nYPosition > 0)
+ {
+ nY = rBox.Y + rBox.Height + rpBitmap->mnYOffset;
+ }
+ else
+ {
+ nY = rBox.Y + nStartOffset;
+ if (bExpand)
+ nH = rBox.Height - nStartOffset + nEndOffset;
+ }
+
+ // Do not paint when bitmap area does not intersect with update box.
+ if (nX >= rUpdateBox.X + rUpdateBox.Width
+ || nX+nW <= rUpdateBox.X
+ || nY >= rUpdateBox.Y + rUpdateBox.Height
+ || nY+nH <= rUpdateBox.Y)
+ {
+ return;
+ }
+
+ /*
+ Reference<rendering::XBitmap> xMaskedBitmap (
+ PresenterBitmapHelper::FillMaskedWithColor (
+ mxCanvas,
+ Reference<rendering::XIntegerBitmap>(xBitmap, UNO_QUERY),
+ rBitmap.mxMaskBitmap,
+ 0x00ff0000,
+ rBackgroundBitmap.maReplacementColor));
+ if (xMaskedBitmap.is())
+ xBitmap = xMaskedBitmap;
+ else if (rBitmap.mxMaskBitmap.is() && mxPresenterHelper.is())
+ {
+ const static sal_Int32 nOutsideMaskColor (0x00ff0000);
+ Reference<rendering::XIntegerBitmap> xMask (
+ mxPresenterHelper->createMask(
+ mxCanvas,
+ rBitmap.mxMaskBitmap,
+ nOutsideMaskColor,
+ false));
+ xBitmap = mxPresenterHelper->applyBitmapMaskWithColor(
+ mxCanvas,
+ Reference<rendering::XIntegerBitmap>(xBitmap, UNO_QUERY),
+ xMask,
+ rBackgroundBitmap.maReplacementColor);
+ }
+ */
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(
+ double(nW)/rpBitmap->mnWidth, 0, nX,
+ 0, double(nH)/rpBitmap->mnHeight, nY),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::OVER);
+
+ if (xBitmap.is())
+ mxCanvas->drawBitmap(
+ xBitmap,
+ maViewState,
+ aRenderState);
+}
+
+void PresenterPaneBorderPainter::Renderer::SetupClipping (
+ const awt::Rectangle& rUpdateBox,
+ const awt::Rectangle& rOuterBox,
+ const OUString& rsPaneStyleName)
+{
+ mxViewStateClip = nullptr;
+ maViewState.Clip = nullptr;
+
+ if ( ! mxCanvas.is())
+ return;
+
+ std::shared_ptr<RendererPaneStyle> pStyle (GetRendererPaneStyle(rsPaneStyleName));
+ if (pStyle == nullptr)
+ {
+ mxViewStateClip = PresenterGeometryHelper::CreatePolygon(
+ rUpdateBox,
+ mxCanvas->getDevice());
+ }
+ else
+ {
+ awt::Rectangle aInnerBox (
+ pStyle->RemoveBorder(rOuterBox, drawing::framework::BorderType_TOTAL_BORDER));
+ ::std::vector<awt::Rectangle> aRectangles
+ {
+ PresenterGeometryHelper::Intersection(rUpdateBox, rOuterBox),
+ PresenterGeometryHelper::Intersection(rUpdateBox, aInnerBox)
+ };
+ mxViewStateClip = PresenterGeometryHelper::CreatePolygon(
+ aRectangles,
+ mxCanvas->getDevice());
+ if (mxViewStateClip.is())
+ mxViewStateClip->setFillRule(rendering::FillRule_EVEN_ODD);
+ }
+ maViewState.Clip = mxViewStateClip;
+}
+
+namespace {
+
+//===== BorderSize ============================================================
+
+BorderSize::BorderSize()
+ : mnLeft(0),
+ mnTop(0),
+ mnRight(0),
+ mnBottom(0)
+{
+}
+
+//===== RendererPaneStyle ============================================================
+
+RendererPaneStyle::RendererPaneStyle (
+ const std::shared_ptr<PresenterTheme>& rpTheme,
+ const OUString& rsStyleName)
+ : mpEmpty(std::make_shared<PresenterBitmapDescriptor>()),
+ mnFontXOffset(0),
+ mnFontYOffset(0),
+ meFontAnchor(Anchor::Center)
+{
+ if (rpTheme == nullptr)
+ return;
+
+ mpTopLeft = GetBitmap(rpTheme, rsStyleName, "TopLeft");
+ mpTop = GetBitmap(rpTheme, rsStyleName, "Top");
+ mpTopRight = GetBitmap(rpTheme, rsStyleName, "TopRight");
+ mpLeft = GetBitmap(rpTheme, rsStyleName,"Left");
+ mpRight = GetBitmap(rpTheme, rsStyleName, "Right");
+ mpBottomLeft = GetBitmap(rpTheme, rsStyleName, "BottomLeft");
+ mpBottom = GetBitmap(rpTheme, rsStyleName, "Bottom");
+ mpBottomRight = GetBitmap(rpTheme, rsStyleName, "BottomRight");
+ mpBottomCallout = GetBitmap(rpTheme, rsStyleName, "BottomCallout");
+
+ // Get font description.
+ mpFont = rpTheme->GetFont(rsStyleName);
+
+ OUString sAnchor ("Left");
+ if (mpFont)
+ {
+ sAnchor = mpFont->msAnchor;
+ mnFontXOffset = mpFont->mnXOffset;
+ mnFontYOffset = mpFont->mnYOffset;
+ }
+
+ if ( sAnchor == "Left" )
+ meFontAnchor = Anchor::Left;
+ else if ( sAnchor == "Right" )
+ meFontAnchor = Anchor::Right;
+ else
+ meFontAnchor = Anchor::Center;
+
+ // Get border sizes.
+ try
+ {
+ ::std::vector<sal_Int32> aInnerBorder (rpTheme->GetBorderSize(rsStyleName, false));
+ OSL_ASSERT(aInnerBorder.size()==4);
+ maInnerBorderSize.mnLeft = aInnerBorder[0];
+ maInnerBorderSize.mnTop = aInnerBorder[1];
+ maInnerBorderSize.mnRight = aInnerBorder[2];
+ maInnerBorderSize.mnBottom = aInnerBorder[3];
+
+ ::std::vector<sal_Int32> aOuterBorder (rpTheme->GetBorderSize(rsStyleName, true));
+ OSL_ASSERT(aOuterBorder.size()==4);
+ maOuterBorderSize.mnLeft = aOuterBorder[0];
+ maOuterBorderSize.mnTop = aOuterBorder[1];
+ maOuterBorderSize.mnRight = aOuterBorder[2];
+ maOuterBorderSize.mnBottom = aOuterBorder[3];
+ }
+ catch(beans::UnknownPropertyException&)
+ {
+ OSL_ASSERT(false);
+ }
+
+ UpdateBorderSizes();
+}
+
+awt::Rectangle RendererPaneStyle::AddBorder (
+ const awt::Rectangle& rBox,
+ const drawing::framework::BorderType eBorderType) const
+{
+ const BorderSize* pBorderSize = nullptr;
+ switch (eBorderType)
+ {
+ case drawing::framework::BorderType_INNER_BORDER:
+ pBorderSize = &maInnerBorderSize;
+ break;
+ case drawing::framework::BorderType_OUTER_BORDER:
+ pBorderSize = &maOuterBorderSize;
+ break;
+ case drawing::framework::BorderType_TOTAL_BORDER:
+ pBorderSize = &maTotalBorderSize;
+ break;
+ default:
+ return rBox;
+ }
+ return awt::Rectangle (
+ rBox.X - pBorderSize->mnLeft,
+ rBox.Y - pBorderSize->mnTop,
+ rBox.Width + pBorderSize->mnLeft + pBorderSize->mnRight,
+ rBox.Height + pBorderSize->mnTop + pBorderSize->mnBottom);
+}
+
+awt::Rectangle RendererPaneStyle::RemoveBorder (
+ const awt::Rectangle& rBox,
+ const css::drawing::framework::BorderType eBorderType) const
+{
+ const BorderSize* pBorderSize = nullptr;
+ switch (eBorderType)
+ {
+ case drawing::framework::BorderType_INNER_BORDER:
+ pBorderSize = &maInnerBorderSize;
+ break;
+ case drawing::framework::BorderType_OUTER_BORDER:
+ pBorderSize = &maOuterBorderSize;
+ break;
+ case drawing::framework::BorderType_TOTAL_BORDER:
+ pBorderSize = &maTotalBorderSize;
+ break;
+ default:
+ return rBox;
+ }
+ return awt::Rectangle (
+ rBox.X + pBorderSize->mnLeft,
+ rBox.Y + pBorderSize->mnTop,
+ rBox.Width - pBorderSize->mnLeft - pBorderSize->mnRight,
+ rBox.Height - pBorderSize->mnTop - pBorderSize->mnBottom);
+}
+
+Reference<rendering::XCanvasFont> RendererPaneStyle::GetFont (
+ const Reference<rendering::XCanvas>& rxCanvas) const
+{
+ if (mpFont)
+ {
+ mpFont->PrepareFont(rxCanvas);
+ return mpFont->mxFont;
+ }
+ return Reference<rendering::XCanvasFont>();
+}
+
+void RendererPaneStyle::UpdateBorderSizes()
+{
+ maTotalBorderSize.mnLeft = maInnerBorderSize.mnLeft + maOuterBorderSize.mnLeft;
+ maTotalBorderSize.mnTop = maInnerBorderSize.mnTop + maOuterBorderSize.mnTop;
+ maTotalBorderSize.mnRight = maInnerBorderSize.mnRight + maOuterBorderSize.mnRight;
+ maTotalBorderSize.mnBottom = maInnerBorderSize.mnBottom + maOuterBorderSize.mnBottom;
+}
+
+SharedBitmapDescriptor RendererPaneStyle::GetBitmap(
+ const std::shared_ptr<PresenterTheme>& rpTheme,
+ const OUString& rsStyleName,
+ const OUString& rsBitmapName)
+{
+ SharedBitmapDescriptor pDescriptor (rpTheme->GetBitmap(rsStyleName, rsBitmapName));
+ if (pDescriptor)
+ return pDescriptor;
+ else
+ return mpEmpty;
+}
+
+} // end of anonymous namespace
+
+} // end of namespace ::sd::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPaneBorderPainter.hxx b/sdext/source/presenter/PresenterPaneBorderPainter.hxx
new file mode 100644
index 000000000..b7b9c0de1
--- /dev/null
+++ b/sdext/source/presenter/PresenterPaneBorderPainter.hxx
@@ -0,0 +1,138 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPANEBORDERPAINTER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPANEBORDERPAINTER_HXX
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/drawing/framework/XPaneBorderPainter.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <memory>
+
+namespace sdext::presenter {
+
+class PresenterPane;
+class PresenterTheme;
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::drawing::framework::XPaneBorderPainter
+> PresenterPaneBorderPainterInterfaceBase;
+
+/** This class is responsible for painting window borders of PresenterPane
+ objects.
+*/
+class PresenterPaneBorderPainter
+ : protected ::cppu::BaseMutex,
+ public PresenterPaneBorderPainterInterfaceBase
+{
+public:
+ explicit PresenterPaneBorderPainter (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext);
+ virtual ~PresenterPaneBorderPainter() override;
+ PresenterPaneBorderPainter(const PresenterPaneBorderPainter&) = delete;
+ PresenterPaneBorderPainter& operator=(const PresenterPaneBorderPainter&) = delete;
+
+ /** Transform the bounding box of the window content to the outer
+ bounding box of the border that is painted around it.
+ @param rsPaneURL
+ Specifies the pane style that is used to determine the border sizes.
+ @param rInnerBox
+ The rectangle of the inner window content.
+ */
+ css::awt::Rectangle AddBorder (
+ const OUString& rsPaneURL,
+ const css::awt::Rectangle& rInnerBox,
+ const css::drawing::framework::BorderType eBorderType) const;
+
+ /** Transform the outer bounding box of a window to the bounding box of
+ the inner content area.
+ @param rsPaneURL
+ Specifies the pane style that is used to determine the border sizes.
+ @param rOuterBox
+ The bounding box of the rectangle around the window.
+ @param bIsTitleVisible
+ This flag controls whether the upper part of the frame is
+ supposed to contain the window title.
+ */
+ css::awt::Rectangle RemoveBorder (
+ const OUString& rsPaneURL,
+ const css::awt::Rectangle& rOuterBox,
+ const css::drawing::framework::BorderType eBorderType) const;
+
+ void SetTheme (const std::shared_ptr<PresenterTheme>& rpTheme);
+
+ class Renderer;
+
+ // XPaneBorderPainter
+
+ virtual css::awt::Rectangle SAL_CALL addBorder (
+ const OUString& rsPaneBorderStyleName,
+ const css::awt::Rectangle& rRectangle,
+ css::drawing::framework::BorderType eBorderType) override;
+
+ virtual css::awt::Rectangle SAL_CALL removeBorder (
+ const OUString& rsPaneBorderStyleName,
+ const css::awt::Rectangle& rRectangle,
+ css::drawing::framework::BorderType eBorderType) override;
+
+ virtual void SAL_CALL paintBorder (
+ const OUString& rsPaneBorderStyleName,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rOuterBorderRectangle,
+ const css::awt::Rectangle& rRepaintArea,
+ const OUString& rsTitle) override;
+
+ virtual void SAL_CALL paintBorderWithCallout (
+ const OUString& rsPaneBorderStyleName,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rOuterBorderRectangle,
+ const css::awt::Rectangle& rRepaintArea,
+ const OUString& rsTitle,
+ const css::awt::Point& rCalloutAnchor) override;
+
+ virtual css::awt::Point SAL_CALL getCalloutOffset (
+ const OUString& rsPaneBorderStyleName) override;
+
+private:
+ css::uno::Reference<css::uno::XComponentContext> mxContext;
+ std::shared_ptr<PresenterTheme> mpTheme;
+ std::unique_ptr<Renderer> mpRenderer;
+
+ /** When the theme for the border has not yet been loaded then try again
+ when this method is called.
+ @return
+ Returns <TRUE/> only one time when the theme is loaded and/or the
+ renderer is initialized.
+ */
+ bool ProvideTheme (
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas);
+ void ProvideTheme();
+
+ /// @throws css::lang::DisposedException
+ void ThrowIfDisposed() const;
+};
+
+} // end of namespace ::sd::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPaneContainer.cxx b/sdext/source/presenter/PresenterPaneContainer.cxx
new file mode 100644
index 000000000..b28f36a39
--- /dev/null
+++ b/sdext/source/presenter/PresenterPaneContainer.cxx
@@ -0,0 +1,331 @@
+/* -*- 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 "PresenterPaneContainer.hxx"
+#include "PresenterPaneBase.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sdext::presenter {
+
+PresenterPaneContainer::PresenterPaneContainer (
+ const Reference<XComponentContext>& rxContext)
+ : PresenterPaneContainerInterfaceBase(m_aMutex)
+{
+ Reference<lang::XMultiComponentFactory> xFactory (rxContext->getServiceManager());
+ if (xFactory.is())
+ {
+ mxPresenterHelper.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.comp.Draw.PresenterHelper",
+ rxContext),
+ UNO_QUERY_THROW);
+ }
+}
+
+PresenterPaneContainer::~PresenterPaneContainer()
+{
+}
+
+void PresenterPaneContainer::PreparePane (
+ const Reference<XResourceId>& rxPaneId,
+ const OUString& rsViewURL,
+ const OUString& rsTitle,
+ const OUString& rsAccessibleTitle,
+ const bool bIsOpaque,
+ const ViewInitializationFunction& rViewInitialization)
+{
+ if ( ! rxPaneId.is())
+ return;
+
+ SharedPaneDescriptor pPane (FindPaneURL(rxPaneId->getResourceURL()));
+ if (pPane)
+ return;
+
+ // No entry found for the given pane id. Create a new one.
+ SharedPaneDescriptor pDescriptor = std::make_shared<PaneDescriptor>();
+ pDescriptor->mxPaneId = rxPaneId;
+ pDescriptor->msViewURL = rsViewURL;
+ pDescriptor->mxPane = nullptr;
+ if (rsTitle.indexOf('%') < 0)
+ {
+ pDescriptor->msTitle = rsTitle;
+ pDescriptor->msTitleTemplate.clear();
+ }
+ else
+ {
+ pDescriptor->msTitleTemplate = rsTitle;
+ pDescriptor->msTitle.clear();
+ }
+ pDescriptor->msAccessibleTitleTemplate = rsAccessibleTitle;
+ pDescriptor->maViewInitialization = rViewInitialization;
+ pDescriptor->mbIsActive = true;
+ pDescriptor->mbIsOpaque = bIsOpaque;
+ pDescriptor->mbIsSprite = false;
+
+ maPanes.push_back(pDescriptor);
+}
+
+void SAL_CALL PresenterPaneContainer::disposing()
+{
+ for (const auto& rxPane : maPanes)
+ if (rxPane->mxPaneId.is())
+ RemovePane(rxPane->mxPaneId);
+}
+
+PresenterPaneContainer::SharedPaneDescriptor
+ PresenterPaneContainer::StorePane (const rtl::Reference<PresenterPaneBase>& rxPane)
+{
+ SharedPaneDescriptor pDescriptor;
+
+ if (rxPane.is())
+ {
+ OUString sPaneURL;
+ Reference<XResourceId> xPaneId (rxPane->getResourceId());
+ if (xPaneId.is())
+ sPaneURL = xPaneId->getResourceURL();
+
+ pDescriptor = FindPaneURL(sPaneURL);
+ if (!pDescriptor)
+ PreparePane(xPaneId, OUString(), OUString(), OUString(),
+ false, ViewInitializationFunction());
+ pDescriptor = FindPaneURL(sPaneURL);
+ if (pDescriptor)
+ {
+ Reference<awt::XWindow> xWindow (rxPane->getWindow());
+ pDescriptor->mxContentWindow = xWindow;
+ pDescriptor->mxPaneId = xPaneId;
+ pDescriptor->mxPane = rxPane;
+ pDescriptor->mxPane->SetTitle(pDescriptor->msTitle);
+
+ if (xWindow.is())
+ xWindow->addEventListener(this);
+ }
+ }
+
+ return pDescriptor;
+}
+
+PresenterPaneContainer::SharedPaneDescriptor
+ PresenterPaneContainer::StoreBorderWindow(
+ const Reference<XResourceId>& rxPaneId,
+ const Reference<awt::XWindow>& rxBorderWindow)
+{
+ // The content window may not be present. Use the resource URL of the
+ // pane id as key.
+ OUString sPaneURL;
+ if (rxPaneId.is())
+ sPaneURL = rxPaneId->getResourceURL();
+
+ SharedPaneDescriptor pDescriptor (FindPaneURL(sPaneURL));
+ if (pDescriptor)
+ {
+ pDescriptor->mxBorderWindow = rxBorderWindow;
+ return pDescriptor;
+ }
+ else
+ return SharedPaneDescriptor();
+}
+
+PresenterPaneContainer::SharedPaneDescriptor
+ PresenterPaneContainer::StoreView (
+ const Reference<XView>& rxView)
+{
+ SharedPaneDescriptor pDescriptor;
+
+ if (rxView.is())
+ {
+ OUString sPaneURL;
+ Reference<XResourceId> xViewId (rxView->getResourceId());
+ if (xViewId.is())
+ {
+ Reference<XResourceId> xPaneId (xViewId->getAnchor());
+ if (xPaneId.is())
+ sPaneURL = xPaneId->getResourceURL();
+ }
+
+ pDescriptor = FindPaneURL(sPaneURL);
+ if (pDescriptor)
+ {
+ pDescriptor->mxView = rxView;
+ try
+ {
+ if (pDescriptor->maViewInitialization)
+ pDescriptor->maViewInitialization(rxView);
+ }
+ catch (RuntimeException&)
+ {
+ OSL_ASSERT(false);
+ }
+ }
+ }
+
+ return pDescriptor;
+}
+
+PresenterPaneContainer::SharedPaneDescriptor
+ PresenterPaneContainer::RemovePane (const Reference<XResourceId>& rxPaneId)
+{
+ SharedPaneDescriptor pDescriptor (FindPaneId(rxPaneId));
+ if (pDescriptor)
+ {
+ if (pDescriptor->mxContentWindow.is())
+ pDescriptor->mxContentWindow->removeEventListener(this);
+ pDescriptor->mxContentWindow = nullptr;
+ pDescriptor->mxBorderWindow = nullptr;
+ pDescriptor->mxPane = nullptr;
+ pDescriptor->mxView = nullptr;
+ pDescriptor->mbIsActive = false;
+ }
+ return pDescriptor;
+}
+
+PresenterPaneContainer::SharedPaneDescriptor
+ PresenterPaneContainer::RemoveView (const Reference<XView>& rxView)
+{
+ SharedPaneDescriptor pDescriptor;
+
+ if (rxView.is())
+ {
+ OUString sPaneURL;
+ Reference<XResourceId> xViewId (rxView->getResourceId());
+ if (xViewId.is())
+ {
+ Reference<XResourceId> xPaneId (xViewId->getAnchor());
+ if (xPaneId.is())
+ sPaneURL = xPaneId->getResourceURL();
+ }
+
+ pDescriptor = FindPaneURL(sPaneURL);
+ if (pDescriptor)
+ {
+ pDescriptor->mxView = nullptr;
+ }
+ }
+
+ return pDescriptor;
+}
+
+PresenterPaneContainer::SharedPaneDescriptor PresenterPaneContainer::FindBorderWindow (
+ const Reference<awt::XWindow>& rxBorderWindow)
+{
+ auto iPane = std::find_if(maPanes.begin(), maPanes.end(),
+ [&rxBorderWindow](const SharedPaneDescriptor& rxPane) { return rxPane->mxBorderWindow == rxBorderWindow; });
+ if (iPane != maPanes.end())
+ return *iPane;
+ return SharedPaneDescriptor();
+}
+
+PresenterPaneContainer::SharedPaneDescriptor PresenterPaneContainer::FindContentWindow (
+ const Reference<awt::XWindow>& rxContentWindow)
+{
+ auto iPane = std::find_if(maPanes.begin(), maPanes.end(),
+ [&rxContentWindow](const SharedPaneDescriptor& rxPane) { return rxPane->mxContentWindow == rxContentWindow; });
+ if (iPane != maPanes.end())
+ return *iPane;
+ return SharedPaneDescriptor();
+}
+
+PresenterPaneContainer::SharedPaneDescriptor PresenterPaneContainer::FindPaneURL (
+ const OUString& rsPaneURL)
+{
+ auto iPane = std::find_if(maPanes.begin(), maPanes.end(),
+ [&rsPaneURL](const SharedPaneDescriptor& rxPane) { return rxPane->mxPaneId->getResourceURL() == rsPaneURL; });
+ if (iPane != maPanes.end())
+ return *iPane;
+ return SharedPaneDescriptor();
+}
+
+PresenterPaneContainer::SharedPaneDescriptor PresenterPaneContainer::FindPaneId (
+ const Reference<XResourceId>& rxPaneId)
+{
+ if ( ! rxPaneId.is())
+ return SharedPaneDescriptor();
+
+ auto iPane = std::find_if(maPanes.begin(), maPanes.end(),
+ [&rxPaneId](const SharedPaneDescriptor& rxPane) { return rxPaneId->compareTo(rxPane->mxPaneId) == 0; });
+ if (iPane != maPanes.end())
+ return *iPane;
+ return SharedPaneDescriptor();
+}
+
+PresenterPaneContainer::SharedPaneDescriptor PresenterPaneContainer::FindViewURL (
+ const OUString& rsViewURL)
+{
+ auto iPane = std::find_if(maPanes.begin(), maPanes.end(),
+ [&rsViewURL](const SharedPaneDescriptor& rxPane) { return rsViewURL == rxPane->msViewURL; });
+ if (iPane != maPanes.end())
+ return *iPane;
+ return SharedPaneDescriptor();
+}
+
+OUString PresenterPaneContainer::GetPaneURLForViewURL (const OUString& rsViewURL)
+{
+ SharedPaneDescriptor pDescriptor (FindViewURL(rsViewURL));
+ if (pDescriptor)
+ if (pDescriptor->mxPaneId.is())
+ return pDescriptor->mxPaneId->getResourceURL();
+ return OUString();
+}
+
+void PresenterPaneContainer::ToTop (const SharedPaneDescriptor& rpDescriptor)
+{
+ if (!rpDescriptor)
+ return;
+
+ // Find iterator for pDescriptor.
+ PaneList::iterator iEnd (maPanes.end());
+ auto iPane = std::find_if(maPanes.begin(), iEnd,
+ [&rpDescriptor](SharedPaneDescriptor& rxPane) { return rxPane.get() == rpDescriptor.get(); });
+ OSL_ASSERT(iPane!=iEnd);
+ if (iPane == iEnd)
+ return;
+
+ if (mxPresenterHelper.is())
+ mxPresenterHelper->toTop(rpDescriptor->mxBorderWindow);
+
+ maPanes.erase(iPane);
+ maPanes.push_back(rpDescriptor);
+}
+
+//----- XEventListener --------------------------------------------------------
+
+void SAL_CALL PresenterPaneContainer::disposing (
+ const css::lang::EventObject& rEvent)
+{
+ SharedPaneDescriptor pDescriptor (
+ FindContentWindow(Reference<awt::XWindow>(rEvent.Source, UNO_QUERY)));
+ if (pDescriptor)
+ {
+ RemovePane(pDescriptor->mxPaneId);
+ }
+}
+
+//===== PresenterPaneContainer::PaneDescriptor ================================
+
+void PresenterPaneContainer::PaneDescriptor::SetActivationState (const bool bIsActive)
+{
+ mbIsActive = bIsActive;
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPaneContainer.hxx b/sdext/source/presenter/PresenterPaneContainer.hxx
new file mode 100644
index 000000000..136c25690
--- /dev/null
+++ b/sdext/source/presenter/PresenterPaneContainer.hxx
@@ -0,0 +1,161 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPANECONTAINER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPANECONTAINER_HXX
+
+#include "PresenterPaneBase.hxx"
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/drawing/XPresenterHelper.hpp>
+#include <com/sun/star/drawing/framework/XResourceId.hpp>
+#include <com/sun/star/drawing/framework/XView.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <rtl/ref.hxx>
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+namespace sdext::presenter {
+
+class PresenterPaneBase;
+class PresenterSprite;
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::lang::XEventListener
+> PresenterPaneContainerInterfaceBase;
+
+/** This class could also be called PresenterPaneAndViewContainer because it
+ stores not only references to all panes that belong to the presenter
+ screen but stores the views displayed in these panes as well.
+*/
+class PresenterPaneContainer
+ : private ::cppu::BaseMutex,
+ public PresenterPaneContainerInterfaceBase
+{
+public:
+ explicit PresenterPaneContainer (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext);
+ virtual ~PresenterPaneContainer() override;
+ PresenterPaneContainer(const PresenterPaneContainer&) = delete;
+ PresenterPaneContainer& operator=(const PresenterPaneContainer&) = delete;
+
+ virtual void SAL_CALL disposing() override;
+
+ typedef ::std::function<void (const css::uno::Reference<css::drawing::framework::XView>&)>
+ ViewInitializationFunction;
+
+ /** Each pane descriptor holds references to one pane and the view
+ displayed in this pane as well as the other information that is used
+ to manage the pane window like an XWindow reference, the title, and
+ the coordinates.
+
+ A initialization function for the view is stored as well. This
+ function is executed as soon as a view is created.
+ */
+ class PaneDescriptor
+ {
+ public:
+ css::uno::Reference<css::drawing::framework::XResourceId> mxPaneId;
+ OUString msViewURL;
+ ::rtl::Reference<PresenterPaneBase> mxPane;
+ css::uno::Reference<css::drawing::framework::XView> mxView;
+ css::uno::Reference<css::awt::XWindow> mxContentWindow;
+ css::uno::Reference<css::awt::XWindow> mxBorderWindow;
+ OUString msTitleTemplate;
+ OUString msAccessibleTitleTemplate;
+ OUString msTitle;
+ ViewInitializationFunction maViewInitialization;
+ bool mbIsActive;
+ bool mbIsOpaque;
+ bool mbIsSprite;
+
+ void SetActivationState (const bool bIsActive);
+ };
+ typedef std::shared_ptr<PaneDescriptor> SharedPaneDescriptor;
+ typedef ::std::vector<SharedPaneDescriptor> PaneList;
+ PaneList maPanes;
+
+ void PreparePane (
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId,
+ const OUString& rsViewURL,
+ const OUString& rsTitle,
+ const OUString& rsAccessibleTitle,
+ const bool bIsOpaque,
+ const ViewInitializationFunction& rViewInitialization);
+
+ SharedPaneDescriptor StorePane (
+ const rtl::Reference<PresenterPaneBase>& rxPane);
+
+ SharedPaneDescriptor StoreBorderWindow(
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId,
+ const css::uno::Reference<css::awt::XWindow>& rxBorderWindow);
+
+ SharedPaneDescriptor StoreView (
+ const css::uno::Reference<css::drawing::framework::XView>& rxView);
+
+ SharedPaneDescriptor RemovePane (
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId);
+
+ SharedPaneDescriptor RemoveView (
+ const css::uno::Reference<css::drawing::framework::XView>& rxView);
+
+ /** Find the pane whose border window is identical to the given border
+ window.
+ */
+ SharedPaneDescriptor FindBorderWindow (
+ const css::uno::Reference<css::awt::XWindow>& rxBorderWindow);
+
+ /** Find the pane whose border window is identical to the given content
+ window.
+ */
+ SharedPaneDescriptor FindContentWindow (
+ const css::uno::Reference<css::awt::XWindow>& rxBorderWindow);
+
+ /** Find the pane whose pane URL is identical to the given URL string.
+ */
+ SharedPaneDescriptor FindPaneURL (const OUString& rsPaneURL);
+
+ /** Find the pane whose resource id is identical to the given one.
+ */
+ SharedPaneDescriptor FindPaneId (const css::uno::Reference<
+ css::drawing::framework::XResourceId>& rxPaneId);
+
+ SharedPaneDescriptor FindViewURL (const OUString& rsViewURL);
+
+ OUString GetPaneURLForViewURL (const OUString& rsViewURL);
+
+ void ToTop (const SharedPaneDescriptor& rpDescriptor);
+
+ // XEventListener
+
+ virtual void SAL_CALL disposing (
+ const css::lang::EventObject& rEvent) override;
+
+private:
+ css::uno::Reference<css::drawing::XPresenterHelper> mxPresenterHelper;
+};
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPaneFactory.cxx b/sdext/source/presenter/PresenterPaneFactory.cxx
new file mode 100644
index 000000000..b4aadc771
--- /dev/null
+++ b/sdext/source/presenter/PresenterPaneFactory.cxx
@@ -0,0 +1,283 @@
+/* -*- 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 "PresenterPaneFactory.hxx"
+#include "PresenterController.hxx"
+#include "PresenterPane.hxx"
+#include "PresenterPaneBorderPainter.hxx"
+#include "PresenterPaneContainer.hxx"
+#include "PresenterSpritePane.hxx"
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sdext::presenter {
+
+//===== PresenterPaneFactory ==================================================
+
+Reference<drawing::framework::XResourceFactory> PresenterPaneFactory::Create (
+ const Reference<uno::XComponentContext>& rxContext,
+ const Reference<frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+{
+ rtl::Reference<PresenterPaneFactory> pFactory (
+ new PresenterPaneFactory(rxContext,rpPresenterController));
+ pFactory->Register(rxController);
+ return Reference<drawing::framework::XResourceFactory>(pFactory);
+}
+
+PresenterPaneFactory::PresenterPaneFactory (
+ const Reference<uno::XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterPaneFactoryInterfaceBase(m_aMutex),
+ mxComponentContextWeak(rxContext),
+ mpPresenterController(rpPresenterController)
+{
+}
+
+void PresenterPaneFactory::Register (const Reference<frame::XController>& rxController)
+{
+ Reference<XConfigurationController> xCC;
+ try
+ {
+ // Get the configuration controller.
+ Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW);
+ xCC.set(xCM->getConfigurationController());
+ mxConfigurationControllerWeak = xCC;
+ if ( ! xCC.is())
+ {
+ throw RuntimeException();
+ }
+ xCC->addResourceFactory(
+ "private:resource/pane/Presenter/*",
+ this);
+ }
+ catch (RuntimeException&)
+ {
+ OSL_ASSERT(false);
+ if (xCC.is())
+ xCC->removeResourceFactoryForReference(this);
+ mxConfigurationControllerWeak = WeakReference<XConfigurationController>();
+
+ throw;
+ }
+}
+
+PresenterPaneFactory::~PresenterPaneFactory()
+{
+}
+
+void SAL_CALL PresenterPaneFactory::disposing()
+{
+ Reference<XConfigurationController> xCC (mxConfigurationControllerWeak);
+ if (xCC.is())
+ xCC->removeResourceFactoryForReference(this);
+ mxConfigurationControllerWeak = WeakReference<XConfigurationController>();
+
+ // Dispose the panes in the cache.
+ if (mpResourceCache != nullptr)
+ {
+ for (const auto& rxPane : *mpResourceCache)
+ {
+ Reference<lang::XComponent> xPaneComponent (rxPane.second, UNO_QUERY);
+ if (xPaneComponent.is())
+ xPaneComponent->dispose();
+ }
+ mpResourceCache.reset();
+ }
+}
+
+//----- XPaneFactory ----------------------------------------------------------
+
+Reference<XResource> SAL_CALL PresenterPaneFactory::createResource (
+ const Reference<XResourceId>& rxPaneId)
+{
+ ThrowIfDisposed();
+
+ if ( ! rxPaneId.is())
+ return nullptr;
+
+ const OUString sPaneURL (rxPaneId->getResourceURL());
+ if (sPaneURL.isEmpty())
+ return nullptr;
+
+ if (mpResourceCache != nullptr)
+ {
+ // Has the requested resource already been created?
+ ResourceContainer::const_iterator iResource (mpResourceCache->find(sPaneURL));
+ if (iResource != mpResourceCache->end())
+ {
+ // Yes. Mark it as active.
+ rtl::Reference<PresenterPaneContainer> pPaneContainer(
+ mpPresenterController->GetPaneContainer());
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ pPaneContainer->FindPaneURL(sPaneURL));
+ if (pDescriptor)
+ {
+ pDescriptor->SetActivationState(true);
+ if (pDescriptor->mxBorderWindow.is())
+ pDescriptor->mxBorderWindow->setVisible(true);
+ pPaneContainer->StorePane(pDescriptor->mxPane);
+ }
+
+ return iResource->second;
+ }
+ }
+
+ // No. Create a new one.
+ Reference<XResource> xResource = CreatePane(rxPaneId);
+ return xResource;
+}
+
+void SAL_CALL PresenterPaneFactory::releaseResource (const Reference<XResource>& rxResource)
+{
+ ThrowIfDisposed();
+
+ if ( ! rxResource.is())
+ throw lang::IllegalArgumentException();
+
+ // Mark the pane as inactive.
+ rtl::Reference<PresenterPaneContainer> pPaneContainer(
+ mpPresenterController->GetPaneContainer());
+ const OUString sPaneURL (rxResource->getResourceId()->getResourceURL());
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ pPaneContainer->FindPaneURL(sPaneURL));
+ if (!pDescriptor)
+ return;
+
+ pDescriptor->SetActivationState(false);
+ if (pDescriptor->mxBorderWindow.is())
+ pDescriptor->mxBorderWindow->setVisible(false);
+
+ if (mpResourceCache != nullptr)
+ {
+ // Store the pane in the cache.
+ (*mpResourceCache)[sPaneURL] = rxResource;
+ }
+ else
+ {
+ // Dispose the pane.
+ Reference<lang::XComponent> xPaneComponent (rxResource, UNO_QUERY);
+ if (xPaneComponent.is())
+ xPaneComponent->dispose();
+ }
+}
+
+
+Reference<XResource> PresenterPaneFactory::CreatePane (
+ const Reference<XResourceId>& rxPaneId)
+{
+ if ( ! rxPaneId.is())
+ return nullptr;
+
+ Reference<XConfigurationController> xCC (mxConfigurationControllerWeak);
+ if ( ! xCC.is())
+ return nullptr;
+
+ Reference<XComponentContext> xContext (mxComponentContextWeak);
+ if ( ! xContext.is())
+ return nullptr;
+
+ Reference<XPane> xParentPane (xCC->getResource(rxPaneId->getAnchor()), UNO_QUERY);
+ if ( ! xParentPane.is())
+ return nullptr;
+
+ try
+ {
+ return CreatePane(
+ rxPaneId,
+ xParentPane,
+ rxPaneId->getFullResourceURL().Arguments == "Sprite=1");
+ }
+ catch (Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+
+ return nullptr;
+}
+
+Reference<XResource> PresenterPaneFactory::CreatePane (
+ const Reference<XResourceId>& rxPaneId,
+ const Reference<drawing::framework::XPane>& rxParentPane,
+ const bool bIsSpritePane)
+{
+ Reference<XComponentContext> xContext (mxComponentContextWeak);
+ Reference<lang::XMultiComponentFactory> xFactory (
+ xContext->getServiceManager(), UNO_SET_THROW);
+
+ // Create a border window and canvas and store it in the pane
+ // container.
+
+ // Create the pane.
+ ::rtl::Reference<PresenterPaneBase> xPane;
+ if (bIsSpritePane)
+ {
+ xPane.set( new PresenterSpritePane(xContext, mpPresenterController));
+ }
+ else
+ {
+ xPane.set( new PresenterPane(xContext, mpPresenterController));
+ }
+
+ // Supply arguments.
+ Sequence<Any> aArguments{ Any(rxPaneId),
+ Any(rxParentPane->getWindow()),
+ Any(rxParentPane->getCanvas()),
+ Any(OUString()),
+ Any(Reference<drawing::framework::XPaneBorderPainter>(
+ mpPresenterController->GetPaneBorderPainter())),
+ Any(!bIsSpritePane) };
+ xPane->initialize(aArguments);
+
+ // Store pane and canvases and windows in container.
+ ::rtl::Reference<PresenterPaneContainer> pContainer (
+ mpPresenterController->GetPaneContainer());
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor(
+ pContainer->StoreBorderWindow(rxPaneId, xPane->GetBorderWindow()));
+ pContainer->StorePane(xPane);
+ if (pDescriptor)
+ {
+ pDescriptor->mbIsSprite = bIsSpritePane;
+
+ // Get the window of the frame and make that visible.
+ Reference<awt::XWindow> xWindow (pDescriptor->mxBorderWindow, UNO_SET_THROW);
+ xWindow->setVisible(true);
+ }
+
+ return Reference<XResource>(static_cast<XWeak*>(xPane.get()), UNO_QUERY_THROW);
+}
+
+void PresenterPaneFactory::ThrowIfDisposed() const
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterPaneFactory object has already been disposed",
+ const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
+ }
+}
+
+} // end of namespace sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterPaneFactory.hxx b/sdext/source/presenter/PresenterPaneFactory.hxx
new file mode 100644
index 000000000..45f9541c6
--- /dev/null
+++ b/sdext/source/presenter/PresenterPaneFactory.hxx
@@ -0,0 +1,117 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPANEFACTORY_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPANEFACTORY_HXX
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <com/sun/star/drawing/framework/XPane.hpp>
+#include <com/sun/star/drawing/framework/XResourceFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <rtl/ref.hxx>
+#include <map>
+#include <memory>
+
+namespace sdext::presenter {
+
+class PresenterController;
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::drawing::framework::XResourceFactory
+> PresenterPaneFactoryInterfaceBase;
+
+/** The PresenterPaneFactory provides a fixed set of panes.
+
+ In order to make the presenter screen more easily extendable in the
+ future the set of supported panes could be made extendable on demand.
+*/
+class PresenterPaneFactory
+ : public ::cppu::BaseMutex,
+ public PresenterPaneFactoryInterfaceBase
+{
+public:
+ static constexpr OUStringLiteral msCurrentSlidePreviewPaneURL
+ = u"private:resource/pane/Presenter/Pane1";
+ static constexpr OUStringLiteral msNextSlidePreviewPaneURL
+ = u"private:resource/pane/Presenter/Pane2";
+ static constexpr OUStringLiteral msNotesPaneURL = u"private:resource/pane/Presenter/Pane3";
+ static constexpr OUStringLiteral msToolBarPaneURL = u"private:resource/pane/Presenter/Pane4";
+ static constexpr OUStringLiteral msSlideSorterPaneURL
+ = u"private:resource/pane/Presenter/Pane5";
+
+ /** Create a new instance of this class and register it as resource
+ factory in the drawing framework of the given controller.
+ This registration keeps it alive. When the drawing framework is
+ shut down and releases its reference to the factory then the factory
+ is destroyed.
+ */
+ static css::uno::Reference<css::drawing::framework::XResourceFactory> Create (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterPaneFactory() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ // XResourceFactory
+
+ virtual css::uno::Reference<css::drawing::framework::XResource>
+ SAL_CALL createResource (
+ const css::uno::Reference<
+ css::drawing::framework::XResourceId>& rxPaneId) override;
+
+ virtual void SAL_CALL
+ releaseResource (
+ const css::uno::Reference<css::drawing::framework::XResource>&
+ rxPane) override;
+
+private:
+ css::uno::WeakReference<css::uno::XComponentContext> mxComponentContextWeak;
+ css::uno::WeakReference<css::drawing::framework::XConfigurationController>
+ mxConfigurationControllerWeak;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ typedef ::std::map<OUString, css::uno::Reference<css::drawing::framework::XResource> >
+ ResourceContainer;
+ std::unique_ptr<ResourceContainer> mpResourceCache;
+
+ PresenterPaneFactory (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+
+ void Register (const css::uno::Reference<css::frame::XController>& rxController);
+
+ css::uno::Reference<css::drawing::framework::XResource> CreatePane (
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId);
+ css::uno::Reference<css::drawing::framework::XResource> CreatePane (
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxPaneId,
+ const css::uno::Reference<css::drawing::framework::XPane>& rxParentPane,
+ const bool bIsSpritePane);
+
+ /// @throws css::lang::DisposedException
+ void ThrowIfDisposed() const;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterProtocolHandler.cxx b/sdext/source/presenter/PresenterProtocolHandler.cxx
new file mode 100644
index 000000000..efd572ee9
--- /dev/null
+++ b/sdext/source/presenter/PresenterProtocolHandler.cxx
@@ -0,0 +1,829 @@
+/* -*- 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 <memory>
+#include "PresenterProtocolHandler.hxx"
+#include "PresenterController.hxx"
+#include "PresenterNotesView.hxx"
+#include "PresenterPaneContainer.hxx"
+#include "PresenterViewFactory.hxx"
+#include "PresenterWindowManager.hxx"
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <algorithm>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sdext::presenter {
+
+namespace {
+ class Command
+ {
+ public:
+ virtual ~Command() {}
+ virtual void Execute() = 0;
+ virtual bool IsEnabled() const { return true; }
+ virtual Any GetState() const { return Any(false); }
+ };
+
+ class GotoPreviousSlideCommand : public Command
+ {
+ public:
+ explicit GotoPreviousSlideCommand (
+ const rtl::Reference<PresenterController>& rpPresenterController);
+ virtual void Execute() override;
+ virtual bool IsEnabled() const override;
+ private:
+ rtl::Reference<PresenterController> mpPresenterController;
+ };
+
+ class GotoNextSlideCommand : public Command
+ {
+ public:
+ explicit GotoNextSlideCommand (
+ const rtl::Reference<PresenterController>& rpPresenterController);
+ virtual void Execute() override;
+ // The next slide command is always enabled, even when the current slide
+ // is the last slide: from the last slide it goes to the pause slide,
+ // and from there it ends the slide show.
+ virtual bool IsEnabled() const override { return true; }
+ private:
+ rtl::Reference<PresenterController> mpPresenterController;
+ };
+
+ class GotoNextEffectCommand : public Command
+ {
+ public:
+ explicit GotoNextEffectCommand (
+ const rtl::Reference<PresenterController>& rpPresenterController);
+ virtual void Execute() override;
+ virtual bool IsEnabled() const override;
+ private:
+ rtl::Reference<PresenterController> mpPresenterController;
+ };
+
+ class SwitchMonitorCommand : public Command
+ {
+ public:
+ explicit SwitchMonitorCommand (
+ const rtl::Reference<PresenterController>& rpPresenterController);
+ virtual void Execute() override;
+ private:
+ rtl::Reference<PresenterController> mpPresenterController;
+ };
+
+ class PauseResumeCommand : public Command
+ {
+ public:
+ explicit PauseResumeCommand(const rtl::Reference<PresenterController>& rpPresenterController);
+ virtual void Execute() override;
+ virtual Any GetState() const override;
+ private:
+ rtl::Reference<PresenterController> mpPresenterController;
+ };
+
+ /// This command restarts the presentation timer.
+ class RestartTimerCommand : public Command
+ {
+ public:
+ explicit RestartTimerCommand(const rtl::Reference<PresenterController>& rpPresenterController);
+ virtual void Execute() override;
+ private:
+ rtl::Reference<PresenterController> mpPresenterController;
+ };
+
+ class SetNotesViewCommand : public Command
+ {
+ public:
+ SetNotesViewCommand (
+ const bool bOn,
+ const rtl::Reference<PresenterController>& rpPresenterController);
+ virtual void Execute() override;
+ virtual Any GetState() const override;
+ private:
+ bool mbOn;
+ rtl::Reference<PresenterController> mpPresenterController;
+ };
+
+ class SetSlideSorterCommand : public Command
+ {
+ public:
+ SetSlideSorterCommand (
+ const bool bOn,
+ const rtl::Reference<PresenterController>& rpPresenterController);
+ virtual void Execute() override;
+ virtual Any GetState() const override;
+ private:
+ bool mbOn;
+ rtl::Reference<PresenterController> mpPresenterController;
+ };
+
+ class SetHelpViewCommand : public Command
+ {
+ public:
+ SetHelpViewCommand (
+ const bool bOn,
+ const rtl::Reference<PresenterController>& rpPresenterController);
+ virtual void Execute() override;
+ virtual Any GetState() const override;
+ private:
+ bool mbOn;
+ rtl::Reference<PresenterController> mpPresenterController;
+ };
+
+ class NotesFontSizeCommand : public Command
+ {
+ public:
+ NotesFontSizeCommand(
+ const rtl::Reference<PresenterController>& rpPresenterController,
+ const sal_Int32 nSizeChange);
+ virtual void Execute() override;
+ virtual Any GetState() const override;
+ protected:
+ ::rtl::Reference<PresenterNotesView> GetNotesView() const;
+ private:
+ rtl::Reference<PresenterController> mpPresenterController;
+ const sal_Int32 mnSizeChange;
+ };
+
+ class ExitPresenterCommand : public Command
+ {
+ public:
+ explicit ExitPresenterCommand(const rtl::Reference<PresenterController>& rpPresenterController);
+ virtual void Execute() override;
+ private:
+ rtl::Reference<PresenterController> mpPresenterController;
+ };
+
+} // end of anonymous namespace
+
+namespace {
+ typedef ::cppu::WeakComponentImplHelper <
+ css::frame::XDispatch,
+ css::document::XEventListener
+ > PresenterDispatchInterfaceBase;
+}
+
+class PresenterProtocolHandler::Dispatch
+ : protected ::cppu::BaseMutex,
+ public PresenterDispatchInterfaceBase
+{
+public:
+ /** Create a new Dispatch object. When the given command name
+ (rsURLPath) is not known then an empty reference is returned.
+ */
+ static Reference<frame::XDispatch> Create (
+ const OUString& rsURLPath,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+
+ void SAL_CALL disposing() override;
+ static Command* CreateCommand (
+ const OUString& rsURLPath,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+
+ // XDispatch
+ virtual void SAL_CALL dispatch(
+ const css::util::URL& aURL,
+ const css::uno::Sequence<css::beans::PropertyValue>& rArguments) override;
+
+ virtual void SAL_CALL addStatusListener(
+ const css::uno::Reference<css::frame::XStatusListener>& rxListener,
+ const css::util::URL& rURL) override;
+
+ virtual void SAL_CALL removeStatusListener (
+ const css::uno::Reference<css::frame::XStatusListener>& rxListener,
+ const css::util::URL& rURL) override;
+
+ // document::XEventListener
+
+ virtual void SAL_CALL notifyEvent (const css::document::EventObject& rEvent) override;
+
+ // lang::XEventListener
+
+ virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override;
+
+private:
+ OUString msURLPath;
+ std::unique_ptr<Command> mpCommand;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ typedef ::std::vector<Reference<frame::XStatusListener> > StatusListenerContainer;
+ StatusListenerContainer maStatusListenerContainer;
+ bool mbIsListeningToWindowManager;
+
+ Dispatch (
+ const OUString& rsURLPath,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~Dispatch() override;
+};
+
+
+//===== PresenterProtocolHandler =========================================================
+
+PresenterProtocolHandler::PresenterProtocolHandler ()
+ : PresenterProtocolHandlerInterfaceBase(m_aMutex)
+{
+}
+
+PresenterProtocolHandler::~PresenterProtocolHandler()
+{
+}
+
+void SAL_CALL PresenterProtocolHandler::disposing()
+{
+}
+
+//----- XInitialize -----------------------------------------------------------
+
+void SAL_CALL PresenterProtocolHandler::initialize (const Sequence<Any>& aArguments)
+{
+ ThrowIfDisposed();
+ if (aArguments.getLength() <= 0)
+ return;
+
+ try
+ {
+ Reference<frame::XFrame> xFrame;
+ if (aArguments[0] >>= xFrame)
+ {
+ mpPresenterController = PresenterController::Instance(xFrame);
+ }
+ }
+ catch (RuntimeException&)
+ {
+ OSL_ASSERT(false);
+ }
+}
+
+OUString PresenterProtocolHandler::getImplementationName()
+{
+ return "org.libreoffice.comp.PresenterScreenProtocolHandler";
+}
+
+sal_Bool PresenterProtocolHandler::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString>
+PresenterProtocolHandler::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ProtocolHandler" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+sdext_PresenterProtocolHandler_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new PresenterProtocolHandler());
+}
+
+//----- XDispatchProvider -----------------------------------------------------
+
+Reference<frame::XDispatch> SAL_CALL PresenterProtocolHandler::queryDispatch (
+ const css::util::URL& rURL,
+ const OUString&,
+ sal_Int32)
+{
+ ThrowIfDisposed();
+
+ Reference<frame::XDispatch> xDispatch;
+
+ // tdf#154546 skip dispatch when presenter controller is not set
+ // mpPresenterController is sometimes unset and this will cause a
+ // crash when pressing the presenter console's Exchange button.
+ if (rURL.Protocol == "vnd.org.libreoffice.presenterscreen:" && mpPresenterController.is())
+ {
+ xDispatch.set(Dispatch::Create(rURL.Path, mpPresenterController));
+ }
+
+ return xDispatch;
+}
+
+Sequence<Reference<frame::XDispatch> > SAL_CALL PresenterProtocolHandler::queryDispatches(
+ const Sequence<frame::DispatchDescriptor>&)
+{
+ ThrowIfDisposed();
+ return Sequence<Reference<frame::XDispatch> >();
+}
+
+
+void PresenterProtocolHandler::ThrowIfDisposed() const
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterProtocolHandler object has already been disposed",
+ const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
+ }
+}
+
+//===== PresenterProtocolHandler::Dispatch ====================================
+
+Reference<frame::XDispatch> PresenterProtocolHandler::Dispatch::Create (
+ const OUString& rsURLPath,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+{
+ ::rtl::Reference<Dispatch> pDispatch (new Dispatch (rsURLPath, rpPresenterController));
+ if (pDispatch->mpCommand != nullptr)
+ return pDispatch;
+ else
+ return nullptr;
+}
+
+PresenterProtocolHandler::Dispatch::Dispatch (
+ const OUString& rsURLPath,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterDispatchInterfaceBase(m_aMutex),
+ msURLPath(rsURLPath),
+ mpCommand(CreateCommand(rsURLPath, rpPresenterController)),
+ mpPresenterController(rpPresenterController),
+ mbIsListeningToWindowManager(false)
+{
+ if (mpCommand != nullptr)
+ {
+ mpPresenterController->GetWindowManager()->AddLayoutListener(this);
+ mbIsListeningToWindowManager = true;
+ }
+}
+
+Command* PresenterProtocolHandler::Dispatch::CreateCommand (
+ const OUString& rsURLPath,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+{
+ if (rsURLPath.getLength() <= 5)
+ return nullptr;
+
+ if (rsURLPath == "CloseNotes")
+ return new SetNotesViewCommand(false, rpPresenterController);
+ if (rsURLPath == "CloseSlideSorter")
+ return new SetSlideSorterCommand(false, rpPresenterController);
+ if (rsURLPath == "CloseHelp")
+ return new SetHelpViewCommand(false, rpPresenterController);
+ if (rsURLPath == "GrowNotesFont")
+ return new NotesFontSizeCommand(rpPresenterController, +1);
+ if (rsURLPath == "NextEffect")
+ return new GotoNextEffectCommand(rpPresenterController);
+ if (rsURLPath == "NextSlide")
+ return new GotoNextSlideCommand(rpPresenterController);
+ if (rsURLPath == "PrevSlide")
+ return new GotoPreviousSlideCommand(rpPresenterController);
+ if (rsURLPath == "SwitchMonitor")
+ return new SwitchMonitorCommand(rpPresenterController);
+ if (rsURLPath == "PauseResumeTimer")
+ return new PauseResumeCommand(rpPresenterController);
+ if (rsURLPath == "RestartTimer")
+ return new RestartTimerCommand(rpPresenterController);
+ if (rsURLPath == "ShowNotes")
+ return new SetNotesViewCommand(true, rpPresenterController);
+ if (rsURLPath == "ShowSlideSorter")
+ return new SetSlideSorterCommand(true, rpPresenterController);
+ if (rsURLPath == "ShowHelp")
+ return new SetHelpViewCommand(true, rpPresenterController);
+ if (rsURLPath == "ShrinkNotesFont")
+ return new NotesFontSizeCommand(rpPresenterController, -1);
+ if (rsURLPath == "ExitPresenter")
+ return new ExitPresenterCommand(rpPresenterController);
+
+ return nullptr;
+}
+
+PresenterProtocolHandler::Dispatch::~Dispatch()
+{
+}
+
+void PresenterProtocolHandler::Dispatch::disposing()
+{
+ if (mbIsListeningToWindowManager)
+ {
+ if (mpPresenterController)
+ mpPresenterController->GetWindowManager()->RemoveLayoutListener(this);
+ mbIsListeningToWindowManager = false;
+ }
+
+ msURLPath.clear();
+ mpCommand.reset();
+}
+
+//----- XDispatch -------------------------------------------------------------
+
+void SAL_CALL PresenterProtocolHandler::Dispatch::dispatch(
+ const css::util::URL& rURL,
+ const css::uno::Sequence<css::beans::PropertyValue>& /*rArguments*/)
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterProtocolHandler::Dispatch object has already been disposed",
+ static_cast<uno::XWeak*>(this));
+ }
+
+ if (rURL.Protocol != "vnd.org.libreoffice.presenterscreen:"
+ || rURL.Path != msURLPath)
+ {
+ // We can not throw an IllegalArgumentException
+ throw RuntimeException();
+ }
+
+ if (mpCommand != nullptr)
+ mpCommand->Execute();
+}
+
+void SAL_CALL PresenterProtocolHandler::Dispatch::addStatusListener(
+ const css::uno::Reference<css::frame::XStatusListener>& rxListener,
+ const css::util::URL& rURL)
+{
+ if (rURL.Path != msURLPath)
+ throw RuntimeException();
+
+ maStatusListenerContainer.push_back(rxListener);
+
+ frame::FeatureStateEvent aEvent;
+ aEvent.FeatureURL = rURL;
+ aEvent.IsEnabled = mpCommand->IsEnabled();
+ aEvent.Requery = false;
+ aEvent.State = mpCommand->GetState();
+ rxListener->statusChanged(aEvent);
+}
+
+void SAL_CALL PresenterProtocolHandler::Dispatch::removeStatusListener (
+ const css::uno::Reference<css::frame::XStatusListener>& rxListener,
+ const css::util::URL& rURL)
+{
+ if (rURL.Path != msURLPath)
+ throw RuntimeException();
+
+ StatusListenerContainer::iterator iListener (
+ ::std::find(
+ maStatusListenerContainer.begin(),
+ maStatusListenerContainer.end(),
+ rxListener));
+ if (iListener != maStatusListenerContainer.end())
+ maStatusListenerContainer.erase(iListener);
+}
+
+//----- document::XEventListener ----------------------------------------------
+
+void SAL_CALL PresenterProtocolHandler::Dispatch::notifyEvent (
+ const css::document::EventObject&)
+{
+ mpCommand->GetState();
+}
+
+//----- lang::XEventListener --------------------------------------------------
+
+void SAL_CALL PresenterProtocolHandler::Dispatch::disposing (const css::lang::EventObject&)
+{
+ mbIsListeningToWindowManager = false;
+}
+
+//===== GotoPreviousSlideCommand ==============================================
+
+GotoPreviousSlideCommand::GotoPreviousSlideCommand (
+ const rtl::Reference<PresenterController>& rpPresenterController)
+ : mpPresenterController(rpPresenterController)
+{
+}
+
+void GotoPreviousSlideCommand::Execute()
+{
+ if ( ! mpPresenterController.is())
+ return;
+
+ if ( ! mpPresenterController->GetSlideShowController().is())
+ return;
+
+ mpPresenterController->GetSlideShowController()->gotoPreviousSlide();
+}
+
+bool GotoPreviousSlideCommand::IsEnabled() const
+{
+ if ( ! mpPresenterController.is())
+ return false;
+
+ if ( ! mpPresenterController->GetSlideShowController().is())
+ return false;
+
+ return mpPresenterController->GetSlideShowController()->getCurrentSlideIndex()>0;
+}
+
+//===== GotoNextEffect ========================================================
+
+GotoNextEffectCommand::GotoNextEffectCommand (
+ const rtl::Reference<PresenterController>& rpPresenterController)
+ : mpPresenterController(rpPresenterController)
+{
+}
+
+void GotoNextEffectCommand::Execute()
+{
+ if ( ! mpPresenterController.is())
+ return;
+
+ if ( ! mpPresenterController->GetSlideShowController().is())
+ return;
+
+ mpPresenterController->GetSlideShowController()->gotoNextEffect();
+}
+
+bool GotoNextEffectCommand::IsEnabled() const
+{
+ if ( ! mpPresenterController.is())
+ return false;
+
+ if ( ! mpPresenterController->GetSlideShowController().is())
+ return false;
+
+ return ( mpPresenterController->GetSlideShowController()->getNextSlideIndex() < mpPresenterController->GetSlideShowController()->getSlideCount() );
+
+}
+
+//===== GotoNextSlide =========================================================
+
+GotoNextSlideCommand::GotoNextSlideCommand (
+ const rtl::Reference<PresenterController>& rpPresenterController)
+ : mpPresenterController(rpPresenterController)
+{
+}
+
+void GotoNextSlideCommand::Execute()
+{
+ if ( ! mpPresenterController.is())
+ return;
+
+ if ( ! mpPresenterController->GetSlideShowController().is())
+ return;
+
+ mpPresenterController->GetSlideShowController()->gotoNextSlide();
+}
+
+//===== SwitchMonitorCommand ==============================================
+
+SwitchMonitorCommand::SwitchMonitorCommand (
+ const rtl::Reference<PresenterController>& rpPresenterController)
+ : mpPresenterController(rpPresenterController)
+{
+}
+
+void SwitchMonitorCommand::Execute()
+{
+ mpPresenterController->SwitchMonitors();
+}
+
+//===== PauseResumeCommand ==============================================
+
+PauseResumeCommand::PauseResumeCommand (const rtl::Reference<PresenterController>& rpPresenterController)
+: mpPresenterController(rpPresenterController)
+{
+}
+
+void PauseResumeCommand::Execute()
+{
+ if ( ! mpPresenterController.is())
+ return;
+
+ ::rtl::Reference<PresenterWindowManager> pWindowManager (
+ mpPresenterController->GetWindowManager());
+ if ( ! pWindowManager.is())
+ return;
+
+ IPresentationTime* pPresentationTime = mpPresenterController->GetPresentationTime();
+ if (!pPresentationTime)
+ return;
+
+ if(pPresentationTime->isPaused())
+ {
+ pPresentationTime->setPauseStatus(false);
+ pWindowManager->SetPauseState(false);
+ }
+ else
+ {
+ pPresentationTime->setPauseStatus(true);
+ pWindowManager->SetPauseState(true);
+ }
+}
+
+Any PauseResumeCommand::GetState() const
+{
+ if ( ! mpPresenterController.is())
+ return Any(false);
+
+ ::rtl::Reference<PresenterWindowManager> pWindowManager (
+ mpPresenterController->GetWindowManager());
+ if ( ! pWindowManager.is())
+ return Any(false);
+
+ if (IPresentationTime* pPresentationTime = mpPresenterController->GetPresentationTime())
+ {
+ return Any(pPresentationTime->isPaused());
+ }
+ else
+ return Any(false);
+}
+
+RestartTimerCommand::RestartTimerCommand (const rtl::Reference<PresenterController>& rpPresenterController)
+: mpPresenterController(rpPresenterController)
+{
+}
+
+void RestartTimerCommand::Execute()
+{
+ if ( ! mpPresenterController.is())
+ return;
+
+ ::rtl::Reference<PresenterWindowManager> pWindowManager (
+ mpPresenterController->GetWindowManager());
+ if ( ! pWindowManager.is())
+ return;
+
+ if (IPresentationTime* pPresentationTime = mpPresenterController->GetPresentationTime())
+ {
+ //Resets the pause status and restarts the timer
+ pPresentationTime->setPauseStatus(false);
+ pWindowManager->SetPauseState(false);
+ pPresentationTime->restart();
+ }
+}
+
+//===== SetNotesViewCommand ===================================================
+
+SetNotesViewCommand::SetNotesViewCommand (
+ const bool bOn,
+ const rtl::Reference<PresenterController>& rpPresenterController)
+ : mbOn(bOn),
+ mpPresenterController(rpPresenterController)
+{
+}
+
+void SetNotesViewCommand::Execute()
+{
+ if ( ! mpPresenterController.is())
+ return;
+
+ ::rtl::Reference<PresenterWindowManager> pWindowManager (
+ mpPresenterController->GetWindowManager());
+ if ( ! pWindowManager.is())
+ return;
+
+ if (mbOn)
+ pWindowManager->SetViewMode(PresenterWindowManager::VM_Notes);
+ else
+ pWindowManager->SetViewMode(PresenterWindowManager::VM_Standard);
+}
+
+Any SetNotesViewCommand::GetState() const
+{
+ if ( ! mpPresenterController.is())
+ return Any(false);
+
+ ::rtl::Reference<PresenterWindowManager> pWindowManager (
+ mpPresenterController->GetWindowManager());
+ if ( ! pWindowManager.is())
+ return Any(false);
+
+ return Any(pWindowManager->GetViewMode() == PresenterWindowManager::VM_Notes);
+}
+
+//===== SetSlideSorterCommand =================================================
+
+SetSlideSorterCommand::SetSlideSorterCommand (
+ const bool bOn,
+ const rtl::Reference<PresenterController>& rpPresenterController)
+ : mbOn(bOn),
+ mpPresenterController(rpPresenterController)
+{
+}
+
+void SetSlideSorterCommand::Execute()
+{
+ if ( ! mpPresenterController.is())
+ return;
+
+ ::rtl::Reference<PresenterWindowManager> pWindowManager (
+ mpPresenterController->GetWindowManager());
+ if ( ! pWindowManager.is())
+ return;
+
+ pWindowManager->SetSlideSorterState(mbOn);
+}
+
+Any SetSlideSorterCommand::GetState() const
+{
+ if ( ! mpPresenterController.is())
+ return Any(false);
+
+ ::rtl::Reference<PresenterWindowManager> pWindowManager (
+ mpPresenterController->GetWindowManager());
+ if ( ! pWindowManager.is())
+ return Any(false);
+
+ return Any(pWindowManager->GetViewMode()==PresenterWindowManager::VM_SlideOverview);
+}
+
+//===== SetHelpViewCommand ===================================================
+
+SetHelpViewCommand::SetHelpViewCommand (
+ const bool bOn,
+ const rtl::Reference<PresenterController>& rpPresenterController)
+ : mbOn(bOn),
+ mpPresenterController(rpPresenterController)
+{
+}
+
+void SetHelpViewCommand::Execute()
+{
+ if ( ! mpPresenterController.is())
+ return;
+
+ ::rtl::Reference<PresenterWindowManager> pWindowManager (
+ mpPresenterController->GetWindowManager());
+ if ( ! pWindowManager.is())
+ return;
+
+ pWindowManager->SetHelpViewState(mbOn);
+}
+
+Any SetHelpViewCommand::GetState() const
+{
+ if ( ! mpPresenterController.is())
+ return Any(false);
+
+ ::rtl::Reference<PresenterWindowManager> pWindowManager (
+ mpPresenterController->GetWindowManager());
+ if ( ! pWindowManager.is())
+ return Any(false);
+
+ return Any(pWindowManager->GetViewMode()==PresenterWindowManager::VM_Help);
+}
+
+//===== NotesFontSizeCommand ==================================================
+
+NotesFontSizeCommand::NotesFontSizeCommand(
+ const rtl::Reference<PresenterController>& rpPresenterController,
+ const sal_Int32 nSizeChange)
+ : mpPresenterController(rpPresenterController),
+ mnSizeChange(nSizeChange)
+{
+}
+
+::rtl::Reference<PresenterNotesView> NotesFontSizeCommand::GetNotesView() const
+{
+ if (!mpPresenterController)
+ return nullptr;
+
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ mpPresenterController->GetPaneContainer()->FindViewURL(
+ PresenterViewFactory::msNotesViewURL));
+ if (!pDescriptor)
+ return nullptr;
+
+ return dynamic_cast<PresenterNotesView*>(pDescriptor->mxView.get());
+}
+
+void NotesFontSizeCommand::Execute()
+{
+ ::rtl::Reference<PresenterNotesView> pView (GetNotesView());
+ if (pView.is())
+ pView->ChangeFontSize(mnSizeChange);
+}
+
+Any NotesFontSizeCommand::GetState() const
+{
+ return Any();
+}
+
+//===== ExitPresenterCommand ==================================================
+
+ExitPresenterCommand::ExitPresenterCommand (const rtl::Reference<PresenterController>& rpPresenterController)
+: mpPresenterController(rpPresenterController)
+{
+}
+
+void ExitPresenterCommand::Execute()
+{
+ if ( ! mpPresenterController.is())
+ return;
+
+ mpPresenterController->ExitPresenter();
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterProtocolHandler.hxx b/sdext/source/presenter/PresenterProtocolHandler.hxx
new file mode 100644
index 000000000..217fb2658
--- /dev/null
+++ b/sdext/source/presenter/PresenterProtocolHandler.hxx
@@ -0,0 +1,91 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPROTOCOLHANDLER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERPROTOCOLHANDLER_HXX
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/XCacheInfo.hpp>
+#include <rtl/ref.hxx>
+
+namespace sdext::presenter {
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::lang::XInitialization,
+ css::lang::XServiceInfo,
+ css::util::XCacheInfo,
+ css::frame::XDispatchProvider
+> PresenterProtocolHandlerInterfaceBase;
+
+class PresenterController;
+
+class PresenterProtocolHandler
+ : protected ::cppu::BaseMutex,
+ public PresenterProtocolHandlerInterfaceBase
+{
+public:
+ PresenterProtocolHandler ();
+ virtual ~PresenterProtocolHandler() override;
+
+ void SAL_CALL disposing() override;
+
+ // XInitialization
+
+ virtual void SAL_CALL initialize(
+ const css::uno::Sequence<css::uno::Any>& aArguments) override;
+
+ OUString SAL_CALL getImplementationName() override;
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XDispatchProvider
+
+ virtual css::uno::Reference<css::frame::XDispatch > SAL_CALL
+ queryDispatch (
+ const css::util::URL& aURL,
+ const OUString& aTargetFrameName,
+ sal_Int32 nSearchFlags ) override;
+
+ virtual css::uno::Sequence<css::uno::Reference<css::frame::XDispatch> > SAL_CALL
+ queryDispatches(
+ const css::uno::Sequence< css::frame::DispatchDescriptor>& rDescriptors) override;
+
+ /// See XCacheInfo::IsCachingAllowed().
+ sal_Bool SAL_CALL isCachingAllowed() override { return false; }
+
+private:
+ class Dispatch;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+
+ /// @throws css::lang::DisposedException
+ void ThrowIfDisposed() const;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterScreen.cxx b/sdext/source/presenter/PresenterScreen.cxx
new file mode 100644
index 000000000..a53f28e86
--- /dev/null
+++ b/sdext/source/presenter/PresenterScreen.cxx
@@ -0,0 +1,801 @@
+/* -*- 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 "PresenterScreen.hxx"
+#include "PresenterConfigurationAccess.hxx"
+#include "PresenterController.hxx"
+#include "PresenterFrameworkObserver.hxx"
+#include "PresenterHelper.hxx"
+#include "PresenterPaneContainer.hxx"
+#include "PresenterPaneFactory.hxx"
+#include "PresenterViewFactory.hxx"
+#include "PresenterWindowManager.hxx"
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <com/sun/star/drawing/framework/ResourceId.hpp>
+#include <com/sun/star/drawing/framework/ResourceActivationMode.hpp>
+#include <com/sun/star/presentation/XPresentation2.hpp>
+#include <com/sun/star/presentation/XPresentationSupplier.hpp>
+#include <com/sun/star/document/XEventBroadcaster.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::presentation;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sdext::presenter {
+
+namespace {
+ typedef ::cppu::WeakComponentImplHelper <
+ css::document::XEventListener
+ > PresenterScreenListenerInterfaceBase;
+
+ /** One instance of a PresenterScreenListener is registered per Impress
+ document and waits for the full screen slide show to start and to
+ end.
+ */
+ class PresenterScreenListener
+ : private ::cppu::BaseMutex,
+ public PresenterScreenListenerInterfaceBase
+ {
+ public:
+ PresenterScreenListener (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::frame::XModel2>& rxModel);
+ PresenterScreenListener(const PresenterScreenListener&) = delete;
+ PresenterScreenListener& operator=(const PresenterScreenListener&) = delete;
+
+ void Initialize();
+ virtual void SAL_CALL disposing() override;
+
+ // document::XEventListener
+
+ virtual void SAL_CALL notifyEvent( const css::document::EventObject& Event ) override;
+
+ // XEventListener
+
+ virtual void SAL_CALL disposing ( const css::lang::EventObject& rEvent) override;
+
+ private:
+ css::uno::Reference<css::frame::XModel2 > mxModel;
+ css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+ rtl::Reference<PresenterScreen> mpPresenterScreen;
+ };
+}
+
+//----- XServiceInfo ---------------------------------------------------------------
+
+Sequence< OUString > SAL_CALL PresenterScreenJob::getSupportedServiceNames()
+{
+ return { };
+}
+
+OUString SAL_CALL PresenterScreenJob::getImplementationName()
+{
+ return "org.libreoffice.comp.PresenterScreenJob";
+}
+
+sal_Bool SAL_CALL PresenterScreenJob::supportsService(const OUString& aServiceName)
+{
+ return cppu::supportsService(this, aServiceName);
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+sdext_PresenterScreenJob_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new PresenterScreenJob(context));
+}
+
+
+//===== PresenterScreenJob ====================================================
+
+PresenterScreenJob::PresenterScreenJob (const Reference<XComponentContext>& rxContext)
+ : PresenterScreenJobInterfaceBase(m_aMutex),
+ mxComponentContext(rxContext)
+{
+}
+
+PresenterScreenJob::~PresenterScreenJob()
+{
+}
+
+void SAL_CALL PresenterScreenJob::disposing()
+{
+ mxComponentContext = nullptr;
+}
+
+//----- XJob -----------------------------------------------------------
+
+Any SAL_CALL PresenterScreenJob::execute(
+ const Sequence< beans::NamedValue >& Arguments )
+{
+ Sequence< beans::NamedValue > lEnv;
+ auto pArg = std::find_if(Arguments.begin(), Arguments.end(),
+ [](const beans::NamedValue& rArg) { return rArg.Name == "Environment"; });
+ if (pArg != Arguments.end())
+ pArg->Value >>= lEnv;
+
+ Reference<frame::XModel2> xModel;
+ auto pProp = std::find_if(std::cbegin(lEnv), std::cend(lEnv),
+ [](const beans::NamedValue& rProp) { return rProp.Name == "Model"; });
+ if (pProp != std::cend(lEnv))
+ pProp->Value >>= xModel;
+
+ Reference< XServiceInfo > xInfo( xModel, UNO_QUERY );
+ if( xInfo.is() && xInfo->supportsService("com.sun.star.presentation.PresentationDocument") )
+ {
+ // Create a new listener that waits for the full screen presentation
+ // to start and to end. It takes care of its own lifetime.
+ ::rtl::Reference<PresenterScreenListener> pListener (
+ new PresenterScreenListener(mxComponentContext, xModel));
+ pListener->Initialize();
+ }
+
+ return Any();
+}
+
+//===== PresenterScreenListener ===============================================
+
+namespace {
+
+PresenterScreenListener::PresenterScreenListener (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::frame::XModel2>& rxModel)
+ : PresenterScreenListenerInterfaceBase(m_aMutex),
+ mxModel(rxModel),
+ mxComponentContext(rxContext)
+{
+}
+
+void PresenterScreenListener::Initialize()
+{
+ Reference< document::XEventListener > xDocListener(this);
+ Reference< document::XEventBroadcaster > xDocBroadcaster( mxModel, UNO_QUERY );
+ if( xDocBroadcaster.is() )
+ xDocBroadcaster->addEventListener(xDocListener);
+}
+
+void SAL_CALL PresenterScreenListener::disposing()
+{
+ Reference< document::XEventBroadcaster > xDocBroadcaster( mxModel, UNO_QUERY );
+ if( xDocBroadcaster.is() )
+ xDocBroadcaster->removeEventListener(
+ Reference<document::XEventListener>(this) );
+
+ if (mpPresenterScreen.is())
+ {
+ mpPresenterScreen->RequestShutdownPresenterScreen();
+ mpPresenterScreen = nullptr;
+ }
+}
+
+// document::XEventListener
+
+void SAL_CALL PresenterScreenListener::notifyEvent( const css::document::EventObject& Event )
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterScreenListener object has already been disposed",
+ static_cast<uno::XWeak*>(this));
+ }
+
+ if ( Event.EventName == "OnStartPresentation" )
+ {
+ mpPresenterScreen = new PresenterScreen(mxComponentContext, mxModel);
+ if(PresenterScreen::isPresenterScreenEnabled(mxComponentContext))
+ mpPresenterScreen->InitializePresenterScreen();
+ }
+ else if ( Event.EventName == "OnEndPresentation" )
+ {
+ if (mpPresenterScreen.is())
+ {
+ mpPresenterScreen->RequestShutdownPresenterScreen();
+ mpPresenterScreen = nullptr;
+ }
+ }
+}
+
+// XEventListener
+
+void SAL_CALL PresenterScreenListener::disposing (const css::lang::EventObject&)
+{
+ if (mpPresenterScreen.is())
+ {
+ mpPresenterScreen->RequestShutdownPresenterScreen();
+ mpPresenterScreen = nullptr;
+ }
+}
+
+} // end of anonymous namespace
+
+//===== PresenterScreen =======================================================
+
+PresenterScreen::PresenterScreen (
+ const Reference<XComponentContext>& rxContext,
+ const css::uno::Reference<css::frame::XModel2>& rxModel)
+ : PresenterScreenInterfaceBase(m_aMutex),
+ mxModel(rxModel),
+ mxContextWeak(rxContext)
+{
+}
+
+PresenterScreen::~PresenterScreen()
+{
+}
+
+bool PresenterScreen::isPresenterScreenEnabled(const css::uno::Reference<css::uno::XComponentContext>& rxContext)
+{
+ bool dEnablePresenterScreen=true;
+ PresenterConfigurationAccess aConfiguration (
+ rxContext,
+ "/org.openoffice.Office.Impress/",
+ PresenterConfigurationAccess::READ_ONLY);
+ aConfiguration.GetConfigurationNode("Misc/Start/EnablePresenterScreen")
+ >>= dEnablePresenterScreen;
+ return dEnablePresenterScreen;
+}
+void SAL_CALL PresenterScreen::disposing()
+{
+ Reference<XConfigurationController> xCC (mxConfigurationControllerWeak);
+ if (xCC.is() && mxSavedConfiguration.is())
+ {
+ xCC->restoreConfiguration(mxSavedConfiguration);
+ }
+ mxConfigurationControllerWeak = Reference<XConfigurationController>(nullptr);
+
+ Reference<lang::XComponent> xViewFactoryComponent (mxViewFactory, UNO_QUERY);
+ if (xViewFactoryComponent.is())
+ xViewFactoryComponent->dispose();
+ Reference<lang::XComponent> xPaneFactoryComponent (mxPaneFactory, UNO_QUERY);
+ if (xPaneFactoryComponent.is())
+ xPaneFactoryComponent->dispose();
+
+ mxModel = nullptr;
+}
+
+//----- XEventListener --------------------------------------------------------
+
+void SAL_CALL PresenterScreen::disposing (const lang::EventObject& /*rEvent*/)
+{
+ RequestShutdownPresenterScreen();
+}
+
+
+void PresenterScreen::InitializePresenterScreen()
+{
+ try
+ {
+ Reference<XComponentContext> xContext (mxContextWeak);
+ mpPaneContainer = new PresenterPaneContainer(xContext);
+
+ Reference<XPresentationSupplier> xPS ( mxModel, UNO_QUERY_THROW);
+ Reference<XPresentation2> xPresentation(xPS->getPresentation(), UNO_QUERY_THROW);
+ Reference<presentation::XSlideShowController> xSlideShowController( xPresentation->getController() );
+
+ if( !xSlideShowController.is() || !xSlideShowController->isFullScreen() )
+ return;
+
+ // find first controller that is not the current controller (the one with the slideshow
+ mxController = mxModel->getCurrentController();
+ Reference< container::XEnumeration > xEnum( mxModel->getControllers() );
+ if( xEnum.is() )
+ {
+ while( xEnum->hasMoreElements() )
+ {
+ Reference< frame::XController > xC( xEnum->nextElement(), UNO_QUERY );
+ if( xC.is() && (xC != mxController) )
+ {
+ mxController = xC;
+ break;
+ }
+ }
+ }
+ // Get the XController from the first argument.
+ Reference<XControllerManager> xCM(mxController, UNO_QUERY_THROW);
+
+ Reference<XConfigurationController> xCC( xCM->getConfigurationController());
+ mxConfigurationControllerWeak = xCC;
+
+ Reference<drawing::framework::XResourceId> xMainPaneId(
+ GetMainPaneId(xPresentation));
+ // An empty reference means that the presenter screen can
+ // not or must not be displayed.
+ if ( ! xMainPaneId.is())
+ return;
+
+ if (xCC.is() && xContext.is())
+ {
+ // Store the current configuration so that we can restore it when
+ // the presenter view is deactivated.
+ mxSavedConfiguration = xCC->getRequestedConfiguration();
+ xCC->lock();
+
+ try
+ {
+ // At the moment the presenter controller is displayed in its
+ // own full screen window that is controlled by the same
+ // configuration controller as the Impress document from
+ // which the presentation was started. Therefore the main
+ // pane is activated additionally to the already existing
+ // panes and does not replace them.
+ xCC->requestResourceActivation(
+ xMainPaneId,
+ ResourceActivationMode_ADD);
+ SetupConfiguration(xContext, xMainPaneId);
+
+ mpPresenterController = new PresenterController(
+ css::uno::WeakReference<css::lang::XEventListener>(this),
+ xContext,
+ mxController,
+ xSlideShowController,
+ mpPaneContainer,
+ xMainPaneId);
+
+ // Create pane and view factories and integrate them into the
+ // drawing framework.
+ SetupPaneFactory(xContext);
+ SetupViewFactory(xContext);
+
+ mpPresenterController->GetWindowManager()->RestoreViewMode();
+ }
+ catch (const RuntimeException&)
+ {
+ xCC->restoreConfiguration(mxSavedConfiguration);
+ }
+ xCC->unlock();
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+}
+
+void PresenterScreen::SwitchMonitors()
+{
+ try {
+ Reference<XPresentationSupplier> xPS ( mxModel, UNO_QUERY_THROW);
+ Reference<XPresentation2> xPresentation(xPS->getPresentation(), UNO_QUERY_THROW);
+
+ // Get the existing presenter console screen, we want to switch the
+ // presentation to use that instead.
+ sal_Int32 nNewScreen = GetPresenterScreenNumber (xPresentation);
+ if (nNewScreen < 0)
+ return;
+
+ // Adapt that display number to be the 'default' setting of 0 if it matches
+ sal_Int32 nExternalDisplay = Application::GetDisplayExternalScreen();
+
+ if (nNewScreen == nExternalDisplay)
+ nNewScreen = 0; // screen zero is best == the primary display
+ else
+ nNewScreen++; // otherwise we store screens offset by one.
+
+ // Set the new presentation display
+ Reference<beans::XPropertySet> xProperties (xPresentation, UNO_QUERY_THROW);
+ xProperties->setPropertyValue("Display", Any(nNewScreen));
+ } catch (const uno::Exception &) {
+ }
+}
+
+/**
+ * Return the real VCL screen number to show the presenter console
+ * on or -1 to not show anything.
+ */
+sal_Int32 PresenterScreen::GetPresenterScreenNumber (
+ const Reference<presentation::XPresentation2>& rxPresentation) const
+{
+ sal_Int32 nScreenNumber (0);
+ try
+ {
+ if ( ! rxPresentation.is())
+ return -1;
+
+ // Determine the screen on which the full screen presentation is being
+ // displayed.
+ sal_Int32 nDisplayNumber (-1);
+ if ( ! (rxPresentation->getPropertyValue("Display") >>= nDisplayNumber))
+ return -1;
+ if (nDisplayNumber == -1)
+ {
+ // The special value -1 indicates that the slide show
+ // spans all available displays. That leaves no room for
+ // the presenter screen.
+ return -1;
+ }
+
+ SAL_INFO("sdext.presenter", "Display number is " << nDisplayNumber);
+
+ if (nDisplayNumber > 0)
+ {
+ nScreenNumber = nDisplayNumber - 1;
+ }
+ else if (nDisplayNumber == 0)
+ {
+ // A display number value of 0 indicates the primary screen.
+ // Find out which screen number that is.
+ nScreenNumber = Application::GetDisplayExternalScreen();
+ }
+
+ // We still have to determine the number of screens to decide
+ // whether the presenter screen may be shown at all.
+ sal_Int32 nScreenCount = Application::GetScreenCount();
+
+ if (nScreenCount < 2 || nDisplayNumber > nScreenCount)
+ {
+ // There is either only one screen or the full screen
+ // presentation spans all available screens. The presenter
+ // screen is shown only when a special flag in the configuration
+ // is set.
+ Reference<XComponentContext> xContext (mxContextWeak);
+ PresenterConfigurationAccess aConfiguration (
+ xContext,
+ "/org.openoffice.Office.PresenterScreen/",
+ PresenterConfigurationAccess::READ_ONLY);
+ bool bStartAlways (false);
+ if (aConfiguration.GetConfigurationNode(
+ "Presenter/StartAlways") >>= bStartAlways)
+ {
+ if (bStartAlways)
+ return GetPresenterScreenFromScreen(nScreenNumber);
+ }
+ return -1;
+ }
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ OSL_ASSERT(false);
+ // For some reason we can not access the screen number. Use
+ // the default instead.
+ }
+ SAL_INFO("sdext.presenter", "Get presenter screen for screen " << nScreenNumber);
+ return GetPresenterScreenFromScreen(nScreenNumber);
+}
+
+sal_Int32 PresenterScreen::GetPresenterScreenFromScreen( sal_Int32 nPresentationScreen )
+{
+ // Setup the resource id of the full screen background pane so that
+ // it is displayed on another screen than the presentation.
+ sal_Int32 nPresenterScreenNumber (1);
+ switch (nPresentationScreen)
+ {
+ case 0:
+ nPresenterScreenNumber = 1;
+ break;
+
+ case 1:
+ nPresenterScreenNumber = 0;
+ break;
+
+ default:
+ SAL_INFO("sdext.presenter", "Warning unexpected, out of bound screen "
+ "mapped to 0" << nPresentationScreen);
+ // When the full screen presentation is displayed on a screen
+ // other than 0 or 1 then place the presenter on the first
+ // available screen.
+ nPresenterScreenNumber = 0;
+ break;
+ }
+ return nPresenterScreenNumber;
+}
+
+Reference<drawing::framework::XResourceId> PresenterScreen::GetMainPaneId (
+ const Reference<presentation::XPresentation2>& rxPresentation) const
+{
+ // A negative value means that the presentation spans all available
+ // displays. That leaves no room for the presenter.
+ const sal_Int32 nScreen(GetPresenterScreenNumber(rxPresentation));
+ if (nScreen < 0)
+ return nullptr;
+
+ return ResourceId::create(
+ Reference<XComponentContext>(mxContextWeak),
+ PresenterHelper::msFullScreenPaneURL
+ + "?FullScreen=true&ScreenNumber="
+ + OUString::number(nScreen));
+}
+
+void PresenterScreen::RequestShutdownPresenterScreen()
+{
+ // Restore the configuration that was active before the presenter screen
+ // has been activated. Now, that the presenter screen is displayed in
+ // its own top level window this probably not necessary, but one never knows.
+ Reference<XConfigurationController> xCC (mxConfigurationControllerWeak);
+ if (xCC.is() && mxSavedConfiguration.is())
+ {
+ xCC->restoreConfiguration(mxSavedConfiguration);
+ mxSavedConfiguration = nullptr;
+ }
+
+ if (xCC.is())
+ {
+ // The actual restoration of the configuration takes place
+ // asynchronously. The view and pane factories can only by disposed
+ // after that. Therefore, set up a listener and wait for the
+ // restoration.
+ rtl::Reference<PresenterScreen> pSelf (this);
+ PresenterFrameworkObserver::RunOnUpdateEnd(
+ xCC,
+ [pSelf](bool){ return pSelf->ShutdownPresenterScreen(); });
+ xCC->update();
+ }
+}
+
+void PresenterScreen::ShutdownPresenterScreen()
+{
+ Reference<lang::XComponent> xViewFactoryComponent (mxViewFactory, UNO_QUERY);
+ if (xViewFactoryComponent.is())
+ xViewFactoryComponent->dispose();
+ mxViewFactory = nullptr;
+
+ Reference<lang::XComponent> xPaneFactoryComponent (mxPaneFactory, UNO_QUERY);
+ if (xPaneFactoryComponent.is())
+ xPaneFactoryComponent->dispose();
+ mxPaneFactory = nullptr;
+
+ if (mpPresenterController)
+ {
+ mpPresenterController->dispose();
+ mpPresenterController.clear();
+ }
+ mpPaneContainer = new PresenterPaneContainer(Reference<XComponentContext>(mxContextWeak));
+}
+
+void PresenterScreen::SetupPaneFactory (const Reference<XComponentContext>& rxContext)
+{
+ try
+ {
+ if ( ! mxPaneFactory.is())
+ mxPaneFactory = PresenterPaneFactory::Create(
+ rxContext,
+ mxController,
+ mpPresenterController);
+ }
+ catch (const RuntimeException&)
+ {
+ OSL_ASSERT(false);
+ }
+}
+
+void PresenterScreen::SetupViewFactory (const Reference<XComponentContext>& rxContext)
+{
+ try
+ {
+ if ( ! mxViewFactory.is())
+ mxViewFactory = PresenterViewFactory::Create(
+ rxContext,
+ mxController,
+ mpPresenterController);
+ }
+ catch (const RuntimeException&)
+ {
+ OSL_ASSERT(false);
+ }
+}
+
+void PresenterScreen::SetupConfiguration (
+ const Reference<XComponentContext>& rxContext,
+ const Reference<XResourceId>& rxAnchorId)
+{
+ try
+ {
+ PresenterConfigurationAccess aConfiguration (
+ rxContext,
+ "org.openoffice.Office.PresenterScreen",
+ PresenterConfigurationAccess::READ_ONLY);
+ maViewDescriptors.clear();
+ ProcessViewDescriptions(aConfiguration);
+ OUString sLayoutName ("DefaultLayout");
+ aConfiguration.GetConfigurationNode(
+ "Presenter/CurrentLayout") >>= sLayoutName;
+ ProcessLayout(aConfiguration, sLayoutName, rxContext, rxAnchorId);
+ }
+ catch (const RuntimeException&)
+ {
+ }
+}
+
+void PresenterScreen::ProcessLayout (
+ PresenterConfigurationAccess& rConfiguration,
+ std::u16string_view rsLayoutName,
+ const Reference<XComponentContext>& rxContext,
+ const Reference<XResourceId>& rxAnchorId)
+{
+ try
+ {
+ Reference<container::XHierarchicalNameAccess> xLayoutNode (
+ rConfiguration.GetConfigurationNode(
+ OUString::Concat("Presenter/Layouts/")+rsLayoutName),
+ UNO_QUERY_THROW);
+
+ // Read the parent layout first, if one is referenced.
+ OUString sParentLayout;
+ PresenterConfigurationAccess::GetConfigurationNode(
+ xLayoutNode,
+ "ParentLayout") >>= sParentLayout;
+ if (!sParentLayout.isEmpty())
+ {
+ // Prevent infinite recursion.
+ if (rsLayoutName != sParentLayout)
+ ProcessLayout(rConfiguration, sParentLayout, rxContext, rxAnchorId);
+ }
+
+ // Process the actual layout list.
+ Reference<container::XNameAccess> xList (
+ PresenterConfigurationAccess::GetConfigurationNode(
+ xLayoutNode,
+ "Layout"),
+ UNO_QUERY_THROW);
+
+ ::std::vector<OUString> aProperties
+ {
+ "PaneURL",
+ "ViewURL",
+ "RelativeX",
+ "RelativeY",
+ "RelativeWidth",
+ "RelativeHeight"
+ };
+ PresenterConfigurationAccess::ForAll(
+ xList,
+ aProperties,
+ [this, rxContext, rxAnchorId](std::vector<uno::Any> const& rArgs)
+ {
+ this->ProcessComponent(rArgs, rxContext, rxAnchorId);
+ });
+ }
+ catch (const RuntimeException&)
+ {
+ }
+}
+
+void PresenterScreen::ProcessViewDescriptions (
+ PresenterConfigurationAccess& rConfiguration)
+{
+ try
+ {
+ Reference<container::XNameAccess> xViewDescriptionsNode (
+ rConfiguration.GetConfigurationNode("Presenter/Views"),
+ UNO_QUERY_THROW);
+
+ ::std::vector<OUString> aProperties
+ {
+ "ViewURL",
+ "Title",
+ "AccessibleTitle",
+ "IsOpaque"
+ };
+ PresenterConfigurationAccess::ForAll(
+ xViewDescriptionsNode,
+ aProperties,
+ [this](std::vector<uno::Any> const& rArgs)
+ {
+ return this->ProcessViewDescription(rArgs);
+ });
+ }
+ catch (const RuntimeException&)
+ {
+ OSL_ASSERT(false);
+ }
+}
+
+void PresenterScreen::ProcessComponent (
+ const ::std::vector<Any>& rValues,
+ const Reference<XComponentContext>& rxContext,
+ const Reference<XResourceId>& rxAnchorId)
+{
+ if (rValues.size() != 6)
+ return;
+
+ try
+ {
+ OUString sPaneURL;
+ OUString sViewURL;
+ double nX = 0;
+ double nY = 0;
+ double nWidth = 0;
+ double nHeight = 0;
+ rValues[0] >>= sPaneURL;
+ rValues[1] >>= sViewURL;
+ rValues[2] >>= nX;
+ rValues[3] >>= nY;
+ rValues[4] >>= nWidth;
+ rValues[5] >>= nHeight;
+
+ if (nX>=0 && nY>=0 && nWidth>0 && nHeight>0)
+ {
+ SetupView(
+ rxContext,
+ rxAnchorId,
+ sPaneURL,
+ sViewURL,
+ PresenterPaneContainer::ViewInitializationFunction());
+ }
+ }
+ catch (const Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+}
+
+void PresenterScreen::ProcessViewDescription (
+ const ::std::vector<Any>& rValues)
+{
+ if (rValues.size() != 4)
+ return;
+
+ try
+ {
+ ViewDescriptor aViewDescriptor;
+ OUString sViewURL;
+ rValues[0] >>= sViewURL;
+ rValues[1] >>= aViewDescriptor.msTitle;
+ rValues[2] >>= aViewDescriptor.msAccessibleTitle;
+ rValues[3] >>= aViewDescriptor.mbIsOpaque;
+ if (aViewDescriptor.msAccessibleTitle.isEmpty())
+ aViewDescriptor.msAccessibleTitle = aViewDescriptor.msTitle;
+ maViewDescriptors[sViewURL] = aViewDescriptor;
+ }
+ catch (const Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+}
+
+void PresenterScreen::SetupView(
+ const Reference<XComponentContext>& rxContext,
+ const Reference<XResourceId>& rxAnchorId,
+ const OUString& rsPaneURL,
+ const OUString& rsViewURL,
+ const PresenterPaneContainer::ViewInitializationFunction& rViewInitialization)
+{
+ Reference<XConfigurationController> xCC (mxConfigurationControllerWeak);
+ if (!xCC.is())
+ return;
+
+ Reference<XResourceId> xPaneId (ResourceId::createWithAnchor(rxContext,rsPaneURL,rxAnchorId));
+ // Look up the view descriptor.
+ ViewDescriptor aViewDescriptor;
+ ViewDescriptorContainer::const_iterator iDescriptor (maViewDescriptors.find(rsViewURL));
+ if (iDescriptor != maViewDescriptors.end())
+ aViewDescriptor = iDescriptor->second;
+
+ // Prepare the pane.
+ OSL_ASSERT(mpPaneContainer);
+ mpPaneContainer->PreparePane(
+ xPaneId,
+ rsViewURL,
+ aViewDescriptor.msTitle,
+ aViewDescriptor.msAccessibleTitle,
+ aViewDescriptor.mbIsOpaque,
+ rViewInitialization);
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterScreen.hxx b/sdext/source/presenter/PresenterScreen.hxx
new file mode 100644
index 000000000..e696c0dc6
--- /dev/null
+++ b/sdext/source/presenter/PresenterScreen.hxx
@@ -0,0 +1,227 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSCREEN_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSCREEN_HXX
+
+#include "PresenterConfigurationAccess.hxx"
+#include "PresenterPaneContainer.hxx"
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XModel2.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <com/sun/star/presentation/XPresentation2.hpp>
+#include <rtl/ref.hxx>
+
+#include <map>
+#include <string_view>
+
+namespace sdext::presenter {
+
+class PresenterController;
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::task::XJob, css::lang::XServiceInfo
+ > PresenterScreenJobInterfaceBase;
+
+/** The PresenterScreenJob service is instantiated every time a document is
+ created or loaded. In its execute() method it then filters out all
+ non-Impress documents and creates and registers a new PresenterScreen
+ object.
+*/
+class PresenterScreenJob
+ : private ::cppu::BaseMutex,
+ public PresenterScreenJobInterfaceBase
+{
+public:
+ PresenterScreenJob(const PresenterScreenJob&) = delete;
+ PresenterScreenJob& operator=(const PresenterScreenJob&) = delete;
+
+ virtual void SAL_CALL disposing() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override;
+
+ // XJob
+ virtual css::uno::Any SAL_CALL execute(
+ const css::uno::Sequence<css::beans::NamedValue >& Arguments) override;
+
+ explicit PresenterScreenJob (const css::uno::Reference<css::uno::XComponentContext>& rxContext);
+ virtual ~PresenterScreenJob() override;
+
+private:
+ css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+};
+
+/** This is the bootstrap class of the presenter screen. It is registered
+ as drawing framework startup service. That means that every drawing
+ framework instance creates an instance of this class.
+
+ <p>A PresenterScreen object registers itself as listener for drawing
+ framework configuration changes. It waits for the full screen marker (a
+ top level resource) to appear in the current configuration. When that
+ happens the actual presenter screen is initialized. A new
+ PresenterController is created and takes over the task of controlling
+ the presenter screen.</p>
+*/
+typedef ::cppu::WeakComponentImplHelper <
+ css::lang::XEventListener
+ > PresenterScreenInterfaceBase;
+class PresenterScreen
+ : private ::cppu::BaseMutex,
+ public PresenterScreenInterfaceBase
+{
+public:
+ PresenterScreen (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::frame::XModel2>& rxModel);
+ virtual ~PresenterScreen() override;
+ PresenterScreen(const PresenterScreen&) = delete;
+ PresenterScreen& operator=(const PresenterScreen&) = delete;
+
+ virtual void SAL_CALL disposing() override;
+
+ static bool isPresenterScreenEnabled(
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext);
+ /** Make the presenter screen visible.
+ */
+ void InitializePresenterScreen();
+
+ /** Do not call ShutdownPresenterScreen() directly. Call
+ RequestShutdownPresenterScreen() instead. It will issue an
+ asynchronous call to ShutdownPresenterScreen() when that is safe.
+ */
+ void RequestShutdownPresenterScreen();
+
+ /** Switch / converse monitors between presenter view and slide output
+ */
+ void SwitchMonitors();
+
+ // XEventListener
+
+ virtual void SAL_CALL disposing ( const css::lang::EventObject& rEvent) override;
+
+private:
+ css::uno::Reference<css::frame::XModel2 > mxModel;
+ css::uno::Reference<css::frame::XController> mxController;
+ css::uno::WeakReference<css::drawing::framework::XConfigurationController>
+ mxConfigurationControllerWeak;
+ css::uno::WeakReference<css::uno::XComponentContext> mxContextWeak;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ css::uno::Reference<css::drawing::framework::XConfiguration> mxSavedConfiguration;
+ ::rtl::Reference<PresenterPaneContainer> mpPaneContainer;
+ css::uno::Reference<css::drawing::framework::XResourceFactory> mxPaneFactory;
+ css::uno::Reference<css::drawing::framework::XResourceFactory> mxViewFactory;
+
+ class ViewDescriptor
+ {
+ public:
+ OUString msTitle;
+ OUString msAccessibleTitle;
+ bool mbIsOpaque;
+ ViewDescriptor()
+ : mbIsOpaque(false)
+ {
+ }
+ };
+ typedef ::std::map<OUString,ViewDescriptor> ViewDescriptorContainer;
+ ViewDescriptorContainer maViewDescriptors;
+
+ void ShutdownPresenterScreen();
+
+ /** Create and initialize the factory for presenter view specific panes.
+ */
+ void SetupPaneFactory (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext);
+
+ /** Create and initialize the factory for presenter view specific views.
+ */
+ void SetupViewFactory (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext);
+
+ /** Read the current layout from the configuration and call
+ ProcessLayout to bring it on to the screen.
+ */
+ void SetupConfiguration (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxAnchorId);
+
+ /** Read one layout from the configuration and make resource activation
+ requests to bring it on to the screen. When one layout references a
+ parent layout then this method calls itself recursively.
+ */
+ void ProcessLayout (
+ PresenterConfigurationAccess& rConfiguration,
+ std::u16string_view rsLayoutName,
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxAnchorId);
+
+ /** Called by ProcessLayout for a single entry of a Layouts
+ configuration list.
+ */
+ void ProcessComponent (
+ const ::std::vector<css::uno::Any>& rValues,
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxAnchorId);
+
+ /** Read the view descriptions from the configuration.
+ */
+ void ProcessViewDescriptions (
+ PresenterConfigurationAccess& rConfiguration);
+
+ /** Called by ProcessViewDescriptions for a single entry.
+ */
+ void ProcessViewDescription (
+ const ::std::vector<css::uno::Any>& rValues);
+
+ void SetupView (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxAnchorId,
+ const OUString& rsPaneURL,
+ const OUString& rsViewURL,
+ const PresenterPaneContainer::ViewInitializationFunction& rViewInitialization);
+
+ /** Return the built-in screen number on the presentation will normally
+ display the presenter console.
+ @return
+ Returns -1 when the presenter screen can or shall not be
+ displayed.
+ */
+ sal_Int32 GetPresenterScreenNumber (
+ const css::uno::Reference<css::presentation::XPresentation2>& rxPresentation) const;
+
+ static sal_Int32 GetPresenterScreenFromScreen( sal_Int32 nPresentationScreen );
+
+ /** Create a resource id for the full screen background pane so that it
+ is displayed on another screen than the full screen presentation.
+ */
+ css::uno::Reference<css::drawing::framework::XResourceId> GetMainPaneId (
+ const css::uno::Reference<css::presentation::XPresentation2>& rxPresentation) const;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterScrollBar.cxx b/sdext/source/presenter/PresenterScrollBar.cxx
new file mode 100644
index 000000000..a4db80277
--- /dev/null
+++ b/sdext/source/presenter/PresenterScrollBar.cxx
@@ -0,0 +1,824 @@
+/* -*- 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 "PresenterScrollBar.hxx"
+#include "PresenterBitmapContainer.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterGeometryHelper.hxx"
+#include "PresenterPaintManager.hxx"
+#include "PresenterTimer.hxx"
+#include "PresenterUIPainter.hxx"
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/XPolyPolygon2D.hpp>
+
+#include <algorithm>
+#include <memory>
+#include <math.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+const double gnScrollBarGap (10);
+
+namespace sdext::presenter {
+
+//===== PresenterScrollBar::MousePressRepeater ================================
+
+class PresenterScrollBar::MousePressRepeater
+ : public std::enable_shared_from_this<MousePressRepeater>
+{
+public:
+ explicit MousePressRepeater (const ::rtl::Reference<PresenterScrollBar>& rpScrollBar);
+ void Dispose();
+ void Start (const PresenterScrollBar::Area& reArea);
+ void Stop();
+ void SetMouseArea (const PresenterScrollBar::Area& reArea);
+
+private:
+ void Callback ();
+ void Execute();
+
+ sal_Int32 mnMousePressRepeaterTaskId;
+ ::rtl::Reference<PresenterScrollBar> mpScrollBar;
+ PresenterScrollBar::Area meMouseArea;
+};
+
+//===== PresenterScrollBar ====================================================
+
+std::weak_ptr<PresenterBitmapContainer> PresenterScrollBar::mpSharedBitmaps;
+
+PresenterScrollBar::PresenterScrollBar (
+ const Reference<XComponentContext>& rxComponentContext,
+ const Reference<awt::XWindow>& rxParentWindow,
+ const std::shared_ptr<PresenterPaintManager>& rpPaintManager,
+ const ::std::function<void (double)>& rThumbMotionListener)
+ : PresenterScrollBarInterfaceBase(m_aMutex),
+ mxComponentContext(rxComponentContext),
+ mpPaintManager(rpPaintManager),
+ mnThumbPosition(0),
+ mnTotalSize(0),
+ mnThumbSize(0),
+ mnLineHeight(10),
+ maDragAnchor(-1,-1),
+ maThumbMotionListener(rThumbMotionListener),
+ meButtonDownArea(None),
+ meMouseMoveArea(None),
+ mbIsNotificationActive(false),
+ mpMousePressRepeater(std::make_shared<MousePressRepeater>(this)),
+ mpCanvasHelper(new PresenterCanvasHelper())
+{
+ try
+ {
+ Reference<lang::XMultiComponentFactory> xFactory (rxComponentContext->getServiceManager());
+ if ( ! xFactory.is())
+ throw RuntimeException();
+
+ mxPresenterHelper.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.comp.Draw.PresenterHelper",
+ rxComponentContext),
+ UNO_QUERY_THROW);
+
+ if (mxPresenterHelper.is())
+ mxWindow = mxPresenterHelper->createWindow(rxParentWindow,
+ false,
+ false,
+ false,
+ false);
+
+ // Make the background transparent. The slide show paints its own background.
+ Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY_THROW);
+ xPeer->setBackground(0xff000000);
+
+ mxWindow->setVisible(true);
+ mxWindow->addWindowListener(this);
+ mxWindow->addPaintListener(this);
+ mxWindow->addMouseListener(this);
+ mxWindow->addMouseMotionListener(this);
+ }
+ catch (RuntimeException&)
+ {
+ }
+}
+
+PresenterScrollBar::~PresenterScrollBar()
+{
+}
+
+void SAL_CALL PresenterScrollBar::disposing()
+{
+ mpMousePressRepeater->Dispose();
+
+ if (mxWindow.is())
+ {
+ mxWindow->removeWindowListener(this);
+ mxWindow->removePaintListener(this);
+ mxWindow->removeMouseListener(this);
+ mxWindow->removeMouseMotionListener(this);
+
+ Reference<lang::XComponent> xComponent = mxWindow;
+ mxWindow = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ mpBitmaps.reset();
+}
+
+void PresenterScrollBar::SetVisible (const bool bIsVisible)
+{
+ if (mxWindow.is())
+ mxWindow->setVisible(bIsVisible);
+}
+
+void PresenterScrollBar::SetPosSize (const css::geometry::RealRectangle2D& rBox)
+{
+ if (mxWindow.is())
+ {
+ mxWindow->setPosSize(
+ sal_Int32(floor(rBox.X1)),
+ sal_Int32(ceil(rBox.Y1)),
+ sal_Int32(ceil(rBox.X2-rBox.X1)),
+ sal_Int32(floor(rBox.Y2-rBox.Y1)),
+ awt::PosSize::POSSIZE);
+ UpdateBorders();
+ }
+}
+
+void PresenterScrollBar::SetThumbPosition (
+ double nPosition,
+ const bool bAsynchronousUpdate)
+{
+ nPosition = ValidateThumbPosition(nPosition);
+
+ if (nPosition == mnThumbPosition || mbIsNotificationActive)
+ return;
+
+ mnThumbPosition = nPosition;
+
+ UpdateBorders();
+ Repaint(GetRectangle(Total), bAsynchronousUpdate);
+
+ mbIsNotificationActive = true;
+ try
+ {
+ maThumbMotionListener(mnThumbPosition);
+ }
+ catch (Exception&)
+ {
+ }
+ mbIsNotificationActive = false;
+}
+
+
+void PresenterScrollBar::SetTotalSize (const double nTotalSize)
+{
+ if (mnTotalSize != nTotalSize)
+ {
+ mnTotalSize = nTotalSize + 1;
+ UpdateBorders();
+ Repaint(GetRectangle(Total), false);
+ }
+}
+
+void PresenterScrollBar::SetThumbSize (const double nThumbSize)
+{
+ OSL_ASSERT(nThumbSize>=0);
+ if (mnThumbSize != nThumbSize)
+ {
+ mnThumbSize = nThumbSize;
+ UpdateBorders();
+ Repaint(GetRectangle(Total), false);
+ }
+}
+
+
+void PresenterScrollBar::SetLineHeight (const double nLineHeight)
+{
+ mnLineHeight = nLineHeight;
+}
+
+
+void PresenterScrollBar::SetCanvas (const Reference<css::rendering::XCanvas>& rxCanvas)
+{
+ if (mxCanvas == rxCanvas)
+ return;
+
+ mxCanvas = rxCanvas;
+ if (!mxCanvas.is())
+ return;
+
+ if (mpBitmaps == nullptr)
+ {
+ mpBitmaps = mpSharedBitmaps.lock();
+ if (!mpBitmaps)
+ {
+ try
+ {
+ mpBitmaps = std::make_shared<PresenterBitmapContainer>(
+ "PresenterScreenSettings/ScrollBar/Bitmaps",
+ std::shared_ptr<PresenterBitmapContainer>(),
+ mxComponentContext,
+ mxCanvas);
+ mpSharedBitmaps = mpBitmaps;
+ }
+ catch(Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+ }
+ UpdateBitmaps();
+ UpdateBorders();
+ }
+
+ Repaint(GetRectangle(Total), false);
+}
+
+void PresenterScrollBar::SetBackground (const SharedBitmapDescriptor& rpBackgroundBitmap)
+{
+ mpBackgroundBitmap = rpBackgroundBitmap;
+}
+
+void PresenterScrollBar::CheckValues()
+{
+ mnThumbPosition = ValidateThumbPosition(mnThumbPosition);
+}
+
+double PresenterScrollBar::ValidateThumbPosition (double nPosition)
+{
+ if (nPosition + mnThumbSize > mnTotalSize)
+ nPosition = mnTotalSize - mnThumbSize;
+ if (nPosition < 0)
+ nPosition = 0;
+ return nPosition;
+}
+
+void PresenterScrollBar::Paint (
+ const awt::Rectangle& rUpdateBox)
+{
+ if ( ! mxCanvas.is() || ! mxWindow.is())
+ {
+ OSL_ASSERT(mxCanvas.is());
+ OSL_ASSERT(mxWindow.is());
+ return;
+ }
+
+ if (PresenterGeometryHelper::AreRectanglesDisjoint (rUpdateBox, mxWindow->getPosSize()))
+ return;
+
+ PaintBackground(rUpdateBox);
+ PaintComposite(rUpdateBox, PagerUp,
+ mpPagerStartDescriptor, mpPagerCenterDescriptor, SharedBitmapDescriptor());
+ PaintComposite(rUpdateBox, PagerDown,
+ SharedBitmapDescriptor(), mpPagerCenterDescriptor, mpPagerEndDescriptor);
+ PaintComposite(rUpdateBox, Thumb,
+ mpThumbStartDescriptor, mpThumbCenterDescriptor, mpThumbEndDescriptor);
+ PaintBitmap(rUpdateBox, PrevButton, mpPrevButtonDescriptor);
+ PaintBitmap(rUpdateBox, NextButton, mpNextButtonDescriptor);
+
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(false);
+}
+
+//----- XWindowListener -------------------------------------------------------
+
+void SAL_CALL PresenterScrollBar::windowResized (const css::awt::WindowEvent&) {}
+
+void SAL_CALL PresenterScrollBar::windowMoved (const css::awt::WindowEvent&) {}
+
+void SAL_CALL PresenterScrollBar::windowShown (const css::lang::EventObject&) {}
+
+void SAL_CALL PresenterScrollBar::windowHidden (const css::lang::EventObject&) {}
+
+//----- XPaintListener --------------------------------------------------------
+
+void SAL_CALL PresenterScrollBar::windowPaint (const css::awt::PaintEvent& rEvent)
+{
+ if (mxWindow.is())
+ {
+ awt::Rectangle aRepaintBox (rEvent.UpdateRect);
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ aRepaintBox.X += aWindowBox.X;
+ aRepaintBox.Y += aWindowBox.Y;
+ Paint(aRepaintBox);
+
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(false);
+ }
+}
+
+//----- XMouseListener --------------------------------------------------------
+
+void SAL_CALL PresenterScrollBar::mousePressed (const css::awt::MouseEvent& rEvent)
+{
+ maDragAnchor.X = rEvent.X;
+ maDragAnchor.Y = rEvent.Y;
+ meButtonDownArea = GetArea(rEvent.X, rEvent.Y);
+
+ mpMousePressRepeater->Start(meButtonDownArea);
+}
+
+void SAL_CALL PresenterScrollBar::mouseReleased (const css::awt::MouseEvent&)
+{
+ mpMousePressRepeater->Stop();
+
+ if (mxPresenterHelper.is())
+ mxPresenterHelper->releaseMouse(mxWindow);
+}
+
+void SAL_CALL PresenterScrollBar::mouseEntered (const css::awt::MouseEvent&) {}
+
+void SAL_CALL PresenterScrollBar::mouseExited (const css::awt::MouseEvent&)
+{
+ if (meMouseMoveArea != None)
+ {
+ const Area eOldMouseMoveArea (meMouseMoveArea);
+ meMouseMoveArea = None;
+ Repaint(GetRectangle(eOldMouseMoveArea), true);
+ }
+ meButtonDownArea = None;
+ meMouseMoveArea = None;
+
+ mpMousePressRepeater->Stop();
+}
+
+//----- XMouseMotionListener --------------------------------------------------
+
+void SAL_CALL PresenterScrollBar::mouseMoved (const css::awt::MouseEvent& rEvent)
+{
+ const Area eArea (GetArea(rEvent.X, rEvent.Y));
+ if (eArea != meMouseMoveArea)
+ {
+ const Area eOldMouseMoveArea (meMouseMoveArea);
+ meMouseMoveArea = eArea;
+ if (eOldMouseMoveArea != None)
+ Repaint(GetRectangle(eOldMouseMoveArea), meMouseMoveArea==None);
+ if (meMouseMoveArea != None)
+ Repaint(GetRectangle(meMouseMoveArea), true);
+ }
+ mpMousePressRepeater->SetMouseArea(eArea);
+}
+
+void SAL_CALL PresenterScrollBar::mouseDragged (const css::awt::MouseEvent& rEvent)
+{
+ if (meButtonDownArea != Thumb)
+ return;
+
+ mpMousePressRepeater->Stop();
+
+ if (mxPresenterHelper.is())
+ mxPresenterHelper->captureMouse(mxWindow);
+
+ const double nDragDistance (GetDragDistance(rEvent.X,rEvent.Y));
+ UpdateDragAnchor(nDragDistance);
+ if (nDragDistance != 0)
+ {
+ SetThumbPosition(mnThumbPosition + nDragDistance, false);
+ }
+}
+
+//----- lang::XEventListener --------------------------------------------------
+
+void SAL_CALL PresenterScrollBar::disposing (const css::lang::EventObject& rEvent)
+{
+ if (rEvent.Source == mxWindow)
+ mxWindow = nullptr;
+}
+
+
+geometry::RealRectangle2D const & PresenterScrollBar::GetRectangle (const Area eArea) const
+{
+ OSL_ASSERT(eArea>=0 && eArea<AreaCount);
+
+ return maBox[eArea];
+}
+
+void PresenterScrollBar::Repaint (
+ const geometry::RealRectangle2D& rBox,
+ const bool bAsynchronousUpdate)
+{
+ if (mpPaintManager != nullptr)
+ mpPaintManager->Invalidate(
+ mxWindow,
+ PresenterGeometryHelper::ConvertRectangle(rBox),
+ bAsynchronousUpdate);
+}
+
+void PresenterScrollBar::PaintBackground(
+ const css::awt::Rectangle& rUpdateBox)
+{
+ if (!mpBackgroundBitmap)
+ return;
+
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ mpCanvasHelper->Paint(
+ mpBackgroundBitmap,
+ mxCanvas,
+ rUpdateBox,
+ aWindowBox,
+ awt::Rectangle());
+}
+
+void PresenterScrollBar::PaintBitmap(
+ const css::awt::Rectangle& rUpdateBox,
+ const Area eArea,
+ const SharedBitmapDescriptor& rpBitmaps)
+{
+ const geometry::RealRectangle2D aLocalBox (GetRectangle(eArea));
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ geometry::RealRectangle2D aBox (aLocalBox);
+ aBox.X1 += aWindowBox.X;
+ aBox.Y1 += aWindowBox.Y;
+ aBox.X2 += aWindowBox.X;
+ aBox.Y2 += aWindowBox.Y;
+
+ Reference<rendering::XBitmap> xBitmap (GetBitmap(eArea,rpBitmaps));
+
+ if (!xBitmap.is())
+ return;
+
+ Reference<rendering::XPolyPolygon2D> xClipPolygon (
+ PresenterGeometryHelper::CreatePolygon(
+ PresenterGeometryHelper::Intersection(rUpdateBox,
+ PresenterGeometryHelper::ConvertRectangle(aBox)),
+ mxCanvas->getDevice()));
+
+ const rendering::ViewState aViewState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ xClipPolygon);
+
+ const geometry::IntegerSize2D aBitmapSize (xBitmap->getSize());
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(
+ 1,0,aBox.X1 + (aBox.X2-aBox.X1 - aBitmapSize.Width)/2,
+ 0,1,aBox.Y1 + (aBox.Y2-aBox.Y1 - aBitmapSize.Height)/2),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ mxCanvas->drawBitmap(
+ xBitmap,
+ aViewState,
+ aRenderState);
+}
+
+PresenterScrollBar::Area PresenterScrollBar::GetArea (const double nX, const double nY) const
+{
+ const geometry::RealPoint2D aPoint(nX, nY);
+
+ if (PresenterGeometryHelper::IsInside(GetRectangle(Pager), aPoint))
+ {
+ if (PresenterGeometryHelper::IsInside(GetRectangle(Thumb), aPoint))
+ return Thumb;
+ else if (PresenterGeometryHelper::IsInside(GetRectangle(PagerUp), aPoint))
+ return PagerUp;
+ else if (PresenterGeometryHelper::IsInside(GetRectangle(PagerDown), aPoint))
+ return PagerDown;
+ }
+ else if (PresenterGeometryHelper::IsInside(GetRectangle(PrevButton), aPoint))
+ return PrevButton;
+ else if (PresenterGeometryHelper::IsInside(GetRectangle(NextButton), aPoint))
+ return NextButton;
+
+ return None;
+}
+
+void PresenterScrollBar::UpdateWidthOrHeight (
+ sal_Int32& rSize,
+ const SharedBitmapDescriptor& rpDescriptor)
+{
+ if (rpDescriptor)
+ {
+ Reference<rendering::XBitmap> xBitmap (rpDescriptor->GetNormalBitmap());
+ if (xBitmap.is())
+ {
+ const geometry::IntegerSize2D aBitmapSize (xBitmap->getSize());
+ const sal_Int32 nBitmapSize = static_cast<sal_Int32>(GetMinor(aBitmapSize.Width, aBitmapSize.Height));
+ if (nBitmapSize > rSize)
+ rSize = nBitmapSize;
+ }
+ }
+}
+
+css::uno::Reference<css::rendering::XBitmap> PresenterScrollBar::GetBitmap (
+ const Area eArea,
+ const SharedBitmapDescriptor& rpBitmaps) const
+{
+ if (!rpBitmaps)
+ return nullptr;
+ else
+ return rpBitmaps->GetBitmap(GetBitmapMode(eArea));
+}
+
+PresenterBitmapContainer::BitmapDescriptor::Mode PresenterScrollBar::GetBitmapMode (
+ const Area eArea) const
+{
+ if (IsDisabled(eArea))
+ return PresenterBitmapContainer::BitmapDescriptor::Disabled;
+ else if (eArea == meMouseMoveArea)
+ return PresenterBitmapContainer::BitmapDescriptor::MouseOver;
+ else
+ return PresenterBitmapContainer::BitmapDescriptor::Normal;
+}
+
+bool PresenterScrollBar::IsDisabled (const Area eArea) const
+{
+ OSL_ASSERT(eArea>=0 && eArea<AreaCount);
+
+ return ! maEnabledState[eArea];
+}
+
+//===== PresenterVerticalScrollBar ============================================
+
+PresenterVerticalScrollBar::PresenterVerticalScrollBar (
+ const Reference<XComponentContext>& rxComponentContext,
+ const Reference<awt::XWindow>& rxParentWindow,
+ const std::shared_ptr<PresenterPaintManager>& rpPaintManager,
+ const ::std::function<void (double)>& rThumbMotionListener)
+ : PresenterScrollBar(rxComponentContext, rxParentWindow, rpPaintManager, rThumbMotionListener),
+ mnScrollBarWidth(0)
+{
+}
+
+PresenterVerticalScrollBar::~PresenterVerticalScrollBar()
+{
+}
+
+double PresenterVerticalScrollBar::GetDragDistance (const sal_Int32, const sal_Int32 nY) const
+{
+ const double nDistance (nY - maDragAnchor.Y);
+ if (nDistance == 0)
+ return 0;
+ else
+ {
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ const double nBarWidth (aWindowBox.Width);
+ const double nPagerHeight (aWindowBox.Height - 2*nBarWidth);
+ const double nDragDistance (mnTotalSize / nPagerHeight * nDistance);
+ if (nDragDistance + mnThumbPosition < 0)
+ return -mnThumbPosition;
+ else if (mnThumbPosition + nDragDistance > mnTotalSize-mnThumbSize)
+ return mnTotalSize-mnThumbSize-mnThumbPosition;
+ else
+ return nDragDistance;
+ }
+}
+
+void PresenterVerticalScrollBar::UpdateDragAnchor (const double nDragDistance)
+{
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ const double nBarWidth (aWindowBox.Width);
+ const double nPagerHeight (aWindowBox.Height - 2*nBarWidth);
+ maDragAnchor.Y += nDragDistance * nPagerHeight / mnTotalSize;
+}
+
+sal_Int32 PresenterVerticalScrollBar::GetSize() const
+{
+ return mnScrollBarWidth;
+}
+
+double PresenterVerticalScrollBar::GetMinor (const double nX, const double) const
+{
+ return nX;
+}
+
+void PresenterVerticalScrollBar::UpdateBorders()
+{
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ double nBottom = aWindowBox.Height;
+
+ if (mpNextButtonDescriptor)
+ {
+ Reference<rendering::XBitmap> xBitmap (mpNextButtonDescriptor->GetNormalBitmap());
+ if (xBitmap.is())
+ {
+ geometry::IntegerSize2D aSize (xBitmap->getSize());
+ maBox[NextButton] = geometry::RealRectangle2D(
+ 0, nBottom - aSize.Height, aWindowBox.Width, nBottom);
+ nBottom -= aSize.Height + gnScrollBarGap;
+ }
+ }
+ if (mpPrevButtonDescriptor)
+ {
+ Reference<rendering::XBitmap> xBitmap (mpPrevButtonDescriptor->GetNormalBitmap());
+ if (xBitmap.is())
+ {
+ geometry::IntegerSize2D aSize (xBitmap->getSize());
+ maBox[PrevButton] = geometry::RealRectangle2D(
+ 0, nBottom - aSize.Height, aWindowBox.Width, nBottom);
+ nBottom -= aSize.Height + gnScrollBarGap;
+ }
+ }
+ const double nPagerHeight (nBottom);
+ maBox[Pager] = geometry::RealRectangle2D(
+ 0,0, aWindowBox.Width, nBottom);
+ if (mnTotalSize < 1)
+ {
+ maBox[Thumb] = maBox[Pager];
+
+ // Set up the enabled/disabled states.
+ maEnabledState[PrevButton] = false;
+ maEnabledState[PagerUp] = false;
+ maEnabledState[NextButton] = false;
+ maEnabledState[PagerDown] = false;
+ maEnabledState[Thumb] = false;
+ }
+ else
+ {
+ const double nThumbSize = ::std::min(mnThumbSize,mnTotalSize);
+ const double nThumbPosition = ::std::clamp(mnThumbPosition, 0.0, mnTotalSize - nThumbSize);
+ maBox[Thumb] = geometry::RealRectangle2D(
+ 0, nThumbPosition / mnTotalSize * nPagerHeight,
+ aWindowBox.Width,
+ (nThumbPosition+nThumbSize) / mnTotalSize * nPagerHeight);
+
+ // Set up the enabled/disabled states.
+ maEnabledState[PrevButton] = nThumbPosition>0;
+ maEnabledState[PagerUp] = nThumbPosition>0;
+ maEnabledState[NextButton] = nThumbPosition+nThumbSize < mnTotalSize;
+ maEnabledState[PagerDown] = nThumbPosition+nThumbSize < mnTotalSize;
+ maEnabledState[Thumb] = nThumbSize < mnTotalSize;
+ }
+ maBox[PagerUp] = geometry::RealRectangle2D(
+ maBox[Pager].X1, maBox[Pager].Y1, maBox[Pager].X2, maBox[Thumb].Y1-1);
+ maBox[PagerDown] = geometry::RealRectangle2D(
+ maBox[Pager].X1, maBox[Thumb].Y2+1, maBox[Pager].X2, maBox[Pager].Y2);
+ maBox[Total] = PresenterGeometryHelper::Union(
+ PresenterGeometryHelper::Union(maBox[PrevButton], maBox[NextButton]),
+ maBox[Pager]);
+}
+
+void PresenterVerticalScrollBar::UpdateBitmaps()
+{
+ if (mpBitmaps == nullptr)
+ return;
+
+ mpPrevButtonDescriptor = mpBitmaps->GetBitmap("Up");
+ mpNextButtonDescriptor = mpBitmaps->GetBitmap("Down");
+ mpPagerStartDescriptor = mpBitmaps->GetBitmap("PagerTop");
+ mpPagerCenterDescriptor = mpBitmaps->GetBitmap("PagerVertical");
+ mpPagerEndDescriptor = mpBitmaps->GetBitmap("PagerBottom");
+ mpThumbStartDescriptor = mpBitmaps->GetBitmap("ThumbTop");
+ mpThumbCenterDescriptor = mpBitmaps->GetBitmap("ThumbVertical");
+ mpThumbEndDescriptor = mpBitmaps->GetBitmap("ThumbBottom");
+
+ mnScrollBarWidth = 0;
+ UpdateWidthOrHeight(mnScrollBarWidth, mpPrevButtonDescriptor);
+ UpdateWidthOrHeight(mnScrollBarWidth, mpNextButtonDescriptor);
+ UpdateWidthOrHeight(mnScrollBarWidth, mpPagerStartDescriptor);
+ UpdateWidthOrHeight(mnScrollBarWidth, mpPagerCenterDescriptor);
+ UpdateWidthOrHeight(mnScrollBarWidth, mpPagerEndDescriptor);
+ UpdateWidthOrHeight(mnScrollBarWidth, mpThumbStartDescriptor);
+ UpdateWidthOrHeight(mnScrollBarWidth, mpThumbCenterDescriptor);
+ UpdateWidthOrHeight(mnScrollBarWidth, mpThumbEndDescriptor);
+ if (mnScrollBarWidth == 0)
+ mnScrollBarWidth = 20;
+}
+
+void PresenterVerticalScrollBar::PaintComposite(
+ const css::awt::Rectangle& rUpdateBox,
+ const Area eArea,
+ const SharedBitmapDescriptor& rpStartBitmaps,
+ const SharedBitmapDescriptor& rpCenterBitmaps,
+ const SharedBitmapDescriptor& rpEndBitmaps)
+{
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ geometry::RealRectangle2D aBox (GetRectangle(eArea));
+ aBox.X1 += aWindowBox.X;
+ aBox.Y1 += aWindowBox.Y;
+ aBox.X2 += aWindowBox.X;
+ aBox.Y2 += aWindowBox.Y;
+
+ // Get bitmaps and sizes.
+
+ PresenterUIPainter::PaintVerticalBitmapComposite(
+ mxCanvas,
+ rUpdateBox,
+ (eArea == Thumb
+ ? PresenterGeometryHelper::ConvertRectangleWithConstantSize(aBox)
+ : PresenterGeometryHelper::ConvertRectangle(aBox)),
+ GetBitmap(eArea, rpStartBitmaps),
+ GetBitmap(eArea, rpCenterBitmaps),
+ GetBitmap(eArea, rpEndBitmaps));
+}
+
+//===== PresenterScrollBar::MousePressRepeater ================================
+
+PresenterScrollBar::MousePressRepeater::MousePressRepeater (
+ const ::rtl::Reference<PresenterScrollBar>& rpScrollBar)
+ : mnMousePressRepeaterTaskId(PresenterTimer::NotAValidTaskId),
+ mpScrollBar(rpScrollBar),
+ meMouseArea(PresenterScrollBar::None)
+{
+}
+
+void PresenterScrollBar::MousePressRepeater::Dispose()
+{
+ Stop();
+ mpScrollBar = nullptr;
+}
+
+void PresenterScrollBar::MousePressRepeater::Start (const PresenterScrollBar::Area& reArea)
+{
+ meMouseArea = reArea;
+
+ if (mnMousePressRepeaterTaskId == PresenterTimer::NotAValidTaskId)
+ {
+ // Execute key press operation at least this one time.
+ Execute();
+
+ // Schedule repeated executions.
+ auto pThis(shared_from_this());
+ mnMousePressRepeaterTaskId = PresenterTimer::ScheduleRepeatedTask (
+ mpScrollBar->GetComponentContext(),
+ [pThis] (TimeValue const&) { return pThis->Callback(); },
+ 500000000,
+ 250000000);
+ }
+ else
+ {
+ // There is already an active repeating task.
+ }
+}
+
+void PresenterScrollBar::MousePressRepeater::Stop()
+{
+ if (mnMousePressRepeaterTaskId != PresenterTimer::NotAValidTaskId)
+ {
+ const sal_Int32 nTaskId (mnMousePressRepeaterTaskId);
+ mnMousePressRepeaterTaskId = PresenterTimer::NotAValidTaskId;
+ PresenterTimer::CancelTask(nTaskId);
+ }
+}
+
+void PresenterScrollBar::MousePressRepeater::SetMouseArea(const PresenterScrollBar::Area& reArea)
+{
+ if (meMouseArea != reArea)
+ {
+ if (mnMousePressRepeaterTaskId != PresenterTimer::NotAValidTaskId)
+ {
+ Stop();
+ }
+ }
+}
+
+void PresenterScrollBar::MousePressRepeater::Callback ()
+{
+ if (!mpScrollBar)
+ {
+ Stop();
+ return;
+ }
+
+ Execute();
+}
+
+void PresenterScrollBar::MousePressRepeater::Execute()
+{
+ const double nThumbPosition (mpScrollBar->GetThumbPosition());
+ switch (meMouseArea)
+ {
+ case PrevButton:
+ mpScrollBar->SetThumbPosition(nThumbPosition - mpScrollBar->GetLineHeight(), true);
+ break;
+
+ case NextButton:
+ mpScrollBar->SetThumbPosition(nThumbPosition + mpScrollBar->GetLineHeight(), true);
+ break;
+
+ case PagerUp:
+ mpScrollBar->SetThumbPosition(nThumbPosition - mpScrollBar->GetThumbSize()*0.8, true);
+ break;
+
+ case PagerDown:
+ mpScrollBar->SetThumbPosition(nThumbPosition + mpScrollBar->GetThumbSize()*0.8, true);
+ break;
+
+ default:
+ break;
+ }
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterScrollBar.hxx b/sdext/source/presenter/PresenterScrollBar.hxx
new file mode 100644
index 000000000..b131c8a43
--- /dev/null
+++ b/sdext/source/presenter/PresenterScrollBar.hxx
@@ -0,0 +1,257 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSCROLLBAR_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSCROLLBAR_HXX
+
+#include "PresenterBitmapContainer.hxx"
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/drawing/XPresenterHelper.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+
+#include <functional>
+#include <memory>
+
+namespace sdext::presenter {
+
+class PresenterCanvasHelper;
+class PresenterPaintManager;
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::awt::XWindowListener,
+ css::awt::XPaintListener,
+ css::awt::XMouseListener,
+ css::awt::XMouseMotionListener
+> PresenterScrollBarInterfaceBase;
+
+/** Base class of horizontal and vertical scroll bars.
+*/
+class PresenterScrollBar
+ : private ::cppu::BaseMutex,
+ public PresenterScrollBarInterfaceBase
+{
+public:
+ virtual ~PresenterScrollBar() override;
+ PresenterScrollBar(const PresenterScrollBar&) = delete;
+ PresenterScrollBar& operator=(const PresenterScrollBar&) = delete;
+
+ virtual void SAL_CALL disposing() override;
+
+ css::uno::Reference<css::uno::XComponentContext> const&
+ GetComponentContext() const { return mxComponentContext; }
+
+ void SetVisible (const bool bIsVisible);
+
+ /** Set the bounding box of the scroll bar.
+ */
+ void SetPosSize (const css::geometry::RealRectangle2D& rBox);
+
+ /** Set the position of the movable thumb.
+ @param nPosition
+ A value between 0 and the last value given to SetTotalSize()
+ minus the last value given to SetThumbSize().
+ */
+ void SetThumbPosition (
+ double nPosition,
+ const bool bAsynchronousRepaint);
+
+ double GetThumbPosition() const { return mnThumbPosition;}
+
+ /** Set the upper border of the slider range.
+ */
+ void SetTotalSize (const double nTotalSize);
+
+ /** Set the size of the movable thumb.
+ @param nThumbSize
+ A value not larger than the last value given to SetTotalSize().
+ */
+ void SetThumbSize (const double nThumbSize);
+ double GetThumbSize() const { return mnThumbSize;}
+
+ void SetLineHeight (const double nLineHeight);
+ double GetLineHeight() const { return mnLineHeight;}
+
+ /** Set the canvas that is used for painting the scroll bar.
+ */
+ void SetCanvas (const css::uno::Reference<css::rendering::XCanvas>& rxCanvas);
+
+ void SetBackground (const SharedBitmapDescriptor& rpBackgroundBitmap);
+
+ /** Call this after changing total size or thumb position or size to
+ move the thumb to a valid position.
+ */
+ void CheckValues();
+
+ /** On some occasions it is necessary to trigger the painting of a
+ scrollbar from the outside.
+ */
+ void Paint (
+ const css::awt::Rectangle& rUpdateBox);
+
+ virtual sal_Int32 GetSize() const = 0;
+
+ // XWindowListener
+
+ virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override;
+
+ // XPaintListener
+
+ virtual void SAL_CALL windowPaint (const css::awt::PaintEvent& rEvent) override;
+
+ // XMouseListener
+
+ virtual void SAL_CALL mousePressed (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseReleased (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseEntered (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseExited (const css::awt::MouseEvent& rEvent) override;
+
+ // XMouseMotionListener
+
+ virtual void SAL_CALL mouseMoved (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseDragged (const css::awt::MouseEvent& rEvent) override;
+
+ // lang::XEventListener
+ virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override;
+
+ enum Area { Total, Pager, Thumb, PagerUp, PagerDown, PrevButton, NextButton, None,
+ AreaCount = None };
+
+protected:
+ css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+ css::uno::Reference<css::awt::XWindow> mxWindow;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+ css::uno::Reference<css::drawing::XPresenterHelper> mxPresenterHelper;
+ std::shared_ptr<PresenterPaintManager> mpPaintManager;
+ double mnThumbPosition;
+ double mnTotalSize;
+ double mnThumbSize;
+ double mnLineHeight;
+ css::geometry::RealPoint2D maDragAnchor;
+ ::std::function<void (double)> maThumbMotionListener;
+ Area meButtonDownArea;
+ Area meMouseMoveArea;
+ css::geometry::RealRectangle2D maBox[AreaCount];
+ bool mbIsNotificationActive;
+ static std::weak_ptr<PresenterBitmapContainer> mpSharedBitmaps;
+ std::shared_ptr<PresenterBitmapContainer> mpBitmaps;
+ SharedBitmapDescriptor mpPrevButtonDescriptor;
+ SharedBitmapDescriptor mpNextButtonDescriptor;
+ SharedBitmapDescriptor mpPagerStartDescriptor;
+ SharedBitmapDescriptor mpPagerCenterDescriptor;
+ SharedBitmapDescriptor mpPagerEndDescriptor;
+ SharedBitmapDescriptor mpThumbStartDescriptor;
+ SharedBitmapDescriptor mpThumbCenterDescriptor;
+ SharedBitmapDescriptor mpThumbEndDescriptor;
+ bool maEnabledState[AreaCount];
+
+ css::geometry::RealRectangle2D const & GetRectangle (const Area eArea) const;
+ virtual double GetDragDistance (const sal_Int32 nX, const sal_Int32 nY) const = 0;
+ virtual void UpdateDragAnchor (const double nDragDistance) = 0;
+ virtual double GetMinor (const double nX, const double nY) const = 0;
+ virtual void UpdateBorders() = 0;
+ virtual void UpdateBitmaps() = 0;
+ virtual void PaintComposite(
+ const css::awt::Rectangle& rRepaintBox,
+ const Area eArea,
+ const SharedBitmapDescriptor& rpStartBitmaps,
+ const SharedBitmapDescriptor& rpCenterBitmaps,
+ const SharedBitmapDescriptor& rpEndBitmaps) = 0;
+
+ PresenterScrollBar (
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow,
+ const std::shared_ptr<PresenterPaintManager>& rpPaintManager,
+ const ::std::function<void (double)>& rThumbMotionListener);
+
+ void Repaint (
+ const css::geometry::RealRectangle2D& rBox,
+ const bool bAsynchronous);
+ void PaintBackground (
+ const css::awt::Rectangle& rRepaintBox);
+ void PaintBitmap(
+ const css::awt::Rectangle& rRepaintBox,
+ const Area eArea,
+ const SharedBitmapDescriptor& rpBitmaps);
+ void UpdateWidthOrHeight (sal_Int32& rSize,
+ const SharedBitmapDescriptor& rpDescriptor);
+ css::uno::Reference<css::rendering::XBitmap> GetBitmap (
+ const Area eArea,
+ const SharedBitmapDescriptor& rpBitmaps) const;
+ PresenterBitmapContainer::BitmapDescriptor::Mode GetBitmapMode (
+ const Area eArea) const;
+ bool IsDisabled (const Area eArea) const;
+ double ValidateThumbPosition (double nPosition);
+
+private:
+ class MousePressRepeater;
+ std::shared_ptr<MousePressRepeater> mpMousePressRepeater;
+ SharedBitmapDescriptor mpBackgroundBitmap;
+ std::unique_ptr<PresenterCanvasHelper> mpCanvasHelper;
+
+ Area GetArea (const double nX, const double nY) const;
+};
+
+/** A vertical scroll bar.
+*/
+class PresenterVerticalScrollBar : public PresenterScrollBar
+{
+public:
+ PresenterVerticalScrollBar (
+ const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow,
+ const std::shared_ptr<PresenterPaintManager>& rpPaintManager,
+ const ::std::function<void (double)>& rThumbMotionListener);
+ virtual ~PresenterVerticalScrollBar() override;
+ virtual sal_Int32 GetSize() const override;
+
+protected:
+ virtual double GetDragDistance (const sal_Int32 nX, const sal_Int32 nY) const override;
+ virtual void UpdateDragAnchor (const double nDragDistance) override;
+ virtual double GetMinor (const double nX, const double nY) const override;
+ virtual void UpdateBorders() override;
+ virtual void UpdateBitmaps() override;
+ virtual void PaintComposite(
+ const css::awt::Rectangle& rRepaintBox,
+ const Area eArea,
+ const SharedBitmapDescriptor& rpStartBitmaps,
+ const SharedBitmapDescriptor& rpCenterBitmaps,
+ const SharedBitmapDescriptor& rpEndBitmaps) override;
+
+private:
+ sal_Int32 mnScrollBarWidth;
+};
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterSlidePreview.cxx b/sdext/source/presenter/PresenterSlidePreview.cxx
new file mode 100644
index 000000000..9de4c8820
--- /dev/null
+++ b/sdext/source/presenter/PresenterSlidePreview.cxx
@@ -0,0 +1,352 @@
+/* -*- 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 "PresenterSlidePreview.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterGeometryHelper.hxx"
+#include "PresenterPaintManager.hxx"
+#include "PresenterBitmapContainer.hxx"
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace
+{
+ // Use a super sample factor greater than 1 to achieve a poor mans
+ // antialiasing effect for slide previews.
+ const sal_Int16 gnSuperSampleFactor = 2;
+}
+
+namespace sdext::presenter {
+
+//===== PresenterSlidePreview =================================================
+
+PresenterSlidePreview::PresenterSlidePreview (
+ const Reference<XComponentContext>& rxContext,
+ const Reference<XResourceId>& rxViewId,
+ const Reference<XPane>& rxAnchorPane,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterSlidePreviewInterfaceBase(m_aMutex),
+ mpPresenterController(rpPresenterController),
+ mxViewId(rxViewId),
+ mnSlideAspectRatio(28.0 / 21.0)
+{
+ if ( ! rxContext.is()
+ || ! rxViewId.is()
+ || ! rxAnchorPane.is()
+ || ! rpPresenterController.is())
+ {
+ throw RuntimeException(
+ "PresenterSlidePreview can not be constructed due to empty argument",
+ static_cast<XWeak*>(this));
+ }
+
+ mxWindow = rxAnchorPane->getWindow();
+ mxCanvas = rxAnchorPane->getCanvas();
+
+ if (mxWindow.is())
+ {
+ mxWindow->addWindowListener(this);
+ mxWindow->addPaintListener(this);
+
+ Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY);
+ if (xPeer.is())
+ xPeer->setBackground(util::Color(0xff000000));
+
+ mxWindow->setVisible(true);
+ }
+
+ if (mpPresenterController)
+ mnSlideAspectRatio = mpPresenterController->GetSlideAspectRatio();
+
+ Reference<lang::XMultiComponentFactory> xFactory = rxContext->getServiceManager();
+ if (xFactory.is())
+ mxPreviewRenderer.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.drawing.SlideRenderer",
+ rxContext),
+ UNO_QUERY);
+ mpBitmaps = std::make_shared<PresenterBitmapContainer>(
+ "PresenterScreenSettings/ScrollBar/Bitmaps",
+ std::shared_ptr<PresenterBitmapContainer>(),
+ rxContext,
+ mxCanvas);
+ Resize();
+}
+
+PresenterSlidePreview::~PresenterSlidePreview()
+{
+}
+
+void SAL_CALL PresenterSlidePreview::disposing()
+{
+ if (mxWindow.is())
+ {
+ mxWindow->removeWindowListener(this);
+ mxWindow->removePaintListener(this);
+ mxWindow = nullptr;
+ mxCanvas = nullptr;
+ }
+
+ Reference<lang::XComponent> xComponent (mxPreviewRenderer, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+}
+
+//----- XResourceId -----------------------------------------------------------
+
+Reference<XResourceId> SAL_CALL PresenterSlidePreview::getResourceId()
+{
+ return mxViewId;
+}
+
+sal_Bool SAL_CALL PresenterSlidePreview::isAnchorOnly()
+{
+ return false;
+}
+
+//----- XWindowListener -------------------------------------------------------
+
+void SAL_CALL PresenterSlidePreview::windowResized (const awt::WindowEvent&)
+{
+ ThrowIfDisposed();
+ ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
+ Resize();
+}
+
+void SAL_CALL PresenterSlidePreview::windowMoved (const awt::WindowEvent&) {}
+
+void SAL_CALL PresenterSlidePreview::windowShown (const lang::EventObject&)
+{
+ ThrowIfDisposed();
+ ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
+ Resize();
+}
+
+void SAL_CALL PresenterSlidePreview::windowHidden (const lang::EventObject&) {}
+
+//----- XPaintListener --------------------------------------------------------
+
+void SAL_CALL PresenterSlidePreview::windowPaint (const awt::PaintEvent& rEvent)
+{
+ ThrowIfDisposed();
+
+ ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
+ if (mxWindow.is())
+ Paint(awt::Rectangle(
+ rEvent.UpdateRect.X,
+ rEvent.UpdateRect.Y,
+ rEvent.UpdateRect.Width,
+ rEvent.UpdateRect.Height));
+}
+
+//----- lang::XEventListener --------------------------------------------------
+
+void SAL_CALL PresenterSlidePreview::disposing (const lang::EventObject& rEvent)
+{
+ if (rEvent.Source == mxWindow)
+ {
+ mxWindow = nullptr;
+ mxCanvas = nullptr;
+ mxPreview = nullptr;
+ }
+}
+
+//----- XDrawView -------------------------------------------------------------
+
+void SAL_CALL PresenterSlidePreview::setCurrentPage (const Reference<drawing::XDrawPage>& rxSlide)
+{
+ ThrowIfDisposed();
+ ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
+ SetSlide(rxSlide);
+}
+
+Reference<drawing::XDrawPage> SAL_CALL PresenterSlidePreview::getCurrentPage()
+{
+ ThrowIfDisposed();
+ return nullptr;
+}
+
+
+void PresenterSlidePreview::SetSlide (const Reference<drawing::XDrawPage>& rxPage)
+{
+ mxCurrentSlide = rxPage;
+ mxPreview = nullptr;
+
+ // The preview is not transparent, therefore only this window, not its
+ // parent, has to be invalidated.
+ mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
+}
+
+void PresenterSlidePreview::Paint (const awt::Rectangle& rBoundingBox)
+{
+ if ( ! mxWindow.is())
+ return;
+ if ( ! mxCanvas.is())
+ return;
+ if ( ! mxPreviewRenderer.is())
+ return;
+
+ // Make sure that a preview in the correct size exists.
+ awt::Rectangle aWindowBox (mxWindow->getPosSize());
+
+ bool bCustomAnimation = false;
+ bool bTransition = false;
+ if( mxCurrentSlide.is() )
+ {
+ bCustomAnimation = PresenterController::HasCustomAnimation(mxCurrentSlide);
+ bTransition = PresenterController::HasTransition(mxCurrentSlide);
+ }
+
+ if ( ! mxPreview.is() && mxCurrentSlide.is())
+ {
+ // Create a new preview bitmap.
+ mxPreview = mxPreviewRenderer->createPreviewForCanvas(
+ mxCurrentSlide,
+ awt::Size(aWindowBox.Width, aWindowBox.Height),
+ gnSuperSampleFactor,
+ mxCanvas);
+ }
+
+ // Determine the bounding box of the preview.
+ awt::Rectangle aPreviewBox;
+ if (mxPreview.is())
+ {
+ const geometry::IntegerSize2D aPreviewSize (mxPreview->getSize());
+ aPreviewBox = awt::Rectangle(
+ (aWindowBox.Width - aPreviewSize.Width)/2,
+ (aWindowBox.Height - aPreviewSize.Height)/2,
+ aPreviewSize.Width,
+ aPreviewSize.Height);
+ }
+ else
+ {
+ if (mnSlideAspectRatio > 0)
+ {
+ const awt::Size aPreviewSize (mxPreviewRenderer->calculatePreviewSize(
+ mnSlideAspectRatio,awt::Size(aWindowBox.Width, aWindowBox.Height)));
+ aPreviewBox = awt::Rectangle(
+ (aWindowBox.Width - aPreviewSize.Width)/2,
+ (aWindowBox.Height - aPreviewSize.Height)/2,
+ aPreviewSize.Width,
+ aPreviewSize.Height);
+ }
+ }
+
+ // Paint the background.
+ mpPresenterController->GetCanvasHelper()->Paint(
+ mpPresenterController->GetViewBackground(mxViewId->getResourceURL()),
+ mxCanvas,
+ rBoundingBox,
+ awt::Rectangle(0,0,aWindowBox.Width,aWindowBox.Height),
+ aPreviewBox);
+
+ // Paint the preview.
+ const rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr);
+
+ Sequence<double> aBackgroundColor(4);
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(1, 0, aPreviewBox.X, 0, 1, aPreviewBox.Y),
+ nullptr,
+ aBackgroundColor,
+ rendering::CompositeOperation::SOURCE);
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, 0x00000000);
+ if (mxPreview.is())
+ {
+ mxCanvas->drawBitmap(mxPreview, aViewState, aRenderState);
+ if( bTransition )
+ {
+ const awt::Rectangle aTransitionPreviewBox(5, aWindowBox.Height-20, 0, 0);
+ SharedBitmapDescriptor aTransitionDescriptor = mpBitmaps->GetBitmap("Transition");
+ Reference<rendering::XBitmap> xTransitionIcon (aTransitionDescriptor->GetNormalBitmap());
+ rendering::RenderState aTransitionRenderState (
+ geometry::AffineMatrix2D(1, 0, aTransitionPreviewBox.X, 0, 1, aTransitionPreviewBox.Y),
+ nullptr,
+ aBackgroundColor,
+ rendering::CompositeOperation::SOURCE);
+ mxCanvas->drawBitmap(xTransitionIcon, aViewState, aTransitionRenderState);
+ }
+ if( bCustomAnimation )
+ {
+ const awt::Rectangle aAnimationPreviewBox(5, aWindowBox.Height-40, 0, 0);
+ SharedBitmapDescriptor aAnimationDescriptor = mpBitmaps->GetBitmap("Animation");
+ Reference<rendering::XBitmap> xAnimationIcon (aAnimationDescriptor->GetNormalBitmap());
+ rendering::RenderState aAnimationRenderState (
+ geometry::AffineMatrix2D(1, 0, aAnimationPreviewBox.X, 0, 1, aAnimationPreviewBox.Y),
+ nullptr,
+ aBackgroundColor,
+ rendering::CompositeOperation::SOURCE);
+ mxCanvas->drawBitmap(xAnimationIcon, aViewState, aAnimationRenderState);
+ }
+ }
+ else
+ {
+ if (mnSlideAspectRatio > 0)
+ {
+ Reference<rendering::XPolyPolygon2D> xPolygon (
+ PresenterGeometryHelper::CreatePolygon(aPreviewBox, mxCanvas->getDevice()));
+ if (xPolygon.is())
+ mxCanvas->fillPolyPolygon(xPolygon, aViewState, aRenderState);
+ }
+ }
+
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(false);
+}
+
+void PresenterSlidePreview::Resize()
+{
+ if (mxPreviewRenderer.is() && mxPreview.is())
+ {
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ const awt::Size aNewPreviewSize (mxPreviewRenderer->calculatePreviewSize(
+ mnSlideAspectRatio,
+ awt::Size(aWindowBox.Width, aWindowBox.Height)));
+ const geometry::IntegerSize2D aPreviewSize (mxPreview->getSize());
+ if (aNewPreviewSize.Width==aPreviewSize.Width
+ && aNewPreviewSize.Height==aPreviewSize.Height)
+ {
+ // The size of the window may have changed but the preview would
+ // be painted in the same size (but not necessarily at the same
+ // position.)
+ return;
+ }
+ }
+ SetSlide(mxCurrentSlide);
+}
+
+void PresenterSlidePreview::ThrowIfDisposed()
+{
+ if (PresenterSlidePreviewInterfaceBase::rBHelper.bDisposed || PresenterSlidePreviewInterfaceBase::rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterSlidePreview object has already been disposed",
+ static_cast<uno::XWeak*>(this));
+ }
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterSlidePreview.hxx b/sdext/source/presenter/PresenterSlidePreview.hxx
new file mode 100644
index 000000000..85107693a
--- /dev/null
+++ b/sdext/source/presenter/PresenterSlidePreview.hxx
@@ -0,0 +1,145 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSLIDEPREVIEW_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSLIDEPREVIEW_HXX
+
+#include "PresenterController.hxx"
+
+#include <com/sun/star/awt/XPaintListener.hpp>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/drawing/XSlideRenderer.hpp>
+#include <com/sun/star/drawing/framework/XPane.hpp>
+#include <com/sun/star/drawing/framework/XView.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <rtl/ref.hxx>
+
+namespace sdext::presenter {
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::drawing::framework::XView,
+ css::drawing::XDrawView,
+ css::awt::XPaintListener,
+ css::awt::XWindowListener
+> PresenterSlidePreviewInterfaceBase;
+
+/** Static preview of a slide. Typically used for the preview of the next
+ slide.
+ This implementation shows a preview of the slide given to the
+ setCurrentSlide. For showing the next slide the PresenterViewFactory
+ uses a derived class that overrides the setCurrentSlide() method.
+*/
+class PresenterSlidePreview
+ : private ::cppu::BaseMutex,
+ public PresenterSlidePreviewInterfaceBase
+{
+public:
+ PresenterSlidePreview (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId,
+ const css::uno::Reference<css::drawing::framework::XPane>& rxAnchorPane,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterSlidePreview() override;
+ PresenterSlidePreview(const PresenterSlidePreview&) = delete;
+ PresenterSlidePreview& operator=(const PresenterSlidePreview&) = delete;
+ virtual void SAL_CALL disposing() override;
+
+ // XResourceId
+
+ virtual css::uno::Reference<css::drawing::framework::XResourceId> SAL_CALL getResourceId() override;
+
+ virtual sal_Bool SAL_CALL isAnchorOnly() override;
+
+ // XWindowListener
+
+ virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override;
+
+ // XPaintListener
+
+ virtual void SAL_CALL windowPaint (const css::awt::PaintEvent& rEvent) override;
+
+ // lang::XEventListener
+ virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override;
+
+ // XDrawView
+
+ virtual void SAL_CALL setCurrentPage (
+ const css::uno::Reference<css::drawing::XDrawPage>& rxSlide) override;
+
+ virtual css::uno::Reference<css::drawing::XDrawPage> SAL_CALL getCurrentPage() override;
+
+protected:
+ ::rtl::Reference<PresenterController> mpPresenterController;
+
+private:
+ css::uno::Reference<css::drawing::framework::XResourceId> mxViewId;
+ css::uno::Reference<css::drawing::XSlideRenderer> mxPreviewRenderer;
+
+ /** This Image holds the preview of the current slide. After resize
+ requests the image may be empty. This results eventually in a call
+ to ProvideSlide() in order to created a preview in the correct new
+ size.
+ */
+ css::uno::Reference<css::rendering::XBitmap> mxPreview;
+ std::shared_ptr<PresenterBitmapContainer> mpBitmaps;
+
+ /** The current slide for which a preview is displayed. This may or
+ may not be the same as the current slide of the PresenterView.
+ */
+ css::uno::Reference<css::drawing::XDrawPage> mxCurrentSlide;
+ double mnSlideAspectRatio;
+
+ css::uno::Reference<css::awt::XWindow> mxWindow;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+
+ /** Set the given slide as the current slide of the called PresenterSlidePreview
+ object.
+ */
+ void SetSlide (const css::uno::Reference<css::drawing::XDrawPage>& rxPage);
+
+ /** Paint the preview of the current slide centered in the window of the
+ anchor pane.
+ */
+ void Paint (const css::awt::Rectangle& rBoundingBox);
+
+ /** React to a resize of the anchor pane.
+ */
+ void Resize();
+
+ /** @throws css::lang::DisposedException when the object has already been
+ disposed.
+ */
+ void ThrowIfDisposed();
+};
+
+} // end of namespace ::sd::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterSlideShowView.cxx b/sdext/source/presenter/PresenterSlideShowView.cxx
new file mode 100644
index 000000000..5ca0ee630
--- /dev/null
+++ b/sdext/source/presenter/PresenterSlideShowView.cxx
@@ -0,0 +1,953 @@
+
+/* -*- 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 "PresenterSlideShowView.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterGeometryHelper.hxx"
+#include "PresenterHelper.hxx"
+#include "PresenterPaneContainer.hxx"
+#include <com/sun/star/awt/InvalidateStyle.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/awt/Pointer.hpp>
+#include <com/sun/star/awt/Toolkit.hpp>
+#include <com/sun/star/awt/WindowAttribute.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/drawing/XPresenterHelper.hpp>
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/TextDirection.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <osl/mutex.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sdext::presenter {
+
+//===== PresenterSlideShowView ================================================
+
+PresenterSlideShowView::PresenterSlideShowView (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterSlideShowViewInterfaceBase(m_aMutex),
+ mxComponentContext(rxContext),
+ mpPresenterController(rpPresenterController),
+ mxViewId(rxViewId),
+ mxController(rxController),
+ mxSlideShowController(PresenterHelper::GetSlideShowController(rxController)),
+ mbIsViewAdded(false),
+ mnPageAspectRatio(28.0/21.0),
+ maBroadcaster(m_aMutex),
+ mbIsForcedPaintPending(false),
+ mbIsPaintPending(true),
+ mbIsEndSlideVisible(false)
+{
+ if (mpPresenterController)
+ {
+ mnPageAspectRatio = mpPresenterController->GetSlideAspectRatio();
+ mpBackground = mpPresenterController->GetViewBackground(mxViewId->getResourceURL());
+ }
+}
+
+void PresenterSlideShowView::LateInit()
+{
+ mxSlideShow.set( mxSlideShowController->getSlideShow(), UNO_SET_THROW);
+ Reference<lang::XComponent> xSlideShowComponent (mxSlideShow, UNO_QUERY);
+ xSlideShowComponent->addEventListener(static_cast<awt::XWindowListener*>(this));
+
+ Reference<lang::XMultiComponentFactory> xFactory (
+ mxComponentContext->getServiceManager(), UNO_SET_THROW);
+ mxPresenterHelper.set (xFactory->createInstanceWithContext(
+ "com.sun.star.comp.Draw.PresenterHelper",
+ mxComponentContext),
+ UNO_QUERY_THROW);
+
+ // Use view id and controller to retrieve window and canvas from
+ // configuration controller.
+ Reference<XControllerManager> xCM (mxController, UNO_QUERY_THROW);
+ Reference<XConfigurationController> xCC (xCM->getConfigurationController());
+
+ if (xCC.is())
+ {
+ mxTopPane.set(xCC->getResource(mxViewId->getAnchor()->getAnchor()), UNO_QUERY);
+
+ Reference<XPane> xPane (xCC->getResource(mxViewId->getAnchor()), UNO_QUERY_THROW);
+
+ mxWindow = xPane->getWindow();
+ mxCanvas = xPane->getCanvas();
+
+ if (mxWindow.is())
+ {
+ mxWindow->addPaintListener(this);
+ mxWindow->addWindowListener(this);
+ }
+
+ // The window does not have to paint a background. We do
+ // that ourself.
+ Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY);
+ if (xPeer.is())
+ xPeer->setBackground(util::Color(0xff000000));
+ }
+
+ // Create a window for the actual slide show view. It is places
+ // centered and with maximal size inside the pane.
+ mxViewWindow = CreateViewWindow(mxWindow);
+
+ mxViewCanvas = CreateViewCanvas(mxViewWindow);
+
+ if (mxViewWindow.is())
+ {
+ // Register listeners at window.
+ mxViewWindow->addPaintListener(this);
+ mxViewWindow->addMouseListener(this);
+ mxViewWindow->addMouseMotionListener(this);
+ }
+
+ if (mxViewWindow.is())
+ Resize();
+
+ if (mxWindow.is())
+ mxWindow->setVisible(true);
+
+ // Add the new slide show view to the slide show.
+ if (mxSlideShow.is() && ! mbIsViewAdded)
+ {
+ impl_addAndConfigureView();
+ mbIsViewAdded = true;
+ }
+
+ // Read text for one past last slide.
+ PresenterConfigurationAccess aConfiguration (
+ mxComponentContext,
+ PresenterConfigurationAccess::msPresenterScreenRootName,
+ PresenterConfigurationAccess::READ_ONLY);
+ aConfiguration.GetConfigurationNode(
+ "Presenter/Views/CurrentSlidePreview/"
+ "Strings/ClickToExitPresentationText/String")
+ >>= msClickToExitPresentationText;
+ aConfiguration.GetConfigurationNode(
+ "Presenter/Views/CurrentSlidePreview/"
+ "Strings/ClickToExitPresentationTitle/String")
+ >>= msClickToExitPresentationTitle;
+}
+
+PresenterSlideShowView::~PresenterSlideShowView()
+{
+}
+
+void PresenterSlideShowView::disposing()
+{
+ // Tell all listeners that we are disposed.
+ lang::EventObject aEvent;
+ aEvent.Source = static_cast<XWeak*>(this);
+
+ ::cppu::OInterfaceContainerHelper* pIterator
+ = maBroadcaster.getContainer(cppu::UnoType<lang::XEventListener>::get());
+ if (pIterator != nullptr)
+ pIterator->disposeAndClear(aEvent);
+
+ // Do this for
+ // XPaintListener, XModifyListener,XMouseListener,XMouseMotionListener,XWindowListener?
+
+ if (mxWindow.is())
+ {
+ mxWindow->removePaintListener(this);
+ mxWindow->removeMouseListener(this);
+ mxWindow->removeMouseMotionListener(this);
+ mxWindow->removeWindowListener(this);
+ mxWindow = nullptr;
+ }
+ mxSlideShowController = nullptr;
+ mxSlideShow = nullptr;
+ if (mxViewCanvas.is())
+ {
+ Reference<XComponent> xComponent (mxViewCanvas, UNO_QUERY);
+ mxViewCanvas = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ if (mxViewWindow.is())
+ {
+ Reference<XComponent> xComponent = mxViewWindow;
+ mxViewWindow = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ if (mxPointer.is())
+ {
+ Reference<XComponent> xComponent (mxPointer, UNO_QUERY);
+ mxPointer = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ if (mxBackgroundPolygon1.is())
+ {
+ Reference<XComponent> xComponent (mxBackgroundPolygon1, UNO_QUERY);
+ mxBackgroundPolygon1 = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ if (mxBackgroundPolygon2.is())
+ {
+ Reference<XComponent> xComponent (mxBackgroundPolygon2, UNO_QUERY);
+ mxBackgroundPolygon2 = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ mxComponentContext = nullptr;
+ mpPresenterController = nullptr;
+ mxViewId = nullptr;
+ mxController = nullptr;
+ mxCanvas = nullptr;
+ mpBackground.reset();
+ msClickToExitPresentationText.clear();
+ msClickToExitPresentationTitle.clear();
+ msTitleTemplate.clear();
+ mxCurrentSlide = nullptr;
+}
+
+//----- XDrawView -------------------------------------------------------------
+
+void SAL_CALL PresenterSlideShowView::setCurrentPage (
+ const css::uno::Reference<css::drawing::XDrawPage>& rxSlide)
+{
+ mxCurrentSlide = rxSlide;
+ if (mpPresenterController
+ && mxSlideShowController.is()
+ && ! mpPresenterController->GetCurrentSlide().is()
+ && ! mxSlideShowController->isPaused())
+ {
+ mbIsEndSlideVisible = true;
+ Reference<awt::XWindowPeer> xPeer (mxViewWindow, UNO_QUERY);
+ if (xPeer.is())
+ xPeer->invalidate(awt::InvalidateStyle::NOTRANSPARENT);
+
+ // For the end slide we use a special title, without the (n of m)
+ // part. Save the title template for the case that the user goes
+ // backwards.
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ mpPresenterController->GetPaneContainer()->FindViewURL(mxViewId->getResourceURL()));
+ if (pDescriptor)
+ {
+ msTitleTemplate = pDescriptor->msTitleTemplate;
+ pDescriptor->msTitleTemplate = msClickToExitPresentationTitle;
+ mpPresenterController->UpdatePaneTitles();
+ }
+ }
+ else if (mbIsEndSlideVisible)
+ {
+ mbIsEndSlideVisible = false;
+
+ // Restore the title template.
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ mpPresenterController->GetPaneContainer()->FindViewURL(mxViewId->getResourceURL()));
+ if (pDescriptor)
+ {
+ pDescriptor->msTitleTemplate = msTitleTemplate;
+ pDescriptor->msTitle.clear();
+ mpPresenterController->UpdatePaneTitles();
+ }
+ }
+}
+
+css::uno::Reference<css::drawing::XDrawPage> SAL_CALL PresenterSlideShowView::getCurrentPage()
+{
+ return mxCurrentSlide;
+}
+
+//----- CachablePresenterView -------------------------------------------------
+
+void PresenterSlideShowView::ReleaseView()
+{
+ if (mxSlideShow.is() && mbIsViewAdded)
+ {
+ mxSlideShow->removeView(this);
+ mbIsViewAdded = false;
+ }
+}
+
+//----- XSlideShowView --------------------------------------------------------
+
+Reference<rendering::XSpriteCanvas> SAL_CALL PresenterSlideShowView::getCanvas()
+{
+ ThrowIfDisposed();
+
+ return Reference<rendering::XSpriteCanvas>(mxViewCanvas, UNO_QUERY);
+}
+
+void SAL_CALL PresenterSlideShowView::clear()
+{
+ ThrowIfDisposed();
+ mbIsForcedPaintPending = false;
+ mbIsPaintPending = false;
+
+ if (!(mxViewCanvas.is() && mxViewWindow.is()))
+ return;
+
+ // Create a polygon for the window outline.
+ awt::Rectangle aViewWindowBox (mxViewWindow->getPosSize());
+ Reference<rendering::XPolyPolygon2D> xPolygon (PresenterGeometryHelper::CreatePolygon(
+ awt::Rectangle(0,0, aViewWindowBox.Width,aViewWindowBox.Height),
+ mxViewCanvas->getDevice()));
+
+ rendering::ViewState aViewState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr);
+ double const aColor[4] = {0,0,0,0};
+ rendering::RenderState aRenderState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr,
+ Sequence<double>(aColor,4),
+ rendering::CompositeOperation::SOURCE);
+ mxViewCanvas->fillPolyPolygon(xPolygon, aViewState, aRenderState);
+}
+
+geometry::AffineMatrix2D SAL_CALL PresenterSlideShowView::getTransformation()
+{
+ ThrowIfDisposed();
+
+ if (mxViewWindow.is())
+ {
+ // When the mbIsInModifyNotification is set then a slightly modified
+ // version of the transformation is returned in order to get past
+ // optimizations the avoid updates when the transformation is
+ // unchanged (when the window size changes then due to the constant
+ // aspect ratio the size of the preview may remain the same while
+ // the position changes. The position, however, is represented by
+ // the position of the view window. This transformation is given
+ // relative to the view window and therefore does not contain the
+ // position.)
+ const awt::Rectangle aWindowBox = mxViewWindow->getPosSize();
+ return geometry::AffineMatrix2D(
+ aWindowBox.Width-1, 0, 0,
+ 0, aWindowBox.Height-1, 0);
+ }
+ else
+ {
+ return geometry::AffineMatrix2D(1,0,0, 0,1,0);
+ }
+}
+
+geometry::IntegerSize2D SAL_CALL PresenterSlideShowView::getTranslationOffset()
+{
+ ThrowIfDisposed();
+ return geometry::IntegerSize2D(0,0);
+}
+
+void SAL_CALL PresenterSlideShowView::addTransformationChangedListener(
+ const Reference<util::XModifyListener>& rxListener)
+{
+ ThrowIfDisposed();
+ maBroadcaster.addListener(
+ cppu::UnoType<util::XModifyListener>::get(),
+ rxListener);
+}
+
+void SAL_CALL PresenterSlideShowView::removeTransformationChangedListener(
+ const Reference<util::XModifyListener>& rxListener)
+{
+ ThrowIfDisposed();
+ maBroadcaster.removeListener(
+ cppu::UnoType<util::XModifyListener>::get(),
+ rxListener);
+}
+
+void SAL_CALL PresenterSlideShowView::addPaintListener(
+ const Reference<awt::XPaintListener>& rxListener)
+{
+ ThrowIfDisposed();
+ maBroadcaster.addListener(
+ cppu::UnoType<awt::XPaintListener>::get(),
+ rxListener);
+}
+
+void SAL_CALL PresenterSlideShowView::removePaintListener(
+ const Reference<awt::XPaintListener>& rxListener)
+{
+ ThrowIfDisposed();
+ maBroadcaster.removeListener(
+ cppu::UnoType<awt::XPaintListener>::get(),
+ rxListener);
+}
+
+void SAL_CALL PresenterSlideShowView::addMouseListener(
+ const Reference<awt::XMouseListener>& rxListener)
+{
+ ThrowIfDisposed();
+ maBroadcaster.addListener(
+ cppu::UnoType<awt::XMouseListener>::get(),
+ rxListener);
+}
+
+void SAL_CALL PresenterSlideShowView::removeMouseListener(
+ const Reference<awt::XMouseListener>& rxListener)
+{
+ ThrowIfDisposed();
+ maBroadcaster.removeListener(
+ cppu::UnoType<awt::XMouseListener>::get(),
+ rxListener);
+}
+
+void SAL_CALL PresenterSlideShowView::addMouseMotionListener(
+ const Reference<awt::XMouseMotionListener>& rxListener)
+{
+ ThrowIfDisposed();
+ maBroadcaster.addListener(
+ cppu::UnoType<awt::XMouseMotionListener>::get(),
+ rxListener);
+}
+
+void SAL_CALL PresenterSlideShowView::removeMouseMotionListener(
+ const Reference<awt::XMouseMotionListener>& rxListener)
+{
+ ThrowIfDisposed();
+ maBroadcaster.removeListener(
+ cppu::UnoType<awt::XMouseMotionListener>::get(),
+ rxListener);
+}
+
+void SAL_CALL PresenterSlideShowView::setMouseCursor(::sal_Int16 nPointerShape)
+{
+ ThrowIfDisposed();
+
+ // Create a pointer when it does not yet exist.
+ if ( ! mxPointer.is())
+ {
+ mxPointer = awt::Pointer::create(mxComponentContext);
+ }
+
+ // Set the pointer to the given shape and the window(peer) to the
+ // pointer.
+ Reference<awt::XWindowPeer> xPeer (mxViewWindow, UNO_QUERY);
+ if (mxPointer.is() && xPeer.is())
+ {
+ mxPointer->setType(nPointerShape);
+ xPeer->setPointer(mxPointer);
+ }
+}
+
+awt::Rectangle SAL_CALL PresenterSlideShowView::getCanvasArea( )
+{
+ if( mxViewWindow.is() && mxTopPane.is() )
+ return mxPresenterHelper->getWindowExtentsRelative( mxViewWindow, mxTopPane->getWindow() );
+
+ awt::Rectangle aRectangle;
+
+ aRectangle.X = aRectangle.Y = aRectangle.Width = aRectangle.Height = 0;
+
+ return aRectangle;
+}
+
+//----- lang::XEventListener --------------------------------------------------
+
+void SAL_CALL PresenterSlideShowView::disposing (const lang::EventObject& rEvent)
+{
+ if (rEvent.Source == mxViewWindow)
+ mxViewWindow = nullptr;
+ else if (rEvent.Source == mxSlideShow)
+ mxSlideShow = nullptr;
+}
+
+//----- XPaintListener --------------------------------------------------------
+
+void SAL_CALL PresenterSlideShowView::windowPaint (const awt::PaintEvent& rEvent)
+{
+ // Deactivated views must not be painted.
+ if ( ! mbIsPresenterViewActive)
+ return;
+
+ awt::Rectangle aViewWindowBox (mxViewWindow->getPosSize());
+ if (aViewWindowBox.Width <= 0 || aViewWindowBox.Height <= 0)
+ return;
+
+ if (rEvent.Source == mxWindow)
+ PaintOuterWindow(rEvent.UpdateRect);
+ else if (mbIsEndSlideVisible)
+ PaintEndSlide(rEvent.UpdateRect);
+ else
+ PaintInnerWindow(rEvent);
+}
+
+//----- XMouseListener --------------------------------------------------------
+
+void SAL_CALL PresenterSlideShowView::mousePressed (const awt::MouseEvent& rEvent)
+{
+ awt::MouseEvent aEvent (rEvent);
+ aEvent.Source = static_cast<XWeak*>(this);
+ ::cppu::OInterfaceContainerHelper* pIterator
+ = maBroadcaster.getContainer(cppu::UnoType<awt::XMouseListener>::get());
+ if (pIterator != nullptr)
+ {
+ pIterator->notifyEach(&awt::XMouseListener::mousePressed, aEvent);
+ }
+
+ // Only when the end slide is displayed we forward the mouse event to
+ // the PresenterController so that it switches to the next slide and
+ // ends the presentation.
+ if (mbIsEndSlideVisible)
+ if (mpPresenterController)
+ mpPresenterController->HandleMouseClick(rEvent);
+}
+
+void SAL_CALL PresenterSlideShowView::mouseReleased (const awt::MouseEvent& rEvent)
+{
+ awt::MouseEvent aEvent (rEvent);
+ aEvent.Source = static_cast<XWeak*>(this);
+ ::cppu::OInterfaceContainerHelper* pIterator
+ = maBroadcaster.getContainer(cppu::UnoType<awt::XMouseListener>::get());
+ if (pIterator != nullptr)
+ {
+ pIterator->notifyEach(&awt::XMouseListener::mouseReleased, aEvent);
+ }
+}
+
+void SAL_CALL PresenterSlideShowView::mouseEntered (const awt::MouseEvent& rEvent)
+{
+ awt::MouseEvent aEvent (rEvent);
+ aEvent.Source = static_cast<XWeak*>(this);
+ ::cppu::OInterfaceContainerHelper* pIterator
+ = maBroadcaster.getContainer(cppu::UnoType<awt::XMouseListener>::get());
+ if (pIterator != nullptr)
+ {
+ pIterator->notifyEach(&awt::XMouseListener::mouseEntered, aEvent);
+ }
+}
+
+void SAL_CALL PresenterSlideShowView::mouseExited (const awt::MouseEvent& rEvent)
+{
+ awt::MouseEvent aEvent (rEvent);
+ aEvent.Source = static_cast<XWeak*>(this);
+ ::cppu::OInterfaceContainerHelper* pIterator
+ = maBroadcaster.getContainer(cppu::UnoType<awt::XMouseListener>::get());
+ if (pIterator != nullptr)
+ {
+ pIterator->notifyEach(&awt::XMouseListener::mouseExited, aEvent);
+ }
+}
+
+//----- XMouseMotionListener --------------------------------------------------
+
+void SAL_CALL PresenterSlideShowView::mouseDragged (const awt::MouseEvent& rEvent)
+{
+ awt::MouseEvent aEvent (rEvent);
+ aEvent.Source = static_cast<XWeak*>(this);
+ ::cppu::OInterfaceContainerHelper* pIterator
+ = maBroadcaster.getContainer(cppu::UnoType<awt::XMouseMotionListener>::get());
+ if (pIterator != nullptr)
+ {
+ pIterator->notifyEach(&awt::XMouseMotionListener::mouseDragged, aEvent);
+ }
+}
+
+void SAL_CALL PresenterSlideShowView::mouseMoved (const awt::MouseEvent& rEvent)
+{
+ awt::MouseEvent aEvent (rEvent);
+ aEvent.Source = static_cast<XWeak*>(this);
+ ::cppu::OInterfaceContainerHelper* pIterator
+ = maBroadcaster.getContainer(cppu::UnoType<awt::XMouseMotionListener>::get());
+ if (pIterator != nullptr)
+ {
+ pIterator->notifyEach(&awt::XMouseMotionListener::mouseMoved, aEvent);
+ }
+}
+
+//----- XWindowListener -------------------------------------------------------
+
+void SAL_CALL PresenterSlideShowView::windowResized (const awt::WindowEvent&)
+{
+ ThrowIfDisposed();
+ ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
+
+ Resize();
+}
+
+void SAL_CALL PresenterSlideShowView::windowMoved (const awt::WindowEvent&)
+{
+ if ( ! mbIsPaintPending)
+ mbIsForcedPaintPending = true;
+}
+
+void SAL_CALL PresenterSlideShowView::windowShown (const lang::EventObject&)
+{
+ Resize();
+}
+
+void SAL_CALL PresenterSlideShowView::windowHidden (const lang::EventObject&) {}
+
+//----- XView -----------------------------------------------------------------
+
+Reference<XResourceId> SAL_CALL PresenterSlideShowView::getResourceId()
+{
+ return mxViewId;
+}
+
+sal_Bool SAL_CALL PresenterSlideShowView::isAnchorOnly()
+{
+ return false;
+}
+
+//----- CachablePresenterView -------------------------------------------------
+
+void PresenterSlideShowView::ActivatePresenterView()
+{
+ if (mxSlideShow.is() && ! mbIsViewAdded)
+ {
+ impl_addAndConfigureView();
+ mbIsViewAdded = true;
+ }
+}
+
+void PresenterSlideShowView::DeactivatePresenterView()
+{
+ if (mxSlideShow.is() && mbIsViewAdded)
+ {
+ mxSlideShow->removeView(this);
+ mbIsViewAdded = false;
+ }
+}
+
+
+void PresenterSlideShowView::PaintOuterWindow (const awt::Rectangle& rRepaintBox)
+{
+ if ( ! mxCanvas.is())
+ return;
+
+ if (!mpBackground)
+ return;
+
+ const rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ PresenterGeometryHelper::CreatePolygon(rRepaintBox, mxCanvas->getDevice()));
+
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ Reference<rendering::XBitmap> xBackgroundBitmap (mpBackground->GetNormalBitmap());
+ if (xBackgroundBitmap.is())
+ {
+ const geometry::IntegerSize2D aBitmapSize(xBackgroundBitmap->getSize());
+ Sequence<rendering::Texture> aTextures
+ {
+ {
+ geometry::AffineMatrix2D( aBitmapSize.Width,0,0, 0,aBitmapSize.Height,0),
+ 1,
+ 0,
+ xBackgroundBitmap,
+ nullptr,
+ nullptr,
+ rendering::StrokeAttributes(),
+ rendering::TexturingMode::REPEAT,
+ rendering::TexturingMode::REPEAT
+ }
+ };
+
+ if (mxBackgroundPolygon1.is())
+ mxCanvas->fillTexturedPolyPolygon(
+ mxBackgroundPolygon1,
+ aViewState,
+ aRenderState,
+ aTextures);
+ if (mxBackgroundPolygon2.is())
+ mxCanvas->fillTexturedPolyPolygon(
+ mxBackgroundPolygon2,
+ aViewState,
+ aRenderState,
+ aTextures);
+ }
+ else
+ {
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, mpBackground->maReplacementColor);
+
+ if (mxBackgroundPolygon1.is())
+ mxCanvas->fillPolyPolygon(mxBackgroundPolygon1, aViewState, aRenderState);
+ if (mxBackgroundPolygon2.is())
+ mxCanvas->fillPolyPolygon(mxBackgroundPolygon2, aViewState, aRenderState);
+ }
+}
+
+void PresenterSlideShowView::PaintEndSlide (const awt::Rectangle& rRepaintBox)
+{
+ if ( ! mxCanvas.is())
+ return;
+
+ const rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ PresenterGeometryHelper::CreatePolygon(rRepaintBox, mxCanvas->getDevice()));
+
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, util::Color(0x00000000));
+ mxCanvas->fillPolyPolygon(
+ PresenterGeometryHelper::CreatePolygon(mxViewWindow->getPosSize(), mxCanvas->getDevice()),
+ aViewState,
+ aRenderState);
+
+ do
+ {
+ if (!mpPresenterController)
+ break;
+ std::shared_ptr<PresenterTheme> pTheme (mpPresenterController->GetTheme());
+ if (pTheme == nullptr)
+ break;
+
+ const OUString sViewStyle (pTheme->GetStyleName(mxViewId->getResourceURL()));
+ PresenterTheme::SharedFontDescriptor pFont (pTheme->GetFont(sViewStyle));
+ if (!pFont)
+ break;
+
+ /// this is responsible of the " presentation exit " text inside the slide windows
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, util::Color(0x00ffffff));
+ aRenderState.AffineTransform.m02 = 20;
+ aRenderState.AffineTransform.m12 = 40;
+ const rendering::StringContext aContext (
+ msClickToExitPresentationText, 0, msClickToExitPresentationText.getLength());
+ pFont->PrepareFont(mxCanvas);
+ const Reference<rendering::XTextLayout> xLayout (
+ pFont->mxFont->createTextLayout(aContext,rendering::TextDirection::WEAK_LEFT_TO_RIGHT,0));
+ mxCanvas->drawTextLayout(
+ xLayout,
+ aViewState,
+ aRenderState);
+ }
+ while (false);
+
+ // Finally, in double buffered environments, request the changes to be
+ // made visible.
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(true);
+}
+
+void PresenterSlideShowView::PaintInnerWindow (const awt::PaintEvent& rEvent)
+{
+ // Forward window paint to listeners.
+ awt::PaintEvent aEvent (rEvent);
+ aEvent.Source = static_cast<XWeak*>(this);
+ ::cppu::OInterfaceContainerHelper* pIterator
+ = maBroadcaster.getContainer(cppu::UnoType<awt::XPaintListener>::get());
+ if (pIterator != nullptr)
+ {
+ pIterator->notifyEach(&awt::XPaintListener::windowPaint, aEvent);
+ }
+
+ /** The slide show relies on the back buffer of the canvas not being
+ modified. With a shared canvas there are times when that can not be
+ guaranteed.
+ */
+ if (mbIsForcedPaintPending && mxSlideShow.is() && mbIsViewAdded)
+ {
+ mxSlideShow->removeView(this);
+ impl_addAndConfigureView();
+ }
+
+ // Finally, in double buffered environments, request the changes to be
+ // made visible.
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(true);
+}
+
+Reference<awt::XWindow> PresenterSlideShowView::CreateViewWindow (
+ const Reference<awt::XWindow>& rxParentWindow) const
+{
+ Reference<awt::XWindow> xViewWindow;
+ try
+ {
+ Reference<lang::XMultiComponentFactory> xFactory (mxComponentContext->getServiceManager());
+ if ( ! xFactory.is())
+ return xViewWindow;
+
+ Reference<awt::XToolkit2> xToolkit = awt::Toolkit::create(mxComponentContext);
+ awt::WindowDescriptor aWindowDescriptor (
+ awt::WindowClass_CONTAINER,
+ OUString(),
+ Reference<awt::XWindowPeer>(rxParentWindow,UNO_QUERY_THROW),
+ -1, // parent index not available
+ awt::Rectangle(0,0,10,10),
+ awt::WindowAttribute::SIZEABLE
+ | awt::WindowAttribute::MOVEABLE
+ | awt::WindowAttribute::NODECORATION);
+ xViewWindow.set( xToolkit->createWindow(aWindowDescriptor),UNO_QUERY_THROW);
+
+ // Make the background transparent. The slide show paints its own background.
+ Reference<awt::XWindowPeer> xPeer (xViewWindow, UNO_QUERY_THROW);
+ xPeer->setBackground(0xff000000);
+
+ xViewWindow->setVisible(true);
+ }
+ catch (RuntimeException&)
+ {
+ }
+ return xViewWindow;
+}
+
+Reference<rendering::XCanvas> PresenterSlideShowView::CreateViewCanvas (
+ const Reference<awt::XWindow>& rxViewWindow) const
+{
+ // Create a canvas for the view window.
+ return mxPresenterHelper->createSharedCanvas(
+ Reference<rendering::XSpriteCanvas>(mxTopPane->getCanvas(), UNO_QUERY),
+ mxTopPane->getWindow(),
+ mxTopPane->getCanvas(),
+ mxTopPane->getWindow(),
+ rxViewWindow);
+}
+
+void PresenterSlideShowView::Resize()
+{
+ if ( ! mxWindow.is() || ! mxViewWindow.is())
+ return;
+
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ if (aWindowBox.Height > 0)
+ {
+ awt::Rectangle aViewWindowBox;
+ const double nWindowAspectRatio (
+ double(aWindowBox.Width) / double(aWindowBox.Height));
+ if (nWindowAspectRatio > mnPageAspectRatio)
+ {
+ // Slides will be painted with the full parent window height.
+ aViewWindowBox.Width = sal_Int32(aWindowBox.Height * mnPageAspectRatio + 0.5);
+ aViewWindowBox.Height = aWindowBox.Height;
+ aViewWindowBox.X = (aWindowBox.Width - aViewWindowBox.Width) / 2;
+ aViewWindowBox.Y = 0;
+ }
+ else
+ {
+ // Slides will be painted with the full parent window width.
+ aViewWindowBox.Width = aWindowBox.Width;
+ aViewWindowBox.Height = sal_Int32(aWindowBox.Width / mnPageAspectRatio + 0.5);
+ aViewWindowBox.X = 0;
+ aViewWindowBox.Y = (aWindowBox.Height - aViewWindowBox.Height) / 2;
+ }
+ mxViewWindow->setPosSize(
+ aViewWindowBox.X,
+ aViewWindowBox.Y,
+ aViewWindowBox.Width,
+ aViewWindowBox.Height,
+ awt::PosSize::POSSIZE);
+ }
+
+ // Clear the background polygon so that on the next paint it is created
+ // for the new size.
+ CreateBackgroundPolygons();
+
+ // Notify listeners that the transformation that maps the view into the
+ // window has changed.
+ lang::EventObject aEvent (static_cast<XWeak*>(this));
+ ::cppu::OInterfaceContainerHelper* pIterator
+ = maBroadcaster.getContainer(cppu::UnoType<util::XModifyListener>::get());
+ if (pIterator != nullptr)
+ {
+ pIterator->notifyEach(&util::XModifyListener::modified, aEvent);
+ }
+
+ // Due to constant aspect ratio resizing may lead a preview that changes
+ // its position but not its size. This invalidates the back buffer and
+ // we have to enforce a complete repaint.
+ if ( ! mbIsPaintPending)
+ mbIsForcedPaintPending = true;
+}
+
+void PresenterSlideShowView::CreateBackgroundPolygons()
+{
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ const awt::Rectangle aViewWindowBox (mxViewWindow->getPosSize());
+ if (aWindowBox.Height == aViewWindowBox.Height && aWindowBox.Width == aViewWindowBox.Width)
+ {
+ mxBackgroundPolygon1 = nullptr;
+ mxBackgroundPolygon2 = nullptr;
+ }
+ else if (aWindowBox.Height == aViewWindowBox.Height)
+ {
+ // Paint two boxes to the left and right of the view window.
+ mxBackgroundPolygon1 = PresenterGeometryHelper::CreatePolygon(
+ awt::Rectangle(
+ 0,
+ 0,
+ aViewWindowBox.X,
+ aWindowBox.Height),
+ mxCanvas->getDevice());
+ mxBackgroundPolygon2 = PresenterGeometryHelper::CreatePolygon(
+ awt::Rectangle(
+ aViewWindowBox.X + aViewWindowBox.Width,
+ 0,
+ aWindowBox.Width - aViewWindowBox.X - aViewWindowBox.Width,
+ aWindowBox.Height),
+ mxCanvas->getDevice());
+ }
+ else
+ {
+ // Paint two boxes above and below the view window.
+ mxBackgroundPolygon1 = PresenterGeometryHelper::CreatePolygon(
+ awt::Rectangle(
+ 0,
+ 0,
+ aWindowBox.Width,
+ aViewWindowBox.Y),
+ mxCanvas->getDevice());
+ mxBackgroundPolygon2 = PresenterGeometryHelper::CreatePolygon(
+ awt::Rectangle(
+ 0,
+ aViewWindowBox.Y + aViewWindowBox.Height,
+ aWindowBox.Width,
+ aWindowBox.Height - aViewWindowBox.Y - aViewWindowBox.Height),
+ mxCanvas->getDevice());
+ }
+}
+
+void PresenterSlideShowView::ThrowIfDisposed()
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterSlideShowView object has already been disposed",
+ static_cast<uno::XWeak*>(this));
+ }
+}
+
+void PresenterSlideShowView::impl_addAndConfigureView()
+{
+ Reference<presentation::XSlideShowView> xView (this);
+ mxSlideShow->addView(xView);
+ // Prevent embedded sounds being played twice at the same time by
+ // disabling sound for the new slide show view.
+ beans::PropertyValue aProperty;
+ aProperty.Name = "IsSoundEnabled";
+ Sequence<Any> aValues{ Any(xView), Any(false) };
+ aProperty.Value <<= aValues;
+ mxSlideShow->setProperty(aProperty);
+}
+
+} // end of namespace ::sd::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterSlideShowView.hxx b/sdext/source/presenter/PresenterSlideShowView.hxx
new file mode 100644
index 000000000..d009888f9
--- /dev/null
+++ b/sdext/source/presenter/PresenterSlideShowView.hxx
@@ -0,0 +1,241 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSLIDESHOWVIEW_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSLIDESHOWVIEW_HXX
+
+#include "PresenterViewFactory.hxx"
+#include <com/sun/star/presentation/XSlideShowView.hpp>
+#include <com/sun/star/awt/XPaintListener.hpp>
+#include <com/sun/star/awt/XMouseListener.hpp>
+#include <com/sun/star/awt/XMouseMotionListener.hpp>
+#include <com/sun/star/awt/XPointer.hpp>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/drawing/framework/XPane.hpp>
+#include <com/sun/star/drawing/framework/XResourceId.hpp>
+#include <com/sun/star/drawing/framework/XView.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/presentation/XSlideShowController.hpp>
+#include <com/sun/star/rendering/XPolyPolygon2D.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+namespace sdext::presenter {
+
+typedef cppu::WeakComponentImplHelper<
+ css::presentation::XSlideShowView,
+ css::awt::XPaintListener,
+ css::awt::XMouseListener,
+ css::awt::XMouseMotionListener,
+ css::awt::XWindowListener,
+ css::drawing::framework::XView,
+ css::drawing::XDrawView
+ > PresenterSlideShowViewInterfaceBase;
+
+/** Life view in a secondary window of a full screen slide show.
+*/
+class PresenterSlideShowView
+ : protected ::cppu::BaseMutex,
+ public PresenterSlideShowViewInterfaceBase,
+ public CachablePresenterView
+{
+public:
+ PresenterSlideShowView (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterSlideShowView() override;
+ PresenterSlideShowView(const PresenterSlideShowView&) = delete;
+ PresenterSlideShowView& operator=(const PresenterSlideShowView&) = delete;
+
+ void LateInit();
+ virtual void SAL_CALL disposing() override;
+
+ // CachablePresenterView
+
+ virtual void ReleaseView() override;
+
+ // XSlideShowView
+
+ 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;
+
+ // lang::XEventListener
+ virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override;
+
+ // XPaintListener
+ virtual void SAL_CALL windowPaint (const css::awt::PaintEvent& rEvent) override;
+
+ // XMouseListener
+ virtual void SAL_CALL mousePressed (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseReleased (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseEntered (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseExited (const css::awt::MouseEvent& rEvent) override;
+
+ // XMouseMotionListener
+
+ virtual void SAL_CALL mouseDragged (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseMoved (const css::awt::MouseEvent& rEvent) override;
+
+ // XWindowListener
+
+ virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override;
+
+ // XView
+
+ virtual css::uno::Reference<css::drawing::framework::XResourceId> SAL_CALL
+ getResourceId() override;
+
+ virtual sal_Bool SAL_CALL isAnchorOnly() override;
+
+ // XDrawView
+
+ virtual void SAL_CALL setCurrentPage (
+ const css::uno::Reference<css::drawing::XDrawPage>& rxSlide) override;
+
+ virtual css::uno::Reference<css::drawing::XDrawPage> SAL_CALL getCurrentPage() override;
+
+ // CachablePresenterView
+
+ virtual void ActivatePresenterView() override;
+
+ virtual void DeactivatePresenterView() override;
+
+private:
+ css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ css::uno::Reference<css::drawing::framework::XResourceId> mxViewId;
+ css::uno::Reference<css::frame::XController> mxController;
+ css::uno::Reference<css::presentation::XSlideShowController> mxSlideShowController;
+ css::uno::Reference<css::presentation::XSlideShow> mxSlideShow;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+ css::uno::Reference<css::rendering::XCanvas> mxViewCanvas;
+ css::uno::Reference<css::awt::XPointer> mxPointer;
+ css::uno::Reference<css::awt::XWindow> mxWindow;
+ css::uno::Reference<css::awt::XWindow> mxViewWindow;
+ css::uno::Reference<css::drawing::framework::XPane> mxTopPane;
+ css::uno::Reference<css::drawing::XPresenterHelper> mxPresenterHelper;
+ css::uno::Reference<css::rendering::XPolyPolygon2D> mxBackgroundPolygon1;
+ css::uno::Reference<css::rendering::XPolyPolygon2D> mxBackgroundPolygon2;
+ bool mbIsViewAdded;
+
+ /** Aspect ratio of the current slide.
+ */
+ double mnPageAspectRatio;
+
+ /** This broadcast helper is used to notify listeners registered to a
+ SlideShowView object.
+ */
+ ::cppu::OBroadcastHelper maBroadcaster;
+
+ SharedBitmapDescriptor mpBackground;
+
+ bool mbIsForcedPaintPending;
+ bool mbIsPaintPending;
+ OUString msClickToExitPresentationText;
+ OUString msClickToExitPresentationTitle;
+ OUString msTitleTemplate;
+ bool mbIsEndSlideVisible;
+ css::uno::Reference<css::drawing::XDrawPage> mxCurrentSlide;
+
+ /** Create the window into which the slide show will render its
+ content. This window has the correct aspect ratio and is displayed centered
+ and as large as possible in its parent window.
+ */
+ css::uno::Reference<css::awt::XWindow> CreateViewWindow (
+ const css::uno::Reference<css::awt::XWindow>& rxParentWindow) const;
+ css::uno::Reference<css::rendering::XCanvas> CreateViewCanvas (
+ const css::uno::Reference<css::awt::XWindow>& rxWindow) const;
+
+ void Resize();
+
+ void PaintOuterWindow (const css::awt::Rectangle& rRepaintBox);
+ void PaintInnerWindow (const css::awt::PaintEvent& rEvent);
+ void PaintEndSlide (const css::awt::Rectangle& rRepaintBox);
+
+ void CreateBackgroundPolygons();
+
+ /** @throws css::lang::DisposedException when the object has already been
+ disposed.
+ */
+ void ThrowIfDisposed();
+
+ void impl_addAndConfigureView();
+};
+
+} // end of namespace ::sd::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterSlideSorter.cxx b/sdext/source/presenter/PresenterSlideSorter.cxx
new file mode 100644
index 000000000..f89061ef6
--- /dev/null
+++ b/sdext/source/presenter/PresenterSlideSorter.cxx
@@ -0,0 +1,1929 @@
+/* -*- 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 <vcl/settings.hxx>
+
+#include "PresenterSlideSorter.hxx"
+#include "PresenterButton.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterGeometryHelper.hxx"
+#include "PresenterPaintManager.hxx"
+#include "PresenterPaneBase.hxx"
+#include "PresenterScrollBar.hxx"
+#include "PresenterUIPainter.hxx"
+#include "PresenterWindowManager.hxx"
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <com/sun/star/rendering/XBitmapCanvas.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/TextDirection.hpp>
+#include <algorithm>
+#include <math.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace {
+ const sal_Int32 gnVerticalGap (10);
+ const sal_Int32 gnVerticalBorder (10);
+ const sal_Int32 gnHorizontalGap (10);
+ const sal_Int32 gnHorizontalBorder (10);
+
+ const double gnMinimalPreviewWidth (200);
+ const double gnPreferredPreviewWidth (300);
+ const double gnMaximalPreviewWidth (400);
+ const sal_Int32 gnPreferredColumnCount (6);
+ const double gnMinimalHorizontalPreviewGap(15);
+ const double gnPreferredHorizontalPreviewGap(25);
+ const double gnMaximalHorizontalPreviewGap(50);
+ const double gnPreferredVerticalPreviewGap(25);
+
+ const sal_Int32 gnHorizontalLabelBorder (3);
+ const sal_Int32 gnHorizontalLabelPadding (5);
+
+ const sal_Int32 gnVerticalButtonPadding (gnVerticalGap);
+}
+
+namespace sdext::presenter {
+
+namespace {
+ sal_Int32 round (const double nValue) { return sal::static_int_cast<sal_Int32>(0.5 + nValue); }
+ sal_Int32 floor (const double nValue) { return sal::static_int_cast<sal_Int32>(nValue); }
+}
+
+//===== PresenterSlideSorter::Layout ==========================================
+
+class PresenterSlideSorter::Layout
+{
+public:
+ explicit Layout (const ::rtl::Reference<PresenterScrollBar>& rpVerticalScrollBar);
+
+ void Update (const geometry::RealRectangle2D& rBoundingBox, const double nSlideAspectRatio);
+ void SetupVisibleArea();
+ void UpdateScrollBars();
+ bool IsScrollBarNeeded (const sal_Int32 nSlideCount);
+ geometry::RealPoint2D GetLocalPosition (const geometry::RealPoint2D& rWindowPoint) const;
+ geometry::RealPoint2D GetWindowPosition(const geometry::RealPoint2D& rLocalPoint) const;
+ sal_Int32 GetColumn (const geometry::RealPoint2D& rLocalPoint) const;
+ sal_Int32 GetRow (const geometry::RealPoint2D& rLocalPoint,
+ const bool bReturnInvalidValue = false) const;
+ sal_Int32 GetSlideIndexForPosition (const css::geometry::RealPoint2D& rPoint) const;
+ css::geometry::RealPoint2D GetPoint (
+ const sal_Int32 nSlideIndex,
+ const sal_Int32 nRelativeHorizontalPosition,
+ const sal_Int32 nRelativeVerticalPosition) const;
+ css::awt::Rectangle GetBoundingBox (const sal_Int32 nSlideIndex) const;
+ void ForAllVisibleSlides (const ::std::function<void (sal_Int32)>& rAction);
+ sal_Int32 GetFirstVisibleSlideIndex() const;
+ sal_Int32 GetLastVisibleSlideIndex() const;
+ bool SetHorizontalOffset (const double nOffset);
+ bool SetVerticalOffset (const double nOffset);
+
+ css::geometry::RealRectangle2D maBoundingBox;
+ css::geometry::IntegerSize2D maPreviewSize;
+ sal_Int32 mnHorizontalOffset;
+ sal_Int32 mnVerticalOffset;
+ sal_Int32 mnHorizontalGap;
+ sal_Int32 mnVerticalGap;
+ sal_Int32 mnHorizontalBorder;
+ sal_Int32 mnVerticalBorder;
+ sal_Int32 mnRowCount;
+ sal_Int32 mnColumnCount;
+ sal_Int32 mnSlideCount;
+ sal_Int32 mnFirstVisibleColumn;
+ sal_Int32 mnLastVisibleColumn;
+ sal_Int32 mnFirstVisibleRow;
+ sal_Int32 mnLastVisibleRow;
+
+private:
+ ::rtl::Reference<PresenterScrollBar> mpVerticalScrollBar;
+
+ sal_Int32 GetIndex (const sal_Int32 nRow, const sal_Int32 nColumn) const;
+ sal_Int32 GetRow (const sal_Int32 nSlideIndex) const;
+ sal_Int32 GetColumn (const sal_Int32 nSlideIndex) const;
+};
+
+//==== PresenterSlideSorter::MouseOverManager =================================
+
+class PresenterSlideSorter::MouseOverManager
+{
+public:
+ MouseOverManager (
+ const Reference<container::XIndexAccess>& rxSlides,
+ const std::shared_ptr<PresenterTheme>& rpTheme,
+ const Reference<awt::XWindow>& rxInvalidateTarget,
+ const std::shared_ptr<PresenterPaintManager>& rpPaintManager);
+ MouseOverManager(const MouseOverManager&) = delete;
+ MouseOverManager& operator=(const MouseOverManager&) = delete;
+
+ void Paint (
+ const sal_Int32 nSlideIndex,
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const Reference<rendering::XPolyPolygon2D>& rxClip);
+
+ void SetSlide (
+ const sal_Int32 nSlideIndex,
+ const awt::Rectangle& rBox);
+
+private:
+ Reference<rendering::XCanvas> mxCanvas;
+ const Reference<container::XIndexAccess> mxSlides;
+ SharedBitmapDescriptor mpLeftLabelBitmap;
+ SharedBitmapDescriptor mpCenterLabelBitmap;
+ SharedBitmapDescriptor mpRightLabelBitmap;
+ PresenterTheme::SharedFontDescriptor mpFont;
+ sal_Int32 mnSlideIndex;
+ awt::Rectangle maSlideBoundingBox;
+ OUString msText;
+ Reference<rendering::XBitmap> mxBitmap;
+ Reference<awt::XWindow> mxInvalidateTarget;
+ std::shared_ptr<PresenterPaintManager> mpPaintManager;
+
+ void SetCanvas (
+ const Reference<rendering::XCanvas>& rxCanvas);
+ /** Create a bitmap that shows the given text and is not wider than the
+ given maximal width.
+ */
+ Reference<rendering::XBitmap> CreateBitmap (
+ const OUString& rsText,
+ const sal_Int32 nMaximalWidth) const;
+ void Invalidate();
+ geometry::IntegerSize2D CalculateLabelSize (
+ const OUString& rsText) const;
+ OUString GetFittingText (const OUString& rsText, const double nMaximalWidth) const;
+ void PaintButtonBackground (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const geometry::IntegerSize2D& rSize) const;
+};
+
+//==== PresenterSlideSorter::CurrentSlideFrameRenderer ========================
+
+class PresenterSlideSorter::CurrentSlideFrameRenderer
+{
+public:
+ CurrentSlideFrameRenderer (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas);
+
+ void PaintCurrentSlideFrame (
+ const awt::Rectangle& rSlideBoundingBox,
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const geometry::RealRectangle2D& rClipBox);
+
+ /** Enlarge the given rectangle to include the current slide indicator.
+ */
+ awt::Rectangle GetBoundingBox (
+ const awt::Rectangle& rSlideBoundingBox);
+
+private:
+ SharedBitmapDescriptor mpTopLeft;
+ SharedBitmapDescriptor mpTop;
+ SharedBitmapDescriptor mpTopRight;
+ SharedBitmapDescriptor mpLeft;
+ SharedBitmapDescriptor mpRight;
+ SharedBitmapDescriptor mpBottomLeft;
+ SharedBitmapDescriptor mpBottom;
+ SharedBitmapDescriptor mpBottomRight;
+ sal_Int32 mnTopFrameSize;
+ sal_Int32 mnLeftFrameSize;
+ sal_Int32 mnRightFrameSize;
+ sal_Int32 mnBottomFrameSize;
+
+ static void PaintBitmapOnce(
+ const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const Reference<rendering::XPolyPolygon2D>& rxClip,
+ const double nX,
+ const double nY);
+ static void PaintBitmapTiled(
+ const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const geometry::RealRectangle2D& rClipBox,
+ const double nX,
+ const double nY,
+ const double nWidth,
+ const double nHeight);
+};
+
+//===== PresenterSlideSorter ==================================================
+
+PresenterSlideSorter::PresenterSlideSorter (
+ const Reference<uno::XComponentContext>& rxContext,
+ const Reference<XResourceId>& rxViewId,
+ const Reference<frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterSlideSorterInterfaceBase(m_aMutex),
+ mxComponentContext(rxContext),
+ mxViewId(rxViewId),
+ mpPresenterController(rpPresenterController),
+ mxSlideShowController(mpPresenterController->GetSlideShowController()),
+ mbIsLayoutPending(true),
+ mnSlideIndexMousePressed(-1),
+ mnCurrentSlideIndex(-1),
+ mnSeparatorY(0),
+ maSeparatorColor(0x00ffffff)
+{
+ if ( ! rxContext.is()
+ || ! rxViewId.is()
+ || ! rxController.is()
+ || ! rpPresenterController)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ if ( ! mxSlideShowController.is())
+ throw RuntimeException();
+
+ try
+ {
+ // Get pane and window.
+ Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW);
+ Reference<XConfigurationController> xCC (
+ xCM->getConfigurationController(), UNO_SET_THROW);
+ Reference<lang::XMultiComponentFactory> xFactory (
+ mxComponentContext->getServiceManager(), UNO_SET_THROW);
+
+ mxPane.set(xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW);
+ mxWindow = mxPane->getWindow();
+
+ // Add window listener.
+ mxWindow->addWindowListener(this);
+ mxWindow->addPaintListener(this);
+ mxWindow->addMouseListener(this);
+ mxWindow->addMouseMotionListener(this);
+ mxWindow->setVisible(true);
+
+ // Remember the current slide.
+ mnCurrentSlideIndex = mxSlideShowController->getCurrentSlideIndex();
+
+ // Create the scroll bar.
+ mpVerticalScrollBar.set(
+ new PresenterVerticalScrollBar(
+ rxContext,
+ mxWindow,
+ mpPresenterController->GetPaintManager(),
+ [this] (double const offset) { return this->SetVerticalOffset(offset); }));
+
+ mpCloseButton = PresenterButton::Create(
+ rxContext,
+ mpPresenterController,
+ mpPresenterController->GetTheme(),
+ mxWindow,
+ mxCanvas,
+ "SlideSorterCloser");
+
+ if (mpPresenterController->GetTheme() != nullptr)
+ {
+ PresenterTheme::SharedFontDescriptor pFont (
+ mpPresenterController->GetTheme()->GetFont("ButtonFont"));
+ if (pFont)
+ maSeparatorColor = pFont->mnColor;
+ }
+
+ // Create the layout.
+ mpLayout = std::make_shared<Layout>(mpVerticalScrollBar);
+
+ // Create the preview cache.
+ mxPreviewCache.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.drawing.PresenterPreviewCache",
+ mxComponentContext),
+ UNO_QUERY_THROW);
+ Reference<container::XIndexAccess> xSlides (mxSlideShowController, UNO_QUERY);
+ mxPreviewCache->setDocumentSlides(xSlides, rxController->getModel());
+ mxPreviewCache->addPreviewCreationNotifyListener(this);
+ if (xSlides.is())
+ {
+ mpLayout->mnSlideCount = xSlides->getCount();
+ }
+
+ // Create the mouse over manager.
+ mpMouseOverManager.reset(new MouseOverManager(
+ Reference<container::XIndexAccess>(mxSlideShowController, UNO_QUERY),
+ mpPresenterController->GetTheme(),
+ mxWindow,
+ mpPresenterController->GetPaintManager()));
+
+ // Listen for changes of the current slide.
+ Reference<beans::XPropertySet> xControllerProperties (rxController, UNO_QUERY_THROW);
+ xControllerProperties->addPropertyChangeListener(
+ "CurrentPage",
+ this);
+
+ // Move the current slide in the center of the window.
+ const awt::Rectangle aCurrentSlideBBox (mpLayout->GetBoundingBox(mnCurrentSlideIndex));
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ SetHorizontalOffset(aCurrentSlideBBox.X - aWindowBox.Width/2.0);
+ }
+ catch (RuntimeException&)
+ {
+ disposing();
+ throw;
+ }
+}
+
+PresenterSlideSorter::~PresenterSlideSorter()
+{
+}
+
+void SAL_CALL PresenterSlideSorter::disposing()
+{
+ mxComponentContext = nullptr;
+ mxViewId = nullptr;
+ mxPane = nullptr;
+
+ if (mpVerticalScrollBar.is())
+ {
+ Reference<lang::XComponent> xComponent = mpVerticalScrollBar;
+ mpVerticalScrollBar = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ if (mpCloseButton.is())
+ {
+ Reference<lang::XComponent> xComponent = mpCloseButton;
+ mpCloseButton = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ if (mxCanvas.is())
+ {
+ Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->removeEventListener(static_cast<awt::XWindowListener*>(this));
+ mxCanvas = nullptr;
+ }
+ mpPresenterController = nullptr;
+ mxSlideShowController = nullptr;
+ mpLayout.reset();
+ mpMouseOverManager.reset();
+
+ if (mxPreviewCache.is())
+ {
+ mxPreviewCache->removePreviewCreationNotifyListener(this);
+
+ Reference<XComponent> xComponent (mxPreviewCache, UNO_QUERY);
+ mxPreviewCache = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+
+ if (mxWindow.is())
+ {
+ mxWindow->removeWindowListener(this);
+ mxWindow->removePaintListener(this);
+ mxWindow->removeMouseListener(this);
+ mxWindow->removeMouseMotionListener(this);
+ }
+}
+
+//----- lang::XEventListener --------------------------------------------------
+
+void SAL_CALL PresenterSlideSorter::disposing (const lang::EventObject& rEventObject)
+{
+ if (rEventObject.Source == mxWindow)
+ {
+ mxWindow = nullptr;
+ dispose();
+ }
+ else if (rEventObject.Source == mxPreviewCache)
+ {
+ mxPreviewCache = nullptr;
+ dispose();
+ }
+ else if (rEventObject.Source == mxCanvas)
+ {
+ mxCanvas = nullptr;
+ mbIsLayoutPending = true;
+
+ mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
+ }
+}
+
+//----- XWindowListener -------------------------------------------------------
+
+void SAL_CALL PresenterSlideSorter::windowResized (const awt::WindowEvent&)
+{
+ ThrowIfDisposed();
+ mbIsLayoutPending = true;
+ mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
+}
+
+void SAL_CALL PresenterSlideSorter::windowMoved (const awt::WindowEvent&)
+{
+ ThrowIfDisposed();
+}
+
+void SAL_CALL PresenterSlideSorter::windowShown (const lang::EventObject&)
+{
+ ThrowIfDisposed();
+ mbIsLayoutPending = true;
+ mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
+}
+
+void SAL_CALL PresenterSlideSorter::windowHidden (const lang::EventObject&)
+{
+ ThrowIfDisposed();
+}
+
+//----- XPaintListener --------------------------------------------------------
+
+void SAL_CALL PresenterSlideSorter::windowPaint (const css::awt::PaintEvent& rEvent)
+{
+ // Deactivated views must not be painted.
+ if ( ! mbIsPresenterViewActive)
+ return;
+
+ Paint(rEvent.UpdateRect);
+
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(false);
+}
+
+//----- XMouseListener --------------------------------------------------------
+
+void SAL_CALL PresenterSlideSorter::mousePressed (const css::awt::MouseEvent& rEvent)
+{
+ css::awt::MouseEvent rTemp =rEvent;
+ /// check whether RTL interface or not
+ if(AllSettings::GetLayoutRTL()){
+ awt::Rectangle aBox = mxWindow->getPosSize();
+ rTemp.X=aBox.Width-rEvent.X;
+ }
+ const geometry::RealPoint2D aPosition(rTemp.X, rEvent.Y);
+ mnSlideIndexMousePressed = mpLayout->GetSlideIndexForPosition(aPosition);
+}
+
+void SAL_CALL PresenterSlideSorter::mouseReleased (const css::awt::MouseEvent& rEvent)
+{
+ css::awt::MouseEvent rTemp =rEvent;
+ /// check whether RTL interface or not
+ if(AllSettings::GetLayoutRTL()){
+ awt::Rectangle aBox = mxWindow->getPosSize();
+ rTemp.X=aBox.Width-rEvent.X;
+ }
+ const geometry::RealPoint2D aPosition(rTemp.X, rEvent.Y);
+ const sal_Int32 nSlideIndex (mpLayout->GetSlideIndexForPosition(aPosition));
+
+ if (nSlideIndex != mnSlideIndexMousePressed || mnSlideIndexMousePressed < 0)
+ return;
+
+ switch (rEvent.ClickCount)
+ {
+ case 1:
+ default:
+ GotoSlide(nSlideIndex);
+ break;
+
+ case 2:
+ OSL_ASSERT(mpPresenterController);
+ OSL_ASSERT(mpPresenterController->GetWindowManager());
+ mpPresenterController->GetWindowManager()->SetSlideSorterState(false);
+ GotoSlide(nSlideIndex);
+ break;
+ }
+}
+
+void SAL_CALL PresenterSlideSorter::mouseEntered (const css::awt::MouseEvent&) {}
+
+void SAL_CALL PresenterSlideSorter::mouseExited (const css::awt::MouseEvent&)
+{
+ mnSlideIndexMousePressed = -1;
+ if (mpMouseOverManager != nullptr)
+ mpMouseOverManager->SetSlide(mnSlideIndexMousePressed, awt::Rectangle(0,0,0,0));
+}
+
+//----- XMouseMotionListener --------------------------------------------------
+
+void SAL_CALL PresenterSlideSorter::mouseMoved (const css::awt::MouseEvent& rEvent)
+{
+ if (mpMouseOverManager == nullptr)
+ return;
+
+ css::awt::MouseEvent rTemp =rEvent;
+ /// check whether RTL interface or not
+ if(AllSettings::GetLayoutRTL()){
+ awt::Rectangle aBox = mxWindow->getPosSize();
+ rTemp.X=aBox.Width-rEvent.X;
+ }
+ const geometry::RealPoint2D aPosition(rTemp.X, rEvent.Y);
+ sal_Int32 nSlideIndex (mpLayout->GetSlideIndexForPosition(aPosition));
+
+ if (nSlideIndex < 0)
+ {
+ mnSlideIndexMousePressed = -1;
+ mpMouseOverManager->SetSlide(nSlideIndex, awt::Rectangle(0,0,0,0));
+ }
+ else
+ {
+ mpMouseOverManager->SetSlide(
+ nSlideIndex,
+ mpLayout->GetBoundingBox(nSlideIndex));
+ }
+}
+
+void SAL_CALL PresenterSlideSorter::mouseDragged (const css::awt::MouseEvent&) {}
+
+//----- XResourceId -----------------------------------------------------------
+
+Reference<XResourceId> SAL_CALL PresenterSlideSorter::getResourceId()
+{
+ ThrowIfDisposed();
+ return mxViewId;
+}
+
+sal_Bool SAL_CALL PresenterSlideSorter::isAnchorOnly()
+{
+ return false;
+}
+
+//----- XPropertyChangeListener -----------------------------------------------
+
+void SAL_CALL PresenterSlideSorter::propertyChange (
+ const css::beans::PropertyChangeEvent&)
+{}
+
+//----- XSlidePreviewCacheListener --------------------------------------------
+
+void SAL_CALL PresenterSlideSorter::notifyPreviewCreation (
+ sal_Int32 nSlideIndex)
+{
+ OSL_ASSERT(mpLayout != nullptr);
+
+ awt::Rectangle aBBox (mpLayout->GetBoundingBox(nSlideIndex));
+ mpPresenterController->GetPaintManager()->Invalidate(mxWindow, aBBox, true);
+}
+
+//----- XDrawView -------------------------------------------------------------
+
+void SAL_CALL PresenterSlideSorter::setCurrentPage (const Reference<drawing::XDrawPage>&)
+{
+ ThrowIfDisposed();
+ ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
+
+ if (!mxSlideShowController.is())
+ return;
+
+ const sal_Int32 nNewCurrentSlideIndex (mxSlideShowController->getCurrentSlideIndex());
+ if (nNewCurrentSlideIndex == mnCurrentSlideIndex)
+ return;
+
+ mnCurrentSlideIndex = nNewCurrentSlideIndex;
+
+ // Request a repaint of the previous current slide to hide its
+ // current slide indicator.
+ mpPresenterController->GetPaintManager()->Invalidate(
+ mxWindow,
+ maCurrentSlideFrameBoundingBox);
+
+ // Request a repaint of the new current slide to show its
+ // current slide indicator.
+ maCurrentSlideFrameBoundingBox = mpCurrentSlideFrameRenderer->GetBoundingBox(
+ mpLayout->GetBoundingBox(mnCurrentSlideIndex));
+ mpPresenterController->GetPaintManager()->Invalidate(
+ mxWindow,
+ maCurrentSlideFrameBoundingBox);
+}
+
+Reference<drawing::XDrawPage> SAL_CALL PresenterSlideSorter::getCurrentPage()
+{
+ ThrowIfDisposed();
+ return nullptr;
+}
+
+
+void PresenterSlideSorter::UpdateLayout()
+{
+ if ( ! mxWindow.is())
+ return;
+
+ mbIsLayoutPending = false;
+
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ sal_Int32 nLeftBorderWidth (aWindowBox.X);
+
+ // Get border width.
+ PresenterPaneContainer::SharedPaneDescriptor pPane (
+ mpPresenterController->GetPaneContainer()->FindViewURL(
+ mxViewId->getResourceURL()));
+ do
+ {
+ if (!pPane)
+ break;
+ if ( ! pPane->mxPane.is())
+ break;
+
+ Reference<drawing::framework::XPaneBorderPainter> xBorderPainter (
+ pPane->mxPane->GetPaneBorderPainter());
+ if ( ! xBorderPainter.is())
+ break;
+ xBorderPainter->addBorder (
+ mxViewId->getAnchor()->getResourceURL(),
+ awt::Rectangle(0, 0, aWindowBox.Width, aWindowBox.Height),
+ drawing::framework::BorderType_INNER_BORDER);
+ }
+ while(false);
+
+ // Place vertical separator.
+ mnSeparatorY = aWindowBox.Height - mpCloseButton->GetSize().Height - gnVerticalButtonPadding;
+
+ PlaceCloseButton(pPane, aWindowBox, nLeftBorderWidth);
+
+ geometry::RealRectangle2D aUpperBox(
+ gnHorizontalBorder,
+ gnVerticalBorder,
+ aWindowBox.Width - 2*gnHorizontalBorder,
+ mnSeparatorY - gnVerticalGap);
+
+ // Determine whether the scroll bar has to be displayed.
+ aUpperBox = PlaceScrollBars(aUpperBox);
+
+ mpLayout->Update(aUpperBox, GetSlideAspectRatio());
+ mpLayout->SetupVisibleArea();
+ mpLayout->UpdateScrollBars();
+
+ // Tell the preview cache about some of the values.
+ mxPreviewCache->setPreviewSize(mpLayout->maPreviewSize);
+ mxPreviewCache->setVisibleRange(
+ mpLayout->GetFirstVisibleSlideIndex(),
+ mpLayout->GetLastVisibleSlideIndex());
+
+ // Clear the frame polygon so that it is re-created on the next paint.
+ mxPreviewFrame = nullptr;
+}
+
+geometry::RealRectangle2D PresenterSlideSorter::PlaceScrollBars (
+ const geometry::RealRectangle2D& rUpperBox)
+{
+ mpLayout->Update(rUpperBox, GetSlideAspectRatio());
+ bool bIsScrollBarNeeded (false);
+ Reference<container::XIndexAccess> xSlides (mxSlideShowController, UNO_QUERY_THROW);
+ bIsScrollBarNeeded = mpLayout->IsScrollBarNeeded(xSlides->getCount());
+ if (mpVerticalScrollBar)
+ {
+ if (bIsScrollBarNeeded)
+ {
+ if(AllSettings::GetLayoutRTL())
+ {
+ mpVerticalScrollBar->SetPosSize(geometry::RealRectangle2D(
+ rUpperBox.X1,
+ rUpperBox.Y1,
+ rUpperBox.X1 + mpVerticalScrollBar->GetSize(),
+ rUpperBox.Y2));
+ mpVerticalScrollBar->SetVisible(true);
+ // Reduce area covered by the scroll bar from the available
+ // space.
+ return geometry::RealRectangle2D(
+ rUpperBox.X1 + gnHorizontalGap + mpVerticalScrollBar->GetSize(),
+ rUpperBox.Y1,
+ rUpperBox.X2,
+ rUpperBox.Y2);
+ }
+ else
+ {
+ // if it's not RTL place vertical scroll bar at right border.
+ mpVerticalScrollBar->SetPosSize(geometry::RealRectangle2D(
+ rUpperBox.X2 - mpVerticalScrollBar->GetSize(),
+ rUpperBox.Y1,
+ rUpperBox.X2,
+ rUpperBox.Y2));
+ mpVerticalScrollBar->SetVisible(true);
+ // Reduce area covered by the scroll bar from the available
+ // space.
+ return geometry::RealRectangle2D(
+ rUpperBox.X1,
+ rUpperBox.Y1,
+ rUpperBox.X2 - mpVerticalScrollBar->GetSize() - gnHorizontalGap,
+ rUpperBox.Y2);
+ }
+ }
+ else
+ mpVerticalScrollBar->SetVisible(false);
+ }
+ return rUpperBox;
+}
+
+void PresenterSlideSorter::PlaceCloseButton (
+ const PresenterPaneContainer::SharedPaneDescriptor& rpPane,
+ const awt::Rectangle& rCenterBox,
+ const sal_Int32 nLeftBorderWidth)
+{
+ // Place button. When the callout is near the center then the button is
+ // centered over the callout. Otherwise it is centered with respect to
+ // the whole window.
+ sal_Int32 nCloseButtonCenter (rCenterBox.Width/2);
+ if (rpPane && rpPane->mxPane.is())
+ {
+ const sal_Int32 nCalloutCenter (-nLeftBorderWidth);
+ const sal_Int32 nDistanceFromWindowCenter (abs(nCalloutCenter - rCenterBox.Width/2));
+ const sal_Int32 nButtonWidth (mpCloseButton->GetSize().Width);
+ const static sal_Int32 nMaxDistanceForCalloutCentering (nButtonWidth * 2);
+ if (nDistanceFromWindowCenter < nMaxDistanceForCalloutCentering)
+ {
+ if (nCalloutCenter < nButtonWidth/2)
+ nCloseButtonCenter = nButtonWidth/2;
+ else if (nCalloutCenter > rCenterBox.Width-nButtonWidth/2)
+ nCloseButtonCenter = rCenterBox.Width-nButtonWidth/2;
+ else
+ nCloseButtonCenter = nCalloutCenter;
+ }
+ }
+ mpCloseButton->SetCenter(geometry::RealPoint2D(
+ nCloseButtonCenter,
+ rCenterBox.Height - mpCloseButton->GetSize().Height/ 2));
+}
+
+void PresenterSlideSorter::ClearBackground (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const awt::Rectangle& rUpdateBox)
+{
+ OSL_ASSERT(rxCanvas.is());
+
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ mpPresenterController->GetCanvasHelper()->Paint(
+ mpPresenterController->GetViewBackground(mxViewId->getResourceURL()),
+ rxCanvas,
+ rUpdateBox,
+ awt::Rectangle(0,0,aWindowBox.Width,aWindowBox.Height),
+ awt::Rectangle());
+}
+
+double PresenterSlideSorter::GetSlideAspectRatio() const
+{
+ double nSlideAspectRatio (28.0/21.0);
+
+ try
+ {
+ Reference<container::XIndexAccess> xSlides(mxSlideShowController, UNO_QUERY_THROW);
+ if (mxSlideShowController.is() && xSlides->getCount()>0)
+ {
+ Reference<beans::XPropertySet> xProperties(xSlides->getByIndex(0),UNO_QUERY_THROW);
+ sal_Int32 nWidth (28000);
+ sal_Int32 nHeight (21000);
+ if ((xProperties->getPropertyValue("Width") >>= nWidth)
+ && (xProperties->getPropertyValue("Height") >>= nHeight)
+ && nHeight > 0)
+ {
+ nSlideAspectRatio = double(nWidth) / double(nHeight);
+ }
+ }
+ }
+ catch (RuntimeException&)
+ {
+ OSL_ASSERT(false);
+ }
+
+ return nSlideAspectRatio;
+}
+
+Reference<rendering::XBitmap> PresenterSlideSorter::GetPreview (const sal_Int32 nSlideIndex)
+{
+ if (nSlideIndex < 0 || nSlideIndex>=mpLayout->mnSlideCount)
+ return nullptr;
+ else if (mxPane.is())
+ return mxPreviewCache->getSlidePreview(nSlideIndex, mxPane->getCanvas());
+ else
+ return nullptr;
+}
+
+void PresenterSlideSorter::PaintPreview (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rUpdateBox,
+ const sal_Int32 nSlideIndex)
+{
+ OSL_ASSERT(rxCanvas.is());
+
+ geometry::IntegerSize2D aSize (mpLayout->maPreviewSize);
+
+ if (PresenterGeometryHelper::AreRectanglesDisjoint(
+ rUpdateBox,
+ mpLayout->GetBoundingBox(nSlideIndex)))
+ {
+ return;
+ }
+
+ Reference<rendering::XBitmap> xPreview (GetPreview(nSlideIndex));
+ bool isRTL = AllSettings::GetLayoutRTL();
+
+ const geometry::RealPoint2D aTopLeft (
+ mpLayout->GetWindowPosition(
+ mpLayout->GetPoint(nSlideIndex, isRTL?1:-1, -1)));
+
+ PresenterBitmapContainer aContainer (
+ "PresenterScreenSettings/ScrollBar/Bitmaps",
+ std::shared_ptr<PresenterBitmapContainer>(),
+ mxComponentContext,
+ rxCanvas);
+ Reference<container::XIndexAccess> xIndexAccess(mxSlideShowController, UNO_QUERY);
+ Reference<drawing::XDrawPage> xPage( xIndexAccess->getByIndex(nSlideIndex), UNO_QUERY);
+ bool bTransition = PresenterController::HasTransition(xPage);
+ bool bCustomAnimation = PresenterController::HasCustomAnimation(xPage);
+
+ // Create clip rectangle as intersection of the current update area and
+ // the bounding box of all previews.
+ geometry::RealRectangle2D aBoundingBox (mpLayout->maBoundingBox);
+ aBoundingBox.Y2 += 1;
+ const geometry::RealRectangle2D aClipBox (
+ PresenterGeometryHelper::Intersection(
+ PresenterGeometryHelper::ConvertRectangle(rUpdateBox),
+ aBoundingBox));
+ Reference<rendering::XPolyPolygon2D> xClip (
+ PresenterGeometryHelper::CreatePolygon(aClipBox, rxCanvas->getDevice()));
+
+ const rendering::ViewState aViewState (geometry::AffineMatrix2D(1,0,0, 0,1,0), xClip);
+
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(
+ 1, 0, aTopLeft.X,
+ 0, 1, aTopLeft.Y),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ // Emphasize the current slide.
+ if (nSlideIndex == mnCurrentSlideIndex)
+ {
+ if (mpCurrentSlideFrameRenderer != nullptr)
+ {
+ const awt::Rectangle aSlideBoundingBox(
+ sal::static_int_cast<sal_Int32>(0.5 + aTopLeft.X),
+ sal::static_int_cast<sal_Int32>(0.5 + aTopLeft.Y),
+ aSize.Width,
+ aSize.Height);
+ maCurrentSlideFrameBoundingBox
+ = mpCurrentSlideFrameRenderer->GetBoundingBox(aSlideBoundingBox);
+ mpCurrentSlideFrameRenderer->PaintCurrentSlideFrame (
+ aSlideBoundingBox,
+ mxCanvas,
+ aClipBox);
+ }
+ }
+
+ // Paint the preview.
+ if (xPreview.is())
+ {
+ aSize = xPreview->getSize();
+ if (aSize.Width > 0 && aSize.Height > 0)
+ {
+ rxCanvas->drawBitmap(xPreview, aViewState, aRenderState);
+ if( bCustomAnimation )
+ {
+ const awt::Rectangle aAnimationPreviewBox(aTopLeft.X+3, aTopLeft.Y+aSize.Height-40, 0, 0);
+ SharedBitmapDescriptor aAnimationDescriptor = aContainer.GetBitmap("Animation");
+ Reference<rendering::XBitmap> xAnimationIcon (aAnimationDescriptor->GetNormalBitmap());
+ rendering::RenderState aAnimationRenderState (
+ geometry::AffineMatrix2D(
+ 1, 0, aAnimationPreviewBox.X,
+ 0, 1, aAnimationPreviewBox.Y),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+ rxCanvas->drawBitmap(xAnimationIcon, aViewState, aAnimationRenderState);
+ }
+ if( bTransition )
+ {
+ const awt::Rectangle aTransitionPreviewBox(aTopLeft.X+3, aTopLeft.Y+aSize.Height-20, 0, 0);
+ SharedBitmapDescriptor aTransitionDescriptor = aContainer.GetBitmap("Transition");
+ Reference<rendering::XBitmap> xTransitionIcon (aTransitionDescriptor->GetNormalBitmap());
+ rendering::RenderState aTransitionRenderState (
+ geometry::AffineMatrix2D(
+ 1, 0, aTransitionPreviewBox.X,
+ 0, 1, aTransitionPreviewBox.Y),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+ rxCanvas->drawBitmap(xTransitionIcon, aViewState, aTransitionRenderState);
+ }
+ }
+ }
+
+ // Create a polygon that is used to paint a frame around previews. Its
+ // coordinates are chosen in the local coordinate system of a preview.
+ if ( ! mxPreviewFrame.is())
+ mxPreviewFrame = PresenterGeometryHelper::CreatePolygon(
+ awt::Rectangle(-1, -1, aSize.Width+2, aSize.Height+2),
+ rxCanvas->getDevice());
+
+ // Paint a border around the preview.
+ if (mxPreviewFrame.is())
+ {
+ const util::Color aFrameColor (0x00000000);
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, aFrameColor);
+ rxCanvas->drawPolyPolygon(mxPreviewFrame, aViewState, aRenderState);
+ }
+
+ // Paint mouse over effect.
+ mpMouseOverManager->Paint(nSlideIndex, mxCanvas, xClip);
+}
+
+void PresenterSlideSorter::Paint (const awt::Rectangle& rUpdateBox)
+{
+ const bool bCanvasChanged ( ! mxCanvas.is());
+ if ( ! ProvideCanvas())
+ return;
+
+ if (mpLayout->mnRowCount<=0 || mpLayout->mnColumnCount<=0)
+ {
+ OSL_ASSERT(mpLayout->mnRowCount>0 || mpLayout->mnColumnCount>0);
+ return;
+ }
+
+ ClearBackground(mxCanvas, rUpdateBox);
+
+ // Give the canvas to the controls.
+ if (bCanvasChanged)
+ {
+ if (mpVerticalScrollBar.is())
+ mpVerticalScrollBar->SetCanvas(mxCanvas);
+ if (mpCloseButton.is())
+ mpCloseButton->SetCanvas(mxCanvas, mxWindow);
+ }
+
+ // Now that the controls have a canvas we can do the layouting.
+ if (mbIsLayoutPending)
+ UpdateLayout();
+
+ // Paint the horizontal separator.
+ rendering::RenderState aRenderState (geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr, Sequence<double>(4), rendering::CompositeOperation::SOURCE);
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, maSeparatorColor);
+ mxCanvas->drawLine(
+ geometry::RealPoint2D(0, mnSeparatorY),
+ geometry::RealPoint2D(mxWindow->getPosSize().Width, mnSeparatorY),
+ rendering::ViewState(geometry::AffineMatrix2D(1,0,0, 0,1,0), nullptr),
+ aRenderState);
+
+ // Paint the slides.
+ if ( ! PresenterGeometryHelper::AreRectanglesDisjoint(
+ rUpdateBox,
+ PresenterGeometryHelper::ConvertRectangle(mpLayout->maBoundingBox)))
+ {
+ mpLayout->ForAllVisibleSlides(
+ [this, &rUpdateBox] (sal_Int32 const nIndex) {
+ return this->PaintPreview(this->mxCanvas, rUpdateBox, nIndex);
+ });
+ }
+
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(false);
+}
+
+void PresenterSlideSorter::SetHorizontalOffset (const double nXOffset)
+{
+ if (mpLayout->SetHorizontalOffset(nXOffset))
+ {
+ mxPreviewCache->setVisibleRange(
+ mpLayout->GetFirstVisibleSlideIndex(),
+ mpLayout->GetLastVisibleSlideIndex());
+
+ mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
+ }
+}
+
+void PresenterSlideSorter::SetVerticalOffset (const double nYOffset)
+{
+ if (mpLayout->SetVerticalOffset(nYOffset))
+ {
+ mxPreviewCache->setVisibleRange(
+ mpLayout->GetFirstVisibleSlideIndex(),
+ mpLayout->GetLastVisibleSlideIndex());
+
+ mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
+ }
+}
+
+void PresenterSlideSorter::GotoSlide (const sal_Int32 nSlideIndex)
+{
+ mxSlideShowController->gotoSlideIndex(nSlideIndex);
+}
+
+bool PresenterSlideSorter::ProvideCanvas()
+{
+ if ( ! mxCanvas.is())
+ {
+ if (mxPane.is())
+ mxCanvas = mxPane->getCanvas();
+
+ // Register as event listener so that we are informed when the
+ // canvas is disposed (and we have to fetch another one).
+ Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->addEventListener(static_cast<awt::XWindowListener*>(this));
+
+ mpCurrentSlideFrameRenderer =
+ std::make_shared<CurrentSlideFrameRenderer>(mxComponentContext, mxCanvas);
+ }
+ return mxCanvas.is();
+}
+
+void PresenterSlideSorter::ThrowIfDisposed()
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterSlideSorter has been already disposed",
+ static_cast<uno::XWeak*>(this));
+ }
+}
+
+//===== PresenterSlideSorter::Layout ==========================================
+
+PresenterSlideSorter::Layout::Layout (
+ const ::rtl::Reference<PresenterScrollBar>& rpVerticalScrollBar)
+ : mnHorizontalOffset(0),
+ mnVerticalOffset(0),
+ mnHorizontalGap(0),
+ mnVerticalGap(0),
+ mnHorizontalBorder(0),
+ mnVerticalBorder(0),
+ mnRowCount(1),
+ mnColumnCount(1),
+ mnSlideCount(0),
+ mnFirstVisibleColumn(-1),
+ mnLastVisibleColumn(-1),
+ mnFirstVisibleRow(-1),
+ mnLastVisibleRow(-1),
+ mpVerticalScrollBar(rpVerticalScrollBar)
+{
+}
+
+void PresenterSlideSorter::Layout::Update (
+ const geometry::RealRectangle2D& rBoundingBox,
+ const double nSlideAspectRatio)
+{
+ maBoundingBox = rBoundingBox;
+
+ mnHorizontalBorder = gnHorizontalBorder;
+ mnVerticalBorder = gnVerticalBorder;
+
+ const double nWidth (rBoundingBox.X2 - rBoundingBox.X1 - 2*mnHorizontalBorder);
+ const double nHeight (rBoundingBox.Y2 - rBoundingBox.Y1 - 2*mnVerticalBorder);
+ if (nWidth<=0 || nHeight<=0)
+ return;
+
+ double nPreviewWidth;
+
+ // Determine column count, preview width, and horizontal gap (borders
+ // are half the gap). Try to use the preferred values. Try more to
+ // stay in the valid intervals. This last constraint may be not
+ // fulfilled in some cases.
+ const double nElementWidth = nWidth / gnPreferredColumnCount;
+ if (nElementWidth < gnMinimalPreviewWidth + gnMinimalHorizontalPreviewGap)
+ {
+ // The preferred column count is too large.
+ // Can we use the preferred preview width?
+ if (nWidth - gnMinimalHorizontalPreviewGap >= gnPreferredPreviewWidth)
+ {
+ // Yes.
+ nPreviewWidth = gnPreferredPreviewWidth;
+ mnColumnCount = floor((nWidth+gnPreferredHorizontalPreviewGap)
+ / (nPreviewWidth+gnPreferredHorizontalPreviewGap));
+ mnHorizontalGap = round((nWidth - mnColumnCount*nPreviewWidth) / mnColumnCount);
+ }
+ else
+ {
+ // No. Set the column count to 1 and adapt preview width and
+ // gap.
+ mnColumnCount = 1;
+ mnHorizontalGap = floor(gnMinimalHorizontalPreviewGap);
+ if (nWidth - gnMinimalHorizontalPreviewGap >= gnPreferredPreviewWidth)
+ nPreviewWidth = nWidth - gnMinimalHorizontalPreviewGap;
+ else
+ nPreviewWidth = ::std::max(gnMinimalPreviewWidth, nWidth-mnHorizontalGap);
+ }
+ }
+ else if (nElementWidth > gnMaximalPreviewWidth + gnMaximalHorizontalPreviewGap)
+ {
+ // The preferred column count is too small.
+ nPreviewWidth = gnPreferredPreviewWidth;
+ mnColumnCount = floor((nWidth+gnPreferredHorizontalPreviewGap)
+ / (nPreviewWidth+gnPreferredHorizontalPreviewGap));
+ mnHorizontalGap = round((nWidth - mnColumnCount*nPreviewWidth) / mnColumnCount);
+ }
+ else
+ {
+ // The preferred column count is possible. Determine gap and
+ // preview width.
+ mnColumnCount = gnPreferredColumnCount;
+ if (nElementWidth - gnPreferredPreviewWidth < gnMinimalHorizontalPreviewGap)
+ {
+ // Use the minimal gap and adapt the preview width.
+ mnHorizontalGap = floor(gnMinimalHorizontalPreviewGap);
+ nPreviewWidth = (nWidth - mnColumnCount*mnHorizontalGap) / mnColumnCount;
+ }
+ else if (nElementWidth - gnPreferredPreviewWidth <= gnMaximalHorizontalPreviewGap)
+ {
+ // Use the maximal gap and adapt the preview width.
+ mnHorizontalGap = round(gnMaximalHorizontalPreviewGap);
+ nPreviewWidth = (nWidth - mnColumnCount*mnHorizontalGap) / mnColumnCount;
+ }
+ else
+ {
+ // Use the preferred preview width and adapt the gap.
+ nPreviewWidth = gnPreferredPreviewWidth;
+ mnHorizontalGap = round((nWidth - mnColumnCount*nPreviewWidth) / mnColumnCount);
+ }
+ }
+
+ // Now determine the row count, preview height, and vertical gap.
+ const double nPreviewHeight = nPreviewWidth / nSlideAspectRatio;
+ mnRowCount = ::std::max(
+ sal_Int32(1),
+ sal_Int32(ceil((nHeight+gnPreferredVerticalPreviewGap)
+ / (nPreviewHeight + gnPreferredVerticalPreviewGap))));
+ mnVerticalGap = round(gnPreferredVerticalPreviewGap);
+
+ maPreviewSize = geometry::IntegerSize2D(floor(nPreviewWidth), floor(nPreviewHeight));
+
+ // Reset the offset.
+ mnVerticalOffset = 0;
+ mnHorizontalOffset = round(-(nWidth
+ - mnColumnCount*maPreviewSize.Width
+ - (mnColumnCount-1)*mnHorizontalGap)
+ / 2);
+}
+
+void PresenterSlideSorter::Layout::SetupVisibleArea()
+{
+ geometry::RealPoint2D aPoint (GetLocalPosition(
+ geometry::RealPoint2D(maBoundingBox.X1, maBoundingBox.Y1)));
+ mnFirstVisibleColumn = 0;
+ mnFirstVisibleRow = ::std::max(sal_Int32(0), GetRow(aPoint));
+
+ aPoint = GetLocalPosition(geometry::RealPoint2D( maBoundingBox.X2, maBoundingBox.Y2));
+ mnLastVisibleColumn = mnColumnCount - 1;
+ mnLastVisibleRow = GetRow(aPoint, true);
+}
+
+bool PresenterSlideSorter::Layout::IsScrollBarNeeded (const sal_Int32 nSlideCount)
+{
+ geometry::RealPoint2D aBottomRight = GetPoint(
+ mnColumnCount * (GetRow(nSlideCount)+1) - 1, +1, +1);
+ return aBottomRight.X > maBoundingBox.X2-maBoundingBox.X1
+ || aBottomRight.Y > maBoundingBox.Y2-maBoundingBox.Y1;
+}
+
+geometry::RealPoint2D PresenterSlideSorter::Layout::GetLocalPosition(
+ const geometry::RealPoint2D& rWindowPoint) const
+{
+ if(AllSettings::GetLayoutRTL())
+ {
+ return css::geometry::RealPoint2D(
+ -rWindowPoint.X + maBoundingBox.X2 + mnHorizontalOffset,
+ rWindowPoint.Y - maBoundingBox.Y1 + mnVerticalOffset);
+ }
+ else
+ {
+ return css::geometry::RealPoint2D(
+ rWindowPoint.X - maBoundingBox.X1 + mnHorizontalOffset,
+ rWindowPoint.Y - maBoundingBox.Y1 + mnVerticalOffset);
+ }
+}
+
+geometry::RealPoint2D PresenterSlideSorter::Layout::GetWindowPosition(
+ const geometry::RealPoint2D& rLocalPoint) const
+{
+ if(AllSettings::GetLayoutRTL())
+ {
+ return css::geometry::RealPoint2D(
+ -rLocalPoint.X + mnHorizontalOffset + maBoundingBox.X2,
+ rLocalPoint.Y - mnVerticalOffset + maBoundingBox.Y1);
+ }
+ else
+ {
+ return css::geometry::RealPoint2D(
+ rLocalPoint.X - mnHorizontalOffset + maBoundingBox.X1,
+ rLocalPoint.Y - mnVerticalOffset + maBoundingBox.Y1);
+ }
+}
+
+sal_Int32 PresenterSlideSorter::Layout::GetColumn (
+ const css::geometry::RealPoint2D& rLocalPoint) const
+{
+ const sal_Int32 nColumn(floor(
+ (rLocalPoint.X + mnHorizontalGap/2.0) / (maPreviewSize.Width+mnHorizontalGap)));
+ if (nColumn>=mnFirstVisibleColumn && nColumn<=mnLastVisibleColumn)
+ {
+ return nColumn;
+ }
+ else
+ return -1;
+}
+
+sal_Int32 PresenterSlideSorter::Layout::GetRow (
+ const css::geometry::RealPoint2D& rLocalPoint,
+ const bool bReturnInvalidValue) const
+{
+ const sal_Int32 nRow (floor(
+ (rLocalPoint.Y + mnVerticalGap/2.0) / (maPreviewSize.Height+mnVerticalGap)));
+ if (bReturnInvalidValue
+ || (nRow>=mnFirstVisibleRow && nRow<=mnLastVisibleRow))
+ {
+ return nRow;
+ }
+ else
+ return -1;
+}
+
+sal_Int32 PresenterSlideSorter::Layout::GetSlideIndexForPosition (
+ const css::geometry::RealPoint2D& rWindowPoint) const
+{
+ if ( ! PresenterGeometryHelper::IsInside(maBoundingBox, rWindowPoint))
+ return -1;
+
+ const css::geometry::RealPoint2D aLocalPosition (GetLocalPosition(rWindowPoint));
+ const sal_Int32 nColumn (GetColumn(aLocalPosition));
+ const sal_Int32 nRow (GetRow(aLocalPosition));
+
+ if (nColumn < 0 || nRow < 0)
+ return -1;
+ else
+ {
+ sal_Int32 nIndex (GetIndex(nRow, nColumn));
+ if (nIndex >= mnSlideCount)
+ return -1;
+ else
+ return nIndex;
+ }
+}
+
+geometry::RealPoint2D PresenterSlideSorter::Layout::GetPoint (
+ const sal_Int32 nSlideIndex,
+ const sal_Int32 nRelativeHorizontalPosition,
+ const sal_Int32 nRelativeVerticalPosition) const
+{
+ sal_Int32 nColumn (GetColumn(nSlideIndex));
+ sal_Int32 nRow (GetRow(nSlideIndex));
+
+ geometry::RealPoint2D aPosition (
+ mnHorizontalBorder + nColumn*(maPreviewSize.Width+mnHorizontalGap),
+ mnVerticalBorder + nRow*(maPreviewSize.Height+mnVerticalGap));
+
+ if (nRelativeHorizontalPosition >= 0)
+ {
+ if (nRelativeHorizontalPosition > 0)
+ aPosition.X += maPreviewSize.Width;
+ else
+ aPosition.X += maPreviewSize.Width / 2.0;
+ }
+ if (nRelativeVerticalPosition >= 0)
+ {
+ if (nRelativeVerticalPosition > 0)
+ aPosition.Y += maPreviewSize.Height;
+ else
+ aPosition.Y += maPreviewSize.Height / 2.0;
+ }
+
+ return aPosition;
+}
+
+awt::Rectangle PresenterSlideSorter::Layout::GetBoundingBox (const sal_Int32 nSlideIndex) const
+{
+ bool isRTL = AllSettings::GetLayoutRTL();
+ const geometry::RealPoint2D aWindowPosition(GetWindowPosition(GetPoint(nSlideIndex, isRTL?1:-1, -1)));
+ return PresenterGeometryHelper::ConvertRectangle(
+ geometry::RealRectangle2D(
+ aWindowPosition.X,
+ aWindowPosition.Y,
+ aWindowPosition.X + maPreviewSize.Width,
+ aWindowPosition.Y + maPreviewSize.Height));
+}
+
+void PresenterSlideSorter::Layout::ForAllVisibleSlides(
+ const ::std::function<void (sal_Int32)>& rAction)
+{
+ for (sal_Int32 nRow=mnFirstVisibleRow; nRow<=mnLastVisibleRow; ++nRow)
+ {
+ for (sal_Int32 nColumn=mnFirstVisibleColumn; nColumn<=mnLastVisibleColumn; ++nColumn)
+ {
+ const sal_Int32 nSlideIndex (GetIndex(nRow, nColumn));
+ if (nSlideIndex >= mnSlideCount)
+ return;
+ rAction(nSlideIndex);
+ }
+ }
+}
+
+sal_Int32 PresenterSlideSorter::Layout::GetFirstVisibleSlideIndex() const
+{
+ return GetIndex(mnFirstVisibleRow, mnFirstVisibleColumn);
+}
+
+sal_Int32 PresenterSlideSorter::Layout::GetLastVisibleSlideIndex() const
+{
+ return ::std::min(
+ GetIndex(mnLastVisibleRow, mnLastVisibleColumn),
+ mnSlideCount);
+}
+
+bool PresenterSlideSorter::Layout::SetHorizontalOffset (const double nOffset)
+{
+ if (mnHorizontalOffset != nOffset)
+ {
+ mnHorizontalOffset = round(nOffset);
+ SetupVisibleArea();
+ UpdateScrollBars();
+ return true;
+ }
+ else
+ return false;
+}
+
+bool PresenterSlideSorter::Layout::SetVerticalOffset (const double nOffset)
+{
+ if (mnVerticalOffset != nOffset)
+ {
+ mnVerticalOffset = round(nOffset);
+ SetupVisibleArea();
+ UpdateScrollBars();
+ return true;
+ }
+ else
+ return false;
+}
+
+void PresenterSlideSorter::Layout::UpdateScrollBars()
+{
+ sal_Int32 nTotalRowCount = sal_Int32(ceil(double(mnSlideCount) / double(mnColumnCount)));
+
+ if (mpVerticalScrollBar)
+ {
+ mpVerticalScrollBar->SetTotalSize(
+ nTotalRowCount * maPreviewSize.Height
+ + (nTotalRowCount-1) * mnVerticalGap
+ + 2*mnVerticalGap);
+ mpVerticalScrollBar->SetThumbPosition(mnVerticalOffset, false);
+ mpVerticalScrollBar->SetThumbSize(maBoundingBox.Y2 - maBoundingBox.Y1 + 1);
+ mpVerticalScrollBar->SetLineHeight(maPreviewSize.Height);
+ }
+
+ // No place yet for the vertical scroll bar.
+}
+
+sal_Int32 PresenterSlideSorter::Layout::GetIndex (
+ const sal_Int32 nRow,
+ const sal_Int32 nColumn) const
+{
+ return nRow * mnColumnCount + nColumn;
+}
+
+sal_Int32 PresenterSlideSorter::Layout::GetRow (const sal_Int32 nSlideIndex) const
+{
+ return nSlideIndex / mnColumnCount;
+}
+
+sal_Int32 PresenterSlideSorter::Layout::GetColumn (const sal_Int32 nSlideIndex) const
+{
+ return nSlideIndex % mnColumnCount;
+}
+
+//===== PresenterSlideSorter::MouseOverManager ================================
+
+PresenterSlideSorter::MouseOverManager::MouseOverManager (
+ const Reference<container::XIndexAccess>& rxSlides,
+ const std::shared_ptr<PresenterTheme>& rpTheme,
+ const Reference<awt::XWindow>& rxInvalidateTarget,
+ const std::shared_ptr<PresenterPaintManager>& rpPaintManager)
+ : mxSlides(rxSlides),
+ mnSlideIndex(-1),
+ mxInvalidateTarget(rxInvalidateTarget),
+ mpPaintManager(rpPaintManager)
+{
+ if (rpTheme != nullptr)
+ {
+ std::shared_ptr<PresenterBitmapContainer> pBitmaps (rpTheme->GetBitmapContainer());
+ if (pBitmaps != nullptr)
+ {
+ mpLeftLabelBitmap = pBitmaps->GetBitmap("LabelLeft");
+ mpCenterLabelBitmap = pBitmaps->GetBitmap("LabelCenter");
+ mpRightLabelBitmap = pBitmaps->GetBitmap("LabelRight");
+ }
+
+ mpFont = rpTheme->GetFont("SlideSorterLabelFont");
+ }
+}
+
+void PresenterSlideSorter::MouseOverManager::Paint (
+ const sal_Int32 nSlideIndex,
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const Reference<rendering::XPolyPolygon2D>& rxClip)
+{
+ if (nSlideIndex != mnSlideIndex)
+ return;
+
+ if (mxCanvas != rxCanvas)
+ SetCanvas(rxCanvas);
+ if (rxCanvas == nullptr)
+ return;
+
+ if ( ! mxBitmap.is())
+ mxBitmap = CreateBitmap(msText, maSlideBoundingBox.Width);
+ if (!mxBitmap.is())
+ return;
+
+ geometry::IntegerSize2D aSize (mxBitmap->getSize());
+ const double nXOffset (maSlideBoundingBox.X
+ + (maSlideBoundingBox.Width - aSize.Width) / 2.0);
+ const double nYOffset (maSlideBoundingBox.Y
+ + (maSlideBoundingBox.Height - aSize.Height) / 2.0);
+ rxCanvas->drawBitmap(
+ mxBitmap,
+ rendering::ViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ rxClip),
+ rendering::RenderState(
+ geometry::AffineMatrix2D(1,0,nXOffset, 0,1,nYOffset),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE));
+}
+
+void PresenterSlideSorter::MouseOverManager::SetCanvas (
+ const Reference<rendering::XCanvas>& rxCanvas)
+{
+ mxCanvas = rxCanvas;
+ if (mpFont)
+ mpFont->PrepareFont(mxCanvas);
+}
+
+void PresenterSlideSorter::MouseOverManager::SetSlide (
+ const sal_Int32 nSlideIndex,
+ const awt::Rectangle& rBox)
+{
+ if (mnSlideIndex == nSlideIndex)
+ return;
+
+ mnSlideIndex = -1;
+ Invalidate();
+
+ maSlideBoundingBox = rBox;
+ mnSlideIndex = nSlideIndex;
+
+ if (nSlideIndex >= 0)
+ {
+ if (mxSlides)
+ {
+ msText.clear();
+
+ Reference<beans::XPropertySet> xSlideProperties(mxSlides->getByIndex(nSlideIndex), UNO_QUERY);
+ if (xSlideProperties.is())
+ xSlideProperties->getPropertyValue("LinkDisplayName") >>= msText;
+
+ if (msText.isEmpty())
+ msText = "Slide " + OUString::number(nSlideIndex + 1);
+ }
+ }
+ else
+ {
+ msText.clear();
+ }
+ mxBitmap = nullptr;
+
+ Invalidate();
+}
+
+Reference<rendering::XBitmap> PresenterSlideSorter::MouseOverManager::CreateBitmap (
+ const OUString& rsText,
+ const sal_Int32 nMaximalWidth) const
+{
+ if ( ! mxCanvas.is())
+ return nullptr;
+
+ if (!mpFont || !mpFont->mxFont.is())
+ return nullptr;
+
+ // Long text has to be shortened.
+ const OUString sText (GetFittingText(rsText, nMaximalWidth
+ - 2*gnHorizontalLabelBorder
+ - 2*gnHorizontalLabelPadding));
+
+ // Determine the size of the label. Its height is defined by the
+ // bitmaps that are used to paints its background. The width is defined
+ // by the text.
+ geometry::IntegerSize2D aLabelSize (CalculateLabelSize(sText));
+
+ // Create a new bitmap that will contain the complete label.
+ Reference<rendering::XBitmap> xBitmap (
+ mxCanvas->getDevice()->createCompatibleAlphaBitmap(aLabelSize));
+
+ if ( ! xBitmap.is())
+ return nullptr;
+
+ Reference<rendering::XBitmapCanvas> xBitmapCanvas (xBitmap, UNO_QUERY);
+ if ( ! xBitmapCanvas.is())
+ return nullptr;
+
+ // Paint the background.
+ PaintButtonBackground(xBitmapCanvas, aLabelSize);
+
+ // Paint the text.
+ if (!sText.isEmpty())
+ {
+
+ const rendering::StringContext aContext (sText, 0, sText.getLength());
+ const Reference<rendering::XTextLayout> xLayout (mpFont->mxFont->createTextLayout(
+ aContext, rendering::TextDirection::WEAK_LEFT_TO_RIGHT,0));
+ const geometry::RealRectangle2D aTextBBox (xLayout->queryTextBounds());
+
+ const double nXOffset = (aLabelSize.Width - aTextBBox.X2 + aTextBBox.X1) / 2;
+ const double nYOffset = aLabelSize.Height
+ - (aLabelSize.Height - aTextBBox.Y2 + aTextBBox.Y1)/2 - aTextBBox.Y2;
+
+ const rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr);
+
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(1,0,nXOffset, 0,1,nYOffset),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
+
+ xBitmapCanvas->drawTextLayout (
+ xLayout,
+ aViewState,
+ aRenderState);
+ }
+
+ return xBitmap;
+}
+
+OUString PresenterSlideSorter::MouseOverManager::GetFittingText (
+ const OUString& rsText,
+ const double nMaximalWidth) const
+{
+ const double nTextWidth (
+ PresenterCanvasHelper::GetTextSize(mpFont->mxFont, rsText).Width);
+ if (nTextWidth > nMaximalWidth)
+ {
+ // Text is too wide. Shorten it by removing characters from the end
+ // and replacing them by ellipses.
+
+ // Guess a start value of the final string length.
+ double nBestWidth (0);
+ OUString sBestCandidate;
+ sal_Int32 nLength (round(rsText.getLength() * nMaximalWidth / nTextWidth));
+ static const OUStringLiteral sEllipses (u"...");
+ while (true)
+ {
+ const OUString sCandidate (rsText.subView(0,nLength) + sEllipses);
+ const double nWidth (
+ PresenterCanvasHelper::GetTextSize(mpFont->mxFont, sCandidate).Width);
+ if (nWidth > nMaximalWidth)
+ {
+ // Candidate still too wide, shorten it.
+ nLength -= 1;
+ if (nLength <= 0)
+ break;
+ }
+ else if (nWidth < nMaximalWidth)
+ {
+ // Candidate short enough.
+ if (nWidth > nBestWidth)
+ {
+ // Best length so far.
+ sBestCandidate = sCandidate;
+ nBestWidth = nWidth;
+ nLength += 1;
+ if (nLength >= rsText.getLength())
+ break;
+ }
+ else
+ break;
+ }
+ else
+ {
+ // Candidate is exactly as long as it may be. Use it
+ // without looking any further.
+ sBestCandidate = sCandidate;
+ break;
+ }
+ }
+ return sBestCandidate;
+ }
+ else
+ return rsText;
+}
+
+geometry::IntegerSize2D PresenterSlideSorter::MouseOverManager::CalculateLabelSize (
+ const OUString& rsText) const
+{
+ // Height is specified by the label bitmaps.
+ sal_Int32 nHeight (32);
+ if (mpCenterLabelBitmap)
+ {
+ Reference<rendering::XBitmap> xBitmap (mpCenterLabelBitmap->GetNormalBitmap());
+ if (xBitmap.is())
+ nHeight = xBitmap->getSize().Height;
+ }
+
+ // Width is specified by text width and maximal width.
+ const geometry::RealSize2D aTextSize (
+ PresenterCanvasHelper::GetTextSize(mpFont->mxFont, rsText));
+
+ const sal_Int32 nWidth (round(aTextSize.Width + 2*gnHorizontalLabelPadding));
+
+ return geometry::IntegerSize2D(nWidth, nHeight);
+}
+
+void PresenterSlideSorter::MouseOverManager::PaintButtonBackground (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const geometry::IntegerSize2D& rSize) const
+{
+ // Get the bitmaps for painting the label background.
+ Reference<rendering::XBitmap> xLeftLabelBitmap;
+ if (mpLeftLabelBitmap)
+ xLeftLabelBitmap = mpLeftLabelBitmap->GetNormalBitmap();
+
+ Reference<rendering::XBitmap> xCenterLabelBitmap;
+ if (mpCenterLabelBitmap)
+ xCenterLabelBitmap = mpCenterLabelBitmap->GetNormalBitmap();
+
+ Reference<rendering::XBitmap> xRightLabelBitmap;
+ if (mpRightLabelBitmap)
+ xRightLabelBitmap = mpRightLabelBitmap->GetNormalBitmap();
+
+ PresenterUIPainter::PaintHorizontalBitmapComposite (
+ rxCanvas,
+ awt::Rectangle(0,0, rSize.Width,rSize.Height),
+ awt::Rectangle(0,0, rSize.Width,rSize.Height),
+ xLeftLabelBitmap,
+ xCenterLabelBitmap,
+ xRightLabelBitmap);
+}
+
+void PresenterSlideSorter::MouseOverManager::Invalidate()
+{
+ if (mpPaintManager != nullptr)
+ mpPaintManager->Invalidate(mxInvalidateTarget, maSlideBoundingBox, true);
+}
+
+//===== PresenterSlideSorter::CurrentSlideFrameRenderer =======================
+
+PresenterSlideSorter::CurrentSlideFrameRenderer::CurrentSlideFrameRenderer (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas)
+ : mnTopFrameSize(0),
+ mnLeftFrameSize(0),
+ mnRightFrameSize(0),
+ mnBottomFrameSize(0)
+{
+ PresenterConfigurationAccess aConfiguration (
+ rxContext,
+ "/org.openoffice.Office.PresenterScreen/",
+ PresenterConfigurationAccess::READ_ONLY);
+ Reference<container::XHierarchicalNameAccess> xBitmaps (
+ aConfiguration.GetConfigurationNode(
+ "PresenterScreenSettings/SlideSorter/CurrentSlideBorderBitmaps"),
+ UNO_QUERY);
+ if ( ! xBitmaps.is())
+ return;
+
+ PresenterBitmapContainer aContainer (
+ "PresenterScreenSettings/SlideSorter/CurrentSlideBorderBitmaps",
+ std::shared_ptr<PresenterBitmapContainer>(),
+ rxContext,
+ rxCanvas);
+
+ mpTopLeft = aContainer.GetBitmap("TopLeft");
+ mpTop = aContainer.GetBitmap("Top");
+ mpTopRight = aContainer.GetBitmap("TopRight");
+ mpLeft = aContainer.GetBitmap("Left");
+ mpRight = aContainer.GetBitmap("Right");
+ mpBottomLeft = aContainer.GetBitmap("BottomLeft");
+ mpBottom = aContainer.GetBitmap("Bottom");
+ mpBottomRight = aContainer.GetBitmap("BottomRight");
+
+ // Determine size of frame.
+ if (mpTop)
+ mnTopFrameSize = mpTop->mnHeight;
+ if (mpLeft)
+ mnLeftFrameSize = mpLeft->mnWidth;
+ if (mpRight)
+ mnRightFrameSize = mpRight->mnWidth;
+ if (mpBottom)
+ mnBottomFrameSize = mpBottom->mnHeight;
+
+ if (mpTopLeft)
+ {
+ mnTopFrameSize = ::std::max(mnTopFrameSize, mpTopLeft->mnHeight);
+ mnLeftFrameSize = ::std::max(mnLeftFrameSize, mpTopLeft->mnWidth);
+ }
+ if (mpTopRight)
+ {
+ mnTopFrameSize = ::std::max(mnTopFrameSize, mpTopRight->mnHeight);
+ mnRightFrameSize = ::std::max(mnRightFrameSize, mpTopRight->mnWidth);
+ }
+ if (mpBottomLeft)
+ {
+ mnLeftFrameSize = ::std::max(mnLeftFrameSize, mpBottomLeft->mnWidth);
+ mnBottomFrameSize = ::std::max(mnBottomFrameSize, mpBottomLeft->mnHeight);
+ }
+ if (mpBottomRight)
+ {
+ mnRightFrameSize = ::std::max(mnRightFrameSize, mpBottomRight->mnWidth);
+ mnBottomFrameSize = ::std::max(mnBottomFrameSize, mpBottomRight->mnHeight);
+ }
+}
+
+void PresenterSlideSorter::CurrentSlideFrameRenderer::PaintCurrentSlideFrame (
+ const awt::Rectangle& rSlideBoundingBox,
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const geometry::RealRectangle2D& rClipBox)
+{
+ if ( ! rxCanvas.is())
+ return;
+
+ const Reference<rendering::XPolyPolygon2D> xClip (
+ PresenterGeometryHelper::CreatePolygon(rClipBox, rxCanvas->getDevice()));
+
+ if (mpTop)
+ {
+ PaintBitmapTiled(
+ mpTop->GetNormalBitmap(),
+ rxCanvas,
+ rClipBox,
+ rSlideBoundingBox.X,
+ rSlideBoundingBox.Y - mpTop->mnHeight,
+ rSlideBoundingBox.Width,
+ mpTop->mnHeight);
+ }
+ if (mpLeft)
+ {
+ PaintBitmapTiled(
+ mpLeft->GetNormalBitmap(),
+ rxCanvas,
+ rClipBox,
+ rSlideBoundingBox.X - mpLeft->mnWidth,
+ rSlideBoundingBox.Y,
+ mpLeft->mnWidth,
+ rSlideBoundingBox.Height);
+ }
+ if (mpRight)
+ {
+ PaintBitmapTiled(
+ mpRight->GetNormalBitmap(),
+ rxCanvas,
+ rClipBox,
+ rSlideBoundingBox.X + rSlideBoundingBox.Width,
+ rSlideBoundingBox.Y,
+ mpRight->mnWidth,
+ rSlideBoundingBox.Height);
+ }
+ if (mpBottom)
+ {
+ PaintBitmapTiled(
+ mpBottom->GetNormalBitmap(),
+ rxCanvas,
+ rClipBox,
+ rSlideBoundingBox.X,
+ rSlideBoundingBox.Y + rSlideBoundingBox.Height,
+ rSlideBoundingBox.Width,
+ mpBottom->mnHeight);
+ }
+ if (mpTopLeft)
+ {
+ PaintBitmapOnce(
+ mpTopLeft->GetNormalBitmap(),
+ rxCanvas,
+ xClip,
+ rSlideBoundingBox.X - mpTopLeft->mnWidth,
+ rSlideBoundingBox.Y - mpTopLeft->mnHeight);
+ }
+ if (mpTopRight)
+ {
+ PaintBitmapOnce(
+ mpTopRight->GetNormalBitmap(),
+ rxCanvas,
+ xClip,
+ rSlideBoundingBox.X + rSlideBoundingBox.Width,
+ rSlideBoundingBox.Y - mpTopLeft->mnHeight);
+ }
+ if (mpBottomLeft)
+ {
+ PaintBitmapOnce(
+ mpBottomLeft->GetNormalBitmap(),
+ rxCanvas,
+ xClip,
+ rSlideBoundingBox.X - mpBottomLeft->mnWidth,
+ rSlideBoundingBox.Y + rSlideBoundingBox.Height);
+ }
+ if (mpBottomRight)
+ {
+ PaintBitmapOnce(
+ mpBottomRight->GetNormalBitmap(),
+ rxCanvas,
+ xClip,
+ rSlideBoundingBox.X + rSlideBoundingBox.Width,
+ rSlideBoundingBox.Y + rSlideBoundingBox.Height);
+ }
+}
+
+awt::Rectangle PresenterSlideSorter::CurrentSlideFrameRenderer::GetBoundingBox (
+ const awt::Rectangle& rSlideBoundingBox)
+{
+ return awt::Rectangle(
+ rSlideBoundingBox.X - mnLeftFrameSize,
+ rSlideBoundingBox.Y - mnTopFrameSize,
+ rSlideBoundingBox.Width + mnLeftFrameSize + mnRightFrameSize,
+ rSlideBoundingBox.Height + mnTopFrameSize + mnBottomFrameSize);
+}
+
+void PresenterSlideSorter::CurrentSlideFrameRenderer::PaintBitmapOnce(
+ const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const Reference<rendering::XPolyPolygon2D>& rxClip,
+ const double nX,
+ const double nY)
+{
+ OSL_ASSERT(rxCanvas.is());
+ if ( ! rxBitmap.is())
+ return;
+
+ const rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ rxClip);
+
+ const rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(
+ 1, 0, nX,
+ 0, 1, nY),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ rxCanvas->drawBitmap(
+ rxBitmap,
+ aViewState,
+ aRenderState);
+}
+
+void PresenterSlideSorter::CurrentSlideFrameRenderer::PaintBitmapTiled(
+ const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const geometry::RealRectangle2D& rClipBox,
+ const double nX0,
+ const double nY0,
+ const double nWidth,
+ const double nHeight)
+{
+ OSL_ASSERT(rxCanvas.is());
+ if ( ! rxBitmap.is())
+ return;
+
+ geometry::IntegerSize2D aSize (rxBitmap->getSize());
+
+ const rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ PresenterGeometryHelper::CreatePolygon(
+ PresenterGeometryHelper::Intersection(
+ rClipBox,
+ geometry::RealRectangle2D(nX0,nY0,nX0+nWidth,nY0+nHeight)),
+ rxCanvas->getDevice()));
+
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(
+ 1, 0, nX0,
+ 0, 1, nY0),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ const double nX1 = nX0 + nWidth;
+ const double nY1 = nY0 + nHeight;
+ for (double nY=nY0; nY<nY1; nY+=aSize.Height)
+ for (double nX=nX0; nX<nX1; nX+=aSize.Width)
+ {
+ aRenderState.AffineTransform.m02 = nX;
+ aRenderState.AffineTransform.m12 = nY;
+ rxCanvas->drawBitmap(
+ rxBitmap,
+ aViewState,
+ aRenderState);
+ }
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterSlideSorter.hxx b/sdext/source/presenter/PresenterSlideSorter.hxx
new file mode 100644
index 000000000..807bc4399
--- /dev/null
+++ b/sdext/source/presenter/PresenterSlideSorter.hxx
@@ -0,0 +1,189 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSLIDESORTER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSLIDESORTER_HXX
+
+#include <memory>
+#include "PresenterController.hxx"
+#include "PresenterPaneContainer.hxx"
+#include "PresenterViewFactory.hxx"
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/awt/XPaintListener.hpp>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/drawing/XSlidePreviewCache.hpp>
+#include <com/sun/star/drawing/framework/XView.hpp>
+#include <com/sun/star/drawing/framework/XResourceId.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/geometry/RealRectangle2D.hpp>
+#include <com/sun/star/rendering/XPolyPolygon2D.hpp>
+
+namespace sdext::presenter {
+
+class PresenterButton;
+class PresenterScrollBar;
+
+typedef cppu::WeakComponentImplHelper<
+ css::drawing::framework::XView,
+ css::awt::XWindowListener,
+ css::awt::XPaintListener,
+ css::beans::XPropertyChangeListener,
+ css::drawing::XSlidePreviewCacheListener,
+ css::awt::XMouseListener,
+ css::awt::XMouseMotionListener,
+ css::drawing::XDrawView
+ > PresenterSlideSorterInterfaceBase;
+
+/** A simple slide sorter for the presenter screen. It uses a preview cache
+ to create the slide previews. Painting is done via a canvas.
+*/
+class PresenterSlideSorter
+ : private ::cppu::BaseMutex,
+ public PresenterSlideSorterInterfaceBase,
+ public CachablePresenterView
+{
+public:
+ PresenterSlideSorter (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterSlideSorter() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ // lang::XEventListener
+
+ virtual void SAL_CALL
+ disposing (const css::lang::EventObject& rEventObject) override;
+
+ // XWindowListener
+
+ virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override;
+
+ // XPaintListener
+
+ virtual void SAL_CALL windowPaint (const css::awt::PaintEvent& rEvent) override;
+
+ // XMouseListener
+
+ virtual void SAL_CALL mousePressed (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseReleased (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseEntered (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseExited (const css::awt::MouseEvent& rEvent) override;
+
+ // XMouseMotionListener
+
+ virtual void SAL_CALL mouseMoved (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseDragged (const css::awt::MouseEvent& rEvent) override;
+
+ // XResourceId
+
+ virtual css::uno::Reference<css::drawing::framework::XResourceId> SAL_CALL getResourceId() override;
+
+ virtual sal_Bool SAL_CALL isAnchorOnly() override;
+
+ // XPropertyChangeListener
+
+ virtual void SAL_CALL propertyChange (
+ const css::beans::PropertyChangeEvent& rEvent) override;
+
+ // XSlidePreviewCacheListener
+
+ virtual void SAL_CALL notifyPreviewCreation (
+ sal_Int32 nSlideIndex) override;
+
+ // XDrawView
+
+ virtual void SAL_CALL setCurrentPage (
+ const css::uno::Reference<css::drawing::XDrawPage>& rxSlide) override;
+
+ virtual css::uno::Reference<css::drawing::XDrawPage> SAL_CALL getCurrentPage() override;
+
+private:
+ css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+ css::uno::Reference<css::drawing::framework::XResourceId> mxViewId;
+ css::uno::Reference<css::drawing::framework::XPane> mxPane;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+ css::uno::Reference<css::awt::XWindow> mxWindow;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ css::uno::Reference<css::presentation::XSlideShowController> mxSlideShowController;
+ css::uno::Reference<css::drawing::XSlidePreviewCache> mxPreviewCache;
+ bool mbIsLayoutPending;
+ class Layout;
+ std::shared_ptr<Layout> mpLayout;
+ ::rtl::Reference<PresenterScrollBar> mpVerticalScrollBar;
+ ::rtl::Reference<PresenterButton> mpCloseButton;
+ class MouseOverManager;
+ std::unique_ptr<MouseOverManager> mpMouseOverManager;
+ sal_Int32 mnSlideIndexMousePressed;
+ sal_Int32 mnCurrentSlideIndex;
+ sal_Int32 mnSeparatorY;
+ css::util::Color maSeparatorColor;
+ css::awt::Rectangle maCurrentSlideFrameBoundingBox;
+ class CurrentSlideFrameRenderer;
+ std::shared_ptr<CurrentSlideFrameRenderer> mpCurrentSlideFrameRenderer;
+ css::uno::Reference<css::rendering::XPolyPolygon2D> mxPreviewFrame;
+
+ void UpdateLayout();
+ css::geometry::RealRectangle2D PlaceScrollBars (
+ const css::geometry::RealRectangle2D& rUpperBox);
+ void PlaceCloseButton (
+ const PresenterPaneContainer::SharedPaneDescriptor& rpPane,
+ const css::awt::Rectangle& rCenterBox,
+ const sal_Int32 nLeftFrameWidth);
+ void ClearBackground (
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRedrawArea);
+ double GetSlideAspectRatio() const;
+ css::uno::Reference<css::rendering::XBitmap> GetPreview (const sal_Int32 nSlideIndex);
+ void PaintPreview (
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rUpdateBox,
+ const sal_Int32 nSlideIndex);
+ void Paint (const css::awt::Rectangle& rUpdateBox);
+ void SetHorizontalOffset (const double nXOffset);
+ void SetVerticalOffset (const double nYOffset);
+ void GotoSlide (const sal_Int32 nSlideIndex);
+ bool ProvideCanvas();
+
+ /** @throws css::lang::DisposedException when the object has already been
+ disposed.
+ */
+ void ThrowIfDisposed();
+};
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterSprite.cxx b/sdext/source/presenter/PresenterSprite.cxx
new file mode 100644
index 000000000..0f7c8f829
--- /dev/null
+++ b/sdext/source/presenter/PresenterSprite.cxx
@@ -0,0 +1,163 @@
+/* -*- 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 "PresenterSprite.hxx"
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/RenderState.hpp>
+#include <com/sun/star/rendering/ViewState.hpp>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+
+namespace sdext::presenter {
+
+PresenterSprite::PresenterSprite()
+ : maSize(0,0),
+ maLocation(0,0),
+ mbIsVisible(false)
+{
+}
+
+PresenterSprite::~PresenterSprite()
+{
+ if (mxSprite.is())
+ {
+ mxSprite->hide();
+ Reference<lang::XComponent> xComponent (mxSprite, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ mxSprite = nullptr;
+ }
+}
+
+void PresenterSprite::SetFactory (
+ const css::uno::Reference<css::rendering::XSpriteCanvas>& rxSpriteFactory)
+{
+ if (mxSpriteFactory != rxSpriteFactory)
+ {
+ DisposeSprite();
+ mxSpriteFactory = rxSpriteFactory;
+ if (mbIsVisible)
+ ProvideSprite();
+ }
+}
+
+css::uno::Reference<css::rendering::XCanvas> PresenterSprite::GetCanvas()
+{
+ ProvideSprite();
+ if (mxSprite.is())
+ return mxSprite->getContentCanvas();
+ else
+ return nullptr;
+}
+
+void PresenterSprite::Show()
+{
+ mbIsVisible = true;
+ if (mxSprite.is())
+ mxSprite->show();
+ else
+ ProvideSprite();
+}
+
+void PresenterSprite::Hide()
+{
+ mbIsVisible = false;
+ if (mxSprite.is())
+ mxSprite->hide();
+}
+
+void PresenterSprite::Resize (const css::geometry::RealSize2D& rSize)
+{
+ maSize = rSize;
+ if (mxSprite.is())
+ DisposeSprite();
+ if (mbIsVisible)
+ ProvideSprite();
+}
+
+void PresenterSprite::MoveTo (const css::geometry::RealPoint2D& rLocation)
+{
+ maLocation = rLocation;
+ if (mxSprite.is())
+ mxSprite->move(
+ maLocation,
+ rendering::ViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr),
+ rendering::RenderState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr,
+ uno::Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE)
+ );
+}
+
+void PresenterSprite::Update()
+{
+ if (mxSpriteFactory.is())
+ mxSpriteFactory->updateScreen(false);
+}
+
+void PresenterSprite::ProvideSprite()
+{
+ if ( !(! mxSprite.is()
+ && mxSpriteFactory.is()
+ && maSize.Width>0
+ && maSize.Height>0))
+ return;
+
+ mxSprite = mxSpriteFactory->createCustomSprite(maSize);
+ if (!mxSprite.is())
+ return;
+
+ mxSprite->move(maLocation,
+ rendering::ViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr),
+ rendering::RenderState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr,
+ uno::Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE)
+ );
+ mxSprite->setAlpha(1.0);
+ mxSprite->setPriority(0);
+ if (mbIsVisible)
+ mxSprite->show();
+}
+
+void PresenterSprite::DisposeSprite()
+{
+ if (mxSprite.is())
+ {
+ mxSprite->hide();
+ Reference<lang::XComponent> xComponent (mxSprite, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ mxSprite = nullptr;
+ }
+}
+
+} //end of namespace sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterSprite.hxx b/sdext/source/presenter/PresenterSprite.hxx
new file mode 100644
index 000000000..b550ec0a8
--- /dev/null
+++ b/sdext/source/presenter/PresenterSprite.hxx
@@ -0,0 +1,73 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSPRITE_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERSPRITE_HXX
+
+#include <com/sun/star/rendering/XCustomSprite.hpp>
+#include <com/sun/star/rendering/XSpriteCanvas.hpp>
+
+namespace sdext::presenter
+{
+/** A wrapper around a css::rendering::XCustomSprite that allows
+ not only setting values like size, location, and transformation but also
+ provides read access to them.
+ It also handles the showing and hiding of a sprite. This includes not
+ to show the sprite when its size is not yet defined (results in a crash)
+ and hiding a sprite before disposing it (results in zombie sprites.)
+*/
+class PresenterSprite final
+{
+public:
+ PresenterSprite();
+ ~PresenterSprite();
+ PresenterSprite(const PresenterSprite&) = delete;
+ PresenterSprite& operator=(const PresenterSprite&) = delete;
+
+ /** The given sprite canvas is used as factory to create the sprite that
+ is wrapped by objects of this class.
+ It is also used to call updateScreen() at (wrapped by the Update() method).
+ */
+ void SetFactory(const css::uno::Reference<css::rendering::XSpriteCanvas>& rxSpriteFactory);
+
+ css::uno::Reference<css::rendering::XCanvas> GetCanvas();
+
+ void Show();
+ void Hide();
+
+ void Resize(const css::geometry::RealSize2D& rSize);
+ void MoveTo(const css::geometry::RealPoint2D& rLocation);
+
+ void Update();
+
+private:
+ css::uno::Reference<css::rendering::XSpriteCanvas> mxSpriteFactory;
+ css::uno::Reference<css::rendering::XCustomSprite> mxSprite;
+ css::geometry::RealSize2D maSize;
+ css::geometry::RealPoint2D maLocation;
+ bool mbIsVisible;
+
+ void ProvideSprite();
+ void DisposeSprite();
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterSpritePane.cxx b/sdext/source/presenter/PresenterSpritePane.cxx
new file mode 100644
index 000000000..452e633a2
--- /dev/null
+++ b/sdext/source/presenter/PresenterSpritePane.cxx
@@ -0,0 +1,172 @@
+/* -*- 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 "PresenterSpritePane.hxx"
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sdext::presenter {
+
+//===== PresenterSpritePane =========================================================
+
+PresenterSpritePane::PresenterSpritePane (const Reference<XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterPaneBase(rxContext, rpPresenterController),
+ mpSprite(std::make_shared<PresenterSprite>())
+{
+ Reference<lang::XMultiComponentFactory> xFactory (
+ mxComponentContext->getServiceManager(), UNO_SET_THROW);
+ mxPresenterHelper.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.comp.Draw.PresenterHelper",
+ mxComponentContext),
+ UNO_QUERY_THROW);
+}
+
+PresenterSpritePane::~PresenterSpritePane()
+{
+}
+
+void PresenterSpritePane::disposing()
+{
+ mpSprite->SetFactory(nullptr);
+ mxParentCanvas = nullptr;
+ PresenterPaneBase::disposing();
+}
+
+//----- XPane -----------------------------------------------------------------
+
+Reference<awt::XWindow> SAL_CALL PresenterSpritePane::getWindow()
+{
+ ThrowIfDisposed();
+ return mxContentWindow;
+}
+
+Reference<rendering::XCanvas> SAL_CALL PresenterSpritePane::getCanvas()
+{
+ ThrowIfDisposed();
+
+ if ( ! mxContentCanvas.is())
+ UpdateCanvases();
+
+ return mxContentCanvas;
+}
+
+//----- XWindowListener -------------------------------------------------------
+
+void SAL_CALL PresenterSpritePane::windowResized (const awt::WindowEvent& rEvent)
+{
+ PresenterPaneBase::windowResized(rEvent);
+
+ mpSprite->Resize(geometry::RealSize2D(rEvent.Width, rEvent.Height));
+ LayoutContextWindow();
+ UpdateCanvases();
+}
+
+void SAL_CALL PresenterSpritePane::windowMoved (const awt::WindowEvent& rEvent)
+{
+ PresenterPaneBase::windowMoved(rEvent);
+
+ awt::Rectangle aBox (
+ mxPresenterHelper->getWindowExtentsRelative(mxBorderWindow, mxParentWindow));
+ mpSprite->MoveTo(geometry::RealPoint2D(aBox.X, aBox.Y));
+ mpSprite->Update();
+}
+
+void SAL_CALL PresenterSpritePane::windowShown (const lang::EventObject& rEvent)
+{
+ PresenterPaneBase::windowShown(rEvent);
+
+ mpSprite->Show();
+ ToTop();
+
+ if (mxContentWindow.is())
+ {
+ LayoutContextWindow();
+ mxContentWindow->setVisible(true);
+ }
+}
+
+void SAL_CALL PresenterSpritePane::windowHidden (const lang::EventObject& rEvent)
+{
+ PresenterPaneBase::windowHidden(rEvent);
+
+ mpSprite->Hide();
+ if (mxContentWindow.is())
+ mxContentWindow->setVisible(false);
+}
+
+//----- XPaintListener --------------------------------------------------------
+
+void SAL_CALL PresenterSpritePane::windowPaint (const awt::PaintEvent&)
+{
+ ThrowIfDisposed();
+
+ /*
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxParentCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(sal_False);
+ */
+}
+
+
+void PresenterSpritePane::UpdateCanvases()
+{
+ Reference<XComponent> xContentCanvasComponent (mxContentCanvas, UNO_QUERY);
+ if (xContentCanvasComponent.is())
+ xContentCanvasComponent->dispose();
+
+ // The border canvas is the content canvas of the sprite.
+ mxBorderCanvas = mpSprite->GetCanvas();
+
+ // The content canvas is a wrapper of the border canvas.
+ if (mxBorderCanvas.is())
+ mxContentCanvas = mxPresenterHelper->createSharedCanvas(
+ mxParentCanvas,
+ mxParentWindow,
+ mxBorderCanvas,
+ mxBorderWindow,
+ mxContentWindow);
+
+ const awt::Rectangle aWindowBox (mxBorderWindow->getPosSize());
+ PaintBorder(awt::Rectangle(0,0,aWindowBox.Width,aWindowBox.Height));
+}
+
+void PresenterSpritePane::CreateCanvases (
+ const css::uno::Reference<css::rendering::XSpriteCanvas>& rxParentCanvas)
+{
+ OSL_ASSERT(!mxParentCanvas.is() || mxParentCanvas==rxParentCanvas);
+ mxParentCanvas = rxParentCanvas;
+
+ mpSprite->SetFactory(mxParentCanvas);
+ if (mxBorderWindow.is())
+ {
+ const awt::Rectangle aBorderBox (mxBorderWindow->getPosSize());
+ mpSprite->Resize(geometry::RealSize2D(aBorderBox.Width, aBorderBox.Height));
+ }
+
+ UpdateCanvases();
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterSpritePane.hxx b/sdext/source/presenter/PresenterSpritePane.hxx
new file mode 100644
index 000000000..1c2c923b9
--- /dev/null
+++ b/sdext/source/presenter/PresenterSpritePane.hxx
@@ -0,0 +1,79 @@
+/* -*- 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 "PresenterPaneBase.hxx"
+#include "PresenterSprite.hxx"
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XSpriteCanvas.hpp>
+#include <rtl/ref.hxx>
+#include <memory>
+
+namespace sdext::presenter
+{
+/** Use a sprite to display the contents and the border of a pane. Windows
+ are still used to define the locations and sizes of both the border and
+ the pane content. Note that every resize results in a disposed canvas.
+ Therefore call getCanvas in every repaint or at least after every resize.
+*/
+class PresenterSpritePane : public PresenterPaneBase
+{
+public:
+ PresenterSpritePane(const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterSpritePane() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ using PresenterPaneBase::disposing;
+
+ // XPane
+
+ virtual css::uno::Reference<css::awt::XWindow> SAL_CALL getWindow() override;
+
+ virtual css::uno::Reference<css::rendering::XCanvas> SAL_CALL getCanvas() override;
+
+ // XWindowListener
+
+ virtual void SAL_CALL windowResized(const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved(const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown(const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden(const css::lang::EventObject& rEvent) override;
+
+ // XPaintListener
+
+ virtual void SAL_CALL windowPaint(const css::awt::PaintEvent& rEvent) override;
+
+private:
+ css::uno::Reference<css::rendering::XSpriteCanvas> mxParentCanvas;
+ std::shared_ptr<PresenterSprite> mpSprite;
+
+ virtual void CreateCanvases(
+ const css::uno::Reference<css::rendering::XSpriteCanvas>& rxParentCanvas) override;
+ void UpdateCanvases();
+};
+
+} // end of namespace ::sd::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterTextView.cxx b/sdext/source/presenter/PresenterTextView.cxx
new file mode 100644
index 000000000..d83229b88
--- /dev/null
+++ b/sdext/source/presenter/PresenterTextView.cxx
@@ -0,0 +1,1192 @@
+/* -*- 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 "PresenterTextView.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterGeometryHelper.hxx"
+#include "PresenterTimer.hxx"
+
+#include <algorithm>
+#include <cmath>
+#include <numeric>
+
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/ScriptDirection.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/TextDirection.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <o3tl/safeint.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+const sal_Int64 CaretBlinkInterval = 500 * 1000 * 1000;
+
+//#define SHOW_CHARACTER_BOXES
+
+namespace {
+ sal_Int32 Signum (const sal_Int32 nValue)
+ {
+ if (nValue < 0)
+ return -1;
+ else if (nValue > 0)
+ return +1;
+ else
+ return 0;
+ }
+}
+
+namespace sdext::presenter {
+
+//===== PresenterTextView =====================================================
+
+PresenterTextView::PresenterTextView (
+ const Reference<XComponentContext>& rxContext,
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const ::std::function<void (const css::awt::Rectangle&)>& rInvalidator)
+ : mxCanvas(rxCanvas),
+ maLocation(0,0),
+ maSize(0,0),
+ mpCaret(std::make_shared<PresenterTextCaret>(
+ rxContext,
+ [this] (sal_Int32 const nParagraphIndex, sal_Int32 const nCharacterIndex)
+ { return this->GetCaretBounds(nParagraphIndex, nCharacterIndex); },
+ rInvalidator)),
+ mnLeftOffset(0),
+ mnTopOffset(0),
+ mbIsFormatPending(false)
+{
+ Reference<lang::XMultiComponentFactory> xFactory =
+ rxContext->getServiceManager();
+ if ( ! xFactory.is())
+ return;
+
+ // Create the break iterator that we use to break text into lines.
+ mxBreakIterator = i18n::BreakIterator::create(rxContext);
+
+ // Create the script type detector that is used to split paragraphs into
+ // portions of the same text direction.
+ mxScriptTypeDetector.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.i18n.ScriptTypeDetector",
+ rxContext),
+ UNO_QUERY_THROW);
+}
+
+void PresenterTextView::SetText (const Reference<text::XText>& rxText)
+{
+ maParagraphs.clear();
+
+ Reference<container::XEnumerationAccess> xParagraphAccess (rxText, UNO_QUERY);
+ if ( ! xParagraphAccess.is())
+ return;
+
+ Reference<container::XEnumeration> xParagraphs =
+ xParagraphAccess->createEnumeration();
+ if ( ! xParagraphs.is())
+ return;
+
+ if ( ! mpFont || ! mpFont->PrepareFont(mxCanvas))
+ return;
+
+ sal_Int32 nCharacterCount (0);
+ while (xParagraphs->hasMoreElements())
+ {
+ SharedPresenterTextParagraph pParagraph = std::make_shared<PresenterTextParagraph>(
+ maParagraphs.size(),
+ mxBreakIterator,
+ mxScriptTypeDetector,
+ Reference<text::XTextRange>(xParagraphs->nextElement(), UNO_QUERY),
+ mpCaret);
+ pParagraph->SetupCellArray(mpFont);
+ pParagraph->SetCharacterOffset(nCharacterCount);
+ nCharacterCount += pParagraph->GetCharacterCount();
+ maParagraphs.push_back(pParagraph);
+ }
+
+ if (mpCaret)
+ mpCaret->HideCaret();
+
+ RequestFormat();
+}
+
+void PresenterTextView::SetTextChangeBroadcaster (
+ const ::std::function<void ()>& rBroadcaster)
+{
+ maTextChangeBroadcaster = rBroadcaster;
+}
+
+void PresenterTextView::SetLocation (const css::geometry::RealPoint2D& rLocation)
+{
+ maLocation = rLocation;
+
+ for (auto& rxParagraph : maParagraphs)
+ {
+ rxParagraph->SetOrigin(
+ maLocation.X - mnLeftOffset,
+ maLocation.Y - mnTopOffset);
+ }
+}
+
+void PresenterTextView::SetSize (const css::geometry::RealSize2D& rSize)
+{
+ maSize = rSize;
+ RequestFormat();
+}
+
+double PresenterTextView::GetTotalTextHeight()
+{
+ if (mbIsFormatPending)
+ {
+ if ( ! mpFont->PrepareFont(mxCanvas))
+ return 0;
+ Format();
+ }
+
+ return std::accumulate(maParagraphs.begin(), maParagraphs.end(), double(0),
+ [](const double& nTotalHeight, const SharedPresenterTextParagraph& rxParagraph) {
+ return nTotalHeight + rxParagraph->GetTotalTextHeight();
+ });
+}
+
+void PresenterTextView::SetFont (const PresenterTheme::SharedFontDescriptor& rpFont)
+{
+ mpFont = rpFont;
+ RequestFormat();
+}
+
+void PresenterTextView::SetOffset(
+ const double nLeft,
+ const double nTop)
+{
+ mnLeftOffset = nLeft;
+ mnTopOffset = nTop;
+
+ // Trigger an update of the text origin stored at the individual paragraphs.
+ SetLocation(maLocation);
+}
+
+void PresenterTextView::MoveCaret (
+ const sal_Int32 nDistance,
+ const sal_Int16 nTextType)
+{
+ if ( ! mpCaret)
+ return;
+
+ // When the caret has not been visible yet then move it to the beginning
+ // of the text.
+ if (mpCaret->GetParagraphIndex() < 0)
+ {
+ mpCaret->SetPosition(0,0);
+ return;
+ }
+
+ sal_Int32 nParagraphIndex (mpCaret->GetParagraphIndex());
+ sal_Int32 nCharacterIndex (mpCaret->GetCharacterIndex());
+ switch (nTextType)
+ {
+ default:
+ case AccessibleTextType::CHARACTER:
+ nCharacterIndex += nDistance;
+ break;
+
+ case AccessibleTextType::WORD:
+ {
+ sal_Int32 nRemainingDistance (nDistance);
+ while (nRemainingDistance != 0)
+ {
+ SharedPresenterTextParagraph pParagraph (GetParagraph(nParagraphIndex));
+ if (pParagraph)
+ {
+ const sal_Int32 nDelta (Signum(nDistance));
+ nCharacterIndex = pParagraph->GetWordBoundary(nCharacterIndex, nDelta);
+ if (nCharacterIndex < 0)
+ {
+ // Go to previous or next paragraph.
+ nParagraphIndex += nDelta;
+ if (nParagraphIndex < 0)
+ {
+ nParagraphIndex = 0;
+ nCharacterIndex = 0;
+ nRemainingDistance = 0;
+ }
+ else if (o3tl::make_unsigned(nParagraphIndex) >= maParagraphs.size())
+ {
+ nParagraphIndex = maParagraphs.size()-1;
+ pParagraph = GetParagraph(nParagraphIndex);
+ if (pParagraph)
+ nCharacterIndex = pParagraph->GetCharacterCount();
+ nRemainingDistance = 0;
+ }
+ else
+ {
+ nRemainingDistance -= nDelta;
+
+ // Move caret one character to the end of
+ // the previous or the start of the next paragraph.
+ pParagraph = GetParagraph(nParagraphIndex);
+ if (pParagraph)
+ {
+ if (nDistance<0)
+ nCharacterIndex = pParagraph->GetCharacterCount();
+ else
+ nCharacterIndex = 0;
+ }
+ }
+ }
+ else
+ nRemainingDistance -= nDelta;
+ }
+ else
+ break;
+ }
+ break;
+ }
+ }
+
+ // Move the caret to the new position.
+ mpCaret->SetPosition(nParagraphIndex, nCharacterIndex);
+}
+
+void PresenterTextView::Paint (
+ const css::awt::Rectangle& rUpdateBox)
+{
+ if ( ! mxCanvas.is())
+ return;
+ if ( ! mpFont->PrepareFont(mxCanvas))
+ return;
+
+ if (mbIsFormatPending)
+ Format();
+
+ // Setup the clipping rectangle. Horizontally we make it a little
+ // larger to allow characters (and the caret) to stick out of their
+ // bounding boxes. This can happen on some characters (like the
+ // uppercase J) for typographical reasons.
+ const sal_Int32 nAdditionalLeftBorder (10);
+ const sal_Int32 nAdditionalRightBorder (5);
+ double nX (maLocation.X - mnLeftOffset);
+ double nY (maLocation.Y - mnTopOffset);
+ const sal_Int32 nClipLeft (::std::max(
+ PresenterGeometryHelper::Round(maLocation.X)-nAdditionalLeftBorder, rUpdateBox.X));
+ const sal_Int32 nClipTop (::std::max(
+ PresenterGeometryHelper::Round(maLocation.Y), rUpdateBox.Y));
+ const sal_Int32 nClipRight (::std::min(
+ PresenterGeometryHelper::Round(maLocation.X+maSize.Width)+nAdditionalRightBorder, rUpdateBox.X+rUpdateBox.Width));
+ const sal_Int32 nClipBottom (::std::min(
+ PresenterGeometryHelper::Round(maLocation.Y+maSize.Height), rUpdateBox.Y+rUpdateBox.Height));
+ if (nClipLeft>=nClipRight || nClipTop>=nClipBottom)
+ return;
+
+ const awt::Rectangle aClipBox(
+ nClipLeft,
+ nClipTop,
+ nClipRight - nClipLeft,
+ nClipBottom - nClipTop);
+ Reference<rendering::XPolyPolygon2D> xClipPolygon (
+ PresenterGeometryHelper::CreatePolygon(aClipBox, mxCanvas->getDevice()));
+
+ const rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ xClipPolygon);
+
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(1,0,nX, 0,1,nY),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
+
+ for (const auto& rxParagraph : maParagraphs)
+ {
+ rxParagraph->Paint(
+ mxCanvas,
+ maSize,
+ mpFont,
+ aViewState,
+ aRenderState,
+ mnTopOffset,
+ nClipTop,
+ nClipBottom);
+ }
+
+ aRenderState.AffineTransform.m02 = 0;
+ aRenderState.AffineTransform.m12 = 0;
+
+#ifdef SHOW_CHARACTER_BOXES
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, 0x00808080);
+ for (sal_Int32 nParagraphIndex(0), nParagraphCount(GetParagraphCount());
+ nParagraphIndex<nParagraphCount;
+ ++nParagraphIndex)
+ {
+ const SharedPresenterTextParagraph pParagraph (GetParagraph(nParagraphIndex));
+ if ( ! pParagraph)
+ continue;
+ for (sal_Int32 nCharacterIndex(0),nCharacterCount(pParagraph->GetCharacterCount());
+ nCharacterIndex<nCharacterCount; ++nCharacterIndex)
+ {
+ const awt::Rectangle aBox (pParagraph->GetCharacterBounds(nCharacterIndex, false));
+ mxCanvas->drawPolyPolygon (
+ PresenterGeometryHelper::CreatePolygon(
+ aBox,
+ mxCanvas->getDevice()),
+ aViewState,
+ aRenderState);
+ }
+ }
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
+#endif
+
+ if (mpCaret && mpCaret->IsVisible())
+ {
+ mxCanvas->fillPolyPolygon (
+ PresenterGeometryHelper::CreatePolygon(
+ mpCaret->GetBounds(),
+ mxCanvas->getDevice()),
+ aViewState,
+ aRenderState);
+ }
+}
+
+const SharedPresenterTextCaret& PresenterTextView::GetCaret() const
+{
+ return mpCaret;
+}
+
+awt::Rectangle PresenterTextView::GetCaretBounds (
+ sal_Int32 nParagraphIndex,
+ const sal_Int32 nCharacterIndex) const
+{
+ SharedPresenterTextParagraph pParagraph (GetParagraph(nParagraphIndex));
+
+ if (pParagraph)
+ return pParagraph->GetCharacterBounds(nCharacterIndex, true);
+ else
+ return awt::Rectangle(0,0,0,0);
+}
+
+//----- private ---------------------------------------------------------------
+
+void PresenterTextView::RequestFormat()
+{
+ mbIsFormatPending = true;
+}
+
+void PresenterTextView::Format()
+{
+ mbIsFormatPending = false;
+
+ double nY (0);
+ for (const auto& rxParagraph : maParagraphs)
+ {
+ rxParagraph->Format(nY, maSize.Width, mpFont);
+ nY += rxParagraph->GetTotalTextHeight();
+ }
+
+ if (maTextChangeBroadcaster)
+ maTextChangeBroadcaster();
+}
+
+sal_Int32 PresenterTextView::GetParagraphCount() const
+{
+ return maParagraphs.size();
+}
+
+SharedPresenterTextParagraph PresenterTextView::GetParagraph (
+ const sal_Int32 nParagraphIndex) const
+{
+ if (nParagraphIndex < 0)
+ return SharedPresenterTextParagraph();
+ else if (o3tl::make_unsigned(nParagraphIndex)>=maParagraphs.size())
+ return SharedPresenterTextParagraph();
+ else
+ return maParagraphs[nParagraphIndex];
+}
+
+//===== PresenterTextParagraph ================================================
+
+PresenterTextParagraph::PresenterTextParagraph (
+ const sal_Int32 nParagraphIndex,
+ const Reference<i18n::XBreakIterator>& rxBreakIterator,
+ const Reference<i18n::XScriptTypeDetector>& rxScriptTypeDetector,
+ const Reference<text::XTextRange>& rxTextRange,
+ const SharedPresenterTextCaret& rpCaret)
+ : mnParagraphIndex(nParagraphIndex),
+ mpCaret(rpCaret),
+ mxBreakIterator(rxBreakIterator),
+ mxScriptTypeDetector(rxScriptTypeDetector),
+ mnVerticalOffset(0),
+ mnXOrigin(0),
+ mnYOrigin(0),
+ mnWidth(0),
+ mnAscent(0),
+ mnDescent(0),
+ mnLineHeight(-1),
+ mnWritingMode (text::WritingMode2::LR_TB),
+ mnCharacterOffset(0)
+{
+ if (!rxTextRange.is())
+ return;
+
+ Reference<beans::XPropertySet> xProperties (rxTextRange, UNO_QUERY);
+ try
+ {
+ xProperties->getPropertyValue("WritingMode") >>= mnWritingMode;
+ }
+ catch(beans::UnknownPropertyException&)
+ {
+ // Ignore the exception. Use the default value.
+ }
+
+ msParagraphText = rxTextRange->getString();
+}
+
+void PresenterTextParagraph::Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const geometry::RealSize2D& rSize,
+ const PresenterTheme::SharedFontDescriptor& rpFont,
+ const rendering::ViewState& rViewState,
+ rendering::RenderState& rRenderState,
+ const double nTopOffset,
+ const double nClipTop,
+ const double nClipBottom)
+{
+ if (mnLineHeight <= 0)
+ return;
+
+ sal_Int8 nTextDirection (GetTextDirection());
+
+ const double nSavedM12 (rRenderState.AffineTransform.m12);
+
+ if ( ! IsTextReferencePointLeft())
+ rRenderState.AffineTransform.m02 += rSize.Width;
+
+#ifdef SHOW_CHARACTER_BOXES
+ for (sal_Int32 nIndex=0,nCount=maLines.size();
+ nIndex<nCount;
+ ++nIndex)
+ {
+ Line& rLine (maLines[nIndex]);
+ rLine.ProvideLayoutedLine(msParagraphText, rpFont, nTextDirection);
+ }
+#endif
+
+ for (sal_Int32 nIndex=0,nCount=maLines.size();
+ nIndex<nCount;
+ ++nIndex, rRenderState.AffineTransform.m12 += mnLineHeight)
+ {
+ Line& rLine (maLines[nIndex]);
+
+ // Paint only visible lines.
+ const double nLineTop = rLine.mnBaseLine - mnAscent - nTopOffset;
+ if (nLineTop + mnLineHeight< nClipTop)
+ continue;
+ else if (nLineTop > nClipBottom)
+ break;
+ rLine.ProvideLayoutedLine(msParagraphText, rpFont, nTextDirection);
+
+ rRenderState.AffineTransform.m12 = nSavedM12 + rLine.mnBaseLine;
+
+ rxCanvas->drawTextLayout (
+ rLine.mxLayoutedLine,
+ rViewState,
+ rRenderState);
+ }
+ rRenderState.AffineTransform.m12 = nSavedM12;
+
+ if ( ! IsTextReferencePointLeft())
+ rRenderState.AffineTransform.m02 -= rSize.Width;
+}
+
+void PresenterTextParagraph::Format (
+ const double nY,
+ const double nWidth,
+ const PresenterTheme::SharedFontDescriptor& rpFont)
+{
+ // Make sure that the text view is in a valid and sane state.
+ if ( ! mxBreakIterator.is() || ! mxScriptTypeDetector.is())
+ return;
+ if (nWidth<=0)
+ return;
+ if ( ! rpFont || ! rpFont->mxFont.is())
+ return;
+
+ sal_Int32 nPosition (0);
+
+ mnWidth = nWidth;
+ maLines.clear();
+ mnLineHeight = 0;
+ mnAscent = 0;
+ mnDescent = 0;
+ mnVerticalOffset = nY;
+ maWordBoundaries.clear();
+ maWordBoundaries.push_back(0);
+
+ const rendering::FontMetrics aMetrics (rpFont->mxFont->getFontMetrics());
+ mnAscent = aMetrics.Ascent;
+ mnDescent = aMetrics.Descent;
+ mnLineHeight = aMetrics.Ascent + aMetrics.Descent + aMetrics.ExternalLeading;
+ nPosition = 0;
+ i18n::Boundary aCurrentLine(0,0);
+ while (true)
+ {
+ const i18n::Boundary aWordBoundary = mxBreakIterator->nextWord(
+ msParagraphText,
+ nPosition,
+ lang::Locale(),
+ i18n::WordType::ANYWORD_IGNOREWHITESPACES);
+ AddWord(nWidth, aCurrentLine, aWordBoundary.startPos, rpFont);
+
+ // Remember the new word boundary for caret travelling by words.
+ // Prevent duplicates.
+ if (aWordBoundary.startPos > maWordBoundaries.back())
+ maWordBoundaries.push_back(aWordBoundary.startPos);
+
+ if (aWordBoundary.endPos>aWordBoundary.startPos)
+ AddWord(nWidth, aCurrentLine, aWordBoundary.endPos, rpFont);
+
+ if (aWordBoundary.startPos<0 || aWordBoundary.endPos<0)
+ break;
+ if (nPosition >= aWordBoundary.endPos)
+ break;
+ nPosition = aWordBoundary.endPos;
+ }
+
+ if (aCurrentLine.endPos>aCurrentLine.startPos)
+ AddLine(aCurrentLine);
+
+}
+
+sal_Int32 PresenterTextParagraph::GetWordBoundary(
+ const sal_Int32 nLocalCharacterIndex,
+ const sal_Int32 nDistance)
+{
+ OSL_ASSERT(nDistance==-1 || nDistance==+1);
+
+ if (nLocalCharacterIndex < 0)
+ {
+ // The caller asked for the start or end position of the paragraph.
+ if (nDistance < 0)
+ return 0;
+ else
+ return GetCharacterCount();
+ }
+
+ sal_Int32 nIndex (0);
+ for (sal_Int32 nCount (maWordBoundaries.size()); nIndex<nCount; ++nIndex)
+ {
+ if (maWordBoundaries[nIndex] >= nLocalCharacterIndex)
+ {
+ // When inside the word (not at its start or end) then
+ // first move to the start or end before going the previous or
+ // next word.
+ if (maWordBoundaries[nIndex] > nLocalCharacterIndex)
+ if (nDistance > 0)
+ --nIndex;
+ break;
+ }
+ }
+
+ nIndex += nDistance;
+
+ if (nIndex < 0)
+ return -1;
+ else if (o3tl::make_unsigned(nIndex)>=maWordBoundaries.size())
+ return -1;
+ else
+ return maWordBoundaries[nIndex];
+}
+
+sal_Int32 PresenterTextParagraph::GetCaretPosition() const
+{
+ if (mpCaret && mpCaret->GetParagraphIndex()==mnParagraphIndex)
+ return mpCaret->GetCharacterIndex();
+ else
+ return -1;
+}
+
+void PresenterTextParagraph::SetCaretPosition (const sal_Int32 nPosition) const
+{
+ if (mpCaret && mpCaret->GetParagraphIndex()==mnParagraphIndex)
+ return mpCaret->SetPosition(mnParagraphIndex, nPosition);
+}
+
+void PresenterTextParagraph::SetOrigin (const double nXOrigin, const double nYOrigin)
+{
+ mnXOrigin = nXOrigin;
+ mnYOrigin = nYOrigin;
+}
+
+awt::Point PresenterTextParagraph::GetRelativeLocation() const
+{
+ return awt::Point(
+ sal_Int32(mnXOrigin),
+ sal_Int32(mnYOrigin + mnVerticalOffset));
+}
+
+awt::Size PresenterTextParagraph::GetSize() const
+{
+ return awt::Size(
+ sal_Int32(mnWidth),
+ sal_Int32(GetTotalTextHeight()));
+}
+
+void PresenterTextParagraph::AddWord (
+ const double nWidth,
+ i18n::Boundary& rCurrentLine,
+ const sal_Int32 nWordBoundary,
+ const PresenterTheme::SharedFontDescriptor& rpFont)
+{
+ sal_Int32 nLineStart (0);
+ if ( ! maLines.empty())
+ nLineStart = rCurrentLine.startPos;
+
+ const OUString sLineCandidate (
+ msParagraphText.copy(nLineStart, nWordBoundary-nLineStart));
+
+ css::geometry::RealRectangle2D aLineBox (
+ PresenterCanvasHelper::GetTextBoundingBox (
+ rpFont->mxFont,
+ sLineCandidate,
+ mnWritingMode));
+ const double nLineWidth (aLineBox.X2 - aLineBox.X1);
+
+ if (nLineWidth >= nWidth)
+ {
+ // Add new line with a single word (so far).
+ AddLine(rCurrentLine);
+ }
+ rCurrentLine.endPos = nWordBoundary;
+}
+
+void PresenterTextParagraph::AddLine (
+ i18n::Boundary& rCurrentLine)
+{
+ Line aLine (rCurrentLine.startPos, rCurrentLine.endPos);
+
+ // Find the start and end of the line with respect to cells.
+ if (!maLines.empty())
+ {
+ aLine.mnLineStartCellIndex = maLines.back().mnLineEndCellIndex;
+ aLine.mnBaseLine = maLines.back().mnBaseLine + mnLineHeight;
+ }
+ else
+ {
+ aLine.mnLineStartCellIndex = 0;
+ aLine.mnBaseLine = mnVerticalOffset + mnAscent;
+ }
+ sal_Int32 nCellIndex (aLine.mnLineStartCellIndex);
+ double nWidth (0);
+ for ( ; nCellIndex<sal_Int32(maCells.size()); ++nCellIndex)
+ {
+ const Cell& rCell (maCells[nCellIndex]);
+ if (rCell.mnCharacterIndex+rCell.mnCharacterCount > aLine.mnLineEndCharacterIndex)
+ break;
+ nWidth += rCell.mnCellWidth;
+ }
+ aLine.mnLineEndCellIndex = nCellIndex;
+ aLine.mnWidth = nWidth;
+
+ maLines.push_back(aLine);
+
+ rCurrentLine.startPos = rCurrentLine.endPos;
+}
+
+double PresenterTextParagraph::GetTotalTextHeight() const
+{
+ return maLines.size() * mnLineHeight;
+}
+
+void PresenterTextParagraph::SetCharacterOffset (const sal_Int32 nCharacterOffset)
+{
+ mnCharacterOffset = nCharacterOffset;
+}
+
+sal_Int32 PresenterTextParagraph::GetCharacterCount() const
+{
+ return msParagraphText.getLength();
+}
+
+sal_Unicode PresenterTextParagraph::GetCharacter (
+ const sal_Int32 nGlobalCharacterIndex) const
+{
+ if (nGlobalCharacterIndex<mnCharacterOffset
+ || nGlobalCharacterIndex>=mnCharacterOffset+msParagraphText.getLength())
+ {
+ return sal_Unicode();
+ }
+ else
+ {
+ return msParagraphText[nGlobalCharacterIndex - mnCharacterOffset];
+ }
+}
+
+const OUString& PresenterTextParagraph::GetText() const
+{
+ return msParagraphText;
+}
+
+TextSegment PresenterTextParagraph::GetTextSegment (
+ const sal_Int32 nOffset,
+ const sal_Int32 nIndex,
+ const sal_Int16 nTextType) const
+{
+ switch(nTextType)
+ {
+ case AccessibleTextType::PARAGRAPH:
+ return TextSegment(
+ msParagraphText,
+ mnCharacterOffset,
+ mnCharacterOffset+msParagraphText.getLength());
+
+ case AccessibleTextType::SENTENCE:
+ if (mxBreakIterator.is())
+ {
+ const sal_Int32 nStart (mxBreakIterator->beginOfSentence(
+ msParagraphText, nIndex-mnCharacterOffset, lang::Locale()));
+ const sal_Int32 nEnd (mxBreakIterator->endOfSentence(
+ msParagraphText, nIndex-mnCharacterOffset, lang::Locale()));
+ if (nStart < nEnd)
+ return TextSegment(
+ msParagraphText.copy(nStart, nEnd-nStart),
+ nStart+mnCharacterOffset,
+ nEnd+mnCharacterOffset);
+ }
+ break;
+
+ case AccessibleTextType::WORD:
+ if (mxBreakIterator.is())
+ return GetWordTextSegment(nOffset, nIndex);
+ break;
+
+ case AccessibleTextType::LINE:
+ {
+ auto iLine = std::find_if(maLines.begin(), maLines.end(),
+ [nIndex](const Line& rLine) { return nIndex < rLine.mnLineEndCharacterIndex; });
+ if (iLine != maLines.end())
+ {
+ return TextSegment(
+ msParagraphText.copy(
+ iLine->mnLineStartCharacterIndex,
+ iLine->mnLineEndCharacterIndex - iLine->mnLineStartCharacterIndex),
+ iLine->mnLineStartCharacterIndex,
+ iLine->mnLineEndCharacterIndex);
+ }
+ }
+ break;
+
+ // Handle GLYPH and ATTRIBUTE_RUN like CHARACTER because we can not
+ // do better at the moment.
+ case AccessibleTextType::CHARACTER:
+ case AccessibleTextType::GLYPH:
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ return CreateTextSegment(nIndex+nOffset, nIndex+nOffset+1);
+ }
+
+ return TextSegment(OUString(), 0,0);
+}
+
+TextSegment PresenterTextParagraph::GetWordTextSegment (
+ const sal_Int32 nOffset,
+ const sal_Int32 nIndex) const
+{
+ sal_Int32 nCurrentOffset (nOffset);
+ sal_Int32 nCurrentIndex (nIndex);
+
+ i18n::Boundary aWordBoundary;
+ if (nCurrentOffset == 0)
+ aWordBoundary = mxBreakIterator->getWordBoundary(
+ msParagraphText,
+ nIndex,
+ lang::Locale(),
+ i18n::WordType::ANYWORD_IGNOREWHITESPACES,
+ true);
+ else if (nCurrentOffset < 0)
+ {
+ while (nCurrentOffset<0 && nCurrentIndex>0)
+ {
+ aWordBoundary = mxBreakIterator->previousWord(
+ msParagraphText,
+ nCurrentIndex,
+ lang::Locale(),
+ i18n::WordType::ANYWORD_IGNOREWHITESPACES);
+ nCurrentIndex = aWordBoundary.startPos;
+ ++nCurrentOffset;
+ }
+ }
+ else
+ {
+ while (nCurrentOffset>0 && nCurrentIndex<=GetCharacterCount())
+ {
+ aWordBoundary = mxBreakIterator->nextWord(
+ msParagraphText,
+ nCurrentIndex,
+ lang::Locale(),
+ i18n::WordType::ANYWORD_IGNOREWHITESPACES);
+ nCurrentIndex = aWordBoundary.endPos;
+ --nCurrentOffset;
+ }
+ }
+
+ return CreateTextSegment(aWordBoundary.startPos, aWordBoundary.endPos);
+}
+
+TextSegment PresenterTextParagraph::CreateTextSegment (
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex) const
+{
+ if (nEndIndex <= nStartIndex)
+ return TextSegment(
+ OUString(),
+ nStartIndex,
+ nEndIndex);
+ else
+ return TextSegment(
+ msParagraphText.copy(nStartIndex, nEndIndex-nStartIndex),
+ nStartIndex,
+ nEndIndex);
+}
+
+awt::Rectangle PresenterTextParagraph::GetCharacterBounds (
+ sal_Int32 nGlobalCharacterIndex,
+ const bool bCaretBox)
+{
+ // Find the line that contains the requested character and accumulate
+ // the previous line heights.
+ double nX (mnXOrigin);
+ double nY (mnYOrigin + mnVerticalOffset + mnAscent);
+ const sal_Int8 nTextDirection (GetTextDirection());
+ for (sal_Int32 nLineIndex=0,nLineCount=maLines.size();
+ nLineIndex<nLineCount;
+ ++nLineIndex, nY+=mnLineHeight)
+ {
+ Line& rLine (maLines[nLineIndex]);
+ // Skip lines before the indexed character.
+ if (nGlobalCharacterIndex >= rLine.mnLineEndCharacterIndex)
+ // When in the last line then allow the index past the last char.
+ if (nLineIndex<nLineCount-1)
+ continue;
+
+ rLine.ProvideCellBoxes();
+
+ const sal_Int32 nCellIndex (nGlobalCharacterIndex - rLine.mnLineStartCharacterIndex);
+
+ // The cell bounding box is defined relative to the origin of
+ // the current line. Therefore we have to add the absolute
+ // position of the line.
+ geometry::RealRectangle2D rCellBox (rLine.maCellBoxes[
+ ::std::min(nCellIndex, rLine.maCellBoxes.getLength()-1)]);
+
+ double nLeft = nX + rCellBox.X1;
+ double nRight = nX + rCellBox.X2;
+ if (nTextDirection == rendering::TextDirection::WEAK_RIGHT_TO_LEFT)
+ {
+ const double nOldRight (nRight);
+ nRight = rLine.mnWidth - nLeft;
+ nLeft = rLine.mnWidth - nOldRight;
+ }
+ double nTop = nY - mnAscent;
+ double nBottom;
+ if (bCaretBox)
+ {
+ nBottom = nTop + mnLineHeight;
+ if (nCellIndex >= rLine.maCellBoxes.getLength())
+ nLeft = nRight-2;
+ if (nLeft < nX)
+ nLeft = nX;
+ nRight = nLeft+2;
+ }
+ else
+ {
+ nBottom = nTop + mnAscent + mnDescent;
+ }
+ const sal_Int32 nX1 = sal_Int32(floor(nLeft));
+ const sal_Int32 nY1 = sal_Int32(floor(nTop));
+ const sal_Int32 nX2 = sal_Int32(ceil(nRight));
+ const sal_Int32 nY2 = sal_Int32(ceil(nBottom));
+
+ return awt::Rectangle(nX1,nY1,nX2-nX1+1,nY2-nY1+1);
+ }
+
+ // We are still here. That means that the given index lies past the
+ // last character in the paragraph.
+ // Return an empty box that lies past the last character. Better than nothing.
+ return awt::Rectangle(sal_Int32(nX+0.5), sal_Int32(nY+0.5), 0, 0);
+}
+
+sal_Int8 PresenterTextParagraph::GetTextDirection() const
+{
+ // Find first portion that has a non-neutral text direction.
+ sal_Int32 nPosition (0);
+ sal_Int32 nTextLength (msParagraphText.getLength());
+ while (nPosition < nTextLength)
+ {
+ const sal_Int16 nScriptDirection (
+ mxScriptTypeDetector->getScriptDirection(
+ msParagraphText, nPosition, i18n::ScriptDirection::NEUTRAL));
+ switch (nScriptDirection)
+ {
+ case i18n::ScriptDirection::NEUTRAL:
+ // continue looping.
+ break;
+ case i18n::ScriptDirection::LEFT_TO_RIGHT:
+ return rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
+
+ case i18n::ScriptDirection::RIGHT_TO_LEFT:
+ return rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
+ }
+
+ nPosition = mxScriptTypeDetector->endOfScriptDirection(
+ msParagraphText, nPosition, nScriptDirection);
+ }
+
+ // All text in paragraph is neutral. Fall back on writing mode taken
+ // from the XText (which may not be properly initialized.)
+ sal_Int8 nTextDirection(rendering::TextDirection::WEAK_LEFT_TO_RIGHT);
+ switch(mnWritingMode)
+ {
+ case text::WritingMode2::LR_TB:
+ nTextDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
+ break;
+
+ case text::WritingMode2::RL_TB:
+ nTextDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
+ break;
+
+ default:
+ case text::WritingMode2::TB_RL:
+ case text::WritingMode2::TB_LR:
+ // Can not handle this. Use default and hope for the best.
+ break;
+ }
+ return nTextDirection;
+}
+
+bool PresenterTextParagraph::IsTextReferencePointLeft() const
+{
+ return mnWritingMode != text::WritingMode2::RL_TB;
+}
+
+void PresenterTextParagraph::SetupCellArray (
+ const PresenterTheme::SharedFontDescriptor& rpFont)
+{
+ maCells.clear();
+
+ if ( ! rpFont || ! rpFont->mxFont.is())
+ return;
+
+ sal_Int32 nPosition (0);
+ sal_Int32 nIndex (0);
+ const sal_Int32 nTextLength (msParagraphText.getLength());
+ const sal_Int8 nTextDirection (GetTextDirection());
+ while (nPosition < nTextLength)
+ {
+ const sal_Int32 nNewPosition (mxBreakIterator->nextCharacters(
+ msParagraphText,
+ nPosition,
+ lang::Locale(),
+ i18n::CharacterIteratorMode::SKIPCELL,
+ 1,
+ nIndex));
+
+ rendering::StringContext aContext (msParagraphText, nPosition, nNewPosition-nPosition);
+ Reference<rendering::XTextLayout> xLayout (
+ rpFont->mxFont->createTextLayout(aContext, nTextDirection, 0));
+ css::geometry::RealRectangle2D aCharacterBox (xLayout->queryTextBounds());
+
+ maCells.emplace_back(
+ nPosition,
+ nNewPosition-nPosition,
+ aCharacterBox.X2-aCharacterBox.X1);
+
+ nPosition = nNewPosition;
+ }
+}
+
+//===== PresenterTextCaret ================================================----
+
+PresenterTextCaret::PresenterTextCaret (
+ uno::Reference<uno::XComponentContext> const& xContext,
+ const ::std::function<css::awt::Rectangle (const sal_Int32,const sal_Int32)>& rCharacterBoundsAccess,
+ const ::std::function<void (const css::awt::Rectangle&)>& rInvalidator)
+ : m_xContext(xContext)
+ , mnParagraphIndex(-1),
+ mnCharacterIndex(-1),
+ mnCaretBlinkTaskId(0),
+ mbIsCaretVisible(false),
+ maCharacterBoundsAccess(rCharacterBoundsAccess),
+ maInvalidator(rInvalidator)
+{
+}
+
+PresenterTextCaret::~PresenterTextCaret()
+{
+ try
+ {
+ HideCaret();
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("sdext.presenter", "unexpected exception in ~PresenterTextCaret");
+ }
+}
+
+void PresenterTextCaret::ShowCaret()
+{
+ if (mnCaretBlinkTaskId == 0)
+ {
+ mnCaretBlinkTaskId = PresenterTimer::ScheduleRepeatedTask (
+ m_xContext,
+ [this] (TimeValue const&) { return this->InvertCaret(); },
+ CaretBlinkInterval,
+ CaretBlinkInterval);
+ }
+ mbIsCaretVisible = true;
+}
+
+void PresenterTextCaret::HideCaret()
+{
+ if (mnCaretBlinkTaskId != 0)
+ {
+ PresenterTimer::CancelTask(mnCaretBlinkTaskId);
+ mnCaretBlinkTaskId = 0;
+ }
+ mbIsCaretVisible = false;
+ // Reset the caret position.
+ mnParagraphIndex = -1;
+ mnCharacterIndex = -1;
+}
+
+
+void PresenterTextCaret::SetPosition (
+ const sal_Int32 nParagraphIndex,
+ const sal_Int32 nCharacterIndex)
+{
+ if (mnParagraphIndex == nParagraphIndex
+ && mnCharacterIndex == nCharacterIndex)
+ return;
+
+ if (mnParagraphIndex >= 0)
+ maInvalidator(maCaretBounds);
+
+ const sal_Int32 nOldParagraphIndex (mnParagraphIndex);
+ const sal_Int32 nOldCharacterIndex (mnCharacterIndex);
+ mnParagraphIndex = nParagraphIndex;
+ mnCharacterIndex = nCharacterIndex;
+ maCaretBounds = maCharacterBoundsAccess(mnParagraphIndex, mnCharacterIndex);
+ if (mnParagraphIndex >= 0)
+ ShowCaret();
+ else
+ HideCaret();
+
+ if (mnParagraphIndex >= 0)
+ maInvalidator(maCaretBounds);
+
+ if (maBroadcaster)
+ maBroadcaster(
+ nOldParagraphIndex,
+ nOldCharacterIndex,
+ mnParagraphIndex,
+ mnCharacterIndex);
+}
+
+
+void PresenterTextCaret::SetCaretMotionBroadcaster (
+ const ::std::function<void (sal_Int32,sal_Int32,sal_Int32,sal_Int32)>& rBroadcaster)
+{
+ maBroadcaster = rBroadcaster;
+}
+
+const css::awt::Rectangle& PresenterTextCaret::GetBounds() const
+{
+ return maCaretBounds;
+}
+
+void PresenterTextCaret::InvertCaret()
+{
+ mbIsCaretVisible = !mbIsCaretVisible;
+ if (mnParagraphIndex >= 0)
+ maInvalidator(maCaretBounds);
+}
+
+//===== PresenterTextParagraph::Cell ==========================================
+
+PresenterTextParagraph::Cell::Cell (
+ const sal_Int32 nCharacterIndex,
+ const sal_Int32 nCharacterCount,
+ const double nCellWidth)
+ : mnCharacterIndex(nCharacterIndex),
+ mnCharacterCount(nCharacterCount),
+ mnCellWidth(nCellWidth)
+{
+}
+
+//===== PresenterTextParagraph::Line ==========================================
+
+PresenterTextParagraph::Line::Line (
+ const sal_Int32 nLineStartCharacterIndex,
+ const sal_Int32 nLineEndCharacterIndex)
+ : mnLineStartCharacterIndex(nLineStartCharacterIndex),
+ mnLineEndCharacterIndex(nLineEndCharacterIndex),
+ mnLineStartCellIndex(-1), mnLineEndCellIndex(-1),
+ mnBaseLine(0), mnWidth(0)
+{
+}
+
+void PresenterTextParagraph::Line::ProvideCellBoxes()
+{
+ if ( mnLineStartCharacterIndex < mnLineEndCharacterIndex && !maCellBoxes.hasElements() )
+ {
+ if (mxLayoutedLine.is())
+ maCellBoxes = mxLayoutedLine->queryInkMeasures();
+ else
+ {
+ OSL_ASSERT(mxLayoutedLine.is());
+ }
+ }
+}
+
+void PresenterTextParagraph::Line::ProvideLayoutedLine (
+ const OUString& rsParagraphText,
+ const PresenterTheme::SharedFontDescriptor& rpFont,
+ const sal_Int8 nTextDirection)
+{
+ if ( ! mxLayoutedLine.is())
+ {
+ const rendering::StringContext aContext (
+ rsParagraphText,
+ mnLineStartCharacterIndex,
+ mnLineEndCharacterIndex - mnLineStartCharacterIndex);
+
+ mxLayoutedLine = rpFont->mxFont->createTextLayout(
+ aContext,
+ nTextDirection,
+ 0);
+ }
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterTextView.hxx b/sdext/source/presenter/PresenterTextView.hxx
new file mode 100644
index 000000000..a732978e6
--- /dev/null
+++ b/sdext/source/presenter/PresenterTextView.hxx
@@ -0,0 +1,279 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERTEXTVIEW_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERTEXTVIEW_HXX
+
+#include "PresenterTheme.hxx"
+#include <com/sun/star/accessibility/TextSegment.hpp>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/geometry/RealPoint2D.hpp>
+#include <com/sun/star/geometry/RealSize2D.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/XScriptTypeDetector.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <functional>
+
+namespace sdext::presenter {
+
+class PresenterTextCaret
+{
+public:
+ PresenterTextCaret (
+ css::uno::Reference<css::uno::XComponentContext> const& xContext,
+ const ::std::function<css::awt::Rectangle (const sal_Int32,const sal_Int32)>&
+ rCharacterBoundsAccess,
+ const ::std::function<void (const css::awt::Rectangle&)>&
+ rInvalidator);
+ ~PresenterTextCaret();
+
+ void ShowCaret();
+ void HideCaret();
+
+ sal_Int32 GetParagraphIndex() const { return mnParagraphIndex;}
+ sal_Int32 GetCharacterIndex() const { return mnCharacterIndex;}
+ void SetPosition (
+ const sal_Int32 nParagraphIndex,
+ const sal_Int32 nCharacterIndex);
+
+ bool IsVisible() const { return mbIsCaretVisible;}
+
+ /** Set a (possibly empty) functor that broadcasts changes of the caret
+ position. This is used when a PresenterTextView object is set at
+ the accessibility object so that accessibility events can be sent
+ when the caret changes position.
+ */
+ void SetCaretMotionBroadcaster (
+ const ::std::function<void (sal_Int32,sal_Int32,sal_Int32,sal_Int32)>& rBroadcaster);
+
+ const css::awt::Rectangle& GetBounds() const;
+
+private:
+ css::uno::Reference<css::uno::XComponentContext> const& m_xContext;
+ sal_Int32 mnParagraphIndex;
+ sal_Int32 mnCharacterIndex;
+ sal_Int32 mnCaretBlinkTaskId;
+ bool mbIsCaretVisible;
+ const ::std::function<css::awt::Rectangle (const sal_Int32,const sal_Int32)> maCharacterBoundsAccess;
+ const ::std::function<void (const css::awt::Rectangle&)> maInvalidator;
+ ::std::function<void (sal_Int32,sal_Int32,sal_Int32,sal_Int32)> maBroadcaster;
+ css::awt::Rectangle maCaretBounds;
+
+ void InvertCaret();
+};
+typedef std::shared_ptr<PresenterTextCaret> SharedPresenterTextCaret;
+
+//===== PresenterTextParagraph ================================================
+
+class PresenterTextParagraph
+{
+public:
+ PresenterTextParagraph (
+ const sal_Int32 nParagraphIndex,
+ const css::uno::Reference<css::i18n::XBreakIterator>& rxBreakIterator,
+ const css::uno::Reference<css::i18n::XScriptTypeDetector>& rxScriptTypeDetector,
+ const css::uno::Reference<css::text::XTextRange>& rxTextRange,
+ const SharedPresenterTextCaret& rpCaret);
+
+ void Paint (
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::geometry::RealSize2D& rSize,
+ const PresenterTheme::SharedFontDescriptor& rpFont,
+ const css::rendering::ViewState& rViewState,
+ css::rendering::RenderState& rRenderState,
+ const double nTopOffset,
+ const double nClipTop,
+ const double nClipBottom);
+
+ double GetTotalTextHeight() const;
+
+ void SetCharacterOffset (const sal_Int32 nCharacterOffset);
+ sal_Int32 GetCharacterCount() const;
+ sal_Unicode GetCharacter (const sal_Int32 nGlobalCharacterIndex) const;
+ const OUString& GetText() const;
+ css::accessibility::TextSegment GetTextSegment (
+ const sal_Int32 nOffset,
+ const sal_Int32 nGlobalCharacterIndex,
+ const sal_Int16 nTextType) const;
+ css::accessibility::TextSegment GetWordTextSegment (
+ const sal_Int32 nOffset,
+ const sal_Int32 nIndex) const;
+ css::accessibility::TextSegment CreateTextSegment (
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex) const;
+ css::awt::Rectangle GetCharacterBounds (
+ sal_Int32 nGlobalCharacterIndex,
+ const bool bCaretBox);
+ void SetupCellArray (
+ const PresenterTheme::SharedFontDescriptor& rpFont);
+ void Format (
+ const double nY,
+ const double nWidth,
+ const PresenterTheme::SharedFontDescriptor& rpFont);
+ sal_Int32 GetWordBoundary(
+ const sal_Int32 nLocalCharacterIndex,
+ const sal_Int32 nDistance);
+ sal_Int32 GetCaretPosition() const;
+ void SetCaretPosition (const sal_Int32 nPosition) const;
+ void SetOrigin (const double nXOrigin, const double nYOrigin);
+ css::awt::Point GetRelativeLocation() const;
+ css::awt::Size GetSize() const;
+
+private:
+ OUString msParagraphText;
+ const sal_Int32 mnParagraphIndex;
+ SharedPresenterTextCaret mpCaret;
+
+ /** A portion of a string that encodes one unicode cell. It describes
+ number of characters in the unicode string that make up the cell and its
+ width in pixel (with respect to some configuration that is stored
+ externally or implicitly).
+ */
+ class Cell
+ {
+ public:
+ Cell (const sal_Int32 nCharacterIndex, const sal_Int32 nCharacterCount, const double nCellWidth);
+ sal_Int32 mnCharacterIndex;
+ sal_Int32 mnCharacterCount;
+ double mnCellWidth;
+ };
+
+ class Line
+ {
+ public:
+ Line (const sal_Int32 nLineStartCharacterIndex, const sal_Int32 nLineEndCharacterIndex);
+ sal_Int32 mnLineStartCharacterIndex;
+ sal_Int32 mnLineEndCharacterIndex;
+ sal_Int32 mnLineStartCellIndex;
+ sal_Int32 mnLineEndCellIndex;
+ css::uno::Reference<css::rendering::XTextLayout> mxLayoutedLine;
+ double mnBaseLine;
+ double mnWidth;
+ css::uno::Sequence<css::geometry::RealRectangle2D> maCellBoxes;
+
+ void ProvideLayoutedLine (
+ const OUString& rsParagraphText,
+ const PresenterTheme::SharedFontDescriptor& rpFont,
+ const sal_Int8 nTextDirection);
+ void ProvideCellBoxes();
+ };
+
+ css::uno::Reference<css::i18n::XBreakIterator> mxBreakIterator;
+ css::uno::Reference<css::i18n::XScriptTypeDetector> mxScriptTypeDetector;
+ ::std::vector<Line> maLines;
+ ::std::vector<sal_Int32> maWordBoundaries;
+ // Offset of the top of the paragraph with respect to the origin of the
+ // whole text (specified by mnXOrigin and mnYOrigin).
+ double mnVerticalOffset;
+ double mnXOrigin;
+ double mnYOrigin;
+ double mnWidth;
+ double mnAscent;
+ double mnDescent;
+ double mnLineHeight;
+ sal_Int8 mnWritingMode;
+ /// The index of the first character in this paragraph with respect to
+ /// the whole text.
+ sal_Int32 mnCharacterOffset;
+ ::std::vector<Cell> maCells;
+
+ void AddWord (
+ const double nWidth,
+ css::i18n::Boundary& rCurrentLine,
+ const sal_Int32 nWordBoundary,
+ const PresenterTheme::SharedFontDescriptor& rpFont);
+ void AddLine (
+ css::i18n::Boundary& rCurrentLine);
+ sal_Int8 GetTextDirection() const;
+ bool IsTextReferencePointLeft() const;
+};
+typedef std::shared_ptr<PresenterTextParagraph> SharedPresenterTextParagraph;
+
+/** A simple text view that paints text onto a given canvas.
+*/
+class PresenterTextView
+{
+public:
+
+ PresenterTextView (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const ::std::function<void (const css::awt::Rectangle&)>& rInvalidator);
+ void SetText (const css::uno::Reference<css::text::XText>& rxText);
+ void SetTextChangeBroadcaster(const ::std::function<void ()>& rBroadcaster);
+
+ void SetLocation (const css::geometry::RealPoint2D& rLocation);
+ void SetSize (const css::geometry::RealSize2D& rSize);
+ double GetTotalTextHeight();
+
+ void SetFont (const PresenterTheme::SharedFontDescriptor& rpFont);
+
+ void SetOffset (
+ const double nLeft,
+ const double nTop);
+
+ /** Move the caret forward or backward by character or by word.
+ @param nDistance
+ Should be either -1 or +1 to move caret backwards or forwards,
+ respectively.
+ @param nTextType
+ Valid values are the
+ css::accessibility::AccessibleTextType constants.
+ */
+ void MoveCaret (
+ const sal_Int32 nDistance,
+ const sal_Int16 nTextType);
+
+ void Paint (const css::awt::Rectangle& rUpdateBox);
+
+ const SharedPresenterTextCaret& GetCaret() const;
+
+ sal_Int32 GetParagraphCount() const;
+ SharedPresenterTextParagraph GetParagraph (const sal_Int32 nParagraphIndex) const;
+
+private:
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+ css::uno::Reference<css::i18n::XBreakIterator> mxBreakIterator;
+ css::uno::Reference<css::i18n::XScriptTypeDetector> mxScriptTypeDetector;
+ css::geometry::RealPoint2D maLocation;
+ css::geometry::RealSize2D maSize;
+ PresenterTheme::SharedFontDescriptor mpFont;
+ ::std::vector<SharedPresenterTextParagraph> maParagraphs;
+ SharedPresenterTextCaret mpCaret;
+ double mnLeftOffset;
+ double mnTopOffset;
+ bool mbIsFormatPending;
+ ::std::function<void ()> maTextChangeBroadcaster;
+
+ void RequestFormat();
+ void Format();
+ css::awt::Rectangle GetCaretBounds (
+ const sal_Int32 nParagraphIndex,
+ const sal_Int32 nCharacterIndex) const;
+};
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterTheme.cxx b/sdext/source/presenter/PresenterTheme.cxx
new file mode 100644
index 000000000..c84747d0f
--- /dev/null
+++ b/sdext/source/presenter/PresenterTheme.cxx
@@ -0,0 +1,1060 @@
+/* -*- 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 "PresenterTheme.hxx"
+#include "PresenterBitmapContainer.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterConfigurationAccess.hxx"
+#include <com/sun/star/drawing/XPresenterHelper.hpp>
+#include <com/sun/star/rendering/PanoseWeight.hpp>
+#include <osl/diagnose.h>
+#include <map>
+#include <numeric>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::std;
+
+namespace sdext::presenter {
+
+namespace {
+
+class BorderSize
+{
+public:
+ const static sal_Int32 mnInvalidValue = -10000;
+
+ BorderSize() : mnLeft(mnInvalidValue),
+ mnTop(mnInvalidValue),
+ mnRight(mnInvalidValue),
+ mnBottom(mnInvalidValue) {}
+
+ sal_Int32 mnLeft;
+ sal_Int32 mnTop;
+ sal_Int32 mnRight;
+ sal_Int32 mnBottom;
+
+ vector<sal_Int32> ToVector()
+ {
+ return
+ {
+ mnLeft == mnInvalidValue ? 0 : mnLeft,
+ mnTop == mnInvalidValue ? 0 : mnTop,
+ mnRight == mnInvalidValue ? 0 : mnRight,
+ mnBottom == mnInvalidValue ? 0 : mnBottom
+ };
+ };
+
+ void Merge (const BorderSize& rBorderSize)
+ {
+ if (mnLeft == mnInvalidValue)
+ mnLeft = rBorderSize.mnLeft;
+ if (mnTop == mnInvalidValue)
+ mnTop = rBorderSize.mnTop;
+ if (mnRight == mnInvalidValue)
+ mnRight = rBorderSize.mnRight;
+ if (mnBottom == mnInvalidValue)
+ mnBottom = rBorderSize.mnBottom;
+ }
+};
+
+/** Reading a theme from the configurations is done in various classes. The
+ ReadContext gives access to frequently used objects and functions to make
+ the configuration handling easier.
+*/
+class ReadContext
+{
+public:
+ Reference<XComponentContext> mxComponentContext;
+ Reference<rendering::XCanvas> mxCanvas;
+ Reference<drawing::XPresenterHelper> mxPresenterHelper;
+
+ ReadContext (
+ const Reference<XComponentContext>& rxContext,
+ const Reference<rendering::XCanvas>& rxCanvas);
+
+ /** Read data describing a font from the node that can be reached from
+ the given root via the given path.
+ @param rsFontPath
+ May be empty.
+ */
+ static PresenterTheme::SharedFontDescriptor ReadFont (
+ const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxTheme,
+ const PresenterTheme::SharedFontDescriptor& rpDefault);
+ static PresenterTheme::SharedFontDescriptor ReadFont (
+ const Reference<beans::XPropertySet>& rxFontProperties,
+ const PresenterTheme::SharedFontDescriptor& rpDefault);
+
+ std::shared_ptr<PresenterTheme::Theme> ReadTheme (
+ PresenterConfigurationAccess& rConfiguration,
+ const OUString& rsThemeName);
+
+ static BorderSize ReadBorderSize (const Reference<container::XNameAccess>& rxNode);
+
+private:
+ static Any GetByName (
+ const Reference<container::XNameAccess>& rxNode,
+ const OUString& rsName);
+};
+
+/** A PaneStyle describes how a pane is rendered.
+*/
+class PaneStyle
+{
+public:
+ PaneStyle();
+
+ SharedBitmapDescriptor GetBitmap (const OUString& sBitmapName) const;
+
+ OUString msStyleName;
+ std::shared_ptr<PaneStyle> mpParentStyle;
+ PresenterTheme::SharedFontDescriptor mpFont;
+ BorderSize maInnerBorderSize;
+ BorderSize maOuterBorderSize;
+ std::shared_ptr<PresenterBitmapContainer> mpBitmaps;
+
+ PresenterTheme::SharedFontDescriptor GetFont() const;
+};
+
+typedef std::shared_ptr<PaneStyle> SharedPaneStyle;
+
+class PaneStyleContainer
+{
+private:
+ ::std::vector<SharedPaneStyle> mStyles;
+
+public:
+ void Read (
+ const ReadContext& rReadContext,
+ const Reference<container::XHierarchicalNameAccess>& rThemeRoot);
+
+ SharedPaneStyle GetPaneStyle (const OUString& rsStyleName) const;
+
+private:
+ void ProcessPaneStyle (
+ ReadContext const & rReadContext,
+ const ::std::vector<css::uno::Any>& rValues);
+};
+
+/** A ViewStyle describes how a view is displayed.
+*/
+class ViewStyle
+{
+public:
+ ViewStyle();
+
+ SharedBitmapDescriptor GetBitmap (std::u16string_view sBitmapName) const;
+
+ PresenterTheme::SharedFontDescriptor GetFont() const;
+
+ OUString msStyleName;
+ std::shared_ptr<ViewStyle> mpParentStyle;
+ PresenterTheme::SharedFontDescriptor mpFont;
+ SharedBitmapDescriptor mpBackground;
+};
+
+typedef std::shared_ptr<ViewStyle> SharedViewStyle;
+
+class ViewStyleContainer
+{
+private:
+ ::std::vector<SharedViewStyle> mStyles;
+
+public:
+ void Read (
+ const ReadContext& rReadContext,
+ const Reference<container::XHierarchicalNameAccess>& rThemeRoot);
+
+ SharedViewStyle GetViewStyle (const OUString& rsStyleName) const;
+
+private:
+ void ProcessViewStyle(
+ ReadContext const & rReadContext,
+ const Reference<beans::XPropertySet>& rxProperties);
+};
+
+class StyleAssociationContainer
+{
+public:
+ void Read (
+ const Reference<container::XHierarchicalNameAccess>& rThemeRoot);
+
+ OUString GetStyleName (const OUString& rsResourceName) const;
+
+private:
+ typedef map<OUString, OUString> StyleAssociations;
+ StyleAssociations maStyleAssociations;
+
+ void ProcessStyleAssociation(
+ const ::std::vector<css::uno::Any>& rValues);
+};
+
+} // end of anonymous namespace
+
+class PresenterTheme::Theme
+{
+public:
+ Theme (
+ const Reference<container::XHierarchicalNameAccess>& rThemeRoot,
+ const OUString& rsNodeName);
+
+ void Read (
+ PresenterConfigurationAccess& rConfiguration,
+ ReadContext& rReadContext);
+
+ OUString msConfigurationNodeName;
+ std::shared_ptr<Theme> mpParentTheme;
+ SharedBitmapDescriptor mpBackground;
+ PaneStyleContainer maPaneStyles;
+ ViewStyleContainer maViewStyles;
+ StyleAssociationContainer maStyleAssociations;
+ Reference<container::XHierarchicalNameAccess> mxThemeRoot;
+ std::shared_ptr<PresenterBitmapContainer> mpIconContainer;
+ typedef map<OUString,SharedFontDescriptor> FontContainer;
+ FontContainer maFontContainer;
+
+ SharedPaneStyle GetPaneStyle (const OUString& rsStyleName) const;
+ SharedViewStyle GetViewStyle (const OUString& rsStyleName) const;
+
+private:
+ void ProcessFont(
+ const OUString& rsKey,
+ const Reference<beans::XPropertySet>& rxProperties);
+};
+
+//===== PresenterTheme ========================================================
+
+PresenterTheme::PresenterTheme (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas)
+ : mxContext(rxContext),
+ mxCanvas(rxCanvas)
+{
+ mpTheme = ReadTheme();
+}
+
+PresenterTheme::~PresenterTheme()
+{
+}
+
+std::shared_ptr<PresenterTheme::Theme> PresenterTheme::ReadTheme()
+{
+ ReadContext aReadContext(mxContext, mxCanvas);
+
+ PresenterConfigurationAccess aConfiguration (
+ mxContext,
+ "/org.openoffice.Office.PresenterScreen/",
+ PresenterConfigurationAccess::READ_ONLY);
+
+ return aReadContext.ReadTheme(aConfiguration, OUString());
+}
+
+bool PresenterTheme::HasCanvas() const
+{
+ return mxCanvas.is();
+}
+
+void PresenterTheme::ProvideCanvas (const Reference<rendering::XCanvas>& rxCanvas)
+{
+ if ( ! mxCanvas.is() && rxCanvas.is())
+ {
+ mxCanvas = rxCanvas;
+ ReadTheme();
+ }
+}
+
+OUString PresenterTheme::GetStyleName (const OUString& rsResourceURL) const
+{
+ OUString sStyleName;
+ std::shared_ptr<Theme> pTheme (mpTheme);
+ while (sStyleName.isEmpty() && pTheme != nullptr)
+ {
+ sStyleName = pTheme->maStyleAssociations.GetStyleName(rsResourceURL);
+ pTheme = pTheme->mpParentTheme;
+ }
+ return sStyleName;
+}
+
+::std::vector<sal_Int32> PresenterTheme::GetBorderSize (
+ const OUString& rsStyleName,
+ const bool bOuter) const
+{
+ OSL_ASSERT(mpTheme != nullptr);
+
+ SharedPaneStyle pPaneStyle (mpTheme->GetPaneStyle(rsStyleName));
+ if (pPaneStyle)
+ if (bOuter)
+ return pPaneStyle->maOuterBorderSize.ToVector();
+ else
+ return pPaneStyle->maInnerBorderSize.ToVector();
+ else
+ {
+ return ::std::vector<sal_Int32>(4,0);
+ }
+}
+
+PresenterTheme::SharedFontDescriptor PresenterTheme::ReadFont (
+ const Reference<container::XHierarchicalNameAccess>& rxNode,
+ const PresenterTheme::SharedFontDescriptor& rpDefault)
+{
+ return ReadContext::ReadFont(rxNode, rpDefault);
+}
+
+bool PresenterTheme::ConvertToColor (
+ const Any& rColorSequence,
+ sal_uInt32& rColor)
+{
+ Sequence<sal_Int8> aByteSequence;
+ if (rColorSequence >>= aByteSequence)
+ {
+ rColor = std::accumulate(std::cbegin(aByteSequence), std::cend(aByteSequence), sal_uInt32(0),
+ [](const sal_uInt32 nRes, const sal_uInt8 nByte) { return (nRes << 8) | nByte; });
+ return true;
+ }
+ else
+ return false;
+}
+
+std::shared_ptr<PresenterConfigurationAccess> PresenterTheme::GetNodeForViewStyle (
+ const OUString& rsStyleName) const
+{
+ if (mpTheme == nullptr)
+ return std::shared_ptr<PresenterConfigurationAccess>();
+
+ // Open configuration for writing.
+ auto pConfiguration = std::make_shared<PresenterConfigurationAccess>(
+ mxContext,
+ "/org.openoffice.Office.PresenterScreen/",
+ PresenterConfigurationAccess::READ_WRITE);
+
+ // Get configuration node for the view style container of the current
+ // theme.
+ if (pConfiguration->GoToChild( OUString(
+ "Presenter/Themes/" + mpTheme->msConfigurationNodeName + "/ViewStyles")))
+ {
+ pConfiguration->GoToChild(
+ [&rsStyleName] (OUString const&, uno::Reference<beans::XPropertySet> const& xProps)
+ {
+ return PresenterConfigurationAccess::IsStringPropertyEqual(
+ rsStyleName, "StyleName", xProps);
+ });
+ }
+ return pConfiguration;
+}
+
+SharedBitmapDescriptor PresenterTheme::GetBitmap (
+ const OUString& rsStyleName,
+ const OUString& rsBitmapName) const
+{
+ if (mpTheme != nullptr)
+ {
+ if (rsStyleName.isEmpty())
+ {
+ if (rsBitmapName == "Background")
+ {
+ std::shared_ptr<Theme> pTheme (mpTheme);
+ while (pTheme != nullptr && !pTheme->mpBackground)
+ pTheme = pTheme->mpParentTheme;
+ if (pTheme != nullptr)
+ return pTheme->mpBackground;
+ else
+ return SharedBitmapDescriptor();
+ }
+ }
+ else
+ {
+ SharedPaneStyle pPaneStyle (mpTheme->GetPaneStyle(rsStyleName));
+ if (pPaneStyle)
+ {
+ SharedBitmapDescriptor pBitmap (pPaneStyle->GetBitmap(rsBitmapName));
+ if (pBitmap)
+ return pBitmap;
+ }
+
+ SharedViewStyle pViewStyle (mpTheme->GetViewStyle(rsStyleName));
+ if (pViewStyle)
+ {
+ SharedBitmapDescriptor pBitmap (pViewStyle->GetBitmap(rsBitmapName));
+ if (pBitmap)
+ return pBitmap;
+ }
+ }
+ }
+
+ return SharedBitmapDescriptor();
+}
+
+SharedBitmapDescriptor PresenterTheme::GetBitmap (
+ const OUString& rsBitmapName) const
+{
+ if (mpTheme != nullptr)
+ {
+ if (rsBitmapName == "Background")
+ {
+ std::shared_ptr<Theme> pTheme (mpTheme);
+ while (pTheme != nullptr && !pTheme->mpBackground)
+ pTheme = pTheme->mpParentTheme;
+ if (pTheme != nullptr)
+ return pTheme->mpBackground;
+ else
+ return SharedBitmapDescriptor();
+ }
+ else
+ {
+ if (mpTheme->mpIconContainer != nullptr)
+ return mpTheme->mpIconContainer->GetBitmap(rsBitmapName);
+ }
+ }
+
+ return SharedBitmapDescriptor();
+}
+
+std::shared_ptr<PresenterBitmapContainer> PresenterTheme::GetBitmapContainer() const
+{
+ if (mpTheme != nullptr)
+ return mpTheme->mpIconContainer;
+ else
+ return std::shared_ptr<PresenterBitmapContainer>();
+}
+
+PresenterTheme::SharedFontDescriptor PresenterTheme::GetFont (
+ const OUString& rsStyleName) const
+{
+ if (mpTheme != nullptr)
+ {
+ SharedPaneStyle pPaneStyle (mpTheme->GetPaneStyle(rsStyleName));
+ if (pPaneStyle)
+ return pPaneStyle->GetFont();
+
+ SharedViewStyle pViewStyle (mpTheme->GetViewStyle(rsStyleName));
+ if (pViewStyle)
+ return pViewStyle->GetFont();
+
+ std::shared_ptr<Theme> pTheme (mpTheme);
+ while (pTheme != nullptr)
+ {
+ Theme::FontContainer::const_iterator iFont (pTheme->maFontContainer.find(rsStyleName));
+ if (iFont != pTheme->maFontContainer.end())
+ return iFont->second;
+
+ pTheme = pTheme->mpParentTheme;
+ }
+ }
+
+ return SharedFontDescriptor();
+}
+
+//===== FontDescriptor ========================================================
+
+PresenterTheme::FontDescriptor::FontDescriptor (
+ const std::shared_ptr<FontDescriptor>& rpDescriptor)
+ : mnSize(12),
+ mnColor(0x00000000),
+ msAnchor(OUString("Left")),
+ mnXOffset(0),
+ mnYOffset(0)
+{
+ if (rpDescriptor != nullptr)
+ {
+ msFamilyName = rpDescriptor->msFamilyName;
+ msStyleName = rpDescriptor->msStyleName;
+ mnSize = rpDescriptor->mnSize;
+ mnColor = rpDescriptor->mnColor;
+ msAnchor = rpDescriptor->msAnchor;
+ mnXOffset = rpDescriptor->mnXOffset;
+ mnYOffset = rpDescriptor->mnYOffset;
+ }
+}
+
+bool PresenterTheme::FontDescriptor::PrepareFont (
+ const Reference<rendering::XCanvas>& rxCanvas)
+{
+ if (mxFont.is())
+ return true;
+
+ if ( ! rxCanvas.is())
+ return false;
+
+ const double nCellSize (GetCellSizeForDesignSize(rxCanvas, mnSize));
+ mxFont = CreateFont(rxCanvas, nCellSize);
+
+ return mxFont.is();
+}
+
+Reference<rendering::XCanvasFont> PresenterTheme::FontDescriptor::CreateFont (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const double nCellSize) const
+{
+ rendering::FontRequest aFontRequest;
+ aFontRequest.FontDescription.FamilyName = msFamilyName;
+ if (msFamilyName.isEmpty())
+ aFontRequest.FontDescription.FamilyName = "Tahoma";
+ aFontRequest.FontDescription.StyleName = msStyleName;
+ aFontRequest.CellSize = nCellSize;
+
+ // Make an attempt at translating the style name(s)into a corresponding
+ // font description.
+ if (msStyleName == "Bold")
+ aFontRequest.FontDescription.FontDescription.Weight = rendering::PanoseWeight::HEAVY;
+
+ return rxCanvas->createFont(
+ aFontRequest,
+ Sequence<beans::PropertyValue>(),
+ geometry::Matrix2D(1,0,0,1));
+}
+
+double PresenterTheme::FontDescriptor::GetCellSizeForDesignSize (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const double nDesignSize) const
+{
+ // Use the given design size as initial value in calculating the cell
+ // size.
+ double nCellSize (nDesignSize);
+
+ if ( ! rxCanvas.is())
+ {
+ // We need the canvas to do the conversion. Return the design size,
+ // it is the our best guess in this circumstance.
+ return nDesignSize;
+ }
+
+ Reference<rendering::XCanvasFont> xFont (CreateFont(rxCanvas, nCellSize));
+ if ( ! xFont.is())
+ return nDesignSize;
+
+ geometry::RealRectangle2D aBox (PresenterCanvasHelper::GetTextBoundingBox (xFont, "X"));
+
+ const double nAscent (-aBox.Y1);
+ //tdf#112408
+ if (nAscent == 0)
+ return nDesignSize;
+ const double nDescent (aBox.Y2);
+ const double nScale = (nAscent+nDescent) / nAscent;
+ return nDesignSize * nScale;
+}
+
+//===== Theme =================================================================
+
+PresenterTheme::Theme::Theme (
+ const Reference<container::XHierarchicalNameAccess>& rxThemeRoot,
+ const OUString& rsNodeName)
+ : msConfigurationNodeName(rsNodeName),
+ maPaneStyles(),
+ maViewStyles(),
+ maStyleAssociations(),
+ mxThemeRoot(rxThemeRoot)
+{
+}
+
+void PresenterTheme::Theme::Read (
+ PresenterConfigurationAccess& rConfiguration,
+ ReadContext& rReadContext)
+{
+ // Parent theme name.
+ OUString sParentThemeName;
+ if ((PresenterConfigurationAccess::GetConfigurationNode(mxThemeRoot, "ParentTheme")
+ >>= sParentThemeName)
+ && !sParentThemeName.isEmpty())
+ {
+ mpParentTheme = rReadContext.ReadTheme(rConfiguration, sParentThemeName);
+ }
+
+ // Background.
+ mpBackground = PresenterBitmapContainer::LoadBitmap(
+ mxThemeRoot,
+ "Background",
+ rReadContext.mxPresenterHelper,
+ rReadContext.mxCanvas,
+ SharedBitmapDescriptor());
+
+ // Style associations.
+ maStyleAssociations.Read(mxThemeRoot);
+
+ // Pane styles.
+ maPaneStyles.Read(rReadContext, mxThemeRoot);
+
+ // View styles.
+ maViewStyles.Read(rReadContext, mxThemeRoot);
+
+ // Read bitmaps.
+ mpIconContainer = std::make_shared<PresenterBitmapContainer>(
+ Reference<container::XNameAccess>(
+ PresenterConfigurationAccess::GetConfigurationNode(mxThemeRoot, "Bitmaps"), UNO_QUERY),
+ mpParentTheme != nullptr ? mpParentTheme->mpIconContainer
+ : std::shared_ptr<PresenterBitmapContainer>(),
+ rReadContext.mxComponentContext, rReadContext.mxCanvas);
+
+ // Read fonts.
+ Reference<container::XNameAccess> xFontNode(
+ PresenterConfigurationAccess::GetConfigurationNode(mxThemeRoot, "Fonts"),
+ UNO_QUERY);
+ PresenterConfigurationAccess::ForAll(
+ xFontNode,
+ [this] (OUString const& rKey, uno::Reference<beans::XPropertySet> const& xProps)
+ {
+ return this->ProcessFont(rKey, xProps);
+ });
+}
+
+SharedPaneStyle PresenterTheme::Theme::GetPaneStyle (const OUString& rsStyleName) const
+{
+ SharedPaneStyle pPaneStyle (maPaneStyles.GetPaneStyle(rsStyleName));
+ if (pPaneStyle)
+ return pPaneStyle;
+ else if (mpParentTheme != nullptr)
+ return mpParentTheme->GetPaneStyle(rsStyleName);
+ else
+ return SharedPaneStyle();
+}
+
+SharedViewStyle PresenterTheme::Theme::GetViewStyle (const OUString& rsStyleName) const
+{
+ SharedViewStyle pViewStyle (maViewStyles.GetViewStyle(rsStyleName));
+ if (pViewStyle)
+ return pViewStyle;
+ else if (mpParentTheme != nullptr)
+ return mpParentTheme->GetViewStyle(rsStyleName);
+ else
+ return SharedViewStyle();
+}
+
+void PresenterTheme::Theme::ProcessFont(
+ const OUString& rsKey,
+ const Reference<beans::XPropertySet>& rxProperties)
+{
+ maFontContainer[rsKey] = ReadContext::ReadFont(rxProperties, SharedFontDescriptor());
+}
+
+namespace {
+
+//===== ReadContext ===========================================================
+
+ReadContext::ReadContext (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const Reference<rendering::XCanvas>& rxCanvas)
+ : mxComponentContext(rxContext),
+ mxCanvas(rxCanvas)
+{
+ Reference<lang::XMultiComponentFactory> xFactory (rxContext->getServiceManager());
+ if (xFactory.is())
+ {
+ mxPresenterHelper.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.comp.Draw.PresenterHelper",
+ rxContext),
+ UNO_QUERY_THROW);
+ }
+}
+
+PresenterTheme::SharedFontDescriptor ReadContext::ReadFont (
+ const Reference<container::XHierarchicalNameAccess>& rxNode,
+ const PresenterTheme::SharedFontDescriptor& rpDefault)
+{
+ if ( ! rxNode.is())
+ return PresenterTheme::SharedFontDescriptor();
+
+ try
+ {
+ Reference<container::XHierarchicalNameAccess> xFont (
+ PresenterConfigurationAccess::GetConfigurationNode(
+ rxNode,
+ /*rsFontPath*/""),
+ UNO_QUERY_THROW);
+
+ Reference<beans::XPropertySet> xProperties (xFont, UNO_QUERY_THROW);
+ return ReadFont(xProperties, rpDefault);
+ }
+ catch (Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+
+ return PresenterTheme::SharedFontDescriptor();
+}
+
+PresenterTheme::SharedFontDescriptor ReadContext::ReadFont (
+ const Reference<beans::XPropertySet>& rxProperties,
+ const PresenterTheme::SharedFontDescriptor& rpDefault)
+{
+ auto pDescriptor = std::make_shared<PresenterTheme::FontDescriptor>(rpDefault);
+
+ PresenterConfigurationAccess::GetProperty(rxProperties, "FamilyName") >>= pDescriptor->msFamilyName;
+ PresenterConfigurationAccess::GetProperty(rxProperties, "Style") >>= pDescriptor->msStyleName;
+ PresenterConfigurationAccess::GetProperty(rxProperties, "Size") >>= pDescriptor->mnSize;
+ PresenterTheme::ConvertToColor(
+ PresenterConfigurationAccess::GetProperty(rxProperties, "Color"),
+ pDescriptor->mnColor);
+ PresenterConfigurationAccess::GetProperty(rxProperties, "Anchor") >>= pDescriptor->msAnchor;
+ PresenterConfigurationAccess::GetProperty(rxProperties, "XOffset") >>= pDescriptor->mnXOffset;
+ PresenterConfigurationAccess::GetProperty(rxProperties, "YOffset") >>= pDescriptor->mnYOffset;
+
+ return pDescriptor;
+}
+
+Any ReadContext::GetByName (
+ const Reference<container::XNameAccess>& rxNode,
+ const OUString& rsName)
+{
+ OSL_ASSERT(rxNode.is());
+ if (rxNode->hasByName(rsName))
+ return rxNode->getByName(rsName);
+ else
+ return Any();
+}
+
+std::shared_ptr<PresenterTheme::Theme> ReadContext::ReadTheme (
+ PresenterConfigurationAccess& rConfiguration,
+ const OUString& rsThemeName)
+{
+ std::shared_ptr<PresenterTheme::Theme> pTheme;
+
+ OUString sCurrentThemeName (rsThemeName);
+ if (sCurrentThemeName.isEmpty())
+ {
+ // No theme name given. Look up the CurrentTheme property.
+ rConfiguration.GetConfigurationNode("Presenter/CurrentTheme") >>= sCurrentThemeName;
+ if (sCurrentThemeName.isEmpty())
+ {
+ // Still no name. Use "DefaultTheme".
+ sCurrentThemeName = "DefaultTheme";
+ }
+ }
+
+ Reference<container::XNameAccess> xThemes (
+ rConfiguration.GetConfigurationNode("Presenter/Themes"),
+ UNO_QUERY);
+ if (xThemes.is())
+ {
+ // Iterate over all themes and search the one with the given name.
+ const Sequence<OUString> aKeys (xThemes->getElementNames());
+ for (const OUString& rsKey : aKeys)
+ {
+ Reference<container::XHierarchicalNameAccess> xTheme (
+ xThemes->getByName(rsKey), UNO_QUERY);
+ if (xTheme.is())
+ {
+ OUString sThemeName;
+ PresenterConfigurationAccess::GetConfigurationNode(xTheme, "ThemeName")
+ >>= sThemeName;
+ if (sThemeName == sCurrentThemeName)
+ {
+ pTheme = std::make_shared<PresenterTheme::Theme>(xTheme,rsKey);
+ break;
+ }
+ }
+ }
+ }
+
+ if (pTheme != nullptr)
+ {
+ pTheme->Read(rConfiguration, *this);
+ }
+
+ return pTheme;
+}
+
+BorderSize ReadContext::ReadBorderSize (const Reference<container::XNameAccess>& rxNode)
+{
+ BorderSize aBorderSize;
+
+ if (rxNode.is())
+ {
+ GetByName(rxNode, "Left") >>= aBorderSize.mnLeft;
+ GetByName(rxNode, "Top") >>= aBorderSize.mnTop;
+ GetByName(rxNode, "Right") >>= aBorderSize.mnRight;
+ GetByName(rxNode, "Bottom") >>= aBorderSize.mnBottom;
+ }
+
+ return aBorderSize;
+}
+
+//===== PaneStyleContainer ====================================================
+
+void PaneStyleContainer::Read (
+ const ReadContext& rReadContext,
+ const Reference<container::XHierarchicalNameAccess>& rxThemeRoot)
+{
+ Reference<container::XNameAccess> xPaneStyleList (
+ PresenterConfigurationAccess::GetConfigurationNode(
+ rxThemeRoot,
+ "PaneStyles"),
+ UNO_QUERY);
+ if (!xPaneStyleList.is())
+ return;
+
+ ::std::vector<OUString> aProperties;
+ aProperties.reserve(6);
+ aProperties.emplace_back("StyleName");
+ aProperties.emplace_back("ParentStyle");
+ aProperties.emplace_back("TitleFont");
+ aProperties.emplace_back("InnerBorderSize");
+ aProperties.emplace_back("OuterBorderSize");
+ aProperties.emplace_back("BorderBitmapList");
+ PresenterConfigurationAccess::ForAll(
+ xPaneStyleList,
+ aProperties,
+ [this, &rReadContext] (std::vector<uno::Any> const& rValues)
+ {
+ return this->ProcessPaneStyle(rReadContext, rValues);
+ });
+}
+
+void PaneStyleContainer::ProcessPaneStyle(
+ ReadContext const & rReadContext,
+ const ::std::vector<Any>& rValues)
+{
+ if (rValues.size() != 6)
+ return;
+
+ auto pStyle = std::make_shared<PaneStyle>();
+
+ rValues[0] >>= pStyle->msStyleName;
+
+ OUString sParentStyleName;
+ if (rValues[1] >>= sParentStyleName)
+ {
+ // Find parent style.
+ auto iStyle = std::find_if(mStyles.begin(), mStyles.end(),
+ [&sParentStyleName](const SharedPaneStyle& rxStyle) { return rxStyle->msStyleName == sParentStyleName; });
+ if (iStyle != mStyles.end())
+ pStyle->mpParentStyle = *iStyle;
+ }
+
+ Reference<container::XHierarchicalNameAccess> xFontNode (rValues[2], UNO_QUERY);
+ pStyle->mpFont = ReadContext::ReadFont(
+ xFontNode, PresenterTheme::SharedFontDescriptor());
+
+ Reference<container::XNameAccess> xInnerBorderSizeNode (rValues[3], UNO_QUERY);
+ pStyle->maInnerBorderSize = ReadContext::ReadBorderSize(xInnerBorderSizeNode);
+ Reference<container::XNameAccess> xOuterBorderSizeNode (rValues[4], UNO_QUERY);
+ pStyle->maOuterBorderSize = ReadContext::ReadBorderSize(xOuterBorderSizeNode);
+
+ if (pStyle->mpParentStyle != nullptr)
+ {
+ pStyle->maInnerBorderSize.Merge(pStyle->mpParentStyle->maInnerBorderSize);
+ pStyle->maOuterBorderSize.Merge(pStyle->mpParentStyle->maOuterBorderSize);
+ }
+
+ if (rReadContext.mxCanvas.is())
+ {
+ Reference<container::XNameAccess> xBitmapsNode (rValues[5], UNO_QUERY);
+ pStyle->mpBitmaps = std::make_shared<PresenterBitmapContainer>(
+ xBitmapsNode,
+ pStyle->mpParentStyle != nullptr ? pStyle->mpParentStyle->mpBitmaps
+ : std::shared_ptr<PresenterBitmapContainer>(),
+ rReadContext.mxComponentContext, rReadContext.mxCanvas,
+ rReadContext.mxPresenterHelper);
+ }
+
+ mStyles.push_back(pStyle);
+}
+
+SharedPaneStyle PaneStyleContainer::GetPaneStyle (const OUString& rsStyleName) const
+{
+ auto iStyle = std::find_if(mStyles.begin(), mStyles.end(),
+ [&rsStyleName](const SharedPaneStyle& rxStyle) { return rxStyle->msStyleName == rsStyleName; });
+ if (iStyle != mStyles.end())
+ return *iStyle;
+ return SharedPaneStyle();
+}
+
+//===== PaneStyle =============================================================
+
+PaneStyle::PaneStyle()
+{
+}
+
+SharedBitmapDescriptor PaneStyle::GetBitmap (const OUString& rsBitmapName) const
+{
+ if (mpBitmaps != nullptr)
+ {
+ SharedBitmapDescriptor pBitmap = mpBitmaps->GetBitmap(rsBitmapName);
+ if (pBitmap)
+ return pBitmap;
+ }
+
+ if (mpParentStyle != nullptr)
+ return mpParentStyle->GetBitmap(rsBitmapName);
+ else
+ return SharedBitmapDescriptor();
+}
+
+PresenterTheme::SharedFontDescriptor PaneStyle::GetFont() const
+{
+ if (mpFont)
+ return mpFont;
+ else if (mpParentStyle != nullptr)
+ return mpParentStyle->GetFont();
+ else
+ return PresenterTheme::SharedFontDescriptor();
+}
+
+//===== ViewStyleContainer ====================================================
+
+void ViewStyleContainer::Read (
+ const ReadContext& rReadContext,
+ const Reference<container::XHierarchicalNameAccess>& rxThemeRoot)
+{
+ Reference<container::XNameAccess> xViewStyleList (
+ PresenterConfigurationAccess::GetConfigurationNode(
+ rxThemeRoot,
+ "ViewStyles"),
+ UNO_QUERY);
+ if (xViewStyleList.is())
+ {
+ PresenterConfigurationAccess::ForAll(
+ xViewStyleList,
+ [this, &rReadContext] (OUString const&, uno::Reference<beans::XPropertySet> const& xProps)
+ {
+ return this->ProcessViewStyle(rReadContext, xProps);
+ });
+ }
+}
+
+void ViewStyleContainer::ProcessViewStyle(
+ ReadContext const & rReadContext,
+ const Reference<beans::XPropertySet>& rxProperties)
+{
+ auto pStyle = std::make_shared<ViewStyle>();
+
+ PresenterConfigurationAccess::GetProperty(rxProperties, "StyleName")
+ >>= pStyle->msStyleName;
+
+ OUString sParentStyleName;
+ if (PresenterConfigurationAccess::GetProperty(rxProperties, "ParentStyle")
+ >>= sParentStyleName)
+ {
+ // Find parent style.
+ auto iStyle = std::find_if(mStyles.begin(), mStyles.end(),
+ [&sParentStyleName](const SharedViewStyle& rxStyle) { return rxStyle->msStyleName == sParentStyleName; });
+ if (iStyle != mStyles.end())
+ {
+ pStyle->mpParentStyle = *iStyle;
+ pStyle->mpFont = (*iStyle)->mpFont;
+ pStyle->mpBackground = (*iStyle)->mpBackground;
+ }
+ }
+
+ Reference<container::XHierarchicalNameAccess> xFontNode (
+ PresenterConfigurationAccess::GetProperty(rxProperties, "Font"), UNO_QUERY);
+ PresenterTheme::SharedFontDescriptor pFont (
+ ReadContext::ReadFont(xFontNode, PresenterTheme::SharedFontDescriptor()));
+ if (pFont)
+ pStyle->mpFont = pFont;
+
+ Reference<container::XHierarchicalNameAccess> xBackgroundNode (
+ PresenterConfigurationAccess::GetProperty(rxProperties, "Background"),
+ UNO_QUERY);
+ SharedBitmapDescriptor pBackground (PresenterBitmapContainer::LoadBitmap(
+ xBackgroundNode,
+ OUString(),
+ rReadContext.mxPresenterHelper,
+ rReadContext.mxCanvas,
+ SharedBitmapDescriptor()));
+ if (pBackground && pBackground->GetNormalBitmap().is())
+ pStyle->mpBackground = pBackground;
+
+ mStyles.push_back(pStyle);
+}
+
+SharedViewStyle ViewStyleContainer::GetViewStyle (const OUString& rsStyleName) const
+{
+ auto iStyle = std::find_if(mStyles.begin(), mStyles.end(),
+ [&rsStyleName](const SharedViewStyle& rxStyle) { return rxStyle->msStyleName == rsStyleName; });
+ if (iStyle != mStyles.end())
+ return *iStyle;
+ return SharedViewStyle();
+}
+
+//===== ViewStyle =============================================================
+
+ViewStyle::ViewStyle()
+{
+}
+
+SharedBitmapDescriptor ViewStyle::GetBitmap (std::u16string_view rsBitmapName) const
+{
+ if (rsBitmapName == u"Background")
+ return mpBackground;
+ else
+ return SharedBitmapDescriptor();
+}
+
+PresenterTheme::SharedFontDescriptor ViewStyle::GetFont() const
+{
+ if (mpFont)
+ return mpFont;
+ else if (mpParentStyle != nullptr)
+ return mpParentStyle->GetFont();
+ else
+ return PresenterTheme::SharedFontDescriptor();
+}
+
+//===== StyleAssociationContainer =============================================
+
+void StyleAssociationContainer::Read (
+ const Reference<container::XHierarchicalNameAccess>& rxThemeRoot)
+{
+ Reference<container::XNameAccess> xStyleAssociationList (
+ PresenterConfigurationAccess::GetConfigurationNode(
+ rxThemeRoot,
+ "StyleAssociations"),
+ UNO_QUERY);
+ if (!xStyleAssociationList.is())
+ return;
+
+ ::std::vector<OUString> aProperties { "ResourceURL", "StyleName" };
+ PresenterConfigurationAccess::ForAll(
+ xStyleAssociationList,
+ aProperties,
+ [this] (std::vector<uno::Any> const& rValues)
+ {
+ return this->ProcessStyleAssociation(rValues);
+ });
+}
+
+OUString StyleAssociationContainer::GetStyleName (const OUString& rsResourceName) const
+{
+ StyleAssociations::const_iterator iAssociation (maStyleAssociations.find(rsResourceName));
+ if (iAssociation != maStyleAssociations.end())
+ return iAssociation->second;
+ else
+ return OUString();
+}
+
+void StyleAssociationContainer::ProcessStyleAssociation(
+ const ::std::vector<Any>& rValues)
+{
+ if (rValues.size() != 2)
+ return;
+
+ OUString sResourceURL;
+ OUString sStyleName;
+ if ((rValues[0] >>= sResourceURL)
+ && (rValues[1] >>= sStyleName))
+ {
+ maStyleAssociations[sResourceURL] = sStyleName;
+ }
+}
+
+} // end of anonymous namespace
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterTheme.hxx b/sdext/source/presenter/PresenterTheme.hxx
new file mode 100644
index 000000000..97fff4842
--- /dev/null
+++ b/sdext/source/presenter/PresenterTheme.hxx
@@ -0,0 +1,134 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERTHEME_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERTHEME_HXX
+
+#include "PresenterBitmapContainer.hxx"
+#include "PresenterConfigurationAccess.hxx"
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XCanvasFont.hpp>
+#include <memory>
+
+namespace sdext::presenter {
+
+/** A theme is a set of properties describing fonts, colors, and bitmaps to be used to draw
+ background, pane borders, and view content.
+
+ At the moment the properties can be accessed via the getPropertyValue() method.
+
+ For a resource URL of a pane or a view you get the name of the
+ associated PaneStyle or ViewStyle.
+
+ For the name of pane or view style suffixed with and underscore and the
+ name of configuration property, and maybe additionally suffixed by
+ another underscore and sub property name you get the associated
+ property.
+
+ Example: you want to access the top left bitmap of a pane border
+ (simplified code):
+
+ OUString sStyleName = getPropertyValue("private:resource/pane/Presenter/Pane1");
+ XBitmap xBitmap = getPropertyValue(sStyleName + "_TopLeftBitmap");
+
+ For the offset of the bitmap you can call
+ Point aOffset = getPropertyValue(sStyleName + "_TopLeftOffset");
+
+ This is work in progress.
+*/
+class PresenterTheme
+{
+public:
+ PresenterTheme (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas);
+ ~PresenterTheme();
+
+ bool HasCanvas() const;
+ void ProvideCanvas (const css::uno::Reference<css::rendering::XCanvas>& rxCanvas);
+
+ OUString GetStyleName (const OUString& rsResourceURL) const;
+ ::std::vector<sal_Int32> GetBorderSize (
+ const OUString& rsStyleName,
+ const bool bOuter) const;
+
+ class Theme;
+ class FontDescriptor
+ {
+ public:
+ explicit FontDescriptor (const std::shared_ptr<FontDescriptor>& rpDescriptor);
+
+ OUString msFamilyName;
+ OUString msStyleName;
+ sal_Int32 mnSize;
+ sal_uInt32 mnColor;
+ OUString msAnchor;
+ sal_Int32 mnXOffset;
+ sal_Int32 mnYOffset;
+ css::uno::Reference<css::rendering::XCanvasFont> mxFont;
+
+ bool PrepareFont (const css::uno::Reference<css::rendering::XCanvas>& rxCanvas);
+
+ private:
+ css::uno::Reference<css::rendering::XCanvasFont> CreateFont (
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const double nCellSize) const;
+ double GetCellSizeForDesignSize (
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const double nDesignSize) const;
+ };
+ typedef std::shared_ptr<FontDescriptor> SharedFontDescriptor;
+
+ SharedBitmapDescriptor GetBitmap (
+ const OUString& rsStyleName,
+ const OUString& rsBitmapName) const;
+
+ SharedBitmapDescriptor GetBitmap (
+ const OUString& rsBitmapName) const;
+
+ std::shared_ptr<PresenterBitmapContainer> GetBitmapContainer() const;
+
+ SharedFontDescriptor GetFont (
+ const OUString& rsStyleName) const;
+
+ static SharedFontDescriptor ReadFont (
+ const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxNode,
+ const SharedFontDescriptor& rDefaultFount);
+
+ static bool ConvertToColor (
+ const css::uno::Any& rColorSequence,
+ sal_uInt32& rColor);
+
+ std::shared_ptr<PresenterConfigurationAccess> GetNodeForViewStyle (
+ const OUString& rsStyleName) const;
+
+private:
+ css::uno::Reference<css::uno::XComponentContext> mxContext;
+ std::shared_ptr<Theme> mpTheme;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+
+ std::shared_ptr<Theme> ReadTheme();
+};
+
+} // end of namespace ::sd::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterTimer.cxx b/sdext/source/presenter/PresenterTimer.cxx
new file mode 100644
index 000000000..b03df035c
--- /dev/null
+++ b/sdext/source/presenter/PresenterTimer.cxx
@@ -0,0 +1,571 @@
+/* -*- 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 "PresenterTimer.hxx"
+
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+
+#include <osl/thread.hxx>
+#include <osl/conditn.hxx>
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+#include <set>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sdext::presenter {
+
+namespace {
+class TimerTask
+{
+public:
+ TimerTask (
+ const PresenterTimer::Task& rTask,
+ const TimeValue& rDueTime,
+ const sal_Int64 nRepeatInterval,
+ const sal_Int32 nTaskId);
+
+ PresenterTimer::Task maTask;
+ TimeValue maDueTime;
+ const sal_Int64 mnRepeatInterval;
+ const sal_Int32 mnTaskId;
+ bool mbIsCanceled;
+};
+
+typedef std::shared_ptr<TimerTask> SharedTimerTask;
+
+class TimerTaskComparator
+{
+public:
+ bool operator() (const SharedTimerTask& rpTask1, const SharedTimerTask& rpTask2) const
+ {
+ return rpTask1->maDueTime.Seconds < rpTask2->maDueTime.Seconds
+ || (rpTask1->maDueTime.Seconds == rpTask2->maDueTime.Seconds
+ && rpTask1->maDueTime.Nanosec < rpTask2->maDueTime.Nanosec);
+ }
+};
+
+/** Queue all scheduled tasks and process them when their time has come.
+*/
+class TimerScheduler
+ : public std::enable_shared_from_this<TimerScheduler>,
+ public ::osl::Thread
+{
+public:
+ static std::shared_ptr<TimerScheduler> Instance(
+ uno::Reference<uno::XComponentContext> const& xContext);
+ static SharedTimerTask CreateTimerTask (
+ const PresenterTimer::Task& rTask,
+ const TimeValue& rDueTime,
+ const sal_Int64 nRepeatInterval);
+
+ void ScheduleTask (const SharedTimerTask& rpTask);
+ void CancelTask (const sal_Int32 nTaskId);
+
+ static bool GetCurrentTime (TimeValue& rCurrentTime);
+ static sal_Int64 GetTimeDifference (
+ const TimeValue& rTargetTime,
+ const TimeValue& rCurrentTime);
+ static void ConvertToTimeValue (
+ TimeValue& rTimeValue,
+ const sal_Int64 nTimeDifference);
+ static sal_Int64 ConvertFromTimeValue (
+ const TimeValue& rTimeValue);
+
+ static void NotifyTermination();
+#if !defined NDEBUG
+ static bool HasInstance() { return mpInstance != nullptr; }
+#endif
+
+private:
+ static std::shared_ptr<TimerScheduler> mpInstance;
+ static std::mutex maInstanceMutex;
+ std::shared_ptr<TimerScheduler> mpLateDestroy; // for clean exit
+ static sal_Int32 mnTaskId;
+
+ std::mutex maTaskContainerMutex;
+ typedef ::std::set<SharedTimerTask,TimerTaskComparator> TaskContainer;
+ TaskContainer maScheduledTasks;
+ std::mutex maCurrentTaskMutex;
+ SharedTimerTask mpCurrentTask;
+ ::osl::Condition m_Shutdown;
+
+ TimerScheduler(
+ uno::Reference<uno::XComponentContext> const& xContext);
+public:
+ virtual void SAL_CALL run() override;
+ virtual void SAL_CALL onTerminated() override { mpLateDestroy.reset(); }
+};
+
+class TerminateListener
+ : public ::cppu::WeakImplHelper<frame::XTerminateListener>
+{
+ virtual ~TerminateListener() override
+ {
+ assert(!TimerScheduler::HasInstance());
+ }
+
+ virtual void SAL_CALL disposing(lang::EventObject const&) override
+ {
+ }
+
+ virtual void SAL_CALL queryTermination(lang::EventObject const&) override
+ {
+ }
+
+ virtual void SAL_CALL notifyTermination(lang::EventObject const&) override
+ {
+ TimerScheduler::NotifyTermination();
+ }
+};
+
+} // end of anonymous namespace
+
+//===== PresenterTimer ========================================================
+
+sal_Int32 PresenterTimer::ScheduleRepeatedTask (
+ const uno::Reference<uno::XComponentContext>& xContext,
+ const Task& rTask,
+ const sal_Int64 nDelay,
+ const sal_Int64 nInterval)
+{
+ assert(xContext.is());
+ TimeValue aCurrentTime;
+ if (TimerScheduler::GetCurrentTime(aCurrentTime))
+ {
+ TimeValue aDueTime;
+ TimerScheduler::ConvertToTimeValue(
+ aDueTime,
+ TimerScheduler::ConvertFromTimeValue (aCurrentTime) + nDelay);
+ SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, aDueTime, nInterval));
+ TimerScheduler::Instance(xContext)->ScheduleTask(pTask);
+ return pTask->mnTaskId;
+ }
+
+ return NotAValidTaskId;
+}
+
+void PresenterTimer::CancelTask (const sal_Int32 nTaskId)
+{
+ auto const pInstance(TimerScheduler::Instance(nullptr));
+ if (pInstance)
+ {
+ pInstance->CancelTask(nTaskId);
+ }
+}
+
+//===== TimerScheduler ========================================================
+
+std::shared_ptr<TimerScheduler> TimerScheduler::mpInstance;
+std::mutex TimerScheduler::maInstanceMutex;
+sal_Int32 TimerScheduler::mnTaskId = PresenterTimer::NotAValidTaskId;
+
+std::shared_ptr<TimerScheduler> TimerScheduler::Instance(
+ uno::Reference<uno::XComponentContext> const& xContext)
+{
+ std::scoped_lock aGuard (maInstanceMutex);
+ if (mpInstance == nullptr)
+ {
+ if (!xContext.is())
+ return nullptr;
+ mpInstance.reset(new TimerScheduler(xContext));
+ mpInstance->create();
+ }
+ return mpInstance;
+}
+
+TimerScheduler::TimerScheduler(
+ uno::Reference<uno::XComponentContext> const& xContext)
+{
+ uno::Reference<frame::XDesktop> const xDesktop(
+ frame::Desktop::create(xContext));
+ uno::Reference<frame::XTerminateListener> const xListener(
+ new TerminateListener);
+ // assuming the desktop can take ownership
+ xDesktop->addTerminateListener(xListener);
+}
+
+SharedTimerTask TimerScheduler::CreateTimerTask (
+ const PresenterTimer::Task& rTask,
+ const TimeValue& rDueTime,
+ const sal_Int64 nRepeatInterval)
+{
+ return std::make_shared<TimerTask>(rTask, rDueTime, nRepeatInterval, ++mnTaskId);
+}
+
+void TimerScheduler::ScheduleTask (const SharedTimerTask& rpTask)
+{
+ if (!rpTask)
+ return;
+ if (rpTask->mbIsCanceled)
+ return;
+
+ {
+ std::scoped_lock aTaskGuard (maTaskContainerMutex);
+ maScheduledTasks.insert(rpTask);
+ }
+}
+
+void TimerScheduler::CancelTask (const sal_Int32 nTaskId)
+{
+ // Set of scheduled tasks is sorted after their due times, not their
+ // task ids. Therefore we have to do a linear search for the task to
+ // cancel.
+ {
+ std::scoped_lock aGuard (maTaskContainerMutex);
+ auto iTask = std::find_if(maScheduledTasks.begin(), maScheduledTasks.end(),
+ [nTaskId](const SharedTimerTask& rxTask) { return rxTask->mnTaskId == nTaskId; });
+ if (iTask != maScheduledTasks.end())
+ maScheduledTasks.erase(iTask);
+ }
+
+ // The task that is to be canceled may be currently about to be
+ // processed. Mark it with a flag that a) prevents a repeating task
+ // from being scheduled again and b) tries to prevent its execution.
+ {
+ std::scoped_lock aGuard (maCurrentTaskMutex);
+ if (mpCurrentTask
+ && mpCurrentTask->mnTaskId == nTaskId)
+ mpCurrentTask->mbIsCanceled = true;
+ }
+
+ // Let the main-loop cleanup in its own time
+}
+
+void TimerScheduler::NotifyTermination()
+{
+ std::shared_ptr<TimerScheduler> const pInstance(TimerScheduler::mpInstance);
+ if (!pInstance)
+ {
+ return;
+ }
+
+ {
+ std::scoped_lock aGuard(pInstance->maTaskContainerMutex);
+ pInstance->maScheduledTasks.clear();
+ }
+
+ {
+ std::scoped_lock aGuard(pInstance->maCurrentTaskMutex);
+ if (pInstance->mpCurrentTask)
+ {
+ pInstance->mpCurrentTask->mbIsCanceled = true;
+ }
+ }
+
+ pInstance->m_Shutdown.set();
+
+ // rhbz#1425304 join thread before shutdown
+ pInstance->join();
+}
+
+void SAL_CALL TimerScheduler::run()
+{
+ osl_setThreadName("sdext::presenter::TimerScheduler");
+
+ while (true)
+ {
+ // Get the current time.
+ TimeValue aCurrentTime;
+ if ( ! GetCurrentTime(aCurrentTime))
+ {
+ // We can not get the current time and thus can not schedule anything.
+ break;
+ }
+
+ // Restrict access to the maScheduledTasks member to one, mutex
+ // guarded, block.
+ SharedTimerTask pTask;
+ sal_Int64 nDifference = 0;
+ {
+ std::scoped_lock aGuard (maTaskContainerMutex);
+
+ // There are no more scheduled task. Leave this loop, function and
+ // live of the TimerScheduler.
+ if (maScheduledTasks.empty())
+ break;
+
+ nDifference = GetTimeDifference(
+ (*maScheduledTasks.begin())->maDueTime,
+ aCurrentTime);
+ if (nDifference <= 0)
+ {
+ pTask = *maScheduledTasks.begin();
+ maScheduledTasks.erase(maScheduledTasks.begin());
+ }
+ }
+
+ // Acquire a reference to the current task.
+ {
+ std::scoped_lock aGuard (maCurrentTaskMutex);
+ mpCurrentTask = pTask;
+ }
+
+ if (!pTask)
+ {
+ // Wait until the first task becomes due.
+ TimeValue aTimeValue;
+ ConvertToTimeValue(aTimeValue, nDifference);
+ // wait on condition variable, so the thread can be stopped
+ m_Shutdown.wait(&aTimeValue);
+ }
+ else
+ {
+ // Execute task.
+ if (pTask->maTask && !pTask->mbIsCanceled)
+ {
+ pTask->maTask(aCurrentTime);
+
+ // Re-schedule repeating tasks.
+ if (pTask->mnRepeatInterval > 0)
+ {
+ ConvertToTimeValue(
+ pTask->maDueTime,
+ ConvertFromTimeValue(pTask->maDueTime)
+ + pTask->mnRepeatInterval);
+ ScheduleTask(pTask);
+ }
+ }
+
+ }
+
+ // Release reference to the current task.
+ {
+ std::scoped_lock aGuard (maCurrentTaskMutex);
+ mpCurrentTask.reset();
+ }
+ }
+
+ // While holding maInstanceMutex
+ std::scoped_lock aInstance( maInstanceMutex );
+ mpLateDestroy = mpInstance;
+ mpInstance.reset();
+}
+
+bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime)
+{
+ TimeValue aSystemTime;
+ if (osl_getSystemTime(&aSystemTime))
+ return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime);
+ return false;
+}
+
+sal_Int64 TimerScheduler::GetTimeDifference (
+ const TimeValue& rTargetTime,
+ const TimeValue& rCurrentTime)
+{
+ return ConvertFromTimeValue(rTargetTime) - ConvertFromTimeValue(rCurrentTime);
+}
+
+void TimerScheduler::ConvertToTimeValue (
+ TimeValue& rTimeValue,
+ const sal_Int64 nTimeDifference)
+{
+ rTimeValue.Seconds = sal::static_int_cast<sal_Int32>(nTimeDifference / 1000000000L);
+ rTimeValue.Nanosec = sal::static_int_cast<sal_Int32>(nTimeDifference % 1000000000L);
+}
+
+sal_Int64 TimerScheduler::ConvertFromTimeValue (
+ const TimeValue& rTimeValue)
+{
+ return sal_Int64(rTimeValue.Seconds) * 1000000000L + rTimeValue.Nanosec;
+}
+
+//===== TimerTask =============================================================
+
+namespace {
+
+TimerTask::TimerTask (
+ const PresenterTimer::Task& rTask,
+ const TimeValue& rDueTime,
+ const sal_Int64 nRepeatInterval,
+ const sal_Int32 nTaskId)
+ : maTask(rTask),
+ maDueTime(rDueTime),
+ mnRepeatInterval(nRepeatInterval),
+ mnTaskId(nTaskId),
+ mbIsCanceled(false)
+{
+}
+
+} // end of anonymous namespace
+
+//===== PresenterTimer ========================================================
+
+::rtl::Reference<PresenterClockTimer> PresenterClockTimer::mpInstance;
+
+::rtl::Reference<PresenterClockTimer> PresenterClockTimer::Instance (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext)
+{
+ ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex());
+
+ ::rtl::Reference<PresenterClockTimer> pTimer;
+ if (mpInstance.is())
+ {
+ pTimer = mpInstance;
+ }
+ if ( ! pTimer.is())
+ {
+ pTimer.set(new PresenterClockTimer(rxContext));
+ mpInstance = pTimer;
+ }
+ return pTimer;
+}
+
+PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext)
+ : PresenterClockTimerInterfaceBase(m_aMutex),
+ maDateTime(),
+ mnTimerTaskId(PresenterTimer::NotAValidTaskId),
+ mbIsCallbackPending(false),
+ m_xContext(rxContext)
+{
+ assert(m_xContext.is());
+ Reference<lang::XMultiComponentFactory> xFactory =
+ rxContext->getServiceManager();
+ if (xFactory.is())
+ mxRequestCallback.set(
+ xFactory->createInstanceWithContext(
+ "com.sun.star.awt.AsyncCallback",
+ rxContext),
+ UNO_QUERY_THROW);
+}
+
+PresenterClockTimer::~PresenterClockTimer()
+{
+ if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
+ {
+ PresenterTimer::CancelTask(mnTimerTaskId);
+ mnTimerTaskId = PresenterTimer::NotAValidTaskId;
+ }
+
+ Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ mxRequestCallback = nullptr;
+}
+
+void PresenterClockTimer::AddListener (const SharedListener& rListener)
+{
+ osl::MutexGuard aGuard (maMutex);
+
+ maListeners.push_back(rListener);
+
+ // Create a timer task when the first listener is added.
+ if (mnTimerTaskId==PresenterTimer::NotAValidTaskId)
+ {
+ mnTimerTaskId = PresenterTimer::ScheduleRepeatedTask(
+ m_xContext,
+ [this] (TimeValue const& rTime) { return this->CheckCurrentTime(rTime); },
+ 0,
+ 250000000 /*ns*/);
+ }
+}
+
+void PresenterClockTimer::RemoveListener (const SharedListener& rListener)
+{
+ osl::MutexGuard aGuard (maMutex);
+
+ ListenerContainer::iterator iListener (::std::find(
+ maListeners.begin(),
+ maListeners.end(),
+ rListener));
+ if (iListener != maListeners.end())
+ maListeners.erase(iListener);
+ if (maListeners.empty())
+ {
+ // We have no more clients and therefore are not interested in time changes.
+ if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
+ {
+ PresenterTimer::CancelTask(mnTimerTaskId);
+ mnTimerTaskId = PresenterTimer::NotAValidTaskId;
+ }
+ mpInstance = nullptr;
+ }
+}
+
+oslDateTime PresenterClockTimer::GetCurrentTime()
+{
+ TimeValue aCurrentTime;
+ TimerScheduler::GetCurrentTime(aCurrentTime);
+ oslDateTime aDateTime;
+ osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime);
+ return aDateTime;
+}
+
+void PresenterClockTimer::CheckCurrentTime (const TimeValue& rCurrentTime)
+{
+ css::uno::Reference<css::awt::XRequestCallback> xRequestCallback;
+ css::uno::Reference<css::awt::XCallback> xCallback;
+ {
+ osl::MutexGuard aGuard (maMutex);
+
+ TimeValue aCurrentTime (rCurrentTime);
+ oslDateTime aDateTime;
+ if (osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime))
+ {
+ if (aDateTime.Seconds != maDateTime.Seconds
+ || aDateTime.Minutes != maDateTime.Minutes
+ || aDateTime.Hours != maDateTime.Hours)
+ {
+ // The displayed part of the current time has changed.
+ // Prepare to call the listeners.
+ maDateTime = aDateTime;
+
+ // Schedule notification of listeners.
+ if (mxRequestCallback.is() && ! mbIsCallbackPending)
+ {
+ mbIsCallbackPending = true;
+ xRequestCallback = mxRequestCallback;
+ xCallback = this;
+ }
+ }
+ }
+ }
+ if (xRequestCallback.is() && xCallback.is())
+ xRequestCallback->addCallback(xCallback, Any());
+}
+
+//----- XCallback -------------------------------------------------------------
+
+void SAL_CALL PresenterClockTimer::notify (const css::uno::Any&)
+{
+ ListenerContainer aListenerCopy;
+
+ {
+ osl::MutexGuard aGuard (maMutex);
+
+ mbIsCallbackPending = false;
+
+ aListenerCopy = maListeners;
+ }
+
+ for (const auto& rxListener : aListenerCopy)
+ {
+ rxListener->TimeHasChanged(maDateTime);
+ }
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterTimer.hxx b/sdext/source/presenter/PresenterTimer.hxx
new file mode 100644
index 000000000..1c2b6530b
--- /dev/null
+++ b/sdext/source/presenter/PresenterTimer.hxx
@@ -0,0 +1,122 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERTIMER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERTIMER_HXX
+
+#include <com/sun/star/awt/XCallback.hpp>
+#include <com/sun/star/awt/XRequestCallback.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <osl/mutex.hxx>
+#include <osl/time.h>
+#include <rtl/ref.hxx>
+#include <sal/types.h>
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace sdext::presenter {
+
+/** The timer allows tasks to be scheduled for execution at a specified time
+ in the future.
+*/
+class PresenterTimer
+{
+public:
+ /** A task is called with the current time.
+ */
+ typedef ::std::function<void (const TimeValue&)> Task;
+
+ static const sal_Int32 NotAValidTaskId = 0;
+
+ /** Schedule a task to be executed repeatedly. The task is executed the
+ first time after nFirst nano-seconds (1000000000 corresponds to one
+ second). After that task is executed in intervals that are
+ nInterval ns long until CancelTask is called.
+ */
+ static sal_Int32 ScheduleRepeatedTask (
+ const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ const Task& rTask,
+ const sal_Int64 nFirst,
+ const sal_Int64 nInterval);
+
+ static void CancelTask (const sal_Int32 nTaskId);
+};
+
+typedef cppu::WeakComponentImplHelper<
+ css::awt::XCallback
+ > PresenterClockTimerInterfaceBase;
+
+/** A timer that calls its listeners, typically clocks, every second to
+ update their current time value.
+*/
+class PresenterClockTimer
+ : protected ::cppu::BaseMutex,
+ public PresenterClockTimerInterfaceBase
+{
+public:
+ class Listener {
+ public:
+ virtual void TimeHasChanged (const oslDateTime& rCurrentTime) = 0;
+
+ protected:
+ ~Listener() {}
+ };
+ typedef std::shared_ptr<Listener> SharedListener;
+
+ static ::rtl::Reference<PresenterClockTimer> Instance (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext);
+
+ void AddListener (const SharedListener& rListener);
+ void RemoveListener (const SharedListener& rListener);
+
+ static oslDateTime GetCurrentTime();
+
+ // XCallback
+
+ virtual void SAL_CALL notify (const css::uno::Any& rUserData) override;
+
+private:
+ static ::rtl::Reference<PresenterClockTimer> mpInstance;
+
+ ::osl::Mutex maMutex;
+ typedef ::std::vector<SharedListener> ListenerContainer;
+ ListenerContainer maListeners;
+ oslDateTime maDateTime;
+ sal_Int32 mnTimerTaskId;
+ bool mbIsCallbackPending;
+ css::uno::Reference<css::awt::XRequestCallback> mxRequestCallback;
+ const css::uno::Reference<css::uno::XComponentContext> m_xContext;
+
+ PresenterClockTimer (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext);
+ virtual ~PresenterClockTimer() override;
+
+ void CheckCurrentTime (const TimeValue& rCurrentTime);
+};
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterToolBar.cxx b/sdext/source/presenter/PresenterToolBar.cxx
new file mode 100644
index 000000000..75a4b7500
--- /dev/null
+++ b/sdext/source/presenter/PresenterToolBar.cxx
@@ -0,0 +1,2015 @@
+/* -*- 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 <vcl/settings.hxx>
+#include "PresenterToolBar.hxx"
+
+#include "PresenterBitmapContainer.hxx"
+#include "PresenterCanvasHelper.hxx"
+#include "PresenterGeometryHelper.hxx"
+#include "PresenterPaintManager.hxx"
+#include "PresenterTimer.hxx"
+#include "PresenterWindowManager.hxx"
+
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <com/sun/star/drawing/framework/XPane.hpp>
+#include <com/sun/star/geometry/AffineMatrix2D.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/RenderState.hpp>
+#include <com/sun/star/rendering/TextDirection.hpp>
+#include <com/sun/star/rendering/ViewState.hpp>
+#include <com/sun/star/rendering/XSpriteCanvas.hpp>
+#include <com/sun/star/util/Color.hpp>
+#include <rtl/ustrbuf.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sdext::presenter {
+
+const sal_Int32 gnGapSize (20);
+
+namespace {
+
+ class Text
+ {
+ public:
+ Text();
+ Text (
+ const OUString& rsText,
+ const PresenterTheme::SharedFontDescriptor& rpFont);
+
+ void SetText (const OUString& rsText);
+ const OUString& GetText() const;
+ const PresenterTheme::SharedFontDescriptor& GetFont() const;
+
+ void Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const rendering::ViewState& rViewState,
+ const awt::Rectangle& rBoundingBox);
+
+ geometry::RealRectangle2D GetBoundingBox (
+ const Reference<rendering::XCanvas>& rxCanvas);
+
+ private:
+ OUString msText;
+ PresenterTheme::SharedFontDescriptor mpFont;
+ };
+
+ class ElementMode
+ {
+ public:
+ ElementMode();
+ ElementMode(const ElementMode&) = delete;
+ ElementMode& operator=(const ElementMode&) = delete;
+
+ SharedBitmapDescriptor mpIcon;
+ OUString msAction;
+ Text maText;
+
+ void ReadElementMode (
+ const Reference<beans::XPropertySet>& rxProperties,
+ const OUString& rsModeName,
+ std::shared_ptr<ElementMode> const & rpDefaultMode,
+ ::sdext::presenter::PresenterToolBar::Context const & rContext);
+ };
+ typedef std::shared_ptr<ElementMode> SharedElementMode;
+
+} // end of anonymous namespace
+
+class PresenterToolBar::Context
+{
+public:
+ Context() = default;
+ Context(const Context&) = delete;
+ Context& operator=(const Context&) = delete;
+ Reference<drawing::XPresenterHelper> mxPresenterHelper;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+};
+
+//===== PresenterToolBar::Element =============================================
+
+namespace {
+ typedef cppu::WeakComponentImplHelper<
+ css::document::XEventListener,
+ css::frame::XStatusListener
+ > ElementInterfaceBase;
+
+ class Element
+ : private ::cppu::BaseMutex,
+ public ElementInterfaceBase
+ {
+ public:
+ explicit Element (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
+ Element(const Element&) = delete;
+ Element& operator=(const Element&) = delete;
+
+ virtual void SAL_CALL disposing() override;
+
+ virtual void SetModes (
+ const SharedElementMode& rpNormalMode,
+ const SharedElementMode& rpMouseOverMode,
+ const SharedElementMode& rpSelectedMode,
+ const SharedElementMode& rpDisabledMode,
+ const SharedElementMode& rpMouseOverSelectedMode);
+ void CurrentSlideHasChanged();
+ void SetLocation (const awt::Point& rLocation);
+ void SetSize (const geometry::RealSize2D& rSize);
+ virtual void Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const rendering::ViewState& rViewState) = 0;
+ awt::Size const & GetBoundingSize (
+ const Reference<rendering::XCanvas>& rxCanvas);
+ awt::Rectangle GetBoundingBox() const;
+ virtual bool SetState (const bool bIsOver, const bool bIsPressed);
+ void Invalidate (const bool bSynchronous);
+ bool IsOutside (const awt::Rectangle& rBox);
+ virtual bool IsFilling() const;
+ void UpdateState();
+
+ // lang::XEventListener
+
+ virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override;
+
+ // document::XEventListener
+
+ virtual void SAL_CALL notifyEvent (const css::document::EventObject& rEvent) override;
+
+ // frame::XStatusListener
+
+ virtual void SAL_CALL statusChanged (const css::frame::FeatureStateEvent& rEvent) override;
+
+ protected:
+ ::rtl::Reference<PresenterToolBar> mpToolBar;
+ awt::Point maLocation;
+ awt::Size maSize;
+ SharedElementMode mpNormal;
+ SharedElementMode mpMouseOver;
+ SharedElementMode mpSelected;
+ SharedElementMode mpDisabled;
+ SharedElementMode mpMouseOverSelected;
+ SharedElementMode mpMode;
+ bool mbIsOver;
+ bool mbIsPressed;
+ bool mbIsSelected;
+
+ virtual awt::Size CreateBoundingSize (
+ const Reference<rendering::XCanvas>& rxCanvas) = 0;
+
+ bool IsEnabled() const { return mbIsEnabled;}
+ private:
+ bool mbIsEnabled;
+ };
+
+} // end of anonymous namespace
+
+class PresenterToolBar::ElementContainerPart
+ : public ::std::vector<rtl::Reference<Element> >
+{
+};
+
+//===== Button ================================================================
+
+namespace {
+
+ class Button : public Element
+ {
+ public:
+ static ::rtl::Reference<Element> Create (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar);
+
+ virtual void SAL_CALL disposing() override;
+
+ virtual void Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const rendering::ViewState& rViewState) override;
+
+ // lang::XEventListener
+
+ virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override;
+
+ protected:
+ virtual awt::Size CreateBoundingSize (
+ const Reference<rendering::XCanvas>& rxCanvas) override;
+
+ private:
+ bool mbIsListenerRegistered;
+
+ Button (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
+ void Initialize();
+ void PaintIcon (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const sal_Int32 nTextHeight,
+ const rendering::ViewState& rViewState);
+ PresenterBitmapDescriptor::Mode GetMode() const;
+ };
+
+//===== Label =================================================================
+
+ class Label : public Element
+ {
+ public:
+ explicit Label (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
+
+ void SetText (const OUString& rsText);
+ virtual void Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const rendering::ViewState& rViewState) override;
+ virtual bool SetState (const bool bIsOver, const bool bIsPressed) override;
+
+ protected:
+ virtual awt::Size CreateBoundingSize (
+ const Reference<rendering::XCanvas>& rxCanvas) override;
+ };
+
+// Some specialized controls.
+
+ class TimeFormatter
+ {
+ public:
+ static OUString FormatTime (const oslDateTime& rTime);
+ };
+
+ class TimeLabel : public Label
+ {
+ public:
+ void ConnectToTimer();
+ virtual void TimeHasChanged (const oslDateTime& rCurrentTime) = 0;
+ protected:
+ explicit TimeLabel(const ::rtl::Reference<PresenterToolBar>& rpToolBar);
+ using Element::disposing;
+ virtual void SAL_CALL disposing() override;
+ private:
+ class Listener : public PresenterClockTimer::Listener
+ {
+ public:
+ explicit Listener (const ::rtl::Reference<TimeLabel>& rxLabel)
+ : mxLabel(rxLabel) {}
+ virtual ~Listener() {}
+ virtual void TimeHasChanged (const oslDateTime& rCurrentTime) override
+ { if (mxLabel.is()) mxLabel->TimeHasChanged(rCurrentTime); }
+ private:
+ ::rtl::Reference<TimeLabel> mxLabel;
+ };
+ std::shared_ptr<PresenterClockTimer::Listener> mpListener;
+ };
+
+ class CurrentTimeLabel : public TimeLabel
+ {
+ public:
+ static ::rtl::Reference<Element> Create (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar);
+ virtual void SetModes (
+ const SharedElementMode& rpNormalMode,
+ const SharedElementMode& rpMouseOverMode,
+ const SharedElementMode& rpSelectedMode,
+ const SharedElementMode& rpDisabledMode,
+ const SharedElementMode& rpMouseOverSelectedMode) override;
+ private:
+ CurrentTimeLabel (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
+ virtual ~CurrentTimeLabel() override;
+ virtual void TimeHasChanged (const oslDateTime& rCurrentTime) override;
+ };
+
+ class PresentationTimeLabel : public TimeLabel, public IPresentationTime
+ {
+ public:
+ static ::rtl::Reference<Element> Create (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar);
+ virtual void SetModes (
+ const SharedElementMode& rpNormalMode,
+ const SharedElementMode& rpMouseOverMode,
+ const SharedElementMode& rpSelectedMode,
+ const SharedElementMode& rpDisabledMode,
+ const SharedElementMode& rpMouseOverSelectedMode) override;
+ virtual void restart() override;
+ virtual bool isPaused() override;
+ virtual void setPauseStatus(const bool pauseStatus) override;
+ const TimeValue& getPauseTimeValue() const;
+ void setPauseTimeValue(const TimeValue pauseTime);
+ private:
+ TimeValue maStartTimeValue;
+ TimeValue pauseTimeValue;
+ PresentationTimeLabel (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
+ bool paused;
+ virtual ~PresentationTimeLabel() override;
+ virtual void TimeHasChanged (const oslDateTime& rCurrentTime) override;
+ };
+
+ class VerticalSeparator : public Element
+ {
+ public:
+ explicit VerticalSeparator (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
+ virtual void Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const rendering::ViewState& rViewState) override;
+ virtual bool IsFilling() const override;
+
+ protected:
+ virtual awt::Size CreateBoundingSize (
+ const Reference<rendering::XCanvas>& rxCanvas) override;
+ };
+
+ class HorizontalSeparator : public Element
+ {
+ public:
+ explicit HorizontalSeparator (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
+ virtual void Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const rendering::ViewState& rViewState) override;
+ virtual bool IsFilling() const override;
+
+ protected:
+ virtual awt::Size CreateBoundingSize (
+ const Reference<rendering::XCanvas>& rxCanvas) override;
+ };
+} // end of anonymous namespace
+
+//===== PresenterToolBar ======================================================
+
+PresenterToolBar::PresenterToolBar (
+ const Reference<XComponentContext>& rxContext,
+ const css::uno::Reference<css::awt::XWindow>& rxWindow,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const ::rtl::Reference<PresenterController>& rpPresenterController,
+ const Anchor eAnchor)
+ : PresenterToolBarInterfaceBase(m_aMutex),
+ mxComponentContext(rxContext),
+ mxWindow(rxWindow),
+ mxCanvas(rxCanvas),
+ mpPresenterController(rpPresenterController),
+ mbIsLayoutPending(false),
+ meAnchor(eAnchor)
+{
+}
+
+void PresenterToolBar::Initialize (
+ const OUString& rsConfigurationPath)
+{
+ try
+ {
+ CreateControls(rsConfigurationPath);
+
+ if (mxWindow.is())
+ {
+ mxWindow->addWindowListener(this);
+ mxWindow->addPaintListener(this);
+ mxWindow->addMouseListener(this);
+ mxWindow->addMouseMotionListener(this);
+
+ Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY);
+ if (xPeer.is())
+ xPeer->setBackground(util::Color(0xff000000));
+
+ mxWindow->setVisible(true);
+ }
+
+ mxSlideShowController = mpPresenterController->GetSlideShowController();
+ UpdateSlideNumber();
+ mbIsLayoutPending = true;
+ }
+ catch (RuntimeException&)
+ {
+ mpCurrentContainerPart.reset();
+ maElementContainer.clear();
+ throw;
+ }
+}
+
+PresenterToolBar::~PresenterToolBar()
+{
+}
+
+void SAL_CALL PresenterToolBar::disposing()
+{
+ if (mxWindow.is())
+ {
+ mxWindow->removeWindowListener(this);
+ mxWindow->removePaintListener(this);
+ mxWindow->removeMouseListener(this);
+ mxWindow->removeMouseMotionListener(this);
+ mxWindow = nullptr;
+ }
+
+ // Dispose tool bar elements.
+ for (const auto& rxPart : maElementContainer)
+ {
+ OSL_ASSERT(rxPart != nullptr);
+ for (const rtl::Reference<Element>& pElement : *rxPart)
+ {
+ if (pElement)
+ {
+ Reference<lang::XComponent> xComponent = pElement;
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ }
+ }
+
+ mpCurrentContainerPart.reset();
+ maElementContainer.clear();
+}
+
+void PresenterToolBar::InvalidateArea (
+ const awt::Rectangle& rRepaintBox,
+ const bool bSynchronous)
+{
+ std::shared_ptr<PresenterPaintManager> xManager(mpPresenterController->GetPaintManager());
+ if (!xManager)
+ return;
+ xManager->Invalidate(
+ mxWindow,
+ rRepaintBox,
+ bSynchronous);
+}
+
+void PresenterToolBar::RequestLayout()
+{
+ mbIsLayoutPending = true;
+
+ std::shared_ptr<PresenterPaintManager> xManager(mpPresenterController->GetPaintManager());
+ if (!xManager)
+ return;
+
+ xManager->Invalidate(mxWindow);
+}
+
+geometry::RealSize2D const & PresenterToolBar::GetMinimalSize()
+{
+ if (mbIsLayoutPending)
+ Layout(mxCanvas);
+ return maMinimalSize;
+}
+
+const ::rtl::Reference<PresenterController>& PresenterToolBar::GetPresenterController() const
+{
+ return mpPresenterController;
+}
+
+const Reference<XComponentContext>& PresenterToolBar::GetComponentContext() const
+{
+ return mxComponentContext;
+}
+
+//----- lang::XEventListener -------------------------------------------------
+
+void SAL_CALL PresenterToolBar::disposing (const lang::EventObject& rEventObject)
+{
+ if (rEventObject.Source == mxWindow)
+ mxWindow = nullptr;
+}
+
+//----- XWindowListener -------------------------------------------------------
+
+void SAL_CALL PresenterToolBar::windowResized (const awt::WindowEvent&)
+{
+ mbIsLayoutPending = true;
+}
+
+void SAL_CALL PresenterToolBar::windowMoved (const awt::WindowEvent&) {}
+
+void SAL_CALL PresenterToolBar::windowShown (const lang::EventObject&)
+{
+ mbIsLayoutPending = true;
+}
+
+void SAL_CALL PresenterToolBar::windowHidden (const lang::EventObject&) {}
+
+//----- XPaintListener --------------------------------------------------------
+void SAL_CALL PresenterToolBar::windowPaint (const css::awt::PaintEvent& rEvent)
+{
+ if ( ! mxCanvas.is())
+ return;
+
+ if ( ! mbIsPresenterViewActive)
+ return;
+
+ const rendering::ViewState aViewState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ PresenterGeometryHelper::CreatePolygon(rEvent.UpdateRect, mxCanvas->getDevice()));
+
+ if (mbIsLayoutPending)
+ Layout(mxCanvas);
+
+ Paint(rEvent.UpdateRect, aViewState);
+
+ // Make the back buffer visible.
+ Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
+ if (xSpriteCanvas.is())
+ xSpriteCanvas->updateScreen(false);
+}
+
+//----- XMouseListener --------------------------------------------------------
+void SAL_CALL PresenterToolBar::mousePressed (const css::awt::MouseEvent& rEvent)
+{
+ ThrowIfDisposed();
+ CheckMouseOver(rEvent, true, true);
+}
+
+void SAL_CALL PresenterToolBar::mouseReleased (const css::awt::MouseEvent& rEvent)
+{
+ ThrowIfDisposed();
+ CheckMouseOver(rEvent, true);
+}
+
+void SAL_CALL PresenterToolBar::mouseEntered (const css::awt::MouseEvent& rEvent)
+{
+ ThrowIfDisposed();
+ CheckMouseOver(rEvent, true);
+}
+
+void SAL_CALL PresenterToolBar::mouseExited (const css::awt::MouseEvent& rEvent)
+{
+ ThrowIfDisposed();
+ CheckMouseOver(rEvent, false);
+ }
+
+//----- XMouseMotionListener --------------------------------------------------
+
+void SAL_CALL PresenterToolBar::mouseMoved (const css::awt::MouseEvent& rEvent)
+{
+ ThrowIfDisposed();
+ CheckMouseOver(rEvent, true);
+ }
+
+void SAL_CALL PresenterToolBar::mouseDragged (const css::awt::MouseEvent&)
+{
+ ThrowIfDisposed();
+}
+
+//----- XDrawView -------------------------------------------------------------
+
+void SAL_CALL PresenterToolBar::setCurrentPage (const Reference<drawing::XDrawPage>& rxSlide)
+{
+ if (rxSlide != mxCurrentSlide)
+ {
+ mxCurrentSlide = rxSlide;
+ UpdateSlideNumber();
+ }
+}
+
+Reference<drawing::XDrawPage> SAL_CALL PresenterToolBar::getCurrentPage()
+{
+ return mxCurrentSlide;
+}
+
+
+void PresenterToolBar::CreateControls (
+ const OUString& rsConfigurationPath)
+{
+ if ( ! mxWindow.is())
+ return;
+
+ // Expand the macro in the bitmap file names.
+ PresenterConfigurationAccess aConfiguration (
+ mxComponentContext,
+ "/org.openoffice.Office.PresenterScreen/",
+ PresenterConfigurationAccess::READ_ONLY);
+
+ mpCurrentContainerPart = std::make_shared<ElementContainerPart>();
+ maElementContainer.clear();
+ maElementContainer.push_back(mpCurrentContainerPart);
+
+ Reference<container::XHierarchicalNameAccess> xToolBarNode (
+ aConfiguration.GetConfigurationNode(rsConfigurationPath),
+ UNO_QUERY);
+ if (!xToolBarNode.is())
+ return;
+
+ Reference<container::XNameAccess> xEntries (
+ PresenterConfigurationAccess::GetConfigurationNode(xToolBarNode, "Entries"),
+ UNO_QUERY);
+ Context aContext;
+ aContext.mxPresenterHelper = mpPresenterController->GetPresenterHelper();
+ aContext.mxCanvas = mxCanvas;
+ if (xEntries.is()
+ && aContext.mxPresenterHelper.is()
+ && aContext.mxCanvas.is())
+ {
+ PresenterConfigurationAccess::ForAll(
+ xEntries,
+ [this, &aContext] (OUString const&, uno::Reference<beans::XPropertySet> const& xProps)
+ {
+ return this->ProcessEntry(xProps, aContext);
+ });
+ }
+}
+
+void PresenterToolBar::ProcessEntry (
+ const Reference<beans::XPropertySet>& rxProperties,
+ Context const & rContext)
+{
+ if ( ! rxProperties.is())
+ return;
+
+ // Type has to be present.
+ OUString sType;
+ if ( ! (PresenterConfigurationAccess::GetProperty(rxProperties, "Type") >>= sType))
+ return;
+
+ // Read mode specific values.
+ SharedElementMode pNormalMode = std::make_shared<ElementMode>();
+ SharedElementMode pMouseOverMode = std::make_shared<ElementMode>();
+ SharedElementMode pSelectedMode = std::make_shared<ElementMode>();
+ SharedElementMode pDisabledMode = std::make_shared<ElementMode>();
+ SharedElementMode pMouseOverSelectedMode = std::make_shared<ElementMode>();
+ pNormalMode->ReadElementMode(rxProperties, "Normal", pNormalMode, rContext);
+ pMouseOverMode->ReadElementMode(rxProperties, "MouseOver", pNormalMode, rContext);
+ pSelectedMode->ReadElementMode(rxProperties, "Selected", pNormalMode, rContext);
+ pDisabledMode->ReadElementMode(rxProperties, "Disabled", pNormalMode, rContext);
+ pMouseOverSelectedMode->ReadElementMode(rxProperties, "MouseOverSelected", pSelectedMode, rContext);
+
+ // Create new element.
+ ::rtl::Reference<Element> pElement;
+ if ( sType == "Button" )
+ pElement = Button::Create(this);
+ else if ( sType == "CurrentTimeLabel" )
+ pElement = CurrentTimeLabel::Create(this);
+ else if ( sType == "PresentationTimeLabel" )
+ pElement = PresentationTimeLabel::Create(this);
+ else if ( sType == "VerticalSeparator" )
+ pElement.set(new VerticalSeparator(this));
+ else if ( sType == "HorizontalSeparator" )
+ pElement.set(new HorizontalSeparator(this));
+ else if ( sType == "Label" )
+ pElement.set(new Label(this));
+ else if ( sType == "ChangeOrientation" )
+ {
+ mpCurrentContainerPart = std::make_shared<ElementContainerPart>();
+ maElementContainer.push_back(mpCurrentContainerPart);
+ return;
+ }
+ if (pElement.is())
+ {
+ pElement->SetModes( pNormalMode, pMouseOverMode, pSelectedMode, pDisabledMode, pMouseOverSelectedMode);
+ pElement->UpdateState();
+ if (mpCurrentContainerPart)
+ mpCurrentContainerPart->push_back(pElement);
+ }
+}
+
+void PresenterToolBar::Layout (
+ const Reference<rendering::XCanvas>& rxCanvas)
+{
+ if (maElementContainer.empty())
+ return;
+
+ mbIsLayoutPending = false;
+
+ const awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ ::std::vector<geometry::RealSize2D> aPartSizes (maElementContainer.size());
+ geometry::RealSize2D aTotalSize (0,0);
+ bool bIsHorizontal (true);
+ sal_Int32 nIndex (0);
+ double nTotalHorizontalGap (0);
+ sal_Int32 nGapCount (0);
+ for (const auto& rxPart : maElementContainer)
+ {
+ geometry::RealSize2D aSize (CalculatePartSize(rxCanvas, rxPart, bIsHorizontal));
+
+ // Remember the size of each part for later.
+ aPartSizes[nIndex] = aSize;
+
+ // Add gaps between elements.
+ if (rxPart->size()>1 && bIsHorizontal)
+ {
+ nTotalHorizontalGap += (rxPart->size() - 1) * gnGapSize;
+ nGapCount += rxPart->size() - 1;
+ }
+
+ // Orientation changes for each part.
+ bIsHorizontal = !bIsHorizontal;
+ // Width is accumulated.
+ aTotalSize.Width += aSize.Width;
+ // Height is the maximum height of all parts.
+ aTotalSize.Height = ::std::max(aTotalSize.Height, aSize.Height);
+ ++nIndex;
+ }
+ // Add gaps between parts.
+ if (maElementContainer.size() > 1)
+ {
+ nTotalHorizontalGap += (maElementContainer.size() - 1) * gnGapSize;
+ nGapCount += maElementContainer.size()-1;
+ }
+
+ // Done to introduce gap between the end of the toolbar and the last button
+ aTotalSize.Width += gnGapSize/2;
+
+ // Calculate the minimal size so that the window size of the tool bar
+ // can be adapted accordingly.
+ maMinimalSize = aTotalSize;
+ maMinimalSize.Width += nTotalHorizontalGap;
+
+ // Calculate the gaps between elements.
+ double nGapWidth (0);
+ if (nGapCount > 0)
+ {
+ if (aTotalSize.Width + nTotalHorizontalGap > aWindowBox.Width)
+ nTotalHorizontalGap = aWindowBox.Width - aTotalSize.Width;
+ nGapWidth = nTotalHorizontalGap / nGapCount;
+ }
+
+ // Determine the location of the left edge.
+ double nX (0);
+ switch (meAnchor)
+ {
+ case Left : nX = 0; break;
+ case Center: nX = (aWindowBox.Width - aTotalSize.Width - nTotalHorizontalGap) / 2; break;
+ }
+
+ // Place the parts.
+ double nY ((aWindowBox.Height - aTotalSize.Height) / 2);
+ bIsHorizontal = true;
+
+ /* push front or back ? ... */
+ /// check whether RTL interface or not
+ if(!AllSettings::GetLayoutRTL()){
+ nIndex = 0;
+ for (const auto& rxPart : maElementContainer)
+ {
+ geometry::RealRectangle2D aBoundingBox(
+ nX, nY,
+ nX+aPartSizes[nIndex].Width, nY+aTotalSize.Height);
+
+ // Add space for gaps between elements.
+ if (rxPart->size() > 1 && bIsHorizontal)
+ aBoundingBox.X2 += (rxPart->size() - 1) * nGapWidth;
+
+ LayoutPart(rxCanvas, rxPart, aBoundingBox, aPartSizes[nIndex], bIsHorizontal);
+ bIsHorizontal = !bIsHorizontal;
+ nX += aBoundingBox.X2 - aBoundingBox.X1 + nGapWidth;
+ ++nIndex;
+ }
+ }
+ else {
+ ElementContainer::iterator iPart;
+ ElementContainer::iterator iBegin (maElementContainer.begin());
+ for (iPart=maElementContainer.end()-1, nIndex=2; iPart!=iBegin-1; --iPart, --nIndex)
+ {
+ geometry::RealRectangle2D aBoundingBox(
+ nX, nY,
+ nX+aPartSizes[nIndex].Width, nY+aTotalSize.Height);
+
+ // Add space for gaps between elements.
+ if ((*iPart)->size() > 1)
+ if (bIsHorizontal)
+ aBoundingBox.X2 += ((*iPart)->size()-1) * nGapWidth;
+
+ LayoutPart(rxCanvas, *iPart, aBoundingBox, aPartSizes[nIndex], bIsHorizontal);
+ bIsHorizontal = !bIsHorizontal;
+ nX += aBoundingBox.X2 - aBoundingBox.X1 + nGapWidth;
+ }
+ }
+
+ // The whole window has to be repainted.
+ std::shared_ptr<PresenterPaintManager> xManager(mpPresenterController->GetPaintManager());
+ if (!xManager)
+ return;
+ xManager->Invalidate(mxWindow);
+}
+
+geometry::RealSize2D PresenterToolBar::CalculatePartSize (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const SharedElementContainerPart& rpPart,
+ const bool bIsHorizontal)
+{
+ geometry::RealSize2D aTotalSize (0,0);
+
+ if (mxWindow.is())
+ {
+ // Calculate the summed width of all elements.
+ for (const auto& rxElement : *rpPart)
+ {
+ if (!rxElement)
+ continue;
+
+ const awt::Size aBSize (rxElement->GetBoundingSize(rxCanvas));
+ if (bIsHorizontal)
+ {
+ aTotalSize.Width += aBSize.Width;
+ if (aBSize.Height > aTotalSize.Height)
+ aTotalSize.Height = aBSize.Height;
+ }
+ else
+ {
+ aTotalSize.Height += aBSize.Height;
+ if (aBSize.Width > aTotalSize.Width)
+ aTotalSize.Width = aBSize.Width;
+ }
+ }
+ }
+ return aTotalSize;
+}
+
+void PresenterToolBar::LayoutPart (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const SharedElementContainerPart& rpPart,
+ const geometry::RealRectangle2D& rBoundingBox,
+ const geometry::RealSize2D& rPartSize,
+ const bool bIsHorizontal)
+{
+ double nGap (0);
+ if (rpPart->size() > 1)
+ {
+ if (bIsHorizontal)
+ nGap = (rBoundingBox.X2 - rBoundingBox.X1 - rPartSize.Width) / (rpPart->size()-1);
+ else
+ nGap = (rBoundingBox.Y2 - rBoundingBox.Y1 - rPartSize.Height) / (rpPart->size()-1);
+ }
+
+ // Place the elements.
+ double nX (rBoundingBox.X1);
+ double nY (rBoundingBox.Y1);
+
+ /// check whether RTL interface or not
+ if(!AllSettings::GetLayoutRTL()){
+ for (auto& rxElement : *rpPart)
+ {
+ if (!rxElement)
+ continue;
+
+ const awt::Size aElementSize (rxElement->GetBoundingSize(rxCanvas));
+ if (bIsHorizontal)
+ {
+ if (rxElement->IsFilling())
+ {
+ nY = rBoundingBox.Y1;
+ rxElement->SetSize(geometry::RealSize2D(aElementSize.Width, rBoundingBox.Y2 - rBoundingBox.Y1));
+ }
+ else
+ nY = rBoundingBox.Y1 + (rBoundingBox.Y2-rBoundingBox.Y1 - aElementSize.Height) / 2;
+ rxElement->SetLocation(awt::Point(sal_Int32(0.5 + nX), sal_Int32(0.5 + nY)));
+ nX += aElementSize.Width + nGap;
+ }
+ else
+ {
+ if (rxElement->IsFilling())
+ {
+ nX = rBoundingBox.X1;
+ rxElement->SetSize(geometry::RealSize2D(rBoundingBox.X2 - rBoundingBox.X1, aElementSize.Height));
+ }
+ else
+ nX = rBoundingBox.X1 + (rBoundingBox.X2-rBoundingBox.X1 - aElementSize.Width) / 2;
+ rxElement->SetLocation(awt::Point(sal_Int32(0.5 + nX), sal_Int32(0.5 + nY)));
+ nY += aElementSize.Height + nGap;
+ }
+ }
+ }
+ else {
+ ElementContainerPart::const_iterator iElement;
+ ElementContainerPart::const_iterator iBegin (rpPart->begin());
+
+ for (iElement=rpPart->end()-1; iElement!=iBegin-1; --iElement)
+ {
+ if (iElement->get() == nullptr)
+ continue;
+
+ const awt::Size aElementSize ((*iElement)->GetBoundingSize(rxCanvas));
+ if (bIsHorizontal)
+ {
+ if ((*iElement)->IsFilling())
+ {
+ nY = rBoundingBox.Y1;
+ (*iElement)->SetSize(geometry::RealSize2D(aElementSize.Width, rBoundingBox.Y2 - rBoundingBox.Y1));
+ }
+ else
+ nY = rBoundingBox.Y1 + (rBoundingBox.Y2-rBoundingBox.Y1 - aElementSize.Height) / 2;
+ (*iElement)->SetLocation(awt::Point(sal_Int32(0.5 + nX), sal_Int32(0.5 + nY)));
+ nX += aElementSize.Width + nGap;
+ }
+ else
+ {
+ // reverse presentation time with current time
+ if (iElement==iBegin){
+ iElement=iBegin+2;
+ }
+ else if (iElement==iBegin+2){
+ iElement=iBegin;
+ }
+ const awt::Size aNewElementSize ((*iElement)->GetBoundingSize(rxCanvas));
+ if ((*iElement)->IsFilling())
+ {
+ nX = rBoundingBox.X1;
+ (*iElement)->SetSize(geometry::RealSize2D(rBoundingBox.X2 - rBoundingBox.X1, aNewElementSize.Height));
+ }
+ else
+ nX = rBoundingBox.X1 + (rBoundingBox.X2-rBoundingBox.X1 - aNewElementSize.Width) / 2;
+ (*iElement)->SetLocation(awt::Point(sal_Int32(0.5 + nX), sal_Int32(0.5 + nY)));
+ nY += aNewElementSize.Height + nGap;
+
+ // return the index as it was before the reversing
+ if (iElement==iBegin)
+ iElement=iBegin+2;
+ else if (iElement==iBegin+2)
+ iElement=iBegin;
+ }
+ }
+ }
+}
+
+void PresenterToolBar::Paint (
+ const awt::Rectangle& rUpdateBox,
+ const rendering::ViewState& rViewState)
+{
+ OSL_ASSERT(mxCanvas.is());
+
+ for (const auto& rxPart : maElementContainer)
+ {
+ for (auto& rxElement : *rxPart)
+ {
+ if (rxElement)
+ {
+ if ( ! rxElement->IsOutside(rUpdateBox))
+ rxElement->Paint(mxCanvas, rViewState);
+ }
+ }
+ }
+}
+
+void PresenterToolBar::UpdateSlideNumber()
+{
+ if( mxSlideShowController.is() )
+ {
+ for (const auto& rxPart : maElementContainer)
+ {
+ for (auto& rxElement : *rxPart)
+ {
+ if (rxElement)
+ rxElement->CurrentSlideHasChanged();
+ }
+ }
+ }
+}
+
+void PresenterToolBar::CheckMouseOver (
+ const css::awt::MouseEvent& rEvent,
+ const bool bOverWindow,
+ const bool bMouseDown)
+{
+ css::awt::MouseEvent rTemp =rEvent;
+ if(AllSettings::GetLayoutRTL()){
+ awt::Rectangle aWindowBox = mxWindow->getPosSize();
+ rTemp.X=aWindowBox.Width-rTemp.X;
+ }
+ for (const auto& rxPart : maElementContainer)
+ {
+ for (auto& rxElement : *rxPart)
+ {
+ if (!rxElement)
+ continue;
+
+ awt::Rectangle aBox (rxElement->GetBoundingBox());
+ const bool bIsOver = bOverWindow
+ && aBox.X <= rTemp.X
+ && aBox.Width+aBox.X-1 >= rTemp.X
+ && aBox.Y <= rTemp.Y
+ && aBox.Height+aBox.Y-1 >= rTemp.Y;
+ rxElement->SetState(
+ bIsOver,
+ bIsOver && rTemp.Buttons!=0 && bMouseDown && rTemp.ClickCount>0);
+ }
+ }
+}
+
+void PresenterToolBar::ThrowIfDisposed() const
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterToolBar has already been disposed",
+ const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
+ }
+}
+
+//===== PresenterToolBarView ==================================================
+
+PresenterToolBarView::PresenterToolBarView (
+ const Reference<XComponentContext>& rxContext,
+ const Reference<XResourceId>& rxViewId,
+ const Reference<frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterToolBarViewInterfaceBase(m_aMutex),
+ mxViewId(rxViewId),
+ mpPresenterController(rpPresenterController)
+{
+ try
+ {
+ Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW);
+ Reference<XConfigurationController> xCC(xCM->getConfigurationController(),UNO_SET_THROW);
+ mxPane.set(xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW);
+
+ mxWindow = mxPane->getWindow();
+ mxCanvas = mxPane->getCanvas();
+
+ mpToolBar = new PresenterToolBar(
+ rxContext,
+ mxWindow,
+ mxCanvas,
+ rpPresenterController,
+ PresenterToolBar::Center);
+ mpToolBar->Initialize("PresenterScreenSettings/ToolBars/ToolBar");
+
+ if (mxWindow.is())
+ {
+ mxWindow->addPaintListener(this);
+
+ Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY);
+ if (xPeer.is())
+ xPeer->setBackground(util::Color(0xff000000));
+
+ mxWindow->setVisible(true);
+ }
+ }
+ catch (RuntimeException&)
+ {
+ mxViewId = nullptr;
+ throw;
+ }
+}
+
+PresenterToolBarView::~PresenterToolBarView()
+{
+}
+
+void SAL_CALL PresenterToolBarView::disposing()
+{
+ Reference<lang::XComponent> xComponent = mpToolBar;
+ mpToolBar = nullptr;
+ if (xComponent.is())
+ xComponent->dispose();
+
+ if (mxWindow.is())
+ {
+ mxWindow->removePaintListener(this);
+ mxWindow = nullptr;
+ }
+ mxCanvas = nullptr;
+ mxViewId = nullptr;
+ mxPane = nullptr;
+ mpPresenterController = nullptr;
+}
+
+const ::rtl::Reference<PresenterToolBar>& PresenterToolBarView::GetPresenterToolBar() const
+{
+ return mpToolBar;
+}
+
+//----- XPaintListener --------------------------------------------------------
+
+void SAL_CALL PresenterToolBarView::windowPaint (const css::awt::PaintEvent& rEvent)
+{
+ awt::Rectangle aWindowBox (mxWindow->getPosSize());
+ mpPresenterController->GetCanvasHelper()->Paint(
+ mpPresenterController->GetViewBackground(mxViewId->getResourceURL()),
+ mxCanvas,
+ rEvent.UpdateRect,
+ awt::Rectangle(0,0,aWindowBox.Width, aWindowBox.Height),
+ awt::Rectangle());
+}
+
+//----- lang::XEventListener -------------------------------------------------
+
+void SAL_CALL PresenterToolBarView::disposing (const lang::EventObject& rEventObject)
+{
+ if (rEventObject.Source == mxWindow)
+ mxWindow = nullptr;
+}
+
+//----- XResourceId -----------------------------------------------------------
+
+Reference<XResourceId> SAL_CALL PresenterToolBarView::getResourceId()
+{
+ return mxViewId;
+}
+
+sal_Bool SAL_CALL PresenterToolBarView::isAnchorOnly()
+{
+ return false;
+}
+
+//----- XDrawView -------------------------------------------------------------
+
+void SAL_CALL PresenterToolBarView::setCurrentPage (const Reference<drawing::XDrawPage>& rxSlide)
+{
+ Reference<drawing::XDrawView> xToolBar = mpToolBar;
+ if (xToolBar.is())
+ xToolBar->setCurrentPage(rxSlide);
+}
+
+Reference<drawing::XDrawPage> SAL_CALL PresenterToolBarView::getCurrentPage()
+{
+ return nullptr;
+}
+
+//===== PresenterToolBar::Element =============================================
+
+namespace {
+
+Element::Element (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar)
+ : ElementInterfaceBase(m_aMutex),
+ mpToolBar(rpToolBar),
+ mbIsOver(false),
+ mbIsPressed(false),
+ mbIsSelected(false),
+ mbIsEnabled(true)
+{
+ if (mpToolBar)
+ {
+ OSL_ASSERT(mpToolBar->GetPresenterController().is());
+ OSL_ASSERT(mpToolBar->GetPresenterController()->GetWindowManager().is());
+ }
+}
+
+void Element::SetModes (
+ const SharedElementMode& rpNormalMode,
+ const SharedElementMode& rpMouseOverMode,
+ const SharedElementMode& rpSelectedMode,
+ const SharedElementMode& rpDisabledMode,
+ const SharedElementMode& rpMouseOverSelectedMode)
+{
+ mpNormal = rpNormalMode;
+ mpMouseOver = rpMouseOverMode;
+ mpSelected = rpSelectedMode;
+ mpDisabled = rpDisabledMode;
+ mpMouseOverSelected = rpMouseOverSelectedMode;
+ mpMode = rpNormalMode;
+}
+
+void Element::disposing()
+{
+}
+
+awt::Size const & Element::GetBoundingSize (
+ const Reference<rendering::XCanvas>& rxCanvas)
+{
+ maSize = CreateBoundingSize(rxCanvas);
+ return maSize;
+}
+
+awt::Rectangle Element::GetBoundingBox() const
+{
+ return awt::Rectangle(maLocation.X,maLocation.Y, maSize.Width, maSize.Height);
+}
+
+void Element::CurrentSlideHasChanged()
+{
+ UpdateState();
+}
+
+void Element::SetLocation (const awt::Point& rLocation)
+{
+ maLocation = rLocation;
+}
+
+void Element::SetSize (const geometry::RealSize2D& rSize)
+{
+ maSize = awt::Size(sal_Int32(0.5+rSize.Width), sal_Int32(0.5+rSize.Height));
+}
+
+bool Element::SetState (
+ const bool bIsOver,
+ const bool bIsPressed)
+{
+ bool bModified (mbIsOver != bIsOver || mbIsPressed != bIsPressed);
+ bool bClicked (mbIsPressed && bIsOver && ! bIsPressed);
+
+ mbIsOver = bIsOver;
+ mbIsPressed = bIsPressed;
+
+ // When the element is disabled then ignore mouse over or selection.
+ // When the element is selected then ignore mouse over.
+ if ( ! mbIsEnabled)
+ mpMode = mpDisabled;
+ else if (mbIsSelected && mbIsOver)
+ mpMode = mpMouseOverSelected;
+ else if (mbIsSelected)
+ mpMode = mpSelected;
+ else if (mbIsOver)
+ mpMode = mpMouseOver;
+ else
+ mpMode = mpNormal;
+
+ if (bClicked && mbIsEnabled)
+ {
+ if (mpMode)
+ {
+ do
+ {
+ if (mpMode->msAction.isEmpty())
+ break;
+
+ if (!mpToolBar)
+ break;
+
+ if (!mpToolBar->GetPresenterController())
+ break;
+
+ mpToolBar->GetPresenterController()->DispatchUnoCommand(mpMode->msAction);
+ mpToolBar->RequestLayout();
+ }
+ while (false);
+ }
+
+ }
+ else if (bModified)
+ {
+ Invalidate(true);
+ }
+
+ return bModified;
+}
+
+void Element::Invalidate (const bool bSynchronous)
+{
+ OSL_ASSERT(mpToolBar.is());
+ mpToolBar->InvalidateArea(GetBoundingBox(), bSynchronous);
+}
+
+bool Element::IsOutside (const awt::Rectangle& rBox)
+{
+ if (rBox.X >= maLocation.X+maSize.Width)
+ return true;
+ else if (rBox.Y >= maLocation.Y+maSize.Height)
+ return true;
+ else if (maLocation.X >= rBox.X+rBox.Width)
+ return true;
+ else if (maLocation.Y >= rBox.Y+rBox.Height)
+ return true;
+ else
+ return false;
+}
+
+
+bool Element::IsFilling() const
+{
+ return false;
+}
+
+void Element::UpdateState()
+{
+ OSL_ASSERT(mpToolBar);
+ OSL_ASSERT(mpToolBar->GetPresenterController());
+
+ if (!mpMode)
+ return;
+
+ util::URL aURL (mpToolBar->GetPresenterController()->CreateURLFromString(mpMode->msAction));
+ Reference<frame::XDispatch> xDispatch (mpToolBar->GetPresenterController()->GetDispatch(aURL));
+ if (xDispatch.is())
+ {
+ xDispatch->addStatusListener(this, aURL);
+ xDispatch->removeStatusListener(this, aURL);
+ }
+}
+
+//----- lang::XEventListener --------------------------------------------------
+
+void SAL_CALL Element::disposing (const css::lang::EventObject&) {}
+
+//----- document::XEventListener ----------------------------------------------
+
+void SAL_CALL Element::notifyEvent (const css::document::EventObject&)
+{
+ UpdateState();
+}
+
+//----- frame::XStatusListener ------------------------------------------------
+
+void SAL_CALL Element::statusChanged (const css::frame::FeatureStateEvent& rEvent)
+{
+ bool bIsSelected (mbIsSelected);
+ bool bIsEnabled (rEvent.IsEnabled);
+ rEvent.State >>= bIsSelected;
+
+ if (bIsSelected != mbIsSelected || bIsEnabled != mbIsEnabled)
+ {
+ mbIsEnabled = bIsEnabled;
+ mbIsSelected = bIsSelected;
+ SetState(mbIsOver, mbIsPressed);
+ mpToolBar->RequestLayout();
+ }
+}
+
+} // end of anonymous namespace
+
+//===== ElementMode ===========================================================
+
+namespace {
+
+ElementMode::ElementMode()
+{
+}
+
+void ElementMode::ReadElementMode (
+ const Reference<beans::XPropertySet>& rxElementProperties,
+ const OUString& rsModeName,
+ std::shared_ptr<ElementMode> const & rpDefaultMode,
+ ::sdext::presenter::PresenterToolBar::Context const & rContext)
+{
+ try
+ {
+ Reference<container::XHierarchicalNameAccess> xNode (
+ PresenterConfigurationAccess::GetProperty(rxElementProperties, rsModeName),
+ UNO_QUERY);
+ Reference<beans::XPropertySet> xProperties (
+ PresenterConfigurationAccess::GetNodeProperties(xNode, OUString()));
+ if (!xProperties.is() && rpDefaultMode != nullptr)
+ {
+ // The mode is not specified. Use the given, possibly empty,
+ // default mode instead.
+ mpIcon = rpDefaultMode->mpIcon;
+ msAction = rpDefaultMode->msAction;
+ maText = rpDefaultMode->maText;
+ }
+
+ // Read action.
+ if ( ! (PresenterConfigurationAccess::GetProperty(xProperties, "Action") >>= msAction))
+ if (rpDefaultMode != nullptr)
+ msAction = rpDefaultMode->msAction;
+
+ // Read text and font
+ OUString sText(rpDefaultMode != nullptr ? rpDefaultMode->maText.GetText() : OUString());
+ PresenterConfigurationAccess::GetProperty(xProperties, "Text") >>= sText;
+ Reference<container::XHierarchicalNameAccess> xFontNode (
+ PresenterConfigurationAccess::GetProperty(xProperties, "Font"), UNO_QUERY);
+ PresenterTheme::SharedFontDescriptor pFont(PresenterTheme::ReadFont(
+ xFontNode, rpDefaultMode != nullptr ? rpDefaultMode->maText.GetFont()
+ : PresenterTheme::SharedFontDescriptor()));
+ maText = Text(sText,pFont);
+
+ // Read bitmaps to display as icons.
+ Reference<container::XHierarchicalNameAccess> xIconNode (
+ PresenterConfigurationAccess::GetProperty(xProperties, "Icon"), UNO_QUERY);
+ mpIcon = PresenterBitmapContainer::LoadBitmap(
+ xIconNode, "", rContext.mxPresenterHelper, rContext.mxCanvas,
+ rpDefaultMode != nullptr ? rpDefaultMode->mpIcon : SharedBitmapDescriptor());
+ }
+ catch(Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+}
+
+} // end of anonymous namespace
+
+//===== Button ================================================================
+
+namespace {
+
+::rtl::Reference<Element> Button::Create (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar)
+{
+ ::rtl::Reference<Button> pElement (new Button(rpToolBar));
+ pElement->Initialize();
+ return pElement;
+}
+
+Button::Button (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar)
+ : Element(rpToolBar),
+ mbIsListenerRegistered(false)
+{
+ OSL_ASSERT(mpToolBar);
+ OSL_ASSERT(mpToolBar->GetPresenterController().is());
+ OSL_ASSERT(mpToolBar->GetPresenterController()->GetWindowManager().is());
+}
+
+void Button::Initialize()
+{
+ mpToolBar->GetPresenterController()->GetWindowManager()->AddLayoutListener(this);
+ mbIsListenerRegistered = true;
+}
+
+void Button::disposing()
+{
+ OSL_ASSERT(mpToolBar);
+ if (mpToolBar && mbIsListenerRegistered)
+ {
+ OSL_ASSERT(mpToolBar->GetPresenterController().is());
+ OSL_ASSERT(mpToolBar->GetPresenterController()->GetWindowManager().is());
+
+ mbIsListenerRegistered = false;
+ mpToolBar->GetPresenterController()->GetWindowManager()->RemoveLayoutListener(this);
+ }
+ Element::disposing();
+}
+
+void Button::Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const rendering::ViewState& rViewState)
+{
+ OSL_ASSERT(rxCanvas.is());
+
+ if (!mpMode)
+ return;
+
+ if (!mpMode->mpIcon)
+ return;
+
+ geometry::RealRectangle2D aTextBBox (mpMode->maText.GetBoundingBox(rxCanvas));
+ sal_Int32 nTextHeight (sal::static_int_cast<sal_Int32>(0.5 + aTextBBox.Y2 - aTextBBox.Y1));
+
+ PaintIcon(rxCanvas, nTextHeight, rViewState);
+ mpMode->maText.Paint(rxCanvas, rViewState, GetBoundingBox());
+}
+
+awt::Size Button::CreateBoundingSize (
+ const Reference<rendering::XCanvas>& rxCanvas)
+{
+ if (!mpMode)
+ return awt::Size();
+
+ geometry::RealRectangle2D aTextBBox (mpMode->maText.GetBoundingBox(rxCanvas));
+
+ // tdf#128964 This ensures that if the text of a button changes due to a change in
+ // the state of the button the other buttons of the toolbar do not move. The button is
+ // allotted the maximum size so that it doesn't resize during a change of state.
+ geometry::RealRectangle2D aTextBBoxNormal (mpNormal->maText.GetBoundingBox(rxCanvas));
+ geometry::RealRectangle2D aTextBBoxMouseOver (mpMouseOver->maText.GetBoundingBox(rxCanvas));
+ geometry::RealRectangle2D aTextBBoxSelected (mpSelected->maText.GetBoundingBox(rxCanvas));
+ geometry::RealRectangle2D aTextBBoxDisabled (mpDisabled->maText.GetBoundingBox(rxCanvas));
+ geometry::RealRectangle2D aTextBBoxMouseOverSelected (mpMouseOverSelected->maText.GetBoundingBox(rxCanvas));
+ std::vector<sal_Int32> widths
+ {
+ sal::static_int_cast<sal_Int32>(0.5 + aTextBBoxNormal.X2 - aTextBBoxNormal.X1),
+ sal::static_int_cast<sal_Int32>(0.5 + aTextBBoxMouseOver.X2 - aTextBBoxMouseOver.X1),
+ sal::static_int_cast<sal_Int32>(0.5 + aTextBBoxSelected.X2 - aTextBBoxSelected.X1),
+ sal::static_int_cast<sal_Int32>(0.5 + aTextBBoxDisabled.X2 - aTextBBoxDisabled.X1),
+ sal::static_int_cast<sal_Int32>(0.5 + aTextBBoxMouseOverSelected.X2 - aTextBBoxMouseOverSelected.X1)
+ };
+
+ sal_Int32 nTextHeight (sal::static_int_cast<sal_Int32>(0.5 + aTextBBox.Y2 - aTextBBox.Y1));
+ Reference<rendering::XBitmap> xBitmap;
+ if (mpMode->mpIcon)
+ xBitmap = mpMode->mpIcon->GetNormalBitmap();
+ if (xBitmap.is())
+ {
+ const sal_Int32 nGap (5);
+ geometry::IntegerSize2D aSize (xBitmap->getSize());
+ return awt::Size(
+ ::std::max(aSize.Width, *std::max_element(widths.begin(), widths.end())),
+ aSize.Height + nGap + nTextHeight);
+ }
+ else
+ {
+ return awt::Size(*std::max_element(widths.begin(), widths.end()), nTextHeight);
+ }
+}
+
+void Button::PaintIcon (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const sal_Int32 nTextHeight,
+ const rendering::ViewState& rViewState)
+{
+ if (!mpMode)
+ return;
+
+ Reference<rendering::XBitmap> xBitmap (mpMode->mpIcon->GetBitmap(GetMode()));
+ if (!xBitmap.is())
+ return;
+
+ /// check whether RTL interface or not
+ if(!AllSettings::GetLayoutRTL()){
+ const sal_Int32 nX (maLocation.X
+ + (maSize.Width-xBitmap->getSize().Width) / 2);
+ const sal_Int32 nY (maLocation.Y
+ + (maSize.Height - nTextHeight - xBitmap->getSize().Height) / 2);
+ const rendering::RenderState aRenderState(
+ geometry::AffineMatrix2D(1,0,nX, 0,1,nY),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::OVER);
+ rxCanvas->drawBitmap(xBitmap, rViewState, aRenderState);
+ }
+ else {
+ const sal_Int32 nX (maLocation.X
+ + (maSize.Width+xBitmap->getSize().Width) / 2);
+ const sal_Int32 nY (maLocation.Y
+ + (maSize.Height - nTextHeight - xBitmap->getSize().Height) / 2);
+ const rendering::RenderState aRenderState(
+ geometry::AffineMatrix2D(-1,0,nX, 0,1,nY),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::OVER);
+ rxCanvas->drawBitmap(xBitmap, rViewState, aRenderState);
+ }
+}
+
+PresenterBitmapDescriptor::Mode Button::GetMode() const
+{
+ if ( ! IsEnabled())
+ return PresenterBitmapDescriptor::Disabled;
+ else if (mbIsPressed)
+ return PresenterBitmapDescriptor::ButtonDown;
+ else if (mbIsOver)
+ return PresenterBitmapDescriptor::MouseOver;
+ else
+ return PresenterBitmapDescriptor::Normal;
+}
+
+//----- lang::XEventListener --------------------------------------------------
+
+void SAL_CALL Button::disposing (const css::lang::EventObject& rEvent)
+{
+ mbIsListenerRegistered = false;
+ Element::disposing(rEvent);
+}
+
+} // end of anonymous namespace
+
+//===== PresenterToolBar::Label ===============================================
+
+namespace {
+
+Label::Label (const ::rtl::Reference<PresenterToolBar>& rpToolBar)
+ : Element(rpToolBar)
+{
+}
+
+awt::Size Label::CreateBoundingSize (
+ const Reference<rendering::XCanvas>& rxCanvas)
+{
+ if (!mpMode)
+ return awt::Size(0,0);
+
+ geometry::RealRectangle2D aTextBBox (mpMode->maText.GetBoundingBox(rxCanvas));
+ return awt::Size(
+ sal::static_int_cast<sal_Int32>(0.5 + aTextBBox.X2 - aTextBBox.X1),
+ sal::static_int_cast<sal_Int32>(0.5 + aTextBBox.Y2 - aTextBBox.Y1));
+}
+
+void Label::SetText (const OUString& rsText)
+{
+ OSL_ASSERT(mpToolBar);
+ if (!mpMode)
+ return;
+
+ const bool bRequestLayout (mpMode->maText.GetText().getLength() != rsText.getLength());
+
+ mpMode->maText.SetText(rsText);
+ // Just use the character count for determining whether a layout is
+ // necessary. This is an optimization to avoid layouts every time a new
+ // time value is set on some labels.
+ if (bRequestLayout)
+ mpToolBar->RequestLayout();
+ else
+ Invalidate(false);
+}
+
+void Label::Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const rendering::ViewState& rViewState)
+{
+ OSL_ASSERT(rxCanvas.is());
+ if (!mpMode)
+ return;
+
+ mpMode->maText.Paint(rxCanvas, rViewState, GetBoundingBox());
+}
+
+bool Label::SetState (const bool, const bool)
+{
+ // For labels there is no mouse over effect.
+ return Element::SetState(false, false);
+}
+
+} // end of anonymous namespace
+
+//===== Text ==================================================================
+
+namespace {
+
+Text::Text()
+{
+}
+
+Text::Text (
+ const OUString& rsText,
+ const PresenterTheme::SharedFontDescriptor& rpFont)
+ : msText(rsText),
+ mpFont(rpFont)
+{
+}
+
+void Text::SetText (const OUString& rsText)
+{
+ msText = rsText;
+}
+
+const OUString& Text::GetText() const
+{
+ return msText;
+}
+
+const PresenterTheme::SharedFontDescriptor& Text::GetFont() const
+{
+ return mpFont;
+}
+
+void Text::Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const rendering::ViewState& rViewState,
+ const awt::Rectangle& rBoundingBox)
+{
+ OSL_ASSERT(rxCanvas.is());
+
+ if (msText.isEmpty())
+ return;
+ if (!mpFont)
+ return;
+
+ if ( ! mpFont->mxFont.is())
+ mpFont->PrepareFont(rxCanvas);
+ if ( ! mpFont->mxFont.is())
+ return;
+
+ rendering::StringContext aContext (msText, 0, msText.getLength());
+
+ Reference<rendering::XTextLayout> xLayout (
+ mpFont->mxFont->createTextLayout(
+ aContext,
+ rendering::TextDirection::WEAK_LEFT_TO_RIGHT,
+ 0));
+ geometry::RealRectangle2D aBox (xLayout->queryTextBounds());
+ const double nTextWidth = aBox.X2 - aBox.X1;
+ const double nY = rBoundingBox.Y + rBoundingBox.Height - aBox.Y2;
+ const double nX = rBoundingBox.X + (rBoundingBox.Width - nTextWidth)/2;
+
+ rendering::RenderState aRenderState(
+ geometry::AffineMatrix2D(1,0,nX, 0,1,nY),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
+ rxCanvas->drawTextLayout(
+ xLayout,
+ rViewState,
+ aRenderState);
+}
+
+geometry::RealRectangle2D Text::GetBoundingBox (const Reference<rendering::XCanvas>& rxCanvas)
+{
+ if (mpFont && !msText.isEmpty())
+ {
+ if ( ! mpFont->mxFont.is())
+ mpFont->PrepareFont(rxCanvas);
+ if (mpFont->mxFont.is())
+ {
+ rendering::StringContext aContext (msText, 0, msText.getLength());
+ Reference<rendering::XTextLayout> xLayout (
+ mpFont->mxFont->createTextLayout(
+ aContext,
+ rendering::TextDirection::WEAK_LEFT_TO_RIGHT,
+ 0));
+ return xLayout->queryTextBounds();
+ }
+ }
+ return geometry::RealRectangle2D(0,0,0,0);
+}
+
+//===== TimeFormatter =========================================================
+
+OUString TimeFormatter::FormatTime (const oslDateTime& rTime)
+{
+ OUStringBuffer sText;
+
+ const sal_Int32 nHours (sal::static_int_cast<sal_Int32>(rTime.Hours));
+ const sal_Int32 nMinutes (sal::static_int_cast<sal_Int32>(rTime.Minutes));
+ const sal_Int32 nSeconds(sal::static_int_cast<sal_Int32>(rTime.Seconds));
+ // Hours
+ sText.append(nHours);
+
+ sText.append(":");
+
+ // Minutes
+ const OUString sMinutes (OUString::number(nMinutes));
+ if (sMinutes.getLength() == 1)
+ sText.append("0");
+ sText.append(sMinutes);
+
+ // Seconds
+ sText.append(":");
+ const OUString sSeconds (OUString::number(nSeconds));
+ if (sSeconds.getLength() == 1)
+ sText.append("0");
+ sText.append(sSeconds);
+ return sText.makeStringAndClear();
+}
+
+//===== TimeLabel =============================================================
+
+TimeLabel::TimeLabel (const ::rtl::Reference<PresenterToolBar>& rpToolBar)
+ : Label(rpToolBar)
+{
+}
+
+void SAL_CALL TimeLabel::disposing()
+{
+ PresenterClockTimer::Instance(mpToolBar->GetComponentContext())->RemoveListener(mpListener);
+ mpListener.reset();
+}
+
+void TimeLabel::ConnectToTimer()
+{
+ mpListener = std::make_shared<Listener>(this);
+ PresenterClockTimer::Instance(mpToolBar->GetComponentContext())->AddListener(mpListener);
+}
+
+//===== CurrentTimeLabel ======================================================
+
+::rtl::Reference<Element> CurrentTimeLabel::Create (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar)
+{
+ ::rtl::Reference<TimeLabel> pElement(new CurrentTimeLabel(rpToolBar));
+ pElement->ConnectToTimer();
+ return pElement;
+}
+
+CurrentTimeLabel::~CurrentTimeLabel()
+{
+}
+
+CurrentTimeLabel::CurrentTimeLabel (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar)
+ : TimeLabel(rpToolBar)
+{
+}
+
+void CurrentTimeLabel::TimeHasChanged (const oslDateTime& rCurrentTime)
+{
+ SetText(TimeFormatter::FormatTime(rCurrentTime));
+ Invalidate(false);
+}
+
+void CurrentTimeLabel::SetModes (
+ const SharedElementMode& rpNormalMode,
+ const SharedElementMode& rpMouseOverMode,
+ const SharedElementMode& rpSelectedMode,
+ const SharedElementMode& rpDisabledMode,
+ const SharedElementMode& rpMouseOverSelectedMode)
+{
+ TimeLabel::SetModes(rpNormalMode, rpMouseOverMode, rpSelectedMode, rpDisabledMode, rpMouseOverSelectedMode);
+ SetText(TimeFormatter::FormatTime(PresenterClockTimer::GetCurrentTime()));
+}
+
+//===== PresentationTimeLabel =================================================
+
+::rtl::Reference<Element> PresentationTimeLabel::Create (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar)
+{
+ ::rtl::Reference<TimeLabel> pElement(new PresentationTimeLabel(rpToolBar));
+ pElement->ConnectToTimer();
+ return pElement;
+}
+
+PresentationTimeLabel::~PresentationTimeLabel()
+{
+ mpToolBar->GetPresenterController()->SetPresentationTime(nullptr);
+}
+
+PresentationTimeLabel::PresentationTimeLabel (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar)
+ : TimeLabel(rpToolBar),
+ maStartTimeValue()
+{
+ restart();
+ setPauseStatus(false);
+ TimeValue pauseTime(0,0);
+ setPauseTimeValue(pauseTime);
+ mpToolBar->GetPresenterController()->SetPresentationTime(this);
+}
+
+void PresentationTimeLabel::restart()
+{
+ TimeValue pauseTime(0, 0);
+ setPauseTimeValue(pauseTime);
+ maStartTimeValue.Seconds = 0;
+ maStartTimeValue.Nanosec = 0;
+}
+
+bool PresentationTimeLabel::isPaused()
+{
+ return paused;
+}
+
+void PresentationTimeLabel::setPauseStatus(const bool pauseStatus)
+{
+ paused = pauseStatus;
+}
+
+const TimeValue& PresentationTimeLabel::getPauseTimeValue() const
+{
+ return pauseTimeValue;
+}
+
+void PresentationTimeLabel::setPauseTimeValue(const TimeValue pauseTime)
+{
+ //store the time at which the presentation was paused
+ pauseTimeValue = pauseTime;
+}
+
+void PresentationTimeLabel::TimeHasChanged (const oslDateTime& rCurrentTime)
+{
+ TimeValue aCurrentTimeValue;
+ if (!osl_getTimeValueFromDateTime(&rCurrentTime, &aCurrentTimeValue))
+ return;
+
+ if (maStartTimeValue.Seconds==0 && maStartTimeValue.Nanosec==0)
+ {
+ // This method is called for the first time. Initialize the
+ // start time. The start time is rounded to nearest second to
+ // keep the time updates synchronized with the current time label.
+ maStartTimeValue = aCurrentTimeValue;
+ if (maStartTimeValue.Nanosec >= 500000000)
+ maStartTimeValue.Seconds += 1;
+ maStartTimeValue.Nanosec = 0;
+ }
+
+ //The start time value is incremented by the amount of time
+ //the presentation was paused for in order to continue the
+ //timer from the same position
+ if(!isPaused())
+ {
+ TimeValue pauseTime = getPauseTimeValue();
+ if(pauseTime.Seconds != 0 || pauseTime.Nanosec != 0)
+ {
+ TimeValue incrementValue(0, 0);
+ incrementValue.Seconds = aCurrentTimeValue.Seconds - pauseTime.Seconds;
+ if(pauseTime.Nanosec > aCurrentTimeValue.Nanosec)
+ {
+ incrementValue.Nanosec = 1000000000 + aCurrentTimeValue.Nanosec - pauseTime.Nanosec;
+ }
+ else
+ {
+ incrementValue.Nanosec = aCurrentTimeValue.Nanosec - pauseTime.Nanosec;
+ }
+
+ maStartTimeValue.Seconds += incrementValue.Seconds;
+ maStartTimeValue.Nanosec += incrementValue.Nanosec;
+ if(maStartTimeValue.Nanosec >= 1000000000)
+ {
+ maStartTimeValue.Seconds += 1;
+ maStartTimeValue.Nanosec -= 1000000000;
+ }
+
+ TimeValue pauseTime_(0, 0);
+ setPauseTimeValue(pauseTime_);
+ }
+ }
+ else
+ {
+ TimeValue pauseTime = getPauseTimeValue();
+ if(pauseTime.Seconds == 0 && pauseTime.Nanosec == 0)
+ {
+ setPauseTimeValue(aCurrentTimeValue);
+ }
+ }
+
+ TimeValue aElapsedTimeValue;
+ aElapsedTimeValue.Seconds = aCurrentTimeValue.Seconds - maStartTimeValue.Seconds;
+ aElapsedTimeValue.Nanosec = aCurrentTimeValue.Nanosec - maStartTimeValue.Nanosec;
+
+ oslDateTime aElapsedDateTime;
+ if (osl_getDateTimeFromTimeValue(&aElapsedTimeValue, &aElapsedDateTime) && !isPaused())
+ {
+ SetText(TimeFormatter::FormatTime(aElapsedDateTime));
+ Invalidate(false);
+ }
+}
+
+void PresentationTimeLabel::SetModes (
+ const SharedElementMode& rpNormalMode,
+ const SharedElementMode& rpMouseOverMode,
+ const SharedElementMode& rpSelectedMode,
+ const SharedElementMode& rpDisabledMode,
+ const SharedElementMode& rpMouseOverSelectedMode)
+{
+ TimeLabel::SetModes(rpNormalMode, rpMouseOverMode, rpSelectedMode, rpDisabledMode, rpMouseOverSelectedMode);
+
+ oslDateTime aStartDateTime;
+ if (osl_getDateTimeFromTimeValue(&maStartTimeValue, &aStartDateTime))
+ {
+ SetText(TimeFormatter::FormatTime(aStartDateTime));
+ }
+}
+
+//===== VerticalSeparator =====================================================
+
+VerticalSeparator::VerticalSeparator (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar)
+ : Element(rpToolBar)
+{
+}
+
+void VerticalSeparator::Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const rendering::ViewState& rViewState)
+{
+ OSL_ASSERT(rxCanvas.is());
+
+ awt::Rectangle aBBox (GetBoundingBox());
+
+ rendering::RenderState aRenderState(
+ geometry::AffineMatrix2D(1,0,aBBox.X, 0,1,aBBox.Y),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::OVER);
+ if (mpMode)
+ {
+ PresenterTheme::SharedFontDescriptor pFont (mpMode->maText.GetFont());
+ if (pFont)
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, pFont->mnColor);
+ }
+
+ Reference<rendering::XBitmap> xBitmap(mpToolBar->GetPresenterController()->GetPresenterHelper()->loadBitmap("bitmaps/Separator.png", rxCanvas));
+ if (!xBitmap.is())
+ return;
+
+ rxCanvas->drawBitmap(
+ xBitmap,
+ rViewState,
+ aRenderState);
+}
+
+awt::Size VerticalSeparator::CreateBoundingSize (
+ const Reference<rendering::XCanvas>&)
+{
+ return awt::Size(1,20);
+}
+
+bool VerticalSeparator::IsFilling() const
+{
+ return true;
+}
+
+//===== HorizontalSeparator ===================================================
+
+HorizontalSeparator::HorizontalSeparator (
+ const ::rtl::Reference<PresenterToolBar>& rpToolBar)
+ : Element(rpToolBar)
+{
+}
+
+void HorizontalSeparator::Paint (
+ const Reference<rendering::XCanvas>& rxCanvas,
+ const rendering::ViewState& rViewState)
+{
+ OSL_ASSERT(rxCanvas.is());
+
+ awt::Rectangle aBBox (GetBoundingBox());
+
+ rendering::RenderState aRenderState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::OVER);
+ if (mpMode)
+ {
+ PresenterTheme::SharedFontDescriptor pFont (mpMode->maText.GetFont());
+ if (pFont)
+ PresenterCanvasHelper::SetDeviceColor(aRenderState, pFont->mnColor);
+ }
+
+ rxCanvas->fillPolyPolygon(
+ PresenterGeometryHelper::CreatePolygon(aBBox, rxCanvas->getDevice()),
+ rViewState,
+ aRenderState);
+}
+
+awt::Size HorizontalSeparator::CreateBoundingSize (
+ const Reference<rendering::XCanvas>&)
+{
+ return awt::Size(20,1);
+}
+
+bool HorizontalSeparator::IsFilling() const
+{
+ return true;
+}
+
+} // end of anonymous namespace
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterToolBar.hxx b/sdext/source/presenter/PresenterToolBar.hxx
new file mode 100644
index 000000000..90931df31
--- /dev/null
+++ b/sdext/source/presenter/PresenterToolBar.hxx
@@ -0,0 +1,250 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERTOOLBAR_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERTOOLBAR_HXX
+
+#include "PresenterController.hxx"
+#include "PresenterViewFactory.hxx"
+
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/awt/XMouseListener.hpp>
+#include <com/sun/star/awt/XMouseMotionListener.hpp>
+#include <com/sun/star/awt/XPaintListener.hpp>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/drawing/framework/XView.hpp>
+#include <com/sun/star/drawing/framework/XResourceId.hpp>
+#include <com/sun/star/frame/XController.hpp>
+
+#include <functional>
+
+namespace sdext::presenter {
+
+typedef cppu::WeakComponentImplHelper<
+ css::awt::XWindowListener,
+ css::awt::XPaintListener,
+ css::awt::XMouseListener,
+ css::awt::XMouseMotionListener,
+ css::drawing::XDrawView
+ > PresenterToolBarInterfaceBase;
+
+typedef cppu::WeakComponentImplHelper<
+ css::awt::XPaintListener,
+ css::drawing::framework::XView,
+ css::drawing::XDrawView
+ > PresenterToolBarViewInterfaceBase;
+
+/** A simple tool bar that can display bitmapped buttons and labels. At the
+ moment there are buttons for moving to the next and previous slide and
+ to the next effect. A label displays the index of the current slide
+ and the total number of slides.
+*/
+class PresenterToolBar
+ : private ::cppu::BaseMutex,
+ public PresenterToolBarInterfaceBase,
+ public CachablePresenterView
+{
+public:
+ typedef ::std::function<void ()> Action;
+
+ enum Anchor { Left, Center };
+
+ PresenterToolBar (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::awt::XWindow>& rxWindow,
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const ::rtl::Reference<PresenterController>& rpPresenterController,
+ const Anchor eAnchor);
+ virtual ~PresenterToolBar() override;
+ PresenterToolBar(const PresenterToolBar&) = delete;
+ PresenterToolBar& operator=(const PresenterToolBar&) = delete;
+
+ void Initialize (
+ const OUString& rsConfigurationPath);
+
+ virtual void SAL_CALL disposing() override;
+
+ void InvalidateArea (
+ const css::awt::Rectangle& rRepaintBox,
+ const bool bSynchronous);
+
+ void RequestLayout();
+ css::geometry::RealSize2D const & GetMinimalSize();
+ const ::rtl::Reference<PresenterController>& GetPresenterController() const;
+ const css::uno::Reference<css::uno::XComponentContext>& GetComponentContext() const;
+
+ // lang::XEventListener
+
+ virtual void SAL_CALL
+ disposing (const css::lang::EventObject& rEventObject) override;
+
+ // XWindowListener
+
+ virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override;
+
+ // XPaintListener
+
+ virtual void SAL_CALL windowPaint (const css::awt::PaintEvent& rEvent) override;
+
+ // XMouseListener
+
+ virtual void SAL_CALL mousePressed (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseReleased (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseEntered (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseExited (const css::awt::MouseEvent& rEvent) override;
+
+ // XMouseMotionListener
+
+ virtual void SAL_CALL mouseMoved (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseDragged (const css::awt::MouseEvent& rEvent) override;
+
+ // XDrawView
+
+ virtual void SAL_CALL setCurrentPage (
+ const css::uno::Reference<css::drawing::XDrawPage>& rxSlide) override;
+
+ virtual css::uno::Reference<css::drawing::XDrawPage> SAL_CALL getCurrentPage() override;
+
+ class Context;
+
+private:
+ css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+
+ class ElementContainerPart;
+ typedef std::shared_ptr<ElementContainerPart> SharedElementContainerPart;
+ typedef ::std::vector<SharedElementContainerPart> ElementContainer;
+ ElementContainer maElementContainer;
+ SharedElementContainerPart mpCurrentContainerPart;
+ css::uno::Reference<css::awt::XWindow> mxWindow;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+ css::uno::Reference<css::presentation::XSlideShowController> mxSlideShowController;
+ css::uno::Reference<css::drawing::XDrawPage> mxCurrentSlide;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ bool mbIsLayoutPending;
+ const Anchor meAnchor;
+ /** The minimal size that is necessary to display all elements without
+ overlap and with minimal gaps between them.
+ */
+ css::geometry::RealSize2D maMinimalSize;
+
+ void CreateControls (
+ const OUString& rsConfigurationPath);
+ void Layout (const css::uno::Reference<css::rendering::XCanvas>& rxCanvas);
+ css::geometry::RealSize2D CalculatePartSize (
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const SharedElementContainerPart& rpPart,
+ const bool bIsHorizontal);
+ static void LayoutPart (
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const SharedElementContainerPart& rpPart,
+ const css::geometry::RealRectangle2D& rBoundingBox,
+ const css::geometry::RealSize2D& rPartSize,
+ const bool bIsHorizontal);
+ void Paint (
+ const css::awt::Rectangle& rUpdateBox,
+ const css::rendering::ViewState& rViewState);
+
+ void UpdateSlideNumber();
+
+ void CheckMouseOver (
+ const css::awt::MouseEvent& rEvent,
+ const bool bOverWindow,
+ const bool bMouseDown=false);
+
+ void ProcessEntry (
+ const css::uno::Reference<css::beans::XPropertySet>& rProperties,
+ Context const & rContext);
+
+ /** @throws css::lang::DisposedException when the object has already been
+ disposed.
+ */
+ void ThrowIfDisposed() const;
+};
+
+/** View for the PresenterToolBar.
+*/
+class PresenterToolBarView
+ : private ::cppu::BaseMutex,
+ public PresenterToolBarViewInterfaceBase
+{
+public:
+ explicit PresenterToolBarView (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterToolBarView() override;
+ PresenterToolBarView(const PresenterToolBarView&) = delete;
+ PresenterToolBarView& operator=(const PresenterToolBarView&) = delete;
+
+ virtual void SAL_CALL disposing() override;
+
+ const ::rtl::Reference<PresenterToolBar>& GetPresenterToolBar() const;
+
+ // XPaintListener
+
+ virtual void SAL_CALL windowPaint (const css::awt::PaintEvent& rEvent) override;
+
+ // lang::XEventListener
+
+ virtual void SAL_CALL
+ disposing (const css::lang::EventObject& rEventObject) override;
+
+ // XResourceId
+
+ virtual css::uno::Reference<css::drawing::framework::XResourceId> SAL_CALL getResourceId() override;
+
+ virtual sal_Bool SAL_CALL isAnchorOnly() override;
+
+ // XDrawView
+
+ virtual void SAL_CALL setCurrentPage (
+ const css::uno::Reference<css::drawing::XDrawPage>& rxSlide) override;
+
+ virtual css::uno::Reference<css::drawing::XDrawPage> SAL_CALL getCurrentPage() override;
+
+private:
+ // css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+ css::uno::Reference<css::drawing::framework::XPane> mxPane;
+ css::uno::Reference<css::drawing::framework::XResourceId> mxViewId;
+ css::uno::Reference<css::awt::XWindow> mxWindow;
+ css::uno::Reference<css::rendering::XCanvas> mxCanvas;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ ::rtl::Reference<PresenterToolBar> mpToolBar;
+
+};
+
+} // end of namespace ::sdext::presenter
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterUIPainter.cxx b/sdext/source/presenter/PresenterUIPainter.cxx
new file mode 100644
index 000000000..7b37736ff
--- /dev/null
+++ b/sdext/source/presenter/PresenterUIPainter.cxx
@@ -0,0 +1,241 @@
+/* -*- 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 "PresenterUIPainter.hxx"
+
+#include "PresenterGeometryHelper.hxx"
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sdext::presenter {
+
+void PresenterUIPainter::PaintHorizontalBitmapComposite (
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::awt::Rectangle& rBoundingBox,
+ const css::uno::Reference<css::rendering::XBitmap>& rxLeftBitmap,
+ const css::uno::Reference<css::rendering::XBitmap>& rxRepeatableCenterBitmap,
+ const css::uno::Reference<css::rendering::XBitmap>& rxRightBitmap)
+{
+ if (PresenterGeometryHelper::AreRectanglesDisjoint(rRepaintBox, rBoundingBox))
+ {
+ // The bounding box lies completely outside the repaint area.
+ // Nothing has to be repainted.
+ return;
+ }
+
+ // Get bitmap sizes.
+ geometry::IntegerSize2D aLeftBitmapSize;
+ if (rxLeftBitmap.is())
+ aLeftBitmapSize = rxLeftBitmap->getSize();
+ geometry::IntegerSize2D aCenterBitmapSize;
+ if (rxRepeatableCenterBitmap.is())
+ aCenterBitmapSize = rxRepeatableCenterBitmap->getSize();
+ geometry::IntegerSize2D aRightBitmapSize;
+ if (rxRightBitmap.is())
+ aRightBitmapSize = rxRightBitmap->getSize();
+
+ // Prepare painting.
+ rendering::ViewState aViewState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr);
+
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ // Paint the left bitmap once.
+ if (rxLeftBitmap.is())
+ {
+ const awt::Rectangle aLeftBoundingBox (
+ rBoundingBox.X,
+ rBoundingBox.Y,
+ ::std::min(aLeftBitmapSize.Width, rBoundingBox.Width),
+ rBoundingBox.Height);
+ aViewState.Clip.set(
+ PresenterGeometryHelper::CreatePolygon(
+ PresenterGeometryHelper::Intersection(rRepaintBox, aLeftBoundingBox),
+ rxCanvas->getDevice()));
+ aRenderState.AffineTransform.m02 = aLeftBoundingBox.X;
+ aRenderState.AffineTransform.m12
+ = aLeftBoundingBox.Y + (aLeftBoundingBox.Height - aLeftBitmapSize.Height) / 2;
+ rxCanvas->drawBitmap(rxLeftBitmap, aViewState, aRenderState);
+ }
+
+ // Paint the right bitmap once.
+ if (rxRightBitmap.is())
+ {
+ const awt::Rectangle aRightBoundingBox (
+ rBoundingBox.X + rBoundingBox.Width - aRightBitmapSize.Width,
+ rBoundingBox.Y,
+ ::std::min(aRightBitmapSize.Width, rBoundingBox.Width),
+ rBoundingBox.Height);
+ aViewState.Clip.set(
+ PresenterGeometryHelper::CreatePolygon(
+ PresenterGeometryHelper::Intersection(rRepaintBox, aRightBoundingBox),
+ rxCanvas->getDevice()));
+ aRenderState.AffineTransform.m02
+ = aRightBoundingBox.X + aRightBoundingBox.Width - aRightBitmapSize.Width;
+ aRenderState.AffineTransform.m12
+ = aRightBoundingBox.Y + (aRightBoundingBox.Height - aRightBitmapSize.Height) / 2;
+ rxCanvas->drawBitmap(rxRightBitmap, aViewState, aRenderState);
+ }
+
+ // Paint the center bitmap to fill the remaining space.
+ if (!rxRepeatableCenterBitmap.is())
+ return;
+
+ const awt::Rectangle aCenterBoundingBox (
+ rBoundingBox.X + aLeftBitmapSize.Width,
+ rBoundingBox.Y,
+ rBoundingBox.Width - aLeftBitmapSize.Width - aRightBitmapSize.Width,
+ rBoundingBox.Height);
+ if (aCenterBoundingBox.Width <= 0)
+ return;
+
+ aViewState.Clip.set(
+ PresenterGeometryHelper::CreatePolygon(
+ PresenterGeometryHelper::Intersection(rRepaintBox, aCenterBoundingBox),
+ rxCanvas->getDevice()));
+ sal_Int32 nX (aCenterBoundingBox.X);
+ const sal_Int32 nRight (aCenterBoundingBox.X + aCenterBoundingBox.Width - 1);
+ aRenderState.AffineTransform.m12
+ = aCenterBoundingBox.Y + (aCenterBoundingBox.Height-aCenterBitmapSize.Height) / 2;
+ while(nX <= nRight)
+ {
+ aRenderState.AffineTransform.m02 = nX;
+ rxCanvas->drawBitmap(rxRepeatableCenterBitmap, aViewState, aRenderState);
+ nX += aCenterBitmapSize.Width;
+ }
+}
+
+void PresenterUIPainter::PaintVerticalBitmapComposite (
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox,
+ const css::awt::Rectangle& rBoundingBox,
+ const css::uno::Reference<css::rendering::XBitmap>& rxTopBitmap,
+ const css::uno::Reference<css::rendering::XBitmap>& rxRepeatableCenterBitmap,
+ const css::uno::Reference<css::rendering::XBitmap>& rxBottomBitmap)
+{
+ if (PresenterGeometryHelper::AreRectanglesDisjoint(rRepaintBox, rBoundingBox))
+ {
+ // The bounding box lies completely outside the repaint area.
+ // Nothing has to be repainted.
+ return;
+ }
+
+ // Get bitmap sizes.
+ geometry::IntegerSize2D aTopBitmapSize;
+ if (rxTopBitmap.is())
+ aTopBitmapSize = rxTopBitmap->getSize();
+ geometry::IntegerSize2D aCenterBitmapSize;
+ if (rxRepeatableCenterBitmap.is())
+ aCenterBitmapSize = rxRepeatableCenterBitmap->getSize();
+ geometry::IntegerSize2D aBottomBitmapSize;
+ if (rxBottomBitmap.is())
+ aBottomBitmapSize = rxBottomBitmap->getSize();
+
+ // Prepare painting.
+ rendering::ViewState aViewState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr);
+
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ // Paint the top bitmap once.
+ if (rxTopBitmap.is())
+ {
+ const awt::Rectangle aTopBoundingBox (
+ rBoundingBox.X,
+ rBoundingBox.Y,
+ rBoundingBox.Width,
+ ::std::min(aTopBitmapSize.Height, rBoundingBox.Height));
+ aViewState.Clip.set(
+ PresenterGeometryHelper::CreatePolygon(
+ PresenterGeometryHelper::Intersection(rRepaintBox, aTopBoundingBox),
+ rxCanvas->getDevice()));
+ aRenderState.AffineTransform.m02
+ = aTopBoundingBox.X + (aTopBoundingBox.Width - aTopBitmapSize.Width) / 2;
+ aRenderState.AffineTransform.m12 = aTopBoundingBox.Y;
+ rxCanvas->drawBitmap(rxTopBitmap, aViewState, aRenderState);
+ }
+
+ // Paint the bottom bitmap once.
+ if (rxBottomBitmap.is())
+ {
+ const sal_Int32 nBBoxHeight (::std::min(aBottomBitmapSize.Height, rBoundingBox.Height));
+ const awt::Rectangle aBottomBoundingBox (
+ rBoundingBox.X,
+ rBoundingBox.Y + rBoundingBox.Height - nBBoxHeight,
+ rBoundingBox.Width,
+ nBBoxHeight);
+ aViewState.Clip.set(
+ PresenterGeometryHelper::CreatePolygon(
+ PresenterGeometryHelper::Intersection(rRepaintBox, aBottomBoundingBox),
+ rxCanvas->getDevice()));
+ aRenderState.AffineTransform.m02
+ = aBottomBoundingBox.X + (aBottomBoundingBox.Width - aBottomBitmapSize.Width) / 2;
+ aRenderState.AffineTransform.m12
+ = aBottomBoundingBox.Y + aBottomBoundingBox.Height - aBottomBitmapSize.Height;
+ rxCanvas->drawBitmap(rxBottomBitmap, aViewState, aRenderState);
+ }
+
+ // Paint the center bitmap to fill the remaining space.
+ if (!rxRepeatableCenterBitmap.is())
+ return;
+
+ const awt::Rectangle aCenterBoundingBox (
+ rBoundingBox.X,
+ rBoundingBox.Y + aTopBitmapSize.Height,
+ rBoundingBox.Width,
+ rBoundingBox.Height - aTopBitmapSize.Height - aBottomBitmapSize.Height);
+ if (aCenterBoundingBox.Height <= 0)
+ return;
+
+ aViewState.Clip.set(
+ PresenterGeometryHelper::CreatePolygon(
+ PresenterGeometryHelper::Intersection(rRepaintBox, aCenterBoundingBox),
+ rxCanvas->getDevice()));
+ sal_Int32 nY (aCenterBoundingBox.Y);
+ const sal_Int32 nBottom (aCenterBoundingBox.Y + aCenterBoundingBox.Height - 1);
+ aRenderState.AffineTransform.m02
+ = aCenterBoundingBox.X + (aCenterBoundingBox.Width-aCenterBitmapSize.Width) / 2;
+ while(nY <= nBottom)
+ {
+ aRenderState.AffineTransform.m12 = nY;
+ rxCanvas->drawBitmap(rxRepeatableCenterBitmap, aViewState, aRenderState);
+ nY += aCenterBitmapSize.Height;
+ }
+}
+
+} // end of namespace sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterUIPainter.hxx b/sdext/source/presenter/PresenterUIPainter.hxx
new file mode 100644
index 000000000..f21ca291a
--- /dev/null
+++ b/sdext/source/presenter/PresenterUIPainter.hxx
@@ -0,0 +1,56 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERUIPAINTER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERUIPAINTER_HXX
+
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XBitmap.hpp>
+
+namespace sdext::presenter
+{
+/** Functions for painting UI elements.
+*/
+class PresenterUIPainter
+{
+public:
+ PresenterUIPainter() = delete;
+ PresenterUIPainter(const PresenterUIPainter&) = delete;
+ PresenterUIPainter& operator=(const PresenterUIPainter&) = delete;
+
+ static void PaintHorizontalBitmapComposite(
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox, const css::awt::Rectangle& rBoundingBox,
+ const css::uno::Reference<css::rendering::XBitmap>& rxLeftBitmap,
+ const css::uno::Reference<css::rendering::XBitmap>& rxRepeatableCenterBitmap,
+ const css::uno::Reference<css::rendering::XBitmap>& rxRightBitmap);
+
+ static void PaintVerticalBitmapComposite(
+ const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
+ const css::awt::Rectangle& rRepaintBox, const css::awt::Rectangle& rBoundingBox,
+ const css::uno::Reference<css::rendering::XBitmap>& rxTopBitmap,
+ const css::uno::Reference<css::rendering::XBitmap>& rxRepeatableCenterBitmap,
+ const css::uno::Reference<css::rendering::XBitmap>& rxBottomBitmap);
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterViewFactory.cxx b/sdext/source/presenter/PresenterViewFactory.cxx
new file mode 100644
index 000000000..7c7f8c98b
--- /dev/null
+++ b/sdext/source/presenter/PresenterViewFactory.cxx
@@ -0,0 +1,503 @@
+/* -*- 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 "PresenterViewFactory.hxx"
+#include "PresenterPaneContainer.hxx"
+#include "PresenterHelpView.hxx"
+#include "PresenterNotesView.hxx"
+#include "PresenterSlideShowView.hxx"
+#include "PresenterSlidePreview.hxx"
+#include "PresenterSlideSorter.hxx"
+#include "PresenterToolBar.hxx"
+#include <com/sun/star/drawing/framework/XControllerManager.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sdext::presenter {
+
+namespace {
+
+/** By default the PresenterSlidePreview shows the preview of the current
+ slide. This adapter class makes it display the preview of the next
+ slide.
+*/
+class NextSlidePreview : public PresenterSlidePreview
+{
+public:
+ NextSlidePreview (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId,
+ const css::uno::Reference<css::drawing::framework::XPane>& rxAnchorPane,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterSlidePreview(rxContext, rxViewId, rxAnchorPane, rpPresenterController)
+ {
+ }
+
+ virtual void SAL_CALL setCurrentPage (
+ const css::uno::Reference<css::drawing::XDrawPage>& rxSlide) override
+ {
+ Reference<presentation::XSlideShowController> xSlideShowController (
+ mpPresenterController->GetSlideShowController());
+ Reference<drawing::XDrawPage> xSlide;
+ if (xSlideShowController.is())
+ {
+ const sal_Int32 nCount (xSlideShowController->getSlideCount());
+ sal_Int32 nNextSlideIndex (-1);
+ if (xSlideShowController->getCurrentSlide() == rxSlide)
+ {
+ nNextSlideIndex = xSlideShowController->getNextSlideIndex();
+ }
+ else
+ {
+ for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
+ {
+ if (rxSlide == xSlideShowController->getSlideByIndex(nIndex))
+ {
+ nNextSlideIndex = nIndex + 1;
+ }
+ }
+ }
+ if (nNextSlideIndex >= 0)
+ {
+ if (nNextSlideIndex < nCount)
+ {
+ xSlide = xSlideShowController->getSlideByIndex(nNextSlideIndex);
+ }
+ }
+ }
+ PresenterSlidePreview::setCurrentPage(xSlide);
+ }
+};
+
+} // end of anonymous namespace
+
+//===== PresenterViewFactory ==============================================
+
+PresenterViewFactory::PresenterViewFactory (
+ const Reference<uno::XComponentContext>& rxContext,
+ const Reference<frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterViewFactoryInterfaceBase(m_aMutex),
+ mxComponentContext(rxContext),
+ mxControllerWeak(rxController),
+ mpPresenterController(rpPresenterController)
+{
+}
+
+Reference<drawing::framework::XResourceFactory> PresenterViewFactory::Create (
+ const Reference<uno::XComponentContext>& rxContext,
+ const Reference<frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+{
+ rtl::Reference<PresenterViewFactory> pFactory (
+ new PresenterViewFactory(rxContext,rxController,rpPresenterController));
+ pFactory->Register(rxController);
+ return Reference<drawing::framework::XResourceFactory>(pFactory);
+}
+
+void PresenterViewFactory::Register (const Reference<frame::XController>& rxController)
+{
+ try
+ {
+ // Get the configuration controller.
+ Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW);
+ mxConfigurationController = xCM->getConfigurationController();
+ if ( ! mxConfigurationController.is())
+ {
+ throw RuntimeException();
+ }
+ mxConfigurationController->addResourceFactory(msCurrentSlidePreviewViewURL, this);
+ mxConfigurationController->addResourceFactory(msNextSlidePreviewViewURL, this);
+ mxConfigurationController->addResourceFactory(msNotesViewURL, this);
+ mxConfigurationController->addResourceFactory(msToolBarViewURL, this);
+ mxConfigurationController->addResourceFactory(msSlideSorterURL, this);
+ mxConfigurationController->addResourceFactory(msHelpViewURL, this);
+ }
+ catch (RuntimeException&)
+ {
+ OSL_ASSERT(false);
+ if (mxConfigurationController.is())
+ mxConfigurationController->removeResourceFactoryForReference(this);
+ mxConfigurationController = nullptr;
+
+ throw;
+ }
+}
+
+PresenterViewFactory::~PresenterViewFactory()
+{
+}
+
+void SAL_CALL PresenterViewFactory::disposing()
+{
+ if (mxConfigurationController.is())
+ mxConfigurationController->removeResourceFactoryForReference(this);
+ mxConfigurationController = nullptr;
+
+ if (mpResourceCache == nullptr)
+ return;
+
+ // Dispose all views in the cache.
+ for (const auto& rView : *mpResourceCache)
+ {
+ try
+ {
+ Reference<lang::XComponent> xComponent (rView.second.first, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ catch (lang::DisposedException&)
+ {
+ }
+ }
+ mpResourceCache.reset();
+}
+
+//----- XViewFactory ----------------------------------------------------------
+
+Reference<XResource> SAL_CALL PresenterViewFactory::createResource (
+ const Reference<XResourceId>& rxViewId)
+{
+ ThrowIfDisposed();
+
+ Reference<XResource> xView;
+
+ if (rxViewId.is())
+ {
+ Reference<XPane> xAnchorPane (
+ mxConfigurationController->getResource(rxViewId->getAnchor()),
+ UNO_QUERY_THROW);
+ xView = GetViewFromCache(rxViewId, xAnchorPane);
+ if (xView == nullptr)
+ xView = CreateView(rxViewId, xAnchorPane);
+
+ // Activate the view.
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ mpPresenterController->GetPaneContainer()->FindPaneId(rxViewId->getAnchor()));
+ if (pDescriptor)
+ pDescriptor->SetActivationState(true);
+ }
+
+ return xView;
+}
+
+void SAL_CALL PresenterViewFactory::releaseResource (const Reference<XResource>& rxView)
+{
+ ThrowIfDisposed();
+
+ if ( ! rxView.is())
+ return;
+
+ // Deactivate the view.
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ mpPresenterController->GetPaneContainer()->FindPaneId(
+ rxView->getResourceId()->getAnchor()));
+ if (pDescriptor)
+ pDescriptor->SetActivationState(false);
+
+ // Dispose only views that we can not put into the cache.
+ CachablePresenterView* pView = dynamic_cast<CachablePresenterView*>(rxView.get());
+ if (pView == nullptr || mpResourceCache == nullptr)
+ {
+ try
+ {
+ if (pView != nullptr)
+ pView->ReleaseView();
+ Reference<lang::XComponent> xComponent (rxView, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ }
+ catch (lang::DisposedException&)
+ {
+ // Do not let disposed exceptions get out. It might be interpreted
+ // as coming from the factory, which would then be removed from the
+ // drawing framework.
+ }
+ }
+ else
+ {
+ // Put cacheable views in the cache.
+ Reference<XResourceId> xViewId (rxView->getResourceId());
+ if (xViewId.is())
+ {
+ Reference<XPane> xAnchorPane (
+ mxConfigurationController->getResource(xViewId->getAnchor()),
+ UNO_QUERY_THROW);
+ (*mpResourceCache)[xViewId->getResourceURL()]
+ = ViewResourceDescriptor(Reference<XView>(rxView, UNO_QUERY), xAnchorPane);
+ pView->DeactivatePresenterView();
+ }
+ }
+}
+
+
+Reference<XResource> PresenterViewFactory::GetViewFromCache(
+ const Reference<XResourceId>& rxViewId,
+ const Reference<XPane>& rxAnchorPane) const
+{
+ if (mpResourceCache == nullptr)
+ return nullptr;
+
+ try
+ {
+ const OUString sResourceURL (rxViewId->getResourceURL());
+
+ // Can we use a view from the cache?
+ ResourceContainer::const_iterator iView (mpResourceCache->find(sResourceURL));
+ if (iView != mpResourceCache->end())
+ {
+ // The view is in the container but it can only be used if
+ // the anchor pane is the same now as it was at creation of
+ // the view.
+ if (iView->second.second == rxAnchorPane)
+ {
+ CachablePresenterView* pView
+ = dynamic_cast<CachablePresenterView*>(iView->second.first.get());
+ if (pView != nullptr)
+ pView->ActivatePresenterView();
+ return iView->second.first;
+ }
+
+ // Right view, wrong pane. Create a new view.
+ }
+ }
+ catch (RuntimeException&)
+ {
+ }
+ return nullptr;
+}
+
+Reference<XResource> PresenterViewFactory::CreateView(
+ const Reference<XResourceId>& rxViewId,
+ const Reference<XPane>& rxAnchorPane)
+{
+ Reference<XView> xView;
+
+ try
+ {
+ const OUString sResourceURL (rxViewId->getResourceURL());
+
+ if (sResourceURL == msCurrentSlidePreviewViewURL)
+ {
+ xView = CreateSlideShowView(rxViewId);
+ }
+ else if (sResourceURL == msNotesViewURL)
+ {
+ xView = CreateNotesView(rxViewId);
+ }
+ else if (sResourceURL == msNextSlidePreviewViewURL)
+ {
+ xView = CreateSlidePreviewView(rxViewId, rxAnchorPane);
+ }
+ else if (sResourceURL == msToolBarViewURL)
+ {
+ xView = CreateToolBarView(rxViewId);
+ }
+ else if (sResourceURL == msSlideSorterURL)
+ {
+ xView = CreateSlideSorterView(rxViewId);
+ }
+ else if (sResourceURL == msHelpViewURL)
+ {
+ xView = CreateHelpView(rxViewId);
+ }
+
+ // Activate it.
+ CachablePresenterView* pView = dynamic_cast<CachablePresenterView*>(xView.get());
+ if (pView != nullptr)
+ pView->ActivatePresenterView();
+ }
+ catch (RuntimeException&)
+ {
+ xView = nullptr;
+ }
+
+ return xView;
+}
+
+Reference<XView> PresenterViewFactory::CreateSlideShowView(
+ const Reference<XResourceId>& rxViewId) const
+{
+ Reference<XView> xView;
+
+ if ( ! mxConfigurationController.is())
+ return xView;
+ if ( ! mxComponentContext.is())
+ return xView;
+
+ try
+ {
+ rtl::Reference<PresenterSlideShowView> pShowView (
+ new PresenterSlideShowView(
+ mxComponentContext,
+ rxViewId,
+ Reference<frame::XController>(mxControllerWeak),
+ mpPresenterController));
+ pShowView->LateInit();
+ xView = pShowView;
+ }
+ catch (RuntimeException&)
+ {
+ xView = nullptr;
+ }
+
+ return xView;
+}
+
+Reference<XView> PresenterViewFactory::CreateSlidePreviewView(
+ const Reference<XResourceId>& rxViewId,
+ const Reference<XPane>& rxAnchorPane) const
+{
+ Reference<XView> xView;
+
+ if ( ! mxConfigurationController.is())
+ return xView;
+ if ( ! mxComponentContext.is())
+ return xView;
+
+ try
+ {
+ xView.set(
+ static_cast<XWeak*>(new NextSlidePreview(
+ mxComponentContext,
+ rxViewId,
+ rxAnchorPane,
+ mpPresenterController)),
+ UNO_QUERY_THROW);
+ }
+ catch (RuntimeException&)
+ {
+ xView = nullptr;
+ }
+
+ return xView;
+}
+
+Reference<XView> PresenterViewFactory::CreateToolBarView(
+ const Reference<XResourceId>& rxViewId) const
+{
+ return new PresenterToolBarView(
+ mxComponentContext,
+ rxViewId,
+ Reference<frame::XController>(mxControllerWeak),
+ mpPresenterController);
+}
+
+Reference<XView> PresenterViewFactory::CreateNotesView(
+ const Reference<XResourceId>& rxViewId) const
+{
+ Reference<XView> xView;
+
+ if ( ! mxConfigurationController.is())
+ return xView;
+ if ( ! mxComponentContext.is())
+ return xView;
+
+ try
+ {
+ xView.set(static_cast<XWeak*>(
+ new PresenterNotesView(
+ mxComponentContext,
+ rxViewId,
+ Reference<frame::XController>(mxControllerWeak),
+ mpPresenterController)),
+ UNO_QUERY_THROW);
+ }
+ catch (RuntimeException&)
+ {
+ xView = nullptr;
+ }
+
+ return xView;
+}
+
+Reference<XView> PresenterViewFactory::CreateSlideSorterView(
+ const Reference<XResourceId>& rxViewId) const
+{
+ Reference<XView> xView;
+
+ if ( ! mxConfigurationController.is())
+ return xView;
+ if ( ! mxComponentContext.is())
+ return xView;
+
+ try
+ {
+ rtl::Reference<PresenterSlideSorter> pView (
+ new PresenterSlideSorter(
+ mxComponentContext,
+ rxViewId,
+ Reference<frame::XController>(mxControllerWeak),
+ mpPresenterController));
+ xView = pView.get();
+ }
+ catch (RuntimeException&)
+ {
+ xView = nullptr;
+ }
+
+ return xView;
+}
+
+Reference<XView> PresenterViewFactory::CreateHelpView(
+ const Reference<XResourceId>& rxViewId) const
+{
+ return Reference<XView>(new PresenterHelpView(
+ mxComponentContext,
+ rxViewId,
+ Reference<frame::XController>(mxControllerWeak),
+ mpPresenterController));
+}
+
+void PresenterViewFactory::ThrowIfDisposed() const
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterViewFactory object has already been disposed",
+ const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
+ }
+}
+
+//===== CachablePresenterView =================================================
+
+CachablePresenterView::CachablePresenterView()
+ : mbIsPresenterViewActive(true)
+{
+}
+
+void CachablePresenterView::ActivatePresenterView()
+{
+ mbIsPresenterViewActive = true;
+}
+
+void CachablePresenterView::DeactivatePresenterView()
+{
+ mbIsPresenterViewActive = false;
+}
+
+void CachablePresenterView::ReleaseView()
+{
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterViewFactory.hxx b/sdext/source/presenter/PresenterViewFactory.hxx
new file mode 100644
index 000000000..30d488cfc
--- /dev/null
+++ b/sdext/source/presenter/PresenterViewFactory.hxx
@@ -0,0 +1,164 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERVIEWFACTORY_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERVIEWFACTORY_HXX
+
+#include "PresenterController.hxx"
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
+#include <com/sun/star/drawing/framework/XResourceFactory.hpp>
+#include <com/sun/star/drawing/framework/XView.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <rtl/ref.hxx>
+#include <memory>
+
+namespace sdext::presenter {
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::drawing::framework::XResourceFactory
+> PresenterViewFactoryInterfaceBase;
+
+/** Base class for presenter views that allows the view factory to store
+ them in a cache and reuse deactivated views.
+*/
+class CachablePresenterView
+{
+public:
+ virtual void ActivatePresenterView();
+
+ /** Called when the view is put into a cache. The view must not paint
+ itself while being deactivated.
+ */
+ virtual void DeactivatePresenterView();
+
+ /** Called before the view is disposed. This gives the view the
+ opportunity to trigger actions that may lead to (synchronous)
+ callbacks that do not result in DisposedExceptions.
+ */
+ virtual void ReleaseView();
+
+protected:
+ bool mbIsPresenterViewActive;
+
+ CachablePresenterView();
+
+ ~CachablePresenterView() {}
+};
+
+/** Factory of the presenter screen specific views. The supported set of
+ views includes:
+ a life view of the current slide,
+ a static preview of the next slide,
+ the notes of the current slide,
+ a tool bar
+*/
+class PresenterViewFactory
+ : public ::cppu::BaseMutex,
+ public PresenterViewFactoryInterfaceBase
+{
+public:
+ static constexpr OUStringLiteral msCurrentSlidePreviewViewURL
+ = u"private:resource/view/Presenter/CurrentSlidePreview";
+ static constexpr OUStringLiteral msNextSlidePreviewViewURL
+ = u"private:resource/view/Presenter/NextSlidePreview";
+ static constexpr OUStringLiteral msNotesViewURL = u"private:resource/view/Presenter/Notes";
+ static constexpr OUStringLiteral msToolBarViewURL = u"private:resource/view/Presenter/ToolBar";
+ static constexpr OUStringLiteral msSlideSorterURL
+ = u"private:resource/view/Presenter/SlideSorter";
+ static constexpr OUStringLiteral msHelpViewURL = u"private:resource/view/Presenter/Help";
+
+ /** Create a new instance of this class and register it as resource
+ factory in the drawing framework of the given controller.
+ This registration keeps it alive. When the drawing framework is
+ shut down and releases its reference to the factory then the factory
+ is destroyed.
+ */
+ static css::uno::Reference<css::drawing::framework::XResourceFactory> Create (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterViewFactory() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ // XResourceFactory
+
+ virtual css::uno::Reference<css::drawing::framework::XResource>
+ SAL_CALL createResource (
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId) override;
+
+ virtual void SAL_CALL
+ releaseResource (
+ const css::uno::Reference<css::drawing::framework::XResource>& rxPane) override;
+
+private:
+ css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+ css::uno::Reference<css::drawing::framework::XConfigurationController>
+ mxConfigurationController;
+ css::uno::WeakReference<css::frame::XController> mxControllerWeak;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ typedef ::std::pair<css::uno::Reference<css::drawing::framework::XView>,
+ css::uno::Reference<css::drawing::framework::XPane> > ViewResourceDescriptor;
+ typedef ::std::map<OUString, ViewResourceDescriptor> ResourceContainer;
+ std::unique_ptr<ResourceContainer> mpResourceCache;
+
+ PresenterViewFactory (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+
+ void Register (const css::uno::Reference<css::frame::XController>& rxController);
+
+ css::uno::Reference<css::drawing::framework::XView> CreateSlideShowView(
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId) const;
+
+ css::uno::Reference<css::drawing::framework::XView> CreateSlidePreviewView(
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId,
+ const css::uno::Reference<css::drawing::framework::XPane>& rxPane) const;
+
+ css::uno::Reference<css::drawing::framework::XView> CreateToolBarView(
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId) const;
+
+ css::uno::Reference<css::drawing::framework::XView> CreateNotesView(
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId) const;
+
+ css::uno::Reference<css::drawing::framework::XView> CreateSlideSorterView(
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId) const;
+
+ css::uno::Reference<css::drawing::framework::XView> CreateHelpView(
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId) const;
+
+ css::uno::Reference<css::drawing::framework::XResource> GetViewFromCache (
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId,
+ const css::uno::Reference<css::drawing::framework::XPane>& rxAnchorPane) const;
+ css::uno::Reference<css::drawing::framework::XResource> CreateView(
+ const css::uno::Reference<css::drawing::framework::XResourceId>& rxViewId,
+ const css::uno::Reference<css::drawing::framework::XPane>& rxAnchorPane);
+
+ /// @throws css::lang::DisposedException
+ void ThrowIfDisposed() const;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterWindowManager.cxx b/sdext/source/presenter/PresenterWindowManager.cxx
new file mode 100644
index 000000000..d1ccaadc7
--- /dev/null
+++ b/sdext/source/presenter/PresenterWindowManager.cxx
@@ -0,0 +1,1043 @@
+/* -*- 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 <vcl/settings.hxx>
+#include "PresenterWindowManager.hxx"
+#include "PresenterController.hxx"
+#include "PresenterGeometryHelper.hxx"
+#include "PresenterPaintManager.hxx"
+#include "PresenterPaneBorderPainter.hxx"
+#include "PresenterPaneContainer.hxx"
+#include "PresenterPaneFactory.hxx"
+#include "PresenterToolBar.hxx"
+#include "PresenterViewFactory.hxx"
+#include "PresenterTheme.hxx"
+#include <com/sun/star/awt/InvalidateStyle.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/FillRule.hpp>
+#include <com/sun/star/rendering/Texture.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <math.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing::framework;
+
+namespace sdext::presenter {
+
+//===== PresenterWindowManager ================================================
+
+PresenterWindowManager::PresenterWindowManager (
+ const Reference<XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterPaneContainer>& rpPaneContainer,
+ const ::rtl::Reference<PresenterController>& rpPresenterController)
+ : PresenterWindowManagerInterfaceBase(m_aMutex),
+ mxComponentContext(rxContext),
+ mpPresenterController(rpPresenterController),
+ mpPaneContainer(rpPaneContainer),
+ mbIsLayoutPending(true),
+ mbIsLayouting(false),
+ meLayoutMode(LM_Generic),
+ mbIsSlideSorterActive(false),
+ mbIsHelpViewActive(false),
+ mbisPaused(false),
+ mbIsMouseClickPending(false)
+{
+
+}
+
+PresenterWindowManager::~PresenterWindowManager()
+{
+}
+
+void SAL_CALL PresenterWindowManager::disposing()
+{
+ NotifyDisposing();
+
+ SetParentPane(nullptr);
+
+ Reference<lang::XComponent> xComponent (mxPaneBorderManager, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ mxPaneBorderManager = nullptr;
+
+ for (const auto& rxPane : mpPaneContainer->maPanes)
+ {
+ if (rxPane->mxBorderWindow.is())
+ {
+ rxPane->mxBorderWindow->removeWindowListener(this);
+ rxPane->mxBorderWindow->removeFocusListener(this);
+ rxPane->mxBorderWindow->removeMouseListener(this);
+ }
+ }
+}
+
+void PresenterWindowManager::SetParentPane (
+ const Reference<drawing::framework::XPane>& rxPane)
+{
+ if (mxParentWindow.is())
+ {
+ mxParentWindow->removeWindowListener(this);
+ mxParentWindow->removePaintListener(this);
+ mxParentWindow->removeMouseListener(this);
+ mxParentWindow->removeFocusListener(this);
+ }
+ mxParentWindow = nullptr;
+ mxParentCanvas = nullptr;
+
+ if (rxPane.is())
+ {
+ mxParentWindow = rxPane->getWindow();
+ mxParentCanvas = rxPane->getCanvas();
+ }
+ else
+ {
+ mxParentWindow = nullptr;
+ }
+
+ if (mxParentWindow.is())
+ {
+ mxParentWindow->addWindowListener(this);
+ mxParentWindow->addPaintListener(this);
+ mxParentWindow->addMouseListener(this);
+ mxParentWindow->addFocusListener(this);
+
+ // We paint our own background, make that of the parent window transparent.
+ Reference<awt::XWindowPeer> xPeer (mxParentWindow, UNO_QUERY);
+ if (xPeer.is())
+ xPeer->setBackground(util::Color(0xff000000));
+ }
+}
+
+void PresenterWindowManager::SetTheme (const std::shared_ptr<PresenterTheme>& rpTheme)
+{
+ mpTheme = rpTheme;
+
+ // Get background bitmap or background color from the theme.
+
+ if (mpTheme != nullptr)
+ {
+ mpBackgroundBitmap = mpTheme->GetBitmap(OUString(), "Background");
+ }
+}
+
+void PresenterWindowManager::NotifyViewCreation (const Reference<XView>& rxView)
+{
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ mpPaneContainer->FindPaneId(rxView->getResourceId()->getAnchor()));
+ OSL_ASSERT(pDescriptor);
+ if (pDescriptor)
+ {
+ Layout();
+
+ mpPresenterController->GetPaintManager()->Invalidate(
+ pDescriptor->mxContentWindow,
+ sal_Int16(awt::InvalidateStyle::TRANSPARENT
+ | awt::InvalidateStyle::CHILDREN));
+ }
+}
+
+void PresenterWindowManager::SetPanePosSizeAbsolute (
+ const OUString& rsPaneURL,
+ const double nX,
+ const double nY,
+ const double nWidth,
+ const double nHeight)
+{
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ mpPaneContainer->FindPaneURL(rsPaneURL));
+ if (pDescriptor)
+ {
+ if (pDescriptor->mxBorderWindow.is())
+ pDescriptor->mxBorderWindow->setPosSize(
+ ::sal::static_int_cast<sal_Int32>(nX),
+ ::sal::static_int_cast<sal_Int32>(nY),
+ ::sal::static_int_cast<sal_Int32>(nWidth),
+ ::sal::static_int_cast<sal_Int32>(nHeight),
+ awt::PosSize::POSSIZE);
+ }
+}
+
+void PresenterWindowManager::SetPaneBorderPainter (
+ const ::rtl::Reference<PresenterPaneBorderPainter>& rPainter)
+{
+ mpPaneBorderPainter = rPainter;
+}
+
+//----- XWindowListener -------------------------------------------------------
+
+void SAL_CALL PresenterWindowManager::windowResized (const awt::WindowEvent& rEvent)
+{
+ ThrowIfDisposed();
+ if (rEvent.Source == mxParentWindow)
+ {
+ Layout();
+ }
+ else
+ {
+ Reference<awt::XWindow> xWindow (rEvent.Source,UNO_QUERY);
+ if (xWindow.is())
+ {
+ UpdateWindowSize(xWindow);
+
+ // Make sure the background of a transparent window is painted.
+ mpPresenterController->GetPaintManager()->Invalidate(mxParentWindow);
+ }
+ }
+}
+
+void SAL_CALL PresenterWindowManager::windowMoved (const awt::WindowEvent& rEvent)
+{
+ ThrowIfDisposed();
+ if (rEvent.Source != mxParentWindow)
+ {
+ Reference<awt::XWindow> xWindow (rEvent.Source,UNO_QUERY);
+ UpdateWindowSize(xWindow);
+
+ // Make sure the background of a transparent window is painted.
+ mpPresenterController->GetPaintManager()->Invalidate(xWindow);
+ }
+}
+
+void SAL_CALL PresenterWindowManager::windowShown (const lang::EventObject&) {}
+
+void SAL_CALL PresenterWindowManager::windowHidden (const lang::EventObject&) {}
+
+//----- XPaintListener --------------------------------------------------------
+
+void SAL_CALL PresenterWindowManager::windowPaint (const awt::PaintEvent& rEvent)
+{
+ ThrowIfDisposed();
+
+ if ( ! mxParentWindow.is())
+ return;
+ if ( ! mxParentCanvas.is())
+ return;
+
+ if (mpTheme == nullptr)
+ return;
+
+ try
+ {
+ if (mbIsLayoutPending)
+ Layout();
+ PaintBackground(rEvent.UpdateRect);
+ PaintChildren(rEvent);
+ }
+ catch (RuntimeException&)
+ {
+ OSL_FAIL("paint failed!");
+ }
+}
+
+//----- XMouseListener --------------------------------------------------------
+
+void SAL_CALL PresenterWindowManager::mousePressed (const css::awt::MouseEvent&)
+{
+ if (!mbIsSlideSorterActive) // tdf#127921
+ mbIsMouseClickPending = true;
+}
+
+void SAL_CALL PresenterWindowManager::mouseReleased (const css::awt::MouseEvent& rEvent)
+{
+ if (mbIsMouseClickPending)
+ {
+ mbIsMouseClickPending = false;
+ mpPresenterController->HandleMouseClick(rEvent);
+ }
+}
+
+void SAL_CALL PresenterWindowManager::mouseEntered (const css::awt::MouseEvent&)
+{
+ mbIsMouseClickPending = false;
+}
+
+void SAL_CALL PresenterWindowManager::mouseExited (const css::awt::MouseEvent&)
+{
+ mbIsMouseClickPending = false;
+}
+
+//----- XFocusListener --------------------------------------------------------
+
+void SAL_CALL PresenterWindowManager::focusGained (const css::awt::FocusEvent& /*rEvent*/)
+{
+ ThrowIfDisposed();
+}
+
+void SAL_CALL PresenterWindowManager::focusLost (const css::awt::FocusEvent&)
+{
+ ThrowIfDisposed();
+}
+
+//----- XEventListener --------------------------------------------------------
+
+void SAL_CALL PresenterWindowManager::disposing (const lang::EventObject& rEvent)
+{
+ if (rEvent.Source == mxParentWindow)
+ mxParentWindow = nullptr;
+}
+
+
+void PresenterWindowManager::PaintChildren (const awt::PaintEvent& rEvent) const
+{
+ // Call windowPaint on all children that lie in or touch the
+ // update rectangle.
+ for (const auto& rxPane : mpPaneContainer->maPanes)
+ {
+ try
+ {
+ // Make sure that the pane shall and can be painted.
+ if ( ! rxPane->mbIsActive)
+ continue;
+ if (rxPane->mbIsSprite)
+ continue;
+ if ( ! rxPane->mxPane.is())
+ continue;
+ if ( ! rxPane->mxBorderWindow.is())
+ continue;
+ Reference<awt::XWindow> xBorderWindow (rxPane->mxBorderWindow);
+ if ( ! xBorderWindow.is())
+ continue;
+
+ // Get the area in which the border of the pane has to be painted.
+ const awt::Rectangle aBorderBox (xBorderWindow->getPosSize());
+ const awt::Rectangle aBorderUpdateBox(
+ PresenterGeometryHelper::Intersection(
+ rEvent.UpdateRect,
+ aBorderBox));
+ if (aBorderUpdateBox.Width<=0 || aBorderUpdateBox.Height<=0)
+ continue;
+
+ const awt::Rectangle aLocalBorderUpdateBox(
+ PresenterGeometryHelper::TranslateRectangle(
+ aBorderUpdateBox,
+ -aBorderBox.X,
+ -aBorderBox.Y));
+
+ // Invalidate the area of the content window.
+ mpPresenterController->GetPaintManager()->Invalidate(
+ xBorderWindow,
+ aLocalBorderUpdateBox,
+ sal_Int16(awt::InvalidateStyle::CHILDREN
+ | awt::InvalidateStyle::NOTRANSPARENT));
+ }
+ catch (RuntimeException&)
+ {
+ OSL_FAIL("paint children failed!");
+ }
+ }
+}
+
+void PresenterWindowManager::SetLayoutMode (const LayoutMode eMode)
+{
+ OSL_ASSERT(mpPresenterController);
+
+ if (meLayoutMode == eMode
+ && !mbIsSlideSorterActive
+ && !mbIsHelpViewActive)
+ return;
+
+ meLayoutMode = eMode;
+ mbIsSlideSorterActive = false;
+ mbIsHelpViewActive = false;
+
+ mpPresenterController->RequestViews(
+ mbIsSlideSorterActive,
+ meLayoutMode==LM_Notes,
+ mbIsHelpViewActive);
+ Layout();
+ NotifyLayoutModeChange();
+}
+
+void PresenterWindowManager::SetSlideSorterState (bool bIsActive)
+{
+ if (mbIsSlideSorterActive == bIsActive)
+ return;
+
+ mbIsSlideSorterActive = bIsActive;
+ if (mbIsSlideSorterActive)
+ mbIsHelpViewActive = false;
+ StoreViewMode(GetViewMode());
+
+ mpPresenterController->RequestViews(
+ mbIsSlideSorterActive,
+ meLayoutMode==LM_Notes,
+ mbIsHelpViewActive);
+ Layout();
+ NotifyLayoutModeChange();
+}
+
+void PresenterWindowManager::SetHelpViewState (bool bIsActive)
+{
+ if (mbIsHelpViewActive == bIsActive)
+ return;
+
+ mbIsHelpViewActive = bIsActive;
+ if (mbIsHelpViewActive)
+ mbIsSlideSorterActive = false;
+ StoreViewMode(GetViewMode());
+
+ mpPresenterController->RequestViews(
+ mbIsSlideSorterActive,
+ meLayoutMode==LM_Notes,
+ mbIsHelpViewActive);
+ Layout();
+ NotifyLayoutModeChange();
+}
+
+void PresenterWindowManager::SetPauseState (bool bIsPaused)
+{
+ if (mbisPaused == bIsPaused)
+ return;
+
+ mbisPaused = bIsPaused;
+
+ NotifyLayoutModeChange();
+}
+
+void PresenterWindowManager::SetViewMode (const ViewMode eMode)
+{
+ switch (eMode)
+ {
+ case VM_Standard:
+ SetSlideSorterState(false);
+ SetHelpViewState(false);
+ SetLayoutMode(LM_Standard);
+ break;
+
+ case VM_Notes:
+ SetSlideSorterState(false);
+ SetHelpViewState(false);
+ SetLayoutMode(LM_Notes);
+ break;
+
+ case VM_SlideOverview:
+ SetHelpViewState(false);
+ SetSlideSorterState(true);
+ break;
+
+ case VM_Help:
+ SetHelpViewState(true);
+ SetSlideSorterState(false);
+ break;
+ }
+
+ StoreViewMode(eMode);
+}
+
+PresenterWindowManager::ViewMode PresenterWindowManager::GetViewMode() const
+{
+ if (mbIsHelpViewActive)
+ return VM_Help;
+ else if (mbIsSlideSorterActive)
+ return VM_SlideOverview;
+ else if (meLayoutMode == LM_Notes)
+ return VM_Notes;
+ else
+ return VM_Standard;
+}
+
+void PresenterWindowManager::RestoreViewMode()
+{
+ sal_Int32 nMode (0);
+ PresenterConfigurationAccess aConfiguration (
+ mxComponentContext,
+ "/org.openoffice.Office.PresenterScreen/",
+ PresenterConfigurationAccess::READ_ONLY);
+ aConfiguration.GetConfigurationNode("Presenter/InitialViewMode") >>= nMode;
+ switch (nMode)
+ {
+ default:
+ case 0:
+ SetViewMode(VM_Standard);
+ break;
+
+ case 1:
+ SetViewMode(VM_Notes);
+ break;
+
+ case 2:
+ SetViewMode(VM_SlideOverview);
+ break;
+ }
+}
+
+void PresenterWindowManager::StoreViewMode (const ViewMode eViewMode)
+{
+ try
+ {
+ PresenterConfigurationAccess aConfiguration (
+ mxComponentContext,
+ "/org.openoffice.Office.PresenterScreen/",
+ PresenterConfigurationAccess::READ_WRITE);
+ aConfiguration.GoToChild("Presenter");
+ Any aValue;
+ switch (eViewMode)
+ {
+ default:
+ case VM_Standard:
+ aValue <<= sal_Int32(0);
+ break;
+
+ case VM_Notes:
+ aValue <<= sal_Int32(1);
+ break;
+
+ case VM_SlideOverview:
+ aValue <<= sal_Int32(2);
+ break;
+ }
+
+ aConfiguration.SetProperty ("InitialViewMode", aValue);
+ aConfiguration.CommitChanges();
+ }
+ catch (Exception&)
+ {
+ }
+}
+
+void PresenterWindowManager::AddLayoutListener (
+ const Reference<document::XEventListener>& rxListener)
+{
+ maLayoutListeners.push_back(rxListener);
+}
+
+void PresenterWindowManager::RemoveLayoutListener (
+ const Reference<document::XEventListener>& rxListener)
+{
+ // Assume that there are no multiple entries.
+ auto iListener = std::find(maLayoutListeners.begin(), maLayoutListeners.end(), rxListener);
+ if (iListener != maLayoutListeners.end())
+ maLayoutListeners.erase(iListener);
+}
+
+void PresenterWindowManager::Layout()
+{
+ if (!mxParentWindow.is() || mbIsLayouting)
+ return;
+
+ mbIsLayoutPending = false;
+ mbIsLayouting = true;
+ mxScaledBackgroundBitmap = nullptr;
+ mxClipPolygon = nullptr;
+
+ try
+ {
+ if (mbIsSlideSorterActive)
+ LayoutSlideSorterMode();
+ else if (mbIsHelpViewActive)
+ LayoutHelpMode();
+ else
+ switch (meLayoutMode)
+ {
+ case LM_Standard:
+ default:
+ LayoutStandardMode();
+ break;
+
+ case LM_Notes:
+ LayoutNotesMode();
+ break;
+ }
+ }
+ catch (Exception&)
+ {
+ OSL_ASSERT(false);
+ throw;
+ }
+
+ mbIsLayouting = false;
+}
+
+void PresenterWindowManager::LayoutStandardMode()
+{
+ awt::Rectangle aBox = mxParentWindow->getPosSize();
+
+ const double nGoldenRatio ((1 + sqrt(5.0)) / 2);
+ const double nGap (20);
+ const double nHorizontalSlideDivide (aBox.Width / nGoldenRatio);
+ double nSlidePreviewTop (0);
+
+
+ // For the current slide view calculate the outer height from the outer
+ // width. This takes into account the slide aspect ratio and thus has to
+ // go over the inner pane size.
+ PresenterPaneContainer::SharedPaneDescriptor pPane (
+ mpPaneContainer->FindPaneURL(PresenterPaneFactory::msCurrentSlidePreviewPaneURL));
+ if (pPane)
+ {
+ const awt::Size aCurrentSlideOuterBox(CalculatePaneSize(
+ nHorizontalSlideDivide - 1.5*nGap,
+ PresenterPaneFactory::msCurrentSlidePreviewPaneURL));
+ nSlidePreviewTop = (aBox.Height - aCurrentSlideOuterBox.Height) / 2;
+ double Temp=nGap;
+ /// check whether RTL interface or not
+ if(AllSettings::GetLayoutRTL())
+ Temp=aBox.Width - aCurrentSlideOuterBox.Width - nGap;
+ SetPanePosSizeAbsolute (
+ PresenterPaneFactory::msCurrentSlidePreviewPaneURL,
+ Temp,
+ nSlidePreviewTop,
+ aCurrentSlideOuterBox.Width,
+ aCurrentSlideOuterBox.Height);
+ }
+
+ // For the next slide view calculate the outer height from the outer
+ // width. This takes into account the slide aspect ratio and thus has to
+ // go over the inner pane size.
+ pPane = mpPaneContainer->FindPaneURL(PresenterPaneFactory::msNextSlidePreviewPaneURL);
+ if (pPane)
+ {
+ const awt::Size aNextSlideOuterBox (CalculatePaneSize(
+ aBox.Width - nHorizontalSlideDivide - 1.5*nGap,
+ PresenterPaneFactory::msNextSlidePreviewPaneURL));
+ double Temp=aBox.Width - aNextSlideOuterBox.Width - nGap;
+ /// check whether RTL interface or not
+ if(AllSettings::GetLayoutRTL())
+ Temp=nGap;
+ SetPanePosSizeAbsolute (
+ PresenterPaneFactory::msNextSlidePreviewPaneURL,
+ Temp,
+ nSlidePreviewTop,
+ aNextSlideOuterBox.Width,
+ aNextSlideOuterBox.Height);
+ }
+
+ LayoutToolBar();
+}
+
+void PresenterWindowManager::LayoutNotesMode()
+{
+ awt::Rectangle aBox = mxParentWindow->getPosSize();
+
+ const geometry::RealRectangle2D aToolBarBox (LayoutToolBar());
+
+ const double nGoldenRatio ((1 + sqrt(5.0)) / 2);
+ const double nGap (20);
+ const double nPrimaryWidth (aBox.Width / nGoldenRatio);
+ const double nSecondaryWidth (aBox.Width - nPrimaryWidth);
+ const double nTertiaryWidth (nSecondaryWidth / nGoldenRatio);
+ double nSlidePreviewTop (0);
+ double nNotesViewBottom (aToolBarBox.Y1 - nGap);
+ /// check whether RTL interface or not
+
+
+ // The notes view has no fixed aspect ratio.
+ PresenterPaneContainer::SharedPaneDescriptor pPane (
+ mpPaneContainer->FindPaneURL(PresenterPaneFactory::msNotesPaneURL));
+ if (pPane)
+ {
+ const geometry::RealSize2D aNotesViewOuterSize(
+ nPrimaryWidth - 1.5*nGap + 0.5,
+ nNotesViewBottom);
+ nSlidePreviewTop = (aBox.Height
+ - aToolBarBox.Y2 + aToolBarBox.Y1 - aNotesViewOuterSize.Height) / 2;
+ /// check whether RTL interface or not
+ double Temp=aBox.Width - aNotesViewOuterSize.Width - nGap;
+ if(AllSettings::GetLayoutRTL())
+ Temp=nGap;
+ SetPanePosSizeAbsolute (
+ PresenterPaneFactory::msNotesPaneURL,
+ Temp,
+ nSlidePreviewTop,
+ aNotesViewOuterSize.Width,
+ aNotesViewOuterSize.Height);
+ nNotesViewBottom = nSlidePreviewTop + aNotesViewOuterSize.Height;
+ }
+
+ // For the current slide view calculate the outer height from the outer
+ // width. This takes into account the slide aspect ratio and thus has to
+ // go over the inner pane size.
+ pPane = mpPaneContainer->FindPaneURL(PresenterPaneFactory::msCurrentSlidePreviewPaneURL);
+ if (pPane)
+ {
+ const awt::Size aCurrentSlideOuterBox(CalculatePaneSize(
+ nSecondaryWidth - 1.5*nGap,
+ PresenterPaneFactory::msCurrentSlidePreviewPaneURL));
+ /// check whether RTL interface or not
+ double Temp=nGap;
+ if(AllSettings::GetLayoutRTL())
+ Temp=aBox.Width - aCurrentSlideOuterBox.Width - nGap;
+ SetPanePosSizeAbsolute (
+ PresenterPaneFactory::msCurrentSlidePreviewPaneURL,
+ Temp,
+ nSlidePreviewTop,
+ aCurrentSlideOuterBox.Width,
+ aCurrentSlideOuterBox.Height);
+ }
+
+ // For the next slide view calculate the outer height from the outer
+ // width. This takes into account the slide aspect ratio and thus has to
+ // go over the inner pane size.
+ pPane = mpPaneContainer->FindPaneURL(PresenterPaneFactory::msNextSlidePreviewPaneURL);
+ if (!pPane)
+ return;
+
+ const awt::Size aNextSlideOuterBox (CalculatePaneSize(
+ nTertiaryWidth,
+ PresenterPaneFactory::msNextSlidePreviewPaneURL));
+ /// check whether RTL interface or not
+ double Temp=nGap;
+ if(AllSettings::GetLayoutRTL())
+ Temp=aBox.Width - aNextSlideOuterBox.Width - nGap;
+ SetPanePosSizeAbsolute (
+ PresenterPaneFactory::msNextSlidePreviewPaneURL,
+ Temp,
+ nNotesViewBottom - aNextSlideOuterBox.Height,
+ aNextSlideOuterBox.Width,
+ aNextSlideOuterBox.Height);
+
+
+}
+
+void PresenterWindowManager::LayoutSlideSorterMode()
+{
+ const geometry::RealRectangle2D aToolBarBox (LayoutToolBar());
+
+ awt::Rectangle aWindowBox = mxParentWindow->getPosSize();
+ const double nGap (20);
+ SetPanePosSizeAbsolute(
+ mpPaneContainer->GetPaneURLForViewURL(PresenterViewFactory::msSlideSorterURL),
+ nGap,
+ nGap,
+ aWindowBox.Width - 2*nGap,
+ aToolBarBox.Y1 - 2*nGap);
+}
+
+void PresenterWindowManager::LayoutHelpMode()
+{
+ const geometry::RealRectangle2D aToolBarBox (LayoutToolBar());
+
+ awt::Rectangle aWindowBox = mxParentWindow->getPosSize();
+ const double nGap (20);
+ const double nGoldenRatio ((1 + sqrt(5.0)) / 2);
+ const double nWidth = ::std::min(aWindowBox.Width - 2*nGap, aWindowBox.Width/nGoldenRatio);
+ SetPanePosSizeAbsolute(
+ mpPaneContainer->GetPaneURLForViewURL(PresenterViewFactory::msHelpViewURL),
+ (aWindowBox.Width - nWidth)/2,
+ nGap,
+ nWidth,
+ aToolBarBox.Y1 - 2*nGap);
+}
+
+geometry::RealRectangle2D PresenterWindowManager::LayoutToolBar()
+{
+ double nToolBarWidth (400);
+ double nToolBarHeight (80);
+
+ // Get access to the tool bar.
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor(
+ mpPaneContainer->FindPaneURL(PresenterPaneFactory::msToolBarPaneURL));
+ if (pDescriptor)
+ {
+ PresenterToolBarView* pToolBarView
+ = dynamic_cast<PresenterToolBarView*>(pDescriptor->mxView.get());
+ if (pToolBarView != nullptr && pToolBarView->GetPresenterToolBar().is())
+ {
+ geometry::RealSize2D aSize (pToolBarView->GetPresenterToolBar()->GetMinimalSize());
+
+ if (mpPaneBorderPainter.is())
+ {
+ const awt::Rectangle aBox (mpPaneBorderPainter->addBorder (
+ PresenterPaneFactory::msToolBarPaneURL,
+ awt::Rectangle(
+ 0,
+ 0,
+ PresenterGeometryHelper::Round(aSize.Width),
+ PresenterGeometryHelper::Round(aSize.Height)),
+ css::drawing::framework::BorderType_TOTAL_BORDER));
+
+ nToolBarWidth = aBox.Width;
+ nToolBarHeight = aBox.Height;
+ }
+ else
+ {
+ nToolBarWidth = aSize.Width + 20;
+ nToolBarHeight = aSize.Height + 10;
+ }
+ }
+ }
+
+ const awt::Rectangle aBox = mxParentWindow->getPosSize();
+ const double nToolBarX ((aBox.Width - nToolBarWidth) / 2);
+ const double nToolBarY (aBox.Height - nToolBarHeight);
+ SetPanePosSizeAbsolute(
+ PresenterPaneFactory::msToolBarPaneURL,
+ nToolBarX,
+ nToolBarY,
+ nToolBarWidth,
+ nToolBarHeight);
+
+ return geometry::RealRectangle2D(
+ nToolBarX,
+ nToolBarY,
+ nToolBarX + nToolBarWidth - 1,
+ nToolBarY + nToolBarHeight - 1);
+}
+
+awt::Size PresenterWindowManager::CalculatePaneSize (
+ const double nOuterWidth,
+ const OUString& rsPaneURL)
+{
+ // Calculate the inner width by removing the pane border.
+ awt::Rectangle aInnerBox (mpPaneBorderPainter->RemoveBorder (
+ rsPaneURL,
+ awt::Rectangle(0,0,
+ sal_Int32(nOuterWidth+0.5),sal_Int32(nOuterWidth)),
+ drawing::framework::BorderType_TOTAL_BORDER));
+
+ // Calculate the inner height with the help of the slide aspect ratio.
+ const double nCurrentSlideInnerHeight (
+ aInnerBox.Width / mpPresenterController->GetSlideAspectRatio());
+
+ // Add the pane border to get the outer box.
+ awt::Rectangle aOuterBox (mpPaneBorderPainter->AddBorder (
+ rsPaneURL,
+ awt::Rectangle(0,0,
+ aInnerBox.Width,sal_Int32(nCurrentSlideInnerHeight+0.5)),
+ drawing::framework::BorderType_TOTAL_BORDER));
+
+ return awt::Size(aOuterBox.Width, aOuterBox.Height);
+}
+
+void PresenterWindowManager::NotifyLayoutModeChange()
+{
+ document::EventObject aEvent;
+ aEvent.Source = Reference<XInterface>(static_cast<XWeak*>(this));
+
+ LayoutListenerContainer aContainerCopy (maLayoutListeners);
+ for (const auto& rxListener : aContainerCopy)
+ {
+ if (rxListener.is())
+ {
+ try
+ {
+ rxListener->notifyEvent(aEvent);
+ }
+ catch (lang::DisposedException&)
+ {
+ RemoveLayoutListener(rxListener);
+ }
+ catch (RuntimeException&)
+ {
+ }
+ }
+ }
+}
+
+void PresenterWindowManager::NotifyDisposing()
+{
+ lang::EventObject aEvent;
+ aEvent.Source = static_cast<XWeak*>(this);
+
+ LayoutListenerContainer aContainer;
+ aContainer.swap(maLayoutListeners);
+ for (auto& rxListener : aContainer)
+ {
+ if (rxListener.is())
+ {
+ try
+ {
+ rxListener->disposing(aEvent);
+ }
+ catch (lang::DisposedException&)
+ {
+ }
+ catch (RuntimeException&)
+ {
+ }
+ }
+ }
+}
+
+void PresenterWindowManager::UpdateWindowSize (const Reference<awt::XWindow>& rxBorderWindow)
+{
+ PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
+ mpPaneContainer->FindBorderWindow(rxBorderWindow));
+ if (pDescriptor)
+ {
+ mxClipPolygon = nullptr;
+
+ // ToTop is called last because it may invalidate the iterator.
+ if ( ! mbIsLayouting)
+ mpPaneContainer->ToTop(pDescriptor);
+ }
+}
+
+void PresenterWindowManager::PaintBackground (const awt::Rectangle& rUpdateBox)
+{
+ if ( ! mxParentWindow.is())
+ return;
+
+ Reference<rendering::XGraphicDevice> xDevice (mxParentCanvas->getDevice());
+ if ( ! xDevice.is())
+ return;
+
+ // Create a polygon for the background and for clipping.
+ Reference<rendering::XPolyPolygon2D> xBackgroundPolygon (
+ PresenterGeometryHelper::CreatePolygon(mxParentWindow->getPosSize(), xDevice));
+ if ( ! mxClipPolygon.is())
+ mxClipPolygon = CreateClipPolyPolygon();
+
+ // Create View- and RenderState structs.
+ const rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ PresenterGeometryHelper::CreatePolygon(rUpdateBox, xDevice));
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ mxClipPolygon,
+ Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ // Paint the background.
+ if (!mpBackgroundBitmap)
+ return;
+
+ ProvideBackgroundBitmap();
+
+ if (mxScaledBackgroundBitmap.is())
+ {
+ const geometry::IntegerSize2D aBitmapSize(mxScaledBackgroundBitmap->getSize());
+ Sequence<rendering::Texture> aTextures
+ {
+ {
+ geometry::AffineMatrix2D( aBitmapSize.Width,0,0, 0,aBitmapSize.Height,0),
+ 1,
+ 0,
+ mxScaledBackgroundBitmap,
+ nullptr,
+ nullptr,
+ rendering::StrokeAttributes(),
+ rendering::TexturingMode::REPEAT,
+ rendering::TexturingMode::REPEAT
+ }
+ };
+
+ mxParentCanvas->fillTexturedPolyPolygon(
+ xBackgroundPolygon,
+ aViewState,
+ aRenderState,
+ aTextures);
+ }
+ else
+ {
+ const util::Color aBackgroundColor (mpBackgroundBitmap->maReplacementColor);
+ auto pDeviceColor = aRenderState.DeviceColor.getArray();
+ pDeviceColor[0] = ((aBackgroundColor >> 16) & 0x0ff) / 255.0;
+ pDeviceColor[1] = ((aBackgroundColor >> 8) & 0x0ff) / 255.0;
+ pDeviceColor[2] = ((aBackgroundColor >> 0) & 0x0ff) / 255.0;
+ pDeviceColor[3] = ((aBackgroundColor >> 24) & 0x0ff) / 255.0;
+ mxParentCanvas->fillPolyPolygon(
+ xBackgroundPolygon,
+ aViewState,
+ aRenderState);
+ }
+}
+
+void PresenterWindowManager::ProvideBackgroundBitmap()
+{
+ if ( mxScaledBackgroundBitmap.is())
+ return;
+
+ Reference<rendering::XBitmap> xBitmap (mpBackgroundBitmap->GetNormalBitmap());
+ if (!xBitmap.is())
+ return;
+
+ const bool bStretchVertical (mpBackgroundBitmap->meVerticalTexturingMode
+ == PresenterBitmapDescriptor::Stretch);
+ const bool bStretchHorizontal (mpBackgroundBitmap->meHorizontalTexturingMode
+ == PresenterBitmapDescriptor::Stretch);
+ if (bStretchHorizontal || bStretchVertical)
+ {
+ geometry::RealSize2D aSize;
+ if (bStretchVertical)
+ aSize.Height = mxParentWindow->getPosSize().Height;
+ else
+ aSize.Height = xBitmap->getSize().Height;
+ if (bStretchHorizontal)
+ aSize.Width = mxParentWindow->getPosSize().Width;
+ else
+ aSize.Width = xBitmap->getSize().Width;
+ mxScaledBackgroundBitmap = xBitmap->getScaledBitmap(aSize, false);
+ }
+ else
+ {
+ mxScaledBackgroundBitmap = xBitmap;
+ }
+}
+
+Reference<rendering::XPolyPolygon2D> PresenterWindowManager::CreateClipPolyPolygon() const
+{
+ // Create a clip polygon that includes the whole update area but has the
+ // content windows as holes.
+ const sal_Int32 nPaneCount (mpPaneContainer->maPanes.size());
+ ::std::vector<awt::Rectangle> aRectangles;
+ aRectangles.reserve(1+nPaneCount);
+ aRectangles.push_back(mxParentWindow->getPosSize());
+ for (const auto& pDescriptor : mpPaneContainer->maPanes)
+ {
+ if ( ! pDescriptor->mbIsActive)
+ continue;
+ if ( ! pDescriptor->mbIsOpaque)
+ continue;
+ if ( ! pDescriptor->mxBorderWindow.is() || ! pDescriptor->mxContentWindow.is())
+ continue;
+ Reference<awt::XWindow2> xWindow (pDescriptor->mxBorderWindow, UNO_QUERY);
+ if (xWindow.is() && ! xWindow->isVisible())
+ continue;
+
+ const awt::Rectangle aOuterBorderBox (pDescriptor->mxBorderWindow->getPosSize());
+ awt::Rectangle aInnerBorderBox (pDescriptor->mxContentWindow->getPosSize());
+ aInnerBorderBox.X += aOuterBorderBox.X;
+ aInnerBorderBox.Y += aOuterBorderBox.Y;
+ aRectangles.push_back(aInnerBorderBox);
+ }
+ Reference<rendering::XPolyPolygon2D> xPolyPolygon (
+ PresenterGeometryHelper::CreatePolygon(
+ aRectangles,
+ mxParentCanvas->getDevice()));
+ if (xPolyPolygon.is())
+ xPolyPolygon->setFillRule(rendering::FillRule_EVEN_ODD);
+ return xPolyPolygon;
+}
+
+void PresenterWindowManager::Update()
+{
+ mxClipPolygon = nullptr;
+ mbIsLayoutPending = true;
+
+ mpPresenterController->GetPaintManager()->Invalidate(mxParentWindow);
+}
+
+void PresenterWindowManager::ThrowIfDisposed() const
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ throw lang::DisposedException (
+ "PresenterWindowManager has already been disposed",
+ const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
+ }
+}
+
+} // end of namespace ::sdext::presenter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/PresenterWindowManager.hxx b/sdext/source/presenter/PresenterWindowManager.hxx
new file mode 100644
index 000000000..9c032e6df
--- /dev/null
+++ b/sdext/source/presenter/PresenterWindowManager.hxx
@@ -0,0 +1,208 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERWINDOWMANAGER_HXX
+#define INCLUDED_SDEXT_SOURCE_PRESENTER_PRESENTERWINDOWMANAGER_HXX
+
+#include "PresenterPaneContainer.hxx"
+#include "PresenterTheme.hxx"
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/awt/XFocusListener.hpp>
+#include <com/sun/star/awt/XPaintListener.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/document/XEventListener.hpp>
+#include <com/sun/star/drawing/framework/XPane.hpp>
+#include <com/sun/star/rendering/XBitmap.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <rtl/ref.hxx>
+#include <memory>
+
+namespace sdext::presenter {
+
+class PresenterController;
+class PresenterPaneBorderPainter;
+class PresenterTheme;
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::awt::XWindowListener,
+ css::awt::XPaintListener,
+ css::awt::XMouseListener,
+ css::awt::XFocusListener
+> PresenterWindowManagerInterfaceBase;
+
+/** A simple manager of the positions of the panes of the presenter screen.
+ Uses relative coordinates of the four sides of each pane. Allows panes
+ to be moved or resized with the mouse.
+*/
+class PresenterWindowManager
+ : protected ::cppu::BaseMutex,
+ public PresenterWindowManagerInterfaceBase
+{
+public:
+ PresenterWindowManager (
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const ::rtl::Reference<PresenterPaneContainer>& rpPaneContainer,
+ const ::rtl::Reference<PresenterController>& rpPresenterController);
+ virtual ~PresenterWindowManager() override;
+ PresenterWindowManager(const PresenterWindowManager&) = delete;
+ PresenterWindowManager& operator=(const PresenterWindowManager&) = delete;
+
+ void SAL_CALL disposing() override;
+
+ void SetParentPane (const css::uno::Reference<css::drawing::framework::XPane>& rxPane);
+ void SetTheme (const std::shared_ptr<PresenterTheme>& rpTheme);
+ void NotifyViewCreation (const css::uno::Reference<css::drawing::framework::XView>& rxView);
+ void SetPanePosSizeAbsolute (
+ const OUString& rsPaneURL,
+ const double nX,
+ const double nY,
+ const double nWidth,
+ const double nHeight);
+ void SetPaneBorderPainter (const ::rtl::Reference<PresenterPaneBorderPainter>& rPainter);
+ void Update();
+ void Layout();
+
+ void SetSlideSorterState (bool bIsActive);
+ void SetHelpViewState (bool bIsActive);
+ void SetPauseState (bool bIsPaused);
+
+ enum LayoutMode { LM_Standard, LM_Notes, LM_Generic };
+private:
+ void SetLayoutMode (const LayoutMode eMode);
+
+public:
+ enum ViewMode { VM_Standard, VM_Notes, VM_SlideOverview, VM_Help };
+ /** The high-level method to switch the view mode. Use this instead of
+ SetLayoutMode and Set(Help|SlideSorter)State when possible.
+ */
+ void SetViewMode (const ViewMode eMode);
+
+ ViewMode GetViewMode() const;
+
+ /** Restore the layout mode (or slide sorter state) from the
+ configuration.
+ */
+ void RestoreViewMode();
+
+ void AddLayoutListener (
+ const css::uno::Reference<css::document::XEventListener>& rxListener);
+ void RemoveLayoutListener (
+ const css::uno::Reference<css::document::XEventListener>& rxListener);
+
+ // XWindowListener
+
+ virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override;
+
+ virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override;
+
+ virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override;
+
+ // XPaintListener
+
+ virtual void SAL_CALL windowPaint (const css::awt::PaintEvent& rEvent) override;
+
+ // XMouseListener
+
+ virtual void SAL_CALL mousePressed (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseReleased (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseEntered (const css::awt::MouseEvent& rEvent) override;
+
+ virtual void SAL_CALL mouseExited (const css::awt::MouseEvent& rEvent) override;
+
+ // XFocusListener
+
+ virtual void SAL_CALL focusGained (const css::awt::FocusEvent& rEvent) override;
+
+ virtual void SAL_CALL focusLost (const css::awt::FocusEvent& rEvent) override;
+
+ // XEventListener
+
+ virtual void SAL_CALL disposing (
+ const css::lang::EventObject& rEvent) override;
+
+private:
+ css::uno::Reference<css::uno::XComponentContext> mxComponentContext;
+ ::rtl::Reference<PresenterController> mpPresenterController;
+ css::uno::Reference<css::awt::XWindow> mxParentWindow;
+ css::uno::Reference<css::rendering::XCanvas> mxParentCanvas;
+ css::uno::Reference<css::uno::XInterface> mxPaneBorderManager;
+ ::rtl::Reference<PresenterPaneBorderPainter> mpPaneBorderPainter;
+ ::rtl::Reference<PresenterPaneContainer> mpPaneContainer;
+ bool mbIsLayoutPending;
+ /** This flag is set to <TRUE/> while the Layout() method is being
+ executed. Prevents windowMoved() and windowResized() from changing
+ the window sizes.
+ */
+ bool mbIsLayouting;
+ std::shared_ptr<PresenterTheme> mpTheme;
+ SharedBitmapDescriptor mpBackgroundBitmap;
+ css::uno::Reference<css::rendering::XBitmap> mxScaledBackgroundBitmap;
+ css::uno::Reference<css::rendering::XPolyPolygon2D> mxClipPolygon;
+ LayoutMode meLayoutMode;
+ bool mbIsSlideSorterActive;
+ bool mbIsHelpViewActive;
+ bool mbisPaused;
+ typedef ::std::vector<css::uno::Reference<css::document::XEventListener> >
+ LayoutListenerContainer;
+ LayoutListenerContainer maLayoutListeners;
+ bool mbIsMouseClickPending;
+
+ void PaintChildren (const css::awt::PaintEvent& rEvent) const;
+ void UpdateWindowSize (const css::uno::Reference<css::awt::XWindow>& rxBorderWindow);
+ void PaintBackground (const css::awt::Rectangle& rUpdateBox);
+ void ProvideBackgroundBitmap();
+ css::uno::Reference<css::rendering::XPolyPolygon2D> CreateClipPolyPolygon() const;
+
+ void StoreViewMode (const ViewMode eViewMode);
+
+ void LayoutStandardMode();
+ void LayoutNotesMode();
+ void LayoutSlideSorterMode();
+ void LayoutHelpMode();
+
+ /** Layout the tool bar and return its outer bounding box.
+ */
+ css::geometry::RealRectangle2D LayoutToolBar();
+
+ css::awt::Size CalculatePaneSize (
+ const double nOuterWidth,
+ const OUString& rsPaneURL);
+
+ /** Notify changes of the layout mode and of the slide sorter state.
+ */
+ void NotifyLayoutModeChange();
+
+ void NotifyDisposing();
+
+ /// @throws css::lang::DisposedException
+ void ThrowIfDisposed() const;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sdext/source/presenter/presenter.component b/sdext/source/presenter/presenter.component
new file mode 100644
index 000000000..01e18b38e
--- /dev/null
+++ b/sdext/source/presenter/presenter.component
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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/.
+ *
+-->
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="org.libreoffice.comp.PresenterScreenJob"
+ constructor="sdext_PresenterScreenJob_get_implementation" />
+ <implementation name="org.libreoffice.comp.PresenterScreenProtocolHandler"
+ constructor="sdext_PresenterProtocolHandler_get_implementation">
+ <service name="com.sun.star.frame.ProtocolHandler"/>
+ </implementation>
+</component>