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