diff --git a/libs/flake/KoShape.h b/libs/flake/KoShape.h index 267e55cdccd..14f0c17a074 100644 --- a/libs/flake/KoShape.h +++ b/libs/flake/KoShape.h @@ -1,1258 +1,1261 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Thorsten Zachmann Copyright (C) 2006, 2008 C. Boemann Copyright (C) 2006-2010 Thomas Zander Copyright (C) 2007-2009,2011 Jan Hambrecht 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 KOSHAPE_H #define KOSHAPE_H #include "KoFlake.h" #include "KoConnectionPoint.h" #include #include #include #include #include //#include #include "flake_export.h" class QPainter; class QRectF; class QPainterPath; class QTransform; class KoShapeContainer; class KoShapeStrokeModel; class KoShapeUserData; class KoViewConverter; class KoShapeApplicationData; class KoShapeSavingContext; class KoShapeLoadingContext; class KoGenStyle; class KoShapeShadow; class KoEventAction; class KoShapePrivate; class KoFilterEffectStack; class KoSnapData; class KoClipPath; class KoShapePaintingContext; class KoShapeAnchor; class KoBorder; struct KoInsets; /** * * Base class for all flake shapes. Shapes extend this class * to allow themselves to be manipulated. This class just represents * a graphical shape in the document and can be manipulated by some default * tools in this library. * * Due to the limited responsibility of this class, the extending object * can have any data backend and is responsible for painting itself. * * We strongly suggest that any extending class will use a Model View * Controller (MVC) design where the View part is all in this class, as well * as the one that inherits from this one. This allows the data that rests * in the model to be reused in different parts of the document. For example * by having two flake objects that show that same data. Or each showing a section of it. * * The KoShape data is completely in postscript-points (pt) (see KoUnit * for conversion methods to and from points). * This image will explain the real-world use of the shape and its options. *
* The Rotation center can be returned with absolutePosition() * *

Flake objects can be created in three ways: *

    *
  • a simple new KoDerivedFlake(), *
  • through an associated tool, *
  • through a factory *
* *

Shape interaction notifications

* We had several notification methods that allow your shape to be notified of changes in other * shapes positions or rotation etc. *
  1. The most general is KoShape::shapeChanged().
    * a virtual method that you can use to check various changed to your shape made by tools or otherwise.
  2. *
  3. for shape hierarchies the parent may receive a notification when a child was modified. * This is done though KoShapeContainerModel::childChanged()
  4. *
  5. any shape that is at a similar position as another shape there is collision detection. * You can register your shape to be sensitive to any changes like moving or whatever to * other shapes that intersect yours. * Such changes will then be notified to your shape using the method from (1) You should call * KoShape::setCollisionDetection(bool) to enable this. *
*/ class FLAKE_EXPORT KoShape { public: /// Used by shapeChanged() to select which change was made enum ChangeType { PositionChanged, ///< used after a setPosition() RotationChanged, ///< used after a setRotation() ScaleChanged, ///< used after a scale() ShearChanged, ///< used after a shear() SizeChanged, ///< used after a setSize() GenericMatrixChange, ///< used after the matrix was changed without knowing which property explicitly changed ParentChanged, ///< used after a setParent() CollisionDetected, ///< used when another shape moved in our boundingrect Deleted, ///< the shape was deleted StrokeChanged, ///< the shapes stroke has changed BackgroundChanged, ///< the shapes background has changed ShadowChanged, ///< the shapes shadow has changed BorderChanged, ///< the shapes border has changed ParameterChanged, ///< the shapes parameter has changed (KoParameterShape only) ContentChanged, ///< the content of the shape changed e.g. a new image inside a pixmap/text change inside a textshape TextRunAroundChanged, ///< used after a setTextRunAroundSide() ChildChanged, ///< a child of a container was changed/removed. This is propagated to all parents ConnectionPointChanged, ///< a connection point has changed - ClipPathChanged ///< the shapes clip path has changed + ClipPathChanged, ///< the shapes clip path has changed + ControlPointChanged, ///< a control point has changed + BeginResize, ///< used during resizing + EndResize ///< used during resizing }; /// The behavior text should do when intersecting this shape. enum TextRunAroundSide { BiggestRunAroundSide, ///< Run other text around the side that has the most space LeftRunAroundSide, ///< Run other text around the left side of the frame RightRunAroundSide, ///< Run other text around the right side of the frame EnoughRunAroundSide, ///< Run other text dynamically around both sides of the shape, provided there is sufficient space left BothRunAroundSide, ///< Run other text around both sides of the shape NoRunAround, ///< The text will be completely avoiding the frame by keeping the horizontal space that this frame occupies blank. RunThrough ///< The text will completely ignore the frame and layout as if it was not there }; /// The behavior text should do when intersecting this shape. enum TextRunAroundContour { ContourBox, /// Run other text around a bounding rect of the outline ContourFull, ///< Run other text around also on the inside ContourOutside ///< Run other text around only on the outside }; /** * TODO */ enum RunThroughLevel { Background, Foreground }; /// Fine grained control of allowed user interactions enum AllowedInteraction { MoveAllowed = 1, ///< Moving the shape is allowed ResizeAllowed = 2, ///< Resizing the shape is allowed ShearingAllowed = 4, ///< Sharing the shape is allowed RotationAllowed = 8, ///< Rotating the shape is allowed SelectionAllowed = 16, ///< Selecting the shape is allowed ContentChangeAllowed = 32, ///< Editing the content is allowed DeletionAllowed = 64 ///< Deleting the shape is allowed }; Q_DECLARE_FLAGS(AllowedInteractions, AllowedInteraction) Q_FLAGS(AllowedInteractions) /** * @brief Constructor */ KoShape(); /** * @brief Destructor */ virtual ~KoShape(); /** * @brief Paint the shape * The class extending this one is responsible for painting itself. Since we do not * assume the shape is square the paint must also clear its background if it will draw * something transparent on top. * This can be done with a method like: * painter.fillRect(converter.normalToView(QRectF(QPointF(0.0,0.0), size())), background()); * Or equivalent for non-square objects. * Do note that a shape's top-left is always at coordinate 0,0. Even if the shape itself is rotated * or translated. * @param painter used for painting the shape * @param converter to convert between internal and view coordinates. * @see applyConversion() * @param paintcontext the painting context. */ virtual void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) = 0; /** * @brief Paint the shape's border * This is a helper function that could be called from the paint() method of all shapes. * @param painter used for painting the shape * @param converter to convert between internal and view coordinates. * @see applyConversion() */ virtual void paintBorder(QPainter &painter, const KoViewConverter &converter); /** * Load a shape from odf * * @param context the KoShapeLoadingContext used for loading * @param element element which represents the shape in odf * * @return false if loading failed */ virtual bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) = 0; /** * @brief store the shape data as ODF XML. * This is the method that will be called when saving a shape as a described in * OpenDocument 9.2 Drawing Shapes. * @see saveOdfAttributes */ virtual void saveOdf(KoShapeSavingContext &context) const = 0; /** * This method can be used while saving the shape as ODF to add the data * stored on this shape to the current element. * * @param context the context for the current save. * @param attributes a number of OdfAttribute items to state which attributes to save. * @see saveOdf */ void saveOdfAttributes(KoShapeSavingContext &context, int attributes) const; /** * This method can be used while saving the shape as Odf to add common child elements * * The office:event-listeners and draw:glue-point are saved. * @param context the context for the current save. */ void saveOdfCommonChildElements(KoShapeSavingContext &context) const; /** * This method can be used to save contour data from the clipPath() * * The draw:contour-polygon or draw:contour-path elements are saved. * @param context the context for the current save. * @param originalSize the original size of the unscaled image. */ void saveOdfClipContour(KoShapeSavingContext &context, const QSizeF &originalSize) const; /** * @brief Scale the shape using the zero-point which is the top-left corner. * @see position() * * @param sx scale in x direction * @param sy scale in y direction */ void scale(qreal sx, qreal sy); /** * @brief Rotate the shape (relative) * * The shape will be rotated from the current rotation using the center of the shape using the size() * * @param angle change the angle of rotation increasing it with 'angle' degrees */ void rotate(qreal angle); /** * Return the current rotation in degrees. * It returns NaN if the shape has a shearing or scaling transformation applied. */ qreal rotation() const; /** * @brief Shear the shape * The shape will be sheared using the zero-point which is the top-left corner. * @see position() * * @param sx shear in x direction * @param sy shear in y direction */ void shear(qreal sx, qreal sy); /** * @brief Resize the shape * * @param size the new size of the shape. This is different from scaling as * scaling is a so called secondary operation which is comparable to zooming in * instead of changing the size of the basic shape. * Easiest example of this difference is that using this method will not distort the * size of pattern-fills and strokes. */ virtual void setSize(const QSizeF &size); /** * @brief Get the size of the shape in pt. * * The size is in shape coordinates. * * @return the size of the shape as set by setSize() */ virtual QSizeF size() const; /** * @brief Set the position of the shape in pt * * @param position the new position of the shape */ virtual void setPosition(const QPointF &position); /** * @brief Get the position of the shape in pt * * @return the position of the shape */ QPointF position() const; /** * @brief Check if the shape is hit on position * @param position the position where the user clicked. * @return true when it hits. */ virtual bool hitTest(const QPointF &position) const; /** * @brief Get the bounding box of the shape * * This includes the line width and the shadow of the shape * * @return the bounding box of the shape */ virtual QRectF boundingRect() const; /** * @brief Add a connector point to the shape * * A connector is a place on the shape that allows a graphical connection to be made * using a line, for example. * * @param point the connection point to add * @return the id of the new connection point */ int addConnectionPoint(const KoConnectionPoint &point); /** * Sets data of connection point with specified id. * * The position of the connector is restricted to the bounding rectangle of the shape. * When setting a default connection point, the new position is ignored, as these * are fixed at their default position. * The function will insert a new connection point if the specified id was not used * before. * * @param connectionPointId the id of the connection point to set * @param point the connection point data * @return false if specified connection point id is invalid, else true */ bool setConnectionPoint(int connectionPointId, const KoConnectionPoint &point); /// Checks if a connection point with the specified id exists bool hasConnectionPoint(int connectionPointId) const; /// Returns connection point with specified connection point id KoConnectionPoint connectionPoint(int connectionPointId) const; /** * Return a list of the connection points that have been added to this shape. * All the points are relative to the shape position, see absolutePosition(). * @return a list of the connectors that have been added to this shape. */ KoConnectionPoints connectionPoints() const; /// Removes connection point with specified id void removeConnectionPoint(int connectionPointId); /// Removes all connection points void clearConnectionPoints(); /** * Add a event action */ void addEventAction(KoEventAction *action); /** * Remove a event action */ void removeEventAction(KoEventAction *action); /** * Get all event actions */ QSet eventActions() const; /** * Return the side text should flow around this shape. This implements the ODF style:wrap * attribute that specifies how text is displayed around a frame or graphic object. */ TextRunAroundSide textRunAroundSide() const; /** * Set the side text should flow around this shape. * @param side the requested side * @param runThrought run through the foreground or background or... */ void setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrough = Background); /** * The space between this shape's left edge and text that runs around this shape. * @return the space around this shape to keep free from text */ qreal textRunAroundDistanceLeft() const; /** * Set the space between this shape's left edge and the text that run around this shape. * @param distance the space around this shape to keep free from text */ void setTextRunAroundDistanceLeft(qreal distance); /** * The space between this shape's top edge and text that runs around this shape. * @return the space around this shape to keep free from text */ qreal textRunAroundDistanceTop() const; /** * Set the space between this shape's top edge and the text that run around this shape. * @param distance the space around this shape to keep free from text */ void setTextRunAroundDistanceTop(qreal distance); /** * The space between this shape's right edge and text that runs around this shape. * @return the space around this shape to keep free from text */ qreal textRunAroundDistanceRight() const; /** * Set the space between this shape's right edge and the text that run around this shape. * @param distance the space around this shape to keep free from text */ void setTextRunAroundDistanceRight(qreal distance); /** * The space between this shape's bottom edge and text that runs around this shape. * @return the space around this shape to keep free from text */ qreal textRunAroundDistanceBottom() const; /** * Set the space between this shape's bottom edge and the text that run around this shape. * @param distance the space around this shape to keep free from text */ void setTextRunAroundDistanceBottom(qreal distance); /** * Return the threshold above which text should flow around this shape. * The text will not flow around the shape on a side unless the space available on that side * is above this threshold. Only used when the text run around side is EnoughRunAroundSide. * @return threshold the threshold */ qreal textRunAroundThreshold() const; /** * Set the threshold above which text should flow around this shape. * The text will not flow around the shape on a side unless the space available on that side * is above this threshold. Only used when the text run around side is EnoughRunAroundSide. * @param threshold the new threshold */ void setTextRunAroundThreshold(qreal threshold); /** * Return the how tight text run around is done around this shape. * @return the contour */ TextRunAroundContour textRunAroundContour() const; /** * Set how tight text run around is done around this shape. * @param contour the new contour */ void setTextRunAroundContour(TextRunAroundContour contour); /** * Set the KoShapeAnchor */ void setAnchor(KoShapeAnchor *anchor); /** * Return the KoShapeAnchor, or 0 */ KoShapeAnchor *anchor() const; /** * Set the minimum height of the shape. * Currently it's not respected but only for informational purpose * @param minimumShapeHeight the minimum height of the frame. */ void setMinimumHeight(qreal height); /** * Return the minimum height of the shape. * @return the minimum height of the shape. Default is 0.0. */ qreal minimumHeight() const; /** * Set the background of the shape. * A shape background can be a plain color, a gradient, a pattern, be fully transparent * or have a complex fill. * Setting such a background will allow the shape to be filled and will be able to tell * if it is transparent or not. * @param background the new shape background. */ void setBackground(QSharedPointer background); /** * return the brush used to paint te background of this shape with. * A QBrush can have a plain color, be fully transparent or have a complex fill. * setting such a brush will allow the shape to fill itself using that brush and * will be able to tell if its transparent or not. * @return the background-brush */ QSharedPointer background() const; /** * Returns true if there is some transparency, false if the shape is fully opaque. * The default implementation will just return if the background has some transparency, * you should override it and always return true if your shape is not square. * @return if the shape is (partly) transparent. */ virtual bool hasTransparency() const; /** * Sets shape level transparency. * @param transparency the new shape level transparency */ void setTransparency(qreal transparency); /** * Returns the shape level transparency. * @param recursive when true takes the parents transparency into account */ qreal transparency(bool recursive=false) const; /** * Retrieve the z-coordinate of this shape. * The zIndex property is used to determine which shape lies on top of other objects. * An shape with a higher z-order is on top, and can obscure another shape. * @return the z-index of this shape. * @see setZIndex() */ int zIndex() const; /** * Set the z-coordinate of this shape. * The zIndex property is used to determine which shape lies on top of other objects. * An shape with a higher z-order is on top, and can obscure, another shape. *

Just like two objects having the same x or y coordinate will make them 'touch', * so will two objects with the same z-index touch on the z plane. In layering the * shape this, however, can cause a little confusion as one always has to be on top. * The layering if two overlapping objects have the same index is implementation dependent * and probably depends on the order in which they are added to the shape manager. * @param zIndex the new z-index; */ void setZIndex(int zIndex); /** * Retrieve the run through property of this shape. * The run through property is used to determine if the shape is behind, inside or before text. * @return the run through of this shape. */ int runThrough(); /** * Set the run through property of this shape. * The run through property is used to determine if the shape is behind, inside or before text. * @param runThrough the new run through; */ virtual void setRunThrough(short int runThrough); /** * Changes the Shape to be visible or invisible. * Being visible means being painted, as well as being used for * things like guidelines or searches. * @param on when true; set the shape to be visible. * @see setGeometryProtected(), setContentProtected(), setSelectable() */ void setVisible(bool on); /** * Returns current visibility state of this shape. * Being visible means being painted, as well as being used for * things like guidelines or searches. * @param recursive when true, checks visibility recursively * @return current visibility state of this shape. * @see isGeometryProtected(), isContentProtected(), isSelectable() */ bool isVisible(bool recursive = false) const; /** * Changes the shape to be printable or not. The default is true. * * If a Shape's print flag is true, the shape will be printed. If * false, the shape will not be printed. If a shape is not visible (@see isVisible), * it isPrinted will return false, too. */ void setPrintable(bool on); /** * Returns the current printable state of this shape. * * A shape can be visible but not printable, not printable and not visible * or visible and printable, but not invisible and still printable. * * @return current printable state of this shape. */ bool isPrintable() const; /** * Makes it possible for the user to select this shape. * This parameter defaults to true. * @param selectable when true; set the shape to be selectable by the user. * @see setGeometryProtected(), setContentProtected(), setVisible() */ void setSelectable(bool selectable); /** * Returns if this shape can be selected by the user. * @return true only when the object is selectable. * @see isGeometryProtected(), isContentProtected(), isVisible() */ bool isSelectable() const; /** * Tells the shape to have its position/rotation and size protected from user-changes. * The geometry being protected means the user can not change shape or position of the * shape. This includes any matrix operation such as rotation. * @param on when true; set the shape to have its geometry protected. * @see setContentProtected(), setSelectable(), setVisible() */ void setGeometryProtected(bool on); /** * Returns current geometry protection state of this shape. * The geometry being protected means the user can not change shape or position of the * shape. This includes any matrix operation such as rotation. * @return current geometry protection state of this shape. * @see isContentProtected(), isSelectable(), isVisible() */ bool isGeometryProtected() const; /** * Marks the shape to have its content protected against editing. * Content protection is a hint for tools to disallow the user editing the content. * @param protect when true set the shapes content to be protected from user modification. * @see setGeometryProtected(), setSelectable(), setVisible() */ void setContentProtected(bool protect); /** * Returns current content protection state of this shape. * Content protection is a hint for tools to disallow the user editing the content. * @return current content protection state of this shape. * @see isGeometryProtected(), isSelectable(), isVisible() */ bool isContentProtected() const; /** * Returns the parent, or 0 if there is no parent. * @return the parent, or 0 if there is no parent. */ KoShapeContainer *parent() const; /** * Set the parent of this shape. * @param parent the new parent of this shape. Can be 0 if the shape has no parent anymore. */ void setParent(KoShapeContainer *parent); /** * Request a repaint to be queued. * The repaint will be of the entire Shape, including its selection handles should this * shape be selected. *

This method will return immediately and only request a repaint. Successive calls * will be merged into an appropriate repaint action. */ virtual void update() const; /** * Request a repaint to be queued. * The repaint will be restricted to the parameters rectangle, which is expected to be * in points (the internal coordinates system of KoShape) and it is expected to be * normalized. *

This method will return immediately and only request a repaint. Successive calls * will be merged into an appropriate repaint action. * @param rect the rectangle (in pt) to queue for repaint. */ virtual void update(const QRectF &rect) const; /// Used by compareShapeZIndex() to order shapes enum ChildZOrderPolicy { ChildZDefault, ChildZParentChild = ChildZDefault, ///< normal parent/child ordering ChildZPassThrough ///< children are considered equal to this shape }; /** * Returns if during compareShapeZIndex() how this shape portrays the values * of its children. The default behaviour is to let this shape's z values take * the place of its children values, so you get a parent/child relationship. * The children are naturally still ordered relatively to their z values * * But for special cases (like Calligra's TextShape) it can be overloaded to return * ChildZPassThrough which means the children keep their own z values * @returns the z order policy of this shape */ virtual ChildZOrderPolicy childZOrderPolicy(); /** * This is a method used to sort a list using the STL sorting methods. * @param s1 the first shape * @param s2 the second shape */ static bool compareShapeZIndex(KoShape *s1, KoShape *s2); /** * returns the outline of the shape in the form of a path. * The outline returned will always be relative to the position() of the shape, so * moving the shape will not alter the result. The outline is used to draw the stroke * on, for example. * @returns the outline of the shape in the form of a path. */ virtual QPainterPath outline() const; /** * returns the outline of the shape in the form of a rect. * The outlineRect returned will always be relative to the position() of the shape, so * moving the shape will not alter the result. The outline is used to calculate * the boundingRect. * @returns the outline of the shape in the form of a rect. */ virtual QRectF outlineRect() const; /** * returns the outline of the shape in the form of a path for the use of painting a shadow. * * Normally this would be the same as outline() if there is a fill (background) set on the * shape and empty if not. However, a shape could reimplement this to return an outline * even if no fill is defined. A typical example of this would be the picture shape * which has a picture but almost never a background. * * @returns the outline of the shape in the form of a path. */ virtual QPainterPath shadowOutline() const; /** * Returns the currently set stroke, or 0 if there is no stroke. * @return the currently set stroke, or 0 if there is no stroke. */ KoShapeStrokeModel *stroke() const; /** * Set a new stroke, removing the old one. * @param stroke the new stroke, or 0 if there should be no stroke. */ void setStroke(KoShapeStrokeModel *stroke); /** * Return the insets of the stroke. * Convenience method for KoShapeStrokeModel::strokeInsets() */ KoInsets strokeInsets() const; /// Sets the new shadow, removing the old one void setShadow(KoShapeShadow *shadow); /// Returns the currently set shadow or 0 if there is no shadow set KoShapeShadow *shadow() const; /// Sets the new border, removing the old one. void setBorder(KoBorder *border); /// Returns the currently set border or 0 if there is no border set KoBorder *border() const; /// Sets a new clip path, removing the old one void setClipPath(KoClipPath *clipPath); /// Returns the currently set clip path or 0 if there is no clip path set KoClipPath * clipPath() const; /** * Setting the shape to keep its aspect-ratio has the effect that user-scaling will * keep the width/hight ratio intact so as not to distort shapes that rely on that * ratio. * @param keepAspect the new value */ void setKeepAspectRatio(bool keepAspect); /** * Setting the shape to keep its aspect-ratio has the effect that user-scaling will * keep the width/hight ratio intact so as not to distort shapes that rely on that * ratio. * @return whether to keep aspect ratio of this shape */ bool keepAspectRatio() const; /** * Return the position of this shape regardless of rotation/skew/scaling and regardless of * this shape having a parent (being in a group) or not.
* @param anchor The place on the (unaltered) shape that you want the position of. * @return the point that is the absolute, centered position of this shape. */ QPointF absolutePosition(KoFlake::Position anchor = KoFlake::CenteredPosition) const; /** * Move this shape to an absolute position where the end location will be the same * regardless of the shape's rotation/skew/scaling and regardless of this shape having * a parent (being in a group) or not.
* The newPosition is going to be the center of the shape. * This has the convenient effect that:

     shape->setAbsolutePosition(QPointF(0,0));
     shape->rotate(45);
Will result in the same visual position of the shape as the opposite:
     shape->rotate(45);
     shape->setAbsolutePosition(QPointF(0,0));
* @param newPosition the new absolute center of the shape. * @param anchor The place on the (unaltered) shape that you set the position of. */ void setAbsolutePosition(const QPointF &newPosition, KoFlake::Position anchor = KoFlake::CenteredPosition); /** * Set a data object on the shape to be used by an application. * This is specifically useful when a shape is created in a plugin and that data from that * shape should be accessible outside the plugin. * @param userData the new user data, or 0 to delete the current one. */ void setUserData(KoShapeUserData *userData); /** * Return the current userData. */ KoShapeUserData *userData() const; /** * Set a data object on the shape to be used by an application. * This is specifically useful when an application wants to have data that is per shape * and should be deleted when the shape is destructed. * @param applicationData the new application data, or 0 to delete the current one. */ void setApplicationData(KoShapeApplicationData *applicationData); /** * Return the current applicationData. */ KoShapeApplicationData *applicationData() const; /** * Return the Id of this shape, identifying the type of shape by the id of the factory. * @see KoShapeFactoryBase::shapeId() * @return the id of the shape-type */ QString shapeId() const; /** * Set the Id of this shape. A shapeFactory is expected to set the Id at creation * so applications can find out what kind of shape this is. * @see KoShapeFactoryBase::shapeId() * @param id the ID from the factory that created this shape */ void setShapeId(const QString &id); /** * Create a matrix that describes all the transformations done on this shape. * * The absolute transformation is the combined transformation of this shape * and all its parents and grandparents. * * @param converter if not null, this method uses the converter to mark the right * offsets in the current view. */ QTransform absoluteTransformation(const KoViewConverter *converter) const; /** * Applies a transformation to this shape. * * The transformation given is relative to the global coordinate system, i.e. the document. * This is a convenience function to apply a global transformation to this shape. * @see applyTransformation * * @param matrix the transformation matrix to apply */ void applyAbsoluteTransformation(const QTransform &matrix); /** * Sets a new transformation matrix describing the local transformations on this shape. * @param matrix the new transformation matrix */ void setTransformation(const QTransform &matrix); /// Returns the shapes local transformation matrix QTransform transformation() const; /** * Applies a transformation to this shape. * * The transformation given is relative to the shape coordinate system. * * @param matrix the transformation matrix to apply */ void applyTransformation(const QTransform &matrix); /** * Copy all the settings from the parameter shape and apply them to this shape. * Settings like the position and rotation to visible and locked. The parent * is a notable exclusion. * @param shape the shape to use as original */ void copySettings(const KoShape *shape); /** * Convenience method that allows people implementing paint() to use the shape * internal coordinate system directly to paint itself instead of considering the * views zoom. * @param painter the painter to alter the zoom level of. * @param converter the converter for the current views zoom. */ static void applyConversion(QPainter &painter, const KoViewConverter &converter); /** * @brief Transforms point from shape coordinates to document coordinates * @param point in shape coordinates * @return point in document coordinates */ QPointF shapeToDocument(const QPointF &point) const; /** * @brief Transforms rect from shape coordinates to document coordinates * @param rect in shape coordinates * @return rect in document coordinates */ QRectF shapeToDocument(const QRectF &rect) const; /** * @brief Transforms point from document coordinates to shape coordinates * @param point in document coordinates * @return point in shape coordinates */ QPointF documentToShape(const QPointF &point) const; /** * @brief Transform rect from document coordinates to shape coordinates * @param rect in document coordinates * @return rect in shape coordinates */ QRectF documentToShape(const QRectF &rect) const; /** * Returns the name of the shape. * @return the shapes name */ QString name() const; /** * Sets the name of the shape. * @param name the new shape name */ void setName(const QString &name); /** * Update the position of the shape in the tree of the KoShapeManager. */ void notifyChanged(); /** * A shape can be in a state that it is doing processing data like loading or text layout. * In this case it can be shown on screen probably partially but it should really not be printed * until it is fully done processing. * Warning! This method can be blocking for a long time * @param asynchronous If set to true the processing will can take place in a different thread and the * function will not block until the shape is finished. * In case of printing Flake will call this method from a non-main thread and only * start printing it when the in case of printing method returned. * If set to false the processing needs to be done synchronously and will * block until the result is finished. */ virtual void waitUntilReady(const KoViewConverter &converter, bool asynchronous = true) const; /// checks recursively if the shape or one of its parents is not visible or locked bool isEditable() const; /** * Adds a shape which depends on this shape. * Making a shape dependent on this one means it will get shapeChanged() called * on each update of this shape. * * If this shape already depends on the given shape, establishing the * dependency is refused to prevent circular dependencies. * * @param shape the shape which depends on this shape * @return true if dependency could be established, otherwise false * @see removeDependee(), hasDependee() */ bool addDependee(KoShape *shape); /** * Removes as shape depending on this shape. * @see addDependee(), hasDependee() */ void removeDependee(KoShape *shape); /// Returns if the given shape is dependent on this shape bool hasDependee(KoShape *shape) const; /// Returns list of shapes depending on this shape QList dependees() const; /// Returns additional snap data the shape wants to have snapping to virtual KoSnapData snapData() const; /** * Set additional attribute * * This can be used to attach additional attributes to a shape for attributes * that are application specific like presentation:placeholder * * @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder * @param value The value of the attribute */ void setAdditionalAttribute(const QString &name, const QString &value); /** * Remove additional attribute * * @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder */ void removeAdditionalAttribute(const QString &name); /** * Check if additional attribute is set * * @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder * * @return true if there is a attribute with prefix:tag set, false otherwise */ bool hasAdditionalAttribute(const QString &name) const; /** * Get additional attribute * * @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder * * @return The value of the attribute if it exists or a null string if not found. */ QString additionalAttribute(const QString &name) const; void setAdditionalStyleAttribute(const char *name, const QString &value); void removeAdditionalStyleAttribute(const char *name); QString additionalStyleAttribute(const QByteArray &name) const; QMap additionalStyleAttributes() const; /** * Returns the filter effect stack of the shape * * @return the list of filter effects applied on the shape when rendering. */ KoFilterEffectStack *filterEffectStack() const; /// Sets the new filter effect stack, removing the old one void setFilterEffectStack(KoFilterEffectStack *filterEffectStack); /** * Set the property collision detection. * Setting this to true will result in calls to shapeChanged() with the CollisionDetected * parameter whenever either this or another shape is moved/rotated etc and intersects this shape. * @param detect if true detect collisions. */ void setCollisionDetection(bool detect); /** * get the property collision detection. * @returns true if collision detection is on. */ bool collisionDetection(); /** * Return the tool delegates for this shape. * In Flake a shape being selected will cause the tool manager to make available all tools that * can edit the selected shapes. In some cases selecting one shape should allow the tool to * edit a related shape be available too. The tool delegates allows this to happen by taking * all the shapes in the set into account on tool selection. * Notice that if the set is non-empty 'this' shape is no longer looked at. You can choose * to add itself to the set too. */ QSet toolDelegates() const; /** * Set the tool delegates. * @param delegates the new delegates. * @see toolDelegates() */ void setToolDelegates(const QSet &delegates); /** * Return the hyperlink for this shape. */ QString hyperLink () const; /** * Set hyperlink for this shape. * @param hyperLink name. */ void setHyperLink(const QString &hyperLink); /** * Makes it possible for the user to delete this shape. * This parameter defaults to true. * @param deletable when true; set the shape to be deletable by the user. * @see isDeletable(), setGeometryProtected(), setContentProtected(), setSelectable() */ void setDeletable(bool deletable); /** * Returns if this shape can be deleted by the user. * @return true only when this shape is deletable. * @see setDeletable(), isGeometryProtected(), isContentProtected(), isSelectable() */ bool isDeletable() const; /// Sets the AllowedInteraction @p flag to @p value void setAllowedInteraction(AllowedInteraction flag, bool value); /// @return the AllowedInteraction state for @p flag. /// Convenience method that just calls allowedInteractions(recursive).testFlag(flag). /// @see allowedInteractions() bool allowedInteraction(AllowedInteraction flag, bool recursive = true) const; /// Sets the interactions the user is allowed to perform on this shape to @p interactions void setAllowedInteractions(AllowedInteractions interactions); /// @return the interactions the user is allowed to perform on this shape. /// If @p recursive is false, the shapes flags are returned as is. Use this e.g. for ui. /// If @p recursive is true: /// If the shape is not visible, no interactions are allowed. /// If there is a parent, the parent is checked. /// /// @see KoShapeContainer::allowedInteractions() AllowedInteractions allowedInteractions(bool recursive = true) const; /** * \internal * Returns the private object for use within the flake lib */ KoShapePrivate *priv(); protected: /// constructor KoShape(KoShapePrivate &); /* ** loading saving helper methods */ /// attributes from ODF 1.1 chapter 9.2.15 Common Drawing Shape Attributes enum OdfAttribute { OdfTransformation = 1, ///< Store transformation information OdfSize = 2, ///< Store size information OdfPosition = 8, ///< Store position OdfAdditionalAttributes = 4, ///< Store additional attributes of the shape OdfCommonChildElements = 16, ///< Event actions and connection points OdfLayer = 64, ///< Store layer name OdfStyle = 128, ///< Store the style OdfId = 256, ///< Store the unique ID OdfName = 512, ///< Store the name of the shape OdfZIndex = 1024, ///< Store the z-index OdfViewbox = 2048, ///< Store the viewbox /// A mask for all mandatory attributes OdfMandatories = OdfLayer | OdfStyle | OdfId | OdfName | OdfZIndex, /// A mask for geometry attributes OdfGeometry = OdfPosition | OdfSize, /// A mask for all the attributes OdfAllAttributes = OdfTransformation | OdfGeometry | OdfAdditionalAttributes | OdfMandatories | OdfCommonChildElements }; /** * This method is used during loading of the shape to load common attributes * * @param context the KoShapeLoadingContext used for loading * @param element element which represents the shape in odf * @param attributes a number of OdfAttribute items to state which attributes to load. */ bool loadOdfAttributes(const KoXmlElement &element, KoShapeLoadingContext &context, int attributes); /** * Parses the transformation attribute from the given string * @param transform the transform attribute string * @param context the loading context * @return the resulting transformation matrix */ QTransform parseOdfTransform(const QString &transform, KoShapeLoadingContext &context); /** * @brief Saves the style used for the shape * * This method fills the given style object with the stroke and * background properties and then adds the style to the context. * * @param style the style object to fill * @param context used for saving * @return the name of the style * @see saveOdf */ virtual QString saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const; /** * Loads the stroke and fill style from the given element. * * @param element the xml element to load the style from * @param context the loading context used for loading */ virtual void loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context); /// Loads the stroke style KoShapeStrokeModel *loadOdfStroke(const KoXmlElement &element, KoShapeLoadingContext &context) const; /// Loads the fill style QSharedPointer loadOdfFill(KoShapeLoadingContext &context) const; /// Loads the connection points void loadOdfGluePoints(const KoXmlElement &element, KoShapeLoadingContext &context); /// Loads the clip contour void loadOdfClipContour(const KoXmlElement &element, KoShapeLoadingContext &context, const QSizeF &scaleFactor); /* ** end loading saving */ /** * A hook that allows inheriting classes to do something after a KoShape property changed * This is called whenever the shape, position rotation or scale properties were altered. * @param type an indicator which type was changed. */ virtual void shapeChanged(ChangeType type, KoShape *shape = 0); /// return the current matrix that contains the rotation/scale/position of this shape QTransform transform() const; KoShapePrivate *d_ptr; private: Q_DECLARE_PRIVATE(KoShape) }; Q_DECLARE_METATYPE(KoShape*) Q_DECLARE_OPERATORS_FOR_FLAGS(KoShape::AllowedInteractions) #endif diff --git a/libs/flake/tools/KoPathTool.h b/libs/flake/tools/KoPathTool.h index fcc45786776..98ff5055e00 100644 --- a/libs/flake/tools/KoPathTool.h +++ b/libs/flake/tools/KoPathTool.h @@ -1,151 +1,150 @@ /* This file is part of the KDE project * Copyright (C) 2006-2012 Jan Hambrecht * Copyright (C) 2006,2007 Thorsten Zachmann * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007 Boudewijn Rempt * * 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 KOPATHTOOL_H #define KOPATHTOOL_H #include "KoPathShape.h" #include "KoToolBase.h" #include "KoPathToolSelection.h" #include #include class QButtonGroup; class KoCanvasBase; class KoInteractionStrategy; class KoPathToolHandle; class KoParameterShape; class QAction; /// The tool for editing a KoPathShape or a KoParameterShape class FLAKE_EXPORT KoPathTool : public KoToolBase { Q_OBJECT public: explicit KoPathTool(KoCanvasBase *canvas); ~KoPathTool(); /// reimplemented virtual void paint(QPainter &painter, const KoViewConverter &converter); /// reimplemented virtual void repaintDecorations(); /// reimplemented virtual void mousePressEvent(KoPointerEvent *event); /// reimplemented virtual void mouseMoveEvent(KoPointerEvent *event); /// reimplemented virtual void mouseReleaseEvent(KoPointerEvent *event); /// reimplemented virtual void keyPressEvent(QKeyEvent *event); /// reimplemented virtual void keyReleaseEvent(QKeyEvent *event); /// reimplemented virtual void mouseDoubleClickEvent(KoPointerEvent *event); /// reimplemented virtual void activate(ToolActivation toolActivation, const QSet &shapes); /// reimplemented virtual void deactivate(); /// reimplemented virtual void deleteSelection(); /// reimplemented virtual KoToolSelection* selection(); /// repaints the specified rect void repaint(const QRectF &repaintRect); public Q_SLOTS: void documentResourceChanged(int key, const QVariant & res); Q_SIGNALS: void typeChanged(int types); void pathChanged(KoPathShape* path); // TODO this is unused, can we remove this one? protected: /// reimplemented virtual QList > createOptionWidgets(); -private: +protected: struct PathSegment; void updateOptionsWidget(); PathSegment* segmentAtPoint(const QPointF &point); -private Q_SLOTS: +protected Q_SLOTS: void pointTypeChanged(QAction *type); void insertPoints(); void removePoints(); void segmentToLine(); void segmentToCurve(); void convertToPath(); void joinPoints(); void mergePoints(); void breakAtPoint(); void breakAtSegment(); void pointSelectionChanged(); void updateActions(); void pointToLine(); void pointToCurve(); void activate(); protected: KoPathToolSelection m_pointSelection; ///< the point selection QCursor m_selectCursor; -private: - +protected: KoPathToolHandle * m_activeHandle; ///< the currently active handle int m_handleRadius; ///< the radius of the control point handles uint m_grabSensitivity; ///< the grab sensitivity QPointF m_lastPoint; ///< needed for interaction strategy PathSegment *m_activeSegment; - // make a frind so that it can test private member/methods + // make a frind so that it can test protected member/methods friend class TestPathTool; KoInteractionStrategy *m_currentStrategy; ///< the rubber selection strategy QButtonGroup *m_pointTypeGroup; QAction *m_actionPathPointCorner; QAction *m_actionPathPointSmooth; QAction *m_actionPathPointSymmetric; QAction *m_actionCurvePoint; QAction *m_actionLinePoint; QAction *m_actionLineSegment; QAction *m_actionCurveSegment; QAction *m_actionAddPoint; QAction *m_actionRemovePoint; QAction *m_actionBreakPoint; QAction *m_actionBreakSegment; QAction *m_actionJoinSegment; QAction *m_actionMergePoints; QAction *m_actionConvertToPath; QCursor m_moveCursor; Q_DECLARE_PRIVATE(KoToolBase) }; #endif diff --git a/libs/widgetutils/KoProperties.cpp b/libs/widgetutils/KoProperties.cpp index efba9dc53ed..b26387a8bcd 100644 --- a/libs/widgetutils/KoProperties.cpp +++ b/libs/widgetutils/KoProperties.cpp @@ -1,204 +1,210 @@ /* * Copyright (c) 2006 Boudewijn Rempt * Copyright (C) 2006-2008 Thomas Zander * * 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 "KoProperties.h" #include #include class Q_DECL_HIDDEN KoProperties::Private { public: QMap properties; }; KoProperties::KoProperties() : d(new Private()) { } KoProperties::KoProperties(const KoProperties & rhs) : d(new Private()) { d->properties = rhs.d->properties; } KoProperties::~KoProperties() { delete d; } QMapIterator KoProperties::propertyIterator() const { return QMapIterator(d->properties); } bool KoProperties::isEmpty() const { return d->properties.isEmpty(); } void KoProperties::load(const QDomElement &root) { d->properties.clear(); QDomElement e = root; QDomNode n = e.firstChild(); while (!n.isNull()) { // We don't nest elements. QDomElement e = n.toElement(); if (!e.isNull()) { if (e.tagName() == "property") { const QString name = e.attribute("name"); const QString value = e.text(); QDataStream in(QByteArray::fromBase64(value.toLatin1())); QVariant v; in >> v; d->properties[name] = v; } } n = n.nextSibling(); } } bool KoProperties::load(const QString & s) { QDomDocument doc; if (!doc.setContent(s)) return false; load(doc.documentElement()); return true; } void KoProperties::save(QDomElement &root) const { QDomDocument doc = root.ownerDocument(); QMap::ConstIterator it; for (it = d->properties.constBegin(); it != d->properties.constEnd(); ++it) { QDomElement e = doc.createElement("property"); e.setAttribute("name", QString(it.key().toLatin1())); QVariant v = it.value(); e.setAttribute("type", v.typeName()); QByteArray bytes; QDataStream out(&bytes, QIODevice::WriteOnly); out << v; QDomText text = doc.createCDATASection(QString::fromLatin1(bytes.toBase64())); e.appendChild(text); root.appendChild(e); } } QString KoProperties::store(const QString &s) const { QDomDocument doc = QDomDocument(s); QDomElement root = doc.createElement(s); doc.appendChild(root); save(root); return doc.toString(); } void KoProperties::setProperty(const QString & name, const QVariant & value) { // If there's an existing value for this name already, replace it. d->properties.insert(name, value); } bool KoProperties::property(const QString & name, QVariant & value) const { QMap::const_iterator it = d->properties.constFind(name); if (it == d->properties.constEnd()) { return false; } else { value = *it; return true; } } QVariant KoProperties::property(const QString & name) const { return d->properties.value(name, QVariant()); } int KoProperties::intProperty(const QString & name, int def) const { const QVariant v = property(name); if (v.isValid()) return v.toInt(); else return def; } qreal KoProperties::doubleProperty(const QString & name, qreal def) const { const QVariant v = property(name); if (v.isValid()) return v.toDouble(); else return def; } bool KoProperties::boolProperty(const QString & name, bool def) const { const QVariant v = property(name); if (v.isValid()) return v.toBool(); else return def; } QString KoProperties::stringProperty(const QString & name, const QString & def) const { const QVariant v = property(name); if (v.isValid()) return v.toString(); else return def; } bool KoProperties::contains(const QString & key) const { return d->properties.contains(key); } QVariant KoProperties::value(const QString & key) const { return d->properties.value(key); } bool KoProperties::operator==(const KoProperties &other) const { if (d->properties.count() != other.d->properties.count()) return false; QMapIterator i(d->properties); while (i.hasNext()) { i.next(); if (other.d->properties.value(i.key()) != i.value()) return false; } return true; } + +KoProperties KoProperties::operator=(const KoProperties &other) const +{ + d->properties = other.d->properties; + return *this; +} diff --git a/libs/widgetutils/KoProperties.h b/libs/widgetutils/KoProperties.h index d17ddaf39a1..83ac97280ac 100644 --- a/libs/widgetutils/KoProperties.h +++ b/libs/widgetutils/KoProperties.h @@ -1,184 +1,185 @@ /* Copyright (c) 2006-2007 Boudewijn Rempt Copyright (C) 2006-2007 Thomas Zander 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 _KO_PROPERTIES_H #define _KO_PROPERTIES_H #include #include #include #include "kowidgetutils_export.h" class QDomElement; /** * A KoProperties is the (de-)serializable representation of * a key-value map. The serialisation format is XML. */ class KOWIDGETUTILS_EXPORT KoProperties { public: /** * Create a new properties object */ KoProperties(); /** * Copy constructor */ KoProperties(const KoProperties &other); ~KoProperties(); public: /** * Fill the properties object from the XML dom node. * * load() does not touch existing properties if loading fails. * * @param root the root node of the properties subtree. */ void load(const QDomElement &root); /** * Fill the properties object from the XML encoded * representation in string. * * load() does not touch existing properties if loading fails. * * @param string the stored properties. * @return false if loading failing, true if it succeeded */ bool load(const QString &string); /** * Returns an iterator over the properties. The iterator is not * suitable for adding or removing properties. */ QMapIterator propertyIterator() const; /** * @return true if this KoProperties object does not contain any * properties. */ bool isEmpty() const; /** * @brief Create a serialized version of these properties (as XML) with root as the root element. * @param root as the root element in the generated XML. */ QString store(const QString &root) const; void save(QDomElement &root) const; /** * Set the property with name to value. */ void setProperty(const QString &name, const QVariant &value); /** * Set value to the value associated with property name * @return false if the specified property did not exist. */ bool property(const QString &name, QVariant &value) const; /** * Return a property by name, wrapped in a QVariant. * A typical usage: * @code * KoProperties *props = new KoProperties(); * props->setProperty("name", "Marcy"); * props->setProperty("age", 25); * QString name = props->property("name").toString(); * int age = props->property("age").toInt(); * @endcode * @return a property by name, wrapped in a QVariant. * @param name the name (or key) with which the variant was registered. * @see intProperty() stringProperty() */ QVariant property(const QString &name) const; /** * Return an integer property by name. * A typical usage: * @code * KoProperties *props = new KoProperties(); * props->setProperty("age", 25); * int age = props->intProperty("age"); * @endcode * @return an integer property by name * @param name the name (or key) with which the variant was registered. * @param defaultValue the default value, should there not be any property by the name this will be returned. * @see property() stringProperty() */ int intProperty(const QString &name, int defaultValue = 0) const; /** * Return a qreal property by name. * @param name the name (or key) with which the variant was registered. * @param defaultValue the default value, should there not be any property by the name this will be returned. */ qreal doubleProperty(const QString &name, qreal defaultValue = 0.0) const; /** * Return a boolean property by name. * @param name the name (or key) with which the variant was registered. * @param defaultValue the default value, should there not be any property by the name this will be returned. */ bool boolProperty(const QString &name, bool defaultValue = false) const; /** * Return an QString property by name. * A typical usage: * @code * KoProperties *props = new KoProperties(); * props->setProperty("name", "Marcy"); * QString name = props->stringProperty("name"); * @endcode * @return an QString property by name * @param name the name (or key) with which the variant was registered. * @see property() intProperty() * @param defaultValue the default value, should there not be any property by the name this will be returned. */ QString stringProperty(const QString &name, const QString &defaultValue = QString()) const; /** * Returns true if the specified key is present in this properties * object. */ bool contains(const QString &key) const; /** * Returns the value associated with the specified key if this * properties object contains the specified key; otherwise return * an empty QVariant. */ QVariant value(const QString &key) const; bool operator==(const KoProperties &other) const; - + KoProperties operator=(const KoProperties &other) const; + private: class Private; Private * const d; }; #endif // _KO_PROPERTIES_H diff --git a/plugins/defaultTools/defaulttool/ShapeResizeStrategy.cpp b/plugins/defaultTools/defaulttool/ShapeResizeStrategy.cpp index de9bcadb19f..8ccc1f2b97f 100644 --- a/plugins/defaultTools/defaulttool/ShapeResizeStrategy.cpp +++ b/plugins/defaultTools/defaulttool/ShapeResizeStrategy.cpp @@ -1,328 +1,340 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007 Thomas Zander * Copyright (C) 2007,2011 Jan Hambrecht * * 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 "ShapeResizeStrategy.h" #include "SelectionDecorator.h" #include "ChartResizeStrategy.h" #include #include #include #include #include #include #include #include #include +#include +#include #include #include #include #include ShapeResizeStrategy::ShapeResizeStrategy(KoToolBase *tool, const QPointF &clicked, KoFlake::SelectionHandle direction ) : KoInteractionStrategy(tool), m_lastScale(1.0,1.0) { // FIXME: This test is also done in DefaultTool, so it should also be responsible for setting the shapes to operate on Q_ASSERT(tool->canvas()->shapeManager()->selection()->count() > 0); QList selectedShapes = tool->canvas()->shapeManager()->selection()->selectedShapes(KoFlake::StrippedSelection); foreach(KoShape *shape, selectedShapes) { if (shape->allowedInteraction(KoShape::ResizeAllowed, false)) { m_selectedShapes << shape; m_startPositions << shape->position(); m_oldTransforms << shape->transformation(); m_transformations << QTransform(); m_startSizes << shape->size(); } } m_start = clicked; KoShape *shp = 0; if (tool->canvas()->shapeManager()->selection()->count()>1) shp = tool->canvas()->shapeManager()->selection(); if (tool->canvas()->shapeManager()->selection()->count()==1) shp = tool->canvas()->shapeManager()->selection()->firstSelectedShape(); if ( shp ) { m_windMatrix = shp->absoluteTransformation(0); m_unwindMatrix = m_windMatrix.inverted(); m_initialSize = shp->size(); m_initialPosition = m_windMatrix.map(QPointF()); } switch(direction) { case KoFlake::TopMiddleHandle: m_start = 0.5 * (shp->absolutePosition(KoFlake::TopLeftCorner) + shp->absolutePosition(KoFlake::TopRightCorner) ); m_top = true; m_bottom = false; m_left = false; m_right = false; break; case KoFlake::TopRightHandle: m_start = shp->absolutePosition(KoFlake::TopRightCorner); m_top = true; m_bottom = false; m_left = false; m_right = true; break; case KoFlake::RightMiddleHandle: m_start = 0.5 * ( shp->absolutePosition(KoFlake::TopRightCorner) + shp->absolutePosition(KoFlake::BottomRightCorner) ); m_top = false; m_bottom = false; m_left = false; m_right = true; break; case KoFlake::BottomRightHandle: m_start = shp->absolutePosition(KoFlake::BottomRightCorner); m_top = false; m_bottom = true; m_left = false; m_right = true; break; case KoFlake::BottomMiddleHandle: m_start = 0.5 * ( shp->absolutePosition(KoFlake::BottomRightCorner) + shp->absolutePosition(KoFlake::BottomLeftCorner) ); m_top = false; m_bottom = true; m_left = false; m_right = false; break; case KoFlake::BottomLeftHandle: m_start = shp->absolutePosition(KoFlake::BottomLeftCorner); m_top = false; m_bottom = true; m_left = true; m_right = false; break; case KoFlake::LeftMiddleHandle: m_start = 0.5 * ( shp->absolutePosition(KoFlake::BottomLeftCorner) + shp->absolutePosition(KoFlake::TopLeftCorner) ); m_top = false; m_bottom = false; m_left = true; m_right = false; break; case KoFlake::TopLeftHandle: m_start = shp->absolutePosition(KoFlake::TopLeftCorner); m_top = true; m_bottom = false; m_left = true; m_right = false; break; default: Q_ASSERT(0); // illegal 'corner' } tool->setStatusText( i18n("Press CTRL to resize from center.") ); // Handle possible chart shapes for (KoShape *s : m_selectedShapes) { if (s->shapeId() == ChartShapeId) { m_chartShapes.insert(s, new ChartResizeStrategy(s)); } } } ShapeResizeStrategy::~ShapeResizeStrategy() { qDeleteAll(m_chartShapes); } void ShapeResizeStrategy::handleMouseMove(const QPointF &point, Qt::KeyboardModifiers modifiers) { tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); QPointF newPos = tool()->canvas()->snapGuide()->snap( point, modifiers ); tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); bool keepAspect = modifiers & Qt::ShiftModifier; foreach(KoShape *shape, m_selectedShapes) keepAspect = keepAspect || shape->keepAspectRatio(); qreal startWidth = m_initialSize.width(); if (startWidth < std::numeric_limits::epsilon()) startWidth = std::numeric_limits::epsilon(); qreal startHeight = m_initialSize.height(); if (startHeight < std::numeric_limits::epsilon()) startHeight = std::numeric_limits::epsilon(); QPointF distance = m_unwindMatrix.map(newPos) - m_unwindMatrix.map( m_start ); // guard against resizing zero width shapes, which would result in huge zoom factors if (m_initialSize.width() < std::numeric_limits::epsilon()) { distance.rx() = 0.0; } // guard against resizing zero height shapes, which would result in huge zoom factors if (m_initialSize.height() < std::numeric_limits::epsilon()) { distance.ry() = 0.0; } const bool scaleFromCenter = modifiers & Qt::ControlModifier; if (scaleFromCenter) { distance *= 2.0; } qreal newWidth = startWidth; qreal newHeight = startHeight; if (m_left) { newWidth = startWidth - distance.x(); } else if (m_right) { newWidth = startWidth + distance.x(); } if (m_top) { newHeight = startHeight - distance.y(); } else if (m_bottom) { newHeight = startHeight + distance.y(); } /** * Do not let a shape be less than 1px in size in current view * coordinates. If the user wants it to be smaller, he can just * zoom-in a bit. */ QSizeF minViewSize(1.0, 1.0); QSizeF minDocSize = tool()->canvas()->viewConverter()->viewToDocument(minViewSize); if (qAbs(newWidth) < minDocSize.width()) { int sign = newWidth >= 0.0 ? 1 : -1; // zero -> '1' newWidth = sign * minDocSize.width(); } if (qAbs(newHeight) < minDocSize.height()) { int sign = newHeight >= 0.0 ? 1 : -1; // zero -> '1' newHeight = sign * minDocSize.height(); } qreal zoomX = newWidth / startWidth; qreal zoomY = newHeight / startHeight; if (keepAspect) { const bool cornerUsed = ((m_bottom?1:0) + (m_top?1:0) + (m_left?1:0) + (m_right?1:0)) == 2; if ((cornerUsed && startWidth < startHeight) || m_left || m_right) zoomY = zoomX; else zoomX = zoomY; } QPointF move; if (scaleFromCenter) move = QPointF(startWidth / 2.0, startHeight / 2.0); else move = QPointF(m_left?startWidth:0, m_top?startHeight:0); resizeBy( move, zoomX, zoomY ); } void ShapeResizeStrategy::handleCustomEvent( KoPointerEvent * event ) { QPointF center = 0.5 * QPointF( m_initialSize.width(), m_initialSize.height() ); qreal zoom = pow(1.01, -0.1 * event->z() ); m_lastScale *= zoom; resizeBy( center, m_lastScale.x(), m_lastScale.y() ); } void ShapeResizeStrategy::resizeBy( const QPointF ¢er, qreal zoomX, qreal zoomY ) { QTransform matrix; matrix.translate(center.x(), center.y()); // translate to matrix.scale(zoomX, zoomY); matrix.translate(-center.x(), -center.y()); // and back // that is the transformation we want to apply to the shapes matrix = m_unwindMatrix * matrix * m_windMatrix; // the resizing transformation without the mirroring part QTransform resizeMatrix; resizeMatrix.translate(center.x(), center.y()); // translate to resizeMatrix.scale( qAbs(zoomX), qAbs(zoomY) ); resizeMatrix.translate(-center.x(), -center.y()); // and back // the mirroring part of the resizing transformation QTransform mirrorMatrix; mirrorMatrix.translate(center.x(), center.y()); // translate to mirrorMatrix.scale( zoomX < 0 ? -1 : 1, zoomY < 0 ? -1 : 1 ); mirrorMatrix.translate(-center.x(), -center.y()); // and back int i = 0; foreach(KoShape *shape, m_selectedShapes) { shape->update(); - + // HACK: Complex shapes like calloutshapes needs more control than setSize() gives + // as when resizing top/left it implies a positional change too. + // We cannot use the GenericMatrixChange above because it is used a number of times + // so it is impossible to know when it is relevant. + KoShapeContainer *container = dynamic_cast(shape); + if (container && container->model()) { + container->model()->containerChanged(container, KoShape::BeginResize); + } + // this uses resize for the zooming part shape->applyAbsoluteTransformation( m_unwindMatrix ); /* normally we would just apply the resizeMatrix now and be done with it, but we want to resize instead of scale, so we have to separate the scaling part of that transformation which can then be used to resize */ // undo the last resize transformation shape->applyAbsoluteTransformation( m_transformations[i].inverted() ); // save the shapes transformation matrix QTransform shapeMatrix = shape->absoluteTransformation(0); // calculate the matrix we would apply to the local shape matrix // that tells us the effective scale values we have to use for the resizing QTransform localMatrix = shapeMatrix * resizeMatrix * shapeMatrix.inverted(); // save the effective scale values qreal scaleX = localMatrix.m11(); qreal scaleY = localMatrix.m22(); // calculate the scale matrix which is equivalent to our resizing above QTransform scaleMatrix = (QTransform().scale( scaleX, scaleY )); scaleMatrix = shapeMatrix.inverted() * scaleMatrix * shapeMatrix; // calculate the new size of the shape, using the effective scale values QSizeF size( scaleX * m_startSizes[i].width(), scaleY * m_startSizes[i].height() ); // If a chart shape, handle its children if (m_chartShapes.contains(shape)) { m_chartShapes[shape]->setSize(m_startSizes[i], scaleX, scaleY); } // apply the transformation shape->setSize( size ); // apply the rest of the transformation without the resizing part shape->applyAbsoluteTransformation( scaleMatrix.inverted() * resizeMatrix ); shape->applyAbsoluteTransformation( mirrorMatrix ); // and remember the applied transformation later for later undoing m_transformations[i] = shapeMatrix.inverted() * shape->absoluteTransformation(0); shape->applyAbsoluteTransformation( m_windMatrix ); - + if (container && container->model()) { + container->model()->containerChanged(container, KoShape::EndResize); + } shape->update(); i++; } tool()->canvas()->shapeManager()->selection()->applyAbsoluteTransformation( matrix * m_scaleMatrix.inverted() ); m_scaleMatrix = matrix; } KUndo2Command* ShapeResizeStrategy::createCommand() { tool()->canvas()->snapGuide()->reset(); QVector newSizes; QVector transformations; const int shapeCount = m_selectedShapes.count(); newSizes.reserve(shapeCount); transformations.reserve(shapeCount); for ( int i = 0; i < shapeCount; ++i ) { newSizes << m_selectedShapes[i]->size(); transformations << m_selectedShapes[i]->transformation(); } - + qInfo()<createCommand(cmd); } } return cmd; } void ShapeResizeStrategy::finishInteraction(Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); } void ShapeResizeStrategy::paint( QPainter &painter, const KoViewConverter &converter) { SelectionDecorator decorator (KoFlake::NoHandle, false, false); decorator.setSelection(tool()->canvas()->shapeManager()->selection()); decorator.setHandleRadius(handleRadius()); decorator.paint(painter, converter); } diff --git a/plugins/defaultTools/defaulttool/ShapeRotateStrategy.cpp b/plugins/defaultTools/defaulttool/ShapeRotateStrategy.cpp index 924a23ad282..46ab1a14d58 100644 --- a/plugins/defaultTools/defaulttool/ShapeRotateStrategy.cpp +++ b/plugins/defaultTools/defaulttool/ShapeRotateStrategy.cpp @@ -1,153 +1,154 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007 Thomas Zander * Copyright (C) 2007-2008 Jan Hambrecht * * 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 "ShapeRotateStrategy.h" #include "SelectionDecorator.h" #include "SelectionTransformCommand.h" #include #include #include #include #include #include #include #include #include ShapeRotateStrategy::ShapeRotateStrategy(KoToolBase *tool, const QPointF &clicked, Qt::MouseButtons buttons) : KoInteractionStrategy(tool) , m_initialBoundingRect() , m_start(clicked) { m_initialSelectionMatrix = tool->canvas()->shapeManager()->selection()->transformation(); // FIXME: This test is also done in DefaultTool, so it should also be responsible for setting the shapes to operate on QList selectedShapes = tool->canvas()->shapeManager()->selection()->selectedShapes(KoFlake::StrippedSelection); foreach(KoShape *shape, selectedShapes) { if (shape->allowedInteraction(KoShape::RotationAllowed, false)) { m_selectedShapes << shape; if(m_selectedShapes.count() == 1) { m_initialBoundingRect = shape->boundingRect(); } else { m_initialBoundingRect = m_initialBoundingRect.united(shape->boundingRect()); } m_oldTransforms << shape->transformation(); } } if( buttons & Qt::RightButton ) m_rotationCenter = tool->canvas()->shapeManager()->selection()->absolutePosition( SelectionDecorator::hotPosition() ); else m_rotationCenter = m_initialBoundingRect.center(); tool->setStatusText( i18n( "Press ALT to rotate in 45 degree steps." ) ); } void ShapeRotateStrategy::handleMouseMove(const QPointF &point, Qt::KeyboardModifiers modifiers) { qreal angle = atan2( point.y() - m_rotationCenter.y(), point.x() - m_rotationCenter.x() ) - atan2( m_start.y() - m_rotationCenter.y(), m_start.x() - m_rotationCenter.x() ); angle = angle / M_PI * 180; // convert to degrees. if(modifiers & (Qt::AltModifier | Qt::ControlModifier)) { // limit to 45 degree angles qreal modula = qAbs(angle); while(modula > 45.0) modula -= 45.0; if(modula > 22.5) modula -= 45.0; angle += (angle>0?-1:1)*modula; } QTransform matrix; matrix.translate(m_rotationCenter.x(), m_rotationCenter.y()); matrix.rotate(angle); matrix.translate(-m_rotationCenter.x(), -m_rotationCenter.y()); QTransform applyMatrix = matrix * m_rotationMatrix.inverted(); m_rotationMatrix = matrix; foreach( KoShape * shape, m_selectedShapes ) { shape->update(); shape->applyAbsoluteTransformation( applyMatrix ); shape->update(); } tool()->canvas()->shapeManager()->selection()->applyAbsoluteTransformation( applyMatrix ); } void ShapeRotateStrategy::handleCustomEvent( KoPointerEvent * event ) { QTransform matrix; matrix.translate(m_rotationCenter.x(), m_rotationCenter.y()); matrix.rotate( 0.1 * event->rotationZ() ); matrix.translate(-m_rotationCenter.x(), -m_rotationCenter.y()); m_rotationMatrix *= matrix; foreach( KoShape * shape, m_selectedShapes ) { shape->update(); shape->applyAbsoluteTransformation( matrix ); shape->update(); } tool()->canvas()->shapeManager()->selection()->applyAbsoluteTransformation( matrix ); } void ShapeRotateStrategy::rotateBy( qreal angle ) { QTransform matrix; matrix.translate(m_rotationCenter.x(), m_rotationCenter.y()); matrix.rotate(angle); matrix.translate(-m_rotationCenter.x(), -m_rotationCenter.y()); QTransform applyMatrix = matrix * m_rotationMatrix.inverted(); m_rotationMatrix = matrix; foreach( KoShape * shape, m_selectedShapes ) { shape->update(); shape->applyAbsoluteTransformation( applyMatrix ); shape->update(); } tool()->canvas()->shapeManager()->selection()->applyAbsoluteTransformation( applyMatrix ); } void ShapeRotateStrategy::paint( QPainter &painter, const KoViewConverter &converter) { SelectionDecorator decorator(KoFlake::NoHandle, true, false); decorator.setSelection(tool()->canvas()->shapeManager()->selection()); decorator.setHandleRadius(handleRadius()); decorator.paint(painter, converter); // paint the rotation center painter.setPen(QPen(Qt::red, 0)); painter.setBrush( QBrush(Qt::red)); painter.setRenderHint( QPainter::Antialiasing, true ); QRectF circle( 0, 0, 5, 5 ); circle.moveCenter( converter.documentToView( m_rotationCenter ) ); painter.drawEllipse( circle ); } KUndo2Command* ShapeRotateStrategy::createCommand() { QVector newTransforms; newTransforms.reserve(m_selectedShapes.count()); foreach( KoShape* shape, m_selectedShapes ) newTransforms << shape->transformation(); + qInfo()<setText( kundo2_i18n("Rotate") ); KoSelection * sel = tool()->canvas()->shapeManager()->selection(); new SelectionTransformCommand(sel, m_initialSelectionMatrix, sel->transformation(), cmd); return cmd; } diff --git a/plugins/pathshapes/CMakeLists.txt b/plugins/pathshapes/CMakeLists.txt index 1bd91d7c424..90635cb5a76 100644 --- a/plugins/pathshapes/CMakeLists.txt +++ b/plugins/pathshapes/CMakeLists.txt @@ -1,49 +1,52 @@ add_definitions(-DTRANSLATION_DOMAIN=\"calligra_shape_paths\") include_directories( ${CMAKE_SOURCE_DIR}/libs/widgets ${FLAKE_INCLUDES} ) add_subdirectory(pics) set(pathshapes_PART_SRCS ellipse/EllipseShape.cpp ellipse/EllipseShapeFactory.cpp ellipse/EllipseShapeConfigWidget.cpp ellipse/EllipseShapeConfigCommand.cpp spiral/SpiralShape.cpp spiral/SpiralShapeFactory.cpp spiral/SpiralShapeConfigWidget.cpp spiral/SpiralShapeConfigCommand.cpp star/StarShape.cpp star/StarShapeFactory.cpp star/StarShapeConfigWidget.cpp star/StarShapeConfigCommand.cpp rectangle/RectangleShape.cpp rectangle/RectangleShapeFactory.cpp rectangle/RectangleShapeConfigWidget.cpp rectangle/RectangleShapeConfigCommand.cpp enhancedpath/EnhancedPathShape.cpp enhancedpath/EnhancedPathShapeFactory.cpp enhancedpath/EnhancedPathCommand.cpp enhancedpath/EnhancedPathParameter.cpp enhancedpath/EnhancedPathFormula.cpp enhancedpath/EnhancedPathHandle.cpp enhancedpath/CalloutShape.cpp enhancedpath/CalloutShapeFactory.cpp + enhancedpath/CalloutContainerModel.cpp + enhancedpath/CalloutToolFactory.cpp + enhancedpath/CalloutDebug.cpp PathShapesPlugin.cpp ) ki18n_wrap_ui(pathshapes_PART_SRCS star/StarShapeConfigWidget.ui rectangle/RectangleShapeConfigWidget.ui ellipse/EllipseShapeConfigWidget.ui spiral/SpiralShapeConfigWidget.ui ) add_library(calligra_shape_paths MODULE ${pathshapes_PART_SRCS}) calligra_shape_desktop_to_json(calligra_shape_paths calligra_shape_paths.desktop) target_link_libraries(calligra_shape_paths flake kowidgets) install(TARGETS calligra_shape_paths DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/shapes) diff --git a/plugins/pathshapes/enhancedpath/CalloutContainerModel.cpp b/plugins/pathshapes/enhancedpath/CalloutContainerModel.cpp new file mode 100644 index 00000000000..57781ce1d48 --- /dev/null +++ b/plugins/pathshapes/enhancedpath/CalloutContainerModel.cpp @@ -0,0 +1,363 @@ +/* This file is part of the KDE project + * Copyright (C) 2018 Dag Andersen + * + * 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 "CalloutContainerModel.h" + +#include "CalloutShape.h" +#include "EnhancedPathShape.h" +#include "CalloutDebug.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +void decompose(const QTransform &m, qreal &scaleX, qreal &scaleY, qreal &rotation, qreal &skewX, qreal &skewY, qreal &transX, qreal &transY) +{ + scaleX=0; scaleY=1; rotation=0; skewX=0; skewY=0; transX=0; transY=0; + qreal a = m.m11(); + qreal b = m.m12(); + qreal c = m.m21(); + qreal d = m.m22(); + qreal e = m.m31(); + qreal f = m.m32(); + + qreal Delta = a * d - b * c; + + if (a != 0 || b != 0) { + qreal r = qSqrt(a*a+b*b); + rotation = b > 0 ? qAcos(a/r) : -qAcos(a/r); + scaleX = r; + scaleY = Delta/r; + skewX = qAtan((a*c+b*d)/(r*r)); + } else if (c != 0 || d != 0) { + qreal s = qSqrt(c*c+d*d); + rotation = M_PI_2 - (d > 0 ? qAcos(-c/s) : -qAcos(c/s)); + scaleX = Delta/s; + scaleY = s; + skewY = qAtan((a*c+b*d)/(s*s)); + } else { // a = b = c = d = 0 + scaleX = 0.0; + scaleY = 0.0; + } + debugCallout<<"decomposed:"<transformation()); + QPointF center(0.5*container->size().width(), 0.5*container->size().height()); + return m.map(center) - center; +} + +CalloutContainerModel::CalloutContainerModel() + : KoShapeContainerDefaultModel() + , m_resizing(false) +{ +} + +void CalloutContainerModel::setIgnore(KoShape *shape, bool state) +{ + m_ignore.insert(shape, state); +} + +bool CalloutContainerModel::ignore(KoShape *shape) const +{ + return m_ignore.contains(shape) && m_ignore[shape]; +} + +void CalloutContainerModel::containerChanged(KoShapeContainer *container, KoShape::ChangeType type) +{ + switch (type) { + case KoShape::PositionChanged: { + QPointF center(0.5*container->size().width(), 0.5*container->size().height()); + QPointF pos = container->position(); + m_prevPosition = position(container); + debugCallout<(container); + Q_ASSERT(callout); + QPointF newPos = position(callout); + resizePath(callout->pathShape(), newPos, callout->size()); + m_prevPosition = newPos; + m_prevSize = container->size(); + } + break; + case KoShape::BeginResize: { + m_resizing = true; + m_prevPosition = position(container); + m_prevSize = container->size(); + break; + } + case KoShape::EndResize: { + debugCalloutF<>>"; + CalloutShape *callout = dynamic_cast(container); + QPointF newPos = position(callout); + resizePath(callout->pathShape(), newPos, callout->size()); + m_prevPosition = newPos; + m_prevSize = container->size(); + m_resizing = false; + debugCalloutF<position()<size()<<'('<absoluteTransformation(0); + //debugCalloutF<absolutePosition(); + break; + } + default: + //debugCalloutF<(shape); +// debugCalloutF<outlineRect(); +// if (!path) { +// return; +// } + //KoProperties params = path->parameters(); + //debugCalloutF<>>>>"; + + if (newSize == m_prevSize) { + debugCalloutF<<"No change"<<"<<<<<"; + return; + } + KoProperties params = path->parameters(); + QSizeF prevPathSize = path->size(); + QSizeF newPathSize; + debugCallout<<'\t'<<"prev:"< modifiers = path->modifiers(); + Q_ASSERT(modifiers.count() >= 2); + + QVariant viewboxData; + params.property("viewBox", viewboxData); + QRect viewBox = path->viewBox(); + + qreal currViewboxModifierRatioX = modifiers[0] / viewBox.width(); + qreal currViewboxModifierRatioY = modifiers[1] / viewBox.height(); + qreal viewboxModifierRatioX = 1.0; + qreal viewboxModifierRatioY = 1.0; + if (m_prevSize.isValid()) { + bool pointerLeft = modifiers[0] < viewBox.left(); + bool pointerRight = modifiers[0] > viewBox.right(); + bool pointerTop = modifiers[1] < viewBox.top(); + bool pointerBottom = modifiers[1] > viewBox.bottom(); + if (pointerTop) debugCallout<<'\t'<<"pointer above"; + if (pointerRight) debugCallout<<'\t'<<"pointer right"; + if (pointerLeft) debugCallout<<'\t'<<"pointer left"; + if (pointerBottom) debugCallout<<'\t'<<"pointer below"; + + bool movedX = qAbs(m_prevPosition.x() - newPos.x()) > 0.001; + bool movedY = qAbs(m_prevPosition.y() - newPos.y()) > 0.001; + if (movedX) debugCallout<<'\t'<<"x moved"<<"prev:"<setModifiers(modifiers); + params = path->parameters(); // get the new parameters + path->setParameters(params); // set new parameters to get everything updated + } + + QRectF pathRect(viewBox); + if (modifiers[0] < pathRect.left()) { + pathRect.setLeft(modifiers[0]); + } else if (modifiers[0] > pathRect.right()) { + pathRect.setRight(modifiers[0]); + } + if (modifiers[1] < pathRect.top()) { + pathRect.setTop(modifiers[1]); + } else if (modifiers[1] > pathRect.bottom()) { + pathRect.setBottom(modifiers[1]); + } + //debugCallout<<'\t'<<"pathrect:"<setSize(pathSize); + + qreal x = 0.0; + qreal y = 0.0; + if (pathRect.left() < 0.0) { + x = newSize.width() - pathSize.width(); + } + if (pathRect.top() < 0.0) { + y = newSize.height() - pathSize.height(); + } + path->setPosition(QPointF(x, y)); + + KoShape *textShape = path->text(); + if (textShape) { + textShape->setSize(newSize); + } + debugCalloutF<<"<<<<"; +} + diff --git a/plugins/pathshapes/enhancedpath/CalloutContainerModel.h b/plugins/pathshapes/enhancedpath/CalloutContainerModel.h new file mode 100644 index 00000000000..b308a70cd68 --- /dev/null +++ b/plugins/pathshapes/enhancedpath/CalloutContainerModel.h @@ -0,0 +1,60 @@ +/* This file is part of the KDE project + * Copyright (C) 2018 Dag Andersen + * + * 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 CALLOUTCONTAINERMODEL_H +#define CALLOUTCONTAINERMODEL_H + +#include "KoShapeContainerDefaultModel.h" + +#include +#include +#include +#include + +class KoShapeContainer; +class CalloutShape; +class PathShape; +class KoShape; + +class CalloutContainerModel : public KoShapeContainerDefaultModel +{ +public: + CalloutContainerModel(); + + virtual void containerChanged(KoShapeContainer *container, KoShape::ChangeType type); + + virtual void childChanged(KoShape *shape, KoShape::ChangeType type); + + bool isChildLocked(const KoShape *child) const; + + void setIgnore(KoShape *shape, bool state); + + bool ignore(KoShape *shape) const; + + void resizePath(PathShape *path, const QPointF &newPos, const QSizeF &newSize); + +private: + QTransform m_prevTrans; + QSizeF m_prevSize; + QPointF m_prevPosition; + bool m_resizing; + QHash m_ignore; +}; + +#endif /* CALLOUTCONTAINERMODEL_H */ diff --git a/plugins/pathshapes/enhancedpath/CalloutShape.h b/plugins/pathshapes/enhancedpath/CalloutDebug.cpp similarity index 57% copy from plugins/pathshapes/enhancedpath/CalloutShape.h copy to plugins/pathshapes/enhancedpath/CalloutDebug.cpp index a48702a5c6b..6d9a0bd829c 100644 --- a/plugins/pathshapes/enhancedpath/CalloutShape.h +++ b/plugins/pathshapes/enhancedpath/CalloutDebug.cpp @@ -1,50 +1,56 @@ /* This file is part of the KDE project * Copyright (C) 2018 Dag Andersen * * 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 CALLOUTSHAPE_H -#define CALLOUTSHAPE_H +#include "CalloutDebug.h" -#include +#include +#include -#define CalloutShapeId "CalloutShape" -/** - * A callout shape reprecents a callout - */ - -class CalloutShape : public EnhancedPathShape +QDebug operator<<(QDebug dbg, KoPathPoint *p) { -public: - explicit CalloutShape(const QRect &viewBox); - ~CalloutShape() override; - - void setType(const QString &type); - QString type() const; - - static bool isZero(qreal value); - -protected: - void saveOdf(KoShapeSavingContext &context) const override; - bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) override; - -private: - QString m_type; -}; + if (p) { + dbg << p->point(); + } else { + dbg << (void*)p; + } + return dbg; +} + +QDebug operator<<(QDebug dbg, KoSubpath *p) +{ + if (p) { + dbg << *p; + } else { + dbg << (void*)p; + } + return dbg; +} + +QDebug operator<<(QDebug dbg, KoProperties &p) +{ + dbg << p.store("Properties"); + return dbg; +} -#endif // KOENHANCEDPATHSHAPE_H +const QLoggingCategory &CALLOUT_LOG() +{ + static const QLoggingCategory category("calligra.plugin.callout"); + return category; +} diff --git a/plugins/pathshapes/enhancedpath/CalloutShape.h b/plugins/pathshapes/enhancedpath/CalloutDebug.h similarity index 57% copy from plugins/pathshapes/enhancedpath/CalloutShape.h copy to plugins/pathshapes/enhancedpath/CalloutDebug.h index a48702a5c6b..782108ea502 100644 --- a/plugins/pathshapes/enhancedpath/CalloutShape.h +++ b/plugins/pathshapes/enhancedpath/CalloutDebug.h @@ -1,50 +1,42 @@ /* This file is part of the KDE project * Copyright (C) 2018 Dag Andersen * * 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 CALLOUTSHAPE_H -#define CALLOUTSHAPE_H +#ifndef CALLOUTDEBUG_H +#define CALLOUTDEBUG_H -#include +#include +#include -#define CalloutShapeId "CalloutShape" +#include -/** - * A callout shape reprecents a callout - */ - -class CalloutShape : public EnhancedPathShape -{ -public: - explicit CalloutShape(const QRect &viewBox); - ~CalloutShape() override; - - void setType(const QString &type); - QString type() const; +class KoPathPoint; +class KoProperties; - static bool isZero(qreal value); +QDebug operator<<(QDebug dbg, KoPathPoint *p); +QDebug operator<<(QDebug dbg, KoSubpath *p); -protected: - void saveOdf(KoShapeSavingContext &context) const override; - bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) override; +QDebug operator<<(QDebug dbg, KoProperties &p); -private: - QString m_type; -}; +extern const QLoggingCategory &CALLOUT_LOG(); +#define debugCallout qCDebug(CALLOUT_LOG) +#define debugCalloutF qCDebug(CALLOUT_LOG)< * * 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 "CalloutShape.h" +#include "CalloutContainerModel.h" +#include "CalloutShapeFactory.h" +#include "CalloutDebug.h" #include "EnhancedPathCommand.h" #include "EnhancedPathFormula.h" #include "EnhancedPathHandle.h" #include #include #include #include #include #include #include +#include +#include -CalloutShape::CalloutShape(const QRect &viewBox) - : EnhancedPathShape(viewBox) -{ -} +#include -CalloutShape::~CalloutShape() -{ -} +#include +#include -void CalloutShape::setType(const QString &type) -{ - m_type = type; -} - -QString CalloutShape::type() const -{ - return m_type; -} -bool CalloutShape::isZero(qreal value) +PathShape::PathShape(const QRect &viewBox) + : EnhancedPathShape(viewBox) { - return qAbs(value) < 1E-5; + qInfo()<model() && type == KoShape::ParameterChanged) { +// // parent()->model()->childChanged(this, type); +// // return; +// // } +// EnhancedPathShape::shapeChanged(type, shape); +// } + +KoProperties PathShape::parameters() const { - context.xmlWriter().startElement("draw:custom-shape"); + KoProperties params; - QSizeF currentSize = size(); - QPointF currentPos = position(); + params.setProperty("viewBox", m_viewBox); - // NOTE: - // A callout shape position and size shall be the position and size - // of the 'bubble' part of the callout. - // The viewbox shall also be the size of the 'bubble' part. - // This implies that the 'pointer' part will (normally) - // go *outside* the viewbox (and shape). - // This is imo (danders) not according to odf spec, - // but is the way LO/OO does it. - - QRectF rect; - if (m_viewBound.top() < m_viewBox.top()) { - qreal topOffset = m_viewBound.top() - m_viewBox.top(); - if (!isZero(topOffset)) { - topOffset *= currentSize.height() / m_viewBound.height(); - rect.setTop(topOffset); - } + if (m_pathStretchPointX != -1) { + params.setProperty("path-stretchpoint-x", m_pathStretchPointX); } - if (m_viewBound.left() < m_viewBox.left()) { - qreal leftOffset = m_viewBound.left() - m_viewBox.left(); - if (!isZero(leftOffset)) { - leftOffset *= currentSize.width() / m_viewBound.width(); - rect.setLeft(leftOffset); - } + if (m_pathStretchPointY != -1) { + params.setProperty("path-stretchpoint-y", m_pathStretchPointY); } - if (m_viewBound.bottom() > m_viewBox.bottom()) { - qreal bottomOffset = m_viewBound.bottom() - m_viewBox.bottom(); - if (!isZero(bottomOffset)) { - bottomOffset *= currentSize.height() / m_viewBound.height(); - rect.setBottom(bottomOffset); - } + + if (m_mirrorHorizontally) { + params.setProperty("mirror-horizontal", "true"); } - if (m_viewBound.right() > m_viewBox.right()) { - qreal rightOffset = m_viewBound.right() - m_viewBox.right(); - if (!isZero(rightOffset)) { - rightOffset *= currentSize.width() / m_viewBound.width(); - rect.setRight(rightOffset); - } + if (m_mirrorVertically) { + params.setProperty("mirror-vertical", "true"); + } + + QString modifiers; + foreach (qreal modifier, m_modifiers) { + modifiers += QString::number(modifier) + ' '; + } + params.setProperty("modifiers", modifiers.trimmed()); + + if (m_textArea.count() >= 4) { + params.setProperty("text-areas", m_textArea.join(" ")); } - if (!rect.topLeft().isNull()) { - // TODO: - // This does not work for sheared shapes, (only rotated) - // but then there are LO interop problems with - // sheared enhanced-path shapes in general. - qreal x = 0, y = 0; - QTransform trans = absoluteTransformation(0); - // remove translation - trans.setMatrix(trans.m11(), trans.m12(), trans.m13(), trans.m21(), trans.m22(), trans.m23(), qreal(0.0), qreal(0.0), trans.m33()); - trans.map(rect.left(), rect.top(), &x, &y); - QTransform offset; - offset.translate(-x, -y); - context.addShapeOffset(this, offset); + QStringList path; + foreach (EnhancedPathCommand * c, m_commands) { + path << c->toString(); } + params.setProperty("commands", path); - saveOdfAttributes(context, OdfAllAttributes&~OdfSize&~OdfViewbox); + CalloutShapeFactory::ComplexType formulae; + FormulaStore::const_iterator i = m_formulae.constBegin(); + for (; i != m_formulae.constEnd(); ++i) { + formulae[i.key()] = i.value()->toString(); + } + params.setProperty("formulae", formulae); - currentSize -= rect.size(); - context.xmlWriter().addAttributePt("svg:width", currentSize.width()); - context.xmlWriter().addAttributePt("svg:height", currentSize.height()); + CalloutShapeFactory::ListType handles; + CalloutShapeFactory::ComplexType handle; + foreach (EnhancedPathHandle *h, m_enhancedHandles) { + QString pos = h->positionX() + ' ' + h->positionY(); + handle["draw:handle-position"] = pos; + } + handles.append(QVariant(handle)); + params.setProperty("handles", handles); - saveText(context); + return params; +} - context.xmlWriter().startElement("draw:enhanced-geometry"); - if (!m_type.isEmpty()) { - context.xmlWriter().addAttribute("draw:type", m_type); +void PathShape::setModifiers(const QList &modifiers) +{ + if (m_modifiers.count() < 2) { + m_modifiers = modifiers; + } else { + modifyReference("$0", modifiers.at(0)); + modifyReference("$1", modifiers.at(1)); } - context.xmlWriter().addAttribute("svg:viewBox", QString("%1 %2 %3 %4").arg(m_viewBox.x()).arg(m_viewBox.y()).arg(m_viewBox.width()).arg(m_viewBox.height())); +} - if (m_pathStretchPointX != -1) { - context.xmlWriter().addAttribute("draw:path-stretchpoint-x", m_pathStretchPointX); + +void PathShape::setViewBox(const QRect &box) { + m_viewBox = box; +} + +QList PathShape::parseModifiers(const QString &m) const +{ + QList lst; + if (m.isEmpty()) { + return lst; } - if (m_pathStretchPointY != -1) { - context.xmlWriter().addAttribute("draw:path-stretchpoint-y", m_pathStretchPointY); + QStringList tokens = m.simplified().split(' '); + int tokenCount = tokens.count(); + for (int i = 0; i < tokenCount; ++i) { + lst.append((qreal)tokens[i].toDouble()); } + return lst; +} - if (m_mirrorHorizontally) { - context.xmlWriter().addAttribute("draw:mirror-horizontal", "true"); +void PathShape::setParameters(const KoProperties ¶ms) +{ + reset(); + + QVariant viewboxData; + params.property("viewBox", viewboxData); + setViewBox(viewboxData.toRect()); +// qInfo()< modifiers = parseModifiers(params.stringProperty("modifiers")); + Q_ASSERT(modifiers.count() >= 2); + m_modifiers = modifiers; +// qInfo()<= 4) { - context.xmlWriter().addAttribute("draw:text-areas", m_textArea.join(" ")); + QVariant color; + if (params.property("background", color)) { + setBackground(QSharedPointer(new KoColorBackground(color.value()))); } - - QString path; - foreach (EnhancedPathCommand * c, m_commands) { - path += c->toString() + ' '; + if (params.contains("text-areas")) { + m_textArea = params.stringProperty("text-areas").split(' '); } - context.xmlWriter().addAttribute("draw:enhanced-path", path.trimmed()); +} - FormulaStore::const_iterator i = m_formulae.constBegin(); - for (; i != m_formulae.constEnd(); ++i) { - context.xmlWriter().startElement("draw:equation"); - context.xmlWriter().addAttribute("draw:name", i.key()); - context.xmlWriter().addAttribute("draw:formula", i.value()->toString()); - context.xmlWriter().endElement(); // draw:equation +KoTextShapeDataBase *PathShape::textData() const +{ + KoTextShapeDataBase *textData = 0; + KoShape *ts = textShape(); + if (ts) { + textData = qobject_cast(ts->userData()); } + return textData; +} - foreach (EnhancedPathHandle * handle, m_enhancedHandles) { - handle->saveOdf(context); + +//----------------------------------------------------------------- +CalloutShape::CalloutShape(const KoProperties *params) + : KoShapeContainer(new CalloutContainerModel()) + , m_path(nullptr) + , m_type("callout") +{ + if (params->contains("type")) { + m_type = params->stringProperty("type"); } + m_path = new PathShape(); + m_path->setParameters(*params); + addShape(m_path); + setClipped(m_path, false); + setInheritsTransform(m_path, true); +} - context.xmlWriter().endElement(); // draw:enhanced-geometry - saveOdfCommonChildElements(context); - context.xmlWriter().endElement(); // draw:custom-shape +CalloutShape::~CalloutShape() +{ } -bool CalloutShape::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context) +void CalloutShape::saveOdf(KoShapeSavingContext &context) const { - reset(); + Q_ASSERT(m_path); + + context.xmlWriter().startElement("draw:custom-shape"); + + // NOTE: + // A callout shape position and size shall be the position and size + // of the 'bubble' part of the callout. + // The viewbox shall also be the size of the 'bubble' part. + // This implies that the 'pointer' part will (normally) + // go *outside* the viewbox (and shape). + // This is imo (danders) not according to odf spec, + // but is the way LO/OO does it. + saveOdfAttributes(context, OdfTransformation | OdfSize); + + m_path->saveOdfAttributes(context, OdfAllAttributes & ~OdfTransformation & ~OdfSize & ~OdfViewbox); + m_path->saveText(context); + m_path->saveEnhancedGeometry(context); +} + +bool CalloutShape::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context) +{ const KoXmlElement enhancedGeometry(KoXml::namedItemNS(element, KoXmlNS::draw, "enhanced-geometry" ) ); if (!enhancedGeometry.isNull() ) { m_type = enhancedGeometry.attributeNS(KoXmlNS::draw, "type", "callout"); - - setPathStretchPointX(enhancedGeometry.attributeNS(KoXmlNS::draw, "path-stretchpoint-x","-1").toDouble()); - setPathStretchPointY(enhancedGeometry.attributeNS(KoXmlNS::draw, "path-stretchpoint-y","-1").toDouble()); - - // load the modifiers - QString modifiers = enhancedGeometry.attributeNS(KoXmlNS::draw, "modifiers", ""); - if (! modifiers.isEmpty()) { - addModifiers(modifiers); - } - - m_textArea = enhancedGeometry.attributeNS(KoXmlNS::draw, "text-areas", "").split(' '); - if (m_textArea.size() >= 4) { - setResizeBehavior(TextFollowsPreferredTextRect); - } - - KoXmlElement grandChild; - forEachElement(grandChild, enhancedGeometry) { - if (grandChild.namespaceURI() != KoXmlNS::draw) - continue; - if (grandChild.localName() == "equation") { - QString name = grandChild.attributeNS(KoXmlNS::draw, "name"); - QString formula = grandChild.attributeNS(KoXmlNS::draw, "formula"); - addFormula(name, formula); - } else if (grandChild.localName() == "handle") { - EnhancedPathHandle * handle = new EnhancedPathHandle(this); - if (handle->loadOdf(grandChild, context)) { - m_enhancedHandles.append(handle); - evaluateHandles(); - } else { - delete handle; - } - } - - } - - setMirrorHorizontally(enhancedGeometry.attributeNS(KoXmlNS::draw, "mirror-horizontal") == "true"); - setMirrorVertically(enhancedGeometry.attributeNS(KoXmlNS::draw, "mirror-vertical") == "true"); - - // load the enhanced path data - QString path = enhancedGeometry.attributeNS(KoXmlNS::draw, "enhanced-path", ""); - - // load the viewbox - m_viewBox = loadOdfViewbox(enhancedGeometry); - - if (!path.isEmpty()) { - parsePathData(path); - } - - if (m_viewBox.isEmpty()) { - // if there is no view box defined make it is big as the path. - m_viewBox = m_viewBound.toAlignedRect(); - } + + m_path->loadEnhancedGeometry(enhancedGeometry, context); } - QSizeF size; - size.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString()))); - size.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString()))); - // the viewbox is to be fitted into the size of the shape, so before setting - // the size we just loaded, we set the viewbox to be the basis to calculate - // the viewbox matrix from - m_viewBound = m_viewBox; - setSize(size); + loadOdfAttributes(element, context, OdfAllAttributes); - QPointF pos; - pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString()))); - pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString()))); - setPosition(pos - m_viewMatrix.map(QPointF(0, 0)) - m_viewBoxOffset); + return true; +} - loadOdfAttributes(element, context, OdfMandatories | OdfTransformation | OdfAdditionalAttributes | OdfCommonChildElements); +KoTextShapeDataBase *CalloutShape::textData() const +{ + return m_path ? m_path->textData() : nullptr; +} - loadText(element, context); - return true; +void CalloutShape::setTextArea(const QRectF &rect) +{ + //setPreferredTextRect(rect); +} + +QRectF CalloutShape::boundingRect() const +{ + return KoShapeContainer::boundingRect() | m_path->boundingRect(); +} + +void CalloutShape::paintComponent(QPainter &/*painter*/, const KoViewConverter &/*converter*/, KoShapePaintingContext &/*paintcontext*/) +{ } diff --git a/plugins/pathshapes/enhancedpath/CalloutShape.h b/plugins/pathshapes/enhancedpath/CalloutShape.h index a48702a5c6b..ac9b8a3219f 100644 --- a/plugins/pathshapes/enhancedpath/CalloutShape.h +++ b/plugins/pathshapes/enhancedpath/CalloutShape.h @@ -1,50 +1,102 @@ /* This file is part of the KDE project * Copyright (C) 2018 Dag Andersen * * 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 CALLOUTSHAPE_H #define CALLOUTSHAPE_H -#include +#include +#include + +#include "EnhancedPathShape.h" + +#include #define CalloutShapeId "CalloutShape" +#define CalloutPathShapeId "CalloutPathShape" + +class KoTextShape; +class KoTextShapeDataBase; +class KoPathPoint; + +class PathShape; /** * A callout shape reprecents a callout */ -class CalloutShape : public EnhancedPathShape +class CalloutShape : public KoShapeContainer { public: - explicit CalloutShape(const QRect &viewBox); + explicit CalloutShape(const KoProperties *params = nullptr); ~CalloutShape() override; void setType(const QString &type); QString type() const; - static bool isZero(qreal value); + void setTextArea(const QRectF &rect); + + KoTextShapeDataBase *textData() const; + + QRectF boundingRect() const override; + + void paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) override; + + PathShape *pathShape() const { return m_path; } protected: void saveOdf(KoShapeSavingContext &context) const override; bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) override; private: + PathShape *m_path; QString m_type; }; -#endif // KOENHANCEDPATHSHAPE_H +class PathShape : public EnhancedPathShape +{ +public: + explicit PathShape(const QRect &viewBox = QRect()); + + // void setSize(const QSizeF &size); + + //void setTextArea(const QRectF &rect); + + KoShape *text() const { return textShape(); } + KoTextShapeDataBase *textData() const; + + KoSubpathList subpaths() const { return m_subpaths; } + + void setModifiers(const QList &modifiers); + QList modifiers() { return m_modifiers; } + + QRectF viewBound() const { return m_viewBound; } + /// Sets the viewbox of the enhanced path shape + void setViewBox(const QRect &box); + void prepare(); + + QList parseModifiers(const QString &m) const; + void setParameters(const KoProperties ¶ms); + KoProperties parameters() const; + +protected: +// virtual void shapeChanged(ChangeType type, KoShape *shape = 0); + +}; + +#endif // CALLOUTSHAPE_H diff --git a/plugins/pathshapes/enhancedpath/CalloutShapeFactory.cpp b/plugins/pathshapes/enhancedpath/CalloutShapeFactory.cpp index 101d3fdcb1f..06d18cc1ccd 100644 --- a/plugins/pathshapes/enhancedpath/CalloutShapeFactory.cpp +++ b/plugins/pathshapes/enhancedpath/CalloutShapeFactory.cpp @@ -1,208 +1,272 @@ /* This file is part of the KDE project * Copyright (C) 2018 Dag Andersen * Copyright (C) 2007 Jan Hambrecht * * 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 "CalloutShapeFactory.h" +#include "CalloutToolFactory.h" #include "CalloutShape.h" +#include "CalloutContainerModel.h" #include #include #include #include #include #include +#include +#include #include #include #include #include CalloutShapeFactory::CalloutShapeFactory() : KoShapeFactoryBase(CalloutShapeId, i18n("A callout shape")) { setToolTip(i18n("A callout shape")); setIconName(koIconName("enhancedpath")); setXmlElementNames(KoXmlNS::draw, QStringList("custom-shape")); setLoadingPriority(5); + // Register all tools for the callout shape. + KoToolRegistry::instance()->add(new CalloutToolFactory()); + addCallout(); + addRoundedCallout(); } KoShape *CalloutShapeFactory::createDefaultShape(KoDocumentResourceManager *) const { Q_ASSERT(!templates().isEmpty()); return createShape(templates().first().properties); } -KoShape *CalloutShapeFactory::createShape(const KoProperties *params, KoDocumentResourceManager *) const +KoShape *CalloutShapeFactory::createShape(const KoProperties *params, KoDocumentResourceManager *resourceManager) const { - Q_ASSERT(params->contains("viewBox")); - QVariant viewboxData; - params->property("viewBox", viewboxData); - - CalloutShape *shape = new CalloutShape(viewboxData.toRect()); - - shape->setShapeId(KoPathShapeId); - shape->setStroke(new KoShapeStroke(1.0)); - shape->setType(params->stringProperty("type")); - shape->addModifiers(params->stringProperty("modifiers")); - - ListType handles = params->property("handles").toList(); - foreach (const QVariant &v, handles) { - shape->addHandle(v.toMap()); - } + CalloutShape *shape = new CalloutShape(params); + shape->setShapeId(CalloutShapeId); + shape->setSize(QSizeF(150, 100)); - ComplexType formulae = params->property("formulae").toMap(); - ComplexType::const_iterator formula = formulae.constBegin(); - ComplexType::const_iterator lastFormula = formulae.constEnd(); - for (; formula != lastFormula; ++formula) { - shape->addFormula(formula.key(), formula.value().toString()); - } - QStringList commands = params->property("commands").toStringList(); - foreach (const QString &cmd, commands) { - shape->addCommand(cmd); - } - - QVariant color; - if (params->property("background", color)) { - shape->setBackground(QSharedPointer(new KoColorBackground(color.value()))); - } - QSizeF size = shape->size(); - if (size.width() > size.height()) { - shape->setSize(QSizeF(100, 100 * size.height() / size.width())); - } else { - shape->setSize(QSizeF(100 * size.width() / size.height(), 100)); + KoShape *textShape = shape->pathShape()->createTextShape(resourceManager); + if (textShape) { + textShape->setSize(shape->size()); + KoTextShapeDataBase *shapeData = qobject_cast(textShape->userData()); + shapeData->setResizeMethod(KoTextShapeDataBase::AutoGrowWidthAndHeight); + //qInfo()<position()<size(); } return shape; } KoProperties* CalloutShapeFactory::dataToProperties( - const QString &modifiers, const QStringList &commands, + const QStringList &commands, const ListType &handles, const ComplexType & formulae) const { KoProperties *props = new KoProperties(); - props->setProperty("modifiers", modifiers); props->setProperty("commands", commands); props->setProperty("handles", handles); props->setProperty("formulae", formulae); props->setProperty("background", QVariant::fromValue(QColor(Qt::red))); return props; } +KoProperties *CalloutShapeFactory::squareProperties() const +{ + QStringList commands; + commands.append("M 0 0"); + commands.append("L 0 3590 ?f2 ?f3 0 8970 0 12630 ?f4 ?f5 0 18010 0 21600 3590 21600 ?f6 ?f7 8970 21600 12630 21600 ?f8 ?f9 18010 21600 21600 21600 21600 18010 ?f10 ?f11 21600 12630 21600 8970 ?f12 ?f13 21600 3590 21600 0 18010 0 ?f14 ?f15 12630 0 8970 0 ?f16 ?f17 3590 0 0 0"); + commands.append("Z"); + commands.append("N"); + + ComplexType formulae; + formulae["f0"] = "$0 -10800"; + formulae["f1"] = "$1 -10800"; + formulae["f2"] = "if(?f18 ,$0 ,0)"; + formulae["f3"] = "if(?f18 ,$1 ,6280)"; + formulae["f4"] = "if(?f23 ,$0 ,0)"; + formulae["f5"] = "if(?f23 ,$1 ,15320)"; + formulae["f6"] = "if(?f26 ,$0 ,6280)"; + formulae["f7"] = "if(?f26 ,$1 ,21600)"; + formulae["f8"] = "if(?f29 ,$0 ,15320)"; + formulae["f9"] = "if(?f29 ,$1 ,21600)"; + formulae["f10"] = "if(?f32 ,$0 ,21600)"; + formulae["f11"] = "if(?f32 ,$1 ,15320)"; + formulae["f12"] = "if(?f34 ,$0 ,21600)"; + formulae["f13"] = "if(?f34 ,$1 ,6280)"; + formulae["f14"] = "if(?f36 ,$0 ,15320)"; + formulae["f15"] = "if(?f36 ,$1 ,0)"; + formulae["f16"] = "if(?f38 ,$0 ,6280)"; + formulae["f17"] = "if(?f38 ,$1 ,0)"; + formulae["f18"] = "if($0 ,-1,?f19)"; + formulae["f19"] = "if(?f1 ,-1,?f22)"; + formulae["f20"] = "abs(?f0)"; + formulae["f21"] = "abs(?f1)"; + formulae["f22"] = "?f20 -?f21 "; + formulae["f23"] = "if($0 ,-1,?f24 )"; + formulae["f24"] = "if(?f1 ,?f22 ,-1)"; + formulae["f25"] = "$1 -21600"; + formulae["f26"] = "if(?f25 ,?f27 ,-1)"; + formulae["f27"] = "if(?f0 ,-1,?f28 )"; + formulae["f28"] = "?f21 -?f20"; + formulae["f29"] = "if(?f25 ,?f30 ,-1)"; + formulae["f30"] = "if(?f0 ,?f28 ,-1)"; + formulae["f31"] = "$0 -21600"; + formulae["f32"] = "if(?f31 ,?f33 ,-1)"; + formulae["f33"] = "if(?f1 ,?f22 ,-1)"; + formulae["f34"] = "if(?f31 ,?f35 ,-1)"; + formulae["f35"] = "if(?f1 ,-1,?f22 )"; + formulae["f36"] = "if($1 ,-1,?f37 )"; + formulae["f37"] = "if(?f0 ,?f28 ,-1)"; + formulae["f38"] = "if($1 ,-1,?f39 )"; + formulae["f39"] = "if(?f0 ,-1,?f28 )"; + formulae["f40"] = "$0"; + formulae["f41"] = "$1"; + + ListType handles; + ComplexType handle; + handle["draw:handle-position"] = "$0 $1"; + handles.append(QVariant(handle)); + + KoProperties* properties = dataToProperties(commands, handles, formulae); + properties->setProperty("viewBox", QRect(0, 0, 21600, 21600)); + + return properties; +} + void CalloutShapeFactory::addCallout() { - QString modifiers("4250 45000"); + + KoShapeTemplate t; + t.id = CalloutShapeId; + t.templateId = "rectangular"; + t.name = i18n("Rectangular Callout"); + t.family = "funny"; + t.toolTip = i18n("A rectangular callout"); + t.iconName = koIconName("callout-shape"); + + KoProperties* properties = squareProperties(); + + properties->setProperty("modifiers", "10800 43200"); + properties->setProperty("type", "rectangular-callout"); + t.properties = properties; + + addTemplate(t); +} +void CalloutShapeFactory::addRoundedCallout() +{ + QStringList commands; commands.append("M 3590 0"); commands.append("X 0 3590"); commands.append("L ?f2 ?f3 0 8970 0 12630 ?f4 ?f5 0 18010"); commands.append("Y 3590 21600"); commands.append("L ?f6 ?f7 8970 21600 12630 21600 ?f8 ?f9 18010 21600"); commands.append("X 21600 18010"); commands.append("L ?f10 ?f11 21600 12630 21600 8970 ?f12 ?f13 21600 3590"); commands.append("Y 18010 0"); commands.append("L ?f14 ?f15 12630 0 8970 0 ?f16 ?f17"); commands.append("Z"); commands.append("N"); - + ComplexType formulae; formulae["f0"] = "$0 -10800"; formulae["f1"] = "$1 -10800"; formulae["f2"] = "if(?f18 ,$0 ,0)"; formulae["f3"] = "if(?f18 ,$1 ,6280)"; formulae["f4"] = "if(?f23 ,$0 ,0)"; formulae["f5"] = "if(?f23 ,$1 ,15320)"; formulae["f6"] = "if(?f26 ,$0 ,6280)"; formulae["f7"] = "if(?f26 ,$1 ,21600)"; formulae["f8"] = "if(?f29 ,$0 ,15320)"; formulae["f9"] = "if(?f29 ,$1 ,21600)"; formulae["f10"] = "if(?f32 ,$0 ,21600)"; formulae["f11"] = "if(?f32 ,$1 ,15320)"; formulae["f12"] = "if(?f34 ,$0 ,21600)"; formulae["f13"] = "if(?f34 ,$1 ,6280)"; formulae["f14"] = "if(?f36 ,$0 ,15320)"; formulae["f15"] = "if(?f36 ,$1 ,0)"; formulae["f16"] = "if(?f38 ,$0 ,6280)"; formulae["f17"] = "if(?f38 ,$1 ,0)"; formulae["f18"] = "if($0 ,-1,?f19)"; formulae["f19"] = "if(?f1 ,-1,?f22)"; formulae["f20"] = "abs(?f0)"; formulae["f21"] = "abs(?f1)"; formulae["f22"] = "?f20 -?f21"; formulae["f23"] = "if($0 ,-1,?f24)"; formulae["f24"] = "if(?f1 ,?f22 ,-1)"; formulae["f25"] = "$1 -21600"; formulae["f26"] = "if(?f25 ,?f27 ,-1)"; formulae["f27"] = "if(?f0 ,-1,?f28)"; formulae["f28"] = "?f21 -?f20"; formulae["f29"] = "if(?f25 ,?f30 ,-1)"; formulae["f30"] = "if(?f0 ,?f28 ,-1)"; formulae["f31"] = "$0 -21600"; formulae["f32"] = "if(?f31 ,?f33 ,-1)"; formulae["f33"] = "if(?f1 ,?f22 ,-1)"; formulae["f34"] = "if(?f31 ,?f35 ,-1)"; formulae["f35"] = "if(?f1 ,-1,?f22)"; formulae["f36"] = "if($1 ,-1,?f37)"; formulae["f37"] = "if(?f0 ,?f28 ,-1)"; formulae["f38"] = "if($1 ,-1,?f39)"; formulae["f39"] = "if(?f0 ,-1,?f28)"; formulae["f40"] = "$0"; formulae["f41"] = "$1"; - + ListType handles; ComplexType handle; handle["draw:handle-position"] = "$0 $1"; handles.append(QVariant(handle)); - + KoShapeTemplate t; - t.id = KoPathShapeId; - t.templateId = "rectangular"; - t.name = i18n("Rectangular Callout"); + t.id = CalloutShapeId; + t.templateId = "rounded-rectangular"; + t.name = i18n("Rounded Callout"); t.family = "funny"; - t.toolTip = i18n("A rectangular callout"); + t.toolTip = i18n("A rounded rectangular callout"); t.iconName = koIconName("callout-shape"); - KoProperties* properties = dataToProperties(modifiers, commands, handles, formulae); + KoProperties* properties = dataToProperties(commands, handles, formulae); properties->setProperty("viewBox", QRect(0, 0, 21600, 21600)); - properties->setProperty("type", "rectangular-callout"); + properties->setProperty("modifiers", "4250 40000"); + properties->setProperty("type", "rounded-rectangular-callout"); t.properties = properties; - + addTemplate(t); } bool CalloutShapeFactory::supports(const KoXmlElement & e, KoShapeLoadingContext &context) const { Q_UNUSED(context); if (e.localName() == "custom-shape" && e.namespaceURI() == KoXmlNS::draw) { KoXmlElement x = KoXml::namedItemNS(e, KoXmlNS::draw, "enhanced-geometry"); if (x.isNull()) { return false; } if (x.attributeNS(KoXmlNS::draw, "type").contains("callout")) { return true; } } return false; } diff --git a/plugins/pathshapes/enhancedpath/CalloutShapeFactory.h b/plugins/pathshapes/enhancedpath/CalloutShapeFactory.h index c10cb80509a..4cd5c6ac1d1 100644 --- a/plugins/pathshapes/enhancedpath/CalloutShapeFactory.h +++ b/plugins/pathshapes/enhancedpath/CalloutShapeFactory.h @@ -1,45 +1,48 @@ /* This file is part of the KDE project * Copyright (C) 2018 Dag Andersen * * 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 CALLOUTSHAPEFACTORY_H #define CALLOUTSHAPEFACTORY_H #include class KoShape; /// Factory for callout shapes class CalloutShapeFactory : public KoShapeFactoryBase { public: CalloutShapeFactory(); ~CalloutShapeFactory() {} KoShape *createShape(const KoProperties *params, KoDocumentResourceManager *documentResources = 0) const override; KoShape *createDefaultShape(KoDocumentResourceManager *documentResources = 0) const override; bool supports(const KoXmlElement &e, KoShapeLoadingContext &context) const override; -private: - void addCallout(); typedef QMap ComplexType; typedef QList ListType; - KoProperties* dataToProperties(const QString &modifiers, const QStringList &commands, const ListType &handles, const ComplexType &formulae) const; +private: + void addCallout(); + void addRoundedCallout(); + + KoProperties* dataToProperties(const QStringList &commands, const ListType &handles, const ComplexType &formulae) const; + KoProperties *squareProperties() const; }; #endif // CALLOUTSHAPEFACTORY_H diff --git a/plugins/pathshapes/enhancedpath/CalloutToolFactory.cpp b/plugins/pathshapes/enhancedpath/CalloutToolFactory.cpp new file mode 100644 index 00000000000..69211317b9d --- /dev/null +++ b/plugins/pathshapes/enhancedpath/CalloutToolFactory.cpp @@ -0,0 +1,99 @@ +/* This file is part of the KDE project + * Copyright (C) 2006 Jan Hambrecht + * + * 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 "CalloutToolFactory.h" +#include "CalloutShape.h" +#include "CalloutDebug.h" + +#include +#include +#include +#include + +#include + +#include + +CalloutToolFactory::CalloutToolFactory() + : KoToolFactoryBase("CalloutPathToolFactoryId") +{ + setToolTip(i18n("Callout editing")); + setToolType(dynamicToolType()); + setIconName(koIconName("editpath")); + setPriority(10); + setActivationShapeId(CalloutPathShapeId); +} + +CalloutToolFactory::~CalloutToolFactory() +{ +} + +KoToolBase *CalloutToolFactory::createTool(KoCanvasBase *canvas) +{ + return new CalloutPathTool(canvas); +} + + +CalloutPathTool::CalloutPathTool(KoCanvasBase *canvas) + : KoPathTool(canvas) +{ + QAction *a = action("convert-to-path"); + qInfo()<disconnect(); + } +} + +QList > CalloutPathTool::createOptionWidgets() +{ + QList > list; + return list; +} + + +void CalloutPathTool::activate(ToolActivation toolActivation, const QSet &shapes) +{ + Q_UNUSED(toolActivation); + // retrieve the actual global handle radius + m_handleRadius = handleRadius(); + canvas()->snapGuide()->reset(); + + repaintDecorations(); + QList selectedShapes; + foreach(KoShape *shape, shapes) { + PathShape *pathShape = dynamic_cast(shape); + debugCalloutF<shapeId(); + if (shape->isSelectable() && pathShape) { + // as the tool is just in activation repaintDecorations does not yet get called + // so we need to use repaint of the tool and it is only needed to repaint the + // current canvas + repaint(pathShape->boundingRect()); + selectedShapes.append(pathShape); + } + } + if (selectedShapes.isEmpty()) { + emit done(); + return; + } + m_pointSelection.setSelectedShapes(selectedShapes); + useCursor(m_selectCursor); +// connect(canvas()->shapeManager()->selection(), SIGNAL(selectionChanged()), this, SLOT(activate())); + updateOptionsWidget(); + updateActions(); +} diff --git a/plugins/pathshapes/enhancedpath/CalloutShape.h b/plugins/pathshapes/enhancedpath/CalloutToolFactory.h similarity index 55% copy from plugins/pathshapes/enhancedpath/CalloutShape.h copy to plugins/pathshapes/enhancedpath/CalloutToolFactory.h index a48702a5c6b..e72dd468f19 100644 --- a/plugins/pathshapes/enhancedpath/CalloutShape.h +++ b/plugins/pathshapes/enhancedpath/CalloutToolFactory.h @@ -1,50 +1,49 @@ /* This file is part of the KDE project - * Copyright (C) 2018 Dag Andersen + * Copyright (C) 2006 Jan Hambrecht * * 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 CALLOUTSHAPE_H -#define CALLOUTSHAPE_H +#ifndef CALLOUTTOOLFACTORY_H +#define CALLOUTTOOLFACTORY_H -#include +#include +#include -#define CalloutShapeId "CalloutShape" -/** - * A callout shape reprecents a callout - */ - -class CalloutShape : public EnhancedPathShape +/// Factory for the KoPathTool +class CalloutToolFactory : public KoToolFactoryBase { public: - explicit CalloutShape(const QRect &viewBox); - ~CalloutShape() override; + CalloutToolFactory(); + ~CalloutToolFactory(); - void setType(const QString &type); - QString type() const; + KoToolBase *createTool(KoCanvasBase *canvas); +}; - static bool isZero(qreal value); +class CalloutPathTool : public KoPathTool +{ + Q_OBJECT +public: + CalloutPathTool(KoCanvasBase *canvas); -protected: - void saveOdf(KoShapeSavingContext &context) const override; - bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) override; + QList > createOptionWidgets(); -private: - QString m_type; +public Q_SLOTS: + void activate(ToolActivation toolActivation, const QSet &shapes); }; -#endif // KOENHANCEDPATHSHAPE_H +#endif diff --git a/plugins/pathshapes/enhancedpath/EnhancedPathFormula.h b/plugins/pathshapes/enhancedpath/EnhancedPathFormula.h index 6bddab546ae..56c1b16af7d 100644 --- a/plugins/pathshapes/enhancedpath/EnhancedPathFormula.h +++ b/plugins/pathshapes/enhancedpath/EnhancedPathFormula.h @@ -1,170 +1,173 @@ /* This file is part of the KDE project * Copyright (C) 2007 Jan Hambrecht * * 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 KOENHANCEDPATHFORMULA_H #define KOENHANCEDPATHFORMULA_H #include #include class EnhancedPathShape; class FormulaToken { public: /// token types enum Type { TypeUnknown = 0, ///< unknown type TypeNumber, ///< 14, 3, 1977, 3.141592, 1e10, 5.9e-7 TypeOperator, ///< +, *, /, - TypeReference, ///< function reference, modifier reference or named variable TypeFunction ///< function name }; /// operator types enum Operator { OperatorInvalid, ///< invalid operator OperatorAdd, ///< + addition OperatorSub, ///< - subtraction OperatorMul, ///< * multiplication OperatorDiv, ///< / division OperatorLeftPar, ///< (left parentheses OperatorRightPar, ///<) right parentheses OperatorComma ///< , comma }; /// Constructs token with given type, text and position explicit FormulaToken(Type type = TypeUnknown, const QString & text = QString(), int position = -1); /// copy constructor FormulaToken(const FormulaToken &token); /// assignment operator FormulaToken& operator=(const FormulaToken &token); /// Returns the type of the token Type type() const { return m_type; } /// Returns the text representation of the token QString text() const { return m_text; } /// Returns the position of the token int position() const { return m_position; } /// Returns if the token is a number bool isNumber() const { return m_type == TypeNumber; } /// Returns if the token is a operator, OperatorInvalid if token is no operator bool isOperator() const { return m_type == TypeOperator; } /// Returns if token is a function bool isFunction() const { return m_type == TypeFunction; } /// Returns if token is a reference bool isReference() const { return m_type == TypeReference; } /// Returns the token converted to qreal qreal asNumber() const; /// Returns the token as operator Operator asOperator() const; private: Type m_type; ///< the token type QString m_text; ///< the token text representation int m_position; ///< the tokens position }; typedef QList TokenList; class Opcode; class EnhancedPathFormula { public: /// predefined functions enum Function { FunctionUnknown, // unary functions FunctionAbs, FunctionSqrt, FunctionSin, FunctionCos, FunctionTan, FunctionAtan, // binary functions FunctionAtan2, FunctionMin, FunctionMax, // ternary functions FunctionIf }; /// The possible error code returned by error() enum Error { ErrorNone, ///< no error ErrorValue, ///< error when converting value ErrorParse, ///< parsing error ErrorCompile, ///< compiling error ErrorName ///< invalid function name value }; /// Constructs a new formula from the specified string representation EnhancedPathFormula(const QString &text, EnhancedPathShape * parent); /// Destroys the formula ~EnhancedPathFormula(); /** * Evaluates the formula using the given path as possible input. * * @param path the path to use as input * @return the evaluated result */ qreal evaluate(); /// Returns the last occurred error Error error() const { return m_error; } /// returns string representation of the formula QString toString() const; + + void resetCompiled() { m_compiled = false; } + private: /// Separates the given formula text into tokens. TokenList scan(const QString &formula) const; /// Compiles the formula tokens into byte code bool compile(const TokenList &tokens); /** * Evaluates a predefined function. * * @param function the identifier of the function to evaluate * @param arguments the functions arguments */ qreal evaluateFunction(Function function, const QList &arguments) const; /// Prints token list void debugTokens(const TokenList &tokens); /// Prints byte code void debugOpcodes(); bool m_valid; ///< flag that shows if function is valid, i.e the function was compiled successfully bool m_compiled; ///< flag that shows if function was compiled Error m_error; ///< the last occurred error QString m_text; ///< the formula text representation QList m_constants; ///< constant values QList m_codes; ///< the compiled byte code EnhancedPathShape *m_parent; }; #endif // KOENHANCEDPATHFORMULA_H diff --git a/plugins/pathshapes/enhancedpath/EnhancedPathHandle.cpp b/plugins/pathshapes/enhancedpath/EnhancedPathHandle.cpp index b21bb66905d..fe43519fc18 100644 --- a/plugins/pathshapes/enhancedpath/EnhancedPathHandle.cpp +++ b/plugins/pathshapes/enhancedpath/EnhancedPathHandle.cpp @@ -1,212 +1,222 @@ /* This file is part of the KDE project * Copyright (C) 2007 Jan Hambrecht * * 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 "EnhancedPathHandle.h" #include "EnhancedPathShape.h" #include "EnhancedPathParameter.h" #include #include #include #include #include #include #include EnhancedPathHandle::EnhancedPathHandle(EnhancedPathShape *parent) : m_parent(parent) , m_positionX(0), m_positionY(0) , m_minimumX(0), m_minimumY(0) , m_maximumX(0), m_maximumY(0) , m_polarX(0), m_polarY(0) , m_minRadius(0), m_maxRadius(0) { Q_ASSERT(m_parent); } EnhancedPathHandle::~EnhancedPathHandle() { } bool EnhancedPathHandle::hasPosition() const { return m_positionX && m_positionY; } void EnhancedPathHandle::setPosition(EnhancedPathParameter *positionX, EnhancedPathParameter *positionY) { m_positionX = positionX; m_positionY = positionY; } QPointF EnhancedPathHandle::position() { if (!hasPosition()) return QPointF(); QPointF position(m_positionX->evaluate(), m_positionY->evaluate()); if (isPolar()) { // convert polar coordinates into cartesian coordinates QPointF center(m_polarX->evaluate(), m_polarY->evaluate()); qreal angleInRadian = position.x() * M_PI / 180.0; position = center + position.y() * QPointF(cos(angleInRadian), sin(angleInRadian)); } return position; } void EnhancedPathHandle::changePosition(const QPointF &position) { if (! hasPosition()) return; QPointF constrainedPosition(position); if (isPolar()) { // convert cartesian coordinates into polar coordinates QPointF polarCenter(m_polarX->evaluate(), m_polarY->evaluate()); QPointF diff = constrainedPosition - polarCenter; // compute the polar radius qreal radius = sqrt(diff.x()*diff.x() + diff.y()*diff.y()); // compute the polar angle qreal angle = atan2(diff.y(), diff.x()); if (angle < 0.0) angle += 2 * M_PI; // constrain the radius if (m_minRadius) radius = qMax(m_minRadius->evaluate(), radius); if (m_maxRadius) radius = qMin(m_maxRadius->evaluate(), radius); constrainedPosition.setX(angle * 180.0 / M_PI); constrainedPosition.setY(radius); } else { // constrain x coordinate if (m_minimumX) constrainedPosition.setX(qMax(m_minimumX->evaluate(), constrainedPosition.x())); if (m_maximumX) constrainedPosition.setX(qMin(m_maximumX->evaluate(), constrainedPosition.x())); // constrain y coordinate if (m_minimumY) constrainedPosition.setY(qMax(m_minimumY->evaluate(), constrainedPosition.y())); if (m_maximumY) constrainedPosition.setY(qMin(m_maximumY->evaluate(), constrainedPosition.y())); } m_positionX->modify(constrainedPosition.x()); m_positionY->modify(constrainedPosition.y()); } void EnhancedPathHandle::setRangeX(EnhancedPathParameter *minX, EnhancedPathParameter *maxX) { m_minimumX = minX; m_maximumX = maxX; } void EnhancedPathHandle::setRangeY(EnhancedPathParameter *minY, EnhancedPathParameter *maxY) { m_minimumY = minY; m_maximumY = maxY; } void EnhancedPathHandle::setPolarCenter(EnhancedPathParameter* polarX, EnhancedPathParameter* polarY) { m_polarX = polarX; m_polarY = polarY; } void EnhancedPathHandle::setRadiusRange(EnhancedPathParameter *minRadius, EnhancedPathParameter *maxRadius) { m_minRadius = minRadius; m_maxRadius = maxRadius; } bool EnhancedPathHandle::isPolar() const { return m_polarX && m_polarY; } void EnhancedPathHandle::saveOdf(KoShapeSavingContext & context) const { if (! hasPosition()) return; context.xmlWriter().startElement("draw:handle"); context.xmlWriter().addAttribute("draw:handle-position", m_positionX->toString() + ' ' + m_positionY->toString()); if (isPolar()) { context.xmlWriter().addAttribute("draw:handle-polar", m_polarX->toString() + ' ' + m_polarY->toString()); if (m_minRadius) context.xmlWriter().addAttribute("draw:handle-radius-range-minimum", m_minRadius->toString()); if (m_maxRadius) context.xmlWriter().addAttribute("draw:handle-radius-range-maximum", m_maxRadius->toString()); } else { if (m_minimumX) context.xmlWriter().addAttribute("draw:handle-range-x-minimum", m_minimumX->toString()); if (m_maximumX) context.xmlWriter().addAttribute("draw:handle-range-x-maximum", m_maximumX->toString()); if (m_minimumY) context.xmlWriter().addAttribute("draw:handle-range-y-minimum", m_minimumY->toString()); if (m_maximumY) context.xmlWriter().addAttribute("draw:handle-range-y-maximum", m_maximumY->toString()); } context.xmlWriter().endElement(); // draw:handle } bool EnhancedPathHandle::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context) { if (element.localName() != "handle" || element.namespaceURI() != KoXmlNS::draw) return false; QString position = element.attributeNS(KoXmlNS::draw, "handle-position"); #ifndef NWORKAROUND_ODF_BUGS KoOdfWorkaround::fixEnhancedPathPolarHandlePosition(position, element, context); #endif QStringList tokens = position.simplified().split(' '); if (tokens.count() != 2) return false; setPosition(m_parent->parameter(tokens[0]), m_parent->parameter(tokens[1])); // check if we have a polar handle if (element.hasAttributeNS(KoXmlNS::draw, "handle-polar")) { QString polar = element.attributeNS(KoXmlNS::draw, "handle-polar"); QStringList tokens = polar.simplified().split(' '); if (tokens.count() == 2) { setPolarCenter(m_parent->parameter(tokens[0]), m_parent->parameter(tokens[1])); QString minRadius = element.attributeNS(KoXmlNS::draw, "handle-radius-range-minimum"); QString maxRadius = element.attributeNS(KoXmlNS::draw, "handle-radius-range-maximum"); if (!minRadius.isEmpty() && !maxRadius.isEmpty()) setRadiusRange(m_parent->parameter(minRadius), m_parent->parameter(maxRadius)); } } else { QString minX = element.attributeNS(KoXmlNS::draw, "handle-range-x-minimum"); QString maxX = element.attributeNS(KoXmlNS::draw, "handle-range-x-maximum"); if (! minX.isEmpty() && ! maxX.isEmpty()) setRangeX(m_parent->parameter(minX), m_parent->parameter(maxX)); QString minY = element.attributeNS(KoXmlNS::draw, "handle-range-y-minimum"); QString maxY = element.attributeNS(KoXmlNS::draw, "handle-range-y-maximum"); if (! minY.isEmpty() && ! maxY.isEmpty()) setRangeY(m_parent->parameter(minY), m_parent->parameter(maxY)); } return hasPosition(); } + +QString EnhancedPathHandle::positionX() const +{ + return m_positionX->toString(); +} + +QString EnhancedPathHandle::positionY() const +{ + return m_positionY->toString(); +} diff --git a/plugins/pathshapes/enhancedpath/EnhancedPathHandle.h b/plugins/pathshapes/enhancedpath/EnhancedPathHandle.h index 80302765666..7b3b71a58f2 100644 --- a/plugins/pathshapes/enhancedpath/EnhancedPathHandle.h +++ b/plugins/pathshapes/enhancedpath/EnhancedPathHandle.h @@ -1,138 +1,142 @@ /* This file is part of the KDE project * Copyright (C) 2007 Jan Hambrecht * * 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 KOENHANCEDPATHHANDLE_H #define KOENHANCEDPATHHANDLE_H #include #include class EnhancedPathShape; class EnhancedPathParameter; class KoShapeSavingContext; class KoShapeLoadingContext; /** * An interaction handle used by the EnhancedPathShape for * changing the shape interactively. */ class EnhancedPathHandle { public: /** * Constructs a new empty handle; * * Note that an empty handle is not valid, as long as there are no * positional parameters set with setPosition. */ explicit EnhancedPathHandle(EnhancedPathShape *parent); /// Destroys the handle ~EnhancedPathHandle(); /** * Evaluates the position of the handle. * @return the actual handle position */ QPointF position(); /** * Attempts to changes the position of the handle. * Only the coordinates of the handle which reference a modifier * can be changed. The new position is automatically stored into * the modifier of the given enhanced path. * * @param position the new position the handle to set * @param path the enhanced path the handle is referenced from */ void changePosition(const QPointF &position); /// Returns if the handle has valid positional parameters.S bool hasPosition() const; /** * Sets the positional parameters, making the handle valid. * * It replaces the actual positional parameters. * * @param positionX the x-coordinate of the handle position * @param positionY the y-coordinate of the handle position */ void setPosition(EnhancedPathParameter *positionX, EnhancedPathParameter *positionY); /** * Sets the range of the handles x-coordinate. * * A zero pointer has the effect of no maximum/minimum value. * * @param minX the minimum x-coordinate * @param maxX the maximum x-coordinate */ void setRangeX(EnhancedPathParameter *minX, EnhancedPathParameter *maxX); /** * Sets the range of the handles y-coordinate. * * A zero pointer has the effect of no maximum/minimum value. * * @param minY the minimum y-coordinate * @param maxY the maximum y-coordinate */ void setRangeY(EnhancedPathParameter *minY, EnhancedPathParameter *maxY); /** * Sets the center of a polar handle. * * If both parameters are valid pointers, then the handle behaves like * a polar handle. This means the x-coordinate of the position represents * an angle in degree and the y-coordinate a radius. * * @param polarX the polar center x-coordinate * @param polarY the polar center y-coordinate */ void setPolarCenter(EnhancedPathParameter *polarX, EnhancedPathParameter *polarY); /** * Sets the range of the radius for polar handles. * @param minRadius the minimum polar radius * @param maxRadius the maximum polar radius */ void setRadiusRange(EnhancedPathParameter *minRadius, EnhancedPathParameter *maxRadius); /// save to the given shape saving context void saveOdf(KoShapeSavingContext &context) const; /// load handle from given element bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context); + + QString positionX() const; + QString positionY() const; + private: /// Returns if handle is polar bool isPolar() const; EnhancedPathShape *m_parent; ///< the enhanced path shape owning this handle EnhancedPathParameter *m_positionX; ///< the position x-coordinate EnhancedPathParameter *m_positionY; ///< the position y-coordinate EnhancedPathParameter *m_minimumX; ///< the minimum x-coordinate EnhancedPathParameter *m_minimumY; ///< the minmum y-coordinate EnhancedPathParameter *m_maximumX; ///< the maximum x-coordinate EnhancedPathParameter *m_maximumY; ///< the maximum y-coordinate EnhancedPathParameter *m_polarX; ///< the polar center x-coordinate EnhancedPathParameter *m_polarY; ///< the polar center y-coordinate EnhancedPathParameter *m_minRadius; ///< the minimum polar radius EnhancedPathParameter *m_maxRadius; ///< the maximum polar radius }; #endif // KOENHANCEDPATHHANDLE_H diff --git a/plugins/pathshapes/enhancedpath/EnhancedPathShape.cpp b/plugins/pathshapes/enhancedpath/EnhancedPathShape.cpp index 2c1721742a8..23eca89f4d1 100644 --- a/plugins/pathshapes/enhancedpath/EnhancedPathShape.cpp +++ b/plugins/pathshapes/enhancedpath/EnhancedPathShape.cpp @@ -1,694 +1,701 @@ /* This file is part of the KDE project * Copyright (C) 2007,2010,2011 Jan Hambrecht * Copyright (C) 2009-2010 Thomas Zander * Copyright (C) 2010 Carlos Licea * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Contact: Suresh Chande suresh.chande@nokia.com * Copyright (C) 2009-2010 Thorsten Zachmann * * 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 "EnhancedPathShape.h" #include "EnhancedPathCommand.h" #include "EnhancedPathParameter.h" #include "EnhancedPathHandle.h" #include "EnhancedPathFormula.h" #include #include #include #include #include #include #include EnhancedPathShape::EnhancedPathShape(const QRect &viewBox) : m_viewBox(viewBox) , m_viewBoxOffset(0.0, 0.0) , m_mirrorVertically(false) , m_mirrorHorizontally(false) , m_pathStretchPointX(-1) , m_pathStretchPointY(-1) , m_cacheResults(false) { } EnhancedPathShape::~EnhancedPathShape() { reset(); } void EnhancedPathShape::reset() { qDeleteAll(m_commands); m_commands.clear(); qDeleteAll(m_enhancedHandles); m_enhancedHandles.clear(); setHandles(QVector()); qDeleteAll(m_formulae); m_formulae.clear(); qDeleteAll(m_parameters); m_parameters.clear(); m_modifiers.clear(); m_viewMatrix.reset(); m_viewBoxOffset = QPointF(); clear(); m_textArea.clear(); } void EnhancedPathShape::moveHandleAction(int handleId, const QPointF & point, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); EnhancedPathHandle *handle = m_enhancedHandles[ handleId ]; if (handle) { handle->changePosition(shapeToViewbox(point)); } } void EnhancedPathShape::updatePath(const QSizeF & size) { if (isParametricShape()) { clear(); enableResultCache(true); foreach (EnhancedPathCommand *cmd, m_commands) cmd->execute(); enableResultCache(false); qreal stretchPointsScale = 1; bool isStretched = useStretchPoints(size, stretchPointsScale); m_viewBound = outline().boundingRect(); m_mirrorMatrix.reset(); m_mirrorMatrix.translate(m_viewBound.center().x(), m_viewBound.center().y()); m_mirrorMatrix.scale(m_mirrorHorizontally ? -1 : 1, m_mirrorVertically ? -1 : 1); m_mirrorMatrix.translate(-m_viewBound.center().x(), -m_viewBound.center().y()); QTransform matrix(1.0, 0.0, 0.0, 1.0, m_viewBoxOffset.x(), m_viewBoxOffset.y()); // if stretch points are set than stretch the path manually if (isStretched) { //if the path was stretched manually the stretch matrix is not more valid //and it has to be recalculated so that stretching in x and y direction is the same matrix.scale(stretchPointsScale, stretchPointsScale); matrix = m_mirrorMatrix * matrix; } else { matrix = m_mirrorMatrix * m_viewMatrix * matrix; } foreach (KoSubpath *subpath, m_subpaths) { foreach (KoPathPoint *point, *subpath) { point->map(matrix); } } const int handleCount = m_enhancedHandles.count(); QVector handles; handles.reserve(handleCount); for (int i = 0; i < handleCount; ++i) handles.append(matrix.map(m_enhancedHandles[i]->position())); setHandles(handles); normalize(); } } void EnhancedPathShape::setSize(const QSizeF &newSize) { // handle offset KoParameterShape::setSize(newSize); - // calculate scaling factors from viewbox size to shape size qreal xScale = m_viewBound.width() == 0 ? 1 : newSize.width()/m_viewBound.width(); qreal yScale = m_viewBound.height() == 0 ? 1 : newSize.height()/m_viewBound.height(); // create view matrix, take mirroring into account m_viewMatrix.reset(); m_viewMatrix.scale(xScale, yScale); updatePath(newSize); } QPointF EnhancedPathShape::normalize() { QPointF offset = KoParameterShape::normalize(); m_viewBoxOffset -= offset; return offset; } QPointF EnhancedPathShape::shapeToViewbox(const QPointF &point) const { return (m_mirrorMatrix * m_viewMatrix).inverted().map( point-m_viewBoxOffset ); } void EnhancedPathShape::evaluateHandles() { const int handleCount = m_enhancedHandles.count(); QVector handles; handles.reserve(handleCount); for (int i = 0; i < handleCount; ++i) handles.append(m_enhancedHandles[i]->position()); setHandles(handles); } QRect EnhancedPathShape::viewBox() const { return m_viewBox; } qreal EnhancedPathShape::evaluateReference(const QString &reference) { if (reference.isEmpty()) return 0.0; const char c = reference[0].toLatin1(); qreal res = 0.0; switch(c) { // referenced modifier case '$': { bool success = false; int modifierIndex = reference.mid(1).toInt(&success); res = m_modifiers.value(modifierIndex); break; } // referenced formula case '?': { QString fname = reference.mid(1); if (m_cacheResults && m_resultCache.contains(fname)) { res = m_resultCache.value(fname); } else { FormulaStore::const_iterator formulaIt = m_formulae.constFind(fname); if (formulaIt != m_formulae.constEnd()) { EnhancedPathFormula * formula = formulaIt.value(); if (formula) { res = formula->evaluate(); if (m_cacheResults) m_resultCache.insert(fname, res); } } } break; } // maybe an identifier ? default: EnhancedPathNamedParameter p(reference, this); res = p.evaluate(); break; } return res; } qreal EnhancedPathShape::evaluateConstantOrReference(const QString &val) { bool ok = true; qreal res = val.toDouble(&ok); if (ok) return res; return evaluateReference(val); } void EnhancedPathShape::modifyReference(const QString &reference, qreal value) { if (reference.isEmpty()) return; const char c = reference[0].toLatin1(); if (c == '$') { bool success = false; int modifierIndex = reference.mid(1).toInt(&success); if (modifierIndex >= 0 && modifierIndex < m_modifiers.count()) m_modifiers[modifierIndex] = value; } } EnhancedPathParameter * EnhancedPathShape::parameter(const QString & text) { Q_ASSERT(! text.isEmpty()); ParameterStore::const_iterator parameterIt = m_parameters.constFind(text); if (parameterIt != m_parameters.constEnd()) { return parameterIt.value(); } else { EnhancedPathParameter *parameter = 0; const char c = text[0].toLatin1(); if (c == '$' || c == '?') { parameter = new EnhancedPathReferenceParameter(text, this); } else { bool success = false; qreal constant = text.toDouble(&success); if (success) { parameter = new EnhancedPathConstantParameter(constant, this); } else { Identifier identifier = EnhancedPathNamedParameter::identifierFromString(text); if (identifier != IdentifierUnknown) parameter = new EnhancedPathNamedParameter(identifier, this); } } if (parameter) m_parameters[text] = parameter; return parameter; } } void EnhancedPathShape::addFormula(const QString &name, const QString &formula) { if (name.isEmpty() || formula.isEmpty()) return; m_formulae[name] = new EnhancedPathFormula(formula, this); } void EnhancedPathShape::addHandle(const QMap &handle) { if (handle.isEmpty()) return; if (! handle.contains("draw:handle-position")) return; QVariant position = handle.value("draw:handle-position"); QStringList tokens = position.toString().simplified().split(' '); if (tokens.count() < 2) return; EnhancedPathHandle *newHandle = new EnhancedPathHandle(this); newHandle->setPosition(parameter(tokens[0]), parameter(tokens[1])); // check if we have a polar handle if (handle.contains("draw:handle-polar")) { QVariant polar = handle.value("draw:handle-polar"); QStringList tokens = polar.toString().simplified().split(' '); if (tokens.count() == 2) { newHandle->setPolarCenter(parameter(tokens[0]), parameter(tokens[1])); QVariant minRadius = handle.value("draw:handle-radius-range-minimum"); QVariant maxRadius = handle.value("draw:handle-radius-range-maximum"); if (minRadius.isValid() && maxRadius.isValid()) newHandle->setRadiusRange(parameter(minRadius.toString()), parameter(maxRadius.toString())); } } else { QVariant minX = handle.value("draw:handle-range-x-minimum"); QVariant maxX = handle.value("draw:handle-range-x-maximum"); if (minX.isValid() && maxX.isValid()) newHandle->setRangeX(parameter(minX.toString()), parameter(maxX.toString())); QVariant minY = handle.value("draw:handle-range-y-minimum"); QVariant maxY = handle.value("draw:handle-range-y-maximum"); if (minY.isValid() && maxY.isValid()) newHandle->setRangeY(parameter(minY.toString()), parameter(maxY.toString())); } m_enhancedHandles.append(newHandle); evaluateHandles(); } void EnhancedPathShape::addModifiers(const QString &modifiers) { if (modifiers.isEmpty()) return; QStringList tokens = modifiers.simplified().split(' '); int tokenCount = tokens.count(); for (int i = 0; i < tokenCount; ++i) m_modifiers.append(tokens[i].toDouble()); } void EnhancedPathShape::addCommand(const QString &command) { addCommand(command, true); } void EnhancedPathShape::addCommand(const QString &command, bool triggerUpdate) { QString commandStr = command.simplified(); if (commandStr.isEmpty()) return; // the first character is the command EnhancedPathCommand *cmd = new EnhancedPathCommand(commandStr[0], this); // strip command char commandStr = commandStr.mid(1).simplified(); // now parse the command parameters if (!commandStr.isEmpty()) { QStringList tokens = commandStr.split(' '); for (int i = 0; i < tokens.count(); ++i) { cmd->addParameter(parameter(tokens[i])); } } m_commands.append(cmd); if (triggerUpdate) updatePath(size()); } bool EnhancedPathShape::useStretchPoints(const QSizeF &size, qreal &scale) { bool retval = false; if (m_pathStretchPointX != -1 && m_pathStretchPointY != -1) { qreal scaleX = size.width(); qreal scaleY = size.height(); if (m_viewBox.width() / m_viewBox.height() < scaleX / scaleY) { qreal deltaX = (scaleX * m_viewBox.height()) / scaleY - m_viewBox.width(); foreach (KoSubpath *subpath, m_subpaths) { foreach (KoPathPoint *currPoint, *subpath) { if (currPoint->point().x() >= m_pathStretchPointX && currPoint->controlPoint1().x() >= m_pathStretchPointX && currPoint->controlPoint2().x() >= m_pathStretchPointX) { currPoint->setPoint(QPointF(currPoint->point().x() + deltaX, currPoint->point().y())); currPoint->setControlPoint1(QPointF(currPoint->controlPoint1().x() + deltaX, currPoint->controlPoint1().y())); currPoint->setControlPoint2(QPointF(currPoint->controlPoint2().x() + deltaX, currPoint->controlPoint2().y())); retval = true; } } } scale = scaleY / m_viewBox.height(); } else if (m_viewBox.width() / m_viewBox.height() > scaleX / scaleY) { qreal deltaY = (m_viewBox.width() * scaleY) / scaleX - m_viewBox.height(); foreach (KoSubpath *subpath, m_subpaths) { foreach (KoPathPoint *currPoint, *subpath) { if (currPoint->point().y() >= m_pathStretchPointY && currPoint->controlPoint1().y() >= m_pathStretchPointY && currPoint->controlPoint2().y() >= m_pathStretchPointY) { currPoint->setPoint(QPointF(currPoint->point().x(), currPoint->point().y() + deltaY)); currPoint->setControlPoint1(QPointF(currPoint->controlPoint1().x(), currPoint->controlPoint1().y() + deltaY)); currPoint->setControlPoint2(QPointF(currPoint->controlPoint2().x(), currPoint->controlPoint2().y() + deltaY)); retval = true; } } } scale = scaleX / m_viewBox.width(); } } return retval; } void EnhancedPathShape::saveOdf(KoShapeSavingContext &context) const { if (isParametricShape()) { context.xmlWriter().startElement("draw:custom-shape"); const QSizeF currentSize = outline().boundingRect().size(); // save the right position so that when loading we fit the viewbox // to the right position without getting any wrong scaling // -> calculate the right position from the current 0 position / viewbound ratio // this is e.g. the case when there is a callout that goes into negative viewbound coordinates QPointF topLeft = m_viewBound.topLeft(); QPointF diff; if (qAbs(topLeft.x()) > 1E-5) { diff.setX(topLeft.x()*currentSize.width()/m_viewBound.width()); } if (qAbs(topLeft.y()) > 1E-5) { diff.setY(topLeft.y()*currentSize.height()/m_viewBound.height()); } if (diff.isNull()) { saveOdfAttributes(context, OdfAllAttributes&~OdfSize); } else { // We get here if the path goes outside the viewbox. // Afaics, this is not allowed, see: http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#attribute-svg_viewBox. // However, OO/LO uses this for callouts in order to get a shape size that covers the 'bubble' part only, // excluding the 'arrow' part of the callout. // OO/LO callouts are marked by eg: draw:type="round-rectangular-callout" // so this can be used to distinguish callouts from proper path shapes. // Also there was a bug in stencil database.odg that giave the same problem. // Handling this should not be neccessary anymore as callouts are treated in a separate class, // and the database.odg has been fixe, but we keep it to be able to read old calligra documents. QTransform offset(context.shapeOffset(this)); QTransform newOffset(offset); newOffset.translate(-diff.x(), -diff.y()); context.addShapeOffset(this, newOffset); saveOdfAttributes(context, OdfAllAttributes&~OdfSize); if (offset.isIdentity()) { context.removeShapeOffset(this); } else { context.addShapeOffset(this, offset); } } // save the right size so that when loading we fit the viewbox // to the right size without getting any wrong scaling // -> calculate the right size from the current size/viewbound ratio const QSizeF contentSize = currentSize.boundedTo(m_viewBox.size()); const QSizeF scaledSize = currentSize.scaled(contentSize, Qt::KeepAspectRatio); context.xmlWriter().addAttributePt("svg:width", scaledSize.width()); context.xmlWriter().addAttributePt("svg:height", scaledSize.height()); saveText(context); - context.xmlWriter().startElement("draw:enhanced-geometry"); - context.xmlWriter().addAttribute("svg:viewBox", QString("%1 %2 %3 %4").arg(m_viewBox.x()).arg(m_viewBox.y()).arg(m_viewBox.width()).arg(m_viewBox.height())); - - if (m_pathStretchPointX != -1) { - context.xmlWriter().addAttribute("draw:path-stretchpoint-x", m_pathStretchPointX); - } - if (m_pathStretchPointY != -1) { - context.xmlWriter().addAttribute("draw:path-stretchpoint-y", m_pathStretchPointY); - } - - if (m_mirrorHorizontally) { - context.xmlWriter().addAttribute("draw:mirror-horizontal", "true"); - } - if (m_mirrorVertically) { - context.xmlWriter().addAttribute("draw:mirror-vertical", "true"); - } - - QString modifiers; - foreach (qreal modifier, m_modifiers) - modifiers += QString::number(modifier) + ' '; - context.xmlWriter().addAttribute("draw:modifiers", modifiers.trimmed()); - - if (m_textArea.size() >= 4) { - context.xmlWriter().addAttribute("draw:text-areas", m_textArea.join(" ")); - } - - QString path; - foreach (EnhancedPathCommand * c, m_commands) - path += c->toString() + ' '; - context.xmlWriter().addAttribute("draw:enhanced-path", path.trimmed()); - - FormulaStore::const_iterator i = m_formulae.constBegin(); - for (; i != m_formulae.constEnd(); ++i) { - context.xmlWriter().startElement("draw:equation"); - context.xmlWriter().addAttribute("draw:name", i.key()); - context.xmlWriter().addAttribute("draw:formula", i.value()->toString()); - context.xmlWriter().endElement(); // draw:equation - } - - foreach (EnhancedPathHandle * handle, m_enhancedHandles) - handle->saveOdf(context); - - context.xmlWriter().endElement(); // draw:enhanced-geometry - saveOdfCommonChildElements(context); - context.xmlWriter().endElement(); // draw:custom-shape - + saveEnhancedGeometry(context); } else { KoPathShape::saveOdf(context); } } -bool EnhancedPathShape::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context) +void EnhancedPathShape::saveEnhancedGeometry(KoShapeSavingContext &context) const { - reset(); - - const KoXmlElement enhancedGeometry(KoXml::namedItemNS(element, KoXmlNS::draw, "enhanced-geometry" ) ); - if (!enhancedGeometry.isNull() ) { + context.xmlWriter().startElement("draw:enhanced-geometry"); + context.xmlWriter().addAttribute("svg:viewBox", QString("%1 %2 %3 %4").arg(m_viewBox.x()).arg(m_viewBox.y()).arg(m_viewBox.width()).arg(m_viewBox.height())); - setPathStretchPointX(enhancedGeometry.attributeNS(KoXmlNS::draw, "path-stretchpoint-x","-1").toDouble()); - setPathStretchPointY(enhancedGeometry.attributeNS(KoXmlNS::draw, "path-stretchpoint-y","-1").toDouble()); + if (m_pathStretchPointX != -1) { + context.xmlWriter().addAttribute("draw:path-stretchpoint-x", m_pathStretchPointX); + } + if (m_pathStretchPointY != -1) { + context.xmlWriter().addAttribute("draw:path-stretchpoint-y", m_pathStretchPointY); + } - // load the modifiers - QString modifiers = enhancedGeometry.attributeNS(KoXmlNS::draw, "modifiers", ""); - if (! modifiers.isEmpty()) { - addModifiers(modifiers); - } + if (m_mirrorHorizontally) { + context.xmlWriter().addAttribute("draw:mirror-horizontal", "true"); + } + if (m_mirrorVertically) { + context.xmlWriter().addAttribute("draw:mirror-vertical", "true"); + } - m_textArea = enhancedGeometry.attributeNS(KoXmlNS::draw, "text-areas", "").split(' '); - if (m_textArea.size() >= 4) { - setResizeBehavior(TextFollowsPreferredTextRect); - } + QString modifiers; + foreach (qreal modifier, m_modifiers) + modifiers += QString::number(modifier) + ' '; + context.xmlWriter().addAttribute("draw:modifiers", modifiers.trimmed()); - KoXmlElement grandChild; - forEachElement(grandChild, enhancedGeometry) { - if (grandChild.namespaceURI() != KoXmlNS::draw) - continue; - if (grandChild.localName() == "equation") { - QString name = grandChild.attributeNS(KoXmlNS::draw, "name"); - QString formula = grandChild.attributeNS(KoXmlNS::draw, "formula"); - addFormula(name, formula); - } else if (grandChild.localName() == "handle") { - EnhancedPathHandle * handle = new EnhancedPathHandle(this); - if (handle->loadOdf(grandChild, context)) { - m_enhancedHandles.append(handle); - evaluateHandles(); - } else { - delete handle; - } - } + if (m_textArea.size() >= 4) { + context.xmlWriter().addAttribute("draw:text-areas", m_textArea.join(" ")); + } - } + QString path; + foreach (EnhancedPathCommand * c, m_commands) + path += c->toString() + ' '; + context.xmlWriter().addAttribute("draw:enhanced-path", path.trimmed()); + + FormulaStore::const_iterator i = m_formulae.constBegin(); + for (; i != m_formulae.constEnd(); ++i) { + context.xmlWriter().startElement("draw:equation"); + context.xmlWriter().addAttribute("draw:name", i.key()); + context.xmlWriter().addAttribute("draw:formula", i.value()->toString()); + context.xmlWriter().endElement(); // draw:equation + } - setMirrorHorizontally(enhancedGeometry.attributeNS(KoXmlNS::draw, "mirror-horizontal") == "true"); - setMirrorVertically(enhancedGeometry.attributeNS(KoXmlNS::draw, "mirror-vertical") == "true"); + foreach (EnhancedPathHandle * handle, m_enhancedHandles) + handle->saveOdf(context); - // load the enhanced path data - QString path = enhancedGeometry.attributeNS(KoXmlNS::draw, "enhanced-path", ""); -#ifndef NWORKAROUND_ODF_BUGS - KoOdfWorkaround::fixEnhancedPath(path, enhancedGeometry, context); -#endif - // load the viewbox - m_viewBox = loadOdfViewbox(enhancedGeometry); + context.xmlWriter().endElement(); // draw:enhanced-geometry + saveOdfCommonChildElements(context); + context.xmlWriter().endElement(); // draw:custom-shape +} - if (!path.isEmpty()) { - parsePathData(path); +bool EnhancedPathShape::loadEnhancedGeometry(const KoXmlElement & enhancedGeometry, KoShapeLoadingContext &context) +{ + setPathStretchPointX(enhancedGeometry.attributeNS(KoXmlNS::draw, "path-stretchpoint-x","-1").toDouble()); + setPathStretchPointY(enhancedGeometry.attributeNS(KoXmlNS::draw, "path-stretchpoint-y","-1").toDouble()); + + // load the modifiers + QString modifiers = enhancedGeometry.attributeNS(KoXmlNS::draw, "modifiers", ""); + if (! modifiers.isEmpty()) { + addModifiers(modifiers); + } + + m_textArea = enhancedGeometry.attributeNS(KoXmlNS::draw, "text-areas", "").split(' '); + if (m_textArea.size() >= 4) { + setResizeBehavior(TextFollowsPreferredTextRect); + } + + KoXmlElement grandChild; + forEachElement(grandChild, enhancedGeometry) { + if (grandChild.namespaceURI() != KoXmlNS::draw) + continue; + if (grandChild.localName() == "equation") { + QString name = grandChild.attributeNS(KoXmlNS::draw, "name"); + QString formula = grandChild.attributeNS(KoXmlNS::draw, "formula"); + addFormula(name, formula); + } else if (grandChild.localName() == "handle") { + EnhancedPathHandle * handle = new EnhancedPathHandle(this); + if (handle->loadOdf(grandChild, context)) { + m_enhancedHandles.append(handle); + evaluateHandles(); + } else { + delete handle; + } } + + } + + setMirrorHorizontally(enhancedGeometry.attributeNS(KoXmlNS::draw, "mirror-horizontal") == "true"); + setMirrorVertically(enhancedGeometry.attributeNS(KoXmlNS::draw, "mirror-vertical") == "true"); + + // load the enhanced path data + QString path = enhancedGeometry.attributeNS(KoXmlNS::draw, "enhanced-path", ""); + #ifndef NWORKAROUND_ODF_BUGS + KoOdfWorkaround::fixEnhancedPath(path, enhancedGeometry, context); + #endif + // load the viewbox + m_viewBox = loadOdfViewbox(enhancedGeometry); + + if (!path.isEmpty()) { + parsePathData(path); + } + + if (m_viewBox.isEmpty()) { + // if there is no view box defined make it is big as the path. + m_viewBox = m_viewBound.toAlignedRect(); + } + return true; +} - if (m_viewBox.isEmpty()) { - // if there is no view box defined make it is big as the path. - m_viewBox = m_viewBound.toAlignedRect(); - } +bool EnhancedPathShape::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context) +{ + reset(); + const KoXmlElement enhancedGeometry(KoXml::namedItemNS(element, KoXmlNS::draw, "enhanced-geometry" ) ); + if (!enhancedGeometry.isNull() ) { + loadEnhancedGeometry(enhancedGeometry, context); } QSizeF size; size.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString()))); size.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString()))); // the viewbox is to be fitted into the size of the shape, so before setting // the size we just loaded // we set the viewbox to be the basis to calculate // the viewbox matrix from m_viewBound = m_viewBox; setSize(size); QPointF pos; pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString()))); pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString()))); setPosition(pos - m_viewMatrix.map(QPointF(0, 0)) - m_viewBoxOffset); loadOdfAttributes(element, context, OdfMandatories | OdfTransformation | OdfAdditionalAttributes | OdfCommonChildElements); loadText(element, context); return true; } void EnhancedPathShape::parsePathData(const QString &data) { if (data.isEmpty()) return; int start = -1; bool separator = true; for (int i = 0; i < data.length(); ++i) { QChar ch = data.at(i); ushort uni_ch = ch.unicode(); if (separator && (uni_ch == 'M' || uni_ch == 'L' || uni_ch == 'C' || uni_ch == 'Z' || uni_ch == 'N' || uni_ch == 'F' || uni_ch == 'S' || uni_ch == 'T' || uni_ch == 'U' || uni_ch == 'A' || uni_ch == 'B' || uni_ch == 'W' || uni_ch == 'V' || uni_ch == 'X' || uni_ch == 'Y' || uni_ch == 'Q')) { if (start != -1) { // process last chars addCommand(data.mid(start, i - start), false); } start = i; } separator = ch.isSpace(); } if (start < data.length()) addCommand(data.mid(start)); if (start != -1) updatePath(size()); } void EnhancedPathShape::setMirrorHorizontally(bool mirrorHorizontally) { if( m_mirrorHorizontally != mirrorHorizontally) { m_mirrorHorizontally = mirrorHorizontally; updatePath(size()); } } void EnhancedPathShape::setMirrorVertically(bool mirrorVertically) { if( m_mirrorVertically != mirrorVertically) { m_mirrorVertically = mirrorVertically; updatePath(size()); } } void EnhancedPathShape::shapeChanged(ChangeType type, KoShape *shape) { KoParameterShape::shapeChanged(type, shape); if (!shape || shape == this) { if (type == ParentChanged || type == ParameterChanged) { updateTextArea(); } } } void EnhancedPathShape::updateTextArea() { if (m_textArea.size() >= 4) { QRectF r = m_viewBox; r.setLeft(evaluateConstantOrReference(m_textArea[0])); r.setTop(evaluateConstantOrReference(m_textArea[1])); r.setRight(evaluateConstantOrReference(m_textArea[2])); r.setBottom(evaluateConstantOrReference(m_textArea[3])); r = m_viewMatrix.mapRect(r).translated(m_viewBoxOffset); setPreferredTextRect(r); } } void EnhancedPathShape::enableResultCache(bool enable) { m_resultCache.clear(); m_cacheResults = enable; } void EnhancedPathShape::setPathStretchPointX(qreal pathStretchPointX) { if (m_pathStretchPointX != pathStretchPointX) { m_pathStretchPointX = pathStretchPointX; } } void EnhancedPathShape::setPathStretchPointY(qreal pathStretchPointY) { if (m_pathStretchPointY != pathStretchPointY) { m_pathStretchPointY = pathStretchPointY; } } diff --git a/plugins/pathshapes/enhancedpath/EnhancedPathShape.h b/plugins/pathshapes/enhancedpath/EnhancedPathShape.h index fe100985fbe..91726f381d8 100644 --- a/plugins/pathshapes/enhancedpath/EnhancedPathShape.h +++ b/plugins/pathshapes/enhancedpath/EnhancedPathShape.h @@ -1,180 +1,184 @@ /* This file is part of the KDE project * Copyright (C) 2007,2010,2011 Jan Hambrecht * Copyright (C) 2010 Carlos Licea * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Contact: Suresh Chande suresh.chande@nokia.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 KOENHANCEDPATHSHAPE_H #define KOENHANCEDPATHSHAPE_H #include #include #include #include #include #define EnhancedPathShapeId "EnhancedPathShape" class EnhancedPathCommand; class EnhancedPathHandle; class EnhancedPathFormula; class EnhancedPathParameter; class KoShapeSavingContext; class KoShapeLoadingContext; /** * An enhanced shape is a custom shape which can be defined * by enhanced geometry data. * The data consists of a list of commands like moveto, * lineto, curveto, etc. which are used to create the outline * of the shape. The coordinates or parameters of the commands * can be constant values, named variables (identifiers), * modifiers, functions or formulae. */ class EnhancedPathShape : public KoParameterShape { public: explicit EnhancedPathShape(const QRect &viewBox); virtual ~EnhancedPathShape(); /** * Evaluates the given reference to a identifier, modifier or formula. * @param reference the reference to evaluate * @return the result of the evaluation */ qreal evaluateReference(const QString &reference); /** * Evaluates the given constant or reference to a identifier, modifier * or formula. * @param val the value to evaluate * @return the result of the evaluation */ qreal evaluateConstantOrReference(const QString &val); /** * Attempts to modify a given reference. * * Only modifiers can me modified, others silently ignore the attempt. * * @param reference the reference to modify * @param value the new value */ void modifyReference(const QString &reference, qreal value); // from KoShape virtual void setSize(const QSizeF &newSize); // from KoParameterShape virtual QPointF normalize(); /// Add formula with given name and textual representation void addFormula(const QString &name, const QString &formula); /// Add a single handle with format: x y minX maxX minY maxY void addHandle(const QMap &handle); /// Add modifiers with format: modifier0 modifier1 modifier2 ... void addModifiers(const QString &modifiers); /// Add command for instance "M 0 0" void addCommand(const QString &command); /// Returns the viewbox of the enhanced path shape QRect viewBox() const; /// Converts from shape coordinates to viewbox coordinates QPointF shapeToViewbox(const QPointF &point) const; /// Sets if the shape is to be mirrored horizontally before applying any other transformations //NOTE: in the standard nothing is mentioned about the priorities of the transformations" //it's assumed like this because of the behavior shown in OOo void setMirrorHorizontally(bool mirrorHorizontally); /// Sets if the shape is to be mirrored vertically before applying any other transformations //NOTE: in the standard nothing is mentioned about the priorities of the transformations" //it's assumed like this because of the behavior shown in OOo void setMirrorVertically(bool mirrorVertically); // Sets member variable representing draw:path-stretchpoint-x attribute void setPathStretchPointX(qreal pathStretchPointX); // Sets member variable representing draw:path-stretchpoint-y attribute void setPathStretchPointY(qreal pathStretchPointY); /// Returns parameter from given textual representation EnhancedPathParameter *parameter(const QString &text); + void saveEnhancedGeometry(KoShapeSavingContext &context) const; + + bool loadEnhancedGeometry(const KoXmlElement & element, KoShapeLoadingContext &context); + protected: // from KoShape virtual void saveOdf(KoShapeSavingContext &context) const; // from KoShape virtual bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context); //from KoShape virtual void shapeChanged(ChangeType type, KoShape *shape = 0); // from KoParameterShape virtual void moveHandleAction(int handleId, const QPointF &point, Qt::KeyboardModifiers modifiers = Qt::NoModifier); // from KoParameterShape virtual void updatePath(const QSizeF &size); void evaluateHandles(); void reset(); /// parses the enhanced path data void parsePathData(const QString &data); /// Adds a new command void addCommand(const QString &command, bool triggerUpdate); /// Updates the size and position of an optionally existing text-on-shape text area void updateTextArea(); /// Enables caching results void enableResultCache(bool enable); // This function checks if draw:path-stretchpoint-x or draw:path-stretchpoint-y attributes are set. // If the attributes are set the path shape coordinates (m_subpaths) are changed so that the form // of the shape is preserved after stretching. It is needed for example in round-rectangles, to // have the corners round after stretching. Without it the corners would be elliptical. // Returns true if any points were actually changed, otherwise false. bool useStretchPoints(const QSizeF &size, qreal &scale); typedef QMap FormulaStore; typedef QList ModifierStore; typedef QMap ParameterStore; QRect m_viewBox; ///< the viewbox rectangle QRectF m_viewBound; ///< the bounding box of the path in viewbox coordinates QTransform m_viewMatrix; ///< matrix to convert from viewbox coordinates to shape coordinates QTransform m_mirrorMatrix; ///< matrix to used for mirroring QPointF m_viewBoxOffset; QStringList m_textArea; QList m_commands; ///< the commands creating the outline QList m_enhancedHandles; ///< the handles for modifying the shape FormulaStore m_formulae; ///< the formulae ModifierStore m_modifiers; ///< the modifier values ParameterStore m_parameters; ///< the shared parameters bool m_mirrorVertically; /// m_resultCache; ///< cache for intermediate results used when evaluating path bool m_cacheResults; ///< indicates if result cache is enabled }; #endif // KOENHANCEDPATHSHAPE_H