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 <rtl/ustrbuf.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/gdimtf.hxx>
24 #include <vcl/virdev.hxx>
25 #include <vcl/metric.hxx>
26 #include <vcl/settings.hxx>
28 #include <cppcanvas/vclfactory.hxx>
29 #include <basegfx/range/b2drange.hxx>
30 #include <osl/diagnose.h>
31 #include <comphelper/diagnose_ex.hxx>
33 #include <com/sun/star/awt/MouseButton.hpp>
34 #include <com/sun/star/awt/MouseEvent.hpp>
35 #include <com/sun/star/rendering/XBitmap.hpp>
36 #include <com/sun/star/rendering/XCanvas.hpp>
38 #include <eventqueue.hxx>
39 #include <screenupdater.hxx>
40 #include <eventmultiplexer.hxx>
41 #include <activitiesqueue.hxx>
42 #include <slideshowcontext.hxx>
43 #include <mouseeventhandler.hxx>
44 #include "rehearsetimingsactivity.hxx"
48 using namespace com::sun::star
;
49 using namespace com::sun::star::uno
;
51 namespace slideshow::internal
{
53 class RehearseTimingsActivity::WakeupEvent
: public Event
56 WakeupEvent( std::shared_ptr
< ::canvas::tools::ElapsedTime
> const& pTimeBase
,
57 ActivitySharedPtr
const& rActivity
,
58 ActivitiesQueue
& rActivityQueue
) :
59 Event(u
"WakeupEvent"_ustr
),
62 mpActivity(rActivity
),
63 mrActivityQueue( rActivityQueue
)
66 WakeupEvent( const WakeupEvent
& ) = delete;
67 WakeupEvent
& operator=( const WakeupEvent
& ) = delete;
69 virtual void dispose() override
{}
70 virtual bool fire() override
72 ActivitySharedPtr
pActivity( mpActivity
.lock() );
76 return mrActivityQueue
.addActivity( pActivity
);
79 virtual bool isCharged() const override
{ return true; }
80 virtual double getActivationTime( double nCurrentTime
) const override
82 const double nElapsedTime( maTimer
.getElapsedTime() );
84 return ::std::max( nCurrentTime
,
85 nCurrentTime
- nElapsedTime
+ mnNextTime
);
88 /// Start the internal timer
89 void start() { maTimer
.reset(); }
91 /** Set the next timeout this object should generate.
94 Absolute time, measured from the last start() call,
95 when this event should wakeup the Activity again. If
96 your time is relative, simply call start() just before
97 every setNextTimeout() call.
99 void setNextTimeout( double nextTime
) { mnNextTime
= nextTime
; }
102 ::canvas::tools::ElapsedTime maTimer
;
104 std::weak_ptr
<Activity
> mpActivity
;
105 ActivitiesQueue
& mrActivityQueue
;
108 class RehearseTimingsActivity::MouseHandler
: public MouseEventHandler
111 explicit MouseHandler( RehearseTimingsActivity
& rta
);
113 MouseHandler( const MouseHandler
& ) = delete;
114 MouseHandler
& operator=( const MouseHandler
& ) = delete;
117 bool hasBeenClicked() const { return mbHasBeenClicked
; }
120 virtual bool handleMousePressed( awt::MouseEvent
const & evt
) override
;
121 virtual bool handleMouseReleased( awt::MouseEvent
const & evt
) override
;
122 virtual bool handleMouseDragged( awt::MouseEvent
const & evt
) override
;
123 virtual bool handleMouseMoved( awt::MouseEvent
const & evt
) override
;
126 bool isInArea( css::awt::MouseEvent
const & evt
) const;
127 void updatePressedState( const bool pressedState
) const;
129 RehearseTimingsActivity
& mrActivity
;
130 bool mbHasBeenClicked
;
131 bool mbMouseStartedInArea
;
134 const sal_Int32 LEFT_BORDER_SPACE
= 10;
135 const sal_Int32 LOWER_BORDER_SPACE
= 30;
137 RehearseTimingsActivity::RehearseTimingsActivity( const SlideShowContext
& rContext
) :
138 mrEventQueue(rContext
.mrEventQueue
),
139 mrScreenUpdater(rContext
.mrScreenUpdater
),
140 mrEventMultiplexer(rContext
.mrEventMultiplexer
),
141 mrActivitiesQueue(rContext
.mrActivitiesQueue
),
142 maElapsedTime( rContext
.mrEventQueue
.getTimer() ),
145 maFont( Application::GetSettings().GetStyleSettings().GetLabelFont() ),
153 maFont
.SetFontHeight( maFont
.GetFontHeight() * 2 );
154 maFont
.SetAverageFontWidth( maFont
.GetAverageFontWidth() * 2 );
155 maFont
.SetAlignment( ALIGN_BASELINE
);
156 maFont
.SetColor( COL_BLACK
);
158 // determine sprite size (in pixel):
159 ScopedVclPtrInstance
< VirtualDevice
> blackHole
;
160 blackHole
->EnableOutput(false);
161 blackHole
->SetFont( maFont
);
162 blackHole
->SetMapMode(MapMode(MapUnit::MapPixel
));
163 tools::Rectangle rect
;
164 const FontMetric
metric( blackHole
->GetFontMetric() );
165 blackHole
->GetTextBoundRect( rect
, u
"XX:XX:XX"_ustr
);
166 maSpriteSizePixel
.setX( rect
.getOpenWidth() * 12 / 10 );
167 maSpriteSizePixel
.setY( metric
.GetLineHeight() * 11 / 10 );
168 mnYOffset
= (metric
.GetAscent() + (metric
.GetLineHeight() / 20));
170 for( const auto& rView
: rContext
.mrViewContainer
)
174 RehearseTimingsActivity::~RehearseTimingsActivity()
180 catch (const uno::Exception
&)
182 TOOLS_WARN_EXCEPTION("slideshow", "");
186 std::shared_ptr
<RehearseTimingsActivity
> RehearseTimingsActivity::create(
187 const SlideShowContext
& rContext
)
189 std::shared_ptr
<RehearseTimingsActivity
> pActivity(
190 new RehearseTimingsActivity( rContext
));
192 pActivity
->mpMouseHandler
=
193 std::make_shared
<MouseHandler
>(*pActivity
);
194 pActivity
->mpWakeUpEvent
=
195 std::make_shared
<WakeupEvent
>( rContext
.mrEventQueue
.getTimer(),
197 rContext
.mrActivitiesQueue
);
199 rContext
.mrEventMultiplexer
.addViewHandler( pActivity
);
204 void RehearseTimingsActivity::start()
206 maElapsedTime
.reset();
207 mbDrawPressed
= false;
210 // paint and show all sprites:
212 for_each_sprite( []( const ::cppcanvas::CustomSpriteSharedPtr
& pSprite
)
213 { return pSprite
->show(); } );
215 mrActivitiesQueue
.addActivity( std::dynamic_pointer_cast
<Activity
>(shared_from_this()) );
217 mpMouseHandler
->reset();
218 mrEventMultiplexer
.addClickHandler(
219 mpMouseHandler
, 42 /* highest prio of all, > 3.0 */ );
220 mrEventMultiplexer
.addMouseMoveHandler(
221 mpMouseHandler
, 42 /* highest prio of all, > 3.0 */ );
224 double RehearseTimingsActivity::stop()
226 mrEventMultiplexer
.removeMouseMoveHandler( mpMouseHandler
);
227 mrEventMultiplexer
.removeClickHandler( mpMouseHandler
);
229 mbActive
= false; // will be removed from queue
231 for_each_sprite( []( const ::cppcanvas::CustomSpriteSharedPtr
& pSprite
)
232 { return pSprite
->hide(); } );
234 return maElapsedTime
.getElapsedTime();
237 bool RehearseTimingsActivity::hasBeenClicked() const
240 return mpMouseHandler
->hasBeenClicked();
245 void RehearseTimingsActivity::dispose()
249 mpWakeUpEvent
.reset();
250 mpMouseHandler
.reset();
252 ViewsVecT().swap( maViews
);
256 double RehearseTimingsActivity::calcTimeLag() const
261 bool RehearseTimingsActivity::perform()
269 mpWakeUpEvent
->start();
270 mpWakeUpEvent
->setNextTimeout( 0.5 );
271 mrEventQueue
.addEvent( mpWakeUpEvent
);
275 // sprites changed, need screen update
276 mrScreenUpdater
.notifyUpdate();
278 return false; // don't reinsert, WakeupEvent will perform
279 // that after the given timeout
282 bool RehearseTimingsActivity::isActive() const
287 void RehearseTimingsActivity::dequeued()
292 void RehearseTimingsActivity::end()
301 basegfx::B2DRange
RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr
const& rView
) const
303 const Reference
<rendering::XBitmap
> xBitmap( rView
->getCanvas()->getUNOCanvas(),
306 return basegfx::B2DRange();
308 const geometry::IntegerSize2D
realSize( xBitmap
->getSize() );
310 basegfx::B2DPoint
spritePos(
311 std::min
<sal_Int32
>( realSize
.Width
, LEFT_BORDER_SPACE
),
312 std::max
<sal_Int32
>( 0, realSize
.Height
- maSpriteSizePixel
.getY()
313 - LOWER_BORDER_SPACE
) );
314 basegfx::B2DHomMatrix
transformation( rView
->getTransformation() );
315 transformation
.invert();
316 spritePos
*= transformation
;
317 basegfx::B2DSize
spriteSize( maSpriteSizePixel
.getX(),
318 maSpriteSizePixel
.getY() );
319 spriteSize
*= transformation
;
320 return basegfx::B2DRange(
321 spritePos
.getX(), spritePos
.getY(),
322 spritePos
.getX() + spriteSize
.getWidth(),
323 spritePos
.getY() + spriteSize
.getHeight() );
326 void RehearseTimingsActivity::viewAdded( const UnoViewSharedPtr
& rView
)
328 cppcanvas::CustomSpriteSharedPtr
sprite(
329 rView
->createSprite( basegfx::B2DSize(
330 maSpriteSizePixel
.getX()+2,
331 maSpriteSizePixel
.getY()+2 ),
332 1001.0 )); // sprite should be in front of all
334 sprite
->setAlpha( 0.8 );
335 const basegfx::B2DRange
spriteRectangle(
336 calcSpriteRectangle( rView
) );
337 sprite
->move( basegfx::B2DPoint(
338 spriteRectangle
.getMinX(),
339 spriteRectangle
.getMinY() ) );
341 if( maViews
.empty() )
342 maSpriteRectangle
= spriteRectangle
;
344 maViews
.emplace_back( rView
, sprite
);
350 void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr
& rView
)
355 ( const ViewsVecT::value_type
& cp
)
356 { return rView
== cp
.first
; } );
359 void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr
& rView
)
361 // find entry corresponding to modified view
362 ViewsVecT::iterator
aModifiedEntry(
367 ( const ViewsVecT::value_type
& cp
)
368 { return rView
== cp
.first
; } )
371 OSL_ASSERT( aModifiedEntry
!= maViews
.end() );
372 if( aModifiedEntry
== maViews
.end() )
375 // new sprite pos, transformation might have changed:
376 maSpriteRectangle
= calcSpriteRectangle( rView
);
378 // reposition sprite:
379 aModifiedEntry
->second
->move( maSpriteRectangle
.getMinimum() );
381 // sprites changed, need screen update
382 mrScreenUpdater
.notifyUpdate( rView
, false );
385 void RehearseTimingsActivity::viewsChanged()
387 if( maViews
.empty() )
390 // new sprite pos, transformation might have changed:
391 maSpriteRectangle
= calcSpriteRectangle( maViews
.front().first
);
393 ::basegfx::B2DPoint nMin
= maSpriteRectangle
.getMinimum();
394 // reposition sprites
395 for_each_sprite( [nMin
]( const ::cppcanvas::CustomSpriteSharedPtr
& pSprite
)
396 { return pSprite
->move( nMin
); } );
398 // sprites changed, need screen update
399 mrScreenUpdater
.notifyUpdate();
402 void RehearseTimingsActivity::paintAllSprites() const
405 [this]( const ::cppcanvas::CustomSpriteSharedPtr
& pSprite
)
406 { return this->paint( pSprite
->getContentCanvas() ); } );
409 void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr
const & canvas
) const
411 // build timer string:
412 const sal_Int32 nTimeSecs
=
413 static_cast<sal_Int32
>(maElapsedTime
.getElapsedTime());
415 sal_Int32 n
= nTimeSecs
/ 3600;
418 buf
.append( OUString::number(n
) + ":" );
419 n
= ((nTimeSecs
% 3600) / 60);
422 buf
.append( OUString::number(n
) + ":" );
423 n
= (nTimeSecs
% 60);
427 const OUString time
= buf
.makeStringAndClear();
429 // create the MetaFile:
430 GDIMetaFile metaFile
;
431 ScopedVclPtrInstance
< VirtualDevice
> blackHole
;
432 metaFile
.Record( blackHole
);
433 metaFile
.SetPrefSize( Size( 1, 1 ) );
434 blackHole
->EnableOutput(false);
435 blackHole
->SetMapMode(MapMode(MapUnit::MapPixel
));
436 blackHole
->SetFont( maFont
);
437 tools::Rectangle
rect( 0,0,
438 maSpriteSizePixel
.getX(),
439 maSpriteSizePixel
.getY());
442 blackHole
->SetTextColor( COL_BLACK
);
443 blackHole
->SetFillColor( COL_LIGHTGRAY
);
444 blackHole
->SetLineColor( COL_GRAY
);
448 blackHole
->SetTextColor( COL_BLACK
);
449 blackHole
->SetFillColor( COL_WHITE
);
450 blackHole
->SetLineColor( COL_GRAY
);
452 blackHole
->DrawRect( rect
);
453 blackHole
->GetTextBoundRect( rect
, time
);
455 Point( (maSpriteSizePixel
.getX() - rect
.getOpenWidth()) / 2,
459 metaFile
.WindStart();
461 cppcanvas::RendererSharedPtr
renderer(
462 cppcanvas::VCLFactory::createRenderer(
463 canvas
, metaFile
, cppcanvas::Renderer::Parameters() ) );
464 const bool succ
= renderer
->draw();
469 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity
& rta
) :
471 mbHasBeenClicked(false),
472 mbMouseStartedInArea(false)
475 void RehearseTimingsActivity::MouseHandler::reset()
477 mbHasBeenClicked
= false;
478 mbMouseStartedInArea
= false;
481 bool RehearseTimingsActivity::MouseHandler::isInArea(
482 awt::MouseEvent
const & evt
) const
484 return mrActivity
.maSpriteRectangle
.isInside(
485 basegfx::B2DPoint( evt
.X
, evt
.Y
) );
488 void RehearseTimingsActivity::MouseHandler::updatePressedState(
489 const bool pressedState
) const
491 if( pressedState
!= mrActivity
.mbDrawPressed
)
493 mrActivity
.mbDrawPressed
= pressedState
;
494 mrActivity
.paintAllSprites();
496 mrActivity
.mrScreenUpdater
.notifyUpdate();
501 bool RehearseTimingsActivity::MouseHandler::handleMousePressed(
502 awt::MouseEvent
const & evt
)
504 if( evt
.Buttons
== awt::MouseButton::LEFT
&& isInArea(evt
) )
506 mbMouseStartedInArea
= true;
507 updatePressedState(true);
508 return true; // consume event
513 bool RehearseTimingsActivity::MouseHandler::handleMouseReleased(
514 awt::MouseEvent
const & evt
)
516 if( evt
.Buttons
== awt::MouseButton::LEFT
&& mbMouseStartedInArea
)
518 mbHasBeenClicked
= isInArea(evt
); // fini if in
519 mbMouseStartedInArea
= false;
520 updatePressedState(false);
521 if( !mbHasBeenClicked
)
522 return true; // consume event, else next slide (manual advance)
527 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
528 awt::MouseEvent
const & evt
)
530 if( mbMouseStartedInArea
)
531 updatePressedState( isInArea(evt
) );
535 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
536 awt::MouseEvent
const & /*evt*/ )
541 } // namespace presentation
543 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */