Code

eliminate last uses of sp_repr_new*
[inkscape.git] / src / xml / event.cpp
1 /*
2  * Repr transaction logging
3  *
4  * Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   MenTaLguY  <mental@rydia.net>
7  *
8  * Copyright (C) 2004-2005 MenTaLguY
9  * Copyright (C) 1999-2003 authors
10  * Copyright (C) 2001-2002 Ximian, Inc.
11  * g++ port Copyright (C) 2003 Nathan Hurst
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #include "event.h"
17 #include "event-fns.h"
18 #include "util/reverse-list.h"
19 #include "xml/document.h"
20 #include "xml/node-observer.h"
21 #include "debug/event-tracker.h"
22 #include "debug/simple-event.h"
24 using Inkscape::Util::List;
25 using Inkscape::Util::reverse_list;
27 int Inkscape::XML::Event::_next_serial=0;
29 void
30 sp_repr_begin_transaction (Inkscape::XML::Document *doc)
31 {
32         using Inkscape::Debug::SimpleEvent;
33         using Inkscape::Debug::EventTracker;
34         using Inkscape::Debug::Event;
36         EventTracker<SimpleEvent<Event::XML> > tracker("begin-transaction");
38         g_assert(doc != NULL);
39         doc->beginTransaction();
40 }
42 void
43 sp_repr_rollback (Inkscape::XML::Document *doc)
44 {
45         using Inkscape::Debug::SimpleEvent;
46         using Inkscape::Debug::EventTracker;
47         using Inkscape::Debug::Event;
49         EventTracker<SimpleEvent<Event::XML> > tracker("rollback");
51         g_assert(doc != NULL);
52         doc->rollback();
53 }
55 void
56 sp_repr_commit (Inkscape::XML::Document *doc)
57 {
58         using Inkscape::Debug::SimpleEvent;
59         using Inkscape::Debug::EventTracker;
60         using Inkscape::Debug::Event;
62         EventTracker<SimpleEvent<Event::XML> > tracker("commit");
64         g_assert(doc != NULL);
65         doc->commit();
66 }
68 Inkscape::XML::Event *
69 sp_repr_commit_undoable (Inkscape::XML::Document *doc)
70 {
71         using Inkscape::Debug::SimpleEvent;
72         using Inkscape::Debug::EventTracker;
73         using Inkscape::Debug::Event;
75         EventTracker<SimpleEvent<Event::XML> > tracker("commit");
77         g_assert(doc != NULL);
78         return doc->commitUndoable();
79 }
81 namespace {
83 class LogPerformer : public Inkscape::XML::NodeObserver {
84 public:
85         typedef Inkscape::XML::Node Node;
87         static LogPerformer &instance() {
88                 static LogPerformer singleton;
89                 return singleton;
90         }
92         void notifyChildAdded(Node &parent, Node &child, Node *ref) {
93                 parent.addChild(&child, ref);
94         }
96         void notifyChildRemoved(Node &parent, Node &child, Node *old_ref) {
97                 parent.removeChild(&child);
98         }
100         void notifyChildOrderChanged(Node &parent, Node &child,
101                                      Node *old_ref, Node *new_ref)
102         {
103                 parent.changeOrder(&child, new_ref);
104         }
106         void notifyAttributeChanged(Node &node, GQuark name,
107                                     Inkscape::Util::ptr_shared<char> old_value,
108                                     Inkscape::Util::ptr_shared<char> new_value)
109         {
110                 node.setAttribute(g_quark_to_string(name), new_value);
111         }
113         void notifyContentChanged(Node &node,
114                                   Inkscape::Util::ptr_shared<char> old_value,
115                                   Inkscape::Util::ptr_shared<char> new_value)
116         {
117                 node.setContent(new_value);
118         }
119 };
123 void Inkscape::XML::undo_log_to_observer(
124         Inkscape::XML::Event const *log,
125         Inkscape::XML::NodeObserver &observer
126 ) {
127         for ( Event const *action = log ; action ; action = action->next ) {
128                 action->undoOne(observer);
129         }
132 void
133 sp_repr_undo_log (Inkscape::XML::Event *log)
135         using Inkscape::Debug::SimpleEvent;
136         using Inkscape::Debug::EventTracker;
137         using Inkscape::Debug::Event;
139         EventTracker<SimpleEvent<Event::XML> > tracker("undo-log");
141         if (log) {
142                 g_assert(!log->repr->document()->inTransaction());
143         }
145         Inkscape::XML::undo_log_to_observer(log, LogPerformer::instance());
148 void Inkscape::XML::EventAdd::_undoOne(
149         Inkscape::XML::NodeObserver &observer
150 ) const {
151         observer.notifyChildRemoved(*this->repr, *this->child, this->ref);
154 void Inkscape::XML::EventDel::_undoOne(
155         Inkscape::XML::NodeObserver &observer
156 ) const {
157         observer.notifyChildAdded(*this->repr, *this->child, this->ref);
160 void Inkscape::XML::EventChgAttr::_undoOne(
161         Inkscape::XML::NodeObserver &observer
162 ) const {
163         observer.notifyAttributeChanged(*this->repr, this->key, this->newval, this->oldval);
166 void Inkscape::XML::EventChgContent::_undoOne(
167         Inkscape::XML::NodeObserver &observer
168 ) const {
169         observer.notifyContentChanged(*this->repr, this->newval, this->oldval);
172 void Inkscape::XML::EventChgOrder::_undoOne(
173         Inkscape::XML::NodeObserver &observer
174 ) const {
175         observer.notifyChildOrderChanged(*this->repr, *this->child, this->newref, this->oldref);
178 void Inkscape::XML::replay_log_to_observer(
179         Inkscape::XML::Event const *log,
180         Inkscape::XML::NodeObserver &observer
181 ) {
182         List<Inkscape::XML::Event const &> reversed =
183           reverse_list<Inkscape::XML::Event::ConstIterator>(log, NULL);
184         for ( ; reversed ; ++reversed ) {
185                 reversed->replayOne(observer);
186         }
189 void
190 sp_repr_replay_log (Inkscape::XML::Event *log)
192         using Inkscape::Debug::SimpleEvent;
193         using Inkscape::Debug::EventTracker;
194         using Inkscape::Debug::Event;
196         EventTracker<SimpleEvent<Event::XML> > tracker("replay-log");
198         if (log) {
199                 if (log->repr->document()) {
200                         g_assert(!log->repr->document()->inTransaction());
201                 }
202         }
204         Inkscape::XML::replay_log_to_observer(log, LogPerformer::instance());
207 void Inkscape::XML::EventAdd::_replayOne(
208         Inkscape::XML::NodeObserver &observer
209 ) const {
210         observer.notifyChildAdded(*this->repr, *this->child, this->ref);
213 void Inkscape::XML::EventDel::_replayOne(
214         Inkscape::XML::NodeObserver &observer
215 ) const {
216         observer.notifyChildRemoved(*this->repr, *this->child, this->ref);
219 void Inkscape::XML::EventChgAttr::_replayOne(
220         Inkscape::XML::NodeObserver &observer
221 ) const {
222         observer.notifyAttributeChanged(*this->repr, this->key, this->oldval, this->newval);
225 void Inkscape::XML::EventChgContent::_replayOne(
226         Inkscape::XML::NodeObserver &observer
227 ) const {
228         observer.notifyContentChanged(*this->repr, this->oldval, this->newval);
231 void Inkscape::XML::EventChgOrder::_replayOne(
232         Inkscape::XML::NodeObserver &observer
233 ) const {
234         observer.notifyChildOrderChanged(*this->repr, *this->child, this->oldref, this->newref);
237 Inkscape::XML::Event *
238 sp_repr_coalesce_log (Inkscape::XML::Event *a, Inkscape::XML::Event *b)
240         Inkscape::XML::Event *action;
241         Inkscape::XML::Event **prev_ptr;
243         if (!b) return a;
244         if (!a) return b;
246         /* find the earliest action in the second log */
247         /* (also noting the pointer that references it, so we can
248          *  replace it later) */
249         prev_ptr = &b;
250         for ( action = b ; action->next ; action = action->next ) {
251                 prev_ptr = &action->next;
252         }
254         /* add the first log after it */
255         action->next = a;
257         /* optimize the result */
258         *prev_ptr = action->optimizeOne();
260         return b;
263 void
264 sp_repr_free_log (Inkscape::XML::Event *log)
266         while (log) {
267                 Inkscape::XML::Event *action;
268                 action = log;
269                 log = action->next;
270                 delete action;
271         }
274 namespace {
276 template <typename T> struct ActionRelations;
278 template <>
279 struct ActionRelations<Inkscape::XML::EventAdd> {
280         typedef Inkscape::XML::EventDel Opposite;
281 };
283 template <>
284 struct ActionRelations<Inkscape::XML::EventDel> {
285         typedef Inkscape::XML::EventAdd Opposite;
286 };
288 template <typename A>
289 Inkscape::XML::Event *cancel_add_or_remove(A *action) {
290         typedef typename ActionRelations<A>::Opposite Opposite;
291         Opposite *opposite=dynamic_cast<Opposite *>(action->next);
293         if ( opposite && opposite->repr == action->repr &&
294              opposite->child == action->child &&
295              opposite->ref == action->ref )
296         {
297                 Inkscape::XML::Event *remaining=opposite->next;
299                 delete opposite;
300                 delete action;
302                 return remaining;
303         } else {
304                 return action;
305         }
310 Inkscape::XML::Event *Inkscape::XML::EventAdd::_optimizeOne() {
311         return cancel_add_or_remove(this);
314 Inkscape::XML::Event *Inkscape::XML::EventDel::_optimizeOne() {
315         return cancel_add_or_remove(this);
318 Inkscape::XML::Event *Inkscape::XML::EventChgAttr::_optimizeOne() {
319         Inkscape::XML::EventChgAttr *chg_attr=dynamic_cast<Inkscape::XML::EventChgAttr *>(this->next);
321         /* consecutive chgattrs on the same key can be combined */
322         if ( chg_attr && chg_attr->repr == this->repr &&
323              chg_attr->key == this->key )
324         {
325                 /* replace our oldval with the prior action's */
326                 this->oldval = chg_attr->oldval;
328                 /* discard the prior action */
329                 this->next = chg_attr->next;
330                 delete chg_attr;
331         }
333         return this;
336 Inkscape::XML::Event *Inkscape::XML::EventChgContent::_optimizeOne() {
337         Inkscape::XML::EventChgContent *chg_content=dynamic_cast<Inkscape::XML::EventChgContent *>(this->next);
339         /* consecutive content changes can be combined */
340         if ( chg_content && chg_content->repr == this->repr ) {
341                 /* replace our oldval with the prior action's */
342                 this->oldval = chg_content->oldval;
344                 /* get rid of the prior action*/
345                 this->next = chg_content->next;
346                 delete chg_content;
347         }
349         return this;
352 Inkscape::XML::Event *Inkscape::XML::EventChgOrder::_optimizeOne() {
353         Inkscape::XML::EventChgOrder *chg_order=dynamic_cast<Inkscape::XML::EventChgOrder *>(this->next);
355         /* consecutive chgorders for the same child may be combined or
356          * canceled out */
357         if ( chg_order && chg_order->repr == this->repr &&
358              chg_order->child == this->child )
359         {
360                 if ( chg_order->oldref == this->newref ) {
361                         /* cancel them out */
362                         Inkscape::XML::Event *after=chg_order->next;
364                         delete chg_order;
365                         delete this;
367                         return after;
368                 } else {
369                         /* combine them */
370                         this->oldref = chg_order->oldref;
372                         /* get rid of the other one */
373                         this->next = chg_order->next;
374                         delete chg_order;
376                         return this;
377                 }
378         } else {
379                 return this;
380         }
383 namespace {
385 class LogPrinter : public Inkscape::XML::NodeObserver {
386 public:
387         typedef Inkscape::XML::Node Node;
389         static LogPrinter &instance() {
390                 static LogPrinter singleton;
391                 return singleton;
392         }
394         static Glib::ustring node_to_string(Node const &node) {
395                 Glib::ustring result;
396                 char const *type_name=NULL;
397                 switch (node.type()) {
398                 case Inkscape::XML::DOCUMENT_NODE:
399                         type_name = "Document";
400                         break;
401                 case Inkscape::XML::ELEMENT_NODE:
402                         type_name = "Element";
403                         break;
404                 case Inkscape::XML::TEXT_NODE:
405                         type_name = "Text";
406                         break;
407                 case Inkscape::XML::COMMENT_NODE:
408                         type_name = "Comment";
409                         break;
410                 default:
411                         g_assert_not_reached();
412                 }
413                 char buffer[40];
414                 result.append("#<");
415                 result.append(type_name);
416                 result.append(":");
417                 snprintf(buffer, 40, "0x%p", &node);
418                 result.append(buffer);
419                 result.append(">");
421                 return result;
422         }
424         static Glib::ustring ref_to_string(Node *ref) {
425                 if (ref) {
426                         return node_to_string(*ref);
427                 } else {
428                         return "beginning";
429                 }
430         }
432         void notifyChildAdded(Node &parent, Node &child, Node *ref) {
433                 g_warning("Event: Added %s to %s after %s", node_to_string(parent).c_str(), node_to_string(child).c_str(), ref_to_string(ref).c_str());
434         }
436         void notifyChildRemoved(Node &parent, Node &child, Node *ref) {
437                 g_warning("Event: Removed %s from %s", node_to_string(parent).c_str(), node_to_string(child).c_str());
438         }
440         void notifyChildOrderChanged(Node &parent, Node &child,
441                                      Node *old_ref, Node *new_ref)
442         {
443                 g_warning("Event: Moved %s after %s in %s", node_to_string(child).c_str(), ref_to_string(new_ref).c_str(), node_to_string(parent).c_str());
444         }
446         void notifyAttributeChanged(Node &node, GQuark name,
447                                     Inkscape::Util::ptr_shared<char> old_value,
448                                     Inkscape::Util::ptr_shared<char> new_value)
449         {
450                 if (new_value) {
451                         g_warning("Event: Set attribute %s to \"%s\" on %s", g_quark_to_string(name), new_value.pointer(), node_to_string(node).c_str());
452                 } else {
453                         g_warning("Event: Unset attribute %s on %s", g_quark_to_string(name), node_to_string(node).c_str());
454                 }
455         }
457         void notifyContentChanged(Node &node,
458                                   Inkscape::Util::ptr_shared<char> old_value,
459                                   Inkscape::Util::ptr_shared<char> new_value)
460         {
461                 if (new_value) {
462                         g_warning("Event: Set content of %s to \"%s\"", node_to_string(node).c_str(), new_value.pointer());
463                 } else {
464                         g_warning("Event: Unset content of %s", node_to_string(node).c_str());
465                 }
466         }
467 };
471 void
472 sp_repr_debug_print_log(Inkscape::XML::Event const *log) {
473         Inkscape::XML::replay_log_to_observer(log, LogPrinter::instance());