| /************************************************************** |
| * |
| * 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 <canvas/debug.hxx> |
| |
| #include <comphelper/anytostring.hxx> |
| #include <cppuhelper/exc_hlp.hxx> |
| |
| #include <com/sun/star/awt/MouseButton.hpp> |
| #include <com/sun/star/presentation/XSlideShowView.hpp> |
| |
| #include <basegfx/point/b2dpoint.hxx> |
| #include <basegfx/polygon/b2dpolygon.hxx> |
| #include <cppcanvas/basegfxfactory.hxx> |
| |
| #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 <boost/bind.hpp> |
| #include <boost/noncopyable.hpp> |
| #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<Slide&>(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() ); |
| } |
| } |
| } |
| } |