diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sdext/source/presenter/PresenterToolBar.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream/4%7.4.7.tar.xz libreoffice-upstream/4%7.4.7.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sdext/source/presenter/PresenterToolBar.cxx')
-rw-r--r-- | sdext/source/presenter/PresenterToolBar.cxx | 2015 |
1 files changed, 2015 insertions, 0 deletions
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: */ |