diff options
Diffstat (limited to 'karbon/core')
54 files changed, 11251 insertions, 0 deletions
diff --git a/karbon/core/Makefile.am b/karbon/core/Makefile.am new file mode 100644 index 00000000..aabebf80 --- /dev/null +++ b/karbon/core/Makefile.am @@ -0,0 +1,73 @@ +INCLUDES = $(KOFFICE_INCLUDES) $(KOPAINTER_INCLUDES) \ + -I$(srcdir)/.. \ + -I$(srcdir)/../render \ + -I$(srcdir)/../visitors \ + $(LIBFREETYPE_CFLAGS) $(LIBFONTCONFIG_CFLAGS) $(all_includes) + +noinst_LTLIBRARIES = libkarboncore.la + +noinst_HEADERS = \ + vcolor.h \ + vcomposite.h \ + vcomposite_iface.h \ + vdashpattern.h \ + vdocument.h \ + vfill.h \ + vglobal.h \ + vgradient.h \ + vgroup.h \ + vobject_iface.h \ + vgroup_iface.h \ + vimage.h \ + vkarbonplugin.h \ + vlayer.h \ + vobject.h \ + vpath.h \ + vpattern.h \ + vsegment.h \ + vselection.h \ + vstroke.h \ + vvisitor.h \ + vlayer_iface.h \ + vtext.h \ + vtext_iface.h \ + vclipgroup.h \ + vcursor.h + +libkarboncore_la_SOURCES = \ + vcolor.cc \ + vcomposite.cc \ + vcomposite_iface.cc \ + vcomposite_iface.skel \ + vdashpattern.cc \ + vdocument.cc \ + vfill.cc \ + vglobal.cc \ + vgradient.cc \ + vgroup.cc \ + vobject_iface.cc \ + vobject_iface.skel \ + vgroup_iface.cc \ + vgroup_iface.skel \ + vimage.cc \ + vkarbonplugin.cc \ + vlayer.cc \ + vobject.cc \ + vpath.cc \ + vpattern.cc \ + vsegment.cc \ + vselection.cc \ + vstroke.cc \ + vvisitor.cc \ + vlayer_iface.cc \ + vlayer_iface.skel \ + vtext.cc \ + vtext_iface.cc \ + vtext_iface.skel \ + vclipgroup.cc \ + vcursor.cc + +libkarboncore_la_LIBADD = $(LIBFONTCONFIG_LIBS) + +libkarboncore_la_METASOURCES = \ + AUTO diff --git a/karbon/core/vclipgroup.cc b/karbon/core/vclipgroup.cc new file mode 100644 index 00000000..fb96f609 --- /dev/null +++ b/karbon/core/vclipgroup.cc @@ -0,0 +1,178 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qdom.h> + +#include <kdebug.h> + +#include "vclipgroup.h" +#include "vgroup.h" +#include "vcomposite.h" +#include "vsegment.h" +#include <vpainter.h> +#include "vtext.h" +VClipGroup::VClipGroup( VObject* parent, VState state ) : VGroup( parent, state ) {} +VClipGroup::VClipGroup( const VClipGroup& group ) : VGroup( group ) {} + +VClipGroup::~VClipGroup() { } + +void VClipGroup::draw( VPainter* painter, const KoRect* rect ) const +{ + return VGroup::draw( painter, rect ); + if( + state() == deleted || + state() == hidden || + state() == hidden_locked ) + { + return; + } + + VObjectListIterator itr = m_objects; + + painter->save(); + + PathRenderer renderer( painter ); + kdDebug(38000) << "calling painter setClipPath" << endl; + painter->setClipPath(); + + VObject *obj = itr.current(); + obj->accept( renderer ); + ++itr; + + for( ; itr.current(); ++itr ) + itr.current()->draw( painter, rect ); + + painter->restore(); +} + +VClipGroup* VClipGroup::clone() const +{ + return new VClipGroup( *this ); +} + + +void VClipGroup::save( QDomElement& element ) const +{ + QDomElement me = element.ownerDocument().createElement( "CLIP" ); + element.appendChild( me ); + + // save objects: + VObjectListIterator itr = m_objects; + + for( ; itr.current(); ++itr ) + itr.current()->save( me ); +} + +void VClipGroup::load( const QDomElement& element ) +{ + m_objects.setAutoDelete( true ); + m_objects.clear(); + m_objects.setAutoDelete( false ); + + QDomNodeList list = element.childNodes(); + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement e = list.item( i ).toElement(); + + if( e.tagName() == "COMPOSITE" || e.tagName() == "PATH" ) // TODO : remove COMPOSITE later + { + VPath* composite = new VPath( this ); + composite->load( e ); + append( composite ); + } + else if( e.tagName() == "GROUP" ) + { + VGroup* group = new VGroup( this ); + group->load( e ); + append( group ); + } + else if( e.tagName() == "CLIP" ) + { + VClipGroup* clip = new VClipGroup( this ); + clip->load( e ); + append( clip ); + } + else if( e.tagName() == "TEXT" ) + { +#ifdef HAVE_KARBONTEXT + VText *text = new VText( this ); + text->load( e ); + append( text ); +#endif + } + } + } +} + +PathRenderer::PathRenderer( VPainter *p_painter ) : VVisitor() +{ + m_painter = p_painter; +} + +PathRenderer::~PathRenderer() {} + +void PathRenderer::visitVSubpath( VSubpath& path ) +{ + if(!m_painter) return; + + if(path.isEmpty()) return; + + for(path.first(); VSegment *segment = path.current(); path.next() ) + { + KoPoint p1; + KoPoint p2; + KoPoint p3; + + QString buffer; + + if(segment->state() != VSegment::deleted) + { + if (segment->isBegin()) { + p1 = segment->point( 0 ); + + kdDebug(38000) << "calling painter.moveTo with " << p1 << endl; + m_painter->moveTo( p1 ); + } else if (segment->isCurve()) { + p1 = segment->point( 0 ); + p2 = segment->point( 1 ); + p3 = segment->point( 2 ); + + kdDebug(38000) << "calling painter.curveTo with " << p1 << " " << p2 << " " << p3 << endl; + m_painter->curveTo( p1, p2, p3 ); + + } else if (segment->isLine()) { + p1 = segment->point( 0 ); + kdDebug(38000) << "calling painter.lineTo with " << p1 << endl; + m_painter->lineTo( p1 ); + } + } + } + + VVisitor::visitVSubpath(path); + +// if( path.isClosed() ) m_painter->closePath(); +} + diff --git a/karbon/core/vclipgroup.h b/karbon/core/vclipgroup.h new file mode 100644 index 00000000..640f3b11 --- /dev/null +++ b/karbon/core/vclipgroup.h @@ -0,0 +1,64 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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. +*/ + +#ifndef __VCLIP_H__ +#define __VCLIP_H__ + +#include "vgroup.h" +#include "vvisitor.h" +#include <koffice_export.h> +class VPainter; + +/** + * Base class for clipping conglomerates + * the first child element is used for clipping + */ + +class KARBONBASE_EXPORT VClipGroup : public VGroup +{ +public: + VClipGroup( VObject* parent, VState state = normal ); + VClipGroup ( const VClipGroup& group ); + + virtual ~VClipGroup(); + + virtual void draw( VPainter* painter, const KoRect* rect = 0L ) const; + + virtual VClipGroup* clone() const; + + virtual void save( QDomElement& element ) const; + virtual void load( const QDomElement& element ); +}; + + +class PathRenderer : public VVisitor +{ +public: + PathRenderer( VPainter *p_painter ); + + virtual ~PathRenderer(); + +protected: + virtual void visitVSubpath( VSubpath& path ); + +private: + VPainter *m_painter; +}; + +#endif diff --git a/karbon/core/vcolor.cc b/karbon/core/vcolor.cc new file mode 100644 index 00000000..c82777eb --- /dev/null +++ b/karbon/core/vcolor.cc @@ -0,0 +1,358 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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 <qdom.h> + +#include "vcolor.h" +#include "vglobal.h" + + +VColor::VColor( VColorSpace colorSpace ) +{ + m_colorSpace = colorSpace; + m_opacity = 1.0; + + m_value[0] = 0.0; + m_value[1] = 0.0; + m_value[2] = 0.0; + m_value[3] = 0.0; +} + +VColor::VColor( const VColor& color ) +{ + m_colorSpace = color.m_colorSpace; + m_opacity = color.m_opacity; + + m_value[0] = color.m_value[0]; + m_value[1] = color.m_value[1]; + m_value[2] = color.m_value[2]; + m_value[3] = color.m_value[3]; +} + +VColor::VColor( const QColor& color ) +{ + m_colorSpace = rgb; + m_opacity = 1.0; + + m_value[0] = color.red() / 255.0; + m_value[1] = color.green() / 255.0; + m_value[2] = color.blue() / 255.0; +} + +VColor::operator QColor() const +{ + VColor copy( *this ); + copy.convertToColorSpace( rgb ); + + QColor color; + color.setRgb( int( 255 * copy[0] ), int( 255 * copy[1] ), int( 255 * copy[2] ) ); + + return color; +} + +void +VColor::setColorSpace( const VColorSpace colorSpace, bool convert ) +{ + if( convert ) + convertToColorSpace( colorSpace ); + + m_colorSpace = colorSpace; +} + +void +VColor::convertToColorSpace( const VColorSpace colorSpace ) +{ + // TODO: numerical stability. + // TODO: undercolor removal with cmyk. + + if( colorSpace == rgb ) + { + if( m_colorSpace == rgb ) + { + // Do nothing. + } + else if( m_colorSpace == cmyk ) + { + m_value[0] = 1.0 - kMin( 1.0f, m_value[0] + m_value[3] ); + m_value[1] = 1.0 - kMin( 1.0f, m_value[1] + m_value[3] ); + m_value[2] = 1.0 - kMin( 1.0f, m_value[2] + m_value[3] ); + } + else if( m_colorSpace == hsb ) + { + // Achromatic case (saturation == 0.0). + if( m_value[1] == 0.0 ) + { + // Set to brightness: + m_value[0] = m_value[2]; + m_value[1] = m_value[2]; + m_value[2] = m_value[2]; // For readability. + } + else + { + float hue6 = 6.0 * m_value[0]; + uint i = static_cast<uint>( hue6 ); + float f = hue6 - i; + + float m = m_value[2] * ( 1.0 - m_value[1] ); + float n = m_value[2] * ( 1.0 - m_value[1] * f ); + float k = m_value[2] * ( 1.0 - m_value[1] * ( 1.0 - f ) ); + + float r; + float g; + float b; + + switch( i ) + { + case 1: + r = n; + g = m_value[2]; + b = m; + break; + case 2: + r = m; + g = m_value[2]; + b = k; + break; + case 3: + r = m; + g = n; + b = m_value[2]; + break; + case 4: + r = k; + g = m; + b = m_value[2]; + break; + case 5: + r = m_value[2]; + g = m; + b = n; + break; + default: + r = m_value[2]; + g = k; + b = m; + } + + m_value[0] = r; + m_value[1] = g; + m_value[2] = b; + } + } + else if( m_colorSpace == gray ) + { + m_value[0] = m_value[0]; // For readability. + m_value[1] = m_value[0]; + m_value[2] = m_value[0]; + } + } + else if( colorSpace == cmyk ) + { + if( m_colorSpace == rgb ) + { + m_value[0] = 1.0 - m_value[0]; + m_value[1] = 1.0 - m_value[1]; + m_value[2] = 1.0 - m_value[2]; + m_value[3] = 0.0; + } + else if( m_colorSpace == cmyk ) + { + // Do nothing. + } + else if( m_colorSpace == hsb ) + { +// TODO + } + else if( m_colorSpace == gray ) + { + m_value[1] = 0.0; + m_value[2] = 0.0; + m_value[3] = 1.0 - m_value[0]; + m_value[0] = 0.0; + } + } + else if( colorSpace == hsb ) + { + if( m_colorSpace == rgb ) + { + if( + m_value[0] == m_value[1] && + m_value[1] == m_value[2] ) + { + // Arbitrary: + m_value[3] = m_value[0]; + m_value[1] = 0.0; + m_value[2] = 0.0; + } + else + { + float max; + float min; + + // Find maximum + minimum rgb component: + if( m_value[0] > m_value[1] ) + { + max = m_value[0]; + min = m_value[1]; + } + else + { + max = m_value[1]; + min = m_value[0]; + } + + if( m_value[2] > max ) + max = m_value[2]; + + if( m_value[2] < min ) + min = m_value[2]; + + + float hue; + const float diff = max - min; + + // Which rgb component is maximum? + if( max == m_value[0] ) + // Red: + hue = ( m_value[1] - m_value[2] ) * VGlobal::one_6 / diff; + else if( max == m_value[1] ) + // Green: + hue = ( m_value[2] - m_value[0] ) * VGlobal::one_6 / diff + VGlobal::one_3; + else + // Blue: + hue = ( m_value[0] - m_value[1] ) * VGlobal::one_6 / diff + VGlobal::two_3; + + if( hue < 0.0 ) + hue += 1.0; + + + m_value[0] = hue; + m_value[1] = diff / max; + m_value[2] = max; + } + } + else if( m_colorSpace == cmyk ) + { +// TODO + } + else if( m_colorSpace == hsb ) + { + // Do nothing. + } + else if( m_colorSpace == gray ) + { + m_value[1] = 0.0; + m_value[2] = m_value[0]; + m_value[0] = 0.0; + } + } + else if( colorSpace == gray ) + { + if( m_colorSpace == rgb ) + { + m_value[0] = + 0.3 * m_value[0] + + 0.59 * m_value[1] + + 0.11 * m_value[2]; + } + else if( m_colorSpace == cmyk ) + { + m_value[0] = + 1.0 - kMin( 1.0, + 0.3 * m_value[0] + + 0.59 * m_value[1] + + 0.11 * m_value[2] + + m_value[3] ); + } + else if( m_colorSpace == hsb ) + { + m_value[0] = m_value[2]; + } + else if( m_colorSpace == gray ) + { + // Do nothing. + } + } +} + +void +VColor::save( QDomElement& element ) const +{ + QDomElement me = element.ownerDocument().createElement( "COLOR" ); + element.appendChild( me ); + + if( m_colorSpace != rgb ) + me.setAttribute( "colorSpace", m_colorSpace ); + if( m_opacity != 1.0 ) + me.setAttribute( "opacity", m_opacity ); + + if( m_colorSpace == gray ) + me.setAttribute( "v", m_value[0] ); + else + { + me.setAttribute( "v1", m_value[0] ); + me.setAttribute( "v2", m_value[1] ); + me.setAttribute( "v3", m_value[2] ); + + if( m_colorSpace == cmyk ) + me.setAttribute( "v4", m_value[3] ); + } +} + +void +VColor::load( const QDomElement& element ) +{ + switch( element.attribute( "colorSpace" ).toUShort() ) + { + case 1: + m_colorSpace = cmyk; break; + case 2: + m_colorSpace = hsb; break; + case 3: + m_colorSpace = gray; break; + default: + m_colorSpace = rgb; + } + + m_opacity = element.attribute( "opacity", "1.0" ).toFloat(); + + if( m_colorSpace == gray ) + m_value[0] = element.attribute( "v", "0.0" ).toFloat(); + else + { + m_value[0] = element.attribute( "v1", "0.0" ).toFloat(); + m_value[1] = element.attribute( "v2", "0.0" ).toFloat(); + m_value[2] = element.attribute( "v3", "0.0" ).toFloat(); + + if( m_colorSpace == cmyk ) + m_value[3] = element.attribute( "v4", "0.0" ).toFloat(); + } + + if( m_value[0] < 0.0 || m_value[0] > 1.0 ) + m_value[0] = 0.0; + if( m_value[1] < 0.0 || m_value[1] > 1.0 ) + m_value[1] = 0.0; + if( m_value[2] < 0.0 || m_value[2] > 1.0 ) + m_value[2] = 0.0; + if( m_value[3] < 0.0 || m_value[3] > 1.0 ) + m_value[3] = 0.0; +} + diff --git a/karbon/core/vcolor.h b/karbon/core/vcolor.h new file mode 100644 index 00000000..cf48370e --- /dev/null +++ b/karbon/core/vcolor.h @@ -0,0 +1,182 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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. +*/ + +#ifndef __VCOLOR_H__ +#define __VCOLOR_H__ + + +#include <qcolor.h> +#include <qstring.h> +#include <koffice_export.h> +class QDomElement; + + +/** + * This class keeps track of color properties. + * The actual color values can be represented in + * rgb and hsv color spaces. Also each color has + * a related opacity value. + * + * Default is opaque, rgb, black color. + */ + +class KARBONBASE_EXPORT VColor +{ +public: + enum VColorSpace + { + rgb = 0, /**< the RGB colorspace (red, green and blue components) */ + cmyk = 1, /**< the CMYK colorspace (cyan, magenta, yellow and black components) */ + hsb = 2, /**< the HSB colorspace (hue, saturation and brightnes components) */ + gray = 3 /**< the Gray colorspace (gray from black to white) */ + }; + + /** + * Constructs a new VColor with the specified colorspace. + * + * @param colorSpace the colorspace of the new color + */ + VColor( VColorSpace colorSpace = rgb ); + + /** + * Constructs a new VColor by copying data from the specified VColor + * + * @param color the color to copy from + */ + VColor( const VColor& color ); + + /** + * Constructs a new VColor by copying data from the specified QColor + * + * @param color the color to copy from + */ + VColor( const QColor& color ); + + /** + * Cast operator to QColor. + */ + operator QColor() const; + + /** + * Index operator to access color components. + * + * @param i the index of the color component to access + * @return the requested color component + */ + float operator[]( unsigned i ) const + { return m_value[i]; } + + /** + * Sets the first color component. + * + * @param v1 the new value of the first color component + */ + void set( float v1 ) + { m_value[0] = v1; } + + /** + * Sets the first and second color component. + * + * @param v1 the new value of the first color component + * @param v2 the new value of the second color component + */ + void set( float v1, float v2 ) + { m_value[0] = v1; m_value[1] = v2; } + + /** + * Sets the first, second and third color component. + * + * @param v1 the new value of the first color component + * @param v2 the new value of the second color component + * @param v3 the new value of the third color component + */ + void set( float v1, float v2, float v3 ) + { m_value[0] = v1; m_value[1] = v2; m_value[2] = v3; } + + /** + * Sets the first, second, third and fourth color component. + * + * @param v1 the new value of the first color component + * @param v2 the new value of the second color component + * @param v3 the new value of the third color component + * @param v4 the new value of the fourth color component + */ + void set( float v1, float v2, float v3, float v4 ) + { m_value[0] = v1; m_value[1] = v2; m_value[2] = v3; m_value[3] = v4; } + + /** + * Returns the color opacity. + * + * Opacity is a value ranging from 0.0 (fully transparent) to 1.0 (opaque). + * + * @return the color opacity + */ + float opacity() const { return m_opacity; } + + /** + * Sets the color opacity. + * + * @param opacity the new color opacity. + */ + void setOpacity( float opacity ) { m_opacity = opacity; } + + /** + * Returns the color's colorspace. + * + * @return the color's colorspace + */ + VColorSpace colorSpace() const { return m_colorSpace; } + + /** + * Sets the color's colorspace. + * + * The color is converted into the new colorspace by setting convert = true. + * + * @param colorSpace the new colorspace + * @param convert controls if color is converted into new colorspace + */ + void setColorSpace( const VColorSpace colorSpace, bool convert = true ); + + /** + * Save this color's state to xml. + * + * @param element the DOM element to which the attributes are saved + */ + void save( QDomElement& element ) const; + + /** + * Load this color's state from xml and initialize it accordingly. + * + * @param element the DOM element from which the attributes are read + */ + void load( const QDomElement& element ); + +private: + void convertToColorSpace( const VColorSpace colorSpace ); + + VColorSpace m_colorSpace; + + float m_value[4]; + float m_opacity; + + QString m_name; +}; + +#endif diff --git a/karbon/core/vcomposite.cc b/karbon/core/vcomposite.cc new file mode 100644 index 00000000..8cd61311 --- /dev/null +++ b/karbon/core/vcomposite.cc @@ -0,0 +1,775 @@ +/* This file is part 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 <qdom.h> +#include <qpainter.h> +#include <qwmatrix.h> +#include <qregexp.h> + +#include <KoPoint.h> +#include <KoRect.h> +#include <KoUnit.h> +#include <KoStore.h> +#include <KoXmlWriter.h> +#include <KoXmlNS.h> +#include <KoGenStyles.h> + +#include "vcomposite.h" +#include "vcomposite_iface.h" +#include "vfill.h" +#include "vpainter.h" +#include "vsegment.h" +#include "vstroke.h" +#include "vvisitor.h" +#include "vpath.h" +#include "commands/vtransformcmd.h" +#include "vdocument.h" + +#include <kdebug.h> + + +VPath::VPath( VObject* parent, VState state ) + : VObject( parent, state ), m_fillRule( winding ) +{ + m_paths.setAutoDelete( true ); + + // add an initial path: + m_paths.append( new VSubpath( this ) ); + + // we need a stroke for boundingBox() at anytime: + m_stroke = new VStroke( this ); + m_fill = new VFill(); + + m_drawCenterNode = false; +} + +VPath::VPath( const VPath& composite ) + : VObject( composite ), SVGPathParser() +{ + m_paths.setAutoDelete( true ); + + VSubpath* path; + + VSubpathListIterator itr( composite.m_paths ); + for( itr.toFirst(); itr.current(); ++itr ) + { + path = itr.current()->clone(); + path->setParent( this ); + m_paths.append( path ); + } + + if ( composite.stroke() ) + setStroke( *composite.stroke() ); + + if ( composite.fill() ) + setFill( *composite.fill() ); + + m_drawCenterNode = false; + m_fillRule = composite.m_fillRule; + m_matrix = composite.m_matrix; +} + +VPath::~VPath() +{ +} + +DCOPObject* VPath::dcopObject() +{ + if ( !m_dcop ) + m_dcop = new VPathIface( this ); + + return m_dcop; +} + + +void +VPath::draw( VPainter* painter, const KoRect *rect ) const +{ + if( + state() == deleted || + state() == hidden || + state() == hidden_locked ) + { + return; + } + + if( rect && !rect->intersects( boundingBox() ) ) + return; + + painter->save(); + + VSubpathListIterator itr( m_paths ); + + // draw simplistic contour: + if( state() == edit ) + { + for( itr.toFirst(); itr.current(); ++itr ) + { + if( !itr.current()->isEmpty() ) + { + painter->newPath(); + painter->setRasterOp( Qt::XorROP ); + painter->setPen( Qt::yellow ); + painter->setBrush( Qt::NoBrush ); + + VSubpathIterator jtr( *( itr.current() ) ); + for( ; jtr.current(); ++jtr ) + { + jtr.current()->draw( painter ); + } + + painter->strokePath(); + } + } + } + else if( state() != edit ) + { + // paint fill: + painter->newPath(); + painter->setFillRule( m_fillRule ); + + for( itr.toFirst(); itr.current(); ++itr ) + { + if( !itr.current()->isEmpty() ) + { + VSubpathIterator jtr( *( itr.current() ) ); + for( ; jtr.current(); ++jtr ) + { + jtr.current()->draw( painter ); + } + } + } + + painter->setRasterOp( Qt::CopyROP ); + painter->setPen( Qt::NoPen ); + painter->setBrush( *fill() ); + painter->fillPath(); + + // draw stroke: + painter->setPen( *stroke() ); + painter->setBrush( Qt::NoBrush ); + painter->strokePath(); + } + + painter->restore(); +} + +const KoPoint& +VPath::currentPoint() const +{ + return m_paths.getLast()->currentPoint(); +} + +bool +VPath::moveTo( const KoPoint& p ) +{ + // Append a new subpath if current subpath is not empty. + if( !m_paths.getLast()->isEmpty() ) + { + VSubpath* path = new VSubpath( this ); + m_paths.append( path ); + } + + return m_paths.getLast()->moveTo( p ); +} + +bool +VPath::lineTo( const KoPoint& p ) +{ + return m_paths.getLast()->lineTo( p ); +} + +bool +VPath::curveTo( + const KoPoint& p1, const KoPoint& p2, const KoPoint& p3 ) +{ + return m_paths.getLast()->curveTo( p1, p2, p3 ); +} + +bool +VPath::curve1To( const KoPoint& p2, const KoPoint& p3 ) +{ + return m_paths.getLast()->curve1To( p2, p3 ); +} + +bool +VPath::curve2To( const KoPoint& p1, const KoPoint& p3 ) +{ + return m_paths.getLast()->curve2To( p1, p3 ); +} + +bool +VPath::arcTo( const KoPoint& p1, const KoPoint& p2, const double r ) +{ + return m_paths.getLast()->arcTo( p1, p2, r ); +} + +void +VPath::close() +{ + m_paths.getLast()->close(); + + // Append a new subpath. + VSubpath* path = new VSubpath( this ); + path->moveTo( currentPoint() ); + m_paths.append( path ); +} + +bool +VPath::isClosed() const +{ + return m_paths.getLast()->isEmpty() || m_paths.getLast()->isClosed(); +} + +void +VPath::combine( const VPath& composite ) +{ + VSubpathListIterator itr( composite.m_paths ); + for( ; itr.current(); ++itr ) + { + combinePath( *( itr.current() ) ); + } +} + +void +VPath::combinePath( const VSubpath& path ) +{ + VSubpath* p = path.clone(); + p->setParent( this ); + + // TODO: do complex inside tests instead: + // Make new segments clock wise oriented: + + m_paths.append( p ); + m_fillRule = fillMode(); +} + +bool +VPath::pointIsInside( const KoPoint& p ) const +{ + // Check if point is inside boundingbox. + if( !boundingBox().contains( p ) ) + return false; + + + VSubpathListIterator itr( m_paths ); + + for( itr.toFirst(); itr.current(); ++itr ) + { + if( itr.current()->pointIsInside( p ) ) + return true; + } + + return false; +} + +bool +VPath::intersects( const VSegment& segment ) const +{ + // Check if boundingboxes intersect. + if( !boundingBox().intersects( segment.boundingBox() ) ) + return false; + + + VSubpathListIterator itr( m_paths ); + + for( itr.toFirst(); itr.current(); ++itr ) + { + if( itr.current()->intersects( segment ) ) + return true; + } + + return false; +} + + +VFillRule +VPath::fillMode() const +{ + return ( m_paths.count() > 1 ) ? evenOdd : winding; +} + +const KoRect& +VPath::boundingBox() const +{ + if( m_boundingBoxIsInvalid ) + { + VSubpathListIterator itr( m_paths ); + itr.toFirst(); + + m_boundingBox = itr.current() ? itr.current()->boundingBox() : KoRect(); + + for( ++itr; itr.current(); ++itr ) + m_boundingBox |= itr.current()->boundingBox(); + + if( !m_boundingBox.isNull() ) + { + // take line width into account: + m_boundingBox.setCoords( + m_boundingBox.left() - 0.5 * stroke()->lineWidth(), + m_boundingBox.top() - 0.5 * stroke()->lineWidth(), + m_boundingBox.right() + 0.5 * stroke()->lineWidth(), + m_boundingBox.bottom() + 0.5 * stroke()->lineWidth() ); + } + m_boundingBoxIsInvalid = false; + } + + return m_boundingBox; +} + +VPath* +VPath::clone() const +{ + return new VPath( *this ); +} + +void +VPath::save( QDomElement& element ) const +{ + if( state() != deleted ) + { + QDomElement me = element.ownerDocument().createElement( "PATH" ); + element.appendChild( me ); + + VObject::save( me ); + + QString d; + saveSvgPath( d ); + me.setAttribute( "d", d ); + + //writeTransform( me ); + + // save fill rule if necessary: + if( !( m_fillRule == evenOdd ) ) + me.setAttribute( "fillRule", m_fillRule ); + } +} + +void +VPath::saveOasis( KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles, int &index ) const +{ + if( state() != deleted ) + { + docWriter->startElement( "draw:path" ); + + QString d; + saveSvgPath( d ); + docWriter->addAttribute( "svg:d", d ); + + double x = boundingBox().x(); + double y = boundingBox().y(); + double w = boundingBox().width(); + double h = boundingBox().height(); + + docWriter->addAttribute( "svg:viewBox", QString( "%1 %2 %3 %4" ).arg( x ).arg( y ).arg( w ).arg( h ) ); + docWriter->addAttributePt( "svg:x", x ); + docWriter->addAttributePt( "svg:y", y ); + docWriter->addAttributePt( "svg:width", w ); + docWriter->addAttributePt( "svg:height", h ); + + VObject::saveOasis( store, docWriter, mainStyles, index ); + + QWMatrix tmpMat; + tmpMat.scale( 1, -1 ); + tmpMat.translate( 0, -document()->height() ); + + QString transform = buildOasisTransform( tmpMat ); + if( !transform.isEmpty() ) + docWriter->addAttribute( "draw:transform", transform ); + + docWriter->endElement(); + } +} + +void +VPath::saveOasisFill( KoGenStyles &mainStyles, KoGenStyle &stylesobjectauto ) const +{ + if( m_fill ) + { + QWMatrix mat; + mat.scale( 1, -1 ); + mat.translate( 0, -document()->height() ); + + // mirror fill before saving + VFill fill( *m_fill ); + fill.transform( mat ); + fill.saveOasis( mainStyles, stylesobjectauto ); + // save fill rule if necessary: + if( !( m_fillRule == evenOdd ) ) + stylesobjectauto.addProperty( "svg:fill-rule", "winding" ); + } +} + +void +VPath::transformByViewbox( const QDomElement &element, QString viewbox ) +{ + if( ! viewbox.isEmpty() ) + { + // allow for viewbox def with ',' or whitespace + QStringList points = QStringList::split( ' ', viewbox.replace( ',', ' ' ).simplifyWhiteSpace() ); + + double w = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "width", QString::null ) ); + double h = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "height", QString::null ) ); + double x = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "x", QString::null ) ); + double y = KoUnit::parseValue( element.attributeNS( KoXmlNS::svg, "y", QString::null ) ); + + QWMatrix mat; + mat.translate( x-KoUnit::parseValue( points[0] ), y-KoUnit::parseValue( points[1] ) ); + mat.scale( w / KoUnit::parseValue( points[2] ) , h / KoUnit::parseValue( points[3] ) ); + VTransformCmd cmd( 0L, mat ); + cmd.visitVPath( *this ); + } +} + +bool +VPath::loadOasis( const QDomElement &element, KoOasisLoadingContext &context ) +{ + setState( normal ); + + QString viewbox; + + if( element.localName() == "path" ) + { + QString data = element.attributeNS( KoXmlNS::svg, "d", QString::null ); + if( data.length() > 0 ) + { + loadSvgPath( data ); + } + + m_fillRule = element.attributeNS( KoXmlNS::svg, "fill-rule", QString::null ) == "winding" ? winding : evenOdd; + + viewbox = element.attributeNS( KoXmlNS::svg, "viewBox", QString::null ); + } + else if( element.localName() == "custom-shape" ) + { + QDomNodeList list = element.childNodes(); + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement e = list.item( i ).toElement(); + if( e.namespaceURI() != KoXmlNS::draw ) + continue; + + if( e.localName() == "enhanced-geometry" ) + { + QString data = e.attributeNS( KoXmlNS::draw, "enhanced-path", QString::null ); + if( ! data.isEmpty() ) + loadSvgPath( data ); + + viewbox = e.attributeNS( KoXmlNS::svg, "viewBox", QString::null ); + } + } + } + } + + transformByViewbox( element, viewbox ); + + QString trafo = element.attributeNS( KoXmlNS::draw, "transform", QString::null ); + if( !trafo.isEmpty() ) + transformOasis( trafo ); + + return VObject::loadOasis( element, context ); +} + +void +VPath::load( const QDomElement& element ) +{ + setState( normal ); + + VObject::load( element ); + + QString data = element.attribute( "d" ); + if( data.length() > 0 ) + { + loadSvgPath( data ); + } + m_fillRule = element.attribute( "fillRule" ) == 0 ? evenOdd : winding; + QDomNodeList list = element.childNodes(); + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement child = list.item( i ).toElement(); + + if( child.tagName() == "PATH" ) + { + VSubpath path( this ); + path.load( child ); + + combinePath( path ); + } + else + { + VObject::load( child ); + } + } + } + + QString trafo = element.attribute( "transform" ); + if( !trafo.isEmpty() ) + transform( trafo ); +} + +void +VPath::loadSvgPath( const QString &d ) +{ + //QTime s;s.start(); + parseSVG( d, true ); + //kdDebug(38000) << "Parsing time : " << s.elapsed() << endl; +} + +void +VPath::saveSvgPath( QString &d ) const +{ + // save paths to svg: + VSubpathListIterator itr( m_paths ); + for( itr.toFirst(); itr.current(); ++itr ) + { + if( !itr.current()->isEmpty() ) + itr.current()->saveSvgPath( d ); + } +} + +void +VPath::svgMoveTo( double x1, double y1, bool ) +{ + moveTo( KoPoint( x1, y1 ) ); +} + +void +VPath::svgLineTo( double x1, double y1, bool ) +{ + lineTo( KoPoint( x1, y1 ) ); +} + +void +VPath::svgCurveToCubic( double x1, double y1, double x2, double y2, double x, double y, bool ) +{ + curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x, y ) ); +} + +void +VPath::svgClosePath() +{ + close(); +} + +void +VPath::accept( VVisitor& visitor ) +{ + visitor.visitVPath( *this ); +} + +void +VPath::transform( const QString &transform ) +{ + VTransformCmd cmd( 0L, parseTransform( transform ) ); + cmd.visitVPath( *this ); +} + +void +VPath::transformOasis( const QString &transform ) +{ + VTransformCmd cmd( 0L, parseOasisTransform( transform ) ); + cmd.visitVPath( *this ); +} + +QWMatrix +VPath::parseTransform( const QString &transform ) +{ + QWMatrix result; + + // Split string for handling 1 transform statement at a time + QStringList subtransforms = QStringList::split(')', transform); + QStringList::ConstIterator it = subtransforms.begin(); + QStringList::ConstIterator end = subtransforms.end(); + for(; it != end; ++it) + { + QStringList subtransform = QStringList::split('(', (*it)); + + subtransform[0] = subtransform[0].stripWhiteSpace().lower(); + subtransform[1] = subtransform[1].simplifyWhiteSpace(); + QRegExp reg("[,( ]"); + QStringList params = QStringList::split(reg, subtransform[1]); + + if(subtransform[0].startsWith(";") || subtransform[0].startsWith(",")) + subtransform[0] = subtransform[0].right(subtransform[0].length() - 1); + + if(subtransform[0] == "rotate") + { + if(params.count() == 3) + { + double x = params[1].toDouble(); + double y = params[2].toDouble(); + + result.translate(x, y); + result.rotate(params[0].toDouble()); + result.translate(-x, -y); + } + else + result.rotate(params[0].toDouble()); + } + else if(subtransform[0] == "translate") + { + if(params.count() == 2) + result.translate(params[0].toDouble(), params[1].toDouble()); + else // Spec : if only one param given, assume 2nd param to be 0 + result.translate(params[0].toDouble() , 0); + } + else if(subtransform[0] == "scale") + { + if(params.count() == 2) + result.scale(params[0].toDouble(), params[1].toDouble()); + else // Spec : if only one param given, assume uniform scaling + result.scale(params[0].toDouble(), params[0].toDouble()); + } + else if(subtransform[0] == "skewx") + result.shear(tan(params[0].toDouble() * VGlobal::pi_180), 0.0F); + else if(subtransform[0] == "skewy") + result.shear(tan(params[0].toDouble() * VGlobal::pi_180), 0.0F); + else if(subtransform[0] == "skewy") + result.shear(0.0F, tan(params[0].toDouble() * VGlobal::pi_180)); + else if(subtransform[0] == "matrix") + { + if(params.count() >= 6) + result.setMatrix(params[0].toDouble(), params[1].toDouble(), params[2].toDouble(), params[3].toDouble(), params[4].toDouble(), params[5].toDouble()); + } + } + + return result; +} + +QWMatrix +VPath::parseOasisTransform( const QString &transform ) +{ + QWMatrix result; + + // Split string for handling 1 transform statement at a time + QStringList subtransforms = QStringList::split(')', transform); + QStringList::ConstIterator it = subtransforms.begin(); + QStringList::ConstIterator end = subtransforms.end(); + for(; it != end; ++it) + { + QStringList subtransform = QStringList::split('(', (*it)); + + subtransform[0] = subtransform[0].stripWhiteSpace().lower(); + subtransform[1] = subtransform[1].simplifyWhiteSpace(); + QRegExp reg("[,( ]"); + QStringList params = QStringList::split(reg, subtransform[1]); + + if(subtransform[0].startsWith(";") || subtransform[0].startsWith(",")) + subtransform[0] = subtransform[0].right(subtransform[0].length() - 1); + + if(subtransform[0] == "rotate") + { + // TODO find out what oo2 really does when rotating, it seems severly broken + if(params.count() == 3) + { + double x = KoUnit::parseValue( params[1] ); + double y = KoUnit::parseValue( params[2] ); + + result.translate(x, y); + // oo2 rotates by radians + result.rotate( params[0].toDouble()*VGlobal::one_pi_180 ); + result.translate(-x, -y); + } + else + { + // oo2 rotates by radians + result.rotate( params[0].toDouble()*VGlobal::one_pi_180 ); + } + } + else if(subtransform[0] == "translate") + { + if(params.count() == 2) + { + double x = KoUnit::parseValue( params[0] ); + double y = KoUnit::parseValue( params[1] ); + result.translate(x, y); + } + else // Spec : if only one param given, assume 2nd param to be 0 + result.translate( KoUnit::parseValue( params[0] ) , 0); + } + else if(subtransform[0] == "scale") + { + if(params.count() == 2) + result.scale(params[0].toDouble(), params[1].toDouble()); + else // Spec : if only one param given, assume uniform scaling + result.scale(params[0].toDouble(), params[0].toDouble()); + } + else if(subtransform[0] == "skewx") + result.shear(tan(params[0].toDouble()), 0.0F); + else if(subtransform[0] == "skewy") + result.shear(tan(params[0].toDouble()), 0.0F); + else if(subtransform[0] == "skewy") + result.shear(0.0F, tan(params[0].toDouble())); + else if(subtransform[0] == "matrix") + { + if(params.count() >= 6) + result.setMatrix(params[0].toDouble(), params[1].toDouble(), params[2].toDouble(), params[3].toDouble(), KoUnit::parseValue( params[4] ), KoUnit::parseValue( params[5] ) ); + } + } + + return result; +} + +QString +VPath::buildSvgTransform() const +{ + return buildSvgTransform( m_matrix ); +} + +QString +VPath::buildSvgTransform( const QWMatrix &mat ) const +{ + QString transform; + if( !mat.isIdentity() ) + { + transform = QString( "matrix(%1, %2, %3, %4, %5, %6)" ).arg( mat.m11() ) + .arg( mat.m12() ) + .arg( mat.m21() ) + .arg( mat.m22() ) + .arg( mat.dx() ) + .arg( mat.dy() ); + } + return transform; +} + +QString +VPath::buildOasisTransform() const +{ + return buildSvgTransform( m_matrix ); +} + +QString +VPath::buildOasisTransform( const QWMatrix &mat ) const +{ + QString transform; + if( !mat.isIdentity() ) + { + transform = QString( "matrix(%1, %2, %3, %4, %5pt, %6pt)" ).arg( mat.m11() ) + .arg( mat.m12() ) + .arg( mat.m21() ) + .arg( mat.m22() ) + .arg( mat.dx() ) + .arg( mat.dy() ); + } + return transform; +} diff --git a/karbon/core/vcomposite.h b/karbon/core/vcomposite.h new file mode 100644 index 00000000..e786294b --- /dev/null +++ b/karbon/core/vcomposite.h @@ -0,0 +1,244 @@ +/* This file is part 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. +*/ + +#ifndef __VCOMPOSITE_H__ +#define __VCOMPOSITE_H__ + + +#include <qptrlist.h> + +#include <KoPoint.h> + +#include "vobject.h" +#include "svgpathparser.h" +#include "vfillrule.h" +#include <koffice_export.h> + +class QDomElement; +class VPainter; +class VSegment; +class VVisitor; +class VSubpath; + +typedef QPtrList<VSubpath> VSubpathList; +typedef QPtrListIterator<VSubpath> VSubpathListIterator; + + +/** + * A composite path consists of one or many subpaths. + */ + +class KARBONBASE_EXPORT VPath : public VObject, SVGPathParser +{ +public: + VPath( VObject* parent, VState state = normal ); + VPath( const VPath& path ); + virtual ~VPath(); + + virtual DCOPObject* dcopObject(); + + /** + * Returns the knot of the last segment of the last subpath. + */ + const KoPoint& currentPoint() const; + + + bool moveTo( const KoPoint& p ); + bool lineTo( const KoPoint& p ); + + /* + curveTo(): + + p1 p2 + O ____ O + : _/ \_ : + :/ \: + x x + currP p3 + */ + + bool curveTo( + const KoPoint& p1, const KoPoint& p2, const KoPoint& p3 ); + + /* + curve1To(): + + p2 + ____ O + __/ \ : + / \: + x x + currP p3 + */ + + bool curve1To( const KoPoint& p2, const KoPoint& p3 ); + + /* + curve2To(): + + p1 + O ____ + : / \__ + :/ \ + x x + currP p3 + */ + + bool curve2To( const KoPoint& p1, const KoPoint& p3 ); + + /** + * A convenience function to aproximate a circular arc with a + * bezier curve. Input: 2 tangent vectors and a radius (same as in PostScript). + */ + + /* + arcTo(): + + p1 x....__--x....x p2 + : _/ + : / + :/ + | + x + | + | + x currP + */ + bool arcTo( const KoPoint& p1, const KoPoint& p2, double r ); + + /** + * Closes the current subpath. + */ + void close(); + + bool isClosed() const; + /** + * Combines two composite paths. For example, the letter "O" is a combination + * of a larger and a smaller ellipitical path. + */ + void combine( const VPath& path ); + + /** + * Adds a path to the composite path. + */ + void combinePath( const VSubpath& path ); + + + /** + * Returns true if point p is located inside the composite. + */ + bool pointIsInside( const KoPoint& p ) const; + + + /** + * Returns true if the segment intersects this composite. + */ + bool intersects( const VSegment& segment ) const; + + + const VSubpathList& paths() const + { + return m_paths; + } + + virtual const KoRect& boundingBox() const; + + + VFillRule fillMode() const; + + // TODO remove these functions. + VFillRule fillRule() const + { + return m_fillRule; + } + + void setFillRule( VFillRule fillRule ) + { + m_fillRule = fillRule; + } + + + virtual void draw( VPainter *painter, const KoRect* rect = 0L ) const; + + bool drawCenterNode() const + { + return m_drawCenterNode; + } + + void setDrawCenterNode( bool drawCenterNode = true ) + { + m_drawCenterNode = drawCenterNode; + } + + + virtual void save( QDomElement& element ) const; + virtual void saveOasis( KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles, int &index ) const; + virtual void load( const QDomElement& element ); + virtual bool loadOasis( const QDomElement &element, KoOasisLoadingContext &context ); + + virtual VPath* clone() const; + + virtual void accept( VVisitor& visitor ); + + void transform( const QString &transform ); + void transformOasis( const QString &transform ); + + static QWMatrix parseTransform( const QString &transform ); + + void transform( const QWMatrix &mat ) + { + m_matrix *= mat; + } + + + void loadSvgPath( const QString & ); + void saveSvgPath( QString & ) const; + +protected: + QString buildSvgTransform() const; + QString buildSvgTransform( const QWMatrix &mat ) const; + QString buildOasisTransform() const; + QString buildOasisTransform( const QWMatrix &mat ) const; + + void transformByViewbox( const QDomElement &element, QString viewbox ); + + /// For svg path data parsing. + virtual void svgMoveTo( double x1, double y1, bool abs = true ); + virtual void svgLineTo( double x1, double y1, bool abs = true ); + virtual void svgCurveToCubic( double x1, double y1, double x2, double y2, double x, double y, bool abs = true ); + virtual void svgClosePath(); + + virtual void saveOasisFill( KoGenStyles &mainStyles, KoGenStyle &stylesojectauto ) const; + QWMatrix parseOasisTransform( const QString &transform ); + +protected: + QWMatrix m_matrix; + +private: + /** + * List of subpaths. + */ + VSubpathList m_paths; + + /// Should a center node be drawn? + bool m_drawCenterNode; + VFillRule m_fillRule : 1; +}; + +#endif diff --git a/karbon/core/vcomposite_iface.cc b/karbon/core/vcomposite_iface.cc new file mode 100644 index 00000000..2cc07c53 --- /dev/null +++ b/karbon/core/vcomposite_iface.cc @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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 "vcomposite_iface.h" +#include "vcomposite.h" + +VPathIface::VPathIface( VPath *composite ) + : VObjectIface( composite ), m_composite( composite ) +{ +} + +bool +VPathIface::moveTo( double x, double y ) +{ + return m_composite->moveTo( KoPoint( x, y ) ); +} + +bool +VPathIface::lineTo( double x, double y ) +{ + return m_composite->lineTo( KoPoint( x, y ) ); +} + +bool +VPathIface::curveTo( double x1, double y1, double x2, double y2, double x3, double y3 ) +{ + return m_composite->curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x3, y3 ) ); +} + +bool +VPathIface::curve1To( double x2, double y2, double x3, double y3 ) +{ + return m_composite->curve1To( KoPoint( x2, y2 ), KoPoint( x3, y3 ) ); +} + +bool +VPathIface::curve2To( double x1, double y1, double x2, double y2 ) +{ + return m_composite->curve2To( KoPoint( x1, y1 ), KoPoint( x2, y2 ) ); +} + +bool +VPathIface::arcTo( double x1, double y1, double x2, double y2, double r ) +{ + return m_composite->arcTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), r ); +} + +void +VPathIface::close() +{ + m_composite->close(); +} + diff --git a/karbon/core/vcomposite_iface.h b/karbon/core/vcomposite_iface.h new file mode 100644 index 00000000..6295047c --- /dev/null +++ b/karbon/core/vcomposite_iface.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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. +*/ + +#ifndef __VCOMPOSITE_IFACE_H__ +#define __VCOMPOSITE_IFACE_H__ + +#include "vobject_iface.h" + +class VPath; + +class VPathIface : public VObjectIface +{ + K_DCOP + +public: + VPathIface( VPath *composite ); + +k_dcop: + bool moveTo( double x, double y ); + bool lineTo( double x, double y ); + bool curveTo( double x1, double y1, double x2, double y2, double x3, double y3 ); + bool curve1To( double x2, double y2, double x3, double y3 ); + bool curve2To( double x1, double y1, double x2, double y2 ); + bool arcTo( double x1, double y1, double x2, double y2, double r ); + void close(); + + //bool drawCenterNode() const; + //void setDrawCenterNode( bool drawCenterNode = true ); + +private: + VPath *m_composite; +}; + +#endif diff --git a/karbon/core/vcursor.cc b/karbon/core/vcursor.cc new file mode 100644 index 00000000..ed9d1f8d --- /dev/null +++ b/karbon/core/vcursor.cc @@ -0,0 +1,166 @@ +/* This file is part of the KDE project + Copyright (C) 2006 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 "vcursor.h" +#include <qbitmap.h> + +static const char* const cminus[] = { +"16 16 6 1", +" c Gray0", +". c #939393", +"X c Gray63", +"o c #aeaeae", +"O c None", +"+ c Gray100", +"OOOOo XXoOOOO", +"OOo ++++ XoOOO", +"OO ++++++++ XoOO", +"Oo ++++++++ XXoO", +"O ++++++++++ XoO", +"O ++ ++ XoO", +"O ++ ++ XoO", +"O ++++++++++ XoO", +"Oo ++++++++ .oOO", +"OO ++++++++ .oOO", +"OOo ++++ .oOO", +"OOOOo O XoO", +"OOOOOOOOOOO Xo", +"OOOOOOOOOOOO X", +"OOOOOOOOOOOOO ", +"OOOOOOOOOOOOOO " +}; + +static const char* const cplus[] = { +"16 16 6 1", +" c Gray0", +". c #939393", +"X c Gray63", +"o c #aeaeae", +"O c None", +"+ c Gray100", +"OOOo XXoOOOOO", +"Oo ++++ XoOOOO", +"O ++++++++ XoOOO", +"o +++ +++ XXoOO", +" ++++ ++++ XoOO", +" ++ ++ XoOO", +" ++ ++ XoOO", +" ++++ ++++ XoOO", +"o +++ +++ .oOOO", +"O ++++++++ .oOOO", +"Oo ++++ .oOOO", +"OOOo O XoOO", +"OOOOOOOOOO XoO", +"OOOOOOOOOOO XO", +"OOOOOOOOOOOO O", +"OOOOOOOOOOOOO O" +}; + +QCursor VCursor::createCursor( CursorType type ) +{ + switch( type ) + { + case CrossHair: + return crossHair(); + break; + case ZoomPlus: + return QCursor( QPixmap( ( const char**) cplus ), -1, -1 ); + break; + case ZoomMinus: + return QCursor( QPixmap( ( const char**) cminus ), -1, -1 ); + break; + case NeedleArrow: + return needleArrow(); + break; + default: return QCursor( Qt::arrowCursor ); + } +} + +QCursor VCursor::createCursor( const char * bitmap[], const char * mask[], int hotX, int hotY ) +{ + // the cursor bitmap and mask + QBitmap b, m; + + b = QPixmap( (const char**) bitmap ); + m = QPixmap( (const char**) mask ); + + return QCursor( b, m, hotX, hotY ); +} + +QCursor VCursor::crossHair() +{ + static unsigned char cross_bits[] = { + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0xff, 0x7f, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00}; + + QBitmap b = QBitmap( 15, 15, cross_bits, true ); + QBitmap m = b.createHeuristicMask( false ); + + return QCursor( b, m, 7, 7 ); +} + +QCursor VCursor::needleArrow() +{ + static unsigned char needle_bits[] = { + 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x01, + 0x80, 0x03, 0x80, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3e, 0x00, 0x7e, + 0x00, 0x7c, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x00}; + + QBitmap b = QBitmap( 16, 16, needle_bits, true ); + QBitmap m = b.createHeuristicMask( false ); + + return QCursor( b, m, 2, 0 ); +} + +QCursor VCursor::needleMoveArrow() +{ + static unsigned char needle_move_bits[] = { + 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x01, + 0x80, 0x03, 0x80, 0x07, 0x10, 0x0f, 0x38, 0x1f, 0x54, 0x3e, 0xfe, 0x7e, + 0x54, 0x7c, 0x38, 0x1c, 0x10, 0x18, 0x00, 0x00}; + + QBitmap b = QBitmap( 16, 16, needle_move_bits, true ); + QBitmap m = b.createHeuristicMask( false ); + + return QCursor( b, m, 2, 0 ); +} + +QCursor VCursor::horzMove() +{ +/* + #define horzMove_width 15 + #define horzMove_height 15 + static unsigned char horzMove_bits[] = { + 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x90, 0x04, + 0x98, 0x0c, 0xfc, 0x1f, 0x98, 0x0c, 0x90, 0x04, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x00, 0x00}; +*/ + #define horzMove_width 15 + #define horzMove_height 15 + static unsigned char horzMove_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, + 0x0c, 0x18, 0xfe, 0x3f, 0x0c, 0x18, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + QBitmap b = QBitmap( 15, 15, horzMove_bits, true ); + QBitmap m = b.createHeuristicMask( false ); + + return QCursor( b, m, 7, 7 ); +} diff --git a/karbon/core/vcursor.h b/karbon/core/vcursor.h new file mode 100644 index 00000000..84c89648 --- /dev/null +++ b/karbon/core/vcursor.h @@ -0,0 +1,74 @@ +/* This file is part of the KDE project + Copyright (C) 2006 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. +*/ + +#ifndef __VCURSOR_H__ +#define __VCURSOR_H__ + +#include <qcursor.h> +#include <koffice_export.h> + +/** +* A helper class for easily creating cursors from XPMs. +* +* One can create a predefined unthemed cursor or create a cursor from two given XMPs, +* the cursor bitmap and the cursor mask. +*/ +class KARBONBASE_EXPORT VCursor +{ +public: + /** Predefined cursor types */ + enum CursorType + { + CrossHair = 0, /**< unthemed crosshair cursor */ + ZoomPlus = 1, /**< zoom in cursor */ + ZoomMinus = 2, /**< zoom out cursor */ + NeedleArrow = 3 /**< needle arrow */ + }; + + /** + * Creates a predefined cursor of the specified type. + * + * @param type the requested cursor id + * @return the predefined cursor + */ + static QCursor createCursor( CursorType type ); + + /** + * Creates a cursor from two specified XPM images. + * This is only a wrapper function for a QCursor ctor. + */ + static QCursor createCursor( const char * bitmap[], const char * mask[], int hotX = -1, int hotY = -1 ); + + /** crosshair cursor */ + static QCursor crossHair(); + + /** needle arraow cursor */ + static QCursor needleArrow(); + + /** needle arrow with four way arrow */ + static QCursor needleMoveArrow(); + + static QCursor horzMove(); + +private: + // prevent instantiation + VCursor() {}; +}; + +#endif diff --git a/karbon/core/vdashpattern.cc b/karbon/core/vdashpattern.cc new file mode 100644 index 00000000..e758e824 --- /dev/null +++ b/karbon/core/vdashpattern.cc @@ -0,0 +1,76 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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 <qdom.h> + +#include "vdashpattern.h" + +VDashPattern::VDashPattern( double offset ) + : m_offset( offset ) +{ +} + +void +VDashPattern::save( QDomElement& element ) const +{ + if( m_array.size() != 0 ) + { + QDomElement me = element.ownerDocument().createElement( "DASHPATTERN" ); + element.appendChild( me ); + + if( m_offset != 0.0 ) + me.setAttribute( "offset", m_offset ); + + QDomElement dash; + + QValueListConstIterator<float> itr; + for( itr = m_array.begin(); itr != m_array.end(); ++itr ) + { + dash = element.ownerDocument().createElement( "DASH" ); + me.appendChild( dash ); + dash.setAttribute( "l", *( itr ) ); + } + } +} + +void +VDashPattern::load( const QDomElement& element ) +{ + m_offset = element.attribute( "offset", "0.0" ).toDouble(); + + float value; + + QDomNodeList list = element.childNodes(); + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement e = list.item( i ).toElement(); + if( e.tagName() == "DASH" ) + { + value = e.attribute( "l", "0.0" ).toFloat(); + if( value < 0.0 ) + value = 0.0; + + m_array.append( value ); + } + } + } +} + diff --git a/karbon/core/vdashpattern.h b/karbon/core/vdashpattern.h new file mode 100644 index 00000000..1abf4e41 --- /dev/null +++ b/karbon/core/vdashpattern.h @@ -0,0 +1,62 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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. +*/ + +#ifndef __VDASHPATTERN_H__ +#define __VDASHPATTERN_H__ + +#include <qvaluelist.h> +#include <koffice_export.h> + +class QDomElement; + + +/** + * The dash pattern consistes of a sequence of on/off values. + * For example 10 5 5 10 would result in a dash of 10 pixels, + * next 5 pixels no dash, then 5 pixels of dashing, finally 10 pixels + * of no dash. This sequence is repeated until the whole outline is dashed. + * + * Also it supports an offset value for when to start the dashing. + * + * Default is no dashes. + */ + +class KARBONBASE_EXPORT VDashPattern +{ +public: + VDashPattern( double dashOffset = 0.0 ); + + const QValueList<float>& array() const { return m_array; } + void setArray( const QValueList<float>& array ) + { m_array = array; } + + // dash offset: + float offset() const { return m_offset; } + void setOffset( float offset ) { m_offset = offset; } + + void save( QDomElement& element ) const; + void load( const QDomElement& element ); + +private: + QValueList<float> m_array; + float m_offset; +}; + +#endif + diff --git a/karbon/core/vdocument.cc b/karbon/core/vdocument.cc new file mode 100644 index 00000000..d87ad368 --- /dev/null +++ b/karbon/core/vdocument.cc @@ -0,0 +1,323 @@ +/* This file is part 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 <qdom.h> + +#include "vdocument.h" +#include "vselection.h" +#include "vvisitor.h" +#include "vlayer.h" +#include "vstroke.h" +#include "vdashpattern.h" +#include "vpainter.h" + +#include <KoStore.h> +#include <KoPageLayout.h> +#include <KoXmlWriter.h> + +#include <kdebug.h> + +VDocument::VDocument() + : VObject( 0L ), + m_width(0.), m_height(0.), + m_selectionMode( VDocument::ActiveLayer ), + m_unit( KoUnit::U_MM ) +{ + m_selection = new VSelection( this ); + + // create a layer. we need at least one: + m_layers.setAutoDelete( true ); + m_layers.append( new VLayer( this ) ); + m_activeLayer = m_layers.getLast(); + m_activeLayer->setSelected( true ); + + m_saveAsPath = true; +} + +VDocument::VDocument( const VDocument& document ) + : VObject( document ), m_width(0), m_height(0) +{ + m_selection = new VSelection( this ); +// TODO +} + +VDocument::~VDocument() +{ + delete( m_selection ); +} + +void +VDocument::drawPage( VPainter *p, const KoPageLayout &pl, bool showPageMargins ) const +{ + p->setPen( Qt::black ); + p->setBrush( Qt::white ); + p->drawRect( 0, 0, m_width, m_height ); + + p->setPen( Qt::NoPen ); + p->setBrush( Qt::black ); + p->drawRect( m_width, - 2, 2, m_height ); + p->drawRect( 0, - 2, m_width, 2 ); + //p->drawRect( 0, m_height - 1, m_width, 1 ); + // Draw Grid + if( m_gridData.isShow ) + { + VStroke s( 0, 1 ); + s.setColor( m_gridData.color ); + double dx = m_gridData.freq.width(); + double dy = m_gridData.freq.height(); + p->setPen( s ); + p->setBrush( Qt::NoBrush ); + KoPoint p0( dx, dy ); + while( p0.x() < m_width ) + { + p->newPath(); + p->moveTo( KoPoint( p0.x(), 0 ) ); + p->lineTo( KoPoint( p0.x(), m_height ) ); + p->strokePath(); + + p0.rx() += dx; + } + while( p0.y() < m_height ) + { + p->newPath(); + p->moveTo( KoPoint( 0, p0.y() ) ); + p->lineTo( KoPoint( m_width, p0.y() ) ); + p->strokePath(); + + p0.ry() += dy; + } + } + // Draw page margins + if( showPageMargins ) + { + int ml = int( pl.ptLeft ); + int mt = int( pl.ptTop ); + int mr = int( pl.ptRight ); + int mb = int( pl.ptBottom ); + + VStroke s( 0, 1 ); + s.setColor( Qt::blue ); + QValueList<float> dashes; + s.dashPattern().setArray( dashes << 5 << 5 ); + p->setPen( s ); + p->setBrush( Qt::NoBrush ); + p->drawRect(ml, mt, m_width-ml-mr, m_height-mt-mb); + } +} + +void +VDocument::draw( VPainter *painter, const KoRect* rect ) const +{ + QPtrListIterator<VLayer> itr = m_layers; + + for ( ; itr.current(); ++itr ) + { + itr.current()->draw( painter, rect ); + } +} + +void +VDocument::insertLayer( VLayer* layer ) +{ +// if ( pos == -1 || !m_layers.insert( layer, pos )) + m_layers.append( layer ); + m_activeLayer = layer; +} // VDocument::insertLayer + +void +VDocument::removeLayer( VLayer* layer ) +{ + m_layers.remove( layer ); + if ( m_layers.count() == 0 ) + m_layers.append( new VLayer( this ) ); + m_activeLayer = m_layers.getLast(); +} // VDocument::removeLayer + +bool VDocument::canRaiseLayer( VLayer* layer ) +{ + int pos = m_layers.find( layer ); + return (pos != int( m_layers.count() ) - 1 && pos >= 0 ); +} + +bool VDocument::canLowerLayer( VLayer* layer ) +{ + int pos = m_layers.find( layer ); + return (pos>0); +} + +void +VDocument::raiseLayer( VLayer* layer ) +{ + int pos = m_layers.find( layer ); + if( pos != int( m_layers.count() ) - 1 && pos >= 0 ) + { + VLayer* layer = m_layers.take( pos ); + m_layers.insert( pos + 1, layer ); + } +} // VDocument::raiseLayer + +void +VDocument::lowerLayer( VLayer* layer ) +{ + int pos = m_layers.find( layer ); + if ( pos > 0 ) + { + VLayer* layer = m_layers.take( pos ); + m_layers.insert( pos - 1, layer ); + } +} // VDocument::lowerLayer + +int +VDocument::layerPos( VLayer* layer ) +{ + return m_layers.find( layer ); +} // VDocument::layerPos + +void +VDocument::setActiveLayer( VLayer* layer ) +{ + if ( m_layers.find( layer ) != -1 ) + m_activeLayer = layer; +} // VDocument::setActiveLayer + +void +VDocument::append( VObject* object ) +{ + m_activeLayer->append( object ); +} + +QDomDocument +VDocument::saveXML() const +{ + QDomDocument doc; + QDomElement me = doc.createElement( "DOC" ); + doc.appendChild( me ); + save( me ); + return doc; +} + +void +VDocument::saveOasis( KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles ) const +{ + docWriter->startElement( "draw:page" ); + docWriter->addAttribute( "draw:name", name()); + docWriter->addAttribute( "draw:id", "page1"); + docWriter->addAttribute( "draw:master-page-name", "Default"); + + // save objects: + VLayerListIterator itr( m_layers ); + + int index = 0; + for ( ; itr.current(); ++itr ) + itr.current()->saveOasis( store, docWriter, mainStyles, ++index ); + + docWriter->endElement(); // draw:page +} + +void +VDocument::save( QDomElement& me ) const +{ + me.setAttribute( "mime", "application/x-karbon" ), + me.setAttribute( "version", "0.1" ); + me.setAttribute( "editor", "Karbon14" ); + me.setAttribute( "syntaxVersion", "0.1" ); + if( m_width > 0. ) + me.setAttribute( "width", m_width ); + if( m_height > 0. ) + me.setAttribute( "height", m_height ); + me.setAttribute( "unit", KoUnit::unitName( m_unit ) ); + + // save objects: + VLayerListIterator itr( m_layers ); + + for ( ; itr.current(); ++itr ) + itr.current()->save( me ); +} + + +VDocument* +VDocument::clone() const +{ + return new VDocument( *this ); +} + +void +VDocument::load( const QDomElement& doc ) +{ + loadXML( doc ); +} + +bool +VDocument::loadXML( const QDomElement& doc ) +{ + if( doc.attribute( "mime" ) != "application/x-karbon" || + doc.attribute( "syntaxVersion" ) != "0.1" ) + { + return false; + } + + m_layers.clear(); + + m_width = doc.attribute( "width", "800.0" ).toDouble(); + m_height = doc.attribute( "height", "550.0" ).toDouble(); + + m_unit = KoUnit::unit( doc.attribute( "unit", KoUnit::unitName( m_unit ) ) ); + + loadDocumentContent( doc ); + return true; +} + +void +VDocument::loadDocumentContent( const QDomElement& doc ) +{ + QDomNodeList list = doc.childNodes(); + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement e = list.item( i ).toElement(); + + if( e.tagName() == "LAYER" ) + { + VLayer* layer = new VLayer( this ); + layer->load( e ); + insertLayer( layer ); + } + } + } +} + +bool +VDocument::loadOasis( const QDomElement &element, KoOasisLoadingContext &context ) +{ + return m_layers.current()->loadOasis( element, context ); +} + +void +VDocument::accept( VVisitor& visitor ) +{ + visitor.visitVDocument( *this ); +} + +QString +VDocument::objectName( const VObject *obj ) const +{ + QMap<const VObject *, QString>::ConstIterator it = m_objectNames.find( obj ); + return it == m_objectNames.end() ? 0L : it.data(); +} diff --git a/karbon/core/vdocument.h b/karbon/core/vdocument.h new file mode 100644 index 00000000..577dd1c2 --- /dev/null +++ b/karbon/core/vdocument.h @@ -0,0 +1,327 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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. +*/ + +#ifndef VDOCUMENT_H +#define VDOCUMENT_H + +#include <KoUnit.h> + +#include <qstring.h> +#include <qptrlist.h> +#include <qptrdict.h> + +#include "vobject.h" + +#include "karbon_grid_data.h" +#include <koffice_export.h> + +class QDomDocument; +class QDomElement; +class VSelection; +class VLayer; +class KoPageLayout; + +typedef QPtrList<VLayer> VLayerList; +typedef QPtrListIterator<VLayer> VLayerListIterator; + + +/** + * All non-visual, static doc info is in here. + * The karbon part uses this class. + * Filters can use this class as well instead of + * the visually oriented karbon part. + */ + +class KARBONBASE_EXPORT VDocument : public VObject +{ +public: + /** The different selection modes */ + enum VSelectionMode { + ActiveLayer, /**< selection within the active layer */ + VisibleLayers, /**< selection within all visible layers */ + SelectedLayers, /**< selection within all selected layers */ + AllLayers /**< selection within all layers */ + }; + + /** + * Constructs a new document. + */ + VDocument(); + + /** + * Copy constructor. + * + * @param document the document to copy properties from + */ + VDocument( const VDocument& document ); + + /** + * Destroys the document and all of the layers. + */ + virtual ~VDocument(); + + virtual void draw( VPainter* painter, const KoRect* rect ) const; + + /** + * Draw the document frame to a painting device. + * + * @param painter abstraction that is used to render to a painting device. + * @param pl layout describing the page to draw on (restricting the painter) + * @param drawPageMargins if @c true, also draw the crop marks for the page margins, + * otherwise, don't draw them. + */ + void drawPage( VPainter *painter, const KoPageLayout &pl, bool drawPageMargins ) const; + + /** + * Returns document width. + * + * @return the document's width + */ + double width() const { return m_width; } + + /** + * Returns document height. + * + * @return the document's height + */ + double height() const { return m_height; } + + /** + * Sets document width. + * + * @param width the new document width + */ + void setWidth( double width ) { m_width = width; m_boundingBox.setWidth( width ); } + + /** + * Sets document height. + * + * @param height the new document height + */ + void setHeight( double height ) { m_height = height; m_boundingBox.setHeight( height ); } + + /** + * Returns document unit. + * + * @return the document's unit + */ + KoUnit::Unit unit() const + { return m_unit; } + + /** + * Sets document unit. + * + * @param unit the new document unit + */ + void setUnit( KoUnit::Unit unit ) + { m_unit = unit; } + + /** + * Checks if specified layer can be raised. + * + * A layer can be raised if there is more than one layer and the specified layer + * is not already at the top. + * + * @param layer the layer to check + * @return true if layer can be raised, else false + */ + bool canRaiseLayer( VLayer* layer ); + + /** + * Checks if specified layer can be lowered. + * + * A layer can be lowered if there is more than one layer and the specified layer + * is not already at the bottom. + * + * @param layer the layer to check + * @return true if layer can be lowered, else false + */ + bool canLowerLayer( VLayer* layer ); + + /** + * Raises the layer. + * + * @param layer the layer to raise + */ + void raiseLayer( VLayer* layer ); + + /** + * Lowers the layer. + * + * @param layer the layer to lower + */ + void lowerLayer( VLayer* layer ); + + /** + * Returns the position of the specified layer. + * + * @param layer the layer to retrieve the position for + * @return the layer position + */ + int layerPos( VLayer* layer ); + + /** + * Inserts a new layer. + * + * The layer is appended at the end, on top of all other layers, and is activated. + * + * @param layer the layer to insert + */ + void insertLayer( VLayer* layer ); + + /** + * Removes the layer. + * + * If there is no layer left, a new layer is created, inserted and activated. + * + * @param layer the layer to remove + */ + void removeLayer( VLayer* layer ); + + /** + * Sets the active layer. + * + * The specified layer is set active, if it is found in the layer list. + * + * @param layer the layer to set active + */ + void setActiveLayer( VLayer* layer ); + + /** + * Returns a pointer to the active layer. + * + * @return the currently active layer + */ + VLayer* activeLayer() const { return m_activeLayer; } + + /** + * Returns the list of layers. + */ + const VLayerList& layers() const { return m_layers; } + + QDomDocument saveXML() const; + virtual void saveOasis( KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles ) const; + enum { STYLE_GRAPHICAUTO = 20, STYLE_LINEAR_GRADIENT, STYLE_RADIAL_GRADIENT, STYLE_STROKE }; + bool loadXML( const QDomElement& doc ); + virtual bool loadOasis( const QDomElement &element, KoOasisLoadingContext &context ); + virtual void save( QDomElement& element ) const; + virtual void load( const QDomElement& element ); + void loadDocumentContent( const QDomElement& doc ); + + virtual VDocument* clone() const; + + virtual void accept( VVisitor& visitor ); + + + /** + * Returns a pointer to the selection. + * + * @return the document's selection + */ + VSelection* selection() const + { return m_selection; } + + /** + * Returns the selection mode. + * + * @return the actual selection mode + */ + VSelectionMode selectionMode() { return m_selectionMode; } + + /** + * Sets the selection mode. + * + * @param mode the new selection mode + */ + void setSelectionMode( VSelectionMode mode ) { m_selectionMode = mode; } + + /** + * Appends a new object to the active layer. + * + * @param object the object to append + */ + void append( VObject* object ); + + /** + * Returns custom name of specified object. + * + * @param obj the object to retrieve name for + * @return the custom name of the object or an empty string if no custom name is set + */ + QString objectName( const VObject *obj ) const; + + /** + * Sets custom name of specified object. + * + * By default all object have generic names like path, rectangle or text that + * is defined within the object's class. + * + * @param obj the object to set custom name for + * @param name the the custom name to set + */ + void setObjectName( const VObject *obj, const QString name ) { m_objectNames.insert( obj, name ); } + + bool saveAsPath() const { return m_saveAsPath; } + void saveAsPath( bool b ) { m_saveAsPath = b; } + + /** + * Returns the document's grid. + * + * @return the doument's grid + */ + KarbonGridData &grid() { return m_gridData; } + +private: + /** + * Document width. + */ + double m_width; + + /** + * Document height. + */ + double m_height; + + + /// The layers in this document. + VLayerList m_layers; + /// The active layer. + VLayer* m_activeLayer; + + /// The selection. A list of selected objects. + VSelection* m_selection; + /// The selectionMode + VSelectionMode m_selectionMode; + + /** + * The unit. + */ + KoUnit::Unit m_unit; + + QMap<const VObject *, QString> m_objectNames; + + // TODO this flag is used nowhere, can we remove it? + bool m_saveAsPath; + + KarbonGridData m_gridData; +}; + +#endif + diff --git a/karbon/core/vfill.cc b/karbon/core/vfill.cc new file mode 100644 index 00000000..44f69f75 --- /dev/null +++ b/karbon/core/vfill.cc @@ -0,0 +1,181 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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 <qdom.h> +#include <kdebug.h> + +#include <KoGenStyles.h> +#include <KoOasisLoadingContext.h> +#include <KoOasisStyles.h> +#include <KoXmlNS.h> + +#include "vfill.h" + +VFill::VFill() + : m_type( none ) +{ + /*m_gradient.addStop( VColor( Qt::red.rgb() ), 0.0 ); + m_gradient.addStop( VColor( Qt::yellow.rgb() ), 1.0 ); + m_gradient.setOrigin( KoPoint( 0, 0 ) ); + m_gradient.setVector( KoPoint( 0, 50 ) ); + m_gradient.setSpreadMethod( gradient_spread_reflect );*/ + //kdDebug(38000) << "Size of VFill : " << sizeof(*this) << endl; +} + +VFill::VFill( const VColor &c ) + : m_type( solid ) +{ + m_color = c; + //kdDebug(38000) << "Size of VFill : " << sizeof(*this) << endl; +} + +VFill::VFill( const VFill& fill ) +{ + // doesn't copy parent: + *this = fill; +} + +void +VFill::save( QDomElement& element ) const +{ + QDomElement me = element.ownerDocument().createElement( "FILL" ); + element.appendChild( me ); + + if( !( m_type == none ) ) + { + // save color: + m_color.save( me ); + } + if( m_type == grad ) + { + // save gradient: + m_gradient.save( me ); + } + else if( m_type == patt ) + { + // save pattern: + m_pattern.save( me ); + } +} + +void +VFill::saveOasis( KoGenStyles &mainStyles, KoGenStyle &style ) const +{ + if( m_type == solid ) + { + style.addProperty( "draw:fill", "solid" ); + style.addProperty( "draw:fill-color", QColor( m_color ).name() ); + if( m_color.opacity() < 1 ) + style.addProperty( "draw:opacity", QString( "%1%" ).arg( m_color.opacity() * 100. ) ); + } + else if( m_type == grad ) + { + style.addProperty( "draw:fill", "gradient" ); + QString grad = m_gradient.saveOasis( mainStyles ); + style.addProperty( "draw:fill-gradient-name", grad ); + if( m_color.opacity() < 1 ) + style.addProperty( "draw:opacity", QString( "%1%" ).arg( m_color.opacity() * 100. ) ); + } + else if( m_type == patt ) + style.addProperty( "draw:fill", "hatch" ); + else + style.addProperty( "draw:fill", "none" ); +} + +void +VFill::loadOasis( const QDomElement &/*object*/, KoOasisLoadingContext &context, VObject* parent ) +{ + KoStyleStack &stack = context.styleStack(); + if( stack.hasAttributeNS( KoXmlNS::draw, "fill" ) ) + { + if( stack.attributeNS( KoXmlNS::draw, "fill" ) == "solid" ) + { + setType( VFill::solid ); + setColor( QColor( stack.attributeNS( KoXmlNS::draw, "fill-color" ) ) ); + } + else if( stack.attributeNS( KoXmlNS::draw, "fill" ) == "gradient" ) + { + setType( VFill::grad ); + QString style = stack.attributeNS( KoXmlNS::draw, "fill-gradient-name" ); + kdDebug()<<" style gradient name :"<<style<<endl; + QDomElement *grad = context.oasisStyles().drawStyles()[ style ]; + kdDebug()<<" style gradient name :"<< grad <<endl; + if( grad ) + m_gradient.loadOasis( *grad, stack, parent ); + } + if( stack.hasAttributeNS( KoXmlNS::draw, "opacity" ) ) + m_color.setOpacity( stack.attributeNS( KoXmlNS::draw, "opacity" ).remove( '%' ).toFloat() / 100. ); + } +} + +void +VFill::load( const QDomElement& element ) +{ + m_type = none; + + // load color: + QDomNodeList list = element.childNodes(); + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement e = list.item( i ).toElement(); + if( e.tagName() == "COLOR" ) + { + m_type = solid; + m_color.load( e ); + } + if( e.tagName() == "GRADIENT" ) + { + m_type = grad; + m_gradient.load( e ); + } + else if( e.tagName() == "PATTERN" ) + { + m_type = patt; + m_pattern.load( e ); + } + } + } +} + +VFill& +VFill::operator=( const VFill& fill ) +{ + if( this != &fill ) + { + // dont copy the parent! + m_type = fill.m_type; + m_color = fill.m_color; + m_gradient = fill.m_gradient; + m_pattern = fill.m_pattern; + } + + return *this; +} + +void +VFill::transform( const QWMatrix& m ) +{ + if( type() == VFill::grad ) + gradient().transform( m ); + else if( type() == VFill::patt ) + pattern().transform( m ); +} diff --git a/karbon/core/vfill.h b/karbon/core/vfill.h new file mode 100644 index 00000000..67a31722 --- /dev/null +++ b/karbon/core/vfill.h @@ -0,0 +1,90 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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. +*/ + +#ifndef __VFILL_H__ +#define __VFILL_H__ + +#include "vcolor.h" +#include "vgradient.h" +#include "vpattern.h" +#include <koffice_export.h> + +class QDomElement; +class KoGenStyle; +class KoGenStyles; +class KoOasisLoadingContext; + + +/** + * Manages the fill of shapes. + * + * The fill can be solid or gradient. + * Also two fill rules are supported that effect how the shape is + * filled. For explanation see the QPainter documentation. + * + * Default is no fill and even-odd filling rule. + */ +class KARBONBASE_EXPORT VFill +{ +public: + enum VFillType + { + none = 0, /// no fill at all + solid = 1, /// solid fill + grad = 2, /// gradient fill + patt = 3, /// pattern fill + unknown = 4 + }; + + VFill(); + VFill( const VColor & ); + VFill( const VFill & ); + + const VColor& color() const { return m_color; } + void setColor( const VColor& color, bool bsolid = true ) { m_color = color; if( bsolid ) m_type = solid; } + + VGradient& gradient() { return m_gradient; } + const VGradient& gradient() const { return m_gradient; } + + VPattern& pattern() { return m_pattern; } + const VPattern& pattern() const { return m_pattern; } + + VFillType type() const { return m_type; } + void setType( VFillType type ) { m_type = type; } + + void save( QDomElement& element ) const; + void saveOasis( KoGenStyles &mainStyles, KoGenStyle &style ) const; + void load( const QDomElement& element ); + void loadOasis( const QDomElement &object, KoOasisLoadingContext &context, VObject* parent = 0L ); + + VFill& operator=( const VFill& fill ); + + void transform( const QWMatrix& m ); + +private: + VColor m_color; + VGradient m_gradient; + VPattern m_pattern; + + VFillType m_type : 3; +}; + +#endif + diff --git a/karbon/core/vfillrule.h b/karbon/core/vfillrule.h new file mode 100644 index 00000000..e6af3606 --- /dev/null +++ b/karbon/core/vfillrule.h @@ -0,0 +1,31 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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. +*/ + +#ifndef __VFILLRULE_H__ +#define __VFILLRULE_H__ + +enum VFillRule +{ + evenOdd = 0, + winding = 1 +}; + +#endif + diff --git a/karbon/core/vglobal.cc b/karbon/core/vglobal.cc new file mode 100644 index 00000000..b118e5e2 --- /dev/null +++ b/karbon/core/vglobal.cc @@ -0,0 +1,90 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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 <math.h> + +#include "vglobal.h" + + +int +VGlobal::binomialCoeff( unsigned n, unsigned k ) +{ + return + static_cast<int>( + 0.5 + + exp( + factorialLn( n ) - + factorialLn( k ) - + factorialLn( n - k ) ) ); +} + +double +VGlobal::factorialLn( unsigned n ) +{ + const unsigned cacheSize = 100; + + // A static array is initalized to zero. + static double cache[ cacheSize ]; + + + if( n <= 1 ) + return 0.0; + + if( n <= cacheSize - 1 ) + { + return cache[ n ] + ? cache[ n ] + : ( cache[ n ] = gammaLn( n + 1.0 ) ); + } + else + { + return gammaLn( n + 1.0 ); + } +} + +double +VGlobal::gammaLn( double x ) +{ + static double coeff[ 6 ] = + { + 76.18009172947146, + -86.50532032941677, + 24.01409824083091, + -1.231739572450155, + 0.1208650973866179e-2, + -0.5395239384953e-5 + }; + + double y = x; + + double tmp = x + 5.5; + tmp -= ( x + 0.5 ) * log( tmp ); + + double ser = 1.000000000190015; + + for( int i = 0; i < 5; ++i ) + { + ser += coeff[ i ] / ++y; + } + + return -tmp + log( 2.5066282746310005 * ser / x ); +} + diff --git a/karbon/core/vglobal.h b/karbon/core/vglobal.h new file mode 100644 index 00000000..c20d48b0 --- /dev/null +++ b/karbon/core/vglobal.h @@ -0,0 +1,109 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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. +*/ + +#ifndef __VGLOBAL_H__ +#define __VGLOBAL_H__ + +#include <kglobal.h> + +// define some often used mathematical constants et al: + +// TODO: optimize those values. + +namespace VGlobal +{ + const double pi = 3.14159265358979323846; // pi + const double twopi = 6.28318530717958647692; // 2pi + const double pi_2 = 1.57079632679489661923; // pi/2 + const double pi_180 = 0.01745329251994329576; // pi/180 + const double one_pi_180 = 57.29577951308232087684; // 180/pi + const double sqrt2 = 1.41421356237309504880; // sqrt(2) + const double one_3 = 0.33333333333333333333; // 1/3 + const double two_3 = 0.66666666666666666667; // 2/3 + const double one_6 = 0.16666666666666666667; // 1/6 + const double one_7 = 0.14285714285714285714; // 1/7 + + /** + * Constants used to decide if a number is equal zero or nearly the same + * as another number. + */ + const double veryBigNumber = 1.0e8; + const double verySmallNumber = 1.0e-8; + + /** + * A bezier with this flatness is considered "flat". Used in subdividing. + */ + const double flatnessTolerance = 0.01; + + /** + * A tolerance used to approximate bezier lengths. If the relative difference + * between chordlength and polylength (length of the controlpolygon) is smaller + * than this value, the length of the bezier is 1/2 chordlength + 1/2 polylength. + */ + const double lengthTolerance = 0.005; + + /** + * A tolerance used to calculate param t on a segment at a given arc + * length (counting from t=0). + * If the relative difference between a length approximation and the given + * length is smaller than this value, they are assumed to be identical. + */ + const double paramLengthTolerance = 0.001; + + /** + * A range for KoPoint::isNear() check, to decide if a KoPoint "is the same" + * as another. + */ + const double isNearRange = 0.001; + + /** + * A tolerance for multiplying normalized (length=1) vectors. A result of + * >= parallelTolerance indicates parallel vectors. + */ + const double parallelTolerance = 0.99; + + /** + * Returns the sign of paramater a. + */ + inline int sign( double a ) + { + return a < 0.0 + ? -1 + : 1; + } + + /** + * Calculates the binomial coefficient n! / ( k! * ( n - k)! ). + */ + int binomialCoeff( unsigned n, unsigned k ); + + /** + * Calculates the value ln( n! ). + */ + double factorialLn( unsigned n ); + + /** + * Calculates the value ln| Gamma(x) | for x > 0. + */ + double gammaLn( double x ); +} + +#endif + diff --git a/karbon/core/vgradient.cc b/karbon/core/vgradient.cc new file mode 100644 index 00000000..cd3b55b6 --- /dev/null +++ b/karbon/core/vgradient.cc @@ -0,0 +1,370 @@ +/* This file is part of the KDE project + Copyright (C) 2002 - 2005, 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 <qdom.h> +#include <qbuffer.h> + +#include "vdocument.h" +#include "vglobal.h" +#include "vgradient.h" + +#include <KoGenStyles.h> +#include <KoXmlWriter.h> +#include <KoXmlNS.h> + +int VGradient::VColorStopList::compareItems( QPtrCollection::Item item1, QPtrCollection::Item item2 ) +{ + float r1 = ( (VColorStop*)item1 )->rampPoint; + float r2 = ( (VColorStop*)item2 )->rampPoint; + + return ( r1 == r2 ? 0 : r1 < r2 ? -1 : 1 ); +} // VGradient::VColorStopList::compareItems + +VGradient::VGradient( VGradientType type ) + : m_type( type ) +{ + m_colorStops.setAutoDelete( true ); + + // set up dummy gradient + VColor color; + + color.set( 1.0, 0.0, 0.0 ); + addStop( color, 0.0, 0.5 ); + + color.set( 1.0, 1.0, 0.0 ); + addStop( color, 1.0, 0.5 ); + + setOrigin( KoPoint( 0, 0 ) ); + setVector( KoPoint( 0, 50 ) ); + setRepeatMethod( VGradient::reflect ); +} + +VGradient::VGradient( const VGradient& gradient ) +{ + m_colorStops.setAutoDelete( true ); + + m_origin = gradient.m_origin; + m_focalPoint = gradient.m_focalPoint; + m_vector = gradient.m_vector; + m_type = gradient.m_type; + m_repeatMethod = gradient.m_repeatMethod; + + m_colorStops.clear(); + QPtrVector<VColorStop> cs = gradient.colorStops(); + for( uint i = 0; i < cs.count(); i++ ) + m_colorStops.append( new VColorStop( *cs[i] ) ); + m_colorStops.sort(); +} // VGradient::VGradient + +VGradient& VGradient::operator=( const VGradient& gradient ) +{ + m_colorStops.setAutoDelete( true ); + + if ( this == &gradient ) + return *this; + + m_origin = gradient.m_origin; + m_focalPoint = gradient.m_focalPoint; + m_vector = gradient.m_vector; + m_type = gradient.m_type; + m_repeatMethod = gradient.m_repeatMethod; + + m_colorStops.clear(); + QPtrVector<VColorStop> cs = gradient.colorStops(); + for( uint i = 0; i < cs.count(); i++ ) + m_colorStops.append( new VColorStop( *cs[i] ) ); + m_colorStops.sort(); + + return *this; +} // VGradient::operator= + +const QPtrVector<VColorStop> VGradient::colorStops() const +{ + QPtrVector<VColorStop> v; + m_colorStops.toVector( &v ); + v.setAutoDelete( false ); + return v; +} // VGradient::colorStops() + +void +VGradient::clearStops() +{ + m_colorStops.clear(); +} + +void +VGradient::addStop( const VColorStop& colorStop ) +{ + m_colorStops.inSort( new VColorStop( colorStop ) ); +} // VGradient::addStop + +void +VGradient::addStop( const VColor &color, float rampPoint, float midPoint ) +{ + // Clamping between 0.0 and 1.0 + rampPoint = kMax( 0.0f, rampPoint ); + rampPoint = kMin( 1.0f, rampPoint ); + // Clamping between 0.0 and 1.0 + midPoint = kMax( 0.0f, midPoint ); + midPoint = kMin( 1.0f, midPoint ); + + // Work around stops with the same position + VColorStop *v; + for(v = m_colorStops.first(); v; v = m_colorStops.next()) + { + if(rampPoint == v->rampPoint) + rampPoint += 0.001f; + } + m_colorStops.inSort( new VColorStop( rampPoint, midPoint, color ) ); +} + +void VGradient::removeStop( const VColorStop& colorstop ) +{ + m_colorStops.remove( &colorstop ); +} // VGradient::removeStop + +void +VGradient::save( QDomElement& element ) const +{ + QDomElement me = element.ownerDocument().createElement( "GRADIENT" ); + + me.setAttribute( "originX", m_origin.x() ); + me.setAttribute( "originY", m_origin.y() ); + me.setAttribute( "focalX", m_focalPoint.x() ); + me.setAttribute( "focalY", m_focalPoint.y() ); + me.setAttribute( "vectorX", m_vector.x() ); + me.setAttribute( "vectorY", m_vector.y() ); + me.setAttribute( "type", m_type ); + me.setAttribute( "repeatMethod", m_repeatMethod ); + + // save stops + VColorStop* colorstop; + QPtrList<VColorStop>& colorStops = const_cast<VColorStopList&>( m_colorStops ); + for( colorstop = colorStops.first(); colorstop; colorstop = colorStops.next() ) + { + QDomElement stop = element.ownerDocument().createElement( "COLORSTOP" ); + colorstop->color.save( stop ); + stop.setAttribute( "ramppoint", colorstop->rampPoint ); + stop.setAttribute( "midpoint", colorstop->midPoint ); + me.appendChild( stop ); + } + + element.appendChild( me ); +} + +QString +VGradient::saveOasis( KoGenStyles &mainStyles ) const +{ + bool radial = m_type == VGradient::radial; + KoGenStyle gradientStyle( radial ? VDocument::STYLE_RADIAL_GRADIENT : VDocument::STYLE_LINEAR_GRADIENT /*no family name*/); + if( radial ) + { + gradientStyle.addAttribute( "draw:style", "radial" ); + gradientStyle.addAttributePt( "svg:cx", m_origin.x() ); + gradientStyle.addAttributePt( "svg:cy", m_origin.y() ); + double dx = m_vector.x() - m_origin.x(); + double dy = m_vector.y() - m_origin.y(); + gradientStyle.addAttributePt( "svg:r", sqrt( dx * dx + dy * dy ) ); + gradientStyle.addAttributePt( "svg:fx", m_focalPoint.x() ); + gradientStyle.addAttributePt( "svg:fy", m_focalPoint.y() ); + } + else + { + gradientStyle.addAttribute( "draw:style", "linear" ); + gradientStyle.addAttributePt( "svg:x1", m_origin.x() ); + gradientStyle.addAttributePt( "svg:y1", m_origin.y() ); + gradientStyle.addAttributePt( "svg:x2", m_vector.x() ); + gradientStyle.addAttributePt( "svg:y2", m_vector.y() ); + } + if( m_repeatMethod == VGradient::repeat ) + gradientStyle.addAttribute( "svg:spreadMethod", "repeat" ); + else if( m_repeatMethod == VGradient::reflect ) + gradientStyle.addAttribute( "svg:spreadMethod", "reflect" ); + else + gradientStyle.addAttribute( "svg:spreadMethod", "pad" ); + QBuffer buffer; + buffer.open( IO_WriteOnly ); + KoXmlWriter elementWriter( &buffer ); // TODO pass indentation level + + // save stops + VColorStop* colorstop; + QPtrList<VColorStop>& colorStops = const_cast<VColorStopList&>( m_colorStops ); + for( colorstop = colorStops.first(); colorstop; colorstop = colorStops.next() ) + { + elementWriter.startElement( "svg:stop" ); + elementWriter.addAttribute( "svg:offset", QString( "%1" ).arg( colorstop->rampPoint ) ); + elementWriter.addAttribute( "svg:color", QColor( colorstop->color ).name() ); + if( colorstop->color.opacity() < 1 ) + elementWriter.addAttribute( "svg:stop-opacity", QString( "%1" ).arg( colorstop->color.opacity() ) ); + elementWriter.endElement(); + } + + QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() ); + gradientStyle.addChildElement( "svg:stop", elementContents ); + return mainStyles.lookup( gradientStyle, "gradient" ); +} + +void +VGradient::loadOasis( const QDomElement &object, KoStyleStack &/*stack*/, VObject* parent ) +{ + kdDebug(38000) << "namespaceURI: " << object.namespaceURI() << endl; + kdDebug(38000) << "localName: " << object.localName() << endl; + + KoRect bb; + + if( parent ) + bb = parent->boundingBox(); + + if( object.namespaceURI() == KoXmlNS::draw && object.localName() == "gradient" ) + { + m_repeatMethod = VGradient::reflect; + QString strType = object.attributeNS( KoXmlNS::draw, "style", QString::null ); + if( strType == "radial" ) + { + m_type = VGradient::radial; + // TODO : find out whether Oasis works with boundingBox only? + double cx = KoUnit::parseValue( object.attributeNS( KoXmlNS::draw, "cx", QString::null ).remove("%") ); + m_origin.setX( bb.bottomLeft().x() + bb.width() * 0.01 * cx ); + double cy = KoUnit::parseValue( object.attributeNS( KoXmlNS::draw, "cy", QString::null ).remove("%") ); + m_origin.setY( bb.bottomLeft().y() - bb.height() * 0.01 * cy ); + m_focalPoint = m_origin; + m_vector = bb.topRight(); + } + else if( strType == "linear" ) + { + m_type = VGradient::linear; + double angle = 90 + object.attributeNS( KoXmlNS::draw, "angle", "0" ).toDouble(); + double radius = 0.5 * sqrt( bb.width()*bb.width() + bb.height()*bb.height() ); + double sx = cos( angle * VGlobal::pi / 180 ) * radius; + double sy = sin( angle * VGlobal::pi / 180 ) * radius; + m_origin.setX( bb.center().x() + sx ); + m_origin.setY( bb.center().y() + sy ); + m_vector.setX( bb.center().x() - sx ); + m_vector.setY( bb.center().y() - sy ); + m_focalPoint = m_origin; + } + else return; + + VColor startColor( QColor( object.attributeNS( KoXmlNS::draw, "start-color", QString::null ) ) ); + VColor endColor( QColor( object.attributeNS( KoXmlNS::draw, "end-color", QString::null ) ) ); + + double startOpacity = 0.01 * object.attributeNS( KoXmlNS::draw, "start-intensity", "100" ).remove("%").toDouble(); + double endOpacity = 0.01 * object.attributeNS( KoXmlNS::draw, "end-intensity", "100" ).remove("%").toDouble(); + + startColor.setOpacity( startOpacity ); + endColor.setOpacity( endOpacity ); + m_colorStops.clear(); + addStop( startColor, 0.0, 0.5 ); + addStop( endColor, 1.0, 0.0 ); + m_colorStops.sort(); + + } + else if( object.namespaceURI() == KoXmlNS::svg ) + { + if( object.localName() == "linearGradient" ) + { + m_type = VGradient::linear; + m_origin.setX( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "x1", QString::null ) ) ); + m_origin.setY( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "y1", QString::null ) ) ); + m_vector.setX( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "x2", QString::null ) ) ); + m_vector.setY( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "y2", QString::null ) ) ); + m_focalPoint = m_origin; + } + else if( object.localName() == "radialGradient" ) + { + m_type = VGradient::radial; + m_origin.setX( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "cx", QString::null ) ) ); + m_origin.setY( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "cy", QString::null ) ) ); + double r = KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "r", QString::null ) ); + m_vector.setX( m_origin.x() + r ); + m_vector.setY( m_origin.y() ); + m_focalPoint.setX( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "fx", QString::null ) ) ); + m_focalPoint.setY( KoUnit::parseValue( object.attributeNS( KoXmlNS::svg, "fy", QString::null ) ) ); + } + + QString strSpread( object.attributeNS( KoXmlNS::svg, "spreadMethod", "pad" ) ); + if( strSpread == "repeat" ) + m_repeatMethod = VGradient::repeat; + else if( strSpread == "reflect" ) + m_repeatMethod = VGradient::reflect; + else + m_repeatMethod = VGradient::none; + + m_colorStops.clear(); + + // load stops + QDomNodeList list = object.childNodes(); + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement colorstop = list.item( i ).toElement(); + + if( colorstop.namespaceURI() == KoXmlNS::svg && colorstop.localName() == "stop" ) + { + VColor color( QColor( colorstop.attributeNS( KoXmlNS::svg, "color", QString::null ) ) ); + color.setOpacity( colorstop.attributeNS( KoXmlNS::svg, "stop-opacity", "1.0" ).toDouble() ); + addStop( color, colorstop.attributeNS( KoXmlNS::svg, "offset", "0.0" ).toDouble(), 0.5 ); + } + } + } + m_colorStops.sort(); + } +} + +void +VGradient::load( const QDomElement& element ) +{ + m_origin.setX( element.attribute( "originX", "0.0" ).toDouble() ); + m_origin.setY( element.attribute( "originY", "0.0" ).toDouble() ); + m_focalPoint.setX( element.attribute( "focalX", "0.0" ).toDouble() ); + m_focalPoint.setY( element.attribute( "focalY", "0.0" ).toDouble() ); + m_vector.setX( element.attribute( "vectorX", "0.0" ).toDouble() ); + m_vector.setY( element.attribute( "vectorY", "0.0" ).toDouble() ); + m_type = (VGradientType)element.attribute( "type", 0 ).toInt(); + m_repeatMethod = (VGradientRepeatMethod)element.attribute( "repeatMethod", 0 ).toInt(); + + m_colorStops.clear(); + + // load stops + QDomNodeList list = element.childNodes(); + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement colorstop = list.item( i ).toElement(); + + if( colorstop.tagName() == "COLORSTOP" ) + { + VColor color; + color.load( colorstop.firstChild().toElement() ); + addStop( color, colorstop.attribute( "ramppoint", "0.0" ).toDouble(), colorstop.attribute( "midpoint", "0.5" ).toDouble() ); + } + } + } + m_colorStops.sort(); +} + +void +VGradient::transform( const QWMatrix &m ) +{ + m_origin = m_origin.transform( m ); + m_focalPoint = m_focalPoint.transform( m ); + m_vector = m_vector.transform( m ); +} diff --git a/karbon/core/vgradient.h b/karbon/core/vgradient.h new file mode 100644 index 00000000..5587af7e --- /dev/null +++ b/karbon/core/vgradient.h @@ -0,0 +1,129 @@ +/* This file is part of the KDE project + Copyright (C) 2002 - 2005, 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. +*/ + +#ifndef __VGRADIENT_H__ +#define __VGRADIENT_H__ + +#include <qptrlist.h> +#include <qptrvector.h> + +#include <koffice_export.h> +#include <KoPoint.h> + +#include "vcolor.h" + +class QDomElement; +class KoGenStyle; +class KoGenStyles; +class KoStyleStack; +class VObject; + +class VColorStop +{ + public: + VColorStop( double r, double m, VColor c ) + { rampPoint = r; midPoint = m; color = c; }; + VColorStop( const VColorStop& colorStop ) + { rampPoint = colorStop.rampPoint; midPoint = colorStop.midPoint; color = colorStop.color; }; + + VColor color; + + // relative position of color point (0.0-1.0): + float rampPoint; + + // relative position of midpoint (0.0-1.0) + // between two ramp points. ignored for last VColorStop. + float midPoint; + + friend inline bool operator== ( VColorStop& s1, VColorStop& s2 ) + { return s1.rampPoint == s2.rampPoint; }; +}; // VColorStop + +class KARBONBASE_EXPORT VGradient +{ +friend class VGradientWidget; + +public: + enum VGradientType + { + linear = 0, + radial = 1, + conic = 2 + }; + + enum VGradientRepeatMethod + { + none = 0, + reflect = 1, + repeat = 2 + }; + + class VColorStopList : public QPtrList<VColorStop> + { + protected: + virtual int compareItems( QPtrCollection::Item item1, QPtrCollection::Item item2 ); + }; // VColorStopList + + VGradient( VGradientType type = linear ); + VGradient( const VGradient& gradient ); + + VGradient& operator=(const VGradient& gradient); + + VGradientType type() const { return m_type; } + void setType( VGradientType type ) { m_type = type; } + + VGradientRepeatMethod repeatMethod() const { return m_repeatMethod; } + void setRepeatMethod( VGradientRepeatMethod repeatMethod ) { m_repeatMethod = repeatMethod; } + + const QPtrVector<VColorStop> colorStops() const; + void addStop( const VColorStop& colorStop ); + void addStop( const VColor &color, float rampPoint, float midPoint ); + void removeStop( const VColorStop& colorStop ); + void clearStops(); + + KoPoint origin() const { return m_origin; } + void setOrigin( const KoPoint &origin ) { m_origin = origin; } + + KoPoint focalPoint() const { return m_focalPoint; } + void setFocalPoint( const KoPoint &focalPoint ) { m_focalPoint = focalPoint; } + + KoPoint vector() const { return m_vector; } + void setVector( const KoPoint &vector ) { m_vector = vector; } + + void save( QDomElement& element ) const; + QString saveOasis( KoGenStyles &mainStyles ) const; + void load( const QDomElement& element ); + void loadOasis( const QDomElement &object, KoStyleStack &stack, VObject* parent = 0L ); + + void transform( const QWMatrix& m ); + +protected: + VColorStopList m_colorStops; + +private: + VGradientType m_type : 2; + VGradientRepeatMethod m_repeatMethod : 2; + + // coordinates: + KoPoint m_origin; + KoPoint m_focalPoint; + KoPoint m_vector; +}; // VGradient + +#endif /* __VGRADIENT_H__ */ diff --git a/karbon/core/vgroup.cc b/karbon/core/vgroup.cc new file mode 100644 index 00000000..d356b2b1 --- /dev/null +++ b/karbon/core/vgroup.cc @@ -0,0 +1,398 @@ +/* This file is part 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. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qdom.h> + +#include <KoStore.h> +#include <KoXmlWriter.h> +#include <KoOasisLoadingContext.h> +#include <KoStyleStack.h> +#include <KoXmlNS.h> + +#include "vcomposite.h" +#include "shapes/vellipse.h" +#include "shapes/vrectangle.h" +#include "shapes/vsinus.h" +#include "shapes/vspiral.h" +#include "shapes/vstar.h" +#include "shapes/vpolyline.h" +#include "shapes/vpolygon.h" +#include "vfill.h" +#include "vgroup.h" +#include "vlayer.h" +#include "vimage.h" +#include "vstroke.h" +#include "vvisitor.h" +#include "vclipgroup.h" +#ifdef HAVE_KARBONTEXT +#include "vtext.h" +#endif + +#include <kdebug.h> + + +VGroup::VGroup( VObject* parent, VState state ) + : VObject( parent, state ) +{ + m_stroke = new VStroke( this ); + m_fill = new VFill(); +} + +VGroup::VGroup( const VGroup& group ) + : VObject( group ) +{ + m_stroke = new VStroke( *group.m_stroke ); + m_stroke->setParent( this ); + m_fill = new VFill( *group.m_fill ); + + VObjectListIterator itr = group.m_objects; + for ( ; itr.current() ; ++itr ) + append( itr.current()->clone() ); +} + +VGroup::~VGroup() +{ + VObjectListIterator itr = m_objects; + for ( ; itr.current(); ++itr ) + { + delete( itr.current() ); + } +} + +void +VGroup::draw( VPainter* painter, const KoRect* rect ) const +{ + if( + state() == deleted || + state() == hidden || + state() == hidden_locked ) + { + return; + } + + VObjectListIterator itr = m_objects; + + for ( ; itr.current(); ++itr ) + itr.current()->draw( painter, rect ); +} + +const KoRect& +VGroup::boundingBox() const +{ + if( m_boundingBoxIsInvalid ) + { + // clear: + m_boundingBox = KoRect(); + + VObjectListIterator itr = m_objects; + for( ; itr.current(); ++itr ) + { + m_boundingBox |= itr.current()->boundingBox(); + } + + m_boundingBoxIsInvalid = false; + } + + return m_boundingBox; +} + +VGroup* +VGroup::clone() const +{ + return new VGroup( *this ); +} + +void +VGroup::setFill( const VFill& fill ) +{ + VObjectListIterator itr = m_objects; + + for ( ; itr.current() ; ++itr ) + itr.current()->setFill( fill ); + + VObject::setFill( fill ); +} + +void +VGroup::setStroke( const VStroke& stroke ) +{ + VObjectListIterator itr = m_objects; + + for ( ; itr.current() ; ++itr ) + itr.current()->setStroke( stroke ); + + VObject::setStroke( stroke ); +} + +void +VGroup::setState( const VState state ) +{ + VObjectListIterator itr = m_objects; + + for ( ; itr.current() ; ++itr ) + if( m_state == VObject::deleted || itr.current()->state() != VObject::deleted ) + itr.current()->setState( state ); + + VObject::setState( state ); +} + +void +VGroup::save( QDomElement& element ) const +{ + if( state() != deleted ) + { + QDomElement me = element.ownerDocument().createElement( "GROUP" ); + element.appendChild( me ); + + // save objects: + VObjectListIterator itr = m_objects; + + for ( ; itr.current(); ++itr ) + itr.current()->save( me ); + + VObject::save( me ); + } +} + +void +VGroup::saveOasis( KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles, int &index ) const +{ + // do not save deleted objects + if( state() == deleted ) + return; + + docWriter->startElement( "draw:g" ); + + // save objects: + VObjectListIterator itr = m_objects; + + for ( ; itr.current(); ++itr ) + itr.current()->saveOasis( store, docWriter, mainStyles, ++index ); + + docWriter->endElement(); +} + +bool +VGroup::loadOasis( const QDomElement &element, KoOasisLoadingContext &context ) +{ + m_objects.setAutoDelete( true ); + m_objects.clear(); + m_objects.setAutoDelete( false ); + + QDomNodeList list = element.childNodes(); + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement e = list.item( i ).toElement(); + + kdDebug(38000) << "VGroup::loadOasis: e.tagName() = " << e.tagName() << endl; + kdDebug(38000) << "VGroup::loadOasis: e.namespaceURI() = " << e.namespaceURI() << endl; + kdDebug(38000) << "VGroup::loadOasis: e.localName() = " << e.localName() << endl; + + if( e.namespaceURI() != KoXmlNS::draw ) + continue; + + context.styleStack().save(); + + if( e.localName() == "path" || e.localName() == "custom-shape" ) + { + VPath* composite = new VPath( this ); + composite->loadOasis( e, context ); + append( composite ); + } + else if( e.localName() == "circle" || e.localName() == "ellipse" ) + { + VEllipse* ellipse = new VEllipse( this ); + ellipse->loadOasis( e, context ); + append( ellipse ); + } + else if( e.localName() == "rect" ) + { + VRectangle* rectangle = new VRectangle( this ); + rectangle->loadOasis( e, context ); + append( rectangle ); + } + else if( e.localName() == "g" ) + { + VGroup* group = new VGroup( this ); + group->loadOasis( e, context ); + append( group ); + } + else if( e.localName() == "polyline" || e.localName() == "line" ) + { + VPolyline* polyline = new VPolyline( this ); + polyline->loadOasis( e, context ); + append( polyline ); + } + else if( e.localName() == "polygon" ) + { + VPolygon* polygon = new VPolygon( this ); + polygon->loadOasis( e, context ); + append( polygon ); + } + + context.styleStack().restore(); + } + } + + return true; +} + +void +VGroup::load( const QDomElement& element ) +{ + m_objects.setAutoDelete( true ); + m_objects.clear(); + m_objects.setAutoDelete( false ); + + VObject::load( element ); + + QDomNodeList list = element.childNodes(); + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement e = list.item( i ).toElement(); + + if( e.tagName() == "COMPOSITE" || e.tagName() == "PATH" ) // TODO : remove COMPOSITE later + { + VPath* composite = new VPath( this ); + composite->load( e ); + append( composite ); + } + else if( e.tagName() == "ELLIPSE" ) + { + VEllipse* ellipse = new VEllipse( this ); + ellipse->load( e ); + append( ellipse ); + } + else if( e.tagName() == "RECT" ) + { + VRectangle* rectangle = new VRectangle( this ); + rectangle->load( e ); + append( rectangle ); + } + else if( e.tagName() == "POLYLINE" ) + { + VPolyline* polyline = new VPolyline( this ); + polyline->load( e ); + append( polyline ); + } + else if( e.tagName() == "POLYGON" ) + { + VPolygon* polygon = new VPolygon( this ); + polygon->load( e ); + append( polygon ); + } + else if( e.tagName() == "SINUS" ) + { + VSinus* sinus = new VSinus( this ); + sinus->load( e ); + append( sinus ); + } + else if( e.tagName() == "SPIRAL" ) + { + VSpiral* spiral = new VSpiral( this ); + spiral->load( e ); + append( spiral ); + } + else if( e.tagName() == "STAR" ) + { + VStar* star = new VStar( this ); + star->load( e ); + append( star ); + } + else if( e.tagName() == "GROUP" ) + { + VGroup* group = new VGroup( this ); + group->load( e ); + append( group ); + } + else if( e.tagName() == "CLIP" ) + { + VClipGroup* grp = new VClipGroup( this ); + grp->load( e ); + append( grp ); + } + else if( e.tagName() == "IMAGE" ) + { + VImage* img = new VImage( this ); + img->load( e ); + append( img ); + } + else if( e.tagName() == "TEXT" ) + { +#ifdef HAVE_KARBONTEXT + VText *text = new VText( this ); + text->load( e ); + append( text ); +#endif + } + } + } +} + +void +VGroup::accept( VVisitor& visitor ) +{ + visitor.visitVGroup( *this ); +} + + +void +VGroup::take( const VObject& object ) +{ + m_objects.removeRef( &object ); + + invalidateBoundingBox(); +} + +void +VGroup::append( VObject* object ) +{ + object->setParent( this ); + + m_objects.append( object ); + + invalidateBoundingBox(); +} + +void +VGroup::insertInfrontOf( VObject* newObject, VObject* oldObject ) +{ + newObject->setParent( this ); + + m_objects.insert( m_objects.find( oldObject ), newObject ); + + invalidateBoundingBox(); +} + +void +VGroup::clear() +{ + m_objects.clear(); + + invalidateBoundingBox(); +} + diff --git a/karbon/core/vgroup.h b/karbon/core/vgroup.h new file mode 100644 index 00000000..25190178 --- /dev/null +++ b/karbon/core/vgroup.h @@ -0,0 +1,117 @@ +/* This file is part of the KDE project + Copyright (C) 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. +*/ + +#ifndef __VGROUP_H__ +#define __VGROUP_H__ + +#include <qptrlist.h> + +#include "vobject.h" +#include <koffice_export.h> +typedef QPtrList<VObject> VObjectList; +typedef QPtrListIterator<VObject> VObjectListIterator; + +/** + * Base class for all sort of VObject conglomerats. + */ + +class KARBONBASE_EXPORT VGroup : public VObject +{ +public: + /** + * Constructs a new group object that is child of parent and has the given state. + * + * The object's fill and stroke are created here. + * + * @param parent the new object's parent + * @param state the new object's state + */ + VGroup( VObject* parent, VState state = normal ); + + /** + * Copy constructor. + * + * @param group the group to copy properties from + */ + VGroup( const VGroup& group ); + + /** + * Destroys the group and all of the child objects. + */ + virtual ~VGroup(); + + virtual void draw( VPainter* painter, const KoRect* rect = 0L ) const; + + virtual const KoRect& boundingBox() const; + + virtual void setStroke( const VStroke& stroke ); + virtual void setFill( const VFill& fill ); + + virtual void setState( const VState state ); + + virtual void save( QDomElement& element ) const; + virtual void saveOasis( KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles, int &index ) const; + virtual void load( const QDomElement& element ); + virtual bool loadOasis( const QDomElement &element, KoOasisLoadingContext &context ); + + virtual VGroup* clone() const; + + virtual void accept( VVisitor& visitor ); + + /** + * Removes the reference to child object, not the object itself. + * + * @param object the child object to remove + */ + void take( const VObject& object ); + + /** + * Appends a new child object. + * + * @param object the object to append + */ + void append( VObject* object ); + + /** + * This function is important for undo/redo. It inserts newObject in front + * of oldObject. + * + * @param newObject the new object to insert + * @param oldObject the old object the new object is inserted in front of + */ + virtual void insertInfrontOf( VObject* newObject, VObject* oldObject ); + + /** + * Clears the group, without destroying the grouped objects. + */ + void clear(); + + /** + * Read only access to the grouped objects. + * + * @return reference to the list of grouped objects + */ + const VObjectList& objects() const { return m_objects; } + +protected: + VObjectList m_objects; /**< the list of grouped objects */ +}; + +#endif + diff --git a/karbon/core/vgroup_iface.cc b/karbon/core/vgroup_iface.cc new file mode 100644 index 00000000..fec75358 --- /dev/null +++ b/karbon/core/vgroup_iface.cc @@ -0,0 +1,46 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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 <kapplication.h> +#include <dcopclient.h> + +#include "vgroup_iface.h" +#include "vgroup.h" + +VGroupIface::VGroupIface( VGroup *group ) + : VObjectIface( group ), m_group( group ) +{ +} + +void +VGroupIface::clear() +{ + m_group->clear(); +} + +QValueList<DCOPRef> +VGroupIface::objects() +{ + QValueList<DCOPRef> lst; + VObjectListIterator itr = m_group->objects(); + for( ; itr.current(); ++itr ) + lst.append( DCOPRef( kapp->dcopClient()->appId(), itr.current()->dcopObject()->objId() ) ); + return lst; +} + diff --git a/karbon/core/vgroup_iface.h b/karbon/core/vgroup_iface.h new file mode 100644 index 00000000..c189a7a1 --- /dev/null +++ b/karbon/core/vgroup_iface.h @@ -0,0 +1,54 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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. +*/ + +#ifndef __VGROUP_IFACE_H__ +#define __VGROUP_IFACE_H__ + +#include <qvaluelist.h> + +#include "vobject_iface.h" + +class VGroup; + +//typedef QPtrList<VObject> VObjectList; +//typedef QPtrListIterator<VObject> VObjectListIterator; + +class VGroupIface : public VObjectIface +{ + K_DCOP + +public: + VGroupIface( VGroup *group ); + +k_dcop: + //void take( const VObject& object ); + + //void append( DCOPRef object ); + + //virtual void insertInfrontOf( VObject* newObject, VObject* oldObject ); + + void clear(); + QValueList<DCOPRef> objects(); + +private: + VGroup *m_group; +}; + +#endif + diff --git a/karbon/core/vimage.cc b/karbon/core/vimage.cc new file mode 100644 index 00000000..a8bbf138 --- /dev/null +++ b/karbon/core/vimage.cc @@ -0,0 +1,158 @@ +/* This file is part of the KDE project + Copyright (C) 2001, 2002, 2003 The Karbon Developers +*/ + +#include "vimage.h" +#include "vpainter.h" +#include "vvisitor.h" +#include "vpath.h" +#include "vfill.h" +#include "vstroke.h" + +#include <qdom.h> +#include <qimage.h> +#include <KoRect.h> + +#include <render/vqpainter.h> + +#include <kdebug.h> + +VImage::VImage( VObject *parent, const QString &fname ) : VObject( parent ), m_image( 0L ), m_fname( fname ) +{ + m_stroke = new VStroke( this ); + m_stroke->setType( VStroke::none ); + m_fill = new VFill(); + m_image = new QImage( m_fname ); + if( m_image->depth() != 32 ) + *m_image = m_image->convertDepth( 32 ); + m_image->setAlphaBuffer( true ); + *m_image = m_image->swapRGB(); + *m_image = m_image->mirror( false, true ); +} + +VImage::VImage( const VImage &other ) : VObject( other ) +{ + if( other.m_image ) + m_image = new QImage( *other.m_image ); + else + m_image = 0L; + + if ( other.stroke() ) + setStroke( *other.stroke() ); + if ( other.fill() ) + setFill( *other.fill() ); + + m_fname = other.m_fname; + m_boundingBox = other.m_boundingBox; + m_matrix = other.m_matrix; +} + +VImage::~VImage() +{ + delete m_image; +} + +void +VImage::draw( VPainter *painter, const KoRect * ) const +{ + if( + state() == deleted || + state() == hidden || + state() == hidden_locked ) + { + return; + } + + if( state() == edit ) + { + KoRect bbox = KoRect( 0, 0, m_image->width(), m_image->height() ); + KoPoint tl = bbox.topLeft().transform( m_matrix ); + KoPoint tr = bbox.topRight().transform( m_matrix ); + KoPoint bl = bbox.bottomLeft().transform( m_matrix ); + KoPoint br = bbox.bottomRight().transform( m_matrix ); + + painter->moveTo( tl ); + painter->lineTo( tr ); + painter->lineTo( br ); + painter->lineTo( bl ); + painter->lineTo( tl ); + + painter->setRasterOp( Qt::XorROP ); + //painter->setPen( stroke() ); + painter->setPen( Qt::yellow ); + painter->setBrush( Qt::NoBrush ); + painter->strokePath(); + return; + } + + //painter->setWorldMatrix( m_matrix ); + + //*m_image = m_image->smoothScale( m_image->width() * zoomFactor, m_image->height() * zoomFactor, QImage::ScaleMin ); + m_boundingBox = KoRect( 0, 0, m_image->width(), m_image->height() ); + m_boundingBox = m_boundingBox.transform( m_matrix ); + if( !m_image->isNull() ) + painter->drawImage( *m_image, m_matrix ); +} + +void +VImage::transform( const QWMatrix& m ) +{ + //QWMatrix m2 = m; + //m_matrix *= m2.scale( 1.0, -1.0 ); + m_matrix *= m; + kdDebug(38000) << "dx : " << m.dx() << ", dy : " << m.dy() << endl; + m_boundingBox = m_boundingBox.transform( m ); +} + +VObject * +VImage::clone() const +{ + return new VImage( *this ); +} + +void +VImage::save( QDomElement& element ) const +{ + if( state() != deleted ) + { + QDomElement me = element.ownerDocument().createElement( "IMAGE" ); + element.appendChild( me ); + + me.setAttribute( "fname", m_fname ); + me.setAttribute( "m11", m_matrix.m11() ); + me.setAttribute( "m12", m_matrix.m12() ); + me.setAttribute( "m21", m_matrix.m21() ); + me.setAttribute( "m22", m_matrix.m22() ); + me.setAttribute( "dx", m_matrix.dx() ); + me.setAttribute( "dy", m_matrix.dy() ); + } +} + +void +VImage::load( const QDomElement& element ) +{ + setState( normal ); + m_fname = element.attribute( "fname" ); + m_matrix.setMatrix( element.attribute( "m11", "1.0" ).toDouble(), + element.attribute( "m12", "0.0" ).toDouble(), + element.attribute( "m21", "0.0" ).toDouble(), + element.attribute( "m22", "1.0" ).toDouble(), + element.attribute( "dx", "0.0" ).toDouble(), + element.attribute( "dy", "0.0" ).toDouble() ); + kdDebug(38000) << "VImage::load : " << m_fname.latin1() << endl; + delete m_image; + m_image = new QImage( m_fname ); + if( m_image->depth() != 32 ) + *m_image = m_image->convertDepth( 32 ); + m_image->setAlphaBuffer( true ); + *m_image = m_image->swapRGB(); + *m_image = m_image->mirror( false, true ); + m_boundingBox = KoRect( 0, 0, m_image->width(), m_image->height() ); +} + +void +VImage::accept( VVisitor& visitor ) +{ + visitor.visitVImage( *this ); +} + diff --git a/karbon/core/vimage.h b/karbon/core/vimage.h new file mode 100644 index 00000000..d73e4ee7 --- /dev/null +++ b/karbon/core/vimage.h @@ -0,0 +1,37 @@ +/* This file is part of the KDE project + Copyright (C) 2001, 2002, 2003 The Karbon Developers +*/ + +#ifndef __VIMAGE_H__ +#define __VIMAGE_H__ + +#include "vobject.h" +#include <koffice_export.h> +class QImage; + +// all vobjects exist inside a layer. + +class KARBONBASE_EXPORT VImage : public VObject +{ +public: + VImage( VObject *parent, const QString &fname = "" ); + VImage( const VImage & ); + virtual ~VImage(); + + virtual void draw( VPainter *painter, const KoRect *rect ) const; + + virtual void transform( const QWMatrix& m ); + virtual VObject* clone() const; + + virtual void save( QDomElement& element ) const; + virtual void load( const QDomElement& element ); + + virtual void accept( VVisitor& visitor ); + +private: + QImage *m_image; + QString m_fname; + QWMatrix m_matrix; +}; + +#endif diff --git a/karbon/core/vkarbonplugin.cc b/karbon/core/vkarbonplugin.cc new file mode 100644 index 00000000..94f38fe5 --- /dev/null +++ b/karbon/core/vkarbonplugin.cc @@ -0,0 +1,25 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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 "vkarbonplugin.h" + +VKarbonPlugin::VKarbonPlugin( QObject *parent, const char* name ) : Plugin( parent, name ) +{ +} + diff --git a/karbon/core/vkarbonplugin.h b/karbon/core/vkarbonplugin.h new file mode 100644 index 00000000..39563829 --- /dev/null +++ b/karbon/core/vkarbonplugin.h @@ -0,0 +1,34 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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. +*/ + +#ifndef __VKARBONPLUGIN_H__ +#define __VKARBONPLUGIN_H__ + +#include <kparts/plugin.h> +#include <koffice_export.h> + +class KARBONBASE_EXPORT VKarbonPlugin : public KParts::Plugin +{ +public: + VKarbonPlugin( QObject *parent, const char* name ); + virtual ~VKarbonPlugin() {} +}; + +#endif + diff --git a/karbon/core/vlayer.cc b/karbon/core/vlayer.cc new file mode 100644 index 00000000..f74a3d4b --- /dev/null +++ b/karbon/core/vlayer.cc @@ -0,0 +1,186 @@ +/* This file is part 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 <qdom.h> + +#include <klocale.h> +#include <KoRect.h> + +#include "vcomposite.h" +#include "vdocument.h" +#include "vgroup.h" +#include "vlayer.h" +#include "vobject.h" +#include "vtext.h" +#include "vvisitor.h" +#include "vlayer_iface.h" +#include <kdebug.h> +#include "vclipgroup.h" +#include "vfill.h" +#include "vstroke.h" + +VLayer::VLayer( VObject* parent, VState state ) + : VGroup( parent, state ) +{ + setName( "Layer" ); + // HACK : vlayer just shouldn't have fill/stroke at all + delete m_fill; + m_fill = 0L; + delete m_stroke; + m_stroke = 0L; +} + +VLayer::VLayer( const VLayer& layer ) + : VGroup( layer ) +{ +} + +VLayer::~VLayer() +{ +} + +DCOPObject* VLayer::dcopObject() +{ + if ( !m_dcop ) + m_dcop = new VLayerIface( this ); + + return m_dcop; +} + +void +VLayer::draw( VPainter* painter, const KoRect* rect ) const +{ + if( + state() == deleted || + state() == hidden || + state() == hidden_locked ) + { + return; + } + + VObjectListIterator itr = m_objects; + + for ( ; itr.current(); ++itr ) + itr.current()->draw( painter, rect ); +} + +void +VLayer::bringToFront( const VObject& object ) +{ + if( m_objects.getLast() == &object ) return; + + m_objects.remove( &object ); + + m_objects.append( &object ); +} + +void +VLayer::upwards( const VObject& object ) +{ + if( m_objects.getLast() == &object ) return; + + m_objects.remove( &object ); + + if( m_objects.current() != m_objects.getLast() ) + { + m_objects.next(); + m_objects.insert( m_objects.at(), &object ); + } + else + m_objects.append( &object ); +} + +void +VLayer::downwards( const VObject& object ) +{ + if( m_objects.getFirst() == &object ) return; + + int index = m_objects.find( &object ); + bool bLast = m_objects.getLast() == &object; + m_objects.remove( index ); + + if( !bLast ) m_objects.prev(); + + m_objects.insert( m_objects.at(), &object ); +} + +void +VLayer::sendToBack( const VObject& object ) +{ + if( m_objects.getFirst() == &object ) return; + + m_objects.remove( &object ); + + m_objects.prepend( &object ); +} + +void +VLayer::save( QDomElement& element ) const +{ + if( state() != deleted ) + { + QDomElement me = element.ownerDocument().createElement( "LAYER" ); + element.appendChild( me ); + + if( state() == normal || state() == normal_locked || state() == VObject::selected ) + me.setAttribute( "visible", 1 ); + + // save objects: + VObjectListIterator itr = m_objects; + for ( ; itr.current(); ++itr ) + itr.current()->save( me ); + + VObject::save( me ); + } +} + +void +VLayer::saveOasis( KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles, int &index ) const +{ + // do not save deleted objects + if( state() == deleted ) + return; + + // save objects: + VObjectListIterator itr = m_objects; + + for ( ; itr.current(); ++itr ) + itr.current()->saveOasis( store, docWriter, mainStyles, ++index ); +} + +void +VLayer::load( const QDomElement& element ) +{ + setState( element.attribute( "visible" ) == 0 ? hidden : normal ); + VGroup::load( element ); +} + + +VLayer* +VLayer::clone() const +{ + return new VLayer( *this ); +} + +void +VLayer::accept( VVisitor& visitor ) +{ + visitor.visitVLayer( *this ); +} + diff --git a/karbon/core/vlayer.h b/karbon/core/vlayer.h new file mode 100644 index 00000000..26524416 --- /dev/null +++ b/karbon/core/vlayer.h @@ -0,0 +1,119 @@ +/* This file is part 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. +*/ + +#ifndef __VLAYER_H__ +#define __VLAYER_H__ + +#include "vgroup.h" +#include <koffice_export.h> +class QDomElement; +class DCOPObject; + + +/** + * VLayer manages a set of vobjects. It keeps the objects from bottom to top + * in a list, ie. objects higher in the list are drawn above lower objects. + * Objects in a layer can be manipulated and worked on independant of objects + * in other layers. + */ + +class KARBONBASE_EXPORT VLayer : public VGroup +{ +public: + /** + * Constructs a new layer object that is child of parent and has the given state. + * + * @param parent the new object's parent + * @param state the new object's state + */ + VLayer( VObject* parent, VState state = normal ); + + /** + * Copy constructor. + * + * @param layer the layer to copy properties from + */ + VLayer( const VLayer& layer ); + + virtual ~VLayer(); + virtual DCOPObject* dcopObject(); + + virtual void draw( VPainter *painter, const KoRect* rect = 0L ) const; + + virtual void save( QDomElement& element ) const; + virtual void saveOasis( KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles, int &index ) const; + virtual void load( const QDomElement& element ); + + virtual VLayer* clone() const; + + virtual void accept( VVisitor& visitor ); + + /** + * Moves the object to the top of the list. + * + * When the object is at the top this method has no effect. + * + * @param object the object to move + */ + void bringToFront( const VObject& object ); + + /** + * Moves the object one step up the list. + * + * When the object is at the top this method has no effect. + * + * @param object the object to move + */ + void upwards( const VObject& object ); + + /** + * Moves the object one step down the list. + * + * When the object is at the bottom this method has no effect. + * + * @param object the object to move + */ + void downwards( const VObject& object ); + + /** + * Moves the object to the end of the list. + * + * When the object is at the bottom this method has no effect. + * + * @param object the object to move + */ + void sendToBack( const VObject& object ); + + /** + * Selects or unselects the layer + * + * @param state the new selection state + */ + void setSelected( bool state ) { setState( state ? VObject::selected : VObject::normal ); } + + /** + * Returns the selection state of the layer + * + * @return the actual selection state + */ + bool selected() { return state() == VObject::selected; } +}; + +#endif + diff --git a/karbon/core/vlayer_iface.cc b/karbon/core/vlayer_iface.cc new file mode 100644 index 00000000..7db8a20b --- /dev/null +++ b/karbon/core/vlayer_iface.cc @@ -0,0 +1,55 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Laurent Montel <lmontel@mandrakesoft.com> + + 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 <dcopclient.h> + +#include "vlayer.h" +#include "vlayer_iface.h" + + +VLayerIface::VLayerIface( VLayer* layer ) + : VGroupIface( layer ), m_layer( layer ) +{ +} + +void +VLayerIface::setName( const QString& name ) +{ + m_layer->setName( name ); +} + +QString +VLayerIface::name() const +{ + return m_layer->name(); +} + +void +VLayerIface::setSelected( bool state ) +{ + m_layer->setSelected( state ); +} + +bool +VLayerIface::selected() const +{ + return m_layer->selected(); +} + diff --git a/karbon/core/vlayer_iface.h b/karbon/core/vlayer_iface.h new file mode 100644 index 00000000..9f34a4e0 --- /dev/null +++ b/karbon/core/vlayer_iface.h @@ -0,0 +1,47 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Laurent Montel <lmontel@mandrakesoft.com> + + 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. +*/ + +#ifndef __VLAYER_IFACE_H__ +#define __VLAYER_IFACE_H__ + +#include "vgroup_iface.h" +#include <qstring.h> + +class VLayer; + +class VLayerIface : public VGroupIface +{ + K_DCOP + +public: + VLayerIface( VLayer* layer ); + +k_dcop: + void setName( const QString& name ); + QString name() const; + + void setSelected( bool state ); + bool selected() const; + +private: + VLayer* m_layer; +}; + +#endif + diff --git a/karbon/core/vobject.cc b/karbon/core/vobject.cc new file mode 100644 index 00000000..af2fee35 --- /dev/null +++ b/karbon/core/vobject.cc @@ -0,0 +1,236 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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 <qdom.h> + +#include "vdocument.h" +#include "vfill.h" +#include "vobject.h" +#include "vobject_iface.h" +#include "vstroke.h" + +#include <KoStore.h> +#include <KoGenStyles.h> +#include <KoStyleStack.h> +#include <KoXmlWriter.h> +#include <KoXmlNS.h> +#include <KoOasisLoadingContext.h> +#include <KoOasisStyles.h> + +VObject::VObject( VObject* parent, VState state ) : m_dcop( 0L ) +{ + m_stroke = 0L; + m_fill = 0L; + + m_parent = parent; + m_state = state; + + invalidateBoundingBox(); +} + +VObject::VObject( const VObject& obj ) +{ + m_stroke = 0L; + m_fill = 0L; + + m_parent = obj.m_parent; + m_state = obj.m_state; + + invalidateBoundingBox(); + m_dcop = 0L; + + VDocument *srcDoc = obj.document(); + if( srcDoc && !srcDoc->objectName( &obj ).isEmpty() ) + { + VDocument *dstDoc = document(); + if( dstDoc ) + dstDoc->setObjectName( this, srcDoc->objectName( &obj ) ); + } +} + +VObject::~VObject() +{ + delete( m_stroke ); + delete( m_fill ); + delete m_dcop; +} + +DCOPObject * +VObject::dcopObject() +{ + if ( !m_dcop ) + m_dcop = new VObjectIface( this ); + + return m_dcop; +} + +void +VObject::setStroke( const VStroke& stroke ) +{ + if( !m_stroke ) + m_stroke = new VStroke( this ); + + *m_stroke = stroke; +} + +void +VObject::setFill( const VFill& fill ) +{ + if( !m_fill ) + m_fill = new VFill(); + + *m_fill = fill; +} + +void +VObject::save( QDomElement& element ) const +{ + if( m_stroke ) + m_stroke->save( element ); + + if( m_fill ) + m_fill->save( element ); + + VDocument *doc = document(); + if( doc && !doc->objectName( this ).isEmpty() ) + element.setAttribute( "ID", QString( doc->objectName( this ) ) ); +} + +void +VObject::saveOasis( KoStore *, KoXmlWriter *docWriter, KoGenStyles &mainStyles, int &index ) const +{ + if( !name().isEmpty() ) + docWriter->addAttribute( "draw:name", name() ); + + QWMatrix mat; + mat.scale( 1, -1 ); + mat.translate( 0, -document()->height() ); + + KoGenStyle styleobjectauto( VDocument::STYLE_GRAPHICAUTO, "graphic" ); + saveOasisFill( mainStyles, styleobjectauto ); + if( m_stroke ) + { + // mirror stroke before saving + VStroke stroke( *m_stroke ); + stroke.transform( mat ); + stroke.saveOasis( styleobjectauto ); + } + QString st = mainStyles.lookup( styleobjectauto, "st" ); + if(document()) + docWriter->addAttribute( "draw:id", "obj" + QString::number( index ) ); + docWriter->addAttribute( "draw:style-name", st ); +} + +void +VObject::saveOasisFill( KoGenStyles &mainStyles, KoGenStyle &stylesobjectauto ) const +{ + if( m_fill ) + { + QWMatrix mat; + mat.scale( 1, -1 ); + mat.translate( 0, -document()->height() ); + + // mirror fill before saving + VFill fill( *m_fill ); + fill.transform( mat ); + fill.saveOasis( mainStyles, stylesobjectauto ); + } +} + +void +VObject::load( const QDomElement& element ) +{ + if( !m_stroke ) + m_stroke = new VStroke( this ); + + if( !m_fill ) + m_fill = new VFill(); + + + if( element.tagName() == "STROKE" ) + { + m_stroke->load( element ); + } + else if( element.tagName() == "FILL" ) + { + m_fill->load( element ); + } + + VDocument *doc = document(); + if( doc && !element.attribute( "ID" ).isEmpty() ) + doc->setObjectName( this, element.attribute( "ID" ) ); +} + +bool +VObject::loadOasis( const QDomElement &object, KoOasisLoadingContext &context ) +{ + if( !m_stroke ) + m_stroke = new VStroke( this ); + + if( !m_fill ) + m_fill = new VFill(); + + if( object.hasAttributeNS( KoXmlNS::draw, "style-name" ) ) + context.fillStyleStack( object, KoXmlNS::draw, "style-name", "graphic" ); + + KoStyleStack &styleStack = context.styleStack(); + styleStack.setTypeProperties( "graphic" ); + m_stroke->loadOasis( styleStack ); + m_fill->loadOasis( object, context, this ); + + if( object.hasAttributeNS( KoXmlNS::draw, "name" ) ) + setName( object.attributeNS( KoXmlNS::draw, "name", QString::null ) ); + + return true; +} + +void +VObject::addStyles( const QDomElement* style, KoOasisLoadingContext & context ) +{ + if(style) + { + // this function is necessary as parent styles can have parents themself + if( style->hasAttributeNS( KoXmlNS::style, "parent-style-name" ) ) + addStyles( context.oasisStyles().findStyle( style->attributeNS( KoXmlNS::style, "parent-style-name", QString::null ) ), context ); + context.addStyles( style, "style-name" ); + } +} + +VDocument * +VObject::document() const +{ + VObject *obj = (VObject *)this; + while( obj->parent() ) + obj = obj->parent(); + return dynamic_cast<VDocument *>( obj ); +} + +QString +VObject::name() const +{ + return document() ? document()->objectName( this ) : QString(); +} + +void +VObject::setName( const QString &s ) +{ + if( document() ) + document()->setObjectName( this, s ); +} + diff --git a/karbon/core/vobject.h b/karbon/core/vobject.h new file mode 100644 index 00000000..9aa6885a --- /dev/null +++ b/karbon/core/vobject.h @@ -0,0 +1,299 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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. +*/ + +#ifndef __VOBJECT_H__ +#define __VOBJECT_H__ + + +#include <KoRect.h> +#include <dcopobject.h> +#include <koffice_export.h> + +class QDomElement; +class VDocument; +class VFill; +class VPainter; +class VStroke; +class VVisitor; +class DCOPObject; +class KoStore; +class KoXmlWriter; +class KoOasisLoadingContext; +class KoGenStyles; +class KoGenStyle; + +/** + * The base class for all karbon objects. Every object should + * have the ability to draw itself using a painter, perform + * hit detection, transform on demand, clone and load/save itself. + * Also each object manages its own bounding box and keeps track of its + * parent object. + */ +class KARBONBASE_EXPORT VObject +{ +public: + enum VState + { + normal = 0, /**< visible, not active */ + normal_locked = 1, /**< visible, but locked (r/o) */ + hidden = 2, /**< hidden */ + hidden_locked = 3, /**< hidden and locked (r/o) */ + deleted = 4, /**< deleted, nearly dead */ + + // shape specific states: + selected = 5, /**< visible, active and can be manipulated by tools */ + edit = 6 /**< visible, active and is currently manipulated by a tool */ + }; + + /** + * Constructs a new object that is child of parent and has the given state. + * + * @param parent the new object's parent + * @param state the new object's state + */ + VObject( VObject* parent, VState state = edit ); + + /** + * Copy constructor. + * Copies parent, state and name of given object. + * + * @param obj the object to copy properties from + */ + VObject( const VObject& obj ); + + /** + * Destroys the object and deletes the stroke, fill and DCOP-object. + */ + virtual ~VObject(); + + /** + * Returns pointer to internal DCOP object. + * + * If no internal DCOP object exist yet, it is created. + */ + virtual DCOPObject* dcopObject(); + + /** + * Draw the object to a painting device. + * + * @param painter abstraction that is used to render to a painting device. + * @param rect represents the visible rectangular area. If this object doesn't + * intersect with this area it is not drawn. + */ + virtual void draw( VPainter* painter, const KoRect* rect = 0L ) const + { + Q_UNUSED( painter ); + Q_UNUSED( rect ); + } + + /** + * Calculates the tightest bounding box around the object. + * + * @return the bounding box. + */ + virtual const KoRect& boundingBox() const + { return m_boundingBox; } + + /** + * Checks if the bounding box is invalid and needs to be recalculated. + * + * @return true if bounding box is invalid. + */ + bool boundingBoxIsInvalid() const + { return m_boundingBoxIsInvalid; } + + /** + * Invalidates the bounding box, so it has to be recalculated. + * This function is public so visitors can access it themself at the right + * time when they manipulate many VSegments. + */ + void invalidateBoundingBox() + { + m_boundingBoxIsInvalid = true; + + if( m_parent ) + m_parent->invalidateBoundingBox(); + } + + /** + * Sets a new parent object. + * + * @param parent the new parent object + */ + void setParent( VObject* parent ) { m_parent = parent; } + + /** + * Returns pointer to current parent object. + * + * @return pointer to current parent object or 0 if no parent object is set + */ + VObject* parent() const { return m_parent; } + + /** + * Get the state the object is in. + * + * @return the object state at time of calling. + */ + VState state() const { return m_state; } + + /** + * Sets the state to a specified new state. + * Note that this will not have any visual effect until draw() is + * called on this object. + * + * @param state the new state. + */ + virtual void setState( const VState state ) { m_state = state; } + + /** + * Gets the object's actual stroke. + * + * @return pointer to the object's stroke + */ + virtual VStroke* stroke() const { return m_stroke; } + + /** + * Gets the object's actual fill. + * + * @return pointer to the object's fill + */ + virtual VFill* fill() const { return m_fill; } + + /** + * Sets the stroke to a given new stroke. + * + * @param stroke the new stroke + */ + virtual void setStroke( const VStroke& stroke ); + + /** + * Sets the fill to a given new fill. + * + * @param fill the new fill + */ + virtual void setFill( const VFill& fill ); + + /** + * Save this object's state to xml. + * + * @param element the DOM element to which the attributes are saved + */ + virtual void save( QDomElement& element ) const; + + /** + * Save this object's state to OpenDocument. + * + * @param store FIXME + * @param docWriter FIXME + * @param mainStyles FIXME + */ + virtual void saveOasis( KoStore *store, KoXmlWriter *docWriter, KoGenStyles &mainStyles, int &index ) const; + + /** + * Load this object's state from xml and initialize + * this object accordingly. + * + * @param element the DOM element from which the attributes are read + */ + virtual void load( const QDomElement& element ); + + /** + * Load this object's state from OpenDocument and initialize + * this object accordingly. + * + * @param element the DOM element to read attributes from + * @param context FIXME + */ + virtual bool loadOasis( const QDomElement &element, KoOasisLoadingContext &context ); + + /** + * Create an exact copy of this object. + * + * @return the exact object copy + */ + virtual VObject* clone() const = 0; + + /** + * Accept a VVisitor. + */ + virtual void accept( VVisitor& /*visitor*/ ) + { } + + /** + * This function is important for undo/redo. It inserts newObject in front + * of oldObject. + * + * @param newObject the new object to insert + * @param oldObject the old object the new object is inserted in front of + */ + virtual void insertInfrontOf( VObject* newObject, VObject* oldObject ) + { + Q_UNUSED( newObject ); + Q_UNUSED( oldObject ); + } + + /** + * Returns the name of the object. + * + * @return the object's name + */ + virtual QString name() const; + + /** + * Sets the object's name to a given new name. + * + * @param s the new object name + */ + void setName( const QString &s ); + + /** + * Return document the object belongs to. + * + * @return pointer to parent document or 0 if object does not belong to a document + */ + VDocument *document() const; + +protected: + /** + * Adds a new given style to the specified OASIS context + * + * @param style FIXME + * @param context FIXME + */ + void addStyles( const QDomElement* style, KoOasisLoadingContext & context ); + + virtual void saveOasisFill( KoGenStyles &mainStyles, KoGenStyle &stylesojectauto ) const; + +protected: + mutable KoRect m_boundingBox; /**< the object's bounding box */ + mutable VState m_state : 8; /**< the object's state */ + mutable bool m_boundingBoxIsInvalid : 1; /**< the flag stating if the bounding box is valid */ + + VStroke* m_stroke; /**< the object's stroke */ + VFill* m_fill; /**< the object's fill */ + + DCOPObject *m_dcop; /**< the object's DCOP object */ + +private: + VObject* m_parent; +}; + +#endif + diff --git a/karbon/core/vobject_iface.cc b/karbon/core/vobject_iface.cc new file mode 100644 index 00000000..c93b68d6 --- /dev/null +++ b/karbon/core/vobject_iface.cc @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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 "vobject_iface.h" +#include "vobject.h" + +#include <kapplication.h> +#include <dcopclient.h> + +VObjectIface::VObjectIface( VObject* obj ) + : DCOPObject(), m_object( obj ) +{ +} + +int +VObjectIface::state() const +{ + return (int)m_object->state(); +} + +void +VObjectIface::setState( int state ) +{ + return m_object->setState( (VObject::VState)state ); +} + +DCOPRef +VObjectIface::parent() const +{ + if( !m_object->parent() ) + return DCOPRef(); + + return DCOPRef( kapp->dcopClient()->appId(), m_object->parent()->dcopObject()->objId() ); +} + diff --git a/karbon/core/vobject_iface.h b/karbon/core/vobject_iface.h new file mode 100644 index 00000000..e1684ba2 --- /dev/null +++ b/karbon/core/vobject_iface.h @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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. +*/ + +#ifndef __VOBJECT_IFACE_H__ +#define __VOBJECT_IFACE_H__ + +#include <KoRect.h> + +class VObject; +class QDomElement; +class VFill; +class VPainter; +class VStroke; +class VVisitor; + +#include <dcopobject.h> +#include <dcopref.h> + +class VObjectIface : public DCOPObject +{ + K_DCOP + +public: + VObjectIface( VObject *obj ); + +k_dcop: + /*void draw( VPainter*, const KoRect* = 0L ) const {} + const KoRect& boundingBox() const { return m_boundingBox; } + + bool boundingBoxIsInvalid() const; + void invalidateBoundingBox(); + void setParent( VObject* parent ) { m_parent = parent; }*/ + + DCOPRef parent() const; + + int state() const; + void setState( int state ); + + /*VStroke* stroke() const { return m_stroke; } + VFill* fill() const { return m_fill; } + void setStroke( const VStroke& stroke ); + void setFill( const VFill& fill ); + void save( QDomElement& element ) const; + void load( const QDomElement& element ); + VObject* clone() const = 0; + void accept( VVisitor& ) {} + void insertInfrontOf( VObject* , VObject* ) { }*/ + +private: + VObject* m_object; +}; + +#endif + diff --git a/karbon/core/vpath.cc b/karbon/core/vpath.cc new file mode 100644 index 00000000..a9fcc8a1 --- /dev/null +++ b/karbon/core/vpath.cc @@ -0,0 +1,1153 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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 <math.h> + +#include <qdom.h> +#include <qvaluelist.h> +#include <qwmatrix.h> + +#include "vpath.h" +#include "vsegment.h" +#include "vvisitor.h" + +#include <kdebug.h> + + +class VSubpathIteratorList +{ +public: + VSubpathIteratorList() + : m_list( 0L ), m_iterator( 0L ) + {} + + ~VSubpathIteratorList() + { + notifyClear( true ); + delete m_list; + } + + void add( VSubpathIterator* itr ) + { + if( !m_iterator ) + m_iterator = itr; + else if( m_list ) + m_list->push_front( itr ); + else + { + m_list = new QValueList<VSubpathIterator*>; + m_list->push_front( itr ); + } + } + + void remove( VSubpathIterator* itr ) + { + if( m_iterator == itr ) + m_iterator = 0L; + else if( m_list ) + { + m_list->remove( itr ); + + if( m_list->isEmpty() ) + { + delete m_list; + m_list = 0L; + } + } + } + + void notifyClear( bool zeroList ) + { + if( m_iterator ) + { + if( zeroList ) + m_iterator->m_list = 0L; + + m_iterator->m_current = 0L; + } + + if( m_list ) + { + for( + QValueList<VSubpathIterator*>::Iterator itr = m_list->begin(); + itr != m_list->end(); + ++itr ) + { + if( zeroList ) + ( *itr )->m_list = 0L; + + ( *itr )->m_current = 0L; + } + } + } + + void notifyRemove( VSegment* segment, VSegment* current ) + { + if( m_iterator ) + { + if( m_iterator->m_current == segment ) + m_iterator->m_current = current; + } + + if( m_list ) + { + for( + QValueList<VSubpathIterator*>::Iterator itr = m_list->begin(); + itr != m_list->end(); + ++itr ) + { + if( ( *itr )->m_current == segment ) + ( *itr )->m_current = current; + } + } + } + +private: + QValueList<VSubpathIterator*>* m_list; + VSubpathIterator* m_iterator; +}; + + +VSubpath::VSubpath( VObject* parent ) + : VObject( parent ) +{ + m_isClosed = false; + + m_first = m_last = m_current = 0L; + m_number = 0; + m_currentIndex = -1; + m_iteratorList = 0L; + + // Add an initial segment. + append( new VSegment( 1 ) ); +} + +VSubpath::VSubpath( const VSubpath& list ) + : VObject( list ) +{ + m_isClosed = list.isClosed(); + + m_first = m_last = m_current = 0L; + m_number = 0; + m_currentIndex = -1; + m_iteratorList = 0L; + + VSegment* segment = list.m_first; + + while( segment ) + { + append( segment->clone() ); + segment = segment->m_next; + } +} + +VSubpath::VSubpath( const VSegment& segment ) + : VObject( 0L ) +{ + m_isClosed = false; + + m_first = m_last = m_current = 0L; + m_number = 0; + m_currentIndex = -1; + m_iteratorList = 0L; + + // The segment is not a "begin" segment. + if( segment.prev() ) + { + // Add an initial segment. + append( new VSegment( 1 ) ); + + // Move the "begin" segment to the new segment's previous knot. + moveTo( segment.prev()->knot() ); + } + + // Append a copy of the segment. + append( segment.clone() ); +} + +VSubpath::~VSubpath() +{ + clear(); + delete m_iteratorList; +} + +const KoPoint& +VSubpath::currentPoint() const +{ + return getLast()->knot(); +} + +bool +VSubpath::moveTo( const KoPoint& p ) +{ + // Move "begin" segment if path is still empty. + if( isEmpty() ) + { + getLast()->setKnot( p ); + return true; + } + + return false; +} + +bool +VSubpath::lineTo( const KoPoint& p ) +{ + if( isClosed() ) + return false; + + VSegment* s = new VSegment( 1 ); + + s->setDegree( 1 ); + s->setKnot( p ); + + append( s ); + + return true; +} + +bool +VSubpath::curveTo( + const KoPoint& p1, const KoPoint& p2, const KoPoint& p3 ) +{ + if( isClosed() ) + return false; + + VSegment* s = new VSegment(); + + s->setDegree( 3 ); + s->setPoint( 0, p1 ); + s->setPoint( 1, p2 ); + s->setPoint( 2, p3 ); + + append( s ); + + + return true; +} + +bool +VSubpath::curve1To( const KoPoint& p2, const KoPoint& p3 ) +{ + if( isClosed() ) + return false; + + VSegment* s = new VSegment(); + + s->setDegree( 3 ); + s->setPoint( 0, currentPoint() ); + s->setPoint( 1, p2 ); + s->setPoint( 2, p3 ); + + append( s ); + + + return true; +} + +bool +VSubpath::curve2To( const KoPoint& p1, const KoPoint& p3 ) +{ + if( isClosed() ) + return false; + + VSegment* s = new VSegment(); + + s->setDegree( 3 ); + s->setPoint( 0, p1 ); + s->setPoint( 1, p3 ); + s->setPoint( 2, p3 ); + + append( s ); + + + return true; +} + +bool +VSubpath::arcTo( + const KoPoint& p1, const KoPoint& p2, const double r ) +{ + /* This routine is inspired by code in GNU ghostscript. + * + * |- P1B3 -| + * + * |- - - T12- - -| + * + * - - P1 x....__--o.....x P2 + * | | : _/ B3 + * P1B0 : / + * | :/ + * | | + * - T10 o B0 + * | + * | | + * | + * | | + * - x P0 + */ + + if( isClosed() || r < 0.0 ) + return false; + + + // We need to calculate the tangent points. Therefore calculate tangents + // T10=P1P0 and T12=P1P2 first. + double dx0 = currentPoint().x() - p1.x(); + double dy0 = currentPoint().y() - p1.y(); + double dx2 = p2.x() - p1.x(); + double dy2 = p2.y() - p1.y(); + + // Calculate distance squares. + double dsqT10 = dx0 * dx0 + dy0 * dy0; + double dsqT12 = dx2 * dx2 + dy2 * dy2; + + // We now calculate tan(a/2) where a is the angle between T10 and T12. + // We benefit from the facts T10*T12 = |T10|*|T12|*cos(a), + // |T10xT12| = |T10|*|T12|*sin(a) (cross product) and tan(a/2) = sin(a)/[1-cos(a)]. + double num = dy0 * dx2 - dy2 * dx0; + + double denom = sqrt( dsqT10 * dsqT12 ) - ( dx0 * dx2 + dy0 * dy2 ); + + // The points are colinear. + if( 1.0 + denom == 1.0 ) + { + // Just add a line. + lineTo( p1 ); + } + else + { + // |P1B0| = |P1B3| = r * tan(a/2). + double dP1B0 = fabs( r * num / denom ); + + // B0 = P1 + |P1B0| * T10/|T10|. + KoPoint b0 = p1 + KoPoint( dx0, dy0 ) * ( dP1B0 / sqrt( dsqT10 ) ); + + // If B0 deviates from current point P0, add a line to it. + if( !b0.isNear( currentPoint(), VGlobal::isNearRange ) ) + lineTo( b0 ); + + // B3 = P1 + |P1B3| * T12/|T12|. + KoPoint b3 = p1 + KoPoint( dx2, dy2 ) * ( dP1B0 / sqrt( dsqT12 ) ); + + + // The two bezier-control points are located on the tangents at a fraction + // of the distance[ tangent points <-> tangent intersection ]. + const KoPoint d = p1 - b0; + + double distsq = d * d; + + double rsq = r * r; + + double fract; + + // r is very small. + if( distsq >= rsq * VGlobal::veryBigNumber ) + { + // Assume dist = r = 0. + fract = 0.0; + } + else + { + fract = ( 4.0 / 3.0 ) / ( 1.0 + sqrt( 1.0 + distsq / rsq ) ); + } + + KoPoint b1 = b0 + ( p1 - b0 ) * fract; + KoPoint b2 = b3 + ( p1 - b3 ) * fract; + + // Finally add the bezier-segment. + curveTo( b1, b2, b3 ); + } + + return true; +} + +void +VSubpath::close() +{ + // In the case the list is 100% empty (which should actually never happen), + // append a "begin" first, to avoid a crash. + if( count() == 0 ) + append( new VSegment( 1 ) ); + + // Move last segment if we are already closed. + if( isClosed() ) + { + getLast()->setKnot( getFirst()->knot() ); + } + // Append a line, if necessary. + else + { + if( + getLast()->knot().isNear( + getFirst()->knot(), VGlobal::isNearRange ) ) + { + // Move last knot. + getLast()->setKnot( getFirst()->knot() ); + } + else + { + // Add a line. + lineTo( getFirst()->knot() ); + } + + m_isClosed = true; + } +} + +bool +VSubpath::pointIsInside( const KoPoint& p ) const +{ + // If the point is not inside the boundingbox, it cannot be inside the path either. + if( !boundingBox().contains( p ) ) + return false; + + // First check if the point is inside the knot polygon (beziers are treated + // as lines). + + /* This algorithm is taken from "Fast Winding Number Inclusion of a Point + * in a Polygon" by Dan Sunday, geometryalgorithms.com. + */ + + /* + int windingNumber = 0; + + // Ommit first segment. + VSegment* segment = getFirst()->next(); + + while( segment ) + { + if( segment->prev()->knot().y() <= p.y() ) + { + // Upward crossing. + if( segment->knot().y() > p.y() ) + { + // Point is left. + if( segment->pointIsLeft( p ) > 0 ) + { + // Valid up intersection. + ++windingNumber; + } + } + } + else + { + // Downward crossing. + if( segment->knot().y() <= p.y() ) + { + // Point is right. + if( segment->pointIsLeft( p ) < 0 ) + { + // Valid down intersection. + --windingNumber; + } + } + } + + segment = segment->next(); + } + + if( static_cast<bool>( windingNumber ) ) + return true; + */ + + // Then check if the point is located in between the knot polygon + // and the bezier curves. + + /* We rotate each segment in order to make their chord (the line between + * the previous knot and the knot ) parallel to the x-axis. Then we + * calculate y(xp) on the segment for the rotated input point (xp,yp) + * and compare y(xp) with yp. + */ +// TODO + + // cache the closed evaluation + bool closed = isClosed() || getLast()->knot() == getFirst()->knot(); + + QValueList<double> rparams; + + VSegment* segment = getFirst()->next(); + + // move all segements so that p is the origin + // and compute their intersections with the x-axis + while( segment ) + { + VSubpath tmpCurve( 0L ); + tmpCurve.append( new VSegment( segment->degree() ) ); + + for( int i = 0; i <= segment->degree(); ++i ) + tmpCurve.current()->setP(i, segment->p(i)-p ); + + tmpCurve.current()->rootParams( rparams ); + + segment = segment->next(); + } + + // if the path is not closed, compute the intersection of + // the line through the first and last knot and the x-axis too + if( ! closed ) + { + KoPoint prevKnot = getLast()->knot() - p; + KoPoint nextKnot = getFirst()->knot() - p; + + double dx = nextKnot.x() - prevKnot.x(); + double dy = nextKnot.y() - prevKnot.y(); + if( dx == 0.0 ) + { + rparams.append( nextKnot.x() ); + } + else if( dy != 0.0 ) + { + if( ( prevKnot.y() < 0.0 && nextKnot.y() > 0.0 ) || ( prevKnot.y() > 0.0 && nextKnot.y() < 0.0 ) ) + { + double n = prevKnot.y() - dy / dx * prevKnot.x(); + rparams.append( -n * dx / dy ); + } + } + } + + kdDebug(38000) << "intersection count: " << rparams.count() << endl; + + // sort all intersections + qHeapSort( rparams ); + + QValueList<double>::iterator itr, etr = rparams.end(); + + for( itr = rparams.begin(); itr != etr; ++itr ) + kdDebug(38000) << "intersection: " << *itr << endl; + + if( closed ) + { + // pair the intersections and check if the origin is within a pair + for( itr = rparams.begin(); itr != etr; ++itr ) + { + if( *itr > 0.0 ) + return false; + + if( ++itr == etr ) + return false; + + if( *itr > 0.0 ) + return true; + } + } + else + { + // only check if point is between first and last intersection if we have an open path + if ( rparams.front() < 0.0 && rparams.back() > 0.0 ) + return true; + } + + return false; +} + +bool +VSubpath::intersects( const VSegment& s ) const +{ + // Check if path is empty and if boundingboxes intersect. + if( + isEmpty() || + !boundingBox().intersects( s.boundingBox() ) ) + { + return false; + } + + + // Ommit first segment. + VSegment* segment = getFirst()->next(); + + while( segment ) + { + if( segment->intersects( s ) ) + { + return true; + } + + segment = segment->next(); + } + + return false; +} + +bool +VSubpath::counterClockwise() const +{ + /* This algorithm is taken from the FAQ of comp.graphics.algorithms: + * "Find the lowest vertex (or, if there is more than one vertex with the + * same lowest coordinate, the rightmost of those vertices) and then take + * the cross product of the edges fore and aft of it." + */ + + // A non closed path does not have a winding. + if( !isClosed() ) + { + return false; + } + + + VSegment* segment = getFirst(); + + // We save the segment not the knot itself. Initialize it with the + // first segment: + const VSegment* bottomRight = getFirst(); + + while( segment ) + { + if( segment->knot().y() < bottomRight->knot().y() ) + bottomRight = segment; + else if( segment->knot().y() - bottomRight->knot().y() + < VGlobal::isNearRange ) + { + if( segment->knot().x() > bottomRight->knot().x() ) + bottomRight = segment; + } + + segment = segment->next(); + } + + + // Catch boundary case (bottomRight is first or last segment): + const VSegment* current; + const VSegment* next; + + if( bottomRight == getFirst() ) + current = getLast(); + else + current = bottomRight; + + if( bottomRight == getLast() ) + next = getFirst()->next(); + else + next = bottomRight->next(); + + // Check "z-component" of cross product: + return + ( next->knot().x() - next->prev()->knot().x() ) * + ( current->knot().y() - current->prev()->knot().y() ) + - + ( next->knot().y() - next->prev()->knot().y() ) * + ( current->knot().x() - current->prev()->knot().x() ) < 0.0; +} + +void +VSubpath::revert() +{ + // Catch case where the list is "empty". + if( isEmpty() ) + return; + + + VSubpath list( parent() ); + list.moveTo( getLast()->knot() ); + + VSegment* segment = getLast(); + + while( segment->prev() ) + { + list.append( segment->revert() ); + segment = segment->prev(); + } + + list.m_isClosed = isClosed(); + + *this = list; +} + +const KoRect& +VSubpath::boundingBox() const +{ + if( m_boundingBoxIsInvalid ) + { + // Reset the boundingbox. + m_boundingBox = KoRect(); + + VSegment* segment = m_first; + + while( segment ) + { + if( segment->state() != VSegment::deleted ) + m_boundingBox |= segment->boundingBox(); + + segment = segment->m_next; + } + + m_boundingBoxIsInvalid = false; + } + + return m_boundingBox; +} + +VSubpath* +VSubpath::clone() const +{ + return new VSubpath( *this ); +} + +void +VSubpath::saveSvgPath( QString &d ) const +{ + // Save segments. + VSegment* segment = getFirst(); + + while( segment ) + { + if( segment->state() == VSegment::normal ) + { + if( segment->degree() <= 2 ) + { + // Line. + if( segment->prev() ) + { + d += QString( "L%1 %2" ). + arg( segment->knot().x() ).arg( segment->knot().y() ); + } + // Moveto. + else + { + d += QString( "M%1 %2" ). + arg( segment->knot().x() ).arg( segment->knot().y() ); + } + } + // Bezier ( degree >= 3 ). + else + { + // We currently treat all beziers as cubic beziers. + d += QString( "C%1 %2 %3 %4 %5 %6" ). + arg( segment->point( segment->degree() - 3 ).x() ). + arg( segment->point( segment->degree() - 3 ).y() ). + arg( segment->point( segment->degree() - 2 ).x() ). + arg( segment->point( segment->degree() - 2 ).y() ). + arg( segment->knot().x() ). + arg( segment->knot().y() ); + } + } + + segment = segment->m_next; + } + + if( isClosed() ) + d += "Z"; +} + +// TODO: remove this backward compatibility function after koffice 1.3.x +void +VSubpath::load( const QDomElement& element ) +{ + // We might have a "begin" segment. + clear(); + + QDomNodeList list = element.childNodes(); + + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement segment = list.item( i ).toElement(); + + VSegment* s = new VSegment(); + s->load( segment ); + append( s ); + } + } + + if( element.attribute( "isClosed" ) == 0 ? false : true ) + close(); +} + +void +VSubpath::accept( VVisitor& visitor ) +{ + visitor.visitVSubpath( *this ); +} + + +VSubpath& +VSubpath::operator=( const VSubpath& list ) +{ + if( this == &list ) + return *this; + + m_isClosed = list.isClosed(); + + clear(); + + VSegment* segment = list.m_first; + + while( segment ) + { + append( segment->clone() ); + segment = segment->m_next; + } + + m_current = m_first; + m_currentIndex = 0; + + return *this; +} + +bool +VSubpath::insert( const VSegment* segment ) +{ + if( m_currentIndex == -1 ) + return false; + + VSegment* s = const_cast<VSegment*>( segment ); + + VSegment* prev = m_current->m_prev; + + m_current->m_prev = s; + prev->m_next = s; + s->m_prev = prev; + s->m_next = m_current; + + m_current = s; + ++m_number; + + invalidateBoundingBox(); + + return true; +} + +bool +VSubpath::insert( uint index, const VSegment* segment ) +{ + VSegment* s = const_cast<VSegment*>( segment ); + + if( index == 0 ) + { + prepend( s ); + return true; + } + else if( index == m_number ) + { + append( s ); + return true; + } + + VSegment* next = locate( index ); + + if( !next ) + return false; + + VSegment* prev = next->m_prev; + + next->m_prev = s; + prev->m_next = s; + s->m_prev = prev; + s->m_next = next; + + m_current = s; + ++m_number; + + invalidateBoundingBox(); + + return true; +} + +void +VSubpath::prepend( const VSegment* segment ) +{ + VSegment* s = const_cast<VSegment*>( segment ); + + s->m_prev = 0L; + + if( ( s->m_next = m_first ) ) + m_first->m_prev = s; + else + m_last = s; + + m_first = m_current = s; + + ++m_number; + m_currentIndex = 0; + + invalidateBoundingBox(); +} + +void +VSubpath::append( const VSegment* segment ) +{ + VSegment* s = const_cast<VSegment*>( segment ); + + s->m_next = 0L; + + if( ( s->m_prev = m_last ) ) + m_last->m_next = s; + else + m_first = s; + + m_last = m_current = s; + + m_currentIndex = m_number; + ++m_number; + + invalidateBoundingBox(); +} + +void +VSubpath::clear() +{ + VSegment* segment = m_first; + + m_first = m_last = m_current = 0L; + m_number = 0; + m_currentIndex = -1; + + if( m_iteratorList ) + m_iteratorList->notifyClear( false ); + + VSegment* prev; + + while( segment ) + { + prev = segment; + segment = segment->m_next; + delete prev; + } + + m_isClosed = false; + + invalidateBoundingBox(); +} + +VSegment* +VSubpath::first() +{ + if( m_first ) + { + m_currentIndex = 0; + return m_current = m_first; + } + + return 0L; +} + +VSegment* +VSubpath::last() +{ + if( m_last ) + { + m_currentIndex = m_number - 1; + return m_current = m_last; + } + + return 0L; +} + +VSegment* +VSubpath::prev() +{ + if( m_current ) + { + if( m_current->m_prev ) + { + --m_currentIndex; + return m_current = m_current->m_prev; + } + + m_currentIndex = -1; + m_current = 0L; + } + + return 0L; +} + +VSegment* +VSubpath::next() +{ + if( m_current ) + { + if( m_current->m_next ) + { + ++m_currentIndex; + return m_current = m_current->m_next; + } + + m_currentIndex = -1; + m_current = 0L; + } + + return 0L; +} + +VSegment* +VSubpath::locate( uint index ) +{ + if( index == static_cast<uint>( m_currentIndex ) ) + return m_current; + + if( !m_current && m_first ) + { + m_current = m_first; + m_currentIndex = 0; + } + + VSegment* segment; + int distance = index - m_currentIndex; + bool forward; + + if( index >= m_number ) + return 0L; + + if( distance < 0 ) + distance = -distance; + + if( + static_cast<uint>( distance ) < index && + static_cast<uint>( distance ) < m_number - index ) + { + segment = m_current; + forward = index > static_cast<uint>( m_currentIndex ); + } + else if( index < m_number - index ) + { + segment = m_first; + distance = index; + forward = true; + } + else + { + segment = m_last; + distance = m_number - index - 1; + if( distance < 0 ) + distance = 0; + forward = false; + } + + if( forward ) + { + while( distance-- ) + segment = segment->m_next; + } + else + { + while( distance-- ) + segment = segment->m_prev; + } + + m_currentIndex = index; + return m_current = segment; +} + + +VSubpathIterator::VSubpathIterator( const VSubpath& list ) +{ + m_list = const_cast<VSubpath*>( &list ); + m_current = m_list->m_first; + + if( !m_list->m_iteratorList ) + m_list->m_iteratorList = new VSubpathIteratorList(); + + m_list->m_iteratorList->add( this ); +} + +VSubpathIterator::VSubpathIterator( const VSubpathIterator& itr ) +{ + m_list = itr.m_list; + m_current = itr.m_current; + + if( m_list ) + m_list->m_iteratorList->add( this ); +} + +VSubpathIterator::~VSubpathIterator() +{ + if( m_list ) + m_list->m_iteratorList->remove( this ); +} + +VSubpathIterator& +VSubpathIterator::operator=( const VSubpathIterator& itr ) +{ + if( m_list ) + m_list->m_iteratorList->remove( this ); + + m_list = itr.m_list; + m_current = itr.m_current; + + if( m_list ) + m_list->m_iteratorList->add( this ); + + return *this; +} + +VSegment* +VSubpathIterator::current() const +{ + // If m_current points to a deleted segment, find the next not + // deleted segment. + if( + m_current && + m_current->state() == VSegment::deleted ) + { + return m_current->next(); + } + + return m_current; +} + +VSegment* +VSubpathIterator::operator()() +{ + if( VSegment* const old = current() ) + { + m_current = current()->next(); + return old; + } + + return 0L; +} + +VSegment* +VSubpathIterator::operator++() +{ + if( current() ) + return m_current = current()->next(); + + return 0L; +} + +VSegment* +VSubpathIterator::operator+=( uint i ) +{ + while( current() && i-- ) + m_current = current()->next(); + + return current(); +} + +VSegment* +VSubpathIterator::operator--() +{ + if( current() ) + return m_current = current()->prev(); + + return 0L; +} + +VSegment* +VSubpathIterator::operator-=( uint i ) +{ + while( current() && i-- ) + m_current = current()->prev(); + + return current(); +} + diff --git a/karbon/core/vpath.h b/karbon/core/vpath.h new file mode 100644 index 00000000..3287a75e --- /dev/null +++ b/karbon/core/vpath.h @@ -0,0 +1,203 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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. +*/ + +#ifndef __VPATH_H__ +#define __VPATH_H__ + + +#include <KoPoint.h> + +#include "vobject.h" +#include <koffice_export.h> + +class QDomElement; +class QWMatrix; +class VSubpathIteratorList; +class VSegment; +class VVisitor; + + +/** + * VSubpath provides a sophisticated list of VSegment. Noted: it also may contain + * segments which are marked "deleted". If you are not interested in those undo/redo + * housholding data, please always use a VSubpathIterator to access segments. + */ + +class KARBONBASE_EXPORT VSubpath : public VObject +{ + friend class VSubpathIterator; + +public: + VSubpath( VObject* parent ); + VSubpath( const VSubpath& list ); + VSubpath( const VSegment& segment ); + virtual ~VSubpath(); + + const KoPoint& currentPoint() const; + + bool moveTo( const KoPoint& p ); + bool lineTo( const KoPoint& p ); + bool curveTo( + const KoPoint& p1, const KoPoint& p2, const KoPoint& p3 ); + bool curve1To( + const KoPoint& p2, const KoPoint& p3 ); + bool curve2To( + const KoPoint& p1, const KoPoint& p3 ); + bool arcTo( + const KoPoint& p1, const KoPoint& p2, const double r ); + + bool isClosed() const + { + return m_isClosed; + } + + void close(); + + + /** + * Returns true if point p is located inside the path. + * The winding number test is used. + */ + bool pointIsInside( const KoPoint& p ) const; + + /** + * Returns true if the segment intersects this path. + */ + bool intersects( const VSegment& segment ) const; + + + /** + * Returns false if segmentlist is oriented clockwise. + */ + bool counterClockwise() const; + + /** + * Reverts the winding orientation. + */ + void revert(); + + + /** + * Returns true if the current path is "emtpy". That means that it has + * zero or just one ( == "begin") segment. + */ + bool isEmpty() const + { + return count() <= 1; + } + + + virtual const KoRect& boundingBox() const; + + + virtual void save( QDomElement& /*element*/) const + { } // VSubpaths cant be saved. + + // TODO: remove this backward compatibility function after koffice 1.3.x + virtual void load( const QDomElement& element ); + + void saveSvgPath( QString & ) const; + + + virtual VSubpath* clone() const; + + virtual void accept( VVisitor& visitor ); + + + // General list stuff. + VSubpath& operator=( const VSubpath& list ); + + bool insert( const VSegment* segment ); + bool insert( uint i, const VSegment* segment ); + void prepend( const VSegment* segment ); + void append( const VSegment* segment ); + void clear(); + + uint count() const + { + return m_number; + } + + VSegment* current() const + { + return m_current; + } + + VSegment* getFirst() const + { + return m_first; + } + + VSegment* getLast() const + { + return m_last; + } + + VSegment* first(); + VSegment* last(); + VSegment* prev(); + VSegment* next(); + +private: + VSegment* locate( uint index ); + + VSegment* m_first; + VSegment* m_last; + VSegment* m_current; + + int m_currentIndex; + uint m_number : 31; + + bool m_isClosed : 1; + + VSubpathIteratorList* m_iteratorList; +}; + + +/** + * VSubpathIterator provides an iterator class for highlevel path access. + * Use VSubpathIterator whenever you want to access segments but are not interested + * in undo/redo operations with (deleted) segments. + */ + +class KARBONBASE_EXPORT VSubpathIterator +{ + friend class VSubpathIteratorList; + +public: + VSubpathIterator( const VSubpath& list ); + VSubpathIterator( const VSubpathIterator& itr ); + ~VSubpathIterator(); + + VSubpathIterator& operator=( const VSubpathIterator& itr ); + + VSegment* current() const; + VSegment* operator()(); + VSegment* operator++(); + VSegment* operator+=( uint i ); + VSegment* operator--(); + VSegment* operator-=( uint i ); + +private: + VSubpath* m_list; + VSegment* m_current; +}; + +#endif + diff --git a/karbon/core/vpattern.cc b/karbon/core/vpattern.cc new file mode 100644 index 00000000..9929a1b9 --- /dev/null +++ b/karbon/core/vpattern.cc @@ -0,0 +1,140 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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 <qdom.h> + +#include "vpattern.h" +#include <qpixmap.h> +#define THUMB_SIZE 30 + +VPattern::VPattern() +{ + m_valid = false; + validThumb = false; +} + +VPattern::VPattern( const QString &tilename ) +{ + load( tilename ); +} + +void +VPattern::load( const QString &tilename ) +{ + m_tilename = tilename; + bool ok = m_image.load( tilename ); + + if( !ok ) + { + m_valid = false; + return; + } + + m_image = m_image.convertDepth( 32 ); + m_pixmap.convertFromImage(m_image, QPixmap::AutoColor); + if( m_image.width() > THUMB_SIZE || m_image.height() > THUMB_SIZE ) + { + int xsize = THUMB_SIZE; + int ysize = THUMB_SIZE; + int picW = m_image.width(); + int picH = m_image.height(); + if( picW > picH ) + { + float yFactor = (float)((float)(float)picH/(float)picW); + ysize = (int)(yFactor * (float)THUMB_SIZE); + if(ysize > 30) ysize = 30; + } + else if( picW < picH ) + { + float xFactor = (float)((float)picW/(float)picH); + xsize = (int)(xFactor * (float)THUMB_SIZE); + if(xsize > 30) xsize = 30; + } + + QImage thumbImg = m_image.smoothScale( xsize, ysize ); + m_pixmapThumb.convertFromImage( thumbImg ); + validThumb = true; + } + m_valid = !m_image.isNull(); +} + +unsigned char * +VPattern::pixels() +{ + return m_image.bits(); +} + +unsigned int +VPattern::tileWidth() const +{ + return m_image.width(); +} + +unsigned int +VPattern::tileHeight() const +{ + return m_image.height(); +} + +void +VPattern::save( QDomElement& element ) const +{ + QDomElement me = element.ownerDocument().createElement( "PATTERN" ); + + me.setAttribute( "originX", m_origin.x() ); + me.setAttribute( "originY", m_origin.y() ); + me.setAttribute( "vectorX", m_vector.x() ); + me.setAttribute( "vectorY", m_vector.y() ); + + me.setAttribute( "tilename", m_tilename ); + + element.appendChild( me ); +} + +void +VPattern::load( const QDomElement& element ) +{ + m_origin.setX( element.attribute( "originX", "0.0" ).toDouble() ); + m_origin.setY( element.attribute( "originY", "0.0" ).toDouble() ); + m_vector.setX( element.attribute( "vectorX", "0.0" ).toDouble() ); + m_vector.setY( element.attribute( "vectorY", "0.0" ).toDouble() ); + + m_tilename = element.attribute( "tilename" ); + load( m_tilename ); +} + +void +VPattern::transform( const QWMatrix &m ) +{ + m_origin = m_origin.transform( m ); + m_vector = m_vector.transform( m ); +} + +QPixmap& VPattern::pixmap() const +{ + return (QPixmap&)m_pixmap; +} + +QPixmap& VPattern::thumbPixmap() const +{ + return (QPixmap&)m_pixmapThumb; +} + + + diff --git a/karbon/core/vpattern.h b/karbon/core/vpattern.h new file mode 100644 index 00000000..04870d7d --- /dev/null +++ b/karbon/core/vpattern.h @@ -0,0 +1,73 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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. +*/ + +#ifndef __VPATTERN_H__ +#define __VPATTERN_H__ + +#include <KoPoint.h> +#include <koIconChooser.h> +#include <qimage.h> +#include <qpixmap.h> +#include <koffice_export.h> +class QDomElement; + + +class KARBONBASE_EXPORT VPattern : public KoIconItem +{ +public: + VPattern(); + VPattern( const QString &tilename ); + + unsigned char *pixels(); + unsigned int tileWidth() const; + unsigned int tileHeight() const; + + KoPoint origin() const { return m_origin; } + void setOrigin( const KoPoint &origin ) { m_origin = origin; } + + KoPoint vector() const { return m_vector; } + void setVector( const KoPoint &vector ) { m_vector = vector; } + + void load( const QString &tilename ); + + void save( QDomElement& element ) const; + void load( const QDomElement& element ); + + void transform( const QWMatrix& m ); + + // for KoIconItem + QPixmap& pixmap() const ; + QPixmap& thumbPixmap() const; + + bool isValid() const { return m_valid; } + + QString tilename() const { return m_tilename; } + +private: + // coordinates: + KoPoint m_origin; + KoPoint m_vector; + QImage m_image; + QPixmap m_pixmap; + QPixmap m_pixmapThumb; + QString m_tilename; + bool m_valid; +}; + +#endif diff --git a/karbon/core/vsegment.cc b/karbon/core/vsegment.cc new file mode 100644 index 00000000..3f0a4590 --- /dev/null +++ b/karbon/core/vsegment.cc @@ -0,0 +1,1112 @@ +/* This file is part 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 <math.h> + +#include <qdom.h> + +#include "vpainter.h" +#include "vpath.h" +#include "vsegment.h" + +#include <kdebug.h> + +VSegment::VSegment( unsigned short deg ) +{ + m_degree = deg; + + m_nodes = new VNodeData[ degree() ]; + + for( unsigned short i = 0; i < degree(); ++i ) + selectPoint( i ); + + m_state = normal; + + m_prev = 0L; + m_next = 0L; +} + +VSegment::VSegment( const VSegment& segment ) +{ + m_degree = segment.degree(); + + m_nodes = new VNodeData[ degree() ]; + + m_state = segment.m_state; + + // Copying the pointers m_prev/m_next has some advantages (see VSegment::length()). + // Inserting a segment into a path overwrites these anyway. + m_prev = segment.m_prev; + m_next = segment.m_next; + + // Copy points. + for( unsigned short i = 0; i < degree(); i++ ) + { + setPoint( i, segment.point( i ) ); + selectPoint( i, segment.pointIsSelected( i ) ); + } +} + +VSegment::~VSegment() +{ + delete[]( m_nodes ); +} + +void +VSegment::setDegree( unsigned short deg ) +{ + // Do nothing if old and new degrees are identical. + if( degree() == deg ) + return; + + // TODO : this code is fishy, please make it sane + + // Remember old nodes. + VNodeData* oldNodes = m_nodes; + KoPoint oldKnot = knot(); + + // Allocate new node data. + m_nodes = new VNodeData[ deg ]; + + if( deg == 1 ) + m_nodes[ 0 ].m_vector = oldKnot; + else + { + // Copy old node data (from the knot "backwards". + unsigned short offset = kMax( 0, deg - m_degree ); + + for( unsigned short i = offset; i < deg; ++i ) + { + m_nodes[ i ].m_vector = oldNodes[ i - offset ].m_vector; + } + + // Fill with "zeros" if necessary. + for( unsigned short i = 0; i < offset; ++i ) + { + m_nodes[ i ].m_vector = KoPoint( 0.0, 0.0 ); + } + } + + // Set new degree. + m_degree = deg; + + // Delete old nodes. + delete[]( oldNodes ); +} + +void +VSegment::draw( VPainter* painter ) const +{ + // Don't draw a deleted segment. + if( state() == deleted ) + return; + + + if( prev() ) + { + if( degree() == 3 ) + { + painter->curveTo( point( 0 ), point( 1 ), point( 2 ) ); + } + else + { + painter->lineTo( knot() ); + } + } + else + { + painter->moveTo( knot() ); + } +} + +bool +VSegment::isFlat( double flatness ) const +{ + // Lines and "begin" segments are flat. + if( + !prev() || + degree() == 1 ) + { + return true; + } + + + // Iterate over control points. + for( unsigned short i = 0; i < degree() - 1; ++i ) + { + if( + height( prev()->knot(), point( i ), knot() ) / chordLength() + >= flatness ) + { + return false; + } + } + + return true; +} + +KoPoint +VSegment::pointAt( double t ) const +{ + KoPoint p; + + pointDerivativesAt( t, &p ); + + return p; +} + +void +VSegment::pointDerivativesAt( double t, KoPoint* p, + KoPoint* d1, KoPoint* d2 ) const +{ + if( !prev() ) + return; + + + // Optimise the line case. + if( degree() == 1 ) + { + const KoPoint diff = knot() - prev()->knot(); + + if( p ) + *p = prev()->knot() + diff * t; + + if( d1 ) + *d1 = diff; + + if( d2 ) + *d2 = KoPoint( 0.0, 0.0 ); + + return; + } + + + // Beziers. + + // Copy points. + KoPoint* q = new KoPoint[ degree() + 1 ]; + + q[ 0 ] = prev()->knot(); + + for( unsigned short i = 0; i < degree(); ++i ) + { + q[ i + 1 ] = point( i ); + } + + + // The De Casteljau algorithm. + for( unsigned short j = 1; j <= degree(); j++ ) + { + for( unsigned short i = 0; i <= degree() - j; i++ ) + { + q[ i ] = ( 1.0 - t ) * q[ i ] + t * q[ i + 1 ]; + } + + // Save second derivative now that we have it. + if( j == degree() - 2 ) + { + if( d2 ) + *d2 = degree() * ( degree() - 1 ) + * ( q[ 2 ] - 2 * q[ 1 ] + q[ 0 ] ); + } + + // Save first derivative now that we have it. + else if( j == degree() - 1 ) + { + if( d1 ) + *d1 = degree() * ( q[ 1 ] - q[ 0 ] ); + } + } + + // Save point. + if( p ) + *p = q[ 0 ]; + + delete[]( q ); + + + return; +} + +KoPoint +VSegment::tangentAt( double t ) const +{ + KoPoint tangent; + + pointTangentNormalAt( t, 0L, &tangent ); + + return tangent; +} + +void +VSegment::pointTangentNormalAt( double t, KoPoint* p, + KoPoint* tn, KoPoint* n ) const +{ + // Calculate derivative if necessary. + KoPoint d; + + pointDerivativesAt( t, p, tn || n ? &d : 0L ); + + + // Normalize derivative. + if( tn || n ) + { + const double norm = + sqrt( d.x() * d.x() + d.y() * d.y() ); + + d = norm ? d * ( 1.0 / norm ) : KoPoint( 0.0, 0.0 ); + } + + // Assign tangent vector. + if( tn ) + *tn = d; + + // Calculate normal vector. + if( n ) + { + // Calculate vector product of "binormal" x tangent + // (0,0,1) x (dx,dy,0), which is simply (dy,-dx,0). + n->setX( d.y() ); + n->setY( -d.x() ); + } +} + +double +VSegment::length( double t ) const +{ + if( !prev() || t == 0.0 ) + { + return 0.0; + } + + + // Optimise the line case. + if( degree() == 1 ) + { + return + t * chordLength(); + } + + + /* This algortihm is by Jens Gravesen <gravesen AT mat DOT dth DOT dk>. + * We calculate the chord length "chord"=|P0P3| and the length of the control point + * polygon "poly"=|P0P1|+|P1P2|+|P2P3|. The approximation for the bezier length is + * 0.5 * poly + 0.5 * chord. "poly - chord" is a measure for the error. + * We subdivide each segment until the error is smaller than a given tolerance + * and add up the subresults. + */ + + // "Copy segment" splitted at t into a path. + VSubpath path( 0L ); + path.moveTo( prev()->knot() ); + + // Optimize a bit: most of the time we'll need the + // length of the whole segment. + if( t == 1.0 ) + path.append( this->clone() ); + else + { + VSegment* copy = this->clone(); + path.append( copy->splitAt( t ) ); + delete copy; + } + + + double chord; + double poly; + + double length = 0.0; + + while( path.current() ) + { + chord = path.current()->chordLength(); + poly = path.current()->polyLength(); + + if( + poly && + ( poly - chord ) / poly > VGlobal::lengthTolerance ) + { + // Split at midpoint. + path.insert( + path.current()->splitAt( 0.5 ) ); + } + else + { + length += 0.5 * poly + 0.5 * chord; + path.next(); + } + } + + + return length; +} + +double +VSegment::chordLength() const +{ + if( !prev() ) + return 0.0; + + + KoPoint d = knot() - prev()->knot(); + + return sqrt( d * d ); +} + +double +VSegment::polyLength() const +{ + if( !prev() ) + return 0.0; + + + // Start with distance |first point - previous knot|. + KoPoint d = point( 0 ) - prev()->knot(); + + double length = sqrt( d * d ); + + // Iterate over remaining points. + for( unsigned short i = 1; i < degree(); ++i ) + { + d = point( i ) - point( i - 1 ); + length += sqrt( d * d ); + } + + + return length; +} + +double +VSegment::lengthParam( double len ) const +{ + if( + !prev() || + len == 0.0 ) // We divide by len below. + { + return 0.0; + } + + + // Optimise the line case. + if( degree() == 1 ) + { + return + len / chordLength(); + } + + + // Perform a successive interval bisection. + double param1 = 0.0; + double paramMid = 0.5; + double param2 = 1.0; + + double lengthMid = length( paramMid ); + + while( QABS( lengthMid - len ) / len > VGlobal::paramLengthTolerance ) + { + if( lengthMid < len ) + param1 = paramMid; + else + param2 = paramMid; + + paramMid = 0.5 * ( param2 + param1 ); + + lengthMid = length( paramMid ); + } + + return paramMid; +} + +double +VSegment::nearestPointParam( const KoPoint& p ) const +{ + if( !prev() ) + { + return 1.0; + } + + + /* This function solves the "nearest point on curve" problem. That means, it + * calculates the point q (to be precise: it's parameter t) on this segment, which + * is located nearest to the input point P. + * The basic idea is best described (because it is freely available) in "Phoenix: + * An Interactive Curve Design System Based on the Automatic Fitting of + * Hand-Sketched Curves", Philip J. Schneider (Master thesis, University of + * Washington). + * + * For the nearest point q = C(t) on this segment, the first derivative is + * orthogonal to the distance vector "C(t) - P". In other words we are looking for + * solutions of f(t) = ( C(t) - P ) * C'(t) = 0. + * ( C(t) - P ) is a nth degree curve, C'(t) a n-1th degree curve => f(t) is a + * (2n - 1)th degree curve and thus has up to 2n - 1 distinct solutions. + * We solve the problem f(t) = 0 by using something called "Approximate Inversion Method". + * Let's write f(t) explicitly (with c_i = p_i - P and d_j = p_{j+1} - p_j): + * + * n n-1 + * f(t) = SUM c_i * B^n_i(t) * SUM d_j * B^{n-1}_j(t) + * i=0 j=0 + * + * n n-1 + * = SUM SUM w_{ij} * B^{2n-1}_{i+j}(t) + * i=0 j=0 + * + * with w_{ij} = c_i * d_j * z_{ij} and + * + * BinomialCoeff( n, i ) * BinomialCoeff( n - i ,j ) + * z_{ij} = ----------------------------------------------- + * BinomialCoeff( 2n - 1, i + j ) + * + * This Bernstein-Bezier polynom representation can now be solved for it's roots. + */ + + + // Calculate the c_i = point( i ) - P. + KoPoint* c = new KoPoint[ degree() + 1 ]; + + c[ 0 ] = prev()->knot() - p; + + for( unsigned short i = 1; i <= degree(); ++i ) + { + c[ i ] = point( i - 1 ) - p; + } + + + // Calculate the d_j = point( j + 1 ) - point( j ). + KoPoint* d = new KoPoint[ degree() ]; + + d[ 0 ] = point( 0 ) - prev()->knot(); + + for( unsigned short j = 1; j <= degree() - 1; ++j ) + { + d[ j ] = 3.0 * ( point( j ) - point( j - 1 ) ); + } + + + // Calculate the z_{ij}. + double* z = new double[ degree() * ( degree() + 1 ) ]; + + for( unsigned short j = 0; j <= degree() - 1; ++j ) + { + for( unsigned short i = 0; i <= degree(); ++i ) + { + z[ j * ( degree() + 1 ) + i ] = + static_cast<double>( + VGlobal::binomialCoeff( degree(), i ) * + VGlobal::binomialCoeff( degree() - i, j ) ) + / + static_cast<double>( + VGlobal::binomialCoeff( 2 * degree() - 1, i + j ) ); + } + } + + + // Calculate the dot products of c_i and d_i. + double* products = new double[ degree() * ( degree() + 1 ) ]; + + for( unsigned short j = 0; j <= degree() - 1; ++j ) + { + for( unsigned short i = 0; i <= degree(); ++i ) + { + products[ j * ( degree() + 1 ) + i ] = + d[ j ] * c[ i ]; + } + } + + // We don't need the c_i and d_i anymore. + delete[]( d ); + delete[]( c ); + + + // Calculate the control points of the new 2n-1th degree curve. + VSubpath newCurve( 0L ); + newCurve.append( new VSegment( 2 * degree() - 1 ) ); + + // Set up control points in the ( u, f(u) )-plane. + for( unsigned short u = 0; u <= 2 * degree() - 1; ++u ) + { + newCurve.current()->setP( + u, + KoPoint( + static_cast<double>( u ) / static_cast<double>( 2 * degree() - 1 ), + 0.0 ) ); + } + + + // Set f(u)-values. + for( unsigned short k = 0; k <= 2 * degree() - 1; ++k ) + { + unsigned short min = kMin( k, degree() ); + + for( + unsigned short i = kMax( 0, k - ( degree() - 1 ) ); + i <= min; + ++i ) + { + unsigned short j = k - i; + + // p_k += products[j][i] * z[j][i]. + newCurve.getLast()->setP( + k, + KoPoint( + newCurve.getLast()->p( k ).x(), + newCurve.getLast()->p( k ).y() + + products[ j * ( degree() + 1 ) + i ] * + z[ j * ( degree() + 1 ) + i ] ) ); + } + } + + // We don't need the c_i/d_i dot products and the z_{ij} anymore. + delete[]( products ); + delete[]( z ); + +kdDebug(38000) << "results" << endl; +for( int i = 0; i <= 2 * degree() - 1; ++i ) +{ +kdDebug(38000) << newCurve.getLast()->p( i ).x() << " " +<< newCurve.getLast()->p( i ).y() << endl; +} +kdDebug(38000) << endl; + + // Find roots. + QValueList<double> params; + + newCurve.getLast()->rootParams( params ); + + + // Now compare the distances of the candidate points. + double resultParam; + double distanceSquared; + double oldDistanceSquared; + KoPoint dist; + + // First candidate is the previous knot. + dist = prev()->knot() - p; + distanceSquared = dist * dist; + resultParam = 0.0; + + // Iterate over the found candidate params. + for( QValueListConstIterator<double> itr = params.begin(); itr != params.end(); ++itr ) + { + pointDerivativesAt( *itr, &dist ); + dist -= p; + oldDistanceSquared = distanceSquared; + distanceSquared = dist * dist; + + if( distanceSquared < oldDistanceSquared ) + resultParam = *itr; + } + + // Last candidate is the knot. + dist = knot() - p; + oldDistanceSquared = distanceSquared; + distanceSquared = dist * dist; + + if( distanceSquared < oldDistanceSquared ) + resultParam = 1.0; + + + return resultParam; +} + +void +VSegment::rootParams( QValueList<double>& params ) const +{ + if( !prev() ) + { + return; + } + + + // Calculate how often the control polygon crosses the x-axis + // This is the upper limit for the number of roots. + switch( controlPolygonZeros() ) + { + // No solutions. + case 0: + return; + // Exactly one solution. + case 1: + if( isFlat( VGlobal::flatnessTolerance / chordLength() ) ) + { + // Calculate intersection of chord with x-axis. + KoPoint chord = knot() - prev()->knot(); + +kdDebug(38000) << prev()->knot().x() << " " << prev()->knot().y() +<< knot().x() << " " << knot().y() << " ---> " +<< ( chord.x() * prev()->knot().y() - chord.y() * prev()->knot().x() ) / - chord.y() << endl; + params.append( + ( chord.x() * prev()->knot().y() - chord.y() * prev()->knot().x() ) + / - chord.y() ); + + return; + } + break; + } + + // Many solutions. Do recursive midpoint subdivision. + VSubpath path( *this ); + path.insert( path.current()->splitAt( 0.5 ) ); + + path.current()->rootParams( params ); + path.next()->rootParams( params ); +} + +int +VSegment::controlPolygonZeros() const +{ + if( !prev() ) + { + return 0; + } + + + int signChanges = 0; + + int sign = VGlobal::sign( prev()->knot().y() ); + int oldSign; + + for( unsigned short i = 0; i < degree(); ++i ) + { + oldSign = sign; + sign = VGlobal::sign( point( i ).y() ); + + if( sign != oldSign ) + { + ++signChanges; + } + } + + + return signChanges; +} + +bool +VSegment::isSmooth( const VSegment& next ) const +{ + // Return false if this segment is a "begin". + if( !prev() ) + return false; + + + // Calculate tangents. + KoPoint t1; + KoPoint t2; + + pointTangentNormalAt( 1.0, 0L, &t1 ); + + next.pointTangentNormalAt( 0.0, 0L, &t2 ); + + + // Dot product. + if( t1 * t2 >= VGlobal::parallelTolerance ) + return true; + + return false; +} + +KoRect +VSegment::boundingBox() const +{ + // Initialize with knot. + KoRect rect( knot(), knot() ); + + // Add p0, if it exists. + if( prev() ) + { + if( prev()->knot().x() < rect.left() ) + rect.setLeft( prev()->knot().x() ); + + if( prev()->knot().x() > rect.right() ) + rect.setRight( prev()->knot().x() ); + + if( prev()->knot().y() < rect.top() ) + rect.setTop( prev()->knot().y() ); + + if( prev()->knot().y() > rect.bottom() ) + rect.setBottom( prev()->knot().y() ); + } + + if( degree() == 3 ) + { + /* + The basic idea for calculating the axis aligned bounding box (AABB) for bezier segments + was found in comp.graphics.algorithms: + + Both the x coordinate and the y coordinate are polynomial. Newton told + us that at a maximum or minimum the derivative will be zero. Take all + those points, and take the ends; their AABB will be that of the curve. + + We have a helpful trick for the derivatives: use the curve defined by + differences of successive control points. + This is a quadratic Bezier curve: + + 2 + r(t) = Sum Bi,2(t) *Pi = B0,2(t) * P0 + B1,2(t) * P1 + B2,2(t) * P2 + i=0 + + r(t) = (1-t)^2 * P0 + 2t(1-t) * P1 + t^2 * P2 + + r(t) = (P2 - 2*P1 + P0) * t^2 + (2*P1 - 2*P0) * t + P0 + + Setting r(t) to zero and using the x and y coordinates of differences of + successive control points lets us find the paramters t, where the original + bezier curve has a minimum or a maximum. + */ + double t[4]; + + // calcualting the differnces between successive control points + KoPoint x0 = p(1)-p(0); + KoPoint x1 = p(2)-p(1); + KoPoint x2 = p(3)-p(2); + + // calculating the coefficents + KoPoint a = x2 - 2.0*x1 + x0; + KoPoint b = 2.0*x1 - 2.0*x0; + KoPoint c = x0; + + // calculating parameter t at minimum/maximum in x-direction + if( a.x() == 0.0 ) + { + t[0] = - c.x() / b.x(); + t[1] = -1.0; + } + else + { + double rx = b.x()*b.x() - 4.0*a.x()*c.x(); + if( rx < 0.0 ) + rx = 0.0; + t[0] = ( -b.x() + sqrt( rx ) ) / (2.0*a.x()); + t[1] = ( -b.x() - sqrt( rx ) ) / (2.0*a.x()); + } + + // calculating parameter t at minimum/maximum in y-direction + if( a.y() == 0.0 ) + { + t[2] = - c.y() / b.y(); + t[3] = -1.0; + } + else + { + double ry = b.y()*b.y() - 4.0*a.y()*c.y(); + if( ry < 0.0 ) + ry = 0.0; + t[2] = ( -b.y() + sqrt( ry ) ) / (2.0*a.y()); + t[3] = ( -b.y() - sqrt( ry ) ) / (2.0*a.y()); + } + + // calculate points at found minimum/maximum and update bounding box + for( int i = 0; i < 4; ++i ) + { + if( t[i] >= 0.0 && t[i] <= 1.0 ) + { + KoPoint p = pointAt( t[i] ); + + if( p.x() < rect.left() ) + rect.setLeft( p.x() ); + + if( p.x() > rect.right() ) + rect.setRight( p.x() ); + + if( p.y() < rect.top() ) + rect.setTop( p.y() ); + + if( p.y() > rect.bottom() ) + rect.setBottom( p.y() ); + } + } + + return rect; + } + + for( unsigned short i = 0; i < degree() - 1; ++i ) + { + if( point( i ).x() < rect.left() ) + rect.setLeft( point( i ).x() ); + + if( point( i ).x() > rect.right() ) + rect.setRight( point( i ).x() ); + + if( point( i ).y() < rect.top() ) + rect.setTop( point( i ).y() ); + + if( point( i ).y() > rect.bottom() ) + rect.setBottom( point( i ).y() ); + } + + + return rect; +} + +VSegment* +VSegment::splitAt( double t ) +{ + if( !prev() ) + { + return 0L; + } + + + // Create new segment. + VSegment* segment = new VSegment( degree() ); + + // Set segment state. + segment->m_state = m_state; + + + // Lines are easy: no need to modify the current segment. + if( degree() == 1 ) + { + segment->setKnot( + prev()->knot() + + ( knot() - prev()->knot() ) * t ); + + return segment; + } + + + // Beziers. + + // Copy points. + KoPoint* q = new KoPoint[ degree() + 1 ]; + + q[ 0 ] = prev()->knot(); + + for( unsigned short i = 0; i < degree(); ++i ) + { + q[ i + 1 ] = point( i ); + } + + + // The De Casteljau algorithm. + for( unsigned short j = 1; j <= degree(); ++j ) + { + for( unsigned short i = 0; i <= degree() - j; ++i ) + { + q[ i ] = ( 1.0 - t ) * q[ i ] + t * q[ i + 1 ]; + } + + // Modify the new segment. + segment->setPoint( j - 1, q[ 0 ] ); + } + + // Modify the current segment (no need to modify the knot though). + for( unsigned short i = 1; i < degree(); ++i ) + { + setPoint( i - 1, q[ i ] ); + } + + + delete[]( q ); + + + return segment; +} + +double +VSegment::height( + const KoPoint& a, + const KoPoint& p, + const KoPoint& b ) +{ + // Calculate determinant of AP and AB to obtain projection of vector AP to + // the orthogonal vector of AB. + const double det = + p.x() * a.y() + b.x() * p.y() - p.x() * b.y() - + a.x() * p.y() + a.x() * b.y() - b.x() * a.y(); + + // Calculate norm = length(AB). + const KoPoint ab = b - a; + const double norm = sqrt( ab * ab ); + + // If norm is very small, simply use distance AP. + if( norm < VGlobal::verySmallNumber ) + return + sqrt( + ( p.x() - a.x() ) * ( p.x() - a.x() ) + + ( p.y() - a.y() ) * ( p.y() - a.y() ) ); + + // Normalize. + return QABS( det ) / norm; +} + +bool +VSegment::linesIntersect( + const KoPoint& a0, + const KoPoint& a1, + const KoPoint& b0, + const KoPoint& b1 ) +{ + const KoPoint delta_a = a1 - a0; + const double det_a = a1.x() * a0.y() - a1.y() * a0.x(); + + const double r_b0 = delta_a.y() * b0.x() - delta_a.x() * b0.y() + det_a; + const double r_b1 = delta_a.y() * b1.x() - delta_a.x() * b1.y() + det_a; + + if( r_b0 != 0.0 && r_b1 != 0.0 && r_b0 * r_b1 > 0.0 ) + return false; + + const KoPoint delta_b = b1 - b0; + + const double det_b = b1.x() * b0.y() - b1.y() * b0.x(); + + const double r_a0 = delta_b.y() * a0.x() - delta_b.x() * a0.y() + det_b; + const double r_a1 = delta_b.y() * a1.x() - delta_b.x() * a1.y() + det_b; + + if( r_a0 != 0.0 && r_a1 != 0.0 && r_a0 * r_a1 > 0.0 ) + return false; + + return true; +} + +bool +VSegment::intersects( const VSegment& segment ) const +{ + if( + !prev() || + !segment.prev() ) + { + return false; + } + + + //TODO: this just dumbs down beziers to lines! + return linesIntersect( segment.prev()->knot(), segment.knot(), prev()->knot(), knot() ); +} + +// TODO: Move this function into "userland" +uint +VSegment::nodeNear( const KoPoint& p, double isNearRange ) const +{ + int index = 0; + + for( unsigned short i = 0; i < degree(); ++i ) + { + if( point( 0 ).isNear( p, isNearRange ) ) + { + index = i + 1; + break; + } + } + + return index; +} + +VSegment* +VSegment::revert() const +{ + if( !prev() ) + return 0L; + + // Create new segment. + VSegment* segment = new VSegment( degree() ); + + segment->m_state = m_state; + + + // Swap points. + for( unsigned short i = 0; i < degree() - 1; ++i ) + { + segment->setPoint( i, point( degree() - 2 - i ) ); + } + + segment->setKnot( prev()->knot() ); + + + // TODO swap node attributes (selected) + + return segment; +} + +VSegment* +VSegment::prev() const +{ + VSegment* segment = m_prev; + + while( segment && segment->state() == deleted ) + { + segment = segment->m_prev; + } + + return segment; +} + +VSegment* +VSegment::next() const +{ + VSegment* segment = m_next; + + while( segment && segment->state() == deleted ) + { + segment = segment->m_next; + } + + return segment; +} + +// TODO: remove this backward compatibility function after koffice 1.3.x +void +VSegment::load( const QDomElement& element ) +{ + if( element.tagName() == "CURVE" ) + { + setDegree( 3 ); + + setPoint( + 0, + KoPoint( + element.attribute( "x1" ).toDouble(), + element.attribute( "y1" ).toDouble() ) ); + + setPoint( + 1, + KoPoint( + element.attribute( "x2" ).toDouble(), + element.attribute( "y2" ).toDouble() ) ); + + setKnot( + KoPoint( + element.attribute( "x3" ).toDouble(), + element.attribute( "y3" ).toDouble() ) ); + } + else if( element.tagName() == "LINE" ) + { + setDegree( 1 ); + + setKnot( + KoPoint( + element.attribute( "x" ).toDouble(), + element.attribute( "y" ).toDouble() ) ); + } + else if( element.tagName() == "MOVE" ) + { + setDegree( 1 ); + + setKnot( + KoPoint( + element.attribute( "x" ).toDouble(), + element.attribute( "y" ).toDouble() ) ); + } +} + +VSegment* +VSegment::clone() const +{ + return new VSegment( *this ); +} + diff --git a/karbon/core/vsegment.h b/karbon/core/vsegment.h new file mode 100644 index 00000000..402946a8 --- /dev/null +++ b/karbon/core/vsegment.h @@ -0,0 +1,432 @@ +/* This file is part 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. +*/ + +#ifndef __VSEGMENT_H__ +#define __VSEGMENT_H__ + +#include <qptrlist.h> +#include <qvaluelist.h> + +#include <KoPoint.h> +#include <KoRect.h> + +#include "vglobal.h" +#include <koffice_export.h> + +class QDomElement; +class VPainter; + +/** + * A class representing lines and beziers. We waste some KoPoints, if we + * would use only lines, but this makes it easy to convert the segment types + * into each other. Make sure yourself, that you pass values to functions within + * proper ranges. + */ + +class KARBONBASE_EXPORT VSegment +{ + friend class VSubpath; + friend class VSubpathIterator; + +public: + /** + * Tells which control point is "fixed" i.e. located at the + * corresponding knot and invisible. This flag makes no sense for + * line segments. + */ + enum VCtrlPointFixing + { + none = 0, + first = 1, + second = 2 + }; + + enum VState + { + normal, + deleted + }; + + + VSegment( unsigned short deg = 3 ); + + VSegment( const VSegment& segment ); + + ~VSegment(); + + /** + * Returns the segment's degree, which is identical to the number of nodes. + * For cubic beziers it is "three" and "one" for lines. + */ + unsigned short degree() const + { + return m_degree; + } + + /** + * Sets the segment's degree and thus resizes the array of node data. + * The node data is copied from the old knot "backwards". + */ + void setDegree( unsigned short deg ); + + /** + * Tests for the segment type ("begin", "line" or "curve"). + */ + bool isBegin() const { return (degree() == 1) && !prev(); } + bool isLine() const { return (degree() == 1) && prev(); } + bool isCurve() const { return degree() > 1; } + + /** + * Returns the segment state. + */ + VState state() const + { + return m_state; + } + + /** + * Sets the segment state. + */ + void setState( VState state ) + { + m_state = state; + } + + + /** + * Returns the segment's point with index 0 <= i < degree(). + */ + const KoPoint& point( int i ) const + { + return m_nodes[ i ].m_vector; + } + + /** + * This is a convenience function. It returns the point with index + * 0 <= i <= degree() while p( 0 ) is the knot of the previous + * segment. + */ + const KoPoint& p( int i ) const + { + return i == 0 + ? prev()->knot() + : m_nodes[ --i ].m_vector; + } + + /** + * Returns the knot. This is a convenience function using point(). + */ + const KoPoint& knot() const + { + return point( degree() - 1 ); + } + + /** + * Sets the segment's point with index 0 <= i < degree() to "p". + */ + void setPoint( int i, const KoPoint& p ) + { + m_nodes[ i ].m_vector = p; + } + + /** + * This is a convenience function. It sets the point with index + * 0 <= i <= degree() to "p" while setP( 0 ) sets the knot of the + * previous segment. + */ + void setP( int i, const KoPoint& p ) + { + if( i == 0 ) + prev()->setKnot( p ); + else + m_nodes[ --i ].m_vector = p; + } + + /** + * Sets the knot. This is a convenience function. + */ + void setKnot( const KoPoint& p ) + { + m_nodes[ degree() - 1 ].m_vector = p; + } + + + /** + * Returns true if the point with index 0 <= i < degree() is selected. + */ + bool pointIsSelected( int i ) const + { + return m_nodes[ i ].m_isSelected; + } + + /** + * Returns true if the knot is selected. This is a convenience function. + */ + bool knotIsSelected() const + { + return m_nodes[ degree() - 1 ].m_isSelected; + } + + /** + * Selects the point with index 0 <= i < degree(). + */ + void selectPoint( int i, bool select = true ) + { + m_nodes[ i ].m_isSelected = select; + } + + /** + * Selects/deselects the knot of this segment. + */ + void selectKnot( bool select = true ) + { + m_nodes[ degree() - 1 ].m_isSelected = select; + } + + + /** + * Returns index of the node at point p. Returns 0 if no + * segment point matches point p. + */ + // TODO: Move this function into "userland" + uint nodeNear( const KoPoint& p, + double isNearRange = VGlobal::isNearRange ) const; + + + /** + * Returns a pointer to the previous not deleted segment, if + * stored in a VSubpath. + */ + VSegment* prev() const; + + /** + * Returns a pointer to the next not deleted segment, if + * stored in a VSubpath. + */ + VSegment* next() const; + + /** + * Returns true if the segment is flat. That means it's height + * is smaller than flatness. + */ + bool isFlat( double flatness = VGlobal::flatnessTolerance ) const; + + + /** + * Calculates the point on this segment at parameter 0 <= t <= 1. + * This is a convenience wrapper for pointDerivativesAt(). + */ + KoPoint pointAt( double t ) const; + + /** + * Calculates the point and the derivatives of first and + * second order for 0 <= t <= 1. + */ + void pointDerivativesAt( double t, KoPoint* p = 0L, + KoPoint* d1 = 0L, KoPoint* d2 = 0L ) const; + + + /** + * Calculates the normalized tangent vector (length=1) at the point + * parameterized by 0 <= t <= 1. This is a convenience wrapper + * for pointTangentNormalAt(). Use the latter function directly if you + * need to calculate the point and normal vector or tangent vector + * at once. + */ + KoPoint tangentAt( double t ) const; + + /** + * Calculates the point, the tangent vector and the normal vector for + * 0 <= t <= 1. The tangent vector and the normal vector are + * normalized (length=1). + */ + void pointTangentNormalAt( double t, KoPoint* p = 0L, + KoPoint* tn = 0L, KoPoint* n = 0L ) const; + + + /** + * Calculates the arclength from p0 to the point parametrized + * by 0 <= t <= 1. For beziers this function is a bit expensive. + */ + double length( double t = 1.0 ) const; + + /** + * Calculates the chord length (the distance from the previous + * knot to the current knot). + */ + double chordLength() const; + + /** + * Calculates the length of the control polygon. + */ + double polyLength() const; + + + /** + * Calculates the parameter of a point located at arclength len. + * This is the exact inverse operation of length( t ). + */ + double lengthParam( double len ) const; + + + /** + * Calculates the parameter of the nearest point on this segment + * to the point p. This function is pretty expensive. + */ + double nearestPointParam( const KoPoint& p ) const; + + + /** + * Calculates wether the tangent at the knot is exactly parallel to + * the tangent at p0 of the next segment. Returns false if the + * current segment is a "begin". + */ + bool isSmooth( const VSegment& next ) const; + + bool isSmooth() const + { + return next() + ? isSmooth( *next() ) + : false; + } + + + /** + * Creates a reverted version of this segment. For example: + * if this segment is a line from A to B, the result is a + * line from B to A. + */ + VSegment* revert() const; + + + /** + * Splits the segment at parameter 0 <= t <= 1. Returns a pointer + * to the first segment and modifies the current one to + * be the second segment. + */ + VSegment* splitAt( double t ); + + + /** + * Calculates height of point p above line AB. + */ + static double height( + const KoPoint& a, + const KoPoint& p, + const KoPoint& b ); + + + /** + * Calculates whether lines A0A1 and B0B1 intersect. + */ + static bool linesIntersect( + const KoPoint& a0, + const KoPoint& a1, + const KoPoint& b0, + const KoPoint& b1 ); + + /** + * Returns true, if this segment intersects the other segment. + */ + bool intersects( const VSegment& segment ) const; + + + /** + * Returns a number > 0 if the point p is left, 0 if it's on and + * a number < 0 if it's right of the infinite line through the + * previous segment's knot and the current knot. + */ + double pointIsLeft( const KoPoint& p ) const + { + return + ( knot().x() - prev()->knot().x() ) * + ( p.y() - prev()->knot().y() ) + - + ( p.x() - prev()->knot().x() ) * + ( knot().y() - prev()->knot().y() ); + } + + /** + * Calculates the bounding box. + */ + KoRect boundingBox() const; + + + void draw( VPainter* painter ) const; + + // TODO: remove this backward compatibility function after koffice 1.3.x. + void load( const QDomElement& element ); + + + /** + * Returns a pointer to a copy of this segment. + */ + VSegment* clone() const; + +private: + /** + * Calculates the solutions of y(x) = 0 where 0 <= x <= 1. The + * returned parameters are not ordered. + */ + void rootParams( QValueList<double>& params ) const; + + /** + * Calculates how often the control polygon crosses the x-axis. + */ + int controlPolygonZeros() const; + + + /** + * The segment degree. For (cubic) beziers "three", "one" for lines. + */ + unsigned short m_degree : 6; + + /** + * The segment state. + */ + VState m_state : 2; + + /** + * Node data. + */ + struct VNodeData + { + KoPoint m_vector; + bool m_isSelected; + }; + + /** + * A pointer to an array of node data. + */ + VNodeData* m_nodes; + + + /** + * Pointer to the previous segment. + */ + VSegment* m_prev; + + /** + * Pointer to the next segment. + */ + VSegment* m_next; +}; + +#endif + diff --git a/karbon/core/vselection.cc b/karbon/core/vselection.cc new file mode 100644 index 00000000..ec9626ce --- /dev/null +++ b/karbon/core/vselection.cc @@ -0,0 +1,328 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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 "vdocument.h" +#include "vdrawselection.h" +#include "vpainter.h" +#include "vselection.h" +#include "vselectnodes.h" +#include "vselectobjects.h" +#include "vvisitor.h" +#include "vcolor.h" +#include "vfill.h" +#include "vstroke.h" + +uint VSelection::m_handleNodeSize = 3; + +VSelection::VSelection( VObject* parent ) + : VObject( parent ), m_showhandle( true ) +{ + m_handleRect = new KoRect[ 10 ]; + setStroke( VStroke( VColor( Qt::black ) ) ); + setFill( VFill() ); + + m_selectObjects = true; +} + +VSelection::VSelection( const VSelection& selection ) + : VObject( selection ), VVisitor() +{ + m_handleRect = new KoRect[ 10 ]; + + VObjectListIterator itr = selection.m_objects; + for ( ; itr.current() ; ++itr ) + append( itr.current() ); // Don't clone objects here. + + m_showhandle = true; + m_selectObjects = selection.m_selectObjects; +} + +VSelection::~VSelection() +{ + //clear(); + delete[]( m_handleRect ); +} + +VSelection* +VSelection::clone() const +{ + return new VSelection( *this ); +} + +void +VSelection::accept( VVisitor& visitor ) +{ + visitor.visitVSelection( *this ); +} + +void +VSelection::take( VObject& object ) +{ + m_objects.removeRef( &object ); + if( object.state() >= selected ) + object.setState( normal ); + invalidateBoundingBox(); +} + +bool +VSelection::take( const KoRect& rect, bool selectObjects, bool exclusive ) +{ + bool success = false; + + if( selectObjects ) + { + VSelectObjects op( m_objects, rect, false ); + if( op.visit( *static_cast<VDocument*>( parent() ) ) ) + { + selectNodes(); + success = true; + } + } + else + { + VObjectListIterator itr( m_objects ); + + // Try to deselect all that have at least one node contained in the rect + for ( ; itr.current(); ++itr ) + { + VSelectNodes op( rect, false, exclusive ); + + if( op.visit( *itr.current() ) ) + { + success = true; + } + } + } + + invalidateBoundingBox(); + + return success; +} + +void +VSelection::append() +{ + clear(); + + VSelectObjects op( m_objects ); + op.visit( *static_cast<VDocument*>( parent() ) ); + selectNodes(); + + invalidateBoundingBox(); +} + +void +VSelection::append( VObject* object ) +{ + // only append if item is not deleted or not already in list + if( object->state() != deleted ) + { + if( ! m_objects.containsRef( object ) ) + m_objects.append( object ); + object->setState( selected ); + invalidateBoundingBox(); + } +} + +void +VSelection::append( const VObjectList &objects ) +{ + VObjectListIterator itr = objects; + for( ; itr.current(); ++itr ) + append( itr.current() ); +} + +bool +VSelection::append( const KoRect& rect, bool selectObjects, bool exclusive ) +{ + bool success = false; + + if( selectObjects ) + { + //m_objects.clear(); + VSelectObjects op( m_objects, rect ); + if( op.visit( *static_cast<VDocument*>( parent() ) ) ) + { + selectNodes(); + success = true; + } + } + else + { + VObjectListIterator itr( m_objects ); + VObjectList notSelected; + + // Try to select all that have at least one node contained in the rect + for ( ; itr.current(); ++itr ) + { + VSelectNodes op( rect, true, exclusive ); + + if( op.visit( *itr.current() ) ) + success = true; + else + notSelected.append( itr.current()); + } + // Remove all that were not selected from this selection + VObjectListIterator jtr( notSelected ); + for ( ; jtr.current(); ++jtr ) + take( *( jtr.current() ) ); + } + + invalidateBoundingBox(); + + return success; +} + +void +VSelection::clear() +{ + VSelectNodes op( true ); + + VObjectListIterator itr = m_objects; + for( ; itr.current(); ++itr ) + { + op.visit( *itr.current() ); + + //if( itr.current()->state() != deleted ) + // itr.current()->setState( normal ); + } + + m_objects.clear(); + invalidateBoundingBox(); +} + +void +VSelection::draw( VPainter* painter, double zoomFactor ) const +{ + if( objects().count() == 0 || state() == VObject::edit ) + return; + + VDrawSelection op( m_objects, painter, !m_selectObjects, m_handleNodeSize ); + op.visitVSelection( (VSelection &)*this ); + + // get bounding box: + const KoRect& rect = boundingBox(); + + // calculate displaycoords of big handle rect: + m_handleRect[ 0 ].setCoords( qRound( rect.left() ), qRound( rect.top() ), + qRound( rect.right() ), qRound( rect.bottom() ) ); + + KoPoint center = m_handleRect[ 0 ].center(); + + double handleNodeSize = m_handleNodeSize / zoomFactor; + + // calculate displaycoords of nodes: + m_handleRect[ node_lb ].setRect( m_handleRect[0].left() - handleNodeSize, m_handleRect[0].top() - handleNodeSize, 2 * handleNodeSize, 2 * handleNodeSize ); + m_handleRect[ node_mb ].setRect( center.x() - handleNodeSize, m_handleRect[0].top() - handleNodeSize, 2 * handleNodeSize, 2 * handleNodeSize ); + m_handleRect[ node_rb ].setRect( m_handleRect[0].right() - handleNodeSize - (1 / zoomFactor), m_handleRect[0].top() - handleNodeSize, 2 * handleNodeSize, 2 * handleNodeSize ); + m_handleRect[ node_rm ].setRect( m_handleRect[0].right() - handleNodeSize - (1 / zoomFactor), center.y() - handleNodeSize, 2 * handleNodeSize, 2 * handleNodeSize ); + m_handleRect[ node_rt ].setRect( m_handleRect[0].right() - handleNodeSize - (1 / zoomFactor) , m_handleRect[0].bottom() - handleNodeSize - (1 / zoomFactor), 2 * handleNodeSize, 2 * handleNodeSize ); + m_handleRect[ node_mt ].setRect( center.x() - handleNodeSize, m_handleRect[0].bottom() - handleNodeSize - (1 / zoomFactor), 2 * handleNodeSize, 2 * handleNodeSize ); + m_handleRect[ node_lt ].setRect( m_handleRect[0].left() - handleNodeSize, m_handleRect[0].bottom() - handleNodeSize - (1 / zoomFactor), 2 * handleNodeSize, 2 * handleNodeSize ); + m_handleRect[ node_lm ].setRect( m_handleRect[0].left() - handleNodeSize, center.y() - handleNodeSize, 2 * handleNodeSize, 2 * handleNodeSize ); + + if( !m_showhandle ) return; + + // draw handle rect: + painter->setPen( Qt::blue.light() ); + painter->setBrush( Qt::NoBrush ); + + painter->drawRect( KoRect( m_handleRect[ 0 ].x() * zoomFactor, m_handleRect[ 0 ].y() * zoomFactor, + m_handleRect[ 0 ].width() * zoomFactor, m_handleRect[ 0 ].height() * zoomFactor ) ); + painter->setPen( Qt::blue.light() ); + + // draw nodes: + if( state() == VObject::selected ) + { + painter->setPen( Qt::blue.light() ); + painter->setBrush( Qt::white ); + + KoRect temp; + for( uint i = node_lt; i <= node_rb; ++i ) + { + if( i != node_mm ) + { + temp.setRect( zoomFactor * m_handleRect[ i ].left(), + zoomFactor * m_handleRect[ i ].top(), + 2 * m_handleNodeSize + 1, 2 * m_handleNodeSize + 1 ); + painter->drawRect( temp ); + } + } + } +} + +const KoRect& +VSelection::boundingBox() const +{ +// disable bbox caching for selection since there is no reliable +// way to get notified of a bbox change: +// if( m_boundingBoxIsInvalid ) +// { + // clear: + m_boundingBox = KoRect(); + + VObjectListIterator itr = m_objects; + for( ; itr.current(); ++itr ) + m_boundingBox |= itr.current()->boundingBox(); + +// m_boundingBoxIsInvalid = false; +// } + + return m_boundingBox; +} + + +VHandleNode +VSelection::handleNode( const KoPoint &point ) const +{ + for( uint i = node_lt; i <= node_rb; ++i ) + { + if( m_handleRect[i].contains( point ) ) + return static_cast<VHandleNode>( i ); + } + + return node_none; +} + +QPtrList<VSegment> +VSelection::getSegments( const KoRect& rect ) +{ + VTestNodes op( rect ); + + VObjectListIterator itr = m_objects; + for( ; itr.current(); ++itr ) + op.visit( *itr.current() ); + + return op.result(); +} + +void +VSelection::selectNodes( bool select ) +{ + VSelectNodes op( select ); + + VObjectListIterator itr = m_objects; + for( ; itr.current(); ++itr ) + { + op.visit( *itr.current() ); + } +} + diff --git a/karbon/core/vselection.h b/karbon/core/vselection.h new file mode 100644 index 00000000..31dbb266 --- /dev/null +++ b/karbon/core/vselection.h @@ -0,0 +1,209 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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. +*/ + +#ifndef __VSELECTION_H__ +#define __VSELECTION_H__ + + +#include <qptrlist.h> + +#include <KoRect.h> + +#include "vobject.h" +#include "vvisitor.h" +#include <koffice_export.h> +class KoPoint; +class QObject; +class VPainter; +class VVisitor; +class VSegment; + +typedef QPtrList<VObject> VObjectList; +typedef QPtrListIterator<VObject> VObjectListIterator; + + +/// Ids of manipulation nodes. +enum VHandleNode +{ + node_none = 0, + node_lt = 1, + node_mt = 2, + node_rt = 3, + node_lm = 4, + node_mm = 5, + node_rm = 6, + node_lb = 7, + node_mb = 8, + node_rb = 9 +}; + + +/** + * VSelection manages a set of selected vobjects. + */ +class KARBONBASE_EXPORT VSelection : public VObject, public VVisitor +{ +public: + /** + * Constructs a vselection with the specified parent. + * + * @param parent the selection's parent + */ + VSelection( VObject* parent = 0L ); + + /** + * Constructs a vselection by copying the specified selection. + * + * @param selection the selection to copy from + */ + VSelection( const VSelection& selection ); + + /** + * Destroys the selection. + */ + virtual ~VSelection(); + + /** + * Paint selected objects outline and handle. + */ + void draw( VPainter* painter, double zoomFactor ) const; + + virtual const KoRect& boundingBox() const; + + virtual VSelection* clone() const; + + virtual void accept( VVisitor& visitor ); + + /** + * Adds all objects to the selection. + */ + void append(); + + /** + * Adds an object to the selection. + */ + void append( VObject* object ); + + /** + * Adds all objects of the specified object list to the selection. + * + * @param objects the list of objects to add + */ + void append( const VObjectList &objects ); + + /** + * Adds all objects ( selectObjects == true ) or all nodes + * ( selectObjects == false ) within rect to the selection. + */ + bool append( const KoRect& rect, bool selectObjects = true, bool exclusive = true ); + + /** + * Removes the reference to the object, not the object itself. + */ + void take( VObject& object ); + + /** + * Removes all objects ( selectObjects == true ) or all nodes + * ( selectObjects == false ) within rect from the selection. + */ + bool take( const KoRect& rect, bool selectObjects = true, bool exclusive = true ); + + /** + * Removes the references to all objects, not the objects themselves. + */ + void clear(); + + /** + * Read only access to the selected objects. + */ + const VObjectList& objects() const { return m_objects; } + + /** + * Returns a list of segments that have at least one control point inside the specified rect. + * + * @param rect the selection rect + * @return the list of segments + */ + QPtrList<VSegment> getSegments( const KoRect& rect ); + + /** + * Selects or deselects all nodes. + * + * @param select controls if nodes are selected or deselected + */ + void selectNodes( bool select = true ); + + /** + * Returns the handle node id, the KoPoint is inside. + * + * @param point the selection point + * @return the handle the point is inside or node_none if point is not inside any node + */ + VHandleNode handleNode( const KoPoint &point ) const; + + /** + * Toggle selection handles on/off. + * + * @param handle controls if handle are shown or not + */ + void showHandle( bool handle = true ) { m_showhandle = handle; } + + /** + * Toggles selection of objects/nodes. + * + * @param selectObjects controls if objects or nodes are selected + */ + virtual void setSelectObjects( bool selectObjects = true ) { m_selectObjects = selectObjects; } + + static void setHandleSize( uint size ) + { m_handleNodeSize = size; } + + static uint handleSize() + { return m_handleNodeSize; } +private: + /** + * Show/Hide handle. + */ + bool m_showhandle; + + /** + * Select objects and not nodes? + */ + bool m_selectObjects; + + /** + * The list of currently selected objects. + */ + VObjectList m_objects; + + /** + * Paint coordinates of handle rectangle and handle nodes. + * Used for handle node determination and handle node drawing. + */ + KoRect *m_handleRect; + + /** + * Paint size of nodes. + */ + static uint m_handleNodeSize; +}; + +#endif + diff --git a/karbon/core/vstroke.cc b/karbon/core/vstroke.cc new file mode 100644 index 00000000..b7431be5 --- /dev/null +++ b/karbon/core/vstroke.cc @@ -0,0 +1,265 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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 <qdom.h> + +#include <KoGenStyles.h> +#include <KoStyleStack.h> +#include <KoUnit.h> +#include <KoXmlNS.h> + +#include "vobject.h" +#include "vstroke.h" +#include <kdebug.h> + +VStroke::VStroke() +: m_parent(0L), m_lineWidth(1.0), m_lineCap(capButt), m_lineJoin(joinMiter), m_miterLimit(10.0), m_type(none) +{} + +VStroke::VStroke( VObject* parent, float width, const VLineCap cap, const VLineJoin join, + float miterLimit ) +{ + m_parent = parent; + m_type = solid; + m_lineWidth = width; + m_lineCap = cap; + m_lineJoin = join; + m_miterLimit = miterLimit; +} + +VStroke::VStroke( const VColor &c, VObject* parent, float width, const VLineCap cap, const VLineJoin join, + float miterLimit ) +{ + m_parent = parent; + m_type = solid; + m_lineWidth = width; + m_lineCap = cap; + m_lineJoin = join; + m_miterLimit = miterLimit; + m_color = c; +} + +VStroke::VStroke( const VStroke& stroke ) +{ + // doesn't copy parent: + *this = stroke; +} + +void +VStroke::setLineWidth( float width ) +{ + m_lineWidth = width; + + // tell our parent so he can update his bbox: + if( m_parent ) + m_parent->invalidateBoundingBox(); +} + +void +VStroke::save( QDomElement& element ) const +{ + QDomElement me = element.ownerDocument().createElement( "STROKE" ); + element.appendChild( me ); + + // save stroke parameters: + if( m_lineWidth != 1.0 ) + me.setAttribute( "lineWidth", m_lineWidth ); + if( !( m_lineCap == capButt ) ) + me.setAttribute( "lineCap", m_lineCap ); + if( !( m_lineJoin == joinMiter ) ) + me.setAttribute( "lineJoin", m_lineJoin ); + if( m_miterLimit != 10.0 ) + me.setAttribute( "miterLimit", m_miterLimit ); + + if( m_type == solid ) + { + // save color: + m_color.save( me ); + } + else if( m_type == grad ) + { + // save gradient: + m_gradient.save( me ); + } + else if( m_type == patt ) + { + // save pattern: + m_pattern.save( me ); + } + + // save dashpattern: + m_dashPattern.save( me ); +} + +void +VStroke::saveOasis( KoGenStyle &style ) const +{ + if( m_type == solid ) + { + style.addProperty( "draw:stroke", "solid" ); + style.addProperty( "svg:stroke-color", QColor( m_color ).name() ); + style.addPropertyPt( "svg:stroke-width", m_lineWidth ); + if( m_color.opacity() < 1 ) + style.addProperty( "svg:stroke-opacity", QString( "%1%" ).arg( m_color.opacity() * 100. ) ); + } + else if( m_type == none ) + style.addProperty( "draw:stroke", "none" ); + /*else if( m_type == grad ) + style.addProperty( "draw:stroke", "gradient" ); + else if( m_type == patt ) + style.addProperty( "draw:stroke", "hatch" );*/ + + if( m_lineJoin == joinRound ) + style.addProperty( "draw:stroke-linejoin", "round" ); + else if( m_lineJoin == joinBevel ) + style.addProperty( "draw:stroke-linejoin", "bevel" ); + else if( m_lineJoin == joinMiter ) + style.addProperty( "draw:stroke-linejoin", "miter" ); +} + +void +VStroke::loadOasis( const KoStyleStack &stack ) +{ + if( stack.hasAttributeNS( KoXmlNS::draw, "stroke" )) + { + if( stack.attributeNS( KoXmlNS::draw, "stroke" ) == "solid" ) + { + setType( VStroke::solid ); + setColor( QColor( stack.attributeNS( KoXmlNS::svg, "stroke-color" ) ) ); + if( stack.hasAttributeNS( KoXmlNS::svg, "stroke-opacity" ) ) + m_color.setOpacity( stack.attributeNS( KoXmlNS::svg, "stroke-opacity" ).remove( '%' ).toFloat() / 100. ); + QString join = stack.attributeNS( KoXmlNS::draw, "stroke-linejoin" ); + if( !join.isEmpty() ) + { + if( join == "round" ) + m_lineJoin = joinRound; + else if( join == "bevel" ) + m_lineJoin = joinBevel; + else + m_lineJoin = joinMiter; + } + } + else if( stack.attributeNS( KoXmlNS::draw, "stroke" ) == "none" ) + setType( VStroke::none ); + } + if( stack.hasAttributeNS( KoXmlNS::svg, "stroke-width" ) ) + m_lineWidth = KoUnit::parseValue( stack.attributeNS( KoXmlNS::svg, "stroke-width" ) ); + if( m_lineWidth < 0.0 ) + m_lineWidth = 0.0; +} + +void +VStroke::load( const QDomElement& element ) +{ + m_type = none; + // load stroke parameters: + m_lineWidth = element.attribute( "lineWidth", "1.0" ).toDouble(); + if( m_lineWidth < 0.0 ) + m_lineWidth = 0.0; + + switch( element.attribute( "lineCap", "0" ).toUShort() ) + { + case 1: + m_lineCap = capRound; break; + case 2: + m_lineCap = capSquare; break; + default: + m_lineCap = capButt; + } + + switch( element.attribute( "lineJoin", "0" ).toUShort() ) + { + case 1: + m_lineJoin = joinRound; break; + case 2: + m_lineJoin = joinBevel; break; + default: + m_lineJoin = joinMiter; + } + + m_miterLimit = element.attribute( "miterLimit", "10.0" ).toDouble(); + if( m_miterLimit < 0.0 ) + m_miterLimit = 0.0; + + + // load color: + QDomNodeList list = element.childNodes(); + for( uint i = 0; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + QDomElement e = list.item( i ).toElement(); + if( e.tagName() == "COLOR" ) + { + m_color.load( e ); + m_type = solid; + } + else if( e.tagName() == "DASHPATTERN" ) + { + m_dashPattern.load( e ); + } + else if( e.tagName() == "GRADIENT" ) + { + m_type = grad; + m_gradient.load( e ); + } + else if( e.tagName() == "PATTERN" ) + { + m_type = patt; + m_pattern.load( e ); + } + } + } +} + + +VStroke& +VStroke::operator=( const VStroke& stroke ) +{ + if( this != &stroke ) + { + // dont copy the parent! + m_type = stroke.m_type; + + m_lineWidth = stroke.m_lineWidth; + // Tell our parent about the linewidth change, so he can update his bbox: + //if( m_parent ) + // m_parent->invalidateBoundingBox(); + + m_lineCap = stroke.m_lineCap; + m_lineJoin = stroke.m_lineJoin; + m_miterLimit = stroke.m_miterLimit; + m_color = stroke.m_color; + m_dashPattern = stroke.m_dashPattern; + m_gradient = stroke.m_gradient; + m_pattern = stroke.m_pattern; + } + + return *this; +} + +void +VStroke::transform( const QWMatrix& m ) +{ + if( type() == VStroke::grad ) + gradient().transform( m ); + else if( type() == VStroke::patt ) + pattern().transform( m ); +} diff --git a/karbon/core/vstroke.h b/karbon/core/vstroke.h new file mode 100644 index 00000000..6cfa338d --- /dev/null +++ b/karbon/core/vstroke.h @@ -0,0 +1,135 @@ +/* This file is part of the KDE project + Copyright (C) 2001, The Karbon Developers + Copyright (C) 2002, 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. +*/ + +#ifndef __VSTROKE_H__ +#define __VSTROKE_H__ + +#include <qvaluelist.h> + +#include "vcolor.h" +#include "vdashpattern.h" +#include "vgradient.h" +#include "vpattern.h" +#include <koffice_export.h> + +class QDomElement; +class VObject; +class KoGenStyle; +class KoStyleStack; + + +/** + * Manages stroke properties. + * Supported are line join/cap styles equivalents to the qpainter ones. + * Also the line width in pixels and line stroke type (solid / gradient). + * Finally it also managed the dashing pattern, see VDashPattern. + * + * Default is black solid outline of width 1 with miter join, butt cap + * style and no dashes. + */ + +class KARBONBASE_EXPORT VStroke +{ +public: + enum VStrokeType + { + none = 0, /// no stroke at all + solid = 1, /// solid stroke + grad = 2, /// gradient as stroke + patt = 3, /// pattern as stroke + unknown = 4 + }; + + enum VLineCap + { + capButt = 0, + capRound = 1, + capSquare = 2 + }; + + enum VLineJoin + { + joinMiter = 0, + joinRound = 1, + joinBevel = 2 + }; + + VStroke(); + VStroke( VObject* parent, float width = 1.0, const VLineCap cap = capButt, + const VLineJoin join = joinMiter, float miterLimit = 10.0 ); + VStroke( const VColor &c, VObject* parent = 0L, float width = 1.0, const VLineCap cap = capButt, + const VLineJoin join = joinMiter, float miterLimit = 10.0 ); + VStroke( const VStroke& stroke ); + + void setParent( VObject* parent ) { m_parent = parent; } + VObject* parent()const { return m_parent; } + + VStrokeType type() const { return m_type; } + void setType( VStrokeType type ) { m_type = type; } + + const VColor& color() const { return m_color; } + void setColor( const VColor& color ) { m_color = color; } + + float lineWidth() const { return m_lineWidth; } + void setLineWidth( float width ); + + VLineCap lineCap() const { return m_lineCap; } + void setLineCap( VLineCap cap ) { m_lineCap = cap; } + + VLineJoin lineJoin() const { return m_lineJoin; } + void setLineJoin( VLineJoin join ) { m_lineJoin = join; } + + float miterLimit() const { return m_miterLimit; } + void setMiterLimit( float limit ) { m_miterLimit = limit; } + + VGradient& gradient() { return m_gradient; } + const VGradient& gradient() const { return m_gradient; } + + VPattern& pattern() { return m_pattern; } + const VPattern& pattern() const { return m_pattern; } + + VDashPattern& dashPattern() { return m_dashPattern; } + const VDashPattern& dashPattern() const { return m_dashPattern; } + + void save( QDomElement& element ) const; + void saveOasis( KoGenStyle &style ) const; + void load( const QDomElement& element ); + void loadOasis( const KoStyleStack &stack ); + + + VStroke& operator=( const VStroke& stroke ); + + void transform( const QWMatrix& m ); + +private: + VObject *m_parent; + + VColor m_color; + VGradient m_gradient; + VPattern m_pattern; + float m_lineWidth; + float m_miterLimit; + VLineCap m_lineCap : 2; + VLineJoin m_lineJoin : 2; + VStrokeType m_type : 3; + VDashPattern m_dashPattern; +}; + +#endif diff --git a/karbon/core/vtext.cc b/karbon/core/vtext.cc new file mode 100644 index 00000000..53c6af51 --- /dev/null +++ b/karbon/core/vtext.cc @@ -0,0 +1,745 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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 <qdom.h> +#include <qfile.h> + +#include <kdebug.h> +#include <KoPoint.h> +#include <KoRect.h> + +#include "vpath.h" +#include "vtext.h" +#include "vtext_iface.h" +#include "vstroke.h" +#include "vfill.h" +#include "vvisitor.h" +#include "vsegment.h" +#include "vgroup.h" +#include "vpainter.h" +#include "commands/vtransformcmd.h" + +#ifdef HAVE_KARBONTEXT + +#include <ft2build.h> +#include <fontconfig/fontconfig.h> + + +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_GLYPH_H + +#define FT_TOFLOAT(x) ((x) * (1.0 / 64.0)) +#define FT_FROMFLOAT(x) ((int) floor ((x) * 64.0 + 0.5)) + + +// Trace routines for ttf / ps font -> VSubpath + +int traceMoveto( FT_Vector *to, VPath *composite ) +{ + double tox = ( to->x / 64.0 ); + double toy = ( -to->y / 64.0 ); + + //QString add = "M" + QString::number(tox) + "," + QString::number(toy) + " "; + //kdDebug(38000) << add.latin1() << endl; + composite->moveTo( KoPoint( tox, toy ) ); + + return 0; +} + +int traceLineto( FT_Vector *to, VPath *composite ) +{ + double tox = ( to->x / 64.0 ); + double toy = ( -to->y / 64.0 ); + + //QString add = "L" + QString::number(tox) + "," + QString::number(toy) + " "; + //kdDebug(38000) << add.latin1() << endl; + + composite->lineTo( KoPoint( tox, toy ) ); + + return 0; +} + +int traceQuadraticBezier( FT_Vector *control, FT_Vector *to, VPath *composite ) +{ + double x1 = ( control->x / 64.0 ); + double y1 = ( -control->y / 64.0 ); + double x2 = ( to->x / 64.0 ); + double y2 = ( -to->y / 64.0 ); + + //QString add = "Q" + QString::number(x1) + "," + QString::number(y1) + "," + QString::number(x2) + "," + QString::number(y2) + " "; + //kdDebug(38000) << add.latin1() << endl; + composite->curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x2, y2 ) ); + //composite->curve2To( KoPoint( x1, y1 ), KoPoint( x2, y2 ) ); + + return 0; +} + +int traceCubicBezier( FT_Vector *p, FT_Vector *q, FT_Vector *to, VPath *composite ) +{ + double x1 = ( p->x / 64.0 ); + double y1 = ( -p->y / 64.0 ); + double x2 = ( q->x / 64.0 ); + double y2 = ( -q->y / 64.0 ); + double x3 = ( to->x / 64.0 ); + double y3 = ( -to->y / 64.0 ); + + //QString add = "C" + QString::number(x1) + "," + QString::number(y1) + "," + QString::number(x2) + "," + QString::number(y2) + "," + QString::number(x3) + "," + QString::number(y3); + //kdDebug(38000) << add.latin1() << endl; + + composite->curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x3, y3 ) ); + + return 0; +} + +FT_Outline_Funcs OutlineMethods = +{ + (FT_Outline_MoveTo_Func) traceMoveto, + (FT_Outline_LineTo_Func) traceLineto, + (FT_Outline_ConicTo_Func) traceQuadraticBezier, + (FT_Outline_CubicTo_Func) traceCubicBezier, + 0, + 0 +}; + +#endif // HAVE_KARBONTEXT + +VText::VText( VObject* parent, VState state ) + : VObject( parent, state ), m_basePath( 0L ) +{ + m_glyphs.setAutoDelete( true ); + m_boundingBoxIsInvalid = true; + m_stroke = new VStroke( this ); + m_fill = new VFill(); + m_position = (VText::Position)0; + m_alignment = (VText::Alignment)0; + m_shadow = false; + m_translucentShadow = false; + m_shadowAngle = 0; + m_shadowDistance = 0; + m_offset = 0.0; +} + + +VText::VText( const QFont &font, const VSubpath& basePath, Position position, Alignment alignment, const QString& text ) + : VObject( 0L ), m_font( font ), m_basePath( basePath ), m_position( position ), m_alignment( alignment ), m_text( text ) +{ + m_glyphs.setAutoDelete( true ); + m_boundingBoxIsInvalid = true; + m_stroke = new VStroke( this ); + m_fill = new VFill(); + m_offset = 0.0; +} + +VText::VText( const VText& text ) + : VObject( text ), m_font( text.m_font ), m_basePath( text.m_basePath ), m_position( text.m_position ), m_alignment( text.m_alignment ), m_text( text.m_text ), m_shadow( text.m_shadow ), m_translucentShadow( text.m_translucentShadow ), m_shadowDistance( text.m_shadowDistance ), m_shadowAngle( text.m_shadowAngle ), m_offset( text.m_offset ) +{ + m_stroke = new VStroke( *text.m_stroke ); + m_stroke->setParent( this ); + m_fill = new VFill( *text.m_fill ); + + // copy glyphs + VPathListIterator itr( text.m_glyphs ); + for( ; itr.current() ; ++itr ) + { + VPath* c = itr.current()->clone(); + c->setParent( this ); + m_glyphs.append( c ); + } + + m_boundingBoxIsInvalid = true; +} + +VText::~VText() +{ +} + +DCOPObject* VText::dcopObject() +{ + if( !m_dcop ) + m_dcop = new VTextIface( this ); + + return m_dcop; +} + + +void +VText::draw( VPainter* painter, const KoRect* /*rect*/ ) const +{ + if( + state() == deleted || + state() == hidden || + state() == hidden_locked ) + { + return; + } + + painter->save(); + + VPathListIterator itr( m_glyphs ); + + if( state() != edit ) + { + // paint fill: + painter->newPath(); + + if ( m_shadow ) + { + VColor color; + if ( m_translucentShadow ) + { + color.set( 0., 0., 0. ); + color.setOpacity( .3 ); + } + else + { + color.set( .3, .3, .3 ); + color.setOpacity( 1. ); + } + int shadowDx = int( m_shadowDistance * cos( m_shadowAngle / 360. * 6.2832 ) ); + int shadowDy = int( m_shadowDistance * sin( m_shadowAngle / 360. * 6.2832 ) ); + + VTransformCmd trafo( 0L, QWMatrix() ); + for( itr.toFirst(); itr.current(); ++itr ) + { + trafo.setMatrix( QWMatrix( 1, 0, 0, 1, shadowDx, shadowDy ) ); + trafo.visit( *( itr.current() ) ); + itr.current()->setFill( VFill( color ) ); + itr.current()->setStroke( VStroke( color ) ); + itr.current()->draw( painter ); + trafo.setMatrix( QWMatrix( 1, 0, 0, 1, -shadowDx, -shadowDy ) ); + trafo.visit( *( itr.current() ) ); + } + } + + for( itr.toFirst(); itr.current(); ++itr ) + { + itr.current()->setFill( *m_fill ); + itr.current()->setStroke( *m_stroke ); + itr.current()->draw( painter ); + } + } + + // draw simplistic contour: + if( state() == edit )//|| state() == selected ) + { + painter->newPath(); + painter->setRasterOp( Qt::XorROP ); + painter->setPen( Qt::yellow ); + painter->setBrush( Qt::NoBrush ); + + for( itr.toFirst(); itr.current(); ++itr ) + itr.current()->draw( painter ); + + painter->strokePath(); + } + + painter->restore(); +} + +const KoRect& +VText::boundingBox() const +{ + if( m_boundingBoxIsInvalid ) + { + VPathListIterator itr( m_glyphs ); + itr.toFirst(); + // clear: + m_boundingBox = itr.current() ? itr.current()->boundingBox() : KoRect(); + for( ++itr; itr.current(); ++itr ) + if( !itr.current()->boundingBox().isEmpty() ) + m_boundingBox |= itr.current()->boundingBox(); + + // take line width into account: + m_boundingBox.setCoords( + m_boundingBox.left() - 0.5 * stroke()->lineWidth(), + m_boundingBox.top() - 0.5 * stroke()->lineWidth(), + m_boundingBox.right() + 0.5 * stroke()->lineWidth(), + m_boundingBox.bottom() + 0.5 * stroke()->lineWidth() ); + + m_boundingBoxIsInvalid = false; + } + + return m_boundingBox; +} + +VText* +VText::clone() const +{ + return new VText( *this ); +} + +VGroup* VText::toVGroup() const +{ + VGroup* group = new VGroup( parent() ); + + VPathListIterator itr( m_glyphs ); + for( itr.toFirst(); itr.current(); ++itr ) + { + VPath* c = itr.current()->clone(); + c->setParent( group ); + group->append( c ); + } + + group->setFill( *m_fill ); + group->setStroke( *m_stroke ); + + return group; +} // VText::toVGroup + +void +VText::save( QDomElement& element ) const +{ + if( state() != deleted ) + { + QDomElement me = element.ownerDocument().createElement( "TEXT" ); + + VPath path( 0L ); + path.combinePath( m_basePath ); + path.save( me ); + + VObject::save( me ); + + // save font properties + me.setAttribute( "text", m_text ); + me.setAttribute( "family", m_font.family() ); + me.setAttribute( "size", m_font.pointSize() ); + me.setAttribute( "italic", m_font.italic() ); + me.setAttribute( "bold", m_font.bold() ); + me.setAttribute( "position", m_position ); + me.setAttribute( "alignment", m_alignment ); + me.setAttribute( "shadow", m_shadow ); + me.setAttribute( "translucentshadow", m_translucentShadow ); + me.setAttribute( "shadowangle", m_shadowAngle ); + me.setAttribute( "shadowdist", m_shadowDistance ); + me.setAttribute( "offset", m_offset ); + element.appendChild( me ); + + // save all glyphs / paths + VPathListIterator itr = m_glyphs; + for( itr.toFirst(); itr.current(); ++itr ) + itr.current()->save( me ); + } +} + +void +VText::load( const QDomElement& element ) +{ + m_glyphs.clear(); + + m_font.setFamily( element.attribute( "family", "Times" ) ); + m_font.setPointSize( element.attribute( "size", "12" ).toInt() ); + m_font.setItalic( element.attribute( "italic" ).toInt() == 1 ); + m_font.setWeight( QFont::Normal ); + m_font.setBold( element.attribute( "bold" ).toInt() == 1 ); + m_position = (Position)element.attribute( "position", "0" ).toInt(); + m_alignment = (Alignment)element.attribute( "alignment", "0" ).toInt(); + m_shadow = ( element.attribute( "shadow" ).toInt() == 1 ); + m_translucentShadow = ( element.attribute( "translucentshadow" ).toInt() == 1 ); + m_shadowAngle = element.attribute( "shadowangle" ).toInt(); + m_shadowDistance = element.attribute( "shadowdist" ).toInt(); + m_offset = element.attribute( "offset" ).toDouble(); + m_text = element.attribute( "text", "" ); + + VObject::load( element ); + + QDomNodeList list = element.childNodes(); + QDomElement e = list.item( 0 ).toElement(); + + // element to start with reading glyph paths and stroke, fill, etc. + uint startElement = 0; + + if( e.tagName() == "PATH" ) + { + VPath path( 0L ); + path.load( e ); + m_basePath = *path.paths().getFirst(); + startElement++; + } + + // load text glyphs: + for( uint i = startElement; i < list.count(); ++i ) + { + if( list.item( i ).isElement() ) + { + e = list.item( i ).toElement(); + if( e.tagName() == "PATH" ) + { + VPath *composite = new VPath( this ); + composite->load( e ); + m_glyphs.append( composite ); + } + if( e.tagName() == "STROKE" ) + m_stroke->load( e ); + if( e.tagName() == "FILL" ) + m_fill->load( e ); + } + } + // if no glyphs yet, trace them +#ifdef HAVE_KARBONTEXT + if( m_glyphs.count() == 0 ) + traceText(); +#endif + m_boundingBoxIsInvalid = true; + //m_fill->setFillRule( VFill::evenOdd ); +} + +void +VText::setText( const QString& text ) +{ + if( m_text != text ) + { + m_text = text; + m_glyphs.clear(); +#ifdef HAVE_KARBONTEXT + traceText(); +#endif + } +} + +void +VText::setState( const VState state ) +{ + VObject::setState( state ); + + VPathListIterator itr( m_glyphs ); + for( itr.toFirst(); itr.current(); ++itr ) + { + itr.current()->setState( state ); + } +} + +void +VText::accept( VVisitor& visitor ) +{ + visitor.visitVText( *this ); +} + +#ifdef HAVE_KARBONTEXT + +void +VText::traceText() +{ + if( m_basePath.count() == 0 ) + { + kdDebug(38000) << "Can't draw a text without base path (was: " << m_text << ")." << endl; + return; + } + + // TODO : set more options + int slant = FC_SLANT_ROMAN; + if( m_font.italic() ) + slant = FC_SLANT_ITALIC; + + int weight = 0; + if( m_font.bold() ) + weight = FC_WEIGHT_BOLD; + + // Build FontConfig request pattern + int id = -1; + QString filename = buildRequest( m_font.family(), weight, slant, m_font.pointSize(), id ); + m_glyphs.clear(); + + kdDebug(38000) << "Loading " << filename.latin1() << " for requested font \"" << m_font.family().latin1() << "\", " << m_font.pointSize() << " pt." << endl; + + FT_UInt glyphIndex; + FT_Face fontFace; + // TODO : this lib should probably be a singleton (Rob) + FT_Library library; + FT_Init_FreeType( &library ); + FT_Error error = FT_New_Face( library, QFile::encodeName(filename), id, &fontFace ); + + if( error ) + { + kdDebug(38000) << "traceText(), could not load font. Aborting!" << endl; + return; + } + + bool foundCharmap = false; + + // Try to choose unicode charmap + for( int charmap = 0; charmap < fontFace->num_charmaps; charmap++ ) + { + if( fontFace->charmaps[charmap]->encoding == ft_encoding_unicode ) + { + FT_Error error = FT_Set_Charmap( fontFace, fontFace->charmaps[charmap] ); + if( error ) + { + kdDebug(38000) << "traceText(), unable to select unicode charmap." << endl; + continue; + } + foundCharmap = true; + } + } + + // Choose first charmap if no unicode charmap was found + if( ! foundCharmap ) + { + error = FT_Set_Charmap( fontFace, fontFace->charmaps[0] ); + if( error ) + { + kdDebug(38000) << "traceText(), unable to select charmap. Aborting!" << endl; + FT_Done_Face( fontFace ); + FT_Done_FreeType( library ); + return; + } + } + + error = FT_Set_Char_Size( fontFace, FT_FROMFLOAT( m_font.pointSize() ), FT_FROMFLOAT( m_font.pointSize() ), 0, 0 ); + if( error ) + { + kdDebug(38000) << "traceText(), unable to set font size. Aborting!" << endl; + FT_Done_Face( fontFace ); + FT_Done_FreeType( library ); + return; + } + + // storing glyphs. + float l = 0; + QValueList<float> glyphXAdvance; + QValueList<float> glyphYAdvance; + for( unsigned int i = 0; i < m_text.length(); i++ ) + { + // get the glyph index for the current character + QChar character = m_text.at( i ); + glyphIndex = FT_Get_Char_Index( fontFace, character.unicode() ); + if( ! glyphIndex ) + { + kdDebug(38000) << "traceText(), unable get index of char : " << character << endl; + continue; + } + //kdDebug(38000) << "glyphIndex : " << glyphIndex << endl; + FT_Error error = FT_Load_Glyph( fontFace, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP ); + if( error ) + { + kdDebug(38000) << "traceText(), unable to load glyph : " << error << endl; + continue; + } + + // decompose to vpaths + FT_OutlineGlyph g; + error = FT_Get_Glyph( fontFace->glyph, reinterpret_cast<FT_Glyph *>( &g ) ); + if( error ) + { + kdDebug(38000) << "traceText(), unable to get glyph: " << error << endl; + continue; + } + + VPath *composite = new VPath( this ); + error = FT_Outline_Check( &g->outline ); + if( error ) + { + kdDebug(38000) << "traceText(), outline is broken : " << error << endl; + continue; + } + + error = FT_Outline_Decompose(&g->outline, &OutlineMethods, composite ); + if( error ) + { + kdDebug(38000) << "traceText(), unable to decompose outline : " << error << endl; + continue; + } + + m_glyphs.append( composite ); + glyphXAdvance.append( FT_TOFLOAT( fontFace->glyph->advance.x ) ); + glyphYAdvance.append( FT_TOFLOAT( fontFace->glyph->advance.y ) ); + l += FT_TOFLOAT( fontFace->glyph->advance.x ); + FT_Done_Glyph( reinterpret_cast<FT_Glyph>( g ) ); + } + + // Placing the stored glyphs. + float pathLength = 0; + VSubpathIterator pIt( m_basePath ); + + VSegment* seg; + for( ; pIt.current(); ++pIt ) + if( (seg = pIt.current() ) ) + pathLength += seg->length(); + + kdDebug(38000) << "traceText(), using offset : " << m_offset << endl; + float x = m_offset * pathLength; + + switch( m_alignment ) + { + case Left: x += 0; break; + case Center: x -= 0.5 * l; break; + case Right: x -= l; break; + } + float y = 0; + float dx = 0; + float sp = 0; + KoPoint point; + KoPoint normal; + KoPoint tangent; + VSubpathIterator pathIt( m_basePath ); + VSegment* oldSeg = pathIt.current(); + seg = ++pathIt; + KoPoint extPoint; + bool ext = false; + float fsx = 0; + float yoffset = ( m_position == Above ? 0 : ( m_position == On ? m_font.pointSize() / 3 : m_font.pointSize() / 1.5 ) ); + kdDebug(38000) << "Position: " << m_position << " -> " << yoffset << endl; + for( unsigned int i = 0; i < m_text.length(); i++ ) + { + VPath* composite = m_glyphs.at( i ); + if( ! composite ) + continue; + // Step 1: place (0, 0) to the rotation center of the glyph. + dx = *glyphXAdvance.at( i ) / 2; + x += dx; + VTransformCmd trafo( 0L, QWMatrix( 1, 0, 0, 1, -dx, y + yoffset ) ); + trafo.visit( *composite ); + + // Step 2: find the position where to draw. + // 3 possibilities: before, on, and after the basePath... + if ( x < 0 ) + { + if( !ext ) + seg->pointTangentNormalAt( 0, &extPoint, &tangent, &normal ); + point = extPoint + x * tangent; + ext = true; + } + else + { + while ( seg && x > fsx + seg->length() ) + { + fsx += seg->length(); + oldSeg = seg; + seg = ++pathIt; + } + if( seg ) + { + ext = false; + sp = ( x - fsx ) / seg->length(); + seg->pointTangentNormalAt( sp, &point, &tangent, &normal ); + } + else + { + if( !ext ) + oldSeg->pointTangentNormalAt( 1, &extPoint, &tangent, &normal ); + point = extPoint + ( x - fsx ) * tangent; + ext = true; + } + } + + // Step 3: transform glyph and append it. That's it, we've got + // text following a path. Really easy, isn't it ;) ? + trafo.setMatrix( QWMatrix( tangent.x(), tangent.y(), tangent.y(), -tangent.x(), point.x(), point.y() ) ); + trafo.visit( *composite ); + composite->setState( state() ); + + //kdDebug(38000) << "Glyph: " << (QString)character << " [String pos: " << x << ", " << y << " / Canvas pos: " << point.x() << ", " << point.y() << "]" << endl; + + x += dx; + y += *glyphYAdvance.at( i ); + } + FT_Done_Face( fontFace ); + FT_Done_FreeType( library ); + m_boundingBoxIsInvalid = true; +} + +// This routine is copied from KSVGFont (Rob) +QString +VText::buildRequest( QString family, int weight, int slant, double size, int &id ) +{ + // Strip those stupid [Xft or whatever]... + int pos; + if( ( pos = family.find( '[' ) ) ) + family = family.left( pos ); + + // Use FontConfig to locate & select fonts and use FreeType2 to open them + FcPattern *pattern; + QString fileName; + + pattern = FcPatternBuild( 0, FC_WEIGHT, FcTypeInteger, weight, + FC_SLANT, FcTypeInteger, slant, + FC_SIZE, FcTypeDouble, size, NULL ); + + // Add font name + FcPatternAddString( pattern, FC_FAMILY, reinterpret_cast<const FcChar8 *>( family.latin1() ) ); + + // Disable hinting + FcPatternAddBool( pattern, FC_HINTING, FcFalse ); + // Enforce scalability + FcPatternAddBool( pattern, FC_SCALABLE, FcTrue ); + + // Perform the default font pattern modification operations. + FcDefaultSubstitute( pattern ); + FcConfigSubstitute( FcConfigGetCurrent(), pattern, FcMatchPattern ); + + FcResult result; + + // we dont want to use bitmap fonts, so get a list of fonts sorted by closeness to pattern + // and use the best matching scalable font + FcFontSet *fset = FcFontSort( 0, pattern, FcFalse, 0L, &result ); + + // Destroy pattern + FcPatternDestroy( pattern ); + + if( fset ) + { + FcBool scalable; + FcChar8 *temp; + + // iterate over font list and take best scaleable font + for( int i = 0; i < fset->nfont; ++i ) + { + pattern = fset->fonts[i]; + if( FcResultMatch != FcPatternGetBool( pattern, FC_SCALABLE, 0, &scalable ) ) + continue; + if( scalable == FcTrue ) + { + // Get index & filename + if( FcPatternGetString(pattern, FC_FILE, 0, &temp) != FcResultMatch || + FcPatternGetInteger(pattern, FC_INDEX, 0, &id) != FcResultMatch ) + { + kdDebug(38000) << "VText::buildRequest(), could not load font file for requested font \"" << family.latin1() << "\"" << endl; + return QString::null; + } + + fileName = QFile::decodeName(reinterpret_cast<const char *>( temp )); + + // get family name of matched font + QString newFamily; + + if( FcResultMatch == FcPatternGetString( pattern, FC_FAMILY, 0, &temp ) ) + m_font.setFamily( reinterpret_cast<const char *>( temp ) ); + + break; + } + } + + FcFontSetDestroy( fset ); + } + + + return fileName; +} + +void VText::setOffset( double offset ) +{ + if( offset < 0.0 ) + m_offset = 0.0; + else if( offset > 1.0 ) + m_offset = 1.0; + else + m_offset = offset; +} + +#endif // HAVE_KARBONTEXT diff --git a/karbon/core/vtext.h b/karbon/core/vtext.h new file mode 100644 index 00000000..962705ea --- /dev/null +++ b/karbon/core/vtext.h @@ -0,0 +1,136 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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. +*/ + +#ifndef __VTEXT_H__ +#define __VTEXT_H__ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qptrlist.h> +#include <qstring.h> +#include <qfont.h> +#include <koffice_export.h> + +#include "vpath.h" +#include "vcomposite.h" + +class VGroup; + +typedef QPtrList<VPath> VPathList; +typedef QPtrListIterator<VPath> VPathListIterator; + +#ifdef Above +#undef Above +#endif + +class KARBONBASE_EXPORT VText : public VObject +{ +public: + enum Position { + Above, + On, + Under + }; + + enum Alignment { + Left, + Center, + Right + }; + + VText( VObject* parent, VState state = normal ); + VText( const QFont &font, const VSubpath& basePath, Position position, Alignment alignment, const QString& text ); + VText( const VText& text ); + virtual ~VText(); + virtual DCOPObject* dcopObject(); + + virtual void setText( const QString& text ); + virtual const QString& text() { return m_text; } + virtual void setFont( const QFont& font ) { m_font = font; } + virtual const QFont& font() { return m_font; } + virtual void setBasePath( const VSubpath& path ) { m_basePath = path; } + virtual VSubpath& basePath() { return m_basePath; } + virtual void setPosition( Position position ) { m_position = position; } + virtual Position position() { return m_position; } + virtual void setAlignment( Alignment alignment ) { m_alignment = alignment; } + virtual Alignment alignment() { return m_alignment; } + virtual void setUseShadow( bool state ) { m_shadow = state; } + virtual bool useShadow() { return m_shadow; } + virtual void setShadow( int angle, int distance, bool translucent ) + { m_translucentShadow = translucent; m_shadowAngle = angle; m_shadowDistance = distance; } + virtual bool translucentShadow() { return m_translucentShadow; } + virtual int shadowAngle() { return m_shadowAngle; } + virtual int shadowDistance() { return m_shadowDistance; } + virtual void setOffset( double offset ); + virtual double offset() { return m_offset; } + + /** + * Provides read only access to the glyphs. + */ + const VPathList& glyphs() const + { + return m_glyphs; + } + + virtual void draw( VPainter *painter, const KoRect* rect = 0L ) const; + + virtual const KoRect& boundingBox() const; + + virtual void save( QDomElement& element ) const; + virtual void load( const QDomElement& element ); + + virtual VText* clone() const; + virtual VGroup* toVGroup() const; + + virtual void setState( const VState state ); + + virtual void accept( VVisitor& visitor ); + +#ifdef HAVE_KARBONTEXT + void traceText(); + +protected: + QString buildRequest( QString family, int weight, int slant, double size, int &id ); +#endif // HAVE_KARBONTEXT + +private: + // The font to use to draw the text. + QFont m_font; + // The base path. Doesn't belong to the document. + VSubpath m_basePath; + // The text position + Position m_position; + // The text alignment + Alignment m_alignment; + // The text to draw + QString m_text; + // Shadow parameters + bool m_shadow; + bool m_translucentShadow; + int m_shadowDistance; + int m_shadowAngle; + // The glyphs (allow to keep a font even if not present on the computer. works as long as you don't edit the text.) + VPathList m_glyphs; + // the position offset + double m_offset; +}; + +#endif diff --git a/karbon/core/vtext_iface.cc b/karbon/core/vtext_iface.cc new file mode 100644 index 00000000..1a01b192 --- /dev/null +++ b/karbon/core/vtext_iface.cc @@ -0,0 +1,53 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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 "vtext_iface.h" +#include "vtext.h" + +VTextIface::VTextIface( VText *text ) + : VObjectIface( text ), m_text( text ) +{ +} + +void +VTextIface::setText( QString text ) +{ + m_text->setText( text ); +} + +QString +VTextIface::text() +{ + return m_text->text(); +} + +void +VTextIface::setFontSize( int pointSize ) +{ + QFont font = m_text->font(); + font.setPointSize( pointSize ); + m_text->setFont( font ); +} + +int +VTextIface::fontSize() +{ + return m_text->font().pointSize(); +} + diff --git a/karbon/core/vtext_iface.h b/karbon/core/vtext_iface.h new file mode 100644 index 00000000..6cca452a --- /dev/null +++ b/karbon/core/vtext_iface.h @@ -0,0 +1,46 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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. +*/ + +#ifndef __VTEXT_IFACE_H__ +#define __VTEXT_IFACE_H__ + +#include "vobject_iface.h" + +class VText; + +class VTextIface : public VObjectIface +{ + K_DCOP + +public: + VTextIface( VText *text ); + +k_dcop: + void setText( QString text ); + QString text(); + + void setFontSize( int pointSize ); + int fontSize(); + +private: + VText *m_text; +}; + +#endif + diff --git a/karbon/core/vvisitor.cc b/karbon/core/vvisitor.cc new file mode 100644 index 00000000..90085c4a --- /dev/null +++ b/karbon/core/vvisitor.cc @@ -0,0 +1,114 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 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 "vcomposite.h" +#include "vdocument.h" +#include "vgroup.h" +#include "vlayer.h" +#include "vpath.h" +#include "vselection.h" +#include "vvisitor.h" +#include "vimage.h" + +bool +VVisitor::visit( VObject& object ) +{ + m_success = false; + + object.accept( *this ); + + return m_success; +} + +void +VVisitor::visitVDocument( VDocument& document ) +{ + VLayerListIterator itr( document.layers() ); + + for( ; itr.current(); ++itr ) + { + itr.current()->accept( *this ); + } +} + +void +VVisitor::visitVGroup( VGroup& group ) +{ + VObjectListIterator itr( group.objects() ); + + for( ; itr.current(); ++itr ) + { + itr.current()->accept( *this ); + } +} + +void +VVisitor::visitVLayer( VLayer& layer ) +{ + VObjectListIterator itr( layer.objects() ); + + for( ; itr.current(); ++itr ) + { + itr.current()->accept( *this ); + } +} + +void +VVisitor::visitVPath( VPath& composite ) +{ + VSubpathListIterator itr( composite.paths() ); + + for( ; itr.current(); ++itr ) + { + if( !itr.current()->isEmpty() ) + itr.current()->accept( *this ); + } +} + +void +VVisitor::visitVSubpath( VSubpath& /*path*/ ) +{ +} + +void +VVisitor::visitVSelection( VSelection& selection ) +{ + VObjectListIterator itr( selection.objects() ); + + for( ; itr.current() ; ++itr ) + { + itr.current()->accept( *this ); + } +} + +void +VVisitor::visitVText( VText& /*text*/ ) +{ +} + +void +VVisitor::visitVImage( VImage& /*img*/ ) +{ +} + +void +VVisitor::visitVObject( VObject& /*object*/ ) +{ +} diff --git a/karbon/core/vvisitor.h b/karbon/core/vvisitor.h new file mode 100644 index 00000000..d442b9ca --- /dev/null +++ b/karbon/core/vvisitor.h @@ -0,0 +1,152 @@ +/* This file is part of the KDE project + Copyright (C) 2002-2005, 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. +*/ + +#ifndef __VVISITOR_H__ +#define __VVISITOR_H__ + +#include <koffice_export.h> + +class VPath; +class VDocument; +class VGroup; +class VLayer; +class VObject; +class VSubpath; +class VSelection; +class VText; +class VImage; + +/** + \brief The abstract visitor class + + (From Wikipedia) + + In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. + + The idea is to use a structure of element classes, each of which has an accept method that takes a visitor object as an argument. The visitor is an interface that has a different visit() method for each element class. The accept() method of an element class calls back the visit() method for its class. Separate concrete visitor classes can then be written that perform some particular operations. + + One of these visit() methods of a concrete visitor can be thought of as methods not of a single class, but rather methods of a pair of classes: the concrete visitor and the particular element class. Thus the visitor pattern simulates double dispatch in a conventional single-dispatch object-oriented language such as Java, Smalltalk, and C++. + + The visitor pattern also specifies how iteration occurs over the object structure. In the simplest version, where each algorithm needs to iterate in the same way, the accept() method of a container element, in addition to calling back the visit() method of the visitor, also passes the visitor object to the accept() method of all its constituent child elements. + + Because the Visitor object has one principal function (manifested in a plurality of specialized methods) and that function is called visit(), the Visitor can be readily identified as a potential function object or functor. Likewise, the accept() function can be identified as a function applicator, a mapper, which knows how to traverse a particular type of object and apply a function to its elements. +*/ +class KARBONBASE_EXPORT VVisitor +{ +public: + /** + Constructs a new visitor class + */ + VVisitor() + { + m_success = false; + } + + /** + General visit method. Pass an \a object to this function. + This is a virtual function so you need to implement it in the subclass if you want to use it. + Return the success value. + */ + virtual bool visit( VObject& object ); + + /** + Visit method for a VObject. Pass an \a object to this function. + This is a virtual function so you need to implement it in the subclass if you want to use it. + */ + virtual void visitVObject( VObject& object ); + + /** + Visit method for a VPath. Pass a \a composite path to this function. + This is a virtual function so you need to implement it in the subclass if you want to use it. + */ + virtual void visitVPath( VPath& composite ); + + /** + Visit method for a VDocument. Pass a \a document to this function. + This is a virtual function so you need to implement it in the subclass if you want to use it. + */ + virtual void visitVDocument( VDocument& document ); + + /** + Visit method for a VGroup. Pass a \a group of objects to this function. + This is a virtual function so you need to implement it in the subclass if you want to use it. + */ + virtual void visitVGroup( VGroup& group ); + + /** + Visit method for a VLayer. Pass a \a layer to this function. + This is a virtual function so you need to implement it in the subclass if you want to use it. + */ + virtual void visitVLayer( VLayer& layer ); + + /** + Visit method for a VSubpath. Pass a \a path to this function. + This is a virtual function so you need to implement it in the subclass if you want to use it. + */ + virtual void visitVSubpath( VSubpath& path ); + + /** + Visit method for a VSelection. Pass a \a selection to this function. + This is a virtual function so you need to implement it in the subclass if you want to use it. + */ + virtual void visitVSelection( VSelection& selection ); + + /** + Visit method for a VText. Pass some \a text to this function. + This is a virtual function so you need to implement it in the subclass if you want to use it. + */ + virtual void visitVText( VText& text ); + + /** + Visit method for a VImage. Pass an \a image to this function. + This is a virtual function so you need to implement it in the subclass if you want to use it. + */ + virtual void visitVImage( VImage& img ); + + /** + Return if the operation was a success or not + */ + bool success() const + { + return m_success; + } + +protected: + /* + * Make this class "abstract". + */ + /** + Destructs a visitor class + */ + virtual ~VVisitor() {} + + /** + Set the success property. + */ + void setSuccess( bool success = true ) + { + m_success = success; + } + +private: + bool m_success; +}; + +#endif + |