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