xref: /AOO41X/main/slideshow/source/engine/slide/userpaintoverlay.cxx (revision 70f497fb4451dd853e622598505702a3cb5381a8)
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