1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <boost/current_function.hpp>
22 #include <rtl/ustrbuf.hxx>
23 #include <vcl/svapp.hxx>
24 #include <vcl/gdimtf.hxx>
25 #include <vcl/virdev.hxx>
26 #include <vcl/metric.hxx>
27 #include <vcl/settings.hxx>
29 #include <cppcanvas/vclfactory.hxx>
30 #include <cppcanvas/basegfxfactory.hxx>
31 #include <basegfx/range/b2drange.hxx>
33 #include <comphelper/anytostring.hxx>
34 #include <cppuhelper/exc_hlp.hxx>
36 #include <com/sun/star/awt/MouseButton.hpp>
37 #include <com/sun/star/awt/MouseEvent.hpp>
38 #include <com/sun/star/rendering/XBitmap.hpp>
40 #include "eventqueue.hxx"
41 #include "screenupdater.hxx"
42 #include "eventmultiplexer.hxx"
43 #include "activitiesqueue.hxx"
44 #include "slideshowcontext.hxx"
45 #include "mouseeventhandler.hxx"
46 #include "rehearsetimingsactivity.hxx"
48 #include <boost/bind.hpp>
49 #include <o3tl/compat_functional.hxx>
52 using namespace com::sun::star
;
53 using namespace com::sun::star::uno
;
58 class RehearseTimingsActivity::WakeupEvent
: public Event
,
59 private ::boost::noncopyable
62 WakeupEvent( boost::shared_ptr
< ::canvas::tools::ElapsedTime
> const& pTimeBase
,
63 ActivitySharedPtr
const& rActivity
,
64 ActivitiesQueue
& rActivityQueue
) :
68 mpActivity(rActivity
),
69 mrActivityQueue( rActivityQueue
)
72 virtual void dispose() SAL_OVERRIDE
{}
73 virtual bool fire() SAL_OVERRIDE
75 ActivitySharedPtr
pActivity( mpActivity
.lock() );
79 return mrActivityQueue
.addActivity( pActivity
);
82 virtual bool isCharged() const SAL_OVERRIDE
{ return true; }
83 virtual double getActivationTime( double nCurrentTime
) const SAL_OVERRIDE
85 const double nElapsedTime( maTimer
.getElapsedTime() );
87 return ::std::max( nCurrentTime
,
88 nCurrentTime
- nElapsedTime
+ mnNextTime
);
91 /// Start the internal timer
92 void start() { maTimer
.reset(); }
94 /** Set the next timeout this object should generate.
97 Absolute time, measured from the last start() call,
98 when this event should wakeup the Activity again. If
99 your time is relative, simply call start() just before
100 every setNextTimeout() call.
102 void setNextTimeout( double nextTime
) { mnNextTime
= nextTime
; }
105 ::canvas::tools::ElapsedTime maTimer
;
107 boost::weak_ptr
<Activity
> mpActivity
;
108 ActivitiesQueue
& mrActivityQueue
;
111 class RehearseTimingsActivity::MouseHandler
: public MouseEventHandler
,
112 private boost::noncopyable
115 explicit MouseHandler( RehearseTimingsActivity
& rta
);
118 bool hasBeenClicked() const { return mbHasBeenClicked
; }
121 virtual bool handleMousePressed( awt::MouseEvent
const & evt
) SAL_OVERRIDE
;
122 virtual bool handleMouseReleased( awt::MouseEvent
const & evt
) SAL_OVERRIDE
;
123 virtual bool handleMouseEntered( awt::MouseEvent
const & evt
) SAL_OVERRIDE
;
124 virtual bool handleMouseExited( awt::MouseEvent
const & evt
) SAL_OVERRIDE
;
125 virtual bool handleMouseDragged( awt::MouseEvent
const & evt
) SAL_OVERRIDE
;
126 virtual bool handleMouseMoved( awt::MouseEvent
const & evt
) SAL_OVERRIDE
;
129 bool isInArea( com::sun::star::awt::MouseEvent
const & evt
) const;
130 void updatePressedState( const bool pressedState
) const;
132 RehearseTimingsActivity
& mrActivity
;
133 bool mbHasBeenClicked
;
134 bool mbMouseStartedInArea
;
137 const sal_Int32 LEFT_BORDER_SPACE
= 10;
138 const sal_Int32 LOWER_BORDER_SPACE
= 30;
140 RehearseTimingsActivity::RehearseTimingsActivity( const SlideShowContext
& rContext
) :
141 mrEventQueue(rContext
.mrEventQueue
),
142 mrScreenUpdater(rContext
.mrScreenUpdater
),
143 mrEventMultiplexer(rContext
.mrEventMultiplexer
),
144 mrActivitiesQueue(rContext
.mrActivitiesQueue
),
145 maElapsedTime( rContext
.mrEventQueue
.getTimer() ),
148 maFont( Application::GetSettings().GetStyleSettings().GetInfoFont() ),
156 maFont
.SetHeight( maFont
.GetHeight() * 2 );
157 maFont
.SetWidth( maFont
.GetWidth() * 2 );
158 maFont
.SetAlign( ALIGN_BASELINE
);
159 maFont
.SetColor( COL_BLACK
);
161 // determine sprite size (in pixel):
162 ScopedVclPtrInstance
< VirtualDevice
> blackHole
;
163 blackHole
->EnableOutput(false);
164 blackHole
->SetFont( maFont
);
165 blackHole
->SetMapMode( MAP_PIXEL
);
167 const FontMetric
metric( blackHole
->GetFontMetric() );
168 blackHole
->GetTextBoundRect( rect
, OUString("XX:XX:XX") );
169 maSpriteSizePixel
.setX( rect
.getWidth() * 12 / 10 );
170 maSpriteSizePixel
.setY( metric
.GetLineHeight() * 11 / 10 );
171 mnYOffset
= (metric
.GetAscent() + (metric
.GetLineHeight() / 20));
173 std::for_each( rContext
.mrViewContainer
.begin(),
174 rContext
.mrViewContainer
.end(),
175 boost::bind( &RehearseTimingsActivity::viewAdded
,
180 RehearseTimingsActivity::~RehearseTimingsActivity()
186 catch (uno::Exception
&)
188 OSL_FAIL( OUStringToOString(
189 comphelper::anyToString(
190 cppu::getCaughtException() ),
191 RTL_TEXTENCODING_UTF8
).getStr() );
195 boost::shared_ptr
<RehearseTimingsActivity
> RehearseTimingsActivity::create(
196 const SlideShowContext
& rContext
)
198 boost::shared_ptr
<RehearseTimingsActivity
> pActivity(
199 new RehearseTimingsActivity( rContext
));
201 pActivity
->mpMouseHandler
.reset(
202 new MouseHandler(*pActivity
.get()) );
203 pActivity
->mpWakeUpEvent
.reset(
204 new WakeupEvent( rContext
.mrEventQueue
.getTimer(),
206 rContext
.mrActivitiesQueue
));
208 rContext
.mrEventMultiplexer
.addViewHandler( pActivity
);
213 void RehearseTimingsActivity::start()
215 maElapsedTime
.reset();
216 mbDrawPressed
= false;
219 // paint and show all sprites:
221 for_each_sprite( boost::bind( &cppcanvas::Sprite::show
, _1
) );
223 mrActivitiesQueue
.addActivity( shared_from_this() );
225 mpMouseHandler
->reset();
226 mrEventMultiplexer
.addClickHandler(
227 mpMouseHandler
, 42 /* highest prio of all, > 3.0 */ );
228 mrEventMultiplexer
.addMouseMoveHandler(
229 mpMouseHandler
, 42 /* highest prio of all, > 3.0 */ );
232 double RehearseTimingsActivity::stop()
234 mrEventMultiplexer
.removeMouseMoveHandler( mpMouseHandler
);
235 mrEventMultiplexer
.removeClickHandler( mpMouseHandler
);
237 mbActive
= false; // will be removed from queue
239 for_each_sprite( boost::bind( &cppcanvas::Sprite::hide
, _1
) );
241 return maElapsedTime
.getElapsedTime();
244 bool RehearseTimingsActivity::hasBeenClicked() const
247 return mpMouseHandler
->hasBeenClicked();
252 void RehearseTimingsActivity::dispose()
256 mpWakeUpEvent
.reset();
257 mpMouseHandler
.reset();
259 ViewsVecT().swap( maViews
);
263 double RehearseTimingsActivity::calcTimeLag() const
268 bool RehearseTimingsActivity::perform()
276 mpWakeUpEvent
->start();
277 mpWakeUpEvent
->setNextTimeout( 0.5 );
278 mrEventQueue
.addEvent( mpWakeUpEvent
);
282 // sprites changed, need screen update
283 mrScreenUpdater
.notifyUpdate();
285 return false; // don't reinsert, WakeupEvent will perform
286 // that after the given timeout
289 bool RehearseTimingsActivity::isActive() const
294 void RehearseTimingsActivity::dequeued()
299 void RehearseTimingsActivity::end()
308 basegfx::B2DRange
RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr
const& rView
) const
310 const Reference
<rendering::XBitmap
> xBitmap( rView
->getCanvas()->getUNOCanvas(),
313 return basegfx::B2DRange();
315 const geometry::IntegerSize2D
realSize( xBitmap
->getSize() );
317 basegfx::B2DPoint
spritePos(
318 std::min
<sal_Int32
>( realSize
.Width
, LEFT_BORDER_SPACE
),
319 std::max
<sal_Int32
>( 0, realSize
.Height
- maSpriteSizePixel
.getY()
320 - LOWER_BORDER_SPACE
) );
321 basegfx::B2DHomMatrix
transformation( rView
->getTransformation() );
322 transformation
.invert();
323 spritePos
*= transformation
;
324 basegfx::B2DSize
spriteSize( maSpriteSizePixel
.getX(),
325 maSpriteSizePixel
.getY() );
326 spriteSize
*= transformation
;
327 return basegfx::B2DRange(
328 spritePos
.getX(), spritePos
.getY(),
329 spritePos
.getX() + spriteSize
.getX(),
330 spritePos
.getY() + spriteSize
.getY() );
333 void RehearseTimingsActivity::viewAdded( const UnoViewSharedPtr
& rView
)
335 cppcanvas::CustomSpriteSharedPtr
sprite(
336 rView
->createSprite( basegfx::B2DSize(
337 maSpriteSizePixel
.getX()+2,
338 maSpriteSizePixel
.getY()+2 ),
339 1001.0 )); // sprite should be in front of all
341 sprite
->setAlpha( 0.8 );
342 const basegfx::B2DRange
spriteRectangle(
343 calcSpriteRectangle( rView
) );
344 sprite
->move( basegfx::B2DPoint(
345 spriteRectangle
.getMinX(),
346 spriteRectangle
.getMinY() ) );
348 if( maViews
.empty() )
349 maSpriteRectangle
= spriteRectangle
;
351 maViews
.push_back( ViewsVecT::value_type( rView
, sprite
) );
357 void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr
& rView
)
361 maViews
.begin(), maViews
.end(),
363 std::equal_to
<UnoViewSharedPtr
>(),
366 boost::bind( o3tl::select1st
<ViewsVecT::value_type
>(), _1
))),
370 void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr
& rView
)
372 // find entry corresponding to modified view
373 ViewsVecT::iterator
aModifiedEntry(
378 std::equal_to
<UnoViewSharedPtr
>(),
381 boost::bind( o3tl::select1st
<ViewsVecT::value_type
>(), _1
))));
383 OSL_ASSERT( aModifiedEntry
!= maViews
.end() );
384 if( aModifiedEntry
== maViews
.end() )
387 // new sprite pos, transformation might have changed:
388 maSpriteRectangle
= calcSpriteRectangle( rView
);
390 // reposition sprite:
391 aModifiedEntry
->second
->move( maSpriteRectangle
.getMinimum() );
393 // sprites changed, need screen update
394 mrScreenUpdater
.notifyUpdate( rView
);
397 void RehearseTimingsActivity::viewsChanged()
399 if( !maViews
.empty() )
401 // new sprite pos, transformation might have changed:
402 maSpriteRectangle
= calcSpriteRectangle( maViews
.front().first
);
404 // reposition sprites
405 for_each_sprite( boost::bind( &cppcanvas::Sprite::move
,
407 maSpriteRectangle
.getMinimum()) );
409 // sprites changed, need screen update
410 mrScreenUpdater
.notifyUpdate();
414 void RehearseTimingsActivity::paintAllSprites() const
417 boost::bind( &RehearseTimingsActivity::paint
, this,
418 // call getContentCanvas() on each sprite:
420 &cppcanvas::CustomSprite::getContentCanvas
, _1
) ) );
423 void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr
const & canvas
) const
425 // build timer string:
426 const sal_Int32 nTimeSecs
=
427 static_cast<sal_Int32
>(maElapsedTime
.getElapsedTime());
429 sal_Int32 n
= (nTimeSecs
/ 3600);
434 n
= ((nTimeSecs
% 3600) / 60);
439 n
= (nTimeSecs
% 60);
443 const OUString time
= buf
.makeStringAndClear();
445 // create the MetaFile:
446 GDIMetaFile metaFile
;
447 ScopedVclPtrInstance
< VirtualDevice
> blackHole
;
448 metaFile
.Record( blackHole
);
449 metaFile
.SetPrefSize( Size( 1, 1 ) );
450 blackHole
->EnableOutput(false);
451 blackHole
->SetMapMode( MAP_PIXEL
);
452 blackHole
->SetFont( maFont
);
453 Rectangle rect
= Rectangle( 0,0,
454 maSpriteSizePixel
.getX(),
455 maSpriteSizePixel
.getY());
458 blackHole
->SetTextColor( COL_BLACK
);
459 blackHole
->SetFillColor( COL_LIGHTGRAY
);
460 blackHole
->SetLineColor( COL_GRAY
);
464 blackHole
->SetTextColor( COL_BLACK
);
465 blackHole
->SetFillColor( COL_WHITE
);
466 blackHole
->SetLineColor( COL_GRAY
);
468 blackHole
->DrawRect( rect
);
469 blackHole
->GetTextBoundRect( rect
, time
);
471 Point( (maSpriteSizePixel
.getX() - rect
.getWidth()) / 2,
475 metaFile
.WindStart();
477 cppcanvas::RendererSharedPtr
renderer(
478 cppcanvas::VCLFactory::createRenderer(
479 canvas
, metaFile
, cppcanvas::Renderer::Parameters() ) );
480 const bool succ
= renderer
->draw();
486 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity
& rta
) :
488 mbHasBeenClicked(false),
489 mbMouseStartedInArea(false)
492 void RehearseTimingsActivity::MouseHandler::reset()
494 mbHasBeenClicked
= false;
495 mbMouseStartedInArea
= false;
498 bool RehearseTimingsActivity::MouseHandler::isInArea(
499 awt::MouseEvent
const & evt
) const
501 return mrActivity
.maSpriteRectangle
.isInside(
502 basegfx::B2DPoint( evt
.X
, evt
.Y
) );
505 void RehearseTimingsActivity::MouseHandler::updatePressedState(
506 const bool pressedState
) const
508 if( pressedState
!= mrActivity
.mbDrawPressed
)
510 mrActivity
.mbDrawPressed
= pressedState
;
511 mrActivity
.paintAllSprites();
513 mrActivity
.mrScreenUpdater
.notifyUpdate();
518 bool RehearseTimingsActivity::MouseHandler::handleMousePressed(
519 awt::MouseEvent
const & evt
)
521 if( evt
.Buttons
== awt::MouseButton::LEFT
&& isInArea(evt
) )
523 mbMouseStartedInArea
= true;
524 updatePressedState(true);
525 return true; // consume event
530 bool RehearseTimingsActivity::MouseHandler::handleMouseReleased(
531 awt::MouseEvent
const & evt
)
533 if( evt
.Buttons
== awt::MouseButton::LEFT
&& mbMouseStartedInArea
)
535 mbHasBeenClicked
= isInArea(evt
); // fini if in
536 mbMouseStartedInArea
= false;
537 updatePressedState(false);
538 if( !mbHasBeenClicked
)
539 return true; // consume event, else next slide (manual advance)
544 bool RehearseTimingsActivity::MouseHandler::handleMouseEntered(
545 awt::MouseEvent
const & /*evt*/ )
550 bool RehearseTimingsActivity::MouseHandler::handleMouseExited(
551 awt::MouseEvent
const & /*evt*/ )
556 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
557 awt::MouseEvent
const & evt
)
559 if( mbMouseStartedInArea
)
560 updatePressedState( isInArea(evt
) );
564 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
565 awt::MouseEvent
const & /*evt*/ )
570 } // namespace internal
571 } // namespace presentation
573 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */