You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/karbon/commands/vtransformcmd.cc

492 lines
12 KiB

/* This file is doc of the KDE project
Copyright (C) 2001, 2002, 2003 The Karbon Developers
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <klocale.h>
#include "vcomposite.h"
#include "vpath.h"
#include "vsegment.h"
#include "vselection.h"
#include "vtext.h"
#include "vimage.h"
#include "vtransformcmd.h"
#include "vstroke.h"
#include "vfill.h"
#include "vdocument.h"
#include <kdebug.h>
VTransformCmd::VTransformCmd( VDocument *doc, const QWMatrix& mat, bool duplicate )
: VCommand( doc, i18n( "Transform Objects" ) ), m_mat( mat ), m_duplicate( duplicate )
{
m_selection = ( document() && document()->selection() )
? document()->selection()->clone()
: new VSelection();
if( m_duplicate )
{
if( !m_selection || m_selection->objects().count() == 1 )
setName( i18n( "Duplicate Object" ) );
else
setName( i18n( "Duplicate Objects" ) );
}
else if( !m_selection || m_selection->objects().count() == 1 )
setName( i18n( "Transform Object" ) );
}
VTransformCmd::VTransformCmd( VDocument *doc, const QString& name, const QString& icon, bool duplicate )
: VCommand( doc, name, icon ), m_duplicate( duplicate )
{
m_selection = ( document() && document()->selection() )
? document()->selection()->clone()
: new VSelection();
if( m_duplicate )
{
if( !m_selection || m_selection->objects().count() == 1 )
setName( i18n( "Duplicate Object" ) );
else
setName( i18n( "Duplicate Objects" ) );
}
}
VTransformCmd::~VTransformCmd()
{
delete( m_selection );
m_selection = 0L;
}
void
VTransformCmd::execute()
{
VObjectListIterator itr( m_selection->objects() );
if( m_duplicate )
{
// clone original objects, add duplicates to document, transform and select them
VObject *copy = 0L;
for( ; itr.current() ; ++itr )
{
copy = itr.current()->clone();
visit( *copy );
document()->append( copy );
document()->selection()->take( *itr.current() );
document()->selection()->append( copy );
m_duplicates.append( copy );
}
}
else
{
// clear selection ...
document()->selection()->clear();
// transform objects
for( ; itr.current() ; ++itr )
{
visit( *itr.current() );
}
// ... and re-add all objects incase we are re-executing the command
document()->selection()->append( m_selection->objects() );
}
setSuccess( true );
}
void
VTransformCmd::unexecute()
{
// inverting the matrix should undo the affine transformation
m_mat = m_mat.invert();
if( m_duplicate )
{
// remove duplicated objects
VObjectListIterator itr( m_duplicates );
for( ; itr.current() ; ++itr )
{
document()->selection()->take( *itr.current() );
itr.current()->setState( VObject::deleted );
}
VObjectListIterator jtr( m_selection->objects() );
// add original selection objects to new selection
for( ; jtr.current() ; ++jtr )
{
document()->selection()->append( jtr.current() );
}
}
else
{
document()->selection()->clear();
// move objects back to original position
visit( *m_selection );
document()->selection()->append( m_selection->objects() );
}
// reset
m_mat = m_mat.invert();
setSuccess( false );
}
void
VTransformCmd::visitVObject( VObject& object )
{
// Apply transformation to gradients.
VStroke* stroke = object.stroke();
if( stroke && stroke->type() == VStroke::grad )
stroke->gradient().transform( m_mat );
else if( stroke && stroke->type() == VStroke::patt )
stroke->pattern().transform( m_mat );
VFill* fill = object.fill();
if( fill && fill->type() == VFill::grad )
fill->gradient().transform( m_mat );
else if( fill && fill->type() == VFill::patt )
fill->pattern().transform( m_mat );
}
void
VTransformCmd::visitVPath( VPath& composite )
{
if( composite.state() == VObject::hidden ||
composite.state() == VObject::normal_locked ||
composite.state() == VObject::hidden_locked )
return;
visitVObject( composite );
composite.transform( m_mat );
VVisitor::visitVPath( composite );
}
void
VTransformCmd::visitVSubpath( VSubpath& path )
{
if( path.state() == VObject::hidden ||
path.state() == VObject::normal_locked ||
path.state() == VObject::hidden_locked )
return;
VSegment* segment = path.first();
while( segment )
{
for( unsigned short i = 0; i < segment->degree(); ++i )
{
segment->setPoint( i, segment->point( i ).transform( m_mat ) );
}
segment = segment->next();
}
path.invalidateBoundingBox();
}
void
VTransformCmd::visitVText( VText& text )
{
if( text.state() == VObject::hidden ||
text.state() == VObject::normal_locked ||
text.state() == VObject::hidden_locked )
return;
visitVObject( text );
visit( text.basePath() );
VPathListIterator itr( text.glyphs() );
for( ; itr.current() ; ++itr )
{
visit( *itr.current() );
}
text.invalidateBoundingBox();
}
void
VTransformCmd::visitVImage( VImage &img )
{
if( img.state() == VObject::hidden ||
img.state() == VObject::normal_locked ||
img.state() == VObject::hidden_locked )
return;
img.transform( m_mat );
}
VTranslateCmd::VTranslateCmd( VDocument *doc, double d1, double d2, bool duplicate )
: VTransformCmd( doc, i18n( "Translate Objects" ), "translate", duplicate )
{
if( !duplicate && ( !m_selection || m_selection->objects().count() == 1 ) )
setName( i18n( "Translate Object" ) );
m_mat.translate( d1, d2 );
}
VScaleCmd::VScaleCmd( VDocument *doc, const KoPoint& p, double s1, double s2, bool duplicate )
: VTransformCmd( doc, i18n( "Scale Objects" ), "14_select", duplicate )
{
if( !duplicate && ( !m_selection || m_selection->objects().count() == 1 ) )
setName( i18n( "Scale Object" ) );
m_mat.translate( p.x(), p.y() );
m_mat.scale( s1, s2 );
m_mat.translate( -p.x(), -p.y() );
}
VShearCmd::VShearCmd( VDocument *doc, const KoPoint& p, double s1, double s2, bool duplicate )
: VTransformCmd( doc, i18n( "Shear Objects" ), "14_shear", duplicate )
{
if( !duplicate && ( !m_selection || m_selection->objects().count() == 1 ) )
setName( i18n( "Shear Object" ) );
m_mat.translate( p.x(), p.y() );
m_mat.shear( s1, s2 );
m_mat.translate( -p.x(), -p.y() );
}
VRotateCmd::VRotateCmd( VDocument *doc, const KoPoint& p, double angle, bool duplicate )
: VTransformCmd( doc, i18n( "Rotate Objects" ), "14_rotate", duplicate )
{
if( !duplicate && ( !m_selection || m_selection->objects().count() == 1 ) )
setName( i18n( "Rotate Object" ) );
m_mat.translate( p.x(), p.y() );
m_mat.rotate( angle );
m_mat.translate( -p.x(), -p.y() );
}
VTranslateBezierCmd::VTranslateBezierCmd( VDocument *doc, VSegment *segment, double d1, double d2, bool firstControl )
: VCommand( doc, i18n( "Translate Bezier" ) ), m_segment( segment ), m_firstControl( firstControl )
, m_subpath(0L)
{
m_mat.translate( d1, d2 );
m_segmenttwo = 0L;
if( document() && document()->selection() )
{
VObjectListIterator itr( document()->selection()->objects() );
// find subpath containing the segment
for( ; itr.current() ; ++itr )
visit( *itr.current() );
}
}
VTranslateBezierCmd::~VTranslateBezierCmd()
{
}
void
VTranslateBezierCmd::execute()
{
if( m_segment->degree() == 3 )
{
QWMatrix m2( m_mat.m11(), m_mat.m12(), m_mat.m21(), m_mat.m22(), -m_mat.dx(), -m_mat.dy() );
if( m_firstControl )
{
if( m_segment->prev() &&
m_segment->prev()->degree() == 3 &&
m_segment->prev()->isSmooth() )
{
m_segmenttwo = m_segment->prev();
for( uint i = 0;i < m_segmenttwo->degree();i++ )
{
m_segmenttwo->selectPoint( i, i == 1 );
if( i == 1 )
m_segmenttwo->setPoint( i, m_segmenttwo->point( i ).transform( m2 ) );
}
}
}
else
{
m_segmenttwo = ( m_segment->isSmooth() && m_segment->next()->degree() == 3 ) ? m_segment->next() : 0L;
if( m_segmenttwo )
{
for( uint i = 0;i < m_segmenttwo->degree();i++ )
{
m_segmenttwo->selectPoint( i, i == 0 );
if( i == 0 )
m_segmenttwo->setPoint( i, m_segmenttwo->point( i ).transform( m2 ) );
}
}
}
for( uint i = 0;i < m_segment->degree();i++ )
{
m_segment->selectPoint( i, i == uint( m_firstControl ? 0 : 1 ) );
if( i == uint( m_firstControl ? 0 : 1 ) )
m_segment->setPoint( i, m_segment->point( i ).transform( m_mat ) );
}
}
if( m_subpath )
m_subpath->invalidateBoundingBox();
setSuccess( true );
}
void
VTranslateBezierCmd::unexecute()
{
QWMatrix m2( m_mat.m11(), m_mat.m12(), m_mat.m21(), m_mat.m22(), -m_mat.dx(), -m_mat.dy() );
if( m_segment )
{
for( uint i = 0;i < m_segment->degree();i++ )
{
m_segment->selectPoint( i, i == uint( m_firstControl ? 0 : 1 ) );
if( i == uint( m_firstControl ? 0 : 1 ) )
m_segment->setPoint( i, m_segment->point( i ).transform( m_mat.invert() ) );
}
if( m_segmenttwo )
{
uint index = m_firstControl ? 1 : 0;
for( uint i = 0;i < m_segmenttwo->degree();i++ )
{
m_segmenttwo->selectPoint( i, i == index );
if( i == index )
m_segmenttwo->setPoint( i, m_segmenttwo->point( i ).transform( m2.invert() ) );
}
}
}
setSuccess( false );
}
void
VTranslateBezierCmd::visitVSubpath( VSubpath& path )
{
if( m_subpath )
return;
VSegment* segment = path.first();
// check all segments of the path
while( segment )
{
if( segment == m_segment )
{
m_subpath = &path;
break;
}
segment = segment->next();
}
}
VTranslatePointCmd::VTranslatePointCmd( VDocument *doc, double d1, double d2 )
: VCommand( doc, i18n( "Translate Points" ), "translate" )
{
m_mat.translate( d1, d2 );
if( document() && document()->selection() )
{
VObjectListIterator itr( document()->selection()->objects() );
// collect all points to translate
for( ; itr.current() ; ++itr )
visit( *itr.current() );
if( m_segPnts.size() > 1 || ( m_segPnts.size() == 0 && m_segPnts.begin().data().size() > 1 ) )
setName( i18n( "Translate Point" ) );
}
}
VTranslatePointCmd::~VTranslatePointCmd()
{
}
void
VTranslatePointCmd::execute()
{
translatePoints();
setSuccess( true );
}
void
VTranslatePointCmd::unexecute()
{
m_mat = m_mat.invert();
translatePoints();
m_mat = m_mat.invert();
setSuccess( false );
}
void
VTranslatePointCmd::visitVSubpath( VSubpath& path )
{
if( path.state() == VObject::hidden ||
path.state() == VObject::normal_locked ||
path.state() == VObject::hidden_locked )
return;
VSegment* segment = path.first();
uint segCnt = m_segPnts.size();
// save indices of selected points for all segments
while( segment )
{
QValueVector<int> pnts;
for( unsigned short i = 0; i < segment->degree(); ++i )
{
if( segment->pointIsSelected( i ) )
pnts.push_back( i );
}
if( pnts.size() )
m_segPnts[segment] = pnts;
segment = segment->next();
}
// save subpaths which have selected points
if( segCnt != m_segPnts.size() )
m_subpaths.append( &path );
}
void
VTranslatePointCmd::translatePoints()
{
QMap<VSegment*, QValueVector<int> >::iterator it, et = m_segPnts.end();
// iterate over the segments and transform all selected points
for( it = m_segPnts.begin(); it != et; ++it )
{
VSegment *segment = it.key();
QValueVector<int> &pnts = it.data();
int pntCnt = pnts.size();
for( int i = 0; i < pntCnt; ++i )
segment->setPoint( pnts[i], segment->point( pnts[i] ).transform( m_mat ) );
}
// invalidate all changed subpaths
VObjectListIterator itr( m_subpaths );
for( ; itr.current(); ++itr )
itr.current()->invalidateBoundingBox();
}