blob: f3a7c8174c7a8406217f9023e3f709a9bcc768e0 [file] [log] [blame]
/**************************************************************
*
* 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() );
}
}
}
}