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.
492 lines
12 KiB
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();
|
|
}
|