xref: /trunk/main/slideshow/source/engine/slide/userpaintoverlay.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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