1 /** @file
2 * @brief Event object representing a change of the XML document
3 */
4 /* Authors:
5 * Unknown author(s)
6 * Krzysztof KosiĆski <tweenk.pl@gmail.com> (documentation)
7 *
8 * Copyright 2008 Authors
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * See the file COPYING for details.
16 */
18 #ifndef SEEN_INKSCAPE_XML_SP_REPR_ACTION_H
19 #define SEEN_INKSCAPE_XML_SP_REPR_ACTION_H
21 #include <glib/gtypes.h>
22 #include <glib/gquark.h>
23 #include <glibmm/ustring.h>
25 #include <iterator>
26 #include "util/share.h"
27 #include "util/forward-pointer-iterator.h"
28 #include "gc-managed.h"
29 #include "xml/xml-forward.h"
30 #include "xml/node.h"
32 namespace Inkscape {
33 namespace XML {
35 /**
36 * @brief Enumeration of all XML event types
37 */
38 enum EventType {
39 EVENT_ADD, ///< Child added
40 EVENT_DEL, ///< Child removed
41 EVENT_CHG_ATTR, ///< Attribute changed
42 EVENT_CHG_CONTENT, ///< Content changed
43 EVENT_CHG_ORDER ///< Order of children changed
44 };
46 /**
47 * @brief Generic XML modification event
48 *
49 * This is the base class for all other modification events. It is actually a singly-linked
50 * list of events, called an event chain or an event log. Logs of events that happened
51 * in a transaction can be obtained from Document::commitUndoable(). Events can be replayed
52 * to a NodeObserver, or undone (which is equivalent to replaying opposite events in reverse
53 * order).
54 *
55 * Event logs are built by appending to the front, so by walking the list one iterates over
56 * the events in reverse chronological order.
57 */
58 class Event
59 : public Inkscape::GC::Managed<Inkscape::GC::SCANNED, Inkscape::GC::MANUAL>
60 {
61 public:
62 virtual ~Event() {}
64 /**
65 * @brief Pointer to the next event in the event chain
66 *
67 * Note that the event this pointer points to actually happened before this event.
68 * This is because the event log is built by appending to the front.
69 */
70 Event *next;
71 /**
72 * @brief Serial number of the event, not used at the moment
73 */
74 int serial;
75 /**
76 * @brief Pointer to the node that was the object of the event
77 *
78 * Because the nodes are garbage-collected, this pointer guarantees that the node
79 * will stay in memory as long as the event does. This simplifies rolling back
80 * extensive deletions.
81 */
82 Node *repr;
84 struct IteratorStrategy {
85 static Event const *next(Event const *action) {
86 return action->next;
87 }
88 };
90 typedef Inkscape::Util::ForwardPointerIterator<Event, IteratorStrategy> Iterator;
91 typedef Inkscape::Util::ForwardPointerIterator<Event const, IteratorStrategy> ConstIterator;
93 /**
94 * @brief If possible, combine this event with the next to reduce memory use
95 * @return Pointer to the optimized event chain, which may have changed
96 */
97 Event *optimizeOne() { return _optimizeOne(); }
98 /**
99 * @brief Undo this event to an observer
100 *
101 * This method notifies the specified observer of an action opposite to the one that
102 * is described by this event.
103 */
104 void undoOne(NodeObserver &observer) const {
105 _undoOne(observer);
106 }
107 /**
108 * @brief Replay this event to an observer
109 *
110 * This method notifies the specified event of the same action that it describes.
111 */
112 void replayOne(NodeObserver &observer) const {
113 _replayOne(observer);
114 }
116 protected:
117 Event(Node *r, Event *n)
118 : next(n), serial(_next_serial++), repr(r) {}
120 virtual Event *_optimizeOne()=0;
121 virtual void _undoOne(NodeObserver &) const=0;
122 virtual void _replayOne(NodeObserver &) const=0;
124 private:
125 static int _next_serial;
126 };
128 /**
129 * @brief Object representing child addition
130 */
131 class EventAdd : public Event {
132 public:
133 EventAdd(Node *repr, Node *c, Node *rr, Event *next)
134 : Event(repr, next), child(c), ref(rr) {}
136 /// The added child node
137 Node *child;
138 /// The node after which the child has been added, or NULL if it was added as first
139 Node *ref;
141 private:
142 Event *_optimizeOne();
143 void _undoOne(NodeObserver &observer) const;
144 void _replayOne(NodeObserver &observer) const;
145 };
147 /**
148 * @brief Object representing child removal
149 */
150 class EventDel : public Event {
151 public:
152 EventDel(Node *repr, Node *c, Node *rr, Event *next)
153 : Event(repr, next), child(c), ref(rr) {}
155 /// The child node that was removed
156 Node *child;
157 /// The node after which the removed node was in the sibling order, or NULL if it was first
158 Node *ref;
160 private:
161 Event *_optimizeOne();
162 void _undoOne(NodeObserver &observer) const;
163 void _replayOne(NodeObserver &observer) const;
164 };
166 /**
167 * @brief Object representing attribute change
168 */
169 class EventChgAttr : public Event {
170 public:
171 EventChgAttr(Node *repr, GQuark k,
172 Inkscape::Util::ptr_shared<char> ov,
173 Inkscape::Util::ptr_shared<char> nv,
174 Event *next)
175 : Event(repr, next), key(k),
176 oldval(ov), newval(nv) {}
178 /// GQuark corresponding to the changed attribute's name
179 GQuark key;
180 /// Value of the attribute before the change
181 Inkscape::Util::ptr_shared<char> oldval;
182 /// Value of the attribute after the change
183 Inkscape::Util::ptr_shared<char> newval;
185 private:
186 Event *_optimizeOne();
187 void _undoOne(NodeObserver &observer) const;
188 void _replayOne(NodeObserver &observer) const;
189 };
191 /**
192 * @brief Object representing content change
193 */
194 class EventChgContent : public Event {
195 public:
196 EventChgContent(Node *repr,
197 Inkscape::Util::ptr_shared<char> ov,
198 Inkscape::Util::ptr_shared<char> nv,
199 Event *next)
200 : Event(repr, next), oldval(ov), newval(nv) {}
202 /// Content of the node before the change
203 Inkscape::Util::ptr_shared<char> oldval;
204 /// Content of the node after the change
205 Inkscape::Util::ptr_shared<char> newval;
207 private:
208 Event *_optimizeOne();
209 void _undoOne(NodeObserver &observer) const;
210 void _replayOne(NodeObserver &observer) const;
211 };
213 /**
214 * @brief Obect representing child order change
215 */
216 class EventChgOrder : public Event {
217 public:
218 EventChgOrder(Node *repr, Node *c, Node *orr, Node *nrr, Event *next)
219 : Event(repr, next), child(c),
220 oldref(orr), newref(nrr) {}
222 /// The node that was relocated in sibling order
223 Node *child;
224 /// The node after which the relocated node was in the sibling order before the change, or NULL if it was first
225 Node *oldref;
226 /// The node after which the relocated node is after the change, or if it's first
227 Node *newref;
229 private:
230 Event *_optimizeOne();
231 void _undoOne(NodeObserver &observer) const;
232 void _replayOne(NodeObserver &observer) const;
233 };
235 }
236 }
238 #endif
239 /*
240 Local Variables:
241 mode:c++
242 c-file-style:"stroustrup"
243 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
244 indent-tabs-mode:nil
245 fill-column:99
246 End:
247 */
248 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :