/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_slideshow.hxx" #include #include #include #include #include #include #include #include #include "activity.hxx" #include "activitiesqueue.hxx" #include "slideshowcontext.hxx" #include "userpaintoverlay.hxx" #include "mouseeventhandler.hxx" #include "eventmultiplexer.hxx" #include "screenupdater.hxx" #include "vieweventhandler.hxx" #include #include #include "slide.hxx" #include "cursormanager.hxx" using namespace ::com::sun::star; namespace slideshow { namespace internal { class PaintOverlayHandler : public MouseEventHandler, public ViewEventHandler, public UserPaintEventHandler { public: PaintOverlayHandler( const RGBColor& rStrokeColor, double nStrokeWidth, ActivitiesQueue& rActivitiesQueue, ScreenUpdater& rScreenUpdater, const UnoViewContainer& rViews, Slide& rSlide, const PolyPolygonVector& rPolygons, bool bActive ) : mrActivitiesQueue( rActivitiesQueue ), mrScreenUpdater( rScreenUpdater ), maViews(), maPolygons( rPolygons ), maStrokeColor( rStrokeColor ), mnStrokeWidth( nStrokeWidth ), maLastPoint(), maLastMouseDownPos(), mbIsLastPointValid( false ), mbIsLastMouseDownPosValid( false ), //handle the "remove all ink from slide" mode of erasing mbIsEraseAllModeActivated( false ), //handle the "remove stroke by stroke" mode of erasing mbIsEraseModeActivated( false ), mrSlide(rSlide), mnSize(100), mbActive( bActive ) { std::for_each( rViews.begin(), rViews.end(), boost::bind( &PaintOverlayHandler::viewAdded, this, _1 )); drawPolygons(); } virtual void dispose() { maViews.clear(); } // ViewEventHandler methods virtual void viewAdded( const UnoViewSharedPtr& rView ) { maViews.push_back( rView ); } virtual void viewRemoved( const UnoViewSharedPtr& rView ) { maViews.erase( ::std::remove( maViews.begin(), maViews.end(), rView ) ); } virtual void viewChanged( const UnoViewSharedPtr& /*rView*/ ) { // TODO(F2): for persistent drawings, need to store // polygon and repaint here. } virtual void viewsChanged() { // TODO(F2): for persistent drawings, need to store // polygon and repaint here. } bool colorChanged( RGBColor const& rUserColor ) { mbIsLastPointValid = false; mbActive = true; this->maStrokeColor = rUserColor; this->mbIsEraseModeActivated = false; return true; } bool widthChanged( double nUserStrokeWidth ) { this->mnStrokeWidth = nUserStrokeWidth; mbIsEraseModeActivated = false; return true; } void repaintWithoutPolygons() { // must get access to the instance to erase all polygon for( UnoViewVector::iterator aIter=maViews.begin(), aEnd=maViews.end(); aIter!=aEnd; ++aIter ) { // fully clear view content to background color //(*aIter)->getCanvas()->clear(); //get via SlideImpl instance the bitmap of the slide unmodified to redraw it SlideBitmapSharedPtr pBitmap( mrSlide.getCurrentSlideBitmap( (*aIter) ) ); ::cppcanvas::CanvasSharedPtr pCanvas( (*aIter)->getCanvas() ); const ::basegfx::B2DHomMatrix aViewTransform( (*aIter)->getTransformation() ); const ::basegfx::B2DPoint aOutPosPixel( aViewTransform * ::basegfx::B2DPoint() ); // setup a canvas with device coordinate space, the slide // bitmap already has the correct dimension. ::cppcanvas::CanvasSharedPtr pDevicePixelCanvas( pCanvas->clone() ); pDevicePixelCanvas->setTransformation( ::basegfx::B2DHomMatrix() ); // render at given output position pBitmap->move( aOutPosPixel ); // clear clip (might have been changed, e.g. from comb // transition) pBitmap->clip( ::basegfx::B2DPolyPolygon() ); pBitmap->draw( pDevicePixelCanvas ); mrScreenUpdater.notifyUpdate(*aIter,true); } } bool eraseAllInkChanged( bool const& rEraseAllInk ) { this->mbIsEraseAllModeActivated= rEraseAllInk; // if the erase all mode is activated it will remove all ink from slide, // therefor destroy all the polygons stored if(mbIsEraseAllModeActivated) { // The Erase Mode should be desactivated mbIsEraseModeActivated = false; repaintWithoutPolygons(); maPolygons.clear(); } mbIsEraseAllModeActivated=false; return true; } bool eraseInkWidthChanged( sal_Int32 rEraseInkSize ) { // Change the size this->mnSize=rEraseInkSize; // Changed to mode Erase this->mbIsEraseModeActivated = true; return true; } bool switchPenMode() { mbIsLastPointValid = false; mbActive = true; this->mbIsEraseModeActivated = false; return true; } bool switchEraserMode() { mbIsLastPointValid = false; mbActive = true; this->mbIsEraseModeActivated = true; return true; } bool disable() { mbIsLastPointValid = false; mbIsLastMouseDownPosValid = false; mbActive = false; return true; } //Draw all registered polygons. void drawPolygons() { for( PolyPolygonVector::iterator aIter=maPolygons.begin(), aEnd=maPolygons.end(); aIter!=aEnd; ++aIter ) { (*aIter)->draw(); } // screen update necessary to show painting mrScreenUpdater.notifyUpdate(); } //Retrieve all registered polygons. PolyPolygonVector getPolygons() { return maPolygons; } // MouseEventHandler methods virtual bool handleMousePressed( const awt::MouseEvent& e ) { if( !mbActive ) return false; if (e.Buttons == awt::MouseButton::RIGHT) { mbIsLastPointValid = false; return false; } if (e.Buttons != awt::MouseButton::LEFT) return false; maLastMouseDownPos.setX( e.X ); maLastMouseDownPos.setY( e.Y ); mbIsLastMouseDownPosValid = true; // eat mouse click (though we don't process it // _directly_, it enables the drag mode return true; } virtual bool handleMouseReleased( const awt::MouseEvent& e ) { if( !mbActive ) return false; if (e.Buttons == awt::MouseButton::RIGHT) { mbIsLastPointValid = false; return false; } if (e.Buttons != awt::MouseButton::LEFT) return false; // check, whether up- and down press are on exactly // the same pixel. If that's the case, ignore the // click, and pass on the event to low-prio // handlers. This effectively permits effect // advancements via clicks also when user paint is // enabled. if( mbIsLastMouseDownPosValid && ::basegfx::B2DPoint( e.X, e.Y ) == maLastMouseDownPos ) { mbIsLastMouseDownPosValid = false; return false; } // invalidate, next downpress will have to start a new // polygon. mbIsLastPointValid = false; // eat mouse click (though we don't process it // _directly_, it enables the drag mode return true; } virtual bool handleMouseEntered( const awt::MouseEvent& e ) { if( !mbActive ) return false; mbIsLastPointValid = true; maLastPoint.setX( e.X ); maLastPoint.setY( e.Y ); return true; } virtual bool handleMouseExited( const awt::MouseEvent& ) { if( !mbActive ) return false; mbIsLastPointValid = false; mbIsLastMouseDownPosValid = false; return true; } virtual bool handleMouseDragged( const awt::MouseEvent& e ) { if( !mbActive ) return false; if (e.Buttons == awt::MouseButton::RIGHT) { mbIsLastPointValid = false; return false; } if(mbIsEraseModeActivated) { //define the last point as an object //we suppose that there's no way this point could be valid ::basegfx::B2DPolygon aPoly; maLastPoint.setX( e.X-mnSize ); maLastPoint.setY( e.Y-mnSize ); aPoly.append( maLastPoint ); maLastPoint.setX( e.X-mnSize ); maLastPoint.setY( e.Y+mnSize ); aPoly.append( maLastPoint ); maLastPoint.setX( e.X+mnSize ); maLastPoint.setY( e.Y+mnSize ); aPoly.append( maLastPoint ); maLastPoint.setX( e.X+mnSize ); maLastPoint.setY( e.Y-mnSize ); aPoly.append( maLastPoint ); maLastPoint.setX( e.X-mnSize ); maLastPoint.setY( e.Y-mnSize ); aPoly.append( maLastPoint ); //now we have defined a Polygon that is closed //The point is to redraw the LastPoint the way it was originally on the bitmap, //of the slide for( UnoViewVector::iterator aIter=maViews.begin(), aEnd=maViews.end(); aIter!=aEnd; ++aIter ) { //get via SlideImpl instance the bitmap of the slide unmodified to redraw it SlideBitmapSharedPtr pBitmap( mrSlide.getCurrentSlideBitmap( (*aIter) ) ); ::cppcanvas::CanvasSharedPtr pCanvas( (*aIter)->getCanvas() ); ::basegfx::B2DHomMatrix aViewTransform( (*aIter)->getTransformation() ); const ::basegfx::B2DPoint aOutPosPixel( aViewTransform * ::basegfx::B2DPoint() ); // setup a canvas with device coordinate space, the slide // bitmap already has the correct dimension. ::cppcanvas::CanvasSharedPtr pDevicePixelCanvas( pCanvas->clone() ); pDevicePixelCanvas->setTransformation( ::basegfx::B2DHomMatrix() ); // render at given output position pBitmap->move( aOutPosPixel ); ::basegfx::B2DPolyPolygon aPolyPoly=::basegfx::B2DPolyPolygon(aPoly); aViewTransform.translate(-aOutPosPixel.getX(), -aOutPosPixel.getY()); aPolyPoly.transform(aViewTransform); // set clip so that we just redraw a part of the canvas pBitmap->clip(aPolyPoly); pBitmap->draw( pDevicePixelCanvas ); mrScreenUpdater.notifyUpdate(*aIter,true); } } else { if( !mbIsLastPointValid ) { mbIsLastPointValid = true; maLastPoint.setX( e.X ); maLastPoint.setY( e.Y ); } else { ::basegfx::B2DPolygon aPoly; aPoly.append( maLastPoint ); maLastPoint.setX( e.X ); maLastPoint.setY( e.Y ); aPoly.append( maLastPoint ); // paint to all views for( UnoViewVector::iterator aIter=maViews.begin(), aEnd=maViews.end(); aIter!=aEnd; ++aIter ) { ::cppcanvas::PolyPolygonSharedPtr pPolyPoly( ::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( (*aIter)->getCanvas(), aPoly ) ); if( pPolyPoly ) { pPolyPoly->setStrokeWidth(mnStrokeWidth); pPolyPoly->setRGBALineColor( maStrokeColor.getIntegerColor() ); pPolyPoly->draw(); maPolygons.push_back(pPolyPoly); } } // screen update necessary to show painting mrScreenUpdater.notifyUpdate(); } } // mouse events captured return true; } virtual bool handleMouseMoved( const awt::MouseEvent& /*e*/ ) { // not used here return false; // did not handle the event } void update_settings( bool bUserPaintEnabled, RGBColor const& aUserPaintColor, double dUserPaintStrokeWidth ) { maStrokeColor = aUserPaintColor; mnStrokeWidth = dUserPaintStrokeWidth; mbActive = bUserPaintEnabled; if( !mbActive ) disable(); } private: ActivitiesQueue& mrActivitiesQueue; ScreenUpdater& mrScreenUpdater; UnoViewVector maViews; PolyPolygonVector maPolygons; RGBColor maStrokeColor; double mnStrokeWidth; basegfx::B2DPoint maLastPoint; basegfx::B2DPoint maLastMouseDownPos; bool mbIsLastPointValid; bool mbIsLastMouseDownPosValid; // added bool for erasing purpose : bool mbIsEraseAllModeActivated; bool mbIsEraseModeActivated; Slide& mrSlide; sal_Int32 mnSize; bool mbActive; }; UserPaintOverlaySharedPtr UserPaintOverlay::create( const RGBColor& rStrokeColor, double nStrokeWidth, const SlideShowContext& rContext, const PolyPolygonVector& rPolygons, bool bActive ) { UserPaintOverlaySharedPtr pRet( new UserPaintOverlay( rStrokeColor, nStrokeWidth, rContext, rPolygons, bActive)); return pRet; } UserPaintOverlay::UserPaintOverlay( const RGBColor& rStrokeColor, double nStrokeWidth, const SlideShowContext& rContext, const PolyPolygonVector& rPolygons, bool bActive ) : mpHandler( new PaintOverlayHandler( rStrokeColor, nStrokeWidth, rContext.mrActivitiesQueue, rContext.mrScreenUpdater, rContext.mrViewContainer, //adding a link to Slide dynamic_cast(rContext.mrCursorManager), rPolygons, bActive )), mrMultiplexer( rContext.mrEventMultiplexer ) { mrMultiplexer.addClickHandler( mpHandler, 3.0 ); mrMultiplexer.addMouseMoveHandler( mpHandler, 3.0 ); mrMultiplexer.addViewHandler( mpHandler ); mrMultiplexer.addUserPaintHandler(mpHandler); } PolyPolygonVector UserPaintOverlay::getPolygons() { return mpHandler->getPolygons(); } void UserPaintOverlay::drawPolygons() { mpHandler->drawPolygons(); } void UserPaintOverlay::update_settings( bool bUserPaintEnabled, RGBColor const& aUserPaintColor, double dUserPaintStrokeWidth ) { mpHandler->update_settings( bUserPaintEnabled, aUserPaintColor, dUserPaintStrokeWidth ); } UserPaintOverlay::~UserPaintOverlay() { try { mrMultiplexer.removeMouseMoveHandler( mpHandler ); mrMultiplexer.removeClickHandler( mpHandler ); mrMultiplexer.removeViewHandler( mpHandler ); mpHandler->dispose(); } catch (uno::Exception &) { OSL_ENSURE( false, rtl::OUStringToOString( comphelper::anyToString( cppu::getCaughtException() ), RTL_TEXTENCODING_UTF8 ).getStr() ); } } } }