diff options
Diffstat (limited to 'slideshow/source/engine/slide/userpaintoverlay.cxx')
-rw-r--r-- | slideshow/source/engine/slide/userpaintoverlay.cxx | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/slideshow/source/engine/slide/userpaintoverlay.cxx b/slideshow/source/engine/slide/userpaintoverlay.cxx new file mode 100644 index 000000000..045c455e6 --- /dev/null +++ b/slideshow/source/engine/slide/userpaintoverlay.cxx @@ -0,0 +1,485 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + + +#include <com/sun/star/awt/MouseButton.hpp> +#include <com/sun/star/awt/MouseEvent.hpp> + +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <cppcanvas/basegfxfactory.hxx> +#include <tools/diagnose_ex.h> + +#include <slideshowcontext.hxx> +#include "userpaintoverlay.hxx" +#include <mouseeventhandler.hxx> +#include <eventmultiplexer.hxx> +#include <screenupdater.hxx> +#include <vieweventhandler.hxx> + +#include <slide.hxx> +#include <cursormanager.hxx> + +using namespace ::com::sun::star; + +namespace slideshow::internal +{ + class PaintOverlayHandler : public MouseEventHandler, + public ViewEventHandler, + public UserPaintEventHandler + { + public: + PaintOverlayHandler( const RGBColor& rStrokeColor, + double nStrokeWidth, + ScreenUpdater& rScreenUpdater, + const UnoViewContainer& rViews, + Slide& rSlide, + const PolyPolygonVector& rPolygons, + bool bActive ) : + 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 ) + { + for( const auto& rView : rViews ) + viewAdded( rView ); + + drawPolygons(); + } + + void dispose() + { + maViews.clear(); + } + + // ViewEventHandler methods + virtual void viewAdded( const UnoViewSharedPtr& rView ) override + { + maViews.push_back( rView ); + } + + virtual void viewRemoved( const UnoViewSharedPtr& rView ) override + { + maViews.erase( ::std::remove( maViews.begin(), + maViews.end(), + rView ) ); + } + + virtual void viewChanged( const UnoViewSharedPtr& /*rView*/ ) override + { + // TODO(F2): for persistent drawings, need to store + // polygon and repaint here. + } + + virtual void viewsChanged() override + { + // TODO(F2): for persistent drawings, need to store + // polygon and repaint here. + } + + bool colorChanged( RGBColor const& rUserColor ) override + { + mbIsLastPointValid = false; + mbActive = true; + maStrokeColor = rUserColor; + mbIsEraseModeActivated = false; + return true; + } + + bool widthChanged( double nUserStrokeWidth ) override + { + mnStrokeWidth = nUserStrokeWidth; + mbIsEraseModeActivated = false; + return true; + } + + void repaintWithoutPolygons() + { + // must get access to the instance to erase all polygon + for( const auto& rxView : maViews ) + { + // fully clear view content to background color + //rxView->getCanvas()->clear(); + + //get via SlideImpl instance the bitmap of the slide unmodified to redraw it + SlideBitmapSharedPtr pBitmap( mrSlide.getCurrentSlideBitmap( rxView ) ); + ::cppcanvas::CanvasSharedPtr pCanvas( rxView->getCanvas() ); + + const ::basegfx::B2DHomMatrix aViewTransform( rxView->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(rxView,true); + } + } + + bool eraseAllInkChanged( bool bEraseAllInk ) override + { + mbIsEraseAllModeActivated = bEraseAllInk; + // if the erase all mode is activated it will remove all ink from slide, + // therefore destroy all the polygons stored + if(mbIsEraseAllModeActivated) + { + // The Erase Mode should be deactivated + mbIsEraseModeActivated = false; + repaintWithoutPolygons(); + maPolygons.clear(); + } + mbIsEraseAllModeActivated=false; + return true; + } + + bool eraseInkWidthChanged( sal_Int32 rEraseInkSize ) override + { + // Change the size + mnSize=rEraseInkSize; + // Changed to mode Erase + mbIsEraseModeActivated = true; + return true; + } + + bool switchPenMode() override + { + mbIsLastPointValid = false; + mbActive = true; + mbIsEraseModeActivated = false; + return true; + } + + bool switchEraserMode() override + { + mbIsLastPointValid = false; + mbActive = true; + mbIsEraseModeActivated = true; + return true; + } + + bool disable() override + { + mbIsLastPointValid = false; + mbIsLastMouseDownPosValid = false; + mbActive = false; + return true; + } + + //Draw all registered polygons. + void drawPolygons() + { + for( const auto& rxPolygon : maPolygons ) + { + rxPolygon->draw(); + } + // screen update necessary to show painting + mrScreenUpdater.notifyUpdate(); + } + + //Retrieve all registered polygons. + const PolyPolygonVector& getPolygons() const + { + return maPolygons; + } + + // MouseEventHandler methods + virtual bool handleMousePressed( const awt::MouseEvent& e ) override + { + 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 ) override + { + 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 handleMouseDragged( const awt::MouseEvent& e ) override + { + 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 (const auto& rxView : maViews) + { + + //get via SlideImpl instance the bitmap of the slide unmodified to redraw it + SlideBitmapSharedPtr pBitmap( mrSlide.getCurrentSlideBitmap( rxView ) ); + ::cppcanvas::CanvasSharedPtr pCanvas( rxView->getCanvas() ); + + ::basegfx::B2DHomMatrix aViewTransform( rxView->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(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(rxView,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 (const auto& rxView : maViews) + { + ::cppcanvas::PolyPolygonSharedPtr pPolyPoly( + ::cppcanvas::BaseGfxFactory::createPolyPolygon( rxView->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*/ ) override + { + // not used here + return false; // did not handle the event + } + + private: + 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( std::make_shared<PaintOverlayHandler>( rStrokeColor, + nStrokeWidth, + 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 const & UserPaintOverlay::getPolygons() const + { + return mpHandler->getPolygons(); + } + + void UserPaintOverlay::drawPolygons() + { + mpHandler->drawPolygons(); + } + + UserPaintOverlay::~UserPaintOverlay() + { + try + { + mrMultiplexer.removeMouseMoveHandler( mpHandler ); + mrMultiplexer.removeClickHandler( mpHandler ); + mrMultiplexer.removeViewHandler( mpHandler ); + mpHandler->dispose(); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("slideshow", ""); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |