~vaifrax/inkscape/bugfix170049

« back to all changes in this revision

Viewing changes to src/sp-object.h

  • Committer: mental
  • Date: 2006-01-16 02:36:01 UTC
  • Revision ID: mental@users.sourceforge.net-20060116023601-wkr0h7edl5veyudq
moving trunk for module inkscape

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ifndef __SP_OBJECT_H__
 
2
#define __SP_OBJECT_H__
 
3
 
 
4
/** \file
 
5
 * Abstract base class for all nodes
 
6
 *
 
7
 * Authors:
 
8
 *   Lauris Kaplinski <lauris@kaplinski.com>
 
9
 *
 
10
 * Copyright (C) 1999-2002 authors
 
11
 * Copyright (C) 2001-2002 Ximian, Inc.
 
12
 *
 
13
 * Released under GNU GPL, read the file 'COPYING' for more information
 
14
 */
 
15
 
 
16
/* SPObject flags */
 
17
 
 
18
/* Async modification flags */
 
19
#define SP_OBJECT_MODIFIED_FLAG (1 << 0)
 
20
#define SP_OBJECT_CHILD_MODIFIED_FLAG (1 << 1)
 
21
#define SP_OBJECT_PARENT_MODIFIED_FLAG (1 << 2)
 
22
#define SP_OBJECT_STYLE_MODIFIED_FLAG (1 << 3)
 
23
#define SP_OBJECT_VIEWPORT_MODIFIED_FLAG (1 << 4)
 
24
#define SP_OBJECT_USER_MODIFIED_FLAG_A (1 << 5)
 
25
#define SP_OBJECT_USER_MODIFIED_FLAG_B (1 << 6)
 
26
#define SP_OBJECT_USER_MODIFIED_FLAG_C (1 << 7)
 
27
 
 
28
/* Conveneience */
 
29
#define SP_OBJECT_FLAGS_ALL 0xff
 
30
 
 
31
/* Flags that mark object as modified */
 
32
/* Object, Child, Style, Viewport, User */
 
33
#define SP_OBJECT_MODIFIED_STATE (SP_OBJECT_FLAGS_ALL & ~(SP_OBJECT_PARENT_MODIFIED_FLAG))
 
34
 
 
35
/* Flags that will propagate downstreams */
 
36
/* Parent, Style, Viewport, User */
 
37
#define SP_OBJECT_MODIFIED_CASCADE (SP_OBJECT_FLAGS_ALL & ~(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))
 
38
 
 
39
/* Generic */
 
40
#define SP_OBJECT_IS_CLONED(o) (((SPObject *) (o))->cloned)
 
41
 
 
42
/* Write flags */
 
43
#define SP_OBJECT_WRITE_BUILD (1 << 0)
 
44
#define SP_OBJECT_WRITE_EXT (1 << 1)
 
45
#define SP_OBJECT_WRITE_ALL (1 << 2)
 
46
 
 
47
/* Convenience stuff */
 
48
#define SP_OBJECT_ID(o) (((SPObject *) (o))->id)
 
49
#define SP_OBJECT_REPR(o) (((SPObject *) (o))->repr)
 
50
#define SP_OBJECT_DOCUMENT(o) (((SPObject *) (o))->document)
 
51
#define SP_OBJECT_PARENT(o) (((SPObject *) (o))->parent)
 
52
#define SP_OBJECT_NEXT(o) (((SPObject *) (o))->next)
 
53
#define SP_OBJECT_PREV(o) (sp_object_prev((SPObject *) (o)))
 
54
#define SP_OBJECT_HREFCOUNT(o) (((SPObject *) (o))->hrefcount)
 
55
#define SP_OBJECT_STYLE(o) (((SPObject *) (o))->style)
 
56
#define SP_OBJECT_TITLE(o) sp_object_title_get((SPObject *) (o))
 
57
#define SP_OBJECT_DESCRIPTION(o) sp_object_description_get((SPObject *) (o))
 
58
 
 
59
 
 
60
#include <glib-object.h>
 
61
#include <sigc++/connection.h>
 
62
#include <sigc++/functors/slot.h>
 
63
#include <sigc++/signal.h>
 
64
 
 
65
#include "forward.h"
 
66
#include "version.h"
 
67
#include "util/forward-pointer-iterator.h"
 
68
 
 
69
namespace Inkscape {
 
70
namespace XML {
 
71
class Node;
 
72
}
 
73
}
 
74
 
 
75
 
 
76
typedef enum {
 
77
    SP_NO_EXCEPTION,
 
78
    SP_INDEX_SIZE_ERR,
 
79
    SP_DOMSTRING_SIZE_ERR,
 
80
    SP_HIERARCHY_REQUEST_ERR,
 
81
    SP_WRONG_DOCUMENT_ERR,
 
82
    SP_INVALID_CHARACTER_ERR,
 
83
    SP_NO_DATA_ALLOWED_ERR,
 
84
    SP_NO_MODIFICATION_ALLOWED_ERR,
 
85
    SP_NOT_FOUND_ERR,
 
86
    SP_NOT_SUPPORTED_ERR,
 
87
    SP_INUSE_ATTRIBUTE_ERR,
 
88
    SP_INVALID_STATE_ERR,
 
89
    SP_SYNTAX_ERR,
 
90
    SP_INVALID_MODIFICATION_ERR,
 
91
    SP_NAMESPACE_ERR,
 
92
    SP_INVALID_ACCESS_ERR
 
93
} SPExceptionType;
 
94
 
 
95
class SPException;
 
96
 
 
97
/// An attempt to implement exceptions, unused?
 
98
struct SPException {
 
99
    SPExceptionType code;
 
100
};
 
101
 
 
102
#define SP_EXCEPTION_INIT(ex) {(ex)->code = SP_NO_EXCEPTION;}
 
103
#define SP_EXCEPTION_IS_OK(ex) (!(ex) || ((ex)->code == SP_NO_EXCEPTION))
 
104
 
 
105
class SPCtx;
 
106
 
 
107
/// Unused
 
108
struct SPCtx {
 
109
    unsigned int flags;
 
110
};
 
111
 
 
112
enum {
 
113
    SP_XML_SPACE_DEFAULT,
 
114
    SP_XML_SPACE_PRESERVE
 
115
};
 
116
 
 
117
class SPIXmlSpace;
 
118
 
 
119
/// Internal class consisting of two bits.
 
120
struct SPIXmlSpace {
 
121
    guint set : 1;
 
122
    guint value : 1;
 
123
};
 
124
 
 
125
class SPObject;
 
126
 
 
127
/*
 
128
 * Refcounting
 
129
 *
 
130
 * Owner is here for debug reasons, you can set it to NULL safely
 
131
 * Ref should return object, NULL is error, unref return always NULL
 
132
 */
 
133
 
 
134
SPObject *sp_object_ref(SPObject *object, SPObject *owner=NULL);
 
135
SPObject *sp_object_unref(SPObject *object, SPObject *owner=NULL);
 
136
 
 
137
SPObject *sp_object_href(SPObject *object, gpointer owner);
 
138
SPObject *sp_object_hunref(SPObject *object, gpointer owner);
 
139
 
 
140
/// A refcounting tree node object.
 
141
struct SPObject : public GObject {
 
142
    enum CollectionPolicy {
 
143
        COLLECT_WITH_PARENT,
 
144
        ALWAYS_COLLECT
 
145
    };
 
146
 
 
147
    unsigned int cloned : 1;
 
148
    unsigned int uflags : 8;
 
149
    unsigned int mflags : 8;
 
150
    SPIXmlSpace xml_space;
 
151
    unsigned int hrefcount; /* number of xlink:href references */
 
152
    unsigned int _total_hrefcount; /* our hrefcount + total descendants */
 
153
    SPDocument *document; /* Document we are part of */
 
154
    SPObject *parent; /* Our parent (only one allowed) */
 
155
    SPObject *children; /* Our children */
 
156
    SPObject *_last_child; /* Remembered last child */
 
157
    SPObject *next; /* Next object in linked list */
 
158
    Inkscape::XML::Node *repr; /* Our xml representation */
 
159
    gchar *id; /* Our very own unique id */
 
160
 
 
161
    /**
 
162
     * Represents the style properties, whether from presentation attributes, the <tt>style</tt>
 
163
     * attribute, or inherited.
 
164
     *
 
165
     * sp_object_private_set doesn't handle SP_ATTR_STYLE or any presentation attributes at the
 
166
     * time of writing, so this is probably NULL for all SPObject's that aren't an SPItem.
 
167
     *
 
168
     * However, this gives rise to the bugs mentioned in sp_object_get_style_property.
 
169
     * Note that some non-SPItem SPObject's, such as SPStop, do need styling information,
 
170
     * and need to inherit properties even through other non-SPItem parents like \<defs\>.
 
171
     */
 
172
    SPStyle *style;
 
173
 
 
174
    /// Switch containing next() method.
 
175
    struct ParentIteratorStrategy {
 
176
        static SPObject const *next(SPObject const *object) {
 
177
            return object->parent;
 
178
        }
 
179
    };
 
180
    /// Switch containing next() method.
 
181
    struct SiblingIteratorStrategy {
 
182
        static SPObject const *next(SPObject const *object) {
 
183
            return object->next;
 
184
        }
 
185
    };
 
186
 
 
187
    typedef Inkscape::Util::ForwardPointerIterator<SPObject, ParentIteratorStrategy> ParentIterator;
 
188
    typedef Inkscape::Util::ForwardPointerIterator<SPObject const, ParentIteratorStrategy> ConstParentIterator;
 
189
    typedef Inkscape::Util::ForwardPointerIterator<SPObject, SiblingIteratorStrategy> SiblingIterator;
 
190
    typedef Inkscape::Util::ForwardPointerIterator<SPObject const, SiblingIteratorStrategy> ConstSiblingIterator;
 
191
 
 
192
    bool isSiblingOf(SPObject const *object) const {
 
193
        g_return_val_if_fail(object != NULL, false);
 
194
        return this->parent && this->parent == object->parent;
 
195
    }
 
196
    bool isAncestorOf(SPObject const *object) const;
 
197
 
 
198
    SPObject const *nearestCommonAncestor(SPObject const *object) const;
 
199
    /* A non-const version can be similarly constructed if you want one.
 
200
     * (Don't just cast away the constness, which would be ill-formed.) */
 
201
 
 
202
    bool hasChildren() const { return ( children != NULL ); }
 
203
 
 
204
    SPObject *firstChild() { return children; }
 
205
    SPObject const *firstChild() const { return children; }
 
206
    SPObject *lastChild() { return _last_child; }
 
207
    SPObject const *lastChild() const { return _last_child; }
 
208
 
 
209
    SPObject *appendChildRepr(Inkscape::XML::Node *repr);
 
210
 
 
211
    /** @brief Gets the author-visible label for this object. */ 
 
212
    gchar const *label() const;
 
213
    /** @brief Returns a default label for this object. */ 
 
214
    gchar const *defaultLabel() const;
 
215
    /** @brief Sets the author-visible label for this object.
 
216
     *
 
217
     * Sets the author-visible label for the object.
 
218
     *
 
219
     * @param label the new label
 
220
     */
 
221
    void setLabel(gchar const *label);
 
222
 
 
223
    /** Retrieves the title of this object */
 
224
    gchar const *title() const { return NULL; /* TODO */ }
 
225
    /** Sets the title of this object */
 
226
    void setTitle(gchar const *title) { /* TODO */ }
 
227
 
 
228
    /** Retrieves the description of this object */
 
229
    gchar const *desc() const { return NULL; /* TODO */ }
 
230
    /** Sets the description of this object */
 
231
    void setDesc(gchar const *desc) { /* TODO */ }
 
232
 
 
233
    /** @brief Set the policy under which this object will be
 
234
     *         orphan-collected.
 
235
     *
 
236
     * Orphan-collection is the process of deleting all objects which no longer have
 
237
     * hyper-references pointing to them.  The policy determines when this happens.  Many objects
 
238
     * should not be deleted simply because they are no longer referred to; other objects (like
 
239
     * "intermediate" gradients) are more or less throw-away and should always be collected when no
 
240
     * longer in use.
 
241
     *
 
242
     * Along these lines, there are currently two orphan-collection policies:
 
243
     *
 
244
     *  COLLECT_WITH_PARENT - don't worry about the object's hrefcount;
 
245
     *                        if its parent is collected, this object
 
246
     *                        will be too
 
247
     *
 
248
     *  COLLECT_ALWAYS - always collect the object as soon as its
 
249
     *                   hrefcount reaches zero
 
250
     *
 
251
     * @returns the current collection policy in effect for this object
 
252
     */
 
253
    CollectionPolicy collectionPolicy() const { return _collection_policy; }
 
254
 
 
255
    /** @brief Sets the orphan-collection policy in effect for this object.
 
256
     *
 
257
     * @see SPObject::collectionPolicy
 
258
     *
 
259
     * @param policy the new policy to adopt
 
260
     */
 
261
    void setCollectionPolicy(CollectionPolicy policy) {
 
262
        _collection_policy = policy;
 
263
    }
 
264
 
 
265
    /** @brief Requests a later automatic call to collectOrphan().
 
266
     *
 
267
     * This method requests that collectOrphan() be called during the document update cycle,
 
268
     * deleting the object if it is no longer used.
 
269
     *
 
270
     * If the current collection policy is COLLECT_WITH_PARENT, this function has no effect.
 
271
     *
 
272
     * @see SPObject::collectOrphan
 
273
     */
 
274
    void requestOrphanCollection();
 
275
 
 
276
    /** @brief Unconditionally delete the object if it is not referenced.
 
277
     *
 
278
     * Unconditionally delete the object if there are no outstanding hyper-references to it.
 
279
     * Observers are not notified of the object's deletion (at the SPObject level; XML tree
 
280
     * notifications still fire).
 
281
     *
 
282
     * @see SPObject::deleteObject
 
283
     */
 
284
    void collectOrphan() {
 
285
        if ( _total_hrefcount == 0 ) {
 
286
            deleteObject(false);
 
287
        }
 
288
    }
 
289
 
 
290
    /** @brief Deletes an object.
 
291
     *
 
292
     * Detaches the object's repr, and optionally sends notification that the object has been
 
293
     * deleted.
 
294
     *
 
295
     * @param propagate notify observers that the object has been deleted?
 
296
     *
 
297
     * @param propagate_descendants notify observers of children that they have been deleted?
 
298
     */
 
299
    void deleteObject(bool propagate, bool propagate_descendants);
 
300
 
 
301
    /** @brief Deletes on object.
 
302
     *
 
303
     * @param propagate Notify observers of this object and its children that they have been
 
304
     *                  deleted?
 
305
     */
 
306
    void deleteObject(bool propagate=true) {
 
307
        deleteObject(propagate, propagate);
 
308
    }
 
309
 
 
310
    /** @brief Connects a slot to be called when an object is deleted.
 
311
     *
 
312
     * This connects a slot to an object's internal delete signal, which is invoked when the object
 
313
     * is deleted
 
314
     *
 
315
     * The signal is mainly useful for e.g. knowing when to break hrefs or dissociate clones.
 
316
     *
 
317
     * @param slot the slot to connect
 
318
     *
 
319
     * @see SPObject::deleteObject
 
320
     */
 
321
    sigc::connection connectDelete(sigc::slot<void, SPObject *> slot) {
 
322
        return _delete_signal.connect(slot);
 
323
    }
 
324
 
 
325
    /** @brief Returns the object which supercedes this one (if any).
 
326
     *
 
327
     * This is mainly useful for ensuring we can correctly perform a series of moves or deletes,
 
328
     * even if the objects in question have been replaced in the middle of the sequence.
 
329
     */
 
330
    SPObject *successor() { return _successor; }
 
331
 
 
332
    /** @brief Indicates that another object supercedes this one. */
 
333
    void setSuccessor(SPObject *successor) {
 
334
        g_assert(successor != NULL);
 
335
        g_assert(_successor == NULL);
 
336
        g_assert(successor->_successor == NULL);
 
337
        sp_object_ref(successor, NULL);
 
338
        _successor = successor;
 
339
    }
 
340
 
 
341
    /* modifications; all three sets of methods should probably ultimately be protected, as they
 
342
     * are not really part of its public interface.  However, other parts of the code to
 
343
     * occasionally use them at present. */
 
344
 
 
345
    /* the no-argument version of updateRepr() is intended to be a bit more public, however -- it
 
346
     * essentially just flushes any changes back to the backing store (the repr layer); maybe it
 
347
     * should be called something else and made public at that point. */
 
348
 
 
349
    /** @brief Updates the object's repr based on the object's state.
 
350
     *
 
351
     *  This method updates the the repr attached to the object to reflect the object's current
 
352
     *  state; see the two-argument version for details.
 
353
     *
 
354
     *  @param flags object write flags that apply to this update
 
355
     *
 
356
     *  @return the updated repr
 
357
     */
 
358
    Inkscape::XML::Node *updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT);
 
359
 
 
360
    /** @brief Updates the given repr based on the object's state.
 
361
     *
 
362
     *  This method updates the given repr to reflect the object's current state.  There are
 
363
     *  several flags that affect this:
 
364
     *
 
365
     *   SP_OBJECT_WRITE_BUILD - create new reprs
 
366
     *
 
367
     *   SP_OBJECT_WRITE_EXT   - write elements and attributes
 
368
     *                           which are not part of pure SVG
 
369
     *                           (i.e. the Inkscape and Sodipodi
 
370
     *                           namespaces)
 
371
     *
 
372
     *   SP_OBJECT_WRITE_ALL   - create all nodes and attributes,
 
373
     *                           even those which might be redundant
 
374
     *
 
375
     *  @param repr the repr to update
 
376
     *  @param flags object write flags that apply to this update
 
377
     *
 
378
     *  @return the updated repr
 
379
     */
 
380
    Inkscape::XML::Node *updateRepr(Inkscape::XML::Node *repr, unsigned int flags);
 
381
 
 
382
    /** @brief Queues an deferred update of this object's display.
 
383
     *
 
384
     *  This method sets flags to indicate updates to be performed later, during the idle loop.
 
385
     *
 
386
     *  There are several flags permitted here:
 
387
     *
 
388
     *   SP_OBJECT_MODIFIED_FLAG - the object has been modified
 
389
     *
 
390
     *   SP_OBJECT_CHILD_MODIFIED_FLAG - a child of the object has been
 
391
     *                                   modified
 
392
     *
 
393
     *   SP_OBJECT_STYLE_MODIFIED_FLAG - the object's style has been
 
394
     *                                   modified
 
395
     *
 
396
     *  There are also some subclass-specific modified flags which are hardly ever used.
 
397
     *
 
398
     *  One of either MODIFIED or CHILD_MODIFIED is required.
 
399
     *
 
400
     *  @param flags flags indicating what to update
 
401
     */
 
402
    void requestDisplayUpdate(unsigned int flags);
 
403
 
 
404
    /** @brief Updates the object's display immediately
 
405
     *
 
406
     *  This method is called during the idle loop by SPDocument in order to update the object's
 
407
     *  display.
 
408
     *
 
409
     *  One additional flag is legal here:
 
410
     *
 
411
     *   SP_OBJECT_PARENT_MODIFIED_FLAG - the parent has been
 
412
     *                                    modified
 
413
     *
 
414
     *  @param ctx an SPCtx which accumulates various state
 
415
     *             during the recursive update -- beware! some
 
416
     *             subclasses try to cast this to an SPItemCtx *
 
417
     *
 
418
     *  @param flags flags indicating what to update (in addition
 
419
     *               to any already set flags)
 
420
     */
 
421
    void updateDisplay(SPCtx *ctx, unsigned int flags);
 
422
 
 
423
    /** @brief Requests that a modification notification signal
 
424
     *         be emitted later (e.g. during the idle loop)
 
425
     *
 
426
     *  @param flags flags indicating what has been modified
 
427
     */
 
428
    void requestModified(unsigned int flags);
 
429
 
 
430
    /** @brief Emits a modification notification signal
 
431
     *
 
432
     *  @param flags indicating what has been modified
 
433
     */
 
434
    void emitModified(unsigned int flags);
 
435
 
 
436
    void _sendDeleteSignalRecursive();
 
437
    void _updateTotalHRefCount(int increment);
 
438
 
 
439
    void _requireSVGVersion(unsigned major, unsigned minor) {
 
440
        _requireSVGVersion(Inkscape::Version(major, minor));
 
441
    }
 
442
    void _requireSVGVersion(Inkscape::Version version);
 
443
 
 
444
    sigc::signal<void, SPObject *> _delete_signal;
 
445
    SPObject *_successor;
 
446
    CollectionPolicy _collection_policy;
 
447
    gchar *_label;
 
448
    mutable gchar *_default_label;
 
449
};
 
450
 
 
451
/// The SPObject vtable.
 
452
struct SPObjectClass {
 
453
    GObjectClass parent_class;
 
454
 
 
455
    void (* build) (SPObject *object, SPDocument *doc, Inkscape::XML::Node *repr);
 
456
    void (* release) (SPObject *object);
 
457
 
 
458
    /* Virtual handlers of repr signals */
 
459
    void (* child_added) (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
 
460
    void (* remove_child) (SPObject *object, Inkscape::XML::Node *child);
 
461
 
 
462
    void (* order_changed) (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *new_repr);
 
463
 
 
464
    void (* set) (SPObject *object, unsigned int key, gchar const *value);
 
465
 
 
466
    void (* read_content) (SPObject *object);
 
467
 
 
468
    /* Update handler */
 
469
    void (* update) (SPObject *object, SPCtx *ctx, unsigned int flags);
 
470
    /* Modification handler */
 
471
    void (* modified) (SPObject *object, unsigned int flags);
 
472
 
 
473
    Inkscape::XML::Node * (* write) (SPObject *object, Inkscape::XML::Node *repr, unsigned int flags);
 
474
};
 
475
 
 
476
 
 
477
/*
 
478
 * Attaching/detaching
 
479
 */
 
480
 
 
481
void sp_object_attach(SPObject *parent, SPObject *object, SPObject *prev);
 
482
void sp_object_reorder(SPObject *object, SPObject *prev);
 
483
void sp_object_detach(SPObject *parent, SPObject *object);
 
484
 
 
485
inline SPObject *sp_object_first_child(SPObject *parent) {
 
486
    return parent->firstChild();
 
487
}
 
488
SPObject *sp_object_get_child_by_repr(SPObject *object, Inkscape::XML::Node *repr);
 
489
 
 
490
void sp_object_invoke_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned);
 
491
void sp_object_invoke_release(SPObject *object);
 
492
 
 
493
void sp_object_set(SPObject *object, unsigned int key, gchar const *value);
 
494
 
 
495
void sp_object_read_attr(SPObject *object, gchar const *key);
 
496
 
 
497
/*
 
498
 * Get and set descriptive parameters.
 
499
 *
 
500
 * These are inefficent, so they are not intended to be used interactively.
 
501
 */
 
502
 
 
503
gchar const *sp_object_title_get(SPObject *object);
 
504
gchar const *sp_object_description_get(SPObject *object);
 
505
unsigned int sp_object_title_set(SPObject *object, gchar const *title);
 
506
unsigned int sp_object_description_set(SPObject *object, gchar const *desc);
 
507
 
 
508
/* Public */
 
509
 
 
510
gchar const *sp_object_tagName_get(SPObject const *object, SPException *ex);
 
511
gchar const *sp_object_getAttribute(SPObject const *object, gchar const *key, SPException *ex);
 
512
void sp_object_setAttribute(SPObject *object, gchar const *key, gchar const *value, SPException *ex);
 
513
void sp_object_removeAttribute(SPObject *object, gchar const *key, SPException *ex);
 
514
 
 
515
/* Style */
 
516
 
 
517
gchar const *sp_object_get_style_property(SPObject const *object,
 
518
                                          gchar const *key, gchar const *def);
 
519
 
 
520
Inkscape::Version sp_object_get_sodipodi_version(SPObject *object);
 
521
 
 
522
int sp_object_compare_position(SPObject const *first, SPObject const *second);
 
523
 
 
524
SPObject *sp_object_prev(SPObject *child);
 
525
 
 
526
 
 
527
#endif
 
528
 
 
529
 
 
530
/*
 
531
  Local Variables:
 
532
  mode:c++
 
533
  c-file-style:"stroustrup"
 
534
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
535
  indent-tabs-mode:nil
 
536
  fill-column:99
 
537
  End:
 
538
*/
 
539
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :