Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 :
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 <cppcanvas/vclfactory.hxx>
28 : #include <cppcanvas/basegfxfactory.hxx>
29 : #include <basegfx/range/b2drange.hxx>
30 :
31 : #include <comphelper/anytostring.hxx>
32 : #include <cppuhelper/exc_hlp.hxx>
33 :
34 : #include <com/sun/star/awt/MouseButton.hpp>
35 : #include <com/sun/star/awt/MouseEvent.hpp>
36 : #include <com/sun/star/rendering/XBitmap.hpp>
37 :
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"
45 :
46 : #include <boost/bind.hpp>
47 : #include <o3tl/compat_functional.hxx>
48 : #include <algorithm>
49 :
50 : using namespace com::sun::star;
51 : using namespace com::sun::star::uno;
52 :
53 : namespace slideshow {
54 : namespace internal {
55 :
56 0 : class RehearseTimingsActivity::WakeupEvent : public Event,
57 : private ::boost::noncopyable
58 : {
59 : public:
60 0 : WakeupEvent( boost::shared_ptr< ::canvas::tools::ElapsedTime > const& pTimeBase,
61 : ActivitySharedPtr const& rActivity,
62 : ActivitiesQueue & rActivityQueue ) :
63 : Event("WakeupEvent"),
64 : maTimer(pTimeBase),
65 : mnNextTime(0.0),
66 : mpActivity(rActivity),
67 0 : mrActivityQueue( rActivityQueue )
68 0 : {}
69 :
70 0 : virtual void dispose() {}
71 0 : virtual bool fire()
72 : {
73 0 : ActivitySharedPtr pActivity( mpActivity.lock() );
74 0 : if( !pActivity )
75 0 : return false;
76 :
77 0 : return mrActivityQueue.addActivity( pActivity );
78 : }
79 :
80 0 : virtual bool isCharged() const { return true; }
81 0 : virtual double getActivationTime( double nCurrentTime ) const
82 : {
83 0 : const double nElapsedTime( maTimer.getElapsedTime() );
84 :
85 : return ::std::max( nCurrentTime,
86 0 : nCurrentTime - nElapsedTime + mnNextTime );
87 : }
88 :
89 : /// Start the internal timer
90 0 : void start() { maTimer.reset(); }
91 :
92 : /** Set the next timeout this object should generate.
93 :
94 : @param nextTime
95 : Absolute time, measured from the last start() call,
96 : when this event should wakeup the Activity again. If
97 : your time is relative, simply call start() just before
98 : every setNextTimeout() call.
99 : */
100 0 : void setNextTimeout( double nextTime ) { mnNextTime = nextTime; }
101 :
102 : private:
103 : ::canvas::tools::ElapsedTime maTimer;
104 : double mnNextTime;
105 : boost::weak_ptr<Activity> mpActivity;
106 : ActivitiesQueue& mrActivityQueue;
107 : };
108 :
109 0 : class RehearseTimingsActivity::MouseHandler : public MouseEventHandler,
110 : private boost::noncopyable
111 : {
112 : public:
113 : explicit MouseHandler( RehearseTimingsActivity& rta );
114 :
115 : void reset();
116 0 : bool hasBeenClicked() const { return mbHasBeenClicked; }
117 :
118 : // MouseEventHandler
119 : virtual bool handleMousePressed( awt::MouseEvent const & evt );
120 : virtual bool handleMouseReleased( awt::MouseEvent const & evt );
121 : virtual bool handleMouseEntered( awt::MouseEvent const & evt );
122 : virtual bool handleMouseExited( awt::MouseEvent const & evt );
123 : virtual bool handleMouseDragged( awt::MouseEvent const & evt );
124 : virtual bool handleMouseMoved( awt::MouseEvent const & evt );
125 :
126 : private:
127 : bool isInArea( com::sun::star::awt::MouseEvent const & evt ) const;
128 : void updatePressedState( const bool pressedState ) const;
129 :
130 : RehearseTimingsActivity& mrActivity;
131 : bool mbHasBeenClicked;
132 : bool mbMouseStartedInArea;
133 : };
134 :
135 : const sal_Int32 LEFT_BORDER_SPACE = 10;
136 : const sal_Int32 LOWER_BORDER_SPACE = 30;
137 :
138 0 : RehearseTimingsActivity::RehearseTimingsActivity( const SlideShowContext& rContext ) :
139 : mrEventQueue(rContext.mrEventQueue),
140 : mrScreenUpdater(rContext.mrScreenUpdater),
141 : mrEventMultiplexer(rContext.mrEventMultiplexer),
142 : mrActivitiesQueue(rContext.mrActivitiesQueue),
143 0 : maElapsedTime( rContext.mrEventQueue.getTimer() ),
144 : maViews(),
145 : maSpriteRectangle(),
146 0 : maFont( Application::GetSettings().GetStyleSettings().GetInfoFont() ),
147 : mpWakeUpEvent(),
148 : mpMouseHandler(),
149 : maSpriteSizePixel(),
150 : mnYOffset(0),
151 : mbActive(false),
152 0 : mbDrawPressed(false)
153 : {
154 0 : maFont.SetHeight( maFont.GetHeight() * 2 );
155 0 : maFont.SetWidth( maFont.GetWidth() * 2 );
156 0 : maFont.SetAlign( ALIGN_BASELINE );
157 0 : maFont.SetColor( COL_BLACK );
158 :
159 : // determine sprite size (in pixel):
160 0 : VirtualDevice blackHole;
161 0 : blackHole.EnableOutput(false);
162 0 : blackHole.SetFont( maFont );
163 0 : blackHole.SetMapMode( MAP_PIXEL );
164 0 : Rectangle rect;
165 0 : const FontMetric metric( blackHole.GetFontMetric() );
166 : blackHole.GetTextBoundRect(
167 0 : rect, String("XX:XX:XX") );
168 0 : maSpriteSizePixel.setX( rect.getWidth() * 12 / 10 );
169 0 : maSpriteSizePixel.setY( metric.GetLineHeight() * 11 / 10 );
170 0 : mnYOffset = (metric.GetAscent() + (metric.GetLineHeight() / 20));
171 :
172 : std::for_each( rContext.mrViewContainer.begin(),
173 : rContext.mrViewContainer.end(),
174 : boost::bind( &RehearseTimingsActivity::viewAdded,
175 : this,
176 0 : _1 ));
177 0 : }
178 :
179 0 : RehearseTimingsActivity::~RehearseTimingsActivity()
180 : {
181 : try
182 : {
183 0 : stop();
184 : }
185 0 : catch (uno::Exception &)
186 : {
187 : OSL_FAIL( rtl::OUStringToOString(
188 : comphelper::anyToString(
189 : cppu::getCaughtException() ),
190 : RTL_TEXTENCODING_UTF8 ).getStr() );
191 : }
192 0 : }
193 :
194 0 : boost::shared_ptr<RehearseTimingsActivity> RehearseTimingsActivity::create(
195 : const SlideShowContext& rContext )
196 : {
197 : boost::shared_ptr<RehearseTimingsActivity> pActivity(
198 0 : new RehearseTimingsActivity( rContext ));
199 :
200 0 : pActivity->mpMouseHandler.reset(
201 0 : new MouseHandler(*pActivity.get()) );
202 0 : pActivity->mpWakeUpEvent.reset(
203 0 : new WakeupEvent( rContext.mrEventQueue.getTimer(),
204 : pActivity,
205 0 : rContext.mrActivitiesQueue ));
206 :
207 0 : rContext.mrEventMultiplexer.addViewHandler( pActivity );
208 :
209 0 : return pActivity;
210 : }
211 :
212 0 : void RehearseTimingsActivity::start()
213 : {
214 0 : maElapsedTime.reset();
215 0 : mbDrawPressed = false;
216 0 : mbActive = true;
217 :
218 : // paint and show all sprites:
219 0 : paintAllSprites();
220 0 : for_each_sprite( boost::bind( &cppcanvas::Sprite::show, _1 ) );
221 :
222 0 : mrActivitiesQueue.addActivity( shared_from_this() );
223 :
224 0 : mpMouseHandler->reset();
225 : mrEventMultiplexer.addClickHandler(
226 0 : mpMouseHandler, 42 /* highest prio of all, > 3.0 */ );
227 : mrEventMultiplexer.addMouseMoveHandler(
228 0 : mpMouseHandler, 42 /* highest prio of all, > 3.0 */ );
229 0 : }
230 :
231 0 : double RehearseTimingsActivity::stop()
232 : {
233 0 : mrEventMultiplexer.removeMouseMoveHandler( mpMouseHandler );
234 0 : mrEventMultiplexer.removeClickHandler( mpMouseHandler );
235 :
236 0 : mbActive = false; // will be removed from queue
237 :
238 0 : for_each_sprite( boost::bind( &cppcanvas::Sprite::hide, _1 ) );
239 :
240 0 : return maElapsedTime.getElapsedTime();
241 : }
242 :
243 0 : bool RehearseTimingsActivity::hasBeenClicked() const
244 : {
245 0 : if (mpMouseHandler)
246 0 : return mpMouseHandler->hasBeenClicked();
247 0 : return false;
248 : }
249 :
250 : // Disposable:
251 0 : void RehearseTimingsActivity::dispose()
252 : {
253 0 : stop();
254 :
255 0 : mpWakeUpEvent.reset();
256 0 : mpMouseHandler.reset();
257 :
258 0 : ViewsVecT().swap( maViews );
259 0 : }
260 :
261 : // Activity:
262 0 : double RehearseTimingsActivity::calcTimeLag() const
263 : {
264 0 : return 0.0;
265 : }
266 :
267 0 : bool RehearseTimingsActivity::perform()
268 : {
269 0 : if( !isActive() )
270 0 : return false;
271 :
272 0 : if( !mpWakeUpEvent )
273 0 : return false;
274 :
275 0 : mpWakeUpEvent->start();
276 0 : mpWakeUpEvent->setNextTimeout( 0.5 );
277 0 : mrEventQueue.addEvent( mpWakeUpEvent );
278 :
279 0 : paintAllSprites();
280 :
281 : // sprites changed, need screen update
282 0 : mrScreenUpdater.notifyUpdate();
283 :
284 0 : return false; // don't reinsert, WakeupEvent will perform
285 : // that after the given timeout
286 : }
287 :
288 0 : bool RehearseTimingsActivity::isActive() const
289 : {
290 0 : return mbActive;
291 : }
292 :
293 0 : void RehearseTimingsActivity::dequeued()
294 : {
295 : // not used here
296 0 : }
297 :
298 0 : void RehearseTimingsActivity::end()
299 : {
300 0 : if (isActive())
301 : {
302 0 : stop();
303 0 : mbActive = false;
304 : }
305 0 : }
306 :
307 0 : basegfx::B2DRange RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr const& rView ) const
308 : {
309 0 : const Reference<rendering::XBitmap> xBitmap( rView->getCanvas()->getUNOCanvas(),
310 0 : UNO_QUERY );
311 0 : if( !xBitmap.is() )
312 0 : return basegfx::B2DRange();
313 :
314 0 : const geometry::IntegerSize2D realSize( xBitmap->getSize() );
315 : // pixel:
316 : basegfx::B2DPoint spritePos(
317 0 : std::min<sal_Int32>( realSize.Width, LEFT_BORDER_SPACE ),
318 0 : std::max<sal_Int32>( 0, realSize.Height - maSpriteSizePixel.getY()
319 0 : - LOWER_BORDER_SPACE ) );
320 0 : basegfx::B2DHomMatrix transformation( rView->getTransformation() );
321 0 : transformation.invert();
322 0 : spritePos *= transformation;
323 0 : basegfx::B2DSize spriteSize( maSpriteSizePixel.getX(),
324 0 : maSpriteSizePixel.getY() );
325 0 : spriteSize *= transformation;
326 : return basegfx::B2DRange(
327 : spritePos.getX(), spritePos.getY(),
328 0 : spritePos.getX() + spriteSize.getX(),
329 0 : spritePos.getY() + spriteSize.getY() );
330 : }
331 :
332 0 : void RehearseTimingsActivity::viewAdded( const UnoViewSharedPtr& rView )
333 : {
334 : cppcanvas::CustomSpriteSharedPtr sprite(
335 0 : rView->createSprite( basegfx::B2DSize(
336 0 : maSpriteSizePixel.getX()+2,
337 0 : maSpriteSizePixel.getY()+2 ),
338 0 : 1001.0 )); // sprite should be in front of all
339 : // other sprites
340 0 : sprite->setAlpha( 0.8 );
341 : const basegfx::B2DRange spriteRectangle(
342 0 : calcSpriteRectangle( rView ) );
343 0 : sprite->move( basegfx::B2DPoint(
344 : spriteRectangle.getMinX(),
345 0 : spriteRectangle.getMinY() ) );
346 :
347 0 : if( maViews.empty() )
348 0 : maSpriteRectangle = spriteRectangle;
349 :
350 0 : maViews.push_back( ViewsVecT::value_type( rView, sprite ) );
351 :
352 0 : if (isActive())
353 0 : sprite->show();
354 0 : }
355 :
356 0 : void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr& rView )
357 : {
358 : maViews.erase(
359 : std::remove_if(
360 : maViews.begin(), maViews.end(),
361 : boost::bind(
362 : std::equal_to<UnoViewSharedPtr>(),
363 : rView,
364 : // select view:
365 : boost::bind( o3tl::select1st<ViewsVecT::value_type>(), _1 ))),
366 0 : maViews.end() );
367 0 : }
368 :
369 0 : void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr& rView )
370 : {
371 : // find entry corresponding to modified view
372 : ViewsVecT::iterator aModifiedEntry(
373 : std::find_if(
374 : maViews.begin(),
375 : maViews.end(),
376 : boost::bind(
377 : std::equal_to<UnoViewSharedPtr>(),
378 : rView,
379 : // select view:
380 0 : boost::bind( o3tl::select1st<ViewsVecT::value_type>(), _1 ))));
381 :
382 : OSL_ASSERT( aModifiedEntry != maViews.end() );
383 0 : if( aModifiedEntry == maViews.end() )
384 0 : return;
385 :
386 : // new sprite pos, transformation might have changed:
387 0 : maSpriteRectangle = calcSpriteRectangle( rView );
388 :
389 : // reposition sprite:
390 0 : aModifiedEntry->second->move( maSpriteRectangle.getMinimum() );
391 :
392 : // sprites changed, need screen update
393 0 : mrScreenUpdater.notifyUpdate( rView );
394 : }
395 :
396 0 : void RehearseTimingsActivity::viewsChanged()
397 : {
398 0 : if( !maViews.empty() )
399 : {
400 : // new sprite pos, transformation might have changed:
401 0 : maSpriteRectangle = calcSpriteRectangle( maViews.front().first );
402 :
403 : // reposition sprites
404 : for_each_sprite( boost::bind( &cppcanvas::Sprite::move,
405 : _1,
406 0 : boost::cref(maSpriteRectangle.getMinimum())) );
407 :
408 : // sprites changed, need screen update
409 0 : mrScreenUpdater.notifyUpdate();
410 : }
411 0 : }
412 :
413 0 : void RehearseTimingsActivity::paintAllSprites() const
414 : {
415 : for_each_sprite(
416 : boost::bind( &RehearseTimingsActivity::paint, this,
417 : // call getContentCanvas() on each sprite:
418 : boost::bind(
419 0 : &cppcanvas::CustomSprite::getContentCanvas, _1 ) ) );
420 0 : }
421 :
422 0 : void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr const & canvas ) const
423 : {
424 : // build timer string:
425 : const sal_Int32 nTimeSecs =
426 0 : static_cast<sal_Int32>(maElapsedTime.getElapsedTime());
427 0 : rtl::OUStringBuffer buf;
428 0 : sal_Int32 n = (nTimeSecs / 3600);
429 0 : if (n < 10)
430 0 : buf.append( static_cast<sal_Unicode>('0') );
431 0 : buf.append( n );
432 0 : buf.append( static_cast<sal_Unicode>(':') );
433 0 : n = ((nTimeSecs % 3600) / 60);
434 0 : if (n < 10)
435 0 : buf.append( static_cast<sal_Unicode>('0') );
436 0 : buf.append( n );
437 0 : buf.append( static_cast<sal_Unicode>(':') );
438 0 : n = (nTimeSecs % 60);
439 0 : if (n < 10)
440 0 : buf.append( static_cast<sal_Unicode>('0') );
441 0 : buf.append( n );
442 0 : const rtl::OUString time = buf.makeStringAndClear();
443 :
444 : // create the MetaFile:
445 0 : GDIMetaFile metaFile;
446 0 : VirtualDevice blackHole;
447 0 : metaFile.Record( &blackHole );
448 0 : metaFile.SetPrefSize( Size( 1, 1 ) );
449 0 : blackHole.EnableOutput(false);
450 0 : blackHole.SetMapMode( MAP_PIXEL );
451 0 : blackHole.SetFont( maFont );
452 : Rectangle rect = Rectangle( 0,0,
453 : maSpriteSizePixel.getX(),
454 0 : maSpriteSizePixel.getY());
455 0 : if (mbDrawPressed)
456 : {
457 0 : blackHole.SetTextColor( COL_BLACK );
458 0 : blackHole.SetFillColor( COL_LIGHTGRAY );
459 0 : blackHole.SetLineColor( COL_GRAY );
460 : }
461 : else
462 : {
463 0 : blackHole.SetTextColor( COL_BLACK );
464 0 : blackHole.SetFillColor( COL_WHITE );
465 0 : blackHole.SetLineColor( COL_GRAY );
466 : }
467 0 : blackHole.DrawRect( rect );
468 0 : blackHole.GetTextBoundRect( rect, time );
469 : blackHole.DrawText(
470 0 : Point( (maSpriteSizePixel.getX() - rect.getWidth()) / 2,
471 0 : mnYOffset ), time );
472 :
473 0 : metaFile.Stop();
474 0 : metaFile.WindStart();
475 :
476 : cppcanvas::RendererSharedPtr renderer(
477 0 : cppcanvas::VCLFactory::getInstance().createRenderer(
478 0 : canvas, metaFile, cppcanvas::Renderer::Parameters() ) );
479 0 : const bool succ = renderer->draw();
480 : OSL_ASSERT( succ );
481 0 : (void)succ;
482 0 : }
483 :
484 :
485 0 : RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity& rta ) :
486 : mrActivity(rta),
487 : mbHasBeenClicked(false),
488 0 : mbMouseStartedInArea(false)
489 0 : {}
490 :
491 0 : void RehearseTimingsActivity::MouseHandler::reset()
492 : {
493 0 : mbHasBeenClicked = false;
494 0 : mbMouseStartedInArea = false;
495 0 : }
496 :
497 0 : bool RehearseTimingsActivity::MouseHandler::isInArea(
498 : awt::MouseEvent const & evt ) const
499 : {
500 : return mrActivity.maSpriteRectangle.isInside(
501 0 : basegfx::B2DPoint( evt.X, evt.Y ) );
502 : }
503 :
504 0 : void RehearseTimingsActivity::MouseHandler::updatePressedState(
505 : const bool pressedState ) const
506 : {
507 0 : if( pressedState != mrActivity.mbDrawPressed )
508 : {
509 0 : mrActivity.mbDrawPressed = pressedState;
510 0 : mrActivity.paintAllSprites();
511 :
512 0 : mrActivity.mrScreenUpdater.notifyUpdate();
513 : }
514 0 : }
515 :
516 : // MouseEventHandler
517 0 : bool RehearseTimingsActivity::MouseHandler::handleMousePressed(
518 : awt::MouseEvent const & evt )
519 : {
520 0 : if( evt.Buttons == awt::MouseButton::LEFT && isInArea(evt) )
521 : {
522 0 : mbMouseStartedInArea = true;
523 0 : updatePressedState(true);
524 0 : return true; // consume event
525 : }
526 0 : return false;
527 : }
528 :
529 0 : bool RehearseTimingsActivity::MouseHandler::handleMouseReleased(
530 : awt::MouseEvent const & evt )
531 : {
532 0 : if( evt.Buttons == awt::MouseButton::LEFT && mbMouseStartedInArea )
533 : {
534 0 : mbHasBeenClicked = isInArea(evt); // fini if in
535 0 : mbMouseStartedInArea = false;
536 0 : updatePressedState(false);
537 0 : if( !mbHasBeenClicked )
538 0 : return true; // consume event, else next slide (manual advance)
539 : }
540 0 : return false;
541 : }
542 :
543 0 : bool RehearseTimingsActivity::MouseHandler::handleMouseEntered(
544 : awt::MouseEvent const & /*evt*/ )
545 : {
546 0 : return false;
547 : }
548 :
549 0 : bool RehearseTimingsActivity::MouseHandler::handleMouseExited(
550 : awt::MouseEvent const & /*evt*/ )
551 : {
552 0 : return false;
553 : }
554 :
555 0 : bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
556 : awt::MouseEvent const & evt )
557 : {
558 0 : if( mbMouseStartedInArea )
559 0 : updatePressedState( isInArea(evt) );
560 0 : return false;
561 : }
562 :
563 0 : bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
564 : awt::MouseEvent const & /*evt*/ )
565 : {
566 0 : return false;
567 : }
568 :
569 : } // namespace internal
570 0 : } // namespace presentation
571 :
572 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|