Beruflich Dokumente
Kultur Dokumente
1. Overview
The most actively used part of the sodipodi document model is the SVG
object tree. This is constructed on top of the XML tree, and reacts to
all mutation events in the agnostic tree, thus always keeping its
internal state synchronized with the backbone. The opposite is not
true - the XML backbone is not aware of the SVG object tree, and thus
does not react to its modifications. If writeback to the backbone is
needed, it must be requested explicitly by the controller. The SVG
tree is constructed of SPObject subclasses - in general there is one
subclass for each SVG element type, plus abstract base classes.
1.4. Controllers
2. Detailed view
2.1. SPRepr
In addition there are some accessor methods and lot of convenience ones.
Each SPRepr can have one or many event vectors associated with it.
Event vector is a block of callback pointers for different kind of
mutation events.
struct _SPReprEventVector {
void (* destroy) (SPRepr *repr, gpointer data);
gboolean (* add_child) (SPRepr *repr, SPRepr *child, SPRepr *ref, gpointer
data);
void (* child_added) (SPRepr *repr, SPRepr *child, SPRepr *ref, gpointer
data);
gboolean (* remove_child) (SPRepr *repr, SPRepr *child, SPRepr *ref,
gpointer data);
void (* child_removed) (SPRepr *repr, SPRepr *child, SPRepr *ref, gpointer
data);
gboolean (* change_attr) (SPRepr *repr, const guchar *key, const guchar
*oldval, const guchar *newval, gpointer data);
void (* attr_changed) (SPRepr *repr, const guchar *key, const guchar
*oldval, const guchar *newval, gpointer data);
gboolean (* change_content) (SPRepr *repr, const guchar *oldcontent, const
guchar *newcontent, gpointer data);
void (* content_changed) (SPRepr *repr, const guchar *oldcontent, const
guchar *newcontent, gpointer data);
gboolean (* change_order) (SPRepr *repr, SPRepr *child, SPRepr *oldref,
SPRepr *newref, gpointer data);
void (* order_changed) (SPRepr *repr, SPRepr *child, SPRepr *oldref, SPRepr
*newref, gpointer data);
}
All events, except destroys (which are unconditional), have pre- and
post- event callbacks. The pre-event callback's return value is used to
signal whether the given modification is allowed. If it is FALSE the
operation will be cancelled and the invoking method will also return
FALSE. Using callbacks in block is much more convenient than adding
them one-by-one, as the listening code usually wants to install several
handlers at once, and the same set of handlers for many different
nodes. NULL pointers are allowed in event vector.
2.2. SPObject
SPObjects are implemented using the Gtk object system (GObjects in gtk
2 version), which provides an extremely powerful and flexible OO
framework in pure C.
SPObject ABSTRACT
SPObjectGroup ABSTRACT
SPNamedView <sodipodi:namedview>
SPClipPath <clipPath>
SPGuide <sodipodi:guide>
SPPaintServer ABSTRACT
SPGradient ABSTRACT
SPLinearGradient <linearGradient>
SPRadialGradient <radialGradient>
SPPattern <pattern>
SPDefs <defs>
SPItem ABSTRACT
SPGroup <g>
SPRoot <svg>
SPAnchor <a>
SPImage <image>
SPPath ABSTARCT
SPShape <path>
SPLine <line>
SPPolyLine <polyline>
SPPolygon <polygon>
SPStar <sodipodi:star>
SPRect <rect>
SPSpiral <sodipodi:spiral>
SPGenericEllipse ABSTRACT
SPCircle <circle>
SPEllipse <ellipse>
SPArc <sodipodi:arc>
SPChars ABSTRACT
SPString TEXT NODE
SPDefs <defs>
SPText <text>
SPTSpan <tspan>
SPObject internals
struct _SPObject {
GtkObject object;
unsigned int hrefcount;
SPDocument *document;
SPObject *parent;
SPObject *next;
SPRepr *repr;
unsigned char *id;
SPStyle *style;
const unsigned char *title;
const unsigned char *description;
};
Virtual methods
These are invoked whenever the given mutation event happens in the XML
tree. ::remove_child is invoked BEFORE removal from the XML tree
happens, so grouping objects can safely release the child data. The
other two will be invoked AFTER the actual XML tree mutation. Only
grouping objects have to implement these.
Signals object that the XML attribute is changed. The frontend checks
for 'id' attribute; implementations have to read the actual attribute
value and update the internal state.
Signals object that the XML node content has changed. Only meaningful for
SPString implementing XML TEXT node.
2.3. SPItem
SPItem is an abstract base class for all graphic (visible) SVG nodes. It
is a subclass of SPObject, with great deal of specific functionality.
SPItem internals
struct _SPItem {
SPObject object;
unsigned int sensitive : 1;
unsigned int stop_paint: 1;
double affine[6];
SPItemView *display;
SPClipPath *clip;
};
Virtual methods
Calculates item's logical bounding box. The logical bbox does not
take into account the stroke width, nor certain other visual
properties. Transformation is a 3x2 matrix describing coordinate
transform from the item's local coordinate system to the coordinate
system of the bounding box.
Appends item specific lines into the menu. This is used to generate
the context menu, and will probably move into a separate module in
the future.
2.4 SPDocument
Many document level operations, like load, save, print, export and so on,
use SPDocument as their basic datatype.
Using the split document model gives sodipodi a very simple and clean
undo implementation. Whenever mutation occurs in the XML tree,
SPObject invokes one of the five corresponding handlers of its
container document. This writes down a generic description of the
given action, and appends it to the recent action list, kept by the
document. There will be as many action records as there are mutation
events, which are all kept and processed together in the undo
stack. Two methods exist to indicate that the given action is completed:
Both move the recent action list into the undo stack and clear the
list afterwards. While the first method does an unconditional push,
the second one first checks the key of the most recent stack entry. If
the keys are identical, the current action list is appended to the
existing stack entry, instead of pushing it onto its own. This
behaviour can be used to collect multi-step actions (like winding the
Gtk spinbutton) from the UI into a single undoable step.
2.6. SPDesktop
Associated with each SPDesktop are the two most important editing
related objects - SPSelection and SPEventContext.
2.7. SPSelection
2.8. SPEventContext
3. Some thoughts
But there are also areas where typed object structure would make
things more complex. For example, to implement the copy buffer we had
to save the full state of copied objects. While this could be done
with the separate virtual method of SPObject, we can use a much easier
way and create the duplicate corresponding SPRepr. As our document
model already has to implement generation of full object
representation for SPRepr tree of nodes, generation of new objects
during paste happens automatically when the given SPRepr is inserted
into XML tree. The agnostic xml tree is also used for undo stack, as
described earlier.
The main benefit comes from the extreme simplicity of the XML tree
manipulation API. All operations can be done, using only around 10
methods, which makes code much more robust, and is perfect for
implementing compatibility sensitive things, like a plugin API.
The XML tree also makes implementing two SVG features - cloning and
animations - much easier by providing an invariant backbone.