Files
libreoffice/slideshow/source/engine/rehearsetimingsactivity.cxx
Oliver Bolte 1d6897835a INTEGRATION: CWS presfixes12 (1.11.12); FILE MERGED
2007/03/05 13:47:06 thb 1.11.12.4: #i37778# Removed Activity::needsScreenUpdate() method - this is now handled by notifying ScreenUpdater explicitely; reorg of SlideShowImpl members, to align lifetime with mutual references; fixed (hopefully) last repaint issues with new LayerManager; added more tests
2007/02/25 01:10:23 thb 1.11.12.3: #i37778# Cleared up error handling a lot: no longer quenching RuntimeExceptions; reporting assertions in the debug case; ViewLayer  now reports resized sprite (which needs re-render from all shapes); fixed missing subset area reduction for glyph-level animations; added return of resize state from  Layer::commitLayerBounds(); adapted unit tests to corrected behaviour
2007/01/29 15:15:29 thb 1.11.12.2: #i37778# Fixed Forte8 compiler breakages
2007/01/29 14:01:54 thb 1.11.12.1: Issue number: #i37778#

Larger slideshow refactoring. Wrote design and coding style manifest,
and adapted the code to actually conform to this. In detail:
 - cleaned up ownership/disposable/weak_ptr story. removed hacks and
   explicit Disposable implementations, where workaround were available
 - removed object mutices, where superfluous
 - reworked EventMultiplexer (using templatized listener class now), added
   more events. EventMultiplexer now serves as a true blackboard
 - reworked directory structure: disjunct parts are now physically separated
   into directories, instantiation happens via factories & abstract interfaces
 - added CursorManager, to make setting mouse cursor less hackish
 - reworked DrawShape, to implement SeparateListener pattern
 - reworked IntrinsicAnimationActivity, to avoid cyclic references
 - modified hyperlink & shape cursor handling to communicate via
   EventMultiplexer
 - renamed & cleaned up files (presentation.cxx now named slideshowimpl.cxx,
   etc.)
 - added first version of the z-order fix to layer/layermanager
 - cleaned up include guards and include syntax
2007-07-17 13:38:24 +00:00

587 lines
18 KiB
C++

/*************************************************************************
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: rehearsetimingsactivity.cxx,v $
*
* $Revision: 1.12 $
*
* last change: $Author: obo $ $Date: 2007-07-17 14:38:24 $
*
* The Contents of this file are made available subject to
* the terms of GNU Lesser General Public License Version 2.1.
*
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2005 by Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, CA 94303, USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
************************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_slideshow.hxx"
#include <boost/current_function.hpp>
#include <rtl/ustrbuf.hxx>
#include <vcl/svapp.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/virdev.hxx>
#include <vcl/metric.hxx>
#include <cppcanvas/vclfactory.hxx>
#include <cppcanvas/basegfxfactory.hxx>
#include <basegfx/range/b2drange.hxx>
#include <comphelper/anytostring.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <com/sun/star/awt/MouseButton.hpp>
#include <com/sun/star/awt/MouseEvent.hpp>
#include <com/sun/star/rendering/XBitmap.hpp>
#include "eventqueue.hxx"
#include "screenupdater.hxx"
#include "eventmultiplexer.hxx"
#include "activitiesqueue.hxx"
#include "slideshowcontext.hxx"
#include "mouseeventhandler.hxx"
#include "rehearsetimingsactivity.hxx"
#include <boost/bind.hpp>
#include <algorithm>
using namespace com::sun::star;
using namespace com::sun::star::uno;
namespace slideshow {
namespace internal {
class RehearseTimingsActivity::WakeupEvent : public Event,
private ::boost::noncopyable
{
public:
WakeupEvent( boost::shared_ptr< ::canvas::tools::ElapsedTime > const& pTimeBase,
ActivitySharedPtr const& rActivity,
ActivitiesQueue & rActivityQueue ) :
maTimer(pTimeBase),
mnNextTime(0.0),
mpActivity(rActivity),
mrActivityQueue( rActivityQueue )
{}
virtual void dispose() {}
virtual bool fire()
{
ActivitySharedPtr pActivity( mpActivity.lock() );
if( !pActivity )
return false;
return mrActivityQueue.addActivity( pActivity );
}
virtual bool isCharged() const { return true; }
virtual double getActivationTime( double nCurrentTime ) const
{
const double nElapsedTime( maTimer.getElapsedTime() );
return ::std::max( nCurrentTime,
nCurrentTime - nElapsedTime + mnNextTime );
}
/// Start the internal timer
void start() { maTimer.reset(); }
/** Set the next timeout this object should generate.
@param nextTime
Absolute time, measured from the last start() call,
when this event should wakeup the Activity again. If
your time is relative, simply call start() just before
every setNextTimeout() call.
*/
void setNextTimeout( double nextTime ) { mnNextTime = nextTime; }
private:
::canvas::tools::ElapsedTime maTimer;
double mnNextTime;
boost::weak_ptr<Activity> mpActivity;
ActivitiesQueue& mrActivityQueue;
};
class RehearseTimingsActivity::MouseHandler : public MouseEventHandler,
private boost::noncopyable
{
public:
explicit MouseHandler( RehearseTimingsActivity& rta );
void reset();
bool hasBeenClicked() const { return mbHasBeenClicked; }
// MouseEventHandler
virtual bool handleMousePressed( awt::MouseEvent const & evt );
virtual bool handleMouseReleased( awt::MouseEvent const & evt );
virtual bool handleMouseEntered( awt::MouseEvent const & evt );
virtual bool handleMouseExited( awt::MouseEvent const & evt );
virtual bool handleMouseDragged( awt::MouseEvent const & evt );
virtual bool handleMouseMoved( awt::MouseEvent const & evt );
private:
bool isInArea( com::sun::star::awt::MouseEvent const & evt ) const;
void updatePressedState( const bool pressedState ) const;
RehearseTimingsActivity& mrActivity;
bool mbHasBeenClicked;
bool mbMouseStartedInArea;
};
const sal_Int32 LEFT_BORDER_SPACE = 10;
const sal_Int32 LOWER_BORDER_SPACE = 30;
RehearseTimingsActivity::RehearseTimingsActivity( const SlideShowContext& rContext ) :
mrEventQueue(rContext.mrEventQueue),
mrScreenUpdater(rContext.mrScreenUpdater),
mrEventMultiplexer(rContext.mrEventMultiplexer),
mrActivitiesQueue(rContext.mrActivitiesQueue),
maElapsedTime( rContext.mrEventQueue.getTimer() ),
maViews(),
maSpriteRectangle(),
maFont( Application::GetSettings().GetStyleSettings().GetInfoFont() ),
mpWakeUpEvent(),
mpMouseHandler(),
maSpriteSizePixel(),
mnYOffset(0),
mbActive(false),
mbDrawPressed(false)
{
maFont.SetHeight( maFont.GetHeight() * 2 );
maFont.SetWidth( maFont.GetWidth() * 2 );
maFont.SetAlign( ALIGN_BASELINE );
maFont.SetColor( COL_BLACK );
// determine sprite size (in pixel):
VirtualDevice blackHole;
blackHole.EnableOutput(false);
blackHole.SetFont( maFont );
blackHole.SetMapMode( MAP_PIXEL );
Rectangle rect;
const FontMetric metric( blackHole.GetFontMetric() );
blackHole.GetTextBoundRect(
rect, String(RTL_CONSTASCII_USTRINGPARAM("XX:XX:XX")) );
maSpriteSizePixel.setX( rect.getWidth() * 12 / 10 );
maSpriteSizePixel.setY( metric.GetLineHeight() * 11 / 10 );
mnYOffset = (metric.GetAscent() + (metric.GetLineHeight() / 20));
std::for_each( rContext.mrViewContainer.begin(),
rContext.mrViewContainer.end(),
boost::bind( &RehearseTimingsActivity::viewAdded,
this,
_1 ));
}
RehearseTimingsActivity::~RehearseTimingsActivity()
{
try
{
stop();
}
catch (uno::Exception &)
{
OSL_ENSURE( false, rtl::OUStringToOString(
comphelper::anyToString(
cppu::getCaughtException() ),
RTL_TEXTENCODING_UTF8 ).getStr() );
}
}
boost::shared_ptr<RehearseTimingsActivity> RehearseTimingsActivity::create(
const SlideShowContext& rContext )
{
boost::shared_ptr<RehearseTimingsActivity> pActivity(
new RehearseTimingsActivity( rContext ));
pActivity->mpMouseHandler.reset(
new MouseHandler(*pActivity.get()) );
pActivity->mpWakeUpEvent.reset(
new WakeupEvent( rContext.mrEventQueue.getTimer(),
pActivity,
rContext.mrActivitiesQueue ));
rContext.mrEventMultiplexer.addViewHandler( pActivity );
return pActivity;
}
void RehearseTimingsActivity::start()
{
maElapsedTime.reset();
mbDrawPressed = false;
mbActive = true;
// paint and show all sprites:
paintAllSprites();
for_each_sprite( boost::bind( &cppcanvas::Sprite::show, _1 ) );
mrActivitiesQueue.addActivity( shared_from_this() );
mpMouseHandler->reset();
mrEventMultiplexer.addClickHandler(
mpMouseHandler, 42 /* highest prio of all, > 3.0 */ );
mrEventMultiplexer.addMouseMoveHandler(
mpMouseHandler, 42 /* highest prio of all, > 3.0 */ );
}
double RehearseTimingsActivity::stop()
{
mrEventMultiplexer.removeMouseMoveHandler( mpMouseHandler );
mrEventMultiplexer.removeClickHandler( mpMouseHandler );
mbActive = false; // will be removed from queue
for_each_sprite( boost::bind( &cppcanvas::Sprite::hide, _1 ) );
return maElapsedTime.getElapsedTime();
}
bool RehearseTimingsActivity::hasBeenClicked() const
{
if (mpMouseHandler)
return mpMouseHandler->hasBeenClicked();
return false;
}
// Disposable:
void RehearseTimingsActivity::dispose()
{
stop();
mpWakeUpEvent.reset();
mpMouseHandler.reset();
ViewsVecT().swap( maViews );
}
// Activity:
double RehearseTimingsActivity::calcTimeLag() const
{
return 0.0;
}
bool RehearseTimingsActivity::perform()
{
if( !isActive() )
return false;
if( !mpWakeUpEvent )
return false;
mpWakeUpEvent->start();
mpWakeUpEvent->setNextTimeout( 0.5 );
mrEventQueue.addEvent( mpWakeUpEvent );
paintAllSprites();
// sprites changed, need screen update
mrScreenUpdater.notifyUpdate();
return false; // don't reinsert, WakeupEvent will perform
// that after the given timeout
}
bool RehearseTimingsActivity::isActive() const
{
return mbActive;
}
void RehearseTimingsActivity::dequeued()
{
// not used here
}
void RehearseTimingsActivity::end()
{
if (isActive())
{
stop();
mbActive = false;
}
}
basegfx::B2DRange RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr const& rView ) const
{
const Reference<rendering::XBitmap> xBitmap( rView->getCanvas()->getUNOCanvas(),
UNO_QUERY );
if( !xBitmap.is() )
return basegfx::B2DRange();
const geometry::IntegerSize2D realSize( xBitmap->getSize() );
// pixel:
basegfx::B2DPoint spritePos(
std::min<sal_Int32>( realSize.Width, LEFT_BORDER_SPACE ),
std::max<sal_Int32>( 0, realSize.Height - maSpriteSizePixel.getY()
- LOWER_BORDER_SPACE ) );
basegfx::B2DHomMatrix transformation( rView->getTransformation() );
transformation.invert();
spritePos *= transformation;
basegfx::B2DSize spriteSize( maSpriteSizePixel.getX(),
maSpriteSizePixel.getY() );
spriteSize *= transformation;
return basegfx::B2DRange(
spritePos.getX(), spritePos.getY(),
spritePos.getX() + spriteSize.getX(),
spritePos.getY() + spriteSize.getY() );
}
void RehearseTimingsActivity::viewAdded( const UnoViewSharedPtr& rView )
{
cppcanvas::CustomSpriteSharedPtr sprite(
rView->createSprite( basegfx::B2DSize(
maSpriteSizePixel.getX()+2,
maSpriteSizePixel.getY()+2 ),
1001.0 )); // sprite should be in front of all
// other sprites
sprite->setAlpha( 0.8 );
const basegfx::B2DRange spriteRectangle(
calcSpriteRectangle( rView ) );
sprite->move( basegfx::B2DPoint(
spriteRectangle.getMinX(),
spriteRectangle.getMinY() ) );
if( maViews.empty() )
maSpriteRectangle = spriteRectangle;
maViews.push_back( ViewsVecT::value_type( rView, sprite ) );
if (isActive())
sprite->show();
}
void RehearseTimingsActivity::viewRemoved( const UnoViewSharedPtr& rView )
{
maViews.erase(
std::remove_if(
maViews.begin(), maViews.end(),
boost::bind(
std::equal_to<UnoViewSharedPtr>(),
rView,
// select view:
boost::bind( std::select1st<ViewsVecT::value_type>(), _1 ))),
maViews.end() );
}
void RehearseTimingsActivity::viewChanged( const UnoViewSharedPtr& rView )
{
// find entry corresponding to modified view
ViewsVecT::iterator aModifiedEntry(
std::find_if(
maViews.begin(),
maViews.end(),
boost::bind(
std::equal_to<UnoViewSharedPtr>(),
rView,
// select view:
boost::bind( std::select1st<ViewsVecT::value_type>(), _1 ))));
OSL_ASSERT( aModifiedEntry != maViews.end() );
if( aModifiedEntry == maViews.end() )
return;
// new sprite pos, transformation might have changed:
maSpriteRectangle = calcSpriteRectangle( rView );
// reposition sprite:
aModifiedEntry->second->move( maSpriteRectangle.getMinimum() );
// sprites changed, need screen update
mrScreenUpdater.notifyUpdate( rView );
}
void RehearseTimingsActivity::viewsChanged()
{
if( !maViews.empty() )
{
// new sprite pos, transformation might have changed:
maSpriteRectangle = calcSpriteRectangle( maViews.front().first );
// reposition sprites
for_each_sprite( boost::bind( &cppcanvas::Sprite::move,
_1,
boost::cref(maSpriteRectangle.getMinimum())) );
// sprites changed, need screen update
mrScreenUpdater.notifyUpdate();
}
}
void RehearseTimingsActivity::paintAllSprites() const
{
for_each_sprite(
boost::bind( &RehearseTimingsActivity::paint, this,
// call getContentCanvas() on each sprite:
boost::bind(
&cppcanvas::CustomSprite::getContentCanvas, _1 ) ) );
}
void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr const & canvas ) const
{
// build timer string:
const sal_Int32 nTimeSecs =
static_cast<sal_Int32>(maElapsedTime.getElapsedTime());
rtl::OUStringBuffer buf;
sal_Int32 n = (nTimeSecs / 3600);
if (n < 10)
buf.append( static_cast<sal_Unicode>('0') );
buf.append( n );
buf.append( static_cast<sal_Unicode>(':') );
n = ((nTimeSecs % 3600) / 60);
if (n < 10)
buf.append( static_cast<sal_Unicode>('0') );
buf.append( n );
buf.append( static_cast<sal_Unicode>(':') );
n = (nTimeSecs % 60);
if (n < 10)
buf.append( static_cast<sal_Unicode>('0') );
buf.append( n );
const rtl::OUString time = buf.makeStringAndClear();
// create the MetaFile:
GDIMetaFile metaFile;
VirtualDevice blackHole;
metaFile.Record( &blackHole );
metaFile.SetPrefSize( Size( 1, 1 ) );
blackHole.EnableOutput(false);
blackHole.SetMapMode( MAP_PIXEL );
blackHole.SetFont( maFont );
Rectangle rect = Rectangle( 0,0,
maSpriteSizePixel.getX(),
maSpriteSizePixel.getY());
if (mbDrawPressed)
{
blackHole.SetTextColor( COL_BLACK );
blackHole.SetFillColor( COL_LIGHTGRAY );
blackHole.SetLineColor( COL_GRAY );
}
else
{
blackHole.SetTextColor( COL_BLACK );
blackHole.SetFillColor( COL_WHITE );
blackHole.SetLineColor( COL_GRAY );
}
blackHole.DrawRect( rect );
blackHole.GetTextBoundRect( rect, time );
blackHole.DrawText(
Point( (maSpriteSizePixel.getX() - rect.getWidth()) / 2,
mnYOffset ), time );
metaFile.Stop();
metaFile.WindStart();
cppcanvas::RendererSharedPtr renderer(
cppcanvas::VCLFactory::getInstance().createRenderer(
canvas, metaFile, cppcanvas::Renderer::Parameters() ) );
const bool succ = renderer->draw();
OSL_ASSERT( succ );
(void)succ;
}
RehearseTimingsActivity::MouseHandler::MouseHandler( RehearseTimingsActivity& rta ) :
mrActivity(rta),
mbHasBeenClicked(false),
mbMouseStartedInArea(false)
{}
void RehearseTimingsActivity::MouseHandler::reset()
{
mbHasBeenClicked = false;
mbMouseStartedInArea = false;
}
bool RehearseTimingsActivity::MouseHandler::isInArea(
awt::MouseEvent const & evt ) const
{
return mrActivity.maSpriteRectangle.isInside(
basegfx::B2DPoint( evt.X, evt.Y ) );
}
void RehearseTimingsActivity::MouseHandler::updatePressedState(
const bool pressedState ) const
{
if( pressedState != mrActivity.mbDrawPressed )
{
mrActivity.mbDrawPressed = pressedState;
mrActivity.paintAllSprites();
mrActivity.mrScreenUpdater.notifyUpdate();
}
}
// MouseEventHandler
bool RehearseTimingsActivity::MouseHandler::handleMousePressed(
awt::MouseEvent const & evt )
{
if( evt.Buttons == awt::MouseButton::LEFT && isInArea(evt) )
{
mbMouseStartedInArea = true;
updatePressedState(true);
return true; // consume event
}
return false;
}
bool RehearseTimingsActivity::MouseHandler::handleMouseReleased(
awt::MouseEvent const & evt )
{
if( evt.Buttons == awt::MouseButton::LEFT && mbMouseStartedInArea )
{
mbHasBeenClicked = isInArea(evt); // fini if in
mbMouseStartedInArea = false;
updatePressedState(false);
if( !mbHasBeenClicked )
return true; // consume event, else next slide (manual advance)
}
return false;
}
bool RehearseTimingsActivity::MouseHandler::handleMouseEntered(
awt::MouseEvent const & /*evt*/ )
{
return false;
}
bool RehearseTimingsActivity::MouseHandler::handleMouseExited(
awt::MouseEvent const & /*evt*/ )
{
return false;
}
bool RehearseTimingsActivity::MouseHandler::handleMouseDragged(
awt::MouseEvent const & evt )
{
if( mbMouseStartedInArea )
updatePressedState( isInArea(evt) );
return false;
}
bool RehearseTimingsActivity::MouseHandler::handleMouseMoved(
awt::MouseEvent const & /*evt*/ )
{
return false;
}
} // namespace internal
} // namespace presentation