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 ::basegfx::B2DPoint aSpriteRectangleMinimum = maSpriteRectangle.getMinimum();
412 for_each_sprite( boost::bind( &cppcanvas::Sprite::move,
413 _1,
414 boost::cref( aSpriteRectangleMinimum ) ) );
415
416 // sprites changed, need screen update
417 mrScreenUpdater.notifyUpdate();
418 }
419 }
420
paintAllSprites() const421 void RehearseTimingsActivity::paintAllSprites() const
422 {
423 for_each_sprite(
424 boost::bind( &RehearseTimingsActivity::paint, this,
425 // call getContentCanvas() on each sprite:
426 boost::bind(
427 &cppcanvas::CustomSprite::getContentCanvas, _1 ) ) );
428 }
429
paint(cppcanvas::CanvasSharedPtr const & canvas) const430 void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr const & canvas ) const
431 {
432 // build timer string:
433 const sal_Int32 nTimeSecs =
434 static_cast<sal_Int32>(maElapsedTime.getElapsedTime());
435 rtl::OUStringBuffer buf;
436 sal_Int32 n = (nTimeSecs / 3600);
437 if (n < 10)
438 buf.append( static_cast<sal_Unicode>('0') );
439 buf.append( n );
440 buf.append( static_cast<sal_Unicode>(':') );
441 n = ((nTimeSecs % 3600) / 60);
442 if (n < 10)
443 buf.append( static_cast<sal_Unicode>('0') );
444 buf.append( n );
445 buf.append( static_cast<sal_Unicode>(':') );
446 n = (nTimeSecs % 60);
447 if (n < 10)
448 buf.append( static_cast<sal_Unicode>('0') );
449 buf.append( n );
450 const rtl::OUString time = buf.makeStringAndClear();
451
452 // create the MetaFile:
453 GDIMetaFile metaFile;
454 VirtualDevice blackHole;
455 metaFile.Record( &blackHole );
456 metaFile.SetPrefSize( Size( 1, 1 ) );
457 blackHole.EnableOutput(false);
458 blackHole.SetMapMode( MAP_PIXEL );
459 blackHole.SetFont( maFont );
460 Rectangle rect = Rectangle( 0,0,
461 maSpriteSizePixel.getX(),
462 maSpriteSizePixel.getY());
463 if (mbDrawPressed)
464 {
465 blackHole.SetTextColor( COL_BLACK );
466 blackHole.SetFillColor( COL_LIGHTGRAY );
467 blackHole.SetLineColor( COL_GRAY );
468 }
469 else
470 {
471 blackHole.SetTextColor( COL_BLACK );
472 blackHole.SetFillColor( COL_WHITE );
473 blackHole.SetLineColor( COL_GRAY );
474 }
475 blackHole.DrawRect( rect );
476 blackHole.GetTextBoundRect( rect, time );
477 blackHole.DrawText(
478 Point( (maSpriteSizePixel.getX() - rect.getWidth()) / 2,
479 mnYOffset ), time );
480
481 metaFile.Stop();
482 metaFile.WindStart();
483
484 cppcanvas::RendererSharedPtr renderer(
485 cppcanvas::VCLFactory::getInstance().createRenderer(
486 canvas, metaFile, cppcanvas::Renderer::Parameters() ) );
487 const bool succ = renderer->draw();
488 OSL_ASSERT( succ );
489 (void)succ;
490 }
491
492
MouseHandler(RehearseTimingsActivity & rta)493 RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity& rta ) :
494 mrActivity(rta),
495 mbHasBeenClicked(false),
496 mbMouseStartedInArea(false)
497 {}
498
reset()499 void RehearseTimingsActivity::MouseHandler::reset()
500 {
501 mbHasBeenClicked = false;
502 mbMouseStartedInArea = false;
503 }
504
isInArea(awt::MouseEvent const & evt) const505 bool RehearseTimingsActivity::MouseHandler::isInArea(
506 awt::MouseEvent const & evt ) const
507 {
508 return mrActivity.maSpriteRectangle.isInside(
509 basegfx::B2DPoint( evt.X, evt.Y ) );
510 }
511
updatePressedState(const bool pressedState) const512 void RehearseTimingsActivity::MouseHandler::updatePressedState(
513 const bool pressedState ) const
514 {
515 if( pressedState != mrActivity.mbDrawPressed )
516 {
517 mrActivity.mbDrawPressed = pressedState;
518 mrActivity.paintAllSprites();
519
520 mrActivity.mrScreenUpdater.notifyUpdate();
521 }
522 }
523
524 // MouseEventHandler
handleMousePressed(awt::MouseEvent const & evt)525 bool RehearseTimingsActivity::MouseHandler::handleMousePressed(
526 awt::MouseEvent const & evt )
527 {
528 if( evt.Buttons == awt::MouseButton::LEFT && isInArea(evt) )
529 {
530 mbMouseStartedInArea = true;
531 updatePressedState(true);
532 return true; // consume event
533 }
534 return false;
535 }
536
handleMouseReleased(awt::MouseEvent const & evt)537 bool RehearseTimingsActivity::MouseHandler::handleMouseReleased(
538 awt::MouseEvent const & evt )
539 {
540 if( evt.Buttons == awt::MouseButton::LEFT && mbMouseStartedInArea )
541 {
542 mbHasBeenClicked = isInArea(evt); // fini if in
543 mbMouseStartedInArea = false;
544 updatePressedState(false);
545 if( !mbHasBeenClicked )
546 return true; // consume event, else next slide (manual advance)
547 }
548 return false;
549 }
550
handleMouseEntered(awt::MouseEvent const &)551 bool RehearseTimingsActivity::MouseHandler::handleMouseEntered(
552 awt::MouseEvent const & /*evt*/ )
553 {
554 return false;
555 }
556
handleMouseExited(awt::MouseEvent const &)557 bool RehearseTimingsActivity::MouseHandler::handleMouseExited(
558 awt::MouseEvent const & /*evt*/ )
559 {
560 return false;
561 }
562
handleMouseDragged(awt::MouseEvent const & evt)563 bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
564 awt::MouseEvent const & evt )
565 {
566 if( mbMouseStartedInArea )
567 updatePressedState( isInArea(evt) );
568 return false;
569 }
570
handleMouseMoved(awt::MouseEvent const &)571 bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
572 awt::MouseEvent const & /*evt*/ )
573 {
574 return false;
575 }
576
577 } // namespace internal
578 } // namespace presentation
579