1928 lines
66 KiB
C++
1928 lines
66 KiB
C++
/* -*- 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 <utility>
|
|
#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 <DrawController.hxx>
|
|
#include <com/sun/star/drawing/framework/XConfigurationController.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 (::rtl::Reference<PresenterScrollBar> xVerticalScrollBar);
|
|
|
|
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,
|
|
std::shared_ptr<PresenterPaintManager> xPaintManager);
|
|
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 rtl::Reference<::sd::DrawController>& 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<XConfigurationController> xCC (
|
|
rxController->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,
|
|
u"SlideSorterCloser"_ustr);
|
|
|
|
if (mpPresenterController->GetTheme() != nullptr)
|
|
{
|
|
PresenterTheme::SharedFontDescriptor pFont (
|
|
mpPresenterController->GetTheme()->GetFont(u"ButtonFont"_ustr));
|
|
if (pFont)
|
|
maSeparatorColor = pFont->mnColor;
|
|
}
|
|
|
|
// Create the layout.
|
|
mpLayout = std::make_shared<Layout>(mpVerticalScrollBar);
|
|
|
|
// Create the preview cache.
|
|
mxPreviewCache.set(
|
|
xFactory->createInstanceWithContext(
|
|
u"com.sun.star.drawing.PresenterPreviewCache"_ustr,
|
|
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.
|
|
rxController->addPropertyChangeListener(
|
|
u"CurrentPage"_ustr,
|
|
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(u"Width"_ustr) >>= nWidth)
|
|
&& (xProperties->getPropertyValue(u"Height"_ustr) >>= 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 (
|
|
u"PresenterScreenSettings/ScrollBar/Bitmaps"_ustr,
|
|
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(u"Animation"_ustr);
|
|
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(u"Transition"_ustr);
|
|
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 (
|
|
u"PresenterSlideSorter has been already disposed"_ustr,
|
|
static_cast<uno::XWeak*>(this));
|
|
}
|
|
}
|
|
|
|
//===== PresenterSlideSorter::Layout ==========================================
|
|
|
|
PresenterSlideSorter::Layout::Layout (
|
|
::rtl::Reference<PresenterScrollBar> xVerticalScrollBar)
|
|
: 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(std::move(xVerticalScrollBar))
|
|
{
|
|
}
|
|
|
|
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,
|
|
std::shared_ptr<PresenterPaintManager> xPaintManager)
|
|
: mxSlides(rxSlides),
|
|
mnSlideIndex(-1),
|
|
mxInvalidateTarget(rxInvalidateTarget),
|
|
mpPaintManager(std::move(xPaintManager))
|
|
{
|
|
if (rpTheme != nullptr)
|
|
{
|
|
std::shared_ptr<PresenterBitmapContainer> pBitmaps (rpTheme->GetBitmapContainer());
|
|
if (pBitmaps != nullptr)
|
|
{
|
|
mpLeftLabelBitmap = pBitmaps->GetBitmap(u"LabelLeft"_ustr);
|
|
mpCenterLabelBitmap = pBitmaps->GetBitmap(u"LabelCenter"_ustr);
|
|
mpRightLabelBitmap = pBitmaps->GetBitmap(u"LabelRight"_ustr);
|
|
}
|
|
|
|
mpFont = rpTheme->GetFont(u"SlideSorterLabelFont"_ustr);
|
|
}
|
|
}
|
|
|
|
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(u"LinkDisplayName"_ustr) >>= 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 constexpr 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,
|
|
u"/org.openoffice.Office.PresenterScreen/"_ustr,
|
|
PresenterConfigurationAccess::READ_ONLY);
|
|
Reference<container::XHierarchicalNameAccess> xBitmaps (
|
|
aConfiguration.GetConfigurationNode(
|
|
u"PresenterScreenSettings/SlideSorter/CurrentSlideBorderBitmaps"_ustr),
|
|
UNO_QUERY);
|
|
if ( ! xBitmaps.is())
|
|
return;
|
|
|
|
PresenterBitmapContainer aContainer (
|
|
u"PresenterScreenSettings/SlideSorter/CurrentSlideBorderBitmaps"_ustr,
|
|
std::shared_ptr<PresenterBitmapContainer>(),
|
|
rxContext,
|
|
rxCanvas);
|
|
|
|
mpTopLeft = aContainer.GetBitmap(u"TopLeft"_ustr);
|
|
mpTop = aContainer.GetBitmap(u"Top"_ustr);
|
|
mpTopRight = aContainer.GetBitmap(u"TopRight"_ustr);
|
|
mpLeft = aContainer.GetBitmap(u"Left"_ustr);
|
|
mpRight = aContainer.GetBitmap(u"Right"_ustr);
|
|
mpBottomLeft = aContainer.GetBitmap(u"BottomLeft"_ustr);
|
|
mpBottom = aContainer.GetBitmap(u"Bottom"_ustr);
|
|
mpBottomRight = aContainer.GetBitmap(u"BottomRight"_ustr);
|
|
|
|
// 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 - mpTopRight->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: */
|