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