| /************************************************************** |
| * |
| * 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_slideshow.hxx" |
| |
| #include <boost/current_function.hpp> |
| #include <rtl/ustrbuf.hxx> |
| #include <vcl/svapp.hxx> |
| #include <vcl/gdimtf.hxx> |
| #include <vcl/virdev.hxx> |
| #include <vcl/metric.hxx> |
| #include <cppcanvas/vclfactory.hxx> |
| #include <cppcanvas/basegfxfactory.hxx> |
| #include <basegfx/range/b2drange.hxx> |
| |
| #include <comphelper/anytostring.hxx> |
| #include <cppuhelper/exc_hlp.hxx> |
| |
| #include <com/sun/star/awt/MouseButton.hpp> |
| #include <com/sun/star/awt/MouseEvent.hpp> |
| #include <com/sun/star/rendering/XBitmap.hpp> |
| |
| #include "eventqueue.hxx" |
| #include "screenupdater.hxx" |
| #include "eventmultiplexer.hxx" |
| #include "activitiesqueue.hxx" |
| #include "slideshowcontext.hxx" |
| #include "mouseeventhandler.hxx" |
| #include "rehearsetimingsactivity.hxx" |
| |
| #include <boost/bind.hpp> |
| #include <algorithm> |
| |
| using namespace com::sun::star; |
| using namespace com::sun::star::uno; |
| |
| namespace slideshow { |
| namespace internal { |
| |
| class RehearseTimingsActivity::WakeupEvent : public Event, |
| private ::boost::noncopyable |
| { |
| public: |
| WakeupEvent( boost::shared_ptr< ::canvas::tools::ElapsedTime > const& pTimeBase, |
| ActivitySharedPtr const& rActivity, |
| ActivitiesQueue & rActivityQueue ) : |
| #if OSL_DEBUG_LEVEL > 1 |
| Event(::rtl::OUString::createFromAscii("WakeupEvent")), |
| #endif |
| maTimer(pTimeBase), |
| mnNextTime(0.0), |
| mpActivity(rActivity), |
| mrActivityQueue( rActivityQueue ) |
| {} |
| |
| virtual void dispose() {} |
| virtual bool fire() |
| { |
| ActivitySharedPtr pActivity( mpActivity.lock() ); |
| if( !pActivity ) |
| return false; |
| |
| return mrActivityQueue.addActivity( pActivity ); |
| } |
| |
| virtual bool isCharged() const { return true; } |
| virtual double getActivationTime( double nCurrentTime ) const |
| { |
| const double nElapsedTime( maTimer.getElapsedTime() ); |
| |
| return ::std::max( nCurrentTime, |
| nCurrentTime - nElapsedTime + mnNextTime ); |
| } |
| |
| /// Start the internal timer |
| void start() { maTimer.reset(); } |
| |
| /** Set the next timeout this object should generate. |
| |
| @param nextTime |
| Absolute time, measured from the last start() call, |
| when this event should wakeup the Activity again. If |
| your time is relative, simply call start() just before |
| every setNextTimeout() call. |
| */ |
| void setNextTimeout( double nextTime ) { mnNextTime = nextTime; } |
| |
| private: |
| ::canvas::tools::ElapsedTime maTimer; |
| double mnNextTime; |
| boost::weak_ptr<Activity> mpActivity; |
| ActivitiesQueue& mrActivityQueue; |
| }; |
| |
| class RehearseTimingsActivity::MouseHandler : public MouseEventHandler, |
| private boost::noncopyable |
| { |
| public: |
| explicit MouseHandler( RehearseTimingsActivity& rta ); |
| |
| void reset(); |
| bool hasBeenClicked() const { return mbHasBeenClicked; } |
| |
| // MouseEventHandler |
| virtual bool handleMousePressed( awt::MouseEvent const & evt ); |
| virtual bool handleMouseReleased( awt::MouseEvent const & evt ); |
| virtual bool handleMouseEntered( awt::MouseEvent const & evt ); |
| virtual bool handleMouseExited( awt::MouseEvent const & evt ); |
| virtual bool handleMouseDragged( awt::MouseEvent const & evt ); |
| virtual bool handleMouseMoved( awt::MouseEvent const & evt ); |
| |
| private: |
| bool isInArea( com::sun::star::awt::MouseEvent const & evt ) const; |
| void updatePressedState( const bool pressedState ) const; |
| |
| RehearseTimingsActivity& mrActivity; |
| bool mbHasBeenClicked; |
| bool mbMouseStartedInArea; |
| }; |
| |
| const sal_Int32 LEFT_BORDER_SPACE = 10; |
| const sal_Int32 LOWER_BORDER_SPACE = 30; |
| |
| RehearseTimingsActivity::RehearseTimingsActivity( const SlideShowContext& rContext ) : |
| mrEventQueue(rContext.mrEventQueue), |
| mrScreenUpdater(rContext.mrScreenUpdater), |
| mrEventMultiplexer(rContext.mrEventMultiplexer), |
| mrActivitiesQueue(rContext.mrActivitiesQueue), |
| maElapsedTime( rContext.mrEventQueue.getTimer() ), |
| maViews(), |
| maSpriteRectangle(), |
| maFont( Application::GetSettings().GetStyleSettings().GetInfoFont() ), |
| mpWakeUpEvent(), |
| mpMouseHandler(), |
| maSpriteSizePixel(), |
| mnYOffset(0), |
| mbActive(false), |
| mbDrawPressed(false) |
| { |
| maFont.SetHeight( maFont.GetHeight() * 2 ); |
| maFont.SetWidth( maFont.GetWidth() * 2 ); |
| maFont.SetAlign( ALIGN_BASELINE ); |
| maFont.SetColor( COL_BLACK ); |
| |
| // determine sprite size (in pixel): |
| VirtualDevice blackHole; |
| blackHole.EnableOutput(false); |
| blackHole.SetFont( maFont ); |
| blackHole.SetMapMode( MAP_PIXEL ); |
| Rectangle rect; |
| const FontMetric metric( blackHole.GetFontMetric() ); |
| blackHole.GetTextBoundRect( |
| rect, String(RTL_CONSTASCII_USTRINGPARAM("XX:XX:XX")) ); |
| maSpriteSizePixel.setX( rect.getWidth() * 12 / 10 ); |
| maSpriteSizePixel.setY( metric.GetLineHeight() * 11 / 10 ); |
| mnYOffset = (metric.GetAscent() + (metric.GetLineHeight() / 20)); |
| |
| std::for_each( rContext.mrViewContainer.begin(), |
| rContext.mrViewContainer.end(), |
| boost::bind( &RehearseTimingsActivity::viewAdded, |
| this, |
| _1 )); |
| } |
| |
| RehearseTimingsActivity::~RehearseTimingsActivity() |
| { |
| try |
| { |
| stop(); |
| } |
| catch (uno::Exception &) |
| { |
| OSL_ENSURE( false, rtl::OUStringToOString( |
| comphelper::anyToString( |
| cppu::getCaughtException() ), |
| RTL_TEXTENCODING_UTF8 ).getStr() ); |
| } |
| } |
| |
| boost::shared_ptr<RehearseTimingsActivity> RehearseTimingsActivity::create( |
| const SlideShowContext& rContext ) |
| { |
| boost::shared_ptr<RehearseTimingsActivity> pActivity( |
| new RehearseTimingsActivity( rContext )); |
| |
| pActivity->mpMouseHandler.reset( |
| new MouseHandler(*pActivity.get()) ); |
| pActivity->mpWakeUpEvent.reset( |
| new WakeupEvent( rContext.mrEventQueue.getTimer(), |
| pActivity, |
| rContext.mrActivitiesQueue )); |
| |
| rContext.mrEventMultiplexer.addViewHandler( pActivity ); |
| |
| return pActivity; |
| } |
| |
| void RehearseTimingsActivity::start() |
| { |
| maElapsedTime.reset(); |
| mbDrawPressed = false; |
| mbActive = true; |
| |
| // paint and show all sprites: |
| paintAllSprites(); |
| for_each_sprite( boost::bind( &cppcanvas::Sprite::show, _1 ) ); |
| |
| mrActivitiesQueue.addActivity( shared_from_this() ); |
| |
| mpMouseHandler->reset(); |
| mrEventMultiplexer.addClickHandler( |
| mpMouseHandler, 42 /* highest prio of all, > 3.0 */ ); |
| mrEventMultiplexer.addMouseMoveHandler( |
| mpMouseHandler, 42 /* highest prio of all, > 3.0 */ ); |
| } |
| |
| double RehearseTimingsActivity::stop() |
| { |
| mrEventMultiplexer.removeMouseMoveHandler( mpMouseHandler ); |
| mrEventMultiplexer.removeClickHandler( mpMouseHandler ); |
| |
| mbActive = false; // will be removed from queue |
| |
| for_each_sprite( boost::bind( &cppcanvas::Sprite::hide, _1 ) ); |
| |
| return maElapsedTime.getElapsedTime(); |
| } |
| |
| bool RehearseTimingsActivity::hasBeenClicked() const |
| { |
| if (mpMouseHandler) |
| return mpMouseHandler->hasBeenClicked(); |
| return false; |
| } |
| |
| // Disposable: |
| void RehearseTimingsActivity::dispose() |
| { |
| stop(); |
| |
| mpWakeUpEvent.reset(); |
| mpMouseHandler.reset(); |
| |
| ViewsVecT().swap( maViews ); |
| } |
| |
| // Activity: |
| double RehearseTimingsActivity::calcTimeLag() const |
| { |
| return 0.0; |
| } |
| |
| bool RehearseTimingsActivity::perform() |
| { |
| if( !isActive() ) |
| return false; |
| |
| if( !mpWakeUpEvent ) |
| return false; |
| |
| mpWakeUpEvent->start(); |
| mpWakeUpEvent->setNextTimeout( 0.5 ); |
| mrEventQueue.addEvent( mpWakeUpEvent ); |
| |
| paintAllSprites(); |
| |
| // sprites changed, need screen update |
| mrScreenUpdater.notifyUpdate(); |
| |
| return false; // don't reinsert, WakeupEvent will perform |
| // that after the given timeout |
| } |
| |
| bool RehearseTimingsActivity::isActive() const |
| { |
| return mbActive; |
| } |
| |
| void RehearseTimingsActivity::dequeued() |
| { |
| // not used here |
| } |
| |
| void RehearseTimingsActivity::end() |
| { |
| if (isActive()) |
| { |
| stop(); |
| mbActive = false; |
| } |
| } |
| |
| basegfx::B2DRange RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr const& rView ) const |
| { |
| const Reference<rendering::XBitmap> xBitmap( rView->getCanvas()->getUNOCanvas(), |
| UNO_QUERY ); |
| if( !xBitmap.is() ) |
| return basegfx::B2DRange(); |
| |
| const geometry::IntegerSize2D realSize( xBitmap->getSize() ); |
| // pixel: |
| basegfx::B2DPoint spritePos( |
| std::min<sal_Int32>( realSize.Width, LEFT_BORDER_SPACE ), |
| std::max<sal_Int32>( 0, realSize.Height - maSpriteSizePixel.getY() |
| - LOWER_BORDER_SPACE ) ); |
| basegfx::B2DHomMatrix transformation( rView->getTransformation() ); |
| transformation.invert(); |
| spritePos *= transformation; |
| basegfx::B2DSize spriteSize( maSpriteSizePixel.getX(), |
| maSpriteSizePixel.getY() ); |
| spriteSize *= transformation; |
| return basegfx::B2DRange( |
| spritePos.getX(), spritePos.getY(), |
| spritePos.getX() + spriteSize.getX(), |
| spritePos.getY() + spriteSize.getY() ); |
| } |
| |
| void RehearseTimingsActivity::viewAdded( const UnoViewSharedPtr& rView ) |
| { |
| cppcanvas::CustomSpriteSharedPtr sprite( |
| rView->createSprite( basegfx::B2DSize( |
| maSpriteSizePixel.getX()+2, |
| maSpriteSizePixel.getY()+2 ), |
| 1001.0 )); // sprite should be in front of all |
| // other sprites |
| sprite->setAlpha( 0.8 ); |
| const basegfx::B2DRange spriteRectangle( |
| calcSpriteRectangle( rView ) ); |
| sprite->move( basegfx::B2DPoint( |
| spriteRectangle.getMinX(), |
| spriteRectangle.getMinY() ) ); |
| |
| if( maViews.empty() ) |
| maSpriteRectangle = spriteRectangle; |
| |
| maViews.push_back( ViewsVecT::value_type( rView, sprite ) ); |
| |
| if (isActive()) |
| sprite->show(); |
| } |
| |
| void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr& rView ) |
| { |
| maViews.erase( |
| std::remove_if( |
| maViews.begin(), maViews.end(), |
| boost::bind( |
| std::equal_to<UnoViewSharedPtr>(), |
| rView, |
| // select view: |
| boost::bind( std::select1st<ViewsVecT::value_type>(), _1 ))), |
| maViews.end() ); |
| } |
| |
| void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr& rView ) |
| { |
| // find entry corresponding to modified view |
| ViewsVecT::iterator aModifiedEntry( |
| std::find_if( |
| maViews.begin(), |
| maViews.end(), |
| boost::bind( |
| std::equal_to<UnoViewSharedPtr>(), |
| rView, |
| // select view: |
| boost::bind( std::select1st<ViewsVecT::value_type>(), _1 )))); |
| |
| OSL_ASSERT( aModifiedEntry != maViews.end() ); |
| if( aModifiedEntry == maViews.end() ) |
| return; |
| |
| // new sprite pos, transformation might have changed: |
| maSpriteRectangle = calcSpriteRectangle( rView ); |
| |
| // reposition sprite: |
| aModifiedEntry->second->move( maSpriteRectangle.getMinimum() ); |
| |
| // sprites changed, need screen update |
| mrScreenUpdater.notifyUpdate( rView ); |
| } |
| |
| void RehearseTimingsActivity::viewsChanged() |
| { |
| if( !maViews.empty() ) |
| { |
| // new sprite pos, transformation might have changed: |
| maSpriteRectangle = calcSpriteRectangle( maViews.front().first ); |
| |
| // reposition sprites |
| for_each_sprite( boost::bind( &cppcanvas::Sprite::move, |
| _1, |
| boost::cref(maSpriteRectangle.getMinimum())) ); |
| |
| // sprites changed, need screen update |
| mrScreenUpdater.notifyUpdate(); |
| } |
| } |
| |
| void RehearseTimingsActivity::paintAllSprites() const |
| { |
| for_each_sprite( |
| boost::bind( &RehearseTimingsActivity::paint, this, |
| // call getContentCanvas() on each sprite: |
| boost::bind( |
| &cppcanvas::CustomSprite::getContentCanvas, _1 ) ) ); |
| } |
| |
| void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr const & canvas ) const |
| { |
| // build timer string: |
| const sal_Int32 nTimeSecs = |
| static_cast<sal_Int32>(maElapsedTime.getElapsedTime()); |
| rtl::OUStringBuffer buf; |
| sal_Int32 n = (nTimeSecs / 3600); |
| if (n < 10) |
| buf.append( static_cast<sal_Unicode>('0') ); |
| buf.append( n ); |
| buf.append( static_cast<sal_Unicode>(':') ); |
| n = ((nTimeSecs % 3600) / 60); |
| if (n < 10) |
| buf.append( static_cast<sal_Unicode>('0') ); |
| buf.append( n ); |
| buf.append( static_cast<sal_Unicode>(':') ); |
| n = (nTimeSecs % 60); |
| if (n < 10) |
| buf.append( static_cast<sal_Unicode>('0') ); |
| buf.append( n ); |
| const rtl::OUString time = buf.makeStringAndClear(); |
| |
| // create the MetaFile: |
| GDIMetaFile metaFile; |
| VirtualDevice blackHole; |
| metaFile.Record( &blackHole ); |
| metaFile.SetPrefSize( Size( 1, 1 ) ); |
| blackHole.EnableOutput(false); |
| blackHole.SetMapMode( MAP_PIXEL ); |
| blackHole.SetFont( maFont ); |
| Rectangle rect = Rectangle( 0,0, |
| maSpriteSizePixel.getX(), |
| maSpriteSizePixel.getY()); |
| if (mbDrawPressed) |
| { |
| blackHole.SetTextColor( COL_BLACK ); |
| blackHole.SetFillColor( COL_LIGHTGRAY ); |
| blackHole.SetLineColor( COL_GRAY ); |
| } |
| else |
| { |
| blackHole.SetTextColor( COL_BLACK ); |
| blackHole.SetFillColor( COL_WHITE ); |
| blackHole.SetLineColor( COL_GRAY ); |
| } |
| blackHole.DrawRect( rect ); |
| blackHole.GetTextBoundRect( rect, time ); |
| blackHole.DrawText( |
| Point( (maSpriteSizePixel.getX() - rect.getWidth()) / 2, |
| mnYOffset ), time ); |
| |
| metaFile.Stop(); |
| metaFile.WindStart(); |
| |
| cppcanvas::RendererSharedPtr renderer( |
| cppcanvas::VCLFactory::getInstance().createRenderer( |
| canvas, metaFile, cppcanvas::Renderer::Parameters() ) ); |
| const bool succ = renderer->draw(); |
| OSL_ASSERT( succ ); |
| (void)succ; |
| } |
| |
| |
| RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity& rta ) : |
| mrActivity(rta), |
| mbHasBeenClicked(false), |
| mbMouseStartedInArea(false) |
| {} |
| |
| void RehearseTimingsActivity::MouseHandler::reset() |
| { |
| mbHasBeenClicked = false; |
| mbMouseStartedInArea = false; |
| } |
| |
| bool RehearseTimingsActivity::MouseHandler::isInArea( |
| awt::MouseEvent const & evt ) const |
| { |
| return mrActivity.maSpriteRectangle.isInside( |
| basegfx::B2DPoint( evt.X, evt.Y ) ); |
| } |
| |
| void RehearseTimingsActivity::MouseHandler::updatePressedState( |
| const bool pressedState ) const |
| { |
| if( pressedState != mrActivity.mbDrawPressed ) |
| { |
| mrActivity.mbDrawPressed = pressedState; |
| mrActivity.paintAllSprites(); |
| |
| mrActivity.mrScreenUpdater.notifyUpdate(); |
| } |
| } |
| |
| // MouseEventHandler |
| bool RehearseTimingsActivity::MouseHandler::handleMousePressed( |
| awt::MouseEvent const & evt ) |
| { |
| if( evt.Buttons == awt::MouseButton::LEFT && isInArea(evt) ) |
| { |
| mbMouseStartedInArea = true; |
| updatePressedState(true); |
| return true; // consume event |
| } |
| return false; |
| } |
| |
| bool RehearseTimingsActivity::MouseHandler::handleMouseReleased( |
| awt::MouseEvent const & evt ) |
| { |
| if( evt.Buttons == awt::MouseButton::LEFT && mbMouseStartedInArea ) |
| { |
| mbHasBeenClicked = isInArea(evt); // fini if in |
| mbMouseStartedInArea = false; |
| updatePressedState(false); |
| if( !mbHasBeenClicked ) |
| return true; // consume event, else next slide (manual advance) |
| } |
| return false; |
| } |
| |
| bool RehearseTimingsActivity::MouseHandler::handleMouseEntered( |
| awt::MouseEvent const & /*evt*/ ) |
| { |
| return false; |
| } |
| |
| bool RehearseTimingsActivity::MouseHandler::handleMouseExited( |
| awt::MouseEvent const & /*evt*/ ) |
| { |
| return false; |
| } |
| |
| bool RehearseTimingsActivity::MouseHandler::handleMouseDragged( |
| awt::MouseEvent const & evt ) |
| { |
| if( mbMouseStartedInArea ) |
| updatePressedState( isInArea(evt) ); |
| return false; |
| } |
| |
| bool RehearseTimingsActivity::MouseHandler::handleMouseMoved( |
| awt::MouseEvent const & /*evt*/ ) |
| { |
| return false; |
| } |
| |
| } // namespace internal |
| } // namespace presentation |