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 <canvas/debug.hxx>
28 
29 #include <comphelper/anytostring.hxx>
30 #include <cppuhelper/exc_hlp.hxx>
31 
32 #include <com/sun/star/awt/MouseButton.hpp>
33 #include <com/sun/star/presentation/XSlideShowView.hpp>
34 
35 #include <basegfx/point/b2dpoint.hxx>
36 #include <basegfx/polygon/b2dpolygon.hxx>
37 #include <cppcanvas/basegfxfactory.hxx>
38 
39 #include "activity.hxx"
40 #include "activitiesqueue.hxx"
41 #include "slideshowcontext.hxx"
42 #include "userpaintoverlay.hxx"
43 #include "mouseeventhandler.hxx"
44 #include "eventmultiplexer.hxx"
45 #include "screenupdater.hxx"
46 #include "vieweventhandler.hxx"
47 
48 #include <boost/bind.hpp>
49 #include <boost/noncopyable.hpp>
50 #include "slide.hxx"
51 #include "cursormanager.hxx"
52 
53 using namespace ::com::sun::star;
54 
55 namespace slideshow
56 {
57     namespace internal
58     {
59         class PaintOverlayHandler : public MouseEventHandler,
60                                     public ViewEventHandler,
61 				    public UserPaintEventHandler
62         {
63         public:
PaintOverlayHandler(const RGBColor & rStrokeColor,double nStrokeWidth,ActivitiesQueue & rActivitiesQueue,ScreenUpdater & rScreenUpdater,const UnoViewContainer & rViews,Slide & rSlide,const PolyPolygonVector & rPolygons,bool bActive)64             PaintOverlayHandler( const RGBColor&          rStrokeColor,
65                                  double                   nStrokeWidth,
66                                  ActivitiesQueue&         rActivitiesQueue,
67                                  ScreenUpdater&           rScreenUpdater,
68                                  const UnoViewContainer&  rViews,
69 								 Slide&		              rSlide,
70                                  const PolyPolygonVector& rPolygons,
71 								 bool					  bActive ) :
72                 mrActivitiesQueue( rActivitiesQueue ),
73                 mrScreenUpdater( rScreenUpdater ),
74                 maViews(),
75                 maPolygons( rPolygons ),
76                 maStrokeColor( rStrokeColor ),
77                 mnStrokeWidth( nStrokeWidth ),
78                 maLastPoint(),
79                 maLastMouseDownPos(),
80                 mbIsLastPointValid( false ),
81                 mbIsLastMouseDownPosValid( false ),
82 				//handle the "remove all ink from slide" mode of erasing
83 				mbIsEraseAllModeActivated( false ),
84 				//handle the "remove stroke by stroke" mode of erasing
85 				mbIsEraseModeActivated( false ),
86 				mrSlide(rSlide),
87                 mnSize(100),
88 				mbActive( bActive )
89             {
90                 std::for_each( rViews.begin(),
91                                rViews.end(),
92                                boost::bind( &PaintOverlayHandler::viewAdded,
93                                             this,
94                                             _1 ));
95                 drawPolygons();
96             }
97 
dispose()98             virtual void dispose()
99             {
100                 maViews.clear();
101             }
102 
103             // ViewEventHandler methods
viewAdded(const UnoViewSharedPtr & rView)104             virtual void viewAdded( const UnoViewSharedPtr& rView )
105             {
106                 maViews.push_back( rView );
107             }
108 
viewRemoved(const UnoViewSharedPtr & rView)109             virtual void viewRemoved( const UnoViewSharedPtr& rView )
110             {
111                 maViews.erase( ::std::remove( maViews.begin(),
112                                               maViews.end(),
113                                               rView ) );
114             }
115 
viewChanged(const UnoViewSharedPtr &)116             virtual void viewChanged( const UnoViewSharedPtr& /*rView*/ )
117             {
118                 // TODO(F2): for persistent drawings, need to store
119                 // polygon and repaint here.
120             }
121 
viewsChanged()122             virtual void viewsChanged()
123             {
124                 // TODO(F2): for persistent drawings, need to store
125                 // polygon and repaint here.
126             }
127 
colorChanged(RGBColor const & rUserColor)128 			bool colorChanged( RGBColor const& rUserColor )
129             {
130 				mbIsLastPointValid = false;
131 				mbActive = true;
132 				this->maStrokeColor = rUserColor;
133 				this->mbIsEraseModeActivated = false;
134                 return true;
135 			}
136 
widthChanged(double nUserStrokeWidth)137             bool widthChanged( double nUserStrokeWidth )
138             {
139                 this->mnStrokeWidth = nUserStrokeWidth;
140                 mbIsEraseModeActivated = false;
141                 return true;
142             }
143 
repaintWithoutPolygons()144 			void repaintWithoutPolygons()
145 			{
146                     // must get access to the instance to erase all polygon
147 					for( UnoViewVector::iterator aIter=maViews.begin(), aEnd=maViews.end();
148 						aIter!=aEnd;
149 						++aIter )
150                     {
151 						// fully clear view content to background color
152 						//(*aIter)->getCanvas()->clear();
153 
154 						//get via SlideImpl instance the bitmap of the slide unmodified to redraw it
155 						SlideBitmapSharedPtr 		 pBitmap( mrSlide.getCurrentSlideBitmap( (*aIter) ) );
156 						::cppcanvas::CanvasSharedPtr pCanvas( (*aIter)->getCanvas() );
157 
158 						const ::basegfx::B2DHomMatrix 	aViewTransform( (*aIter)->getTransformation() );
159 						const ::basegfx::B2DPoint 		aOutPosPixel( aViewTransform * ::basegfx::B2DPoint() );
160 
161 						// setup a canvas with device coordinate space, the slide
162 						// bitmap already has the correct dimension.
163 						::cppcanvas::CanvasSharedPtr pDevicePixelCanvas( pCanvas->clone() );
164 
165 						pDevicePixelCanvas->setTransformation( ::basegfx::B2DHomMatrix() );
166 
167 						// render at given output position
168 						pBitmap->move( aOutPosPixel );
169 
170 						// clear clip (might have been changed, e.g. from comb
171 						// transition)
172 						pBitmap->clip( ::basegfx::B2DPolyPolygon() );
173 						pBitmap->draw( pDevicePixelCanvas );
174 
175 						mrScreenUpdater.notifyUpdate(*aIter,true);
176 					}
177 			}
178 
eraseAllInkChanged(bool const & rEraseAllInk)179 			bool eraseAllInkChanged( bool const& rEraseAllInk )
180             {
181 				this->mbIsEraseAllModeActivated= rEraseAllInk;
182 				// if the erase all mode is activated it will remove all ink from slide,
183 				// therefor destroy all the polygons stored
184 				if(mbIsEraseAllModeActivated)
185                 {
186 					// The Erase Mode should be desactivated
187                     mbIsEraseModeActivated = false;
188 					repaintWithoutPolygons();
189 					maPolygons.clear();
190                 }
191             mbIsEraseAllModeActivated=false;
192             return true;
193             }
194 
eraseInkWidthChanged(sal_Int32 rEraseInkSize)195             bool eraseInkWidthChanged( sal_Int32 rEraseInkSize )
196             {
197                 // Change the size
198                 this->mnSize=rEraseInkSize;
199                 // Changed to mode Erase
200                 this->mbIsEraseModeActivated = true;
201                 return true;
202             }
203 
switchPenMode()204             bool switchPenMode()
205             {
206 				mbIsLastPointValid = false;
207 				mbActive = true;
208                 this->mbIsEraseModeActivated = false;
209                 return true;
210             }
211 
switchEraserMode()212             bool switchEraserMode()
213             {
214 				mbIsLastPointValid = false;
215 				mbActive = true;
216                 this->mbIsEraseModeActivated = true;
217                 return true;
218             }
219 
disable()220             bool disable()
221             {
222 				mbIsLastPointValid = false;
223                 mbIsLastMouseDownPosValid = false;
224 				mbActive = false;
225                 return true;
226             }
227 
228             //Draw all registered polygons.
drawPolygons()229             void drawPolygons()
230             {
231                 for( PolyPolygonVector::iterator aIter=maPolygons.begin(), aEnd=maPolygons.end();
232                                      aIter!=aEnd;
233                                      ++aIter )
234                 {
235                     (*aIter)->draw();
236                 }
237                 // screen update necessary to show painting
238                 mrScreenUpdater.notifyUpdate();
239             }
240 
241             //Retrieve all registered polygons.
getPolygons()242             PolyPolygonVector getPolygons()
243             {
244                 return maPolygons;
245             }
246 
247             // MouseEventHandler methods
handleMousePressed(const awt::MouseEvent & e)248             virtual bool handleMousePressed( const awt::MouseEvent& e )
249             {
250 				if( !mbActive )
251 					return false;
252 
253                 if (e.Buttons == awt::MouseButton::RIGHT)
254                 {
255                     mbIsLastPointValid = false;
256                     return false;
257                 }
258 
259                 if (e.Buttons != awt::MouseButton::LEFT)
260 					return false;
261 
262                 maLastMouseDownPos.setX( e.X );
263                 maLastMouseDownPos.setY( e.Y );
264                 mbIsLastMouseDownPosValid = true;
265 
266                 // eat mouse click (though we don't process it
267                 // _directly_, it enables the drag mode
268                 return true;
269             }
270 
handleMouseReleased(const awt::MouseEvent & e)271             virtual bool handleMouseReleased( const awt::MouseEvent& e )
272             {
273 				if( !mbActive )
274 					return false;
275 
276                 if (e.Buttons == awt::MouseButton::RIGHT)
277                 {
278                     mbIsLastPointValid = false;
279                     return false;
280                 }
281 
282                 if (e.Buttons != awt::MouseButton::LEFT)
283                     return false;
284 
285                 // check, whether up- and down press are on exactly
286                 // the same pixel. If that's the case, ignore the
287                 // click, and pass on the event to low-prio
288                 // handlers. This effectively permits effect
289                 // advancements via clicks also when user paint is
290                 // enabled.
291                 if( mbIsLastMouseDownPosValid &&
292                     ::basegfx::B2DPoint( e.X,
293                                          e.Y ) == maLastMouseDownPos )
294                 {
295                     mbIsLastMouseDownPosValid = false;
296                     return false;
297                 }
298 
299                 // invalidate, next downpress will have to start a new
300                 // polygon.
301                 mbIsLastPointValid = false;
302 
303                 // eat mouse click (though we don't process it
304                 // _directly_, it enables the drag mode
305                 return true;
306             }
307 
handleMouseEntered(const awt::MouseEvent & e)308             virtual bool handleMouseEntered( const awt::MouseEvent& e )
309             {
310 				if( !mbActive )
311 					return false;
312 
313                 mbIsLastPointValid = true;
314                 maLastPoint.setX( e.X );
315                 maLastPoint.setY( e.Y );
316 
317                 return true;
318             }
319 
handleMouseExited(const awt::MouseEvent &)320             virtual bool handleMouseExited( const awt::MouseEvent& )
321             {
322 				if( !mbActive )
323 					return false;
324 
325 				mbIsLastPointValid = false;
326                 mbIsLastMouseDownPosValid = false;
327 
328                 return true;
329             }
330 
handleMouseDragged(const awt::MouseEvent & e)331             virtual bool handleMouseDragged( const awt::MouseEvent& e )
332             {
333 				if( !mbActive )
334 					return false;
335 
336                 if (e.Buttons == awt::MouseButton::RIGHT)
337                 {
338                     mbIsLastPointValid = false;
339                     return false;
340                 }
341 
342 				if(mbIsEraseModeActivated)
343                 {
344 					//define the last point as an object
345 					//we suppose that there's no way this point could be valid
346 					::basegfx::B2DPolygon aPoly;
347 
348                     maLastPoint.setX( e.X-mnSize );
349                     maLastPoint.setY( e.Y-mnSize );
350 
351                     aPoly.append( maLastPoint );
352 
353                     maLastPoint.setX( e.X-mnSize );
354                     maLastPoint.setY( e.Y+mnSize );
355 
356                     aPoly.append( maLastPoint );
357                     maLastPoint.setX( e.X+mnSize );
358                     maLastPoint.setY( e.Y+mnSize );
359 
360                     aPoly.append( maLastPoint );
361                     maLastPoint.setX( e.X+mnSize );
362                     maLastPoint.setY( e.Y-mnSize );
363 
364                     aPoly.append( maLastPoint );
365                     maLastPoint.setX( e.X-mnSize );
366                     maLastPoint.setY( e.Y-mnSize );
367 
368 					aPoly.append( maLastPoint );
369 
370 					//now we have defined a Polygon that is closed
371 
372 					//The point is to redraw the LastPoint the way it was originally on the bitmap,
373 					//of the slide
374 		    for( UnoViewVector::iterator aIter=maViews.begin(), aEnd=maViews.end();
375 						aIter!=aEnd;
376 						++aIter )
377                     {
378 
379 						//get via SlideImpl instance the bitmap of the slide unmodified to redraw it
380 						SlideBitmapSharedPtr 		 pBitmap( mrSlide.getCurrentSlideBitmap( (*aIter) ) );
381 						::cppcanvas::CanvasSharedPtr pCanvas( (*aIter)->getCanvas() );
382 
383 						::basegfx::B2DHomMatrix 	aViewTransform( (*aIter)->getTransformation() );
384 						const ::basegfx::B2DPoint 		aOutPosPixel( aViewTransform * ::basegfx::B2DPoint() );
385 
386 						// setup a canvas with device coordinate space, the slide
387 						// bitmap already has the correct dimension.
388 						::cppcanvas::CanvasSharedPtr pDevicePixelCanvas( pCanvas->clone() );
389 
390 						pDevicePixelCanvas->setTransformation( ::basegfx::B2DHomMatrix() );
391 
392 						// render at given output position
393 						pBitmap->move( aOutPosPixel );
394 
395                         ::basegfx::B2DPolyPolygon aPolyPoly=::basegfx::B2DPolyPolygon(aPoly);
396                         aViewTransform.translate(-aOutPosPixel.getX(), -aOutPosPixel.getY());
397                         aPolyPoly.transform(aViewTransform);
398 						// set clip so that we just redraw a part of the canvas
399 						pBitmap->clip(aPolyPoly);
400 						pBitmap->draw( pDevicePixelCanvas );
401 
402 						mrScreenUpdater.notifyUpdate(*aIter,true);
403 					}
404 
405 		}
406                 else
407                 {
408 					if( !mbIsLastPointValid )
409 					{
410 						mbIsLastPointValid = true;
411 						maLastPoint.setX( e.X );
412 						maLastPoint.setY( e.Y );
413 					}
414 					else
415 					{
416 						::basegfx::B2DPolygon aPoly;
417 						aPoly.append( maLastPoint );
418 
419 						maLastPoint.setX( e.X );
420 						maLastPoint.setY( e.Y );
421 
422 						aPoly.append( maLastPoint );
423 
424 						// paint to all views
425 						for( UnoViewVector::iterator aIter=maViews.begin(), aEnd=maViews.end();
426                              aIter!=aEnd;
427                              ++aIter )
428 						{
429 							::cppcanvas::PolyPolygonSharedPtr pPolyPoly(
430                                 ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( (*aIter)->getCanvas(),
431                                                                                               aPoly ) );
432 
433 							if( pPolyPoly )
434 							{
435 								pPolyPoly->setStrokeWidth(mnStrokeWidth);
436                                 pPolyPoly->setRGBALineColor( maStrokeColor.getIntegerColor() );
437 								pPolyPoly->draw();
438                                 maPolygons.push_back(pPolyPoly);
439                             }
440 						}
441 
442 						// screen update necessary to show painting
443 						mrScreenUpdater.notifyUpdate();
444 					}
445 				}
446                 // mouse events captured
447                 return true;
448             }
449 
handleMouseMoved(const awt::MouseEvent &)450             virtual bool handleMouseMoved( const awt::MouseEvent& /*e*/ )
451             {
452                 // not used here
453                 return false; // did not handle the event
454             }
455 
456 
update_settings(bool bUserPaintEnabled,RGBColor const & aUserPaintColor,double dUserPaintStrokeWidth)457 			void update_settings( bool bUserPaintEnabled, RGBColor const& aUserPaintColor, double dUserPaintStrokeWidth )
458 			{
459 				maStrokeColor = aUserPaintColor;
460 				mnStrokeWidth = dUserPaintStrokeWidth;
461 				mbActive = bUserPaintEnabled;
462 				if( !mbActive )
463 					disable();
464 			}
465 
466         private:
467             ActivitiesQueue&        mrActivitiesQueue;
468             ScreenUpdater&          mrScreenUpdater;
469             UnoViewVector           maViews;
470             PolyPolygonVector 	    maPolygons;
471 			RGBColor                maStrokeColor;
472             double                  mnStrokeWidth;
473             basegfx::B2DPoint       maLastPoint;
474             basegfx::B2DPoint       maLastMouseDownPos;
475             bool                    mbIsLastPointValid;
476             bool                    mbIsLastMouseDownPosValid;
477 			// added bool for erasing purpose :
478 			bool					mbIsEraseAllModeActivated;
479 			bool					mbIsEraseModeActivated;
480 			Slide&					mrSlide;
481             sal_Int32               mnSize;
482 			bool					mbActive;
483         };
484 
create(const RGBColor & rStrokeColor,double nStrokeWidth,const SlideShowContext & rContext,const PolyPolygonVector & rPolygons,bool bActive)485         UserPaintOverlaySharedPtr UserPaintOverlay::create( const RGBColor&          rStrokeColor,
486                                                             double                   nStrokeWidth,
487                                                             const SlideShowContext&  rContext,
488                                                             const PolyPolygonVector& rPolygons,
489 															bool					 bActive )
490         {
491             UserPaintOverlaySharedPtr pRet( new UserPaintOverlay( rStrokeColor,
492                                                                   nStrokeWidth,
493                                                                   rContext,
494                                                                   rPolygons,
495 																  bActive));
496 
497             return pRet;
498         }
499 
UserPaintOverlay(const RGBColor & rStrokeColor,double nStrokeWidth,const SlideShowContext & rContext,const PolyPolygonVector & rPolygons,bool bActive)500         UserPaintOverlay::UserPaintOverlay( const RGBColor&          rStrokeColor,
501                                             double                   nStrokeWidth,
502                                             const SlideShowContext&  rContext,
503                                             const PolyPolygonVector& rPolygons,
504 											bool					 bActive ) :
505             mpHandler( new PaintOverlayHandler( rStrokeColor,
506                                                 nStrokeWidth,
507                                                 rContext.mrActivitiesQueue,
508                                                 rContext.mrScreenUpdater,
509                                                 rContext.mrViewContainer,
510                                                 //adding a link to Slide
511                                                 dynamic_cast<Slide&>(rContext.mrCursorManager),
512                                                 rPolygons, bActive )),
513             mrMultiplexer( rContext.mrEventMultiplexer )
514         {
515             mrMultiplexer.addClickHandler( mpHandler, 3.0 );
516             mrMultiplexer.addMouseMoveHandler( mpHandler, 3.0 );
517             mrMultiplexer.addViewHandler( mpHandler );
518 			mrMultiplexer.addUserPaintHandler(mpHandler);
519         }
520 
getPolygons()521         PolyPolygonVector UserPaintOverlay::getPolygons()
522         {
523             return mpHandler->getPolygons();
524         }
525 
drawPolygons()526         void UserPaintOverlay::drawPolygons()
527         {
528             mpHandler->drawPolygons();
529         }
530 
update_settings(bool bUserPaintEnabled,RGBColor const & aUserPaintColor,double dUserPaintStrokeWidth)531 		void UserPaintOverlay::update_settings( bool bUserPaintEnabled, RGBColor const& aUserPaintColor, double dUserPaintStrokeWidth )
532 		{
533 			mpHandler->update_settings( bUserPaintEnabled, aUserPaintColor, dUserPaintStrokeWidth );
534 		}
535 
536 
~UserPaintOverlay()537         UserPaintOverlay::~UserPaintOverlay()
538         {
539             try
540             {
541                 mrMultiplexer.removeMouseMoveHandler( mpHandler );
542                 mrMultiplexer.removeClickHandler( mpHandler );
543                 mrMultiplexer.removeViewHandler( mpHandler );
544                 mpHandler->dispose();
545             }
546             catch (uno::Exception &)
547             {
548                 OSL_ENSURE( false, rtl::OUStringToOString(
549                                 comphelper::anyToString(
550                                     cppu::getCaughtException() ),
551                                 RTL_TEXTENCODING_UTF8 ).getStr() );
552             }
553         }
554     }
555 }
556