diff options
Diffstat (limited to 'sdext/source/presenter/PresenterClock.cxx')
-rw-r--r-- | sdext/source/presenter/PresenterClock.cxx | 1445 |
1 files changed, 1445 insertions, 0 deletions
diff --git a/sdext/source/presenter/PresenterClock.cxx b/sdext/source/presenter/PresenterClock.cxx new file mode 100644 index 000000000000..1d14b6473e20 --- /dev/null +++ b/sdext/source/presenter/PresenterClock.cxx @@ -0,0 +1,1445 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sdext.hxx" + +#include "PresenterClock.hxx" +#include "PresenterComponent.hxx" +#include "PresenterConfigurationAccess.hxx" +#include "PresenterGeometryHelper.hxx" +#include <com/sun/star/awt/InvalidateStyle.hpp> +#include <com/sun/star/awt/MouseButton.hpp> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/deployment/XPackageInformationProvider.hpp> +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XConfigurationController.hpp> +#include <com/sun/star/rendering/CompositeOperation.hpp> +#include <com/sun/star/rendering/PathCapType.hpp> +#include <com/sun/star/rendering/TextDirection.hpp> +#include <com/sun/star/rendering/XCanvasFont.hpp> +#include <com/sun/star/rendering/XSpriteCanvas.hpp> +#include <com/sun/star/util/Color.hpp> +#include <osl/mutex.hxx> +#include <osl/time.h> +#include <rtl/ref.hxx> +#include <salhelper/timer.hxx> +#include <boost/bind.hpp> +#include <cmath> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::rtl::OUString; + +namespace sdext { namespace presenter { + + +/** Wrapper around a library timer. +*/ +class PresenterClock::Timer : public salhelper::Timer +{ +public: + explicit Timer (const ::rtl::Reference<PresenterClock>& rpClock); + virtual ~Timer (void); + + void Stop (void); + +protected: + virtual void SAL_CALL onShot (void); + +private: + ::rtl::Reference<PresenterClock> mpClock; +}; + + + + +namespace { + bool GetDateTime (oslDateTime& rDateTime); + + class BitmapDescriptor + { + public: + Reference<rendering::XBitmap> mxBitmap; + awt::Point maOffset; + Reference<rendering::XBitmap> mxScaledBitmap; + geometry::RealPoint2D maScaledOffset; + }; +} + + + + +class PresenterClock::Painter +{ +public: + virtual void Paint ( + const Reference<rendering::XCanvas>& rxCanvas, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState, + const util::Color& rBackgroundColor, + const sal_Int32 nHour, + const sal_Int32 nMinute, + const sal_Int32 nSecond, + const bool bShowSeconds) = 0; + virtual void Resize (const awt::Size& rSize) = 0; +}; + + + + +namespace { + class AnalogDefaultPainter : public PresenterClock::Painter + { + public: + AnalogDefaultPainter (void); + virtual ~AnalogDefaultPainter (void) {} + virtual void Paint ( + const Reference<rendering::XCanvas>& rxCanvas, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState, + const util::Color& rBackgroundColor, + const sal_Int32 nHour, + const sal_Int32 nMinute, + const sal_Int32 nSecond, + const bool bShowSeconds); + virtual void Resize (const awt::Size& rSize); + private: + geometry::RealPoint2D maCenter; + double mnOuterRadius; + awt::Size maSize; + Reference<rendering::XBitmap> mxBitmap; + + /** Relative length (with respect to radius) from center to the tip of + the hand. + */ + static const double mnRelativeHourHandLength; + /** Relative length (with respect to radius) from center to the + oposing end of the tip of the hand. + */ + static const double mnRelativeHourHandLength2; + static const double mnRelativeHourHandWidth; + static const double mnRelativeMinuteHandLength; + static const double mnRelativeMinuteHandLength2; + static const double mnRelativeMinuteHandWidth; + static const double mnRelativeSecondHandLength; + static const double mnRelativeSecondHandLength2; + static const double mnRelativeSecondHandWidth; + + void PaintAngledLine ( + const double nAngle, + const double nInnerRadius, + const double nOuterRadius, + const double nStrokeWidth, + const Reference<rendering::XCanvas>& rxCanvas, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState); + }; + + + class AnalogBitmapPainter : public PresenterClock::Painter + { + public: + AnalogBitmapPainter( + const Reference<XComponentContext>& rxContext, + const OUString& rsThemeName); + virtual ~AnalogBitmapPainter (void) {} + virtual void Paint ( + const Reference<rendering::XCanvas>& rxCanvas, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState, + const util::Color& rBackgroundColor, + const sal_Int32 nHour, + const sal_Int32 nMinute, + const sal_Int32 nSecond, + const bool bShowSeconds); + virtual void Resize (const awt::Size& rSize); + private: + css::uno::Reference<css::uno::XComponentContext> mxComponentContext; + const OUString msThemeName; + bool mbThemeLoaded; + bool mbThemeLoadingFailed; + geometry::RealPoint2D maCenter; + double mnOuterRadius; + BitmapDescriptor maFace; + BitmapDescriptor maMinuteHand; + BitmapDescriptor maHourHand; + + void PrepareBitmaps (const Reference<rendering::XCanvas>& rxCanvas); + Reference<container::XNameAccess> GetTheme ( + PresenterConfigurationAccess& rConfiguration); + bool ThemeNameComparator ( + const ::rtl::OUString& rsKey, + const Reference<container::XNameAccess>& rxCandidate, + const ::rtl::OUString& rsCurrentThemeName); + void LoadBitmaps ( + PresenterConfigurationAccess& rConfiguration, + const Reference<container::XNameAccess>& rxNameAccess, + const Reference<rendering::XCanvas>& rxCanvas); + void LoadBitmap ( + const OUString& rsKey, + const ::std::vector<Any>& rValues, + const OUString& rsBitmapPath, + const Reference<container::XNameAccess>& rxBitmapLoader); + void ScaleBitmaps (void); + }; + + + class DigitalDefaultPainter : public PresenterClock::Painter + { + public: + DigitalDefaultPainter ( + const ::rtl::Reference<PresenterController>& rpPresenterController, + const Reference<XResourceId>& rxViewId); + virtual ~DigitalDefaultPainter (void); + + virtual void Paint ( + const Reference<rendering::XCanvas>& rxCanvas, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState, + const util::Color& rBackgroundColor, + const sal_Int32 nHour, + const sal_Int32 nMinute, + const sal_Int32 nSecond, + const bool bShowSeconds); + virtual void Resize (const awt::Size& rSize); + + private: + ::rtl::Reference<PresenterController> mpPresenterController; + bool mbIs24HourFormat; + bool mbIsAdaptFontSize; + Reference<rendering::XCanvasFont> mxFont; + awt::Size maWindowSize; + OUString msViewURL; + + void CreateFont ( + const Reference<rendering::XCanvas>& rxCanvas, + const bool bIsShowSeconds); + }; + + +} // end of anonymous namespace + + + + +//===== PresenterClock ================================================================= + +::rtl::Reference<PresenterClock> PresenterClock::Create ( + const Reference<XComponentContext>& rxContext, + const Reference<XResourceId>& rxViewId, + const Reference<frame::XController>& rxController, + const ::rtl::Reference<PresenterController>& rpPresenterController) +{ + ::rtl::Reference<PresenterClock> pClock (new PresenterClock( + rxContext, + rxViewId, + rxController, + rpPresenterController)); + pClock->LateInit(); + return pClock; +} + + + + +PresenterClock::PresenterClock ( + const Reference<XComponentContext>& rxContext, + const Reference<XResourceId>& rxViewId, + const Reference<frame::XController>& rxController, + const ::rtl::Reference<PresenterController>& rpPresenterController) + : PresenterClockInterfaceBase(m_aMutex), + mxComponentContext(rxContext), + mxViewId(rxViewId), + mxWindow(), + mxCanvas(), + mxPane(), + mpPresenterController(rpPresenterController), + mbIsResizePending(true), + maViewState(), + maRenderState(), + mpTimer(), + mpClockPainter(), + mpClockPainter2(), + mnMode(1), + mnHour(-1), + mnMinute(-1), + mnSecond(-1), + mbIsShowSeconds(true) +{ + SetMode(mnMode); + + maViewState.AffineTransform = geometry::AffineMatrix2D(1,0,0, 0,1,0); + maRenderState.AffineTransform = geometry::AffineMatrix2D(1,0,0, 0,1,0); + maRenderState.DeviceColor = Sequence<double>(4); + PresenterCanvasHelper::SetDeviceColor(maRenderState, util::Color(0x00000000)); + + try + { + + Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW); + Reference<XConfigurationController> xCC (xCM->getConfigurationController(), UNO_QUERY_THROW); + mxPane = Reference<XPane>(xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW); + + mxWindow = mxPane->getWindow(); + if (mxWindow.is()) + { + mxWindow->addPaintListener(this); + mxWindow->addWindowListener(this); + mxWindow->addMouseListener(this); + Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY); + if (xPeer.is()) + xPeer->setBackground(util::Color(0xff000000)); + mxWindow->setVisible(sal_True); + } + + Resize(); + } + catch (RuntimeException&) + { + disposing(); + throw; + } +} + + + + +PresenterClock::~PresenterClock (void) +{ +} + + + + +void PresenterClock::LateInit (void) +{ + mpTimer = new Timer(this); +} + + + + +void SAL_CALL PresenterClock::disposing (void) +{ + if (mpTimer != NULL) + { + mpTimer->Stop(); + } + if (mxWindow.is()) + { + mxWindow->removePaintListener(this); + mxWindow->removeWindowListener(this); + mxWindow->removeMouseListener(this); + mxWindow = NULL; + } + mxCanvas = NULL; + mxViewId = NULL; +} + + + + +void PresenterClock::UpdateTime (void) +{ + // Get current time and check whether it is different from last time. + oslDateTime aDateTime; + if ( ! GetDateTime(aDateTime)) + return; + if (aDateTime.Hours != mnHour + || aDateTime.Minutes != mnMinute + || aDateTime.Seconds != mnSecond) + { + mnHour = aDateTime.Hours % 24; + mnMinute = aDateTime.Minutes % 60; + mnSecond = aDateTime.Seconds % 60; + + Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY); + if (xPeer.is()) + xPeer->invalidate(awt::InvalidateStyle::NOERASE | + awt::InvalidateStyle::UPDATE); + } +} + + + + +//----- lang::XEventListener ------------------------------------------------- + +void SAL_CALL PresenterClock::disposing (const lang::EventObject& rEventObject) + throw (RuntimeException) +{ + if (rEventObject.Source == mxWindow) + { + mxWindow = NULL; + if (mpTimer != NULL) + mpTimer->Stop(); + } +} + + + + +//----- XPaintListener -------------------------------------------------------- + +void SAL_CALL PresenterClock::windowPaint (const awt::PaintEvent& rEvent) + throw (RuntimeException) +{ + ThrowIfDisposed(); + Paint(rEvent.UpdateRect); +} + + + + +//----- XWindowListener ------------------------------------------------------- + +void SAL_CALL PresenterClock::windowResized (const awt::WindowEvent& rEvent) + throw (RuntimeException) +{ + (void)rEvent; + mbIsResizePending = true; +} + + + + +void SAL_CALL PresenterClock::windowMoved (const awt::WindowEvent& rEvent) + throw (RuntimeException) +{ + (void)rEvent; + mbIsResizePending = true; +} + + + + +void SAL_CALL PresenterClock::windowShown (const lang::EventObject& rEvent) + throw (RuntimeException) +{ + (void)rEvent; + mbIsResizePending = true; +} + + + + +void SAL_CALL PresenterClock::windowHidden (const lang::EventObject& rEvent) + throw (RuntimeException) +{ + (void)rEvent; +} + + + + +//----- XMouseListener -------------------------------------------------------- + +void SAL_CALL PresenterClock::mousePressed (const css::awt::MouseEvent& rEvent) + throw (css::uno::RuntimeException) +{ + if (rEvent.Buttons == awt::MouseButton::LEFT) + { + SetMode(mnMode+1); + } +} + + + + +void SAL_CALL PresenterClock::mouseReleased (const css::awt::MouseEvent& rEvent) + throw (css::uno::RuntimeException) +{ + (void)rEvent; +} + + + + +void SAL_CALL PresenterClock::mouseEntered (const css::awt::MouseEvent& rEvent) + throw (css::uno::RuntimeException) +{ + (void)rEvent; +} + + + + +void SAL_CALL PresenterClock::mouseExited (const css::awt::MouseEvent& rEvent) + throw (css::uno::RuntimeException) +{ + (void)rEvent; +} + + + + +//----- XResourceId ----------------------------------------------------------- + +Reference<XResourceId> SAL_CALL PresenterClock::getResourceId (void) + throw (RuntimeException) +{ + return mxViewId; +} + + + + +sal_Bool SAL_CALL PresenterClock::isAnchorOnly (void) + throw (RuntimeException) +{ + return false; +} + + + + +//----------------------------------------------------------------------------- + +void PresenterClock::Resize (void) +{ + if (mxPane.is()) + mxCanvas = Reference<rendering::XCanvas>(mxPane->getCanvas(), UNO_QUERY); + if (mxWindow.is() && mxCanvas.is()) + { + const awt::Rectangle aWindowBox (mxWindow->getPosSize()); + const awt::Size aWindowSize(aWindowBox.Width,aWindowBox.Height); + if (mpClockPainter.get() != NULL) + mpClockPainter->Resize(aWindowSize); + if (mpClockPainter2.get() != NULL) + mpClockPainter2->Resize(aWindowSize); + mbIsResizePending = false; + } +} + + + + +void PresenterClock::Paint (const awt::Rectangle& rUpdateBox) +{ + if ( ! mxCanvas.is() && mxPane.is()) + mxCanvas = Reference<rendering::XCanvas>(mxPane->getCanvas(), UNO_QUERY); + if ( ! mxWindow.is() + || ! mxCanvas.is() + || ! mxCanvas->getDevice().is()) + { + return; + } + + try + { + if (mbIsResizePending) + Resize(); + + Reference<rendering::XPolyPolygon2D> xUpdatePolygon ( + PresenterGeometryHelper::CreatePolygon(rUpdateBox, mxCanvas->getDevice())); + + Clear(xUpdatePolygon); + + if (mpClockPainter.get() != NULL) + mpClockPainter->Paint(mxCanvas, + maViewState, + maRenderState, + mpPresenterController->GetViewBackgroundColor(mxViewId->getResourceURL()), + mnHour, + mnMinute, + mnSecond, + mbIsShowSeconds); + + if (mpClockPainter2.get() != NULL) + mpClockPainter2->Paint( + mxCanvas, + maViewState, + maRenderState, + mpPresenterController->GetViewBackgroundColor(mxViewId->getResourceURL()), + mnHour, + mnMinute, + mnSecond, + mbIsShowSeconds); + } + catch (RuntimeException& e) + { + (void)e; + } + + // Make the back buffer visible. + Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY); + if (xSpriteCanvas.is()) + xSpriteCanvas->updateScreen(sal_False); +} + + + + +void PresenterClock::Clear (const Reference<rendering::XPolyPolygon2D>& rxUpdatePolygon) +{ + rendering::RenderState aRenderState = maRenderState; + const sal_Int32 nColor ( + mpPresenterController->GetViewBackgroundColor(mxViewId->getResourceURL())); + aRenderState.DeviceColor[0] = ((nColor&0x00ff0000) >> 16) / 255.0; + aRenderState.DeviceColor[1] = ((nColor&0x0000ff00) >> 8) / 255.0; + aRenderState.DeviceColor[2] = ((nColor&0x000000ff) >> 0) / 255.0; + + if (rxUpdatePolygon.is()) + mxCanvas->fillPolyPolygon( + rxUpdatePolygon, + maViewState, + aRenderState); +} + + + + +void PresenterClock::SetMode (const sal_Int32 nMode) +{ + mnMode = nMode % 3; + + switch (mnMode) + { + case 0: + mpClockPainter.reset( + new AnalogBitmapPainter( + mxComponentContext, + OUString(RTL_CONSTASCII_USTRINGPARAM("ClockTheme")))); + mpClockPainter2.reset(); + break; + + case 1: + mpClockPainter.reset(); + mpClockPainter2.reset(new AnalogDefaultPainter()); + break; + + case 2: + mpClockPainter.reset(); + mpClockPainter2.reset(new DigitalDefaultPainter(mpPresenterController, mxViewId)); + break; + + case 3: + mpClockPainter.reset( + new AnalogBitmapPainter( + mxComponentContext, + OUString(RTL_CONSTASCII_USTRINGPARAM("ClockTheme")))); + mpClockPainter2.reset(new AnalogDefaultPainter()); + break; + } + Resize(); +} + + + + +void PresenterClock::ThrowIfDisposed (void) + throw (::com::sun::star::lang::DisposedException) +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + throw lang::DisposedException ( + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "PresenterClock object has already been disposed")), + static_cast<uno::XWeak*>(this)); + } +} + + + + +//===== Timer ================================================================= + +PresenterClock::Timer::Timer (const ::rtl::Reference<PresenterClock>& rpClock) + : salhelper::Timer(salhelper::TTimeValue(10), salhelper::TTimeValue(100/*ms*/)), + mpClock(rpClock) +{ + acquire(); + start(); +} + + + + +PresenterClock::Timer::~Timer (void) +{ + if (mpClock.is()) + Stop(); +} + + + + +void PresenterClock::Timer::Stop (void) +{ + mpClock = NULL; + stop(); + release(); +} + + + + +void SAL_CALL PresenterClock::Timer::onShot (void) +{ + if (mpClock.get() != NULL) + mpClock->UpdateTime(); +} + + + +namespace { + +//============================================================================= + +bool GetDateTime (oslDateTime& rDateTime) +{ + TimeValue aSystemTime; + TimeValue aLocalTime; + if (osl_getSystemTime(&aSystemTime)) + if (osl_getLocalTimeFromSystemTime(&aSystemTime, &aLocalTime)) + if (osl_getDateTimeFromTimeValue(&aLocalTime, &rDateTime)) + return true; + return false; +} + + + + +//===== AnalogDefaultPainter ================================================== + +const double AnalogDefaultPainter::mnRelativeHourHandLength = 0.65; +const double AnalogDefaultPainter::mnRelativeHourHandLength2 (-0.1); +const double AnalogDefaultPainter::mnRelativeHourHandWidth (0.055); +const double AnalogDefaultPainter::mnRelativeMinuteHandLength (-0.2); +const double AnalogDefaultPainter::mnRelativeMinuteHandLength2 (0.85); +const double AnalogDefaultPainter::mnRelativeMinuteHandWidth (0.025); +const double AnalogDefaultPainter::mnRelativeSecondHandLength (-0.25); +const double AnalogDefaultPainter::mnRelativeSecondHandLength2 (0.95); +const double AnalogDefaultPainter::mnRelativeSecondHandWidth (0.015); + +AnalogDefaultPainter::AnalogDefaultPainter (void) + : maCenter(0,0), + mnOuterRadius(0), + maSize(0,0), + mxBitmap() +{ +} + + + + +void AnalogDefaultPainter::Paint ( + const Reference<rendering::XCanvas>& rxCanvas, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState, + const util::Color& rBackgroundColor, + const sal_Int32 nHour, + const sal_Int32 nMinute, + const sal_Int32 nSecond, + const bool bShowSeconds) +{ + double nInnerRadius (0); + double nStrokeWidth (0.1); + const double nClockSize (2*mnOuterRadius); + + // Some antialiasing is created by painting into a bitmap twice the + // screen size and then scaling it down. + const sal_Int32 nSuperSampleFactor (2); + if ( ! mxBitmap.is()) + { + mxBitmap = (rxCanvas->getDevice()->createCompatibleBitmap( + geometry::IntegerSize2D( + maSize.Width*nSuperSampleFactor, + maSize.Height*nSuperSampleFactor))); + } + Reference<rendering::XCanvas> xBitmapCanvas (mxBitmap, UNO_QUERY); + rendering::RenderState aRenderState(rRenderState); + aRenderState.AffineTransform.m00 = nSuperSampleFactor; + aRenderState.AffineTransform.m11 = nSuperSampleFactor; + + // Clear the background. + aRenderState.DeviceColor[0] = ((rBackgroundColor&0x00ff0000) >> 16) / 255.0; + aRenderState.DeviceColor[1] = ((rBackgroundColor&0x0000ff00) >> 8) / 255.0; + aRenderState.DeviceColor[2] = ((rBackgroundColor&0x000000ff) >> 0) / 255.0; + Reference<rendering::XPolyPolygon2D> xPolygon ( + PresenterGeometryHelper::CreatePolygon( + awt::Rectangle(0,0,maSize.Width,maSize.Height), + xBitmapCanvas->getDevice())); + if (xPolygon.is()) + xBitmapCanvas->fillPolyPolygon(xPolygon, rViewState, aRenderState); + + // Clock face and clock hands are painted in black. + aRenderState.DeviceColor[0] = 0; + aRenderState.DeviceColor[1] = 0; + aRenderState.DeviceColor[2] = 0; + + // Paint the clock face. + for (sal_Int32 nHourMark=0; nHourMark<12; ++nHourMark) + { + if (nHourMark%3 == 0) + { + nInnerRadius = 0.7 * mnOuterRadius; + nStrokeWidth = 0.05 * nClockSize; + } + else + { + nInnerRadius = 0.8 * mnOuterRadius; + nStrokeWidth = 0.03 * nClockSize; + } + + const double nAngle (nHourMark * 2 * M_PI / 12); + PaintAngledLine(nAngle, nInnerRadius, mnOuterRadius, nStrokeWidth, + xBitmapCanvas, rViewState, aRenderState); + } + + // Paint the hour hand. + const double nHoursAngle (((nHour%12)+nMinute/60.0) * 2 * M_PI / 12); + PaintAngledLine(nHoursAngle, + mnRelativeHourHandLength2*mnOuterRadius, + mnRelativeHourHandLength*mnOuterRadius, + mnRelativeHourHandWidth*nClockSize, + xBitmapCanvas, rViewState, aRenderState); + + // Paint the minute hand. + const double nMinutesAngle ((nMinute+nSecond/60.0) * 2 * M_PI / 60); + PaintAngledLine(nMinutesAngle, + mnRelativeMinuteHandLength2*mnOuterRadius, + mnRelativeMinuteHandLength*mnOuterRadius, + mnRelativeMinuteHandWidth*nClockSize, + xBitmapCanvas, rViewState, aRenderState); + + // Optionally paint the second hand. + if (bShowSeconds) + { + const double nSecondsAngle (nSecond * 2 * M_PI / 60); + PaintAngledLine(nSecondsAngle, + mnRelativeSecondHandLength2*mnOuterRadius, + mnRelativeSecondHandLength*mnOuterRadius, + mnRelativeSecondHandWidth*nClockSize, + xBitmapCanvas, rViewState, aRenderState); + } + + aRenderState.AffineTransform.m00 = 1.0 / nSuperSampleFactor; + aRenderState.AffineTransform.m11 = 1.0 / nSuperSampleFactor; + rxCanvas->drawBitmap(mxBitmap,rViewState,aRenderState); +} + + + + +void AnalogDefaultPainter::PaintAngledLine ( + const double nAngle, + const double nInnerRadius, + const double nOuterRadius, + const double nStrokeWidth, + const Reference<rendering::XCanvas>& rxCanvas, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState) +{ + if ( ! rxCanvas.is()) + return; + + rendering::StrokeAttributes aStrokeAttributes; + aStrokeAttributes.StrokeWidth = nStrokeWidth; + aStrokeAttributes.StartCapType = rendering::PathCapType::SQUARE; + aStrokeAttributes.EndCapType = rendering::PathCapType::SQUARE; + aStrokeAttributes.StartCapType = rendering::PathCapType::BUTT; + aStrokeAttributes.EndCapType = rendering::PathCapType::BUTT; + const double nCos (cos(nAngle - M_PI/2)); + const double nSin (sin(nAngle - M_PI/2)); + + Sequence<Sequence<geometry::RealPoint2D> > aPoints(1); + aPoints[0] = Sequence<geometry::RealPoint2D>(2); + aPoints[0][0] = geometry::RealPoint2D( + maCenter.X + nInnerRadius*nCos + 0.5, + maCenter.Y + nInnerRadius*nSin + 0.5); + aPoints[0][1] = geometry::RealPoint2D( + maCenter.X + nOuterRadius*nCos + 0.5, + maCenter.Y + nOuterRadius*nSin + 0.5); + + Reference<rendering::XPolyPolygon2D> xLine ( + rxCanvas->getDevice()->createCompatibleLinePolyPolygon(aPoints), + UNO_QUERY); + if ( ! xLine.is()) + return; + rxCanvas->strokePolyPolygon( + xLine, + rViewState, + rRenderState, + aStrokeAttributes); +} + + + + +void AnalogDefaultPainter::Resize (const awt::Size& rWindowSize) +{ + maSize = rWindowSize; + maCenter = geometry::RealPoint2D(rWindowSize.Width/2.0, rWindowSize.Height/2.0); + mnOuterRadius = ::std::min(rWindowSize.Width, rWindowSize.Height) / 2.0 - 2; + mxBitmap = NULL; +} + + + + +//===== AnalogBitmapPainter =================================================== + +AnalogBitmapPainter::AnalogBitmapPainter ( + const Reference<XComponentContext>& rxContext, + const OUString& rsThemeName) + : mxComponentContext(rxContext), + msThemeName(rsThemeName), + mbThemeLoaded(false), + mbThemeLoadingFailed(false), + maCenter(), + mnOuterRadius(), + maFace(), + maMinuteHand(), + maHourHand() +{ +} + + + + +void AnalogBitmapPainter::Paint ( + const Reference<rendering::XCanvas>& rxCanvas, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState, + const util::Color& rBackgroundColor, + const sal_Int32 nHour, + const sal_Int32 nMinute, + const sal_Int32 nSecond, + const bool bShowSeconds) +{ + (void)rBackgroundColor; + (void)nSecond; + (void)bShowSeconds; + + if ( ! rxCanvas.is()) + return; + + rendering::RenderState aRenderState = rRenderState; + + try + { + PrepareBitmaps(rxCanvas); + + if (maFace.mxScaledBitmap.is()) + { + aRenderState.AffineTransform = geometry::AffineMatrix2D( + 1,0, maCenter.X - maFace.maScaledOffset.X, + 0,1, maCenter.Y - maFace.maScaledOffset.Y); + rxCanvas->drawBitmap(maFace.mxScaledBitmap, rViewState, aRenderState); + } + + if (maMinuteHand.mxScaledBitmap.is()) + { + const double nMinuteAngle ((nMinute+nSecond/60.0) * 2.0 * M_PI / 60.0); + const double nCos (cos(nMinuteAngle - M_PI/2)); + const double nSin (sin(nMinuteAngle - M_PI/2)); + aRenderState.AffineTransform = geometry::AffineMatrix2D( + nCos, + -nSin, + -maMinuteHand.maScaledOffset.X*nCos + + maMinuteHand.maScaledOffset.Y*nSin+maCenter.X, + nSin, + nCos, + -maMinuteHand.maScaledOffset.X*nSin + - maMinuteHand.maScaledOffset.Y*nCos+maCenter.Y); + rxCanvas->drawBitmap(maMinuteHand.mxScaledBitmap, rViewState, aRenderState); + } + + if (maHourHand.mxScaledBitmap.is()) + { + const double nHoursAngle ((nHour%12+nMinute/60.0) * 2.0 * M_PI / 12.0); + const double nCos (cos(nHoursAngle - M_PI/2)); + const double nSin (sin(nHoursAngle - M_PI/2)); + aRenderState.AffineTransform = geometry::AffineMatrix2D( + nCos, + -nSin, + -maHourHand.maScaledOffset.X*nCos+maHourHand.maScaledOffset.Y*nSin+maCenter.X, + nSin, + nCos, + -maHourHand.maScaledOffset.X*nSin-maHourHand.maScaledOffset.Y*nCos+maCenter.Y); + rxCanvas->drawBitmap(maHourHand.mxScaledBitmap, rViewState, aRenderState); + } + } + catch(beans::UnknownPropertyException&) + { + } + catch(RuntimeException&) + { + } +} + + + + +void AnalogBitmapPainter::Resize (const awt::Size& rWindowSize) +{ + maCenter = geometry::RealPoint2D(rWindowSize.Width/2.0, rWindowSize.Height/2.0); + mnOuterRadius = ::std::min(rWindowSize.Width, rWindowSize.Height) / 2.0 - 2; + maFace.mxScaledBitmap = NULL; + maHourHand.mxScaledBitmap = NULL; + maMinuteHand.mxScaledBitmap = NULL; +} + + + + +void AnalogBitmapPainter::PrepareBitmaps (const Reference<rendering::XCanvas>& rxCanvas) +{ + if (mbThemeLoadingFailed) + { + // Theme loading has failed previously. Do not try a second time. + return; + } + if ( ! rxCanvas.is()) + { + // No canvas => bitmaps can neither be loaded, transformed into the + // right format, nor can they be painted. + return; + } + + if ( ! mbThemeLoaded) + { + mbThemeLoaded = true; + + // Get access to the clock bitmaps in the configuration. + PresenterConfigurationAccess aConfiguration ( + mxComponentContext, + OUString(RTL_CONSTASCII_USTRINGPARAM("org.openoffice.Office.extension.PresenterScreen")), + PresenterConfigurationAccess::READ_ONLY); + + Reference<container::XNameAccess> xTheme (GetTheme(aConfiguration)); + if (xTheme.is()) + LoadBitmaps(aConfiguration, xTheme, rxCanvas); + else + mbThemeLoadingFailed = true; + } + + ScaleBitmaps(); +} + + + + +Reference<container::XNameAccess> AnalogBitmapPainter::GetTheme ( + PresenterConfigurationAccess& rConfiguration) +{ + Reference<container::XNameAccess> xTheme; + + // Get root of clock themes. + Reference<container::XHierarchicalNameAccess> xClock ( + rConfiguration.GetConfigurationNode( + OUString(RTL_CONSTASCII_USTRINGPARAM("PresenterScreenSettings/AnalogBitmapClock"))), + UNO_QUERY); + + // Determine the name of the theme to use. + OUString sCurrentThemeName (RTL_CONSTASCII_USTRINGPARAM("DefaultTheme")); + rConfiguration.GetConfigurationNode( + xClock, + OUString(RTL_CONSTASCII_USTRINGPARAM("CurrentTheme"))) >>= sCurrentThemeName; + + // Load the clock theme. + Reference<container::XNameAccess> xThemes ( + rConfiguration.GetConfigurationNode( + xClock, + OUString(RTL_CONSTASCII_USTRINGPARAM("Themes"))), + UNO_QUERY); + if (xThemes.is()) + { + xTheme = Reference<container::XNameAccess>( + PresenterConfigurationAccess::Find( + xThemes, + ::boost::bind(&AnalogBitmapPainter::ThemeNameComparator, + this, _1, _2, sCurrentThemeName)), + UNO_QUERY); + } + + return xTheme; +} + + + + +bool AnalogBitmapPainter::ThemeNameComparator ( + const OUString& rsKey, + const Reference<container::XNameAccess>& rxCandidate, + const OUString& rsCurrentThemeName) +{ + (void)rsKey; + if (rxCandidate.is()) + { + OUString sThemeName; + if (rxCandidate->getByName(OUString(RTL_CONSTASCII_USTRINGPARAM("ThemeName"))) >>= sThemeName) + { + return sThemeName == rsCurrentThemeName; + } + } + return false; +} + + + + + +void AnalogBitmapPainter::LoadBitmaps ( + PresenterConfigurationAccess& rConfiguration, + const Reference<container::XNameAccess>& rxClockTheme, + const Reference<rendering::XCanvas>& rxCanvas) +{ + (void)rConfiguration; + + // Get base path to bitmaps. + Reference<deployment::XPackageInformationProvider> xInformationProvider ( + mxComponentContext->getValueByName(OUString(RTL_CONSTASCII_USTRINGPARAM( + "/singletons/com.sun.star.deployment.PackageInformationProvider"))), + UNO_QUERY); + OUString sLocation; + if (xInformationProvider.is()) + sLocation = xInformationProvider->getPackageLocation(gsExtensionIdentifier); + sLocation += OUString(RTL_CONSTASCII_USTRINGPARAM("/")); + + // Create the bitmap loader. + Reference<lang::XMultiComponentFactory> xFactory ( + mxComponentContext->getServiceManager(), UNO_QUERY); + if ( ! xFactory.is()) + return; + Sequence<Any> aArguments(1); + aArguments[0] <<= rxCanvas; + Reference<container::XNameAccess> xBitmapLoader( + xFactory->createInstanceWithArgumentsAndContext( + OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.drawing.PresenterWorkaroundService")), + aArguments, + mxComponentContext), + UNO_QUERY); + if ( ! xBitmapLoader.is()) + return; + + + // Iterate over all entries in the bitmap list and load the bitmaps. + Reference<container::XNameAccess> xBitmaps ( + rxClockTheme->getByName(OUString(RTL_CONSTASCII_USTRINGPARAM("Bitmaps"))), + UNO_QUERY); + ::std::vector<rtl::OUString> aBitmapProperties (3); + aBitmapProperties[0] = OUString(RTL_CONSTASCII_USTRINGPARAM("FileName")); + aBitmapProperties[1] = OUString(RTL_CONSTASCII_USTRINGPARAM("XOffset")); + aBitmapProperties[2] = OUString(RTL_CONSTASCII_USTRINGPARAM("YOffset")); + PresenterConfigurationAccess::ForAll( + xBitmaps, + aBitmapProperties, + ::boost::bind(&AnalogBitmapPainter::LoadBitmap, + this, + _1, + _2, + sLocation, + xBitmapLoader)); +} + + + + +void AnalogBitmapPainter::LoadBitmap ( + const OUString& rsKey, + const ::std::vector<Any>& rValues, + const OUString& rsBitmapPath, + const Reference<container::XNameAccess>& rxBitmapLoader) +{ + if (rValues.size() == 3) + { + BitmapDescriptor* pDescriptor = NULL; + if (rsKey == OUString(RTL_CONSTASCII_USTRINGPARAM("Face"))) + pDescriptor = &maFace; + else if (rsKey == OUString(RTL_CONSTASCII_USTRINGPARAM("HourHand"))) + pDescriptor = &maHourHand; + else if (rsKey == OUString(RTL_CONSTASCII_USTRINGPARAM("MinuteHand"))) + pDescriptor = &maMinuteHand; + + if (pDescriptor == NULL) + return; + + OUString sFileName; + if ( ! (rValues[0] >>= sFileName)) + return; + + rValues[1] >>= pDescriptor->maOffset.X; + rValues[2] >>= pDescriptor->maOffset.Y; + + pDescriptor->mxBitmap = Reference<rendering::XBitmap>( + rxBitmapLoader->getByName(rsBitmapPath+sFileName), UNO_QUERY); + + if ( ! pDescriptor->mxBitmap.is()) + mbThemeLoadingFailed = true; + } +} + + + + +void AnalogBitmapPainter::ScaleBitmaps (void) +{ + if (mbThemeLoadingFailed) + return; + if ( ! maFace.mxBitmap.is()) + return; + + const geometry::IntegerSize2D aFaceSize (maFace.mxBitmap->getSize()); + const sal_Int32 nSize = std::max(aFaceSize.Width, aFaceSize.Height); + const double nScale = mnOuterRadius*2 / nSize; + + BitmapDescriptor* aDescriptors[3] = { &maFace, &maHourHand, &maMinuteHand }; + for (int nIndex=0; nIndex<3; ++nIndex) + { + BitmapDescriptor& rDescriptor (*aDescriptors[nIndex]); + if ( ! rDescriptor.mxScaledBitmap.is() && rDescriptor.mxBitmap.is()) + { + const geometry::IntegerSize2D aBitmapSize (rDescriptor.mxBitmap->getSize()); + rDescriptor.mxScaledBitmap = rDescriptor.mxBitmap->getScaledBitmap( + geometry::RealSize2D(aBitmapSize.Width*nScale, aBitmapSize.Height*nScale), + sal_False); + rDescriptor.maScaledOffset = geometry::RealPoint2D( + rDescriptor.maOffset.X * nScale, + rDescriptor.maOffset.Y * nScale); + } + } +} + + + + +//===== DigitalDefaultPainter ================================================= + +DigitalDefaultPainter::DigitalDefaultPainter ( + const ::rtl::Reference<PresenterController>& rpPresenterController, + const Reference<XResourceId>& rxViewId) + : mpPresenterController(rpPresenterController), + mbIs24HourFormat(false), + mbIsAdaptFontSize(true), + mxFont(), + maWindowSize(0,0), + msViewURL(rxViewId.is() ? rxViewId->getResourceURL() : OUString()) +{ +} + + + + +DigitalDefaultPainter::~DigitalDefaultPainter (void) +{ +} + + + + +void DigitalDefaultPainter::Paint ( + const Reference<rendering::XCanvas>& rxCanvas, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState, + const util::Color& rBackgroundColor, + const sal_Int32 nHour, + const sal_Int32 nMinute, + const sal_Int32 nSecond, + const bool bIsShowSeconds) +{ + (void)rBackgroundColor; + (void)rRenderState; + + if ( ! mxFont.is()) + CreateFont(rxCanvas,bIsShowSeconds); + if ( ! mxFont.is()) + return; + + OUString sText; + + if (mbIs24HourFormat) + sText = OUString::valueOf(nHour); + else + { + sText = OUString::valueOf(nHour>12 ? nHour-12 : nHour); + } + sText += OUString(RTL_CONSTASCII_USTRINGPARAM(":")); + const OUString sMinutes (OUString::valueOf(nMinute)); + switch (sMinutes.getLength()) + { + case 1 : + sText += OUString(RTL_CONSTASCII_USTRINGPARAM("0")) + sMinutes; + break; + case 2: + sText += sMinutes; + break; + + default: + return; + } + if (bIsShowSeconds) + { + sText += OUString(RTL_CONSTASCII_USTRINGPARAM(":")); + const OUString sSeconds (OUString::valueOf(nSecond)); + switch (sSeconds.getLength()) + { + case 1 : + sText += OUString(RTL_CONSTASCII_USTRINGPARAM("0")) + sSeconds; + break; + case 2: + sText += sSeconds; + break; + + default: + return; + } + } + + rendering::StringContext aContext ( + sText, + 0, + sText.getLength()); + Reference<rendering::XTextLayout> xLayout (mxFont->createTextLayout( + aContext, + rendering::TextDirection::WEAK_LEFT_TO_RIGHT, + 0)); + if ( ! xLayout.is()) + return; + geometry::RealRectangle2D aBox (xLayout->queryTextBounds()); + + + rendering::RenderState aRenderState( + geometry::AffineMatrix2D(1,0,0, 0,1,0), + NULL, + Sequence<double>(4), + rendering::CompositeOperation::SOURCE); + + util::Color aFontColor (mpPresenterController->GetViewFontColor(msViewURL)); + PresenterCanvasHelper::SetDeviceColor(aRenderState, aFontColor); + aRenderState.AffineTransform.m02 + = (maWindowSize.Width - (aBox.X2-aBox.X1+1)) / 2 - aBox.X1; + aRenderState.AffineTransform.m12 + = (maWindowSize.Height - (aBox.Y2-aBox.Y1+1)) / 2 - aBox.Y1; + rxCanvas->drawText( + aContext, + mxFont, + rViewState, + aRenderState, + rendering::TextDirection::WEAK_LEFT_TO_RIGHT); +} + + + + +void DigitalDefaultPainter::Resize (const awt::Size& rSize) +{ + if (maWindowSize.Width != rSize.Width || maWindowSize.Height != rSize.Height) + { + maWindowSize = rSize; + if (mbIsAdaptFontSize) + mxFont = NULL; + } +} + + + + +void DigitalDefaultPainter::CreateFont ( + const Reference<rendering::XCanvas>& rxCanvas, + const bool bIsShowSeconds) +{ + if (rxCanvas.is() + && rxCanvas->getDevice().is() + && maWindowSize.Width>0 + && maWindowSize.Height>0) + { + // Create a time template for determinging the right font size. + // Assume that 0 is the widest digit or that all digits have the + // same width. + OUString sTimeTemplate; + // For the case that not all digits have the same width, create + // different templates for 12 and 24 hour mode. + if (mbIs24HourFormat) + sTimeTemplate = OUString(RTL_CONSTASCII_USTRINGPARAM("20")); + else + sTimeTemplate = OUString(RTL_CONSTASCII_USTRINGPARAM("10")); + if (bIsShowSeconds) + sTimeTemplate += OUString(RTL_CONSTASCII_USTRINGPARAM(":00:00")); + else + sTimeTemplate += OUString(RTL_CONSTASCII_USTRINGPARAM(":00")); + + rendering::StringContext aContext ( + sTimeTemplate, + 0, + sTimeTemplate.getLength()); + + // When the font size is adapted to the window size (as large as + // possible without overlapping) then that is done in a four step + // process: + // 1. Create a font in a default size, e.g. 10pt. + // 2. Determine a scale factor from enlarging the text bounding box + // to maximal size inside the window. + // 3. Create a new font by scaling the default size with the factor + // calculated in step 2. + // 4. Text may be rendered differently in different sizes. + // Therefore repeat step 2 and 3 once. More iterations may lead to + // even better results but probably not to visible differences. + rendering::FontRequest aFontRequest (mpPresenterController->GetViewFontRequest(msViewURL)); + // TODO: use font from view style from configuration + aFontRequest.CellSize = 10; + + for (sal_Int32 nLoop=0; nLoop<3; ++nLoop) + { + mxFont = rxCanvas->createFont( + aFontRequest, + Sequence<beans::PropertyValue>(), + geometry::Matrix2D(1,0,0,1)); + if (mxFont.is()) + { + Reference<rendering::XTextLayout> xLayout (mxFont->createTextLayout( + aContext, + rendering::TextDirection::WEAK_LEFT_TO_RIGHT, + 0)); + + if ( ! xLayout.is()) + break; + + geometry::RealRectangle2D aBox (xLayout->queryTextBounds()); + if (aBox.X2<=aBox.X1 || aBox.Y2<=aBox.Y1) + break; + const double nHorizontalFactor = maWindowSize.Width / (aBox.X2-aBox.X1+1); + const double nVerticalFactor = maWindowSize.Height / (aBox.Y2-aBox.Y1+1); + aFontRequest.CellSize *= ::std::min(nHorizontalFactor,nVerticalFactor); + } + } + } +} + + +} // end of anonymous namespace + + +} } // end of namespace ::sdext::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |