// SPDX-License-Identifier: GPL-2.0-or-later #ifndef SEEN_SP_ITEM_H #define SEEN_SP_ITEM_H /** * @file * Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx, SPItemClass, SPEvent. */ /* * Authors: * Lauris Kaplinski * bulia byak * Johan Engelen * Abhishek Sharma * * Copyright (C) 1999-2006 authors * Copyright (C) 2001-2002 Ximian, Inc. * Copyright (C) 2004 Monash University * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include <2geom/forward.h> #include <2geom/affine.h> #include <2geom/rect.h> #include #include "sp-object.h" #include "xml/repr.h" class SPGroup; class SPClipPath; class SPClipPathReference; class SPMask; class SPMaskReference; class SPAvoidRef; class SPPattern; struct SPPrintContext; typedef unsigned int guint32; namespace Inkscape { class Drawing; class DrawingItem; class URIReference; class SnapCandidatePoint; class SnapPreferences; namespace UI { namespace View { class SVGViewWidget; } } } // TODO make a completely new function that transforms either the fill or // stroke of any SPItem without adding an extra parameter to adjust_pattern. enum PaintServerTransform { TRANSFORM_BOTH, TRANSFORM_FILL, TRANSFORM_STROKE }; /** * Event structure. * * @todo This is just placeholder. Plan: * We do extensible event structure, that hold applicable (ui, non-ui) * data pointers. So it is up to given object/arena implementation * to process correct ones in meaningful way. * Also, this probably goes to SPObject base class. * * GUI Code should not be here! */ class SPEvent { public: enum Type { INVALID, NONE, ACTIVATE, MOUSEOVER, MOUSEOUT }; Type type; Inkscape::UI::View::SVGViewWidget* view; }; class SPItemView { public: SPItemView *next; unsigned int flags; unsigned int key; Inkscape::DrawingItem *arenaitem; }; /* flags */ #define SP_ITEM_BBOX_VISUAL 1 #define SP_ITEM_SHOW_DISPLAY (1 << 0) /** * Flag for referenced views (i.e. markers, clippaths, masks and patterns); * currently unused, does the same as DISPLAY */ #define SP_ITEM_REFERENCE_FLAGS (1 << 1) /** * Contains transformations to document/viewport and the viewport size. */ class SPItemCtx : public SPCtx { public: /** Item to document transformation */ Geom::Affine i2doc; /** Viewport size */ Geom::Rect viewport; /** Item to viewport transformation */ Geom::Affine i2vp; }; /** * Base class for visual SVG elements. * SPItem is an abstract base class for all graphic (visible) SVG nodes. It * is a subclass of SPObject, with great deal of specific functionality. */ class SPItem : public SPObject { public: enum BBoxType { // legacy behavior: includes crude stroke, markers; excludes long miters, blur margin; is known to be wrong for caps APPROXIMATE_BBOX, // includes only the bare path bbox, no stroke, no nothing GEOMETRIC_BBOX, // includes everything: correctly done stroke (with proper miters and caps), markers, filter margins (e.g. blur) VISUAL_BBOX }; enum PaintServerType { PATTERN, HATCH, GRADIENT }; SPItem(); ~SPItem() override; unsigned int sensitive : 1; unsigned int stop_paint: 1; mutable unsigned bbox_valid : 1; double transform_center_x; double transform_center_y; bool freeze_stroke_width; Geom::Affine transform; mutable Geom::OptRect doc_bbox; Geom::Rect viewport; // Cache viewport information SPClipPath *getClipObject() const; SPMask *getMaskObject() const; SPClipPathReference &getClipRef(); SPMaskReference &getMaskRef(); SPAvoidRef &getAvoidRef(); private: SPClipPathReference *clip_ref; SPMaskReference *mask_ref; // Used for object-avoiding connectors SPAvoidRef *avoidRef; public: SPItemView *display; sigc::signal _transformed_signal; bool isLocked() const; void setLocked(bool lock); bool isHidden() const; void setHidden(bool hidden); // Objects dialogue bool isSensitive() const { return sensitive; }; void setHighlight(guint32 color); bool isHighlightSet() const; virtual guint32 highlight_color() const; //==================== bool isEvaluated() const; void setEvaluated(bool visible); void resetEvaluated(); bool isHidden(unsigned display_key) const; /** * Returns something suitable for the `Hide' checkbox in the Object Properties dialog box. * Corresponds to setExplicitlyHidden. */ bool isExplicitlyHidden() const; /** * Sets the display CSS property to `hidden' if \a val is true, * otherwise makes it unset. */ void setExplicitlyHidden(bool val); /** * Sets the transform_center_x and transform_center_y properties to retain the rotation center */ void setCenter(Geom::Point const &object_centre); void unsetCenter(); bool isCenterSet() const; Geom::Point getCenter() const; void scaleCenter(Geom::Scale const &sc); bool isVisibleAndUnlocked() const; bool isVisibleAndUnlocked(unsigned display_key) const; Geom::Affine getRelativeTransform(SPObject const *obj) const; bool raiseOne(); bool lowerOne(); void raiseToTop(); void lowerToBottom(); SPGroup *getParentGroup() const; /** * Move this SPItem into or after another SPItem in the doc. * * @param target the SPItem to move into or after. * @param intoafter move to after the target (false), move inside (sublayer) of the target (true). */ void moveTo(SPItem *target, bool intoafter); sigc::connection connectTransformed(sigc::slot slot) { return _transformed_signal.connect(slot); } /** * Get item's geometric bounding box in this item's coordinate system. * * The geometric bounding box includes only the path, disregarding all style attributes. */ Geom::OptRect geometricBounds(Geom::Affine const &transform = Geom::identity()) const; /** * Get item's visual bounding box in this item's coordinate system. * * The visual bounding box includes the stroke and the filter region. * @param wfilter use filter expand in bbox calculation * @param wclip use clip data in bbox calculation * @param wmask use mask data in bbox calculation */ Geom::OptRect visualBounds(Geom::Affine const &transform = Geom::identity(), bool wfilter = true, bool wclip = true, bool wmask = true) const; Geom::OptRect bounds(BBoxType type, Geom::Affine const &transform = Geom::identity()) const; /** * Get item's geometric bbox in document coordinate system. * Document coordinates are the default coordinates of the root element: * the origin is at the top left, X grows to the right and Y grows downwards. */ Geom::OptRect documentGeometricBounds() const; /** * Get item's visual bbox in document coordinate system. */ Geom::OptRect documentVisualBounds() const; Geom::OptRect documentBounds(BBoxType type) const; Geom::OptRect documentPreferredBounds() const; /** * Get item's geometric bbox in desktop coordinate system. * Desktop coordinates should be user defined. Currently they are hardcoded: * origin is at bottom left, X grows to the right and Y grows upwards. */ Geom::OptRect desktopGeometricBounds() const; /** * Get item's visual bbox in desktop coordinate system. */ Geom::OptRect desktopVisualBounds() const; Geom::OptRect desktopPreferredBounds() const; Geom::OptRect desktopBounds(BBoxType type) const; unsigned int pos_in_parent() const; /** * Returns a string suitable for status bar, formatted in pango markup language. * * Must be freed by caller. */ char *detailedDescription() const; /** * Returns true if the item is filtered, false otherwise. * Used with groups/lists to determine how many, or if any, are filtered. */ bool isFiltered() const; SPObject* isInMask() const; SPObject* isInClipPath() const; void invoke_print(SPPrintContext *ctx); /** * Allocates unique integer keys. * * @param numkeys Number of keys required. * @return First allocated key; hence if the returned key is n * you can use n, n + 1, ..., n + (numkeys - 1) */ static unsigned int display_key_new(unsigned int numkeys); Inkscape::DrawingItem *invoke_show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags); // Removed item from display tree. void invoke_hide(unsigned int key); void getSnappoints(std::vector &p, Inkscape::SnapPreferences const *snapprefs=nullptr) const; void adjust_pattern(/* Geom::Affine const &premul, */ Geom::Affine const &postmul, bool set = false, PaintServerTransform = TRANSFORM_BOTH); void adjust_hatch(/* Geom::Affine const &premul, */ Geom::Affine const &postmul, bool set = false, PaintServerTransform = TRANSFORM_BOTH); void adjust_gradient(/* Geom::Affine const &premul, */ Geom::Affine const &postmul, bool set = false); void adjust_stroke(double ex); /** * Recursively scale stroke width in \a item and its children by \a expansion. */ void adjust_stroke_width_recursive(double ex); void freeze_stroke_width_recursive(bool freeze); /** * Recursively compensate pattern or gradient transform. */ void adjust_paint_recursive(Geom::Affine advertized_transform, Geom::Affine t_ancestors, PaintServerType type = GRADIENT); /** * Set a new transform on an object. * * Compensate for stroke scaling and gradient/pattern fill transform, if * necessary. Call the object's set_transform method if transforms are * stored optimized. Send _transformed_signal. Invoke _write method so that * the repr is updated with the new transform. */ void doWriteTransform(Geom::Affine const &transform, Geom::Affine const *adv = nullptr, bool compensate = true); /** * Sets item private transform (not propagated to repr), without compensating stroke widths, * gradients, patterns as sp_item_write_transform does. */ void set_item_transform(Geom::Affine const &transform_matrix); int emitEvent (SPEvent &event); /** * Return the arenaitem corresponding to the given item in the display * with the given key */ Inkscape::DrawingItem *get_arenaitem(unsigned int key); /** * Returns the accumulated transformation of the item and all its ancestors, including root's viewport. * @pre (item != NULL) and SP_IS_ITEM(item). */ Geom::Affine i2doc_affine() const; /** * Returns the transformation from item to desktop coords */ Geom::Affine i2dt_affine() const; void set_i2d_affine(Geom::Affine const &transform); /** * should rather be named "sp_item_d2i_affine" to match "sp_item_i2d_affine" (or vice versa). */ Geom::Affine dt2i_affine() const; guint32 _highlightColor; private: enum EvaluatedStatus { StatusUnknown, StatusCalculated, StatusSet }; mutable bool _is_evaluated; mutable EvaluatedStatus _evaluated_status; static SPItemView *sp_item_view_new_prepend(SPItemView *list, SPItem *item, unsigned flags, unsigned key, Inkscape::DrawingItem *arenaitem); static void clip_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); static void mask_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); static void fill_ps_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); static void stroke_ps_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); public: void rotate_rel(Geom::Rotate const &rotation); void scale_rel(Geom::Scale const &scale); void skew_rel(double skewX, double skewY); void move_rel( Geom::Translate const &tr); void build(SPDocument *document, Inkscape::XML::Node *repr) override; void release() override; void set(SPAttr key, char const* value) override; void update(SPCtx *ctx, unsigned int flags) override; void modified(unsigned int flags) override; Inkscape::XML::Node* write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override; virtual Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type) const; virtual void print(SPPrintContext *ctx); virtual const char* typeName() const; virtual const char* displayName() const; virtual char* description() const; virtual Inkscape::DrawingItem* show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags); virtual void hide(unsigned int key); virtual void snappoints(std::vector &p, Inkscape::SnapPreferences const *snapprefs) const; virtual Geom::Affine set_transform(Geom::Affine const &transform); virtual void convert_to_guides() const; virtual int event(SPEvent *event); }; // Utility /** * @pre \a ancestor really is an ancestor (\>=) of \a object, or NULL. * ("Ancestor (\>=)" here includes as far as \a object itself.) */ Geom::Affine i2anc_affine(SPObject const *item, SPObject const *ancestor); Geom::Affine i2i_affine(SPObject const *src, SPObject const *dest); Geom::Affine sp_item_transform_repr (SPItem *item); /* fixme: - these are evil, but OK */ int sp_item_repr_compare_position(SPItem const *first, SPItem const *second); inline bool sp_item_repr_compare_position_bool(SPObject const *first, SPObject const *second) { return sp_repr_compare_position(first->getRepr(), second->getRepr())<0; } SPItem *sp_item_first_item_child (SPObject *obj); SPItem const *sp_item_first_item_child (SPObject const *obj); MAKE_SP_OBJECT_DOWNCAST_FUNCTIONS(SP_ITEM, SPItem) MAKE_SP_OBJECT_TYPECHECK_FUNCTIONS(SP_IS_ITEM, SPItem) #endif // SEEN_SP_ITEM_H /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :