Code

Other patch by Johan to comment out a warning. Thanks Johan!
[inkscape.git] / doc / architecture.txt
1 Sodipodi internal architecture
3 1. Overview
5 The Sodipodi display and editing engine is built using the
6 "Model-View-Controller" (MVC) paradigm.  Unlike "classic" MVC
7 programs, we have further split model into two distinct layers,
8 'backbone' and 'document'. This has proven to be extremely powerful
9 technique, giving us clear and fast implementation, functional
10 granularity and easy expandibility.
12 1.1. Agnostic XML backbone
14 The basis of the sodipodi document is its plain XML representation in
15 memory. This is a tree-shaped structure, in which each node is
16 represented by a lightweight typeless object (SPRepr). These objects
17 implement a minimal interface of both control (methods) and mutation
18 events (callbacks).  We use the term 'agnostic' for describing that
19 part of model, to underline the typeless nature of SPRepr. More or
20 less, this is just an XML file representation in memory.
22 1.2. Typed SVG document
24 The most actively used part of the sodipodi document model is the SVG
25 object tree. This is constructed on top of the XML tree, and reacts to
26 all mutation events in the agnostic tree, thus always keeping its
27 internal state synchronized with the backbone. The opposite is not
28 true - the XML backbone is not aware of the SVG object tree, and thus
29 does not react to its modifications. If writeback to the backbone is
30 needed, it must be requested explicitly by the controller.  The SVG
31 tree is constructed of SPObject subclasses - in general there is one
32 subclass for each SVG element type, plus abstract base classes.
34 1.3. NRArena view
36 NRarena is an abstract display engine that allows construction of
37 'display caches' from NRArenaItem subclasses. These are lightweight,
38 having only some basic object types, and used for most of the display
39 needs of Sodipodi.  Both the editing window, and the bitmap export
40 code create special NRArena instances, and ask the SVG document to
41 show itself to the given NRArena.  There is a ::show virtual method,
42 implemented by all visible object classes, that adds an NRArenaItem
43 node to the display tree. The completed display cache is used for fast
44 screen updates and stripe based bitmap exports.  During the NRArena
45 lifetime SVG objects keep all of the display cache elements constantly
46 updated, thus ensuring the display is always up to date.
48 1.4. Controllers
50 Like the model suggests, controllers can be implemented acting on
51 different layers.  Which one is best depends on the type of action
52 that the given controller performs. Usually very generic and
53 single-shot operating controllers act on the SPRepr layer, while those
54 providing visual feedback or tied to a certain object type act on the
55 SPObject layer.
59 2. Detailed view
61 2.1. SPRepr
63 The most basic SVG (XML) document backbone is implemented as an
64 in-memory tree of SPRepr objects, each object corresponding to a
65 single node in the XML file.  Currently there are only two types of
66 SPReprs - normal element nodes and text nodes.  More types may be
67 added in the future, but the structure will probably always remain
68 much simpler (and faster) than DOM.
70 SPRepr may have:
71 - attributes (keyword/value) pairs
72 - content (text)
73 - child nodes
75 Attribute values are textual, and no checks are performed in that
76 layer to ensure document validity. Also, CSS style strings are
77 unparsed in that layer.  The SPRepr tree is built during document
78 loading or creation. As it is textual and always synchronized with the
79 display, unfiltered saving involves just dumping it into a file.
81 The basic API acting on SPRepr level is really spartan.
83 SPRepr *sp_repr_new (const unsigned char *name)
84 SPRepr *sp_repr_new_text (const unsigned char *content)
85 SPRepr *sp_repr_ref (SPRepr *repr)
86 SPRepr *sp_repr_unref (SPRepr *repr)
87 SPRepr *sp_repr_duplicate (SPRepr *repr)
89 int sp_repr_set_content (SPRepr *repr, const unsigned char *content)
90 int sp_repr_set_attr (SPRepr *repr, const unsigned char *key, const unsigned char *value)
91 int sp_repr_add_child (SPRepr *repr, SPRepr *child, SPRepr *ref)
92 int sp_repr_remove_child (SPRepr *repr, SPRepr *child)
93 int sp_repr_change_order (SPRepr *repr, SPRepr *child, SPRepr *ref)
95 In addition there are some accessor methods and lot of convenience ones.
97 Each SPRepr can have one or many event vectors associated with it.
98 Event vector is a block of callback pointers for different kind of
99 mutation events.
101 void sp_repr_add_listener (SPRepr *repr, const SPReprEventVector *vector, void *data)
102 void sp_repr_remove_listener_by_data (SPRepr *repr, void *data)
104 struct _SPReprEventVector {
105         void (* destroy) (SPRepr *repr, gpointer data);
106         gboolean (* add_child) (SPRepr *repr, SPRepr *child, SPRepr *ref, gpointer data);
107         void (* child_added) (SPRepr *repr, SPRepr *child, SPRepr *ref, gpointer data);
108         gboolean (* remove_child) (SPRepr *repr, SPRepr *child, SPRepr *ref, gpointer data);
109         void (* child_removed) (SPRepr *repr, SPRepr *child, SPRepr *ref, gpointer data);
110         gboolean (* change_attr) (SPRepr *repr, const guchar *key, const guchar *oldval, const guchar *newval, gpointer data);
111         void (* attr_changed) (SPRepr *repr, const guchar *key, const guchar *oldval, const guchar *newval, gpointer data);
112         gboolean (* change_content) (SPRepr *repr, const guchar *oldcontent, const guchar *newcontent, gpointer data);
113         void (* content_changed) (SPRepr *repr, const guchar *oldcontent, const guchar *newcontent, gpointer data);
114         gboolean (* change_order) (SPRepr *repr, SPRepr *child, SPRepr *oldref, SPRepr *newref, gpointer data);
115         void (* order_changed) (SPRepr *repr, SPRepr *child, SPRepr *oldref, SPRepr *newref, gpointer data);
118 All events, except destroys (which are unconditional), have pre- and
119 post- event callbacks.  The pre-event callback's return value is used to
120 signal whether the given modification is allowed. If it is FALSE the
121 operation will be cancelled and the invoking method will also return
122 FALSE.  Using callbacks in block is much more convenient than adding
123 them one-by-one, as the listening code usually wants to install several
124 handlers at once, and the same set of handlers for many different
125 nodes. NULL pointers are allowed in event vector.
127 Although the most important functionality of the SPRepr tree is to
128 serve as a document backbone, it has other functions besides
129 that. SPReprs are also used to store preferences, the copy buffer and
130 the undo stack.
134 2.2. SPObject
136 SPObject is an abstract base class of all of the document nodes at the
137 SVG document level. Each SPObject subclass implements a certain SVG
138 element node type, or is an abstract base class for different node
139 types.  The SPObject layer is bound to the SPRepr layer, closely
140 following the SPRepr mutations via callbacks.  During creation,
141 SPObject parses and interprets all textual attributes and CSS style
142 strings of the SPRepr, and later updates the internal state whenever
143 it receives a signal about a change. The opposite is not true - there
144 are methods manipulating SPObjects directly and such changes do not
145 propagate to the SPRepr layer. This is important for implementation of
146 the undo stack, animations and other features.
148 SPObjects are bound to the higher-level container SPDocument, which
149 provides document level functionality such as the undo stack,
150 dictionary and so on.
152 SPObjects are implemented using the Gtk object system (GObjects in gtk
153 2 version), which provides an extremely powerful and flexible OO
154 framework in pure C.
156 SPObject class hierarchy
158 SPObject ABSTRACT
159   SPObjectGroup ABSTRACT
160     SPNamedView <sodipodi:namedview>
161     SPClipPath <clipPath>
162   SPGuide <sodipodi:guide>
163   SPPaintServer ABSTRACT
164     SPGradient ABSTRACT
165       SPLinearGradient <linearGradient>
166       SPRadialGradient <radialGradient>
167     SPPattern <pattern>
168   SPDefs <defs>
169   SPItem ABSTRACT
170     SPGroup <g>
171       SPRoot <svg>
172       SPAnchor <a>
173   SPImage <image>
174   SPPath ABSTARCT
175     SPShape <path>
176       SPLine <line>
177       SPPolyLine <polyline>
178       SPPolygon <polygon>
179         SPStar <sodipodi:star>
180       SPRect <rect>
181       SPSpiral <sodipodi:spiral>
182       SPGenericEllipse ABSTRACT
183         SPCircle <circle>
184         SPEllipse <ellipse>
185         SPArc <sodipodi:arc>
186   SPChars ABSTRACT
187     SPString TEXT NODE
188   SPDefs <defs>
189   SPText <text>
190   SPTSpan <tspan>
192 SPObject internals
194 struct _SPObject {
195         GtkObject object;
196         unsigned int hrefcount;
197         SPDocument *document;
198         SPObject *parent;
199         SPObject *next;
200         SPRepr *repr;
201         unsigned char *id;
202         SPStyle *style;
203         const unsigned char *title;
204         const unsigned char *description;
205 };
207 The basic refcounting is handled by the parent class
208 (GtkObject). Hrefcount is used for weak references, for example, to
209 determine whether any graphical element references a certain gradient
210 node.  The parent and next fields are used to establish the tree
211 structure.  Id is copy of the SPRepr 'id' attribute for normal nodes,
212 and is used as a unique index of all objects in the given document.
214 Virtual methods
216 /******** Disclaimer *******/
217 This will change a lot in the future
219 void ::build (SPObject *object, SPDocument *document, SPRepr *repr)
221 This has to be invoked immediately after creation of an SPObject. The
222 frontend method ensures that the new object is properly attached to
223 the document and repr; implementation then will parse all of the attributes,
224 generate the children objects and so on.  Invoking ::build on the SPRoot
225 object results in creation of the whole document tree (this is, what
226 SPDocument does after the creation of the XML tree).
228 void ::release (SPObject *object)
230 This is the opposite of ::build. It has to be invoked as soon as the
231 object is removed from the tree, even if it is still alive according
232 to reference count. The frontend unregisters the object from the
233 document and releases the SPRepr bindings; implementations should free
234 state data and release all child objects.  Invoking ::release on
235 SPRoot destroys the whole document tree.
237 void ::child_added (SPObject *object, SPRepr *child, SPRepr *ref)
238 void ::remove_child (SPObject *object, SPRepr *child)
239 void ::order_changed (SPObject *object, SPRepr *repr, SPRepr *oldref, SPRepr *newref)
241 These are invoked whenever the given mutation event happens in the XML
242 tree.  ::remove_child is invoked BEFORE removal from the XML tree
243 happens, so grouping objects can safely release the child data.  The
244 other two will be invoked AFTER the actual XML tree mutation.  Only
245 grouping objects have to implement these.
247 void ::read_attr (SPObject *object, const unsigned char *key)
249 Signals object that the XML attribute is changed. The frontend checks
250 for 'id' attribute; implementations have to read the actual attribute
251 value and update the internal state.
253 void ::read_content (SPObject *object)
255 Signals object that the XML node content has changed. Only meaningful for
256 SPString implementing XML TEXT node.
258 void ::modified (SPObject *object, unsigned int flags)
260 Virtual method AND signal implementing asynchronous state change
261 notification. Whenever the object internal state changes, it requests
262 that ::modified will be scheduled from the idle loop.  Flags are given
263 as hints as to what exactly changes. Read the relevant section for
264 more information.
266 SPRepr ::write (SPObject *object, SPRepr *repr, unsigned int flags)
268 Requests SPObject internal state to be written back to the SPRepr. If
269 the SP_OBJECT_WRITE_BUILD flag is set, SPRepr is created, if necessary.
270 This is used at various places, most notably to generate a plain SVG
271 document, and to complete certain GUI operations.
275 2.3. SPItem
277 SPItem is an abstract base class for all graphic (visible) SVG nodes. It
278 is a subclass of SPObject, with great deal of specific functionality.
280 SPItem internals
282 struct _SPItem {
283         SPObject object;
284         unsigned int sensitive : 1;
285         unsigned int stop_paint: 1;
286         double affine[6];
287         SPItemView *display;
288         SPClipPath *clip;
289 };
291 Affine is a 3x2 matrix describing transformation from the item to the
292 parent local coordinate systems. Each display in linked list has a link
293 to a single NRArenaItem that implements actual renderable image of
294 the item.
296 Virtual methods
298 /******** Disclaimer *******/
299 This will change a lot in the future
300 Only the most important are listed
302 void ::bbox (SPItem *item, ArtDRect *bbox, const double *transform)
304 Calculates item's logical bounding box.  The logical bbox does not
305 take into account the stroke width, nor certain other visual
306 properties. Transformation is a 3x2 matrix describing coordinate
307 transform from the item's local coordinate system to the coordinate
308 system of the bounding box.
310 void ::print (SPItem *item, SPPrintContext *ctx)
312 Prints the item's visual representation, using the internal printing
313 frontend.  In the future this may be turned into a more generic
314 exporting method.
316 char ::description (SPItem *item)
318 Gives a short description of the item suitable for use in a statusbar,
319 etc. 
321 NRArenaItem ::show (SPItem *item, NRArena *arena)
323 Creates an NRArena display cache representation of the item. The
324 frontend places the generated item into a hierarchy; implementations
325 have to build a correct NRArenaItem and keep it up to date.
327 void (* hide) (SPitem *item, NRArena *arena)
329 The opposite of ::show.
331 void ::write_transform (SPItem *item, SPRepr *repr, double *transform)
333 Tries to remove the extra transformation by modifying other aspects of
334 the item representation.  For example, by changing the rectangle width
335 and height, the scaling component of the transformation can be
336 dropped.  This is used to make the SVG file more human-readable.
338 void ::menu (SPItem *item, SPDesktop *desktop, GtkMenu *menu)
340 Appends item specific lines into the menu. This is used to generate
341 the context menu, and will probably move into a separate module in 
342 the future.
346 2.4 SPDocument
348 SPDocument serves as the container of both model trees (agnostic XML
349 and typed object tree), and implements all of the document-level
350 functionality used by the program.
352 SPDocument implements undo and redo stacks and an id-based object
353 dictionary.  Thanks to unique id attributes, the latter can be used to
354 map from the XML tree back to the object tree.  Documents are
355 themselves registered by the main program metaobject 'Sodipodi', which
356 does elementary bookeeping.
358 SPDocument performs the basic operations needed for asynchronous
359 update notification (SPObject ::modified virtual method), and implements
360 the 'modified' signal, as well.
362 Many document level operations, like load, save, print, export and so on,
363 use SPDocument as their basic datatype.
365 2.4.1. Undo and Redo implementation
367 Using the split document model gives sodipodi a very simple and clean
368 undo implementation. Whenever mutation occurs in the XML tree,
369 SPObject invokes one of the five corresponding handlers of its
370 container document. This writes down a generic description of the
371 given action, and appends it to the recent action list, kept by the
372 document. There will be as many action records as there are mutation
373 events, which are all kept and processed together in the undo
374 stack. Two methods exist to indicate that the given action is completed:
376 void sp_document_done (SPDocument *document)
377 void sp_document_maybe_done (SPDocument *document, const unsigned char *key)
379 Both move the recent action list into the undo stack and clear the
380 list afterwards.  While the first method does an unconditional push,
381 the second one first checks the key of the most recent stack entry. If
382 the keys are identical, the current action list is appended to the
383 existing stack entry, instead of pushing it onto its own.  This
384 behaviour can be used to collect multi-step actions (like winding the
385 Gtk spinbutton) from the UI into a single undoable step.
387 For controls implemented by Sodipodi itself, implementing undo as a
388 single step is usually done in a more efficent way. Most controls have
389 the abstract model of grab, drag, release, and change user
390 action. During the grab phase, all modifications are done to the
391 SPObject directly - i.e. they do not change XML tree, and thus do not
392 generate undo actions either.  Only at the release phase (normally
393 associated with releasing the mousebutton), changes are written back
394 to the XML tree, thus generating only a single set of undo actions.
397 2.5. SPView and SPviewWidget
399 SPView is an abstract base class of all UI document views.  This
400 includes both the editing window and the SVG preview, but does not
401 include the non-UI RGBA buffer-based NRArenas nor the XML editor or
402 similar views.  The SPView base class has very little functionality of
403 its own.
405 SPViewWidget is a GUI widget that contain a single SPView. It is also
406 an abstract base class with little funtionality of its own.
408 2.6. SPDesktop
410 SPDesktop is a subclass of SPView, implementing an editable document
411 canvas.  It is extensively used by many UI controls that need certain
412 visual representations of their own.
414 SPDesktop provides a certain set of SPCanvasItems, serving as GUI
415 layers of different control objects. The one containing the whole
416 document is the drawing layer. In addition to it, there are grid,
417 guide, sketch and control layers. The sketch layer is used for
418 temporary drawing objects, before the real objects in document are
419 created. The control layer contains editing knots, rubberband and
420 similar non-document UI objects.
422 Each SPDesktop is associated with a SPNamedView node of the document
423 tree.  Currently, all desktops are created from a single main named
424 view, but in the future there may be support for different ones.
425 SPNamedView serves as an in-document container for desktop-related
426 data, like grid and guideline placement, snapping options and so on.
428 Associated with each SPDesktop are the two most important editing
429 related objects - SPSelection and SPEventContext.
431 Sodipodi keeps track of the active desktop and invokes notification
432 signals whenever it changes. UI elements can use these to update their
433 display to the selection of the currently active editing window.
435 2.7. SPSelection
437 This is a per-desktop object that keeps the list of selected objects
438 at the given desktop. Both SPItem and SPRepr lists can be retrieved
439 from the selection. Many actions operate on the selection, so it is
440 widely used throughout the Sodipodi code.
442 SPSelection also implements its own asynchronous notification signals,
443 that UI elements can listen to.
445 2.8. SPEventContext
447 SPEventContext is an abstract base class of all tools. As the name
448 indicates, event context implementations process UI events (mouse
449 movements and keypresses) and take actions (like creating or modifying
450 objects).  There is one event context implementation for each tool,
451 plus few abstract base classes. Writing a new tool involves
452 subclassing SPEventContext.
456 3. Some thoughts
458 3.1. Why do we need a two-level model tree?
460 The need for a typed object tree is obvious if we want to utilize OO
461 programming - which we certainly want to do. Although implemented in pure C,
462 Sodipodi uses the gtk (glib in future versions) type and object system,
463 which gives us an extremely powerful set of OO functionality. As SVG is
464 designed with inheritance in mind, using object subclassing to represent
465 it is perfectly the right thing to do.
467 But there are also areas where typed object structure would make
468 things more complex. For example, to implement the copy buffer we had
469 to save the full state of copied objects. While this could be done
470 with the separate virtual method of SPObject, we can use a much easier
471 way and create the duplicate corresponding SPRepr.  As our document
472 model already has to implement generation of full object
473 representation for SPRepr tree of nodes, generation of new objects
474 during paste happens automatically when the given SPRepr is inserted
475 into XML tree. The agnostic xml tree is also used for undo stack, as
476 described earlier.
478 The main benefit comes from the extreme simplicity of the XML tree
479 manipulation API.  All operations can be done, using only around 10
480 methods, which makes code much more robust, and is perfect for
481 implementing compatibility sensitive things, like a plugin API.
483 The XML tree also makes implementing two SVG features - cloning and
484 animations - much easier by providing an invariant backbone.
488 22. Novemebr 2002
489 Lauris Kaplinski
490 <lauris@kaplinski.com>