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