~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/doc/architecture.txt

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Sodipodi internal architecture
 
2
 
 
3
1. Overview
 
4
 
 
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.
 
11
 
 
12
1.1. Agnostic XML backbone
 
13
 
 
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.
 
21
 
 
22
1.2. Typed SVG document
 
23
 
 
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.
 
33
 
 
34
1.3. NRArena view
 
35
 
 
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.
 
47
 
 
48
1.4. Controllers
 
49
 
 
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.
 
56
 
 
57
 
 
58
 
 
59
2. Detailed view
 
60
 
 
61
2.1. SPRepr
 
62
 
 
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.
 
69
 
 
70
SPRepr may have:
 
71
- attributes (keyword/value) pairs
 
72
- content (text)
 
73
- child nodes
 
74
 
 
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.
 
80
 
 
81
The basic API acting on SPRepr level is really spartan.
 
82
 
 
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)
 
88
 
 
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)
 
94
 
 
95
In addition there are some accessor methods and lot of convenience ones.
 
96
 
 
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.
 
100
 
 
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)
 
103
 
 
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);
 
116
}
 
117
 
 
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.
 
126
 
 
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.
 
131
 
 
132
 
 
133
 
 
134
2.2. SPObject
 
135
 
 
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.
 
147
 
 
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.
 
151
 
 
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.
 
155
 
 
156
SPObject class hierarchy
 
157
 
 
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>
 
191
 
 
192
SPObject internals
 
193
 
 
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
};
 
206
 
 
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.
 
213
 
 
214
Virtual methods
 
215
 
 
216
/******** Disclaimer *******/
 
217
This will change a lot in the future
 
218
 
 
219
void ::build (SPObject *object, SPDocument *document, SPRepr *repr)
 
220
 
 
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).
 
227
 
 
228
void ::release (SPObject *object)
 
229
 
 
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.
 
236
 
 
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)
 
240
 
 
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.
 
246
 
 
247
void ::read_attr (SPObject *object, const unsigned char *key)
 
248
 
 
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.
 
252
 
 
253
void ::read_content (SPObject *object)
 
254
 
 
255
Signals object that the XML node content has changed. Only meaningful for
 
256
SPString implementing XML TEXT node.
 
257
 
 
258
void ::modified (SPObject *object, unsigned int flags)
 
259
 
 
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.
 
265
 
 
266
SPRepr ::write (SPObject *object, SPRepr *repr, unsigned int flags)
 
267
 
 
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.
 
272
 
 
273
 
 
274
 
 
275
2.3. SPItem
 
276
 
 
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.
 
279
 
 
280
SPItem internals
 
281
 
 
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
};
 
290
 
 
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.
 
295
 
 
296
Virtual methods
 
297
 
 
298
/******** Disclaimer *******/
 
299
This will change a lot in the future
 
300
Only the most important are listed
 
301
 
 
302
void ::bbox (SPItem *item, ArtDRect *bbox, const double *transform)
 
303
 
 
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.
 
309
 
 
310
void ::print (SPItem *item, SPPrintContext *ctx)
 
311
 
 
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.
 
315
 
 
316
char ::description (SPItem *item)
 
317
 
 
318
Gives a short description of the item suitable for use in a statusbar,
 
319
etc. 
 
320
 
 
321
NRArenaItem ::show (SPItem *item, NRArena *arena)
 
322
 
 
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.
 
326
 
 
327
void (* hide) (SPitem *item, NRArena *arena)
 
328
 
 
329
The opposite of ::show.
 
330
 
 
331
void ::write_transform (SPItem *item, SPRepr *repr, double *transform)
 
332
 
 
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.
 
337
 
 
338
void ::menu (SPItem *item, SPDesktop *desktop, GtkMenu *menu)
 
339
 
 
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.
 
343
 
 
344
 
 
345
 
 
346
2.4 SPDocument
 
347
 
 
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.
 
351
 
 
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.
 
357
 
 
358
SPDocument performs the basic operations needed for asynchronous
 
359
update notification (SPObject ::modified virtual method), and implements
 
360
the 'modified' signal, as well.
 
361
 
 
362
Many document level operations, like load, save, print, export and so on,
 
363
use SPDocument as their basic datatype.
 
364
 
 
365
2.4.1. Undo and Redo implementation
 
366
 
 
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:
 
375
 
 
376
void sp_document_done (SPDocument *document)
 
377
void sp_document_maybe_done (SPDocument *document, const unsigned char *key)
 
378
 
 
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.
 
386
 
 
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.
 
395
 
 
396
 
 
397
2.5. SPView and SPviewWidget
 
398
 
 
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.
 
404
 
 
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.
 
407
 
 
408
2.6. SPDesktop
 
409
 
 
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.
 
413
 
 
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.
 
421
 
 
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.
 
427
 
 
428
Associated with each SPDesktop are the two most important editing
 
429
related objects - SPSelection and SPEventContext.
 
430
 
 
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.
 
434
 
 
435
2.7. SPSelection
 
436
 
 
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.
 
441
 
 
442
SPSelection also implements its own asynchronous notification signals,
 
443
that UI elements can listen to.
 
444
 
 
445
2.8. SPEventContext
 
446
 
 
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.
 
453
 
 
454
 
 
455
 
 
456
3. Some thoughts
 
457
 
 
458
3.1. Why do we need a two-level model tree?
 
459
 
 
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.
 
466
 
 
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.
 
477
 
 
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.
 
482
 
 
483
The XML tree also makes implementing two SVG features - cloning and
 
484
animations - much easier by providing an invariant backbone.
 
485
 
 
486
 
 
487
 
 
488
22. Novemebr 2002
 
489
Lauris Kaplinski
 
490
<lauris@kaplinski.com>