/************************************************************** * * 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 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sdext.hxx" #include "PresenterClock.hxx" #include "PresenterConfigurationAccess.hxx" #include "PresenterGeometryHelper.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 vos::OTimer { public: explicit Timer (const ::rtl::Reference& rpClock); virtual ~Timer (void); void Stop (void); protected: virtual void SAL_CALL onShot (void); private: ::rtl::Reference mpClock; }; namespace { bool GetDateTime (oslDateTime& rDateTime); class BitmapDescriptor { public: Reference mxBitmap; awt::Point maOffset; Reference mxScaledBitmap; geometry::RealPoint2D maScaledOffset; }; } class PresenterClock::Painter { public: virtual void Paint ( const Reference& 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& 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 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& rxCanvas, const rendering::ViewState& rViewState, const rendering::RenderState& rRenderState); }; class AnalogBitmapPainter : public PresenterClock::Painter { public: AnalogBitmapPainter( const Reference& rxContext, const OUString& rsThemeName); virtual ~AnalogBitmapPainter (void) {} virtual void Paint ( const Reference& 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 mxComponentContext; const OUString msThemeName; bool mbThemeLoaded; bool mbThemeLoadingFailed; geometry::RealPoint2D maCenter; double mnOuterRadius; BitmapDescriptor maFace; BitmapDescriptor maMinuteHand; BitmapDescriptor maHourHand; void PrepareBitmaps (const Reference& rxCanvas); Reference GetTheme ( PresenterConfigurationAccess& rConfiguration); bool ThemeNameComparator ( const ::rtl::OUString& rsKey, const Reference& rxCandidate, const ::rtl::OUString& rsCurrentThemeName); void LoadBitmaps ( PresenterConfigurationAccess& rConfiguration, const Reference& rxNameAccess, const Reference& rxCanvas); void LoadBitmap ( const OUString& rsKey, const ::std::vector& rValues, const Reference& rxBitmapLoader); void ScaleBitmaps (void); }; class DigitalDefaultPainter : public PresenterClock::Painter { public: DigitalDefaultPainter ( const ::rtl::Reference& rpPresenterController, const Reference& rxViewId); virtual ~DigitalDefaultPainter (void); virtual void Paint ( const Reference& 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 mpPresenterController; bool mbIs24HourFormat; bool mbIsAdaptFontSize; Reference mxFont; awt::Size maWindowSize; OUString msViewURL; void CreateFont ( const Reference& rxCanvas, const bool bIsShowSeconds); }; } // end of anonymous namespace //===== PresenterClock ================================================================= ::rtl::Reference PresenterClock::Create ( const Reference& rxContext, const Reference& rxViewId, const Reference& rxController, const ::rtl::Reference& rpPresenterController) { ::rtl::Reference pClock (new PresenterClock( rxContext, rxViewId, rxController, rpPresenterController)); pClock->LateInit(); return pClock; } PresenterClock::PresenterClock ( const Reference& rxContext, const Reference& rxViewId, const Reference& rxController, const ::rtl::Reference& 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(4); PresenterCanvasHelper::SetDeviceColor(maRenderState, util::Color(0x00000000)); try { Reference xCM (rxController, UNO_QUERY_THROW); Reference xCC (xCM->getConfigurationController(), UNO_QUERY_THROW); mxPane = Reference(xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW); mxWindow = mxPane->getWindow(); if (mxWindow.is()) { mxWindow->addPaintListener(this); mxWindow->addWindowListener(this); mxWindow->addMouseListener(this); Reference 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) { // osl::MutexGuard aGuard (m_aMutex); 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 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) { // ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex()); // osl::MutexGuard aGuard (m_aMutex); if (rEventObject.Source == mxWindow) { mxWindow = NULL; if (mpTimer != NULL) mpTimer->Stop(); } } //----- XPaintListener -------------------------------------------------------- void SAL_CALL PresenterClock::windowPaint (const awt::PaintEvent& rEvent) throw (RuntimeException) { (void)rEvent; ThrowIfDisposed(); ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex()); 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) { (void)rEvent; 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 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(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(mxPane->getCanvas(), UNO_QUERY); if ( ! mxWindow.is() || ! mxCanvas.is() || ! mxCanvas->getDevice().is()) { return; } try { if (mbIsResizePending) Resize(); Reference 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 xSpriteCanvas (mxCanvas, UNO_QUERY); if (xSpriteCanvas.is()) xSpriteCanvas->updateScreen(sal_False); } void PresenterClock::Clear (const Reference& 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::createFromAscii("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::createFromAscii("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(this)); } } //===== Timer ================================================================= PresenterClock::Timer::Timer (const ::rtl::Reference& rpClock) : OTimer(vos::TTimeValue(10), vos::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& 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 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 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& 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 > aPoints(1); aPoints[0] = Sequence(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 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& rxContext, const OUString& rsThemeName) : mxComponentContext(rxContext), msThemeName(rsThemeName), mbThemeLoaded(false), mbThemeLoadingFailed(false), maCenter(), mnOuterRadius(), maFace(), maMinuteHand(), maHourHand() { } void AnalogBitmapPainter::Paint ( const Reference& 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& 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::createFromAscii("org.openoffice.Office.PresenterScreen"), PresenterConfigurationAccess::READ_ONLY); Reference xTheme (GetTheme(aConfiguration)); if (xTheme.is()) LoadBitmaps(aConfiguration, xTheme, rxCanvas); else mbThemeLoadingFailed = true; } ScaleBitmaps(); } Reference AnalogBitmapPainter::GetTheme ( PresenterConfigurationAccess& rConfiguration) { Reference xTheme; // Get root of clock themes. Reference xClock ( rConfiguration.GetConfigurationNode( OUString::createFromAscii("PresenterScreenSettings/AnalogBitmapClock")), UNO_QUERY); // Determine the name of the theme to use. OUString sCurrentThemeName (OUString::createFromAscii("DefaultTheme")); rConfiguration.GetConfigurationNode( xClock, OUString::createFromAscii("CurrentTheme")) >>= sCurrentThemeName; // Load the clock theme. Reference xThemes ( rConfiguration.GetConfigurationNode( xClock, OUString::createFromAscii("Themes")), UNO_QUERY); if (xThemes.is()) { xTheme = Reference( PresenterConfigurationAccess::Find( xThemes, ::boost::bind(&AnalogBitmapPainter::ThemeNameComparator, this, _1, _2, sCurrentThemeName)), UNO_QUERY); } return xTheme; } bool AnalogBitmapPainter::ThemeNameComparator ( const OUString& rsKey, const Reference& rxCandidate, const OUString& rsCurrentThemeName) { (void)rsKey; if (rxCandidate.is()) { OUString sThemeName; if (rxCandidate->getByName(OUString::createFromAscii("ThemeName")) >>= sThemeName) { return sThemeName == rsCurrentThemeName; } } return false; } void AnalogBitmapPainter::LoadBitmaps ( PresenterConfigurationAccess& rConfiguration, const Reference& rxClockTheme, const Reference& rxCanvas) { (void)rConfiguration; // Create the bitmap loader. Reference xFactory ( mxComponentContext->getServiceManager(), UNO_QUERY); if ( ! xFactory.is()) return; Sequence aArguments(1); aArguments[0] <<= rxCanvas; Reference xBitmapLoader( xFactory->createInstanceWithArgumentsAndContext( OUString::createFromAscii("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 xBitmaps ( rxClockTheme->getByName(OUString::createFromAscii("Bitmaps")), UNO_QUERY); ::std::vector aBitmapProperties (3); aBitmapProperties[0] = OUString::createFromAscii("FileName"); aBitmapProperties[1] = OUString::createFromAscii("XOffset"); aBitmapProperties[2] = OUString::createFromAscii("YOffset"); PresenterConfigurationAccess::ForAll( xBitmaps, aBitmapProperties, ::boost::bind(&AnalogBitmapPainter::LoadBitmap, this, _1, _2, xBitmapLoader)); } void AnalogBitmapPainter::LoadBitmap ( const OUString& rsKey, const ::std::vector& rValues, const Reference& rxBitmapLoader) { if (rValues.size() == 3) { BitmapDescriptor* pDescriptor = NULL; if (rsKey == OUString::createFromAscii("Face")) pDescriptor = &maFace; else if (rsKey == OUString::createFromAscii("HourHand")) pDescriptor = &maHourHand; else if (rsKey == OUString::createFromAscii("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( rxBitmapLoader->getByName(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& rpPresenterController, const Reference& 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& 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::createFromAscii(":"); const OUString sMinutes (OUString::valueOf(nMinute)); switch (sMinutes.getLength()) { case 1 : sText += OUString::createFromAscii("0") + sMinutes; break; case 2: sText += sMinutes; break; default: return; } if (bIsShowSeconds) { sText += OUString::createFromAscii(":"); const OUString sSeconds (OUString::valueOf(nSecond)); switch (sSeconds.getLength()) { case 1 : sText += OUString::createFromAscii("0") + sSeconds; break; case 2: sText += sSeconds; break; default: return; } } rendering::StringContext aContext ( sText, 0, sText.getLength()); Reference 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(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& 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::createFromAscii("20"); else sTimeTemplate = OUString::createFromAscii("10"); if (bIsShowSeconds) sTimeTemplate += OUString::createFromAscii(":00:00"); else sTimeTemplate += OUString::createFromAscii(":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(), geometry::Matrix2D(1,0,0,1)); if (mxFont.is()) { Reference 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