Code

Pot and Dutch translation update
[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 <glib.h> // g_assert()
17 #include <cstdio>
19 #include "event.h"
20 #include "event-fns.h"
21 #include "util/reverse-list.h"
22 #include "xml/document.h"
23 #include "xml/node-observer.h"
24 #include "debug/event-tracker.h"
25 #include "debug/simple-event.h"
27 using Inkscape::Util::List;
28 using Inkscape::Util::reverse_list;
30 int Inkscape::XML::Event::_next_serial=0;
32 void
33 sp_repr_begin_transaction (Inkscape::XML::Document *doc)
34 {
35         using Inkscape::Debug::SimpleEvent;
36         using Inkscape::Debug::EventTracker;
37         using Inkscape::Debug::Event;
39         EventTracker<SimpleEvent<Event::XML> > tracker("begin-transaction");
41         g_assert(doc != NULL);
42         doc->beginTransaction();
43 }
45 void
46 sp_repr_rollback (Inkscape::XML::Document *doc)
47 {
48         using Inkscape::Debug::SimpleEvent;
49         using Inkscape::Debug::EventTracker;
50         using Inkscape::Debug::Event;
52         EventTracker<SimpleEvent<Event::XML> > tracker("rollback");
54         g_assert(doc != NULL);
55         doc->rollback();
56 }
58 void
59 sp_repr_commit (Inkscape::XML::Document *doc)
60 {
61         using Inkscape::Debug::SimpleEvent;
62         using Inkscape::Debug::EventTracker;
63         using Inkscape::Debug::Event;
65         EventTracker<SimpleEvent<Event::XML> > tracker("commit");
67         g_assert(doc != NULL);
68         doc->commit();
69 }
71 Inkscape::XML::Event *
72 sp_repr_commit_undoable (Inkscape::XML::Document *doc)
73 {
74         using Inkscape::Debug::SimpleEvent;
75         using Inkscape::Debug::EventTracker;
76         using Inkscape::Debug::Event;
78         EventTracker<SimpleEvent<Event::XML> > tracker("commit");
80         g_assert(doc != NULL);
81         return doc->commitUndoable();
82 }
84 namespace {
86 class LogPerformer : public Inkscape::XML::NodeObserver {
87 public:
88         typedef Inkscape::XML::Node Node;
90         static LogPerformer &instance() {
91                 static LogPerformer singleton;
92                 return singleton;
93         }
95         void notifyChildAdded(Node &parent, Node &child, Node *ref) {
96                 parent.addChild(&child, ref);
97         }
99         void notifyChildRemoved(Node &parent, Node &child, Node */*old_ref*/) {
100                 parent.removeChild(&child);
101         }
103         void notifyChildOrderChanged(Node &parent, Node &child,
104                                      Node */*old_ref*/, Node *new_ref)
105         {
106                 parent.changeOrder(&child, new_ref);
107         }
109         void notifyAttributeChanged(Node &node, GQuark name,
110                                     Inkscape::Util::ptr_shared<char> /*old_value*/,
111                                     Inkscape::Util::ptr_shared<char> new_value)
112         {
113                 node.setAttribute(g_quark_to_string(name), new_value);
114         }
116         void notifyContentChanged(Node &node,
117                                   Inkscape::Util::ptr_shared<char> /*old_value*/,
118                                   Inkscape::Util::ptr_shared<char> new_value)
119         {
120                 node.setContent(new_value);
121         }
122 };
126 void Inkscape::XML::undo_log_to_observer(
127         Inkscape::XML::Event const *log,
128         Inkscape::XML::NodeObserver &observer
129 ) {
130         for ( Event const *action = log ; action ; action = action->next ) {
131                 action->undoOne(observer);
132         }
135 void
136 sp_repr_undo_log (Inkscape::XML::Event *log)
138         using Inkscape::Debug::SimpleEvent;
139         using Inkscape::Debug::EventTracker;
140         using Inkscape::Debug::Event;
142         EventTracker<SimpleEvent<Event::XML> > tracker("undo-log");
144         if (log && log->repr) {
145                 g_assert(!log->repr->document()->inTransaction());
146         }
148         Inkscape::XML::undo_log_to_observer(log, LogPerformer::instance());
151 void Inkscape::XML::EventAdd::_undoOne(
152         Inkscape::XML::NodeObserver &observer
153 ) const {
154         observer.notifyChildRemoved(*this->repr, *this->child, this->ref);
157 void Inkscape::XML::EventDel::_undoOne(
158         Inkscape::XML::NodeObserver &observer
159 ) const {
160         observer.notifyChildAdded(*this->repr, *this->child, this->ref);
163 void Inkscape::XML::EventChgAttr::_undoOne(
164         Inkscape::XML::NodeObserver &observer
165 ) const {
166         observer.notifyAttributeChanged(*this->repr, this->key, this->newval, this->oldval);
169 void Inkscape::XML::EventChgContent::_undoOne(
170         Inkscape::XML::NodeObserver &observer
171 ) const {
172         observer.notifyContentChanged(*this->repr, this->newval, this->oldval);
175 void Inkscape::XML::EventChgOrder::_undoOne(
176         Inkscape::XML::NodeObserver &observer
177 ) const {
178         observer.notifyChildOrderChanged(*this->repr, *this->child, this->newref, this->oldref);
181 void Inkscape::XML::replay_log_to_observer(
182         Inkscape::XML::Event const *log,
183         Inkscape::XML::NodeObserver &observer
184 ) {
185         List<Inkscape::XML::Event const &> reversed =
186           reverse_list<Inkscape::XML::Event::ConstIterator>(log, NULL);
187         for ( ; reversed ; ++reversed ) {
188                 reversed->replayOne(observer);
189         }
192 void
193 sp_repr_replay_log (Inkscape::XML::Event *log)
195         using Inkscape::Debug::SimpleEvent;
196         using Inkscape::Debug::EventTracker;
197         using Inkscape::Debug::Event;
199         EventTracker<SimpleEvent<Event::XML> > tracker("replay-log");
201         if (log) {
202                 if (log->repr->document()) {
203                         g_assert(!log->repr->document()->inTransaction());
204                 }
205         }
207         Inkscape::XML::replay_log_to_observer(log, LogPerformer::instance());
210 void Inkscape::XML::EventAdd::_replayOne(
211         Inkscape::XML::NodeObserver &observer
212 ) const {
213         observer.notifyChildAdded(*this->repr, *this->child, this->ref);
216 void Inkscape::XML::EventDel::_replayOne(
217         Inkscape::XML::NodeObserver &observer
218 ) const {
219         observer.notifyChildRemoved(*this->repr, *this->child, this->ref);
222 void Inkscape::XML::EventChgAttr::_replayOne(
223         Inkscape::XML::NodeObserver &observer
224 ) const {
225         observer.notifyAttributeChanged(*this->repr, this->key, this->oldval, this->newval);
228 void Inkscape::XML::EventChgContent::_replayOne(
229         Inkscape::XML::NodeObserver &observer
230 ) const {
231         observer.notifyContentChanged(*this->repr, this->oldval, this->newval);
234 void Inkscape::XML::EventChgOrder::_replayOne(
235         Inkscape::XML::NodeObserver &observer
236 ) const {
237         observer.notifyChildOrderChanged(*this->repr, *this->child, this->oldref, this->newref);
240 Inkscape::XML::Event *
241 sp_repr_coalesce_log (Inkscape::XML::Event *a, Inkscape::XML::Event *b)
243         Inkscape::XML::Event *action;
244         Inkscape::XML::Event **prev_ptr;
246         if (!b) return a;
247         if (!a) return b;
249         /* find the earliest action in the second log */
250         /* (also noting the pointer that references it, so we can
251          *  replace it later) */
252         prev_ptr = &b;
253         for ( action = b ; action->next ; action = action->next ) {
254                 prev_ptr = &action->next;
255         }
257         /* add the first log after it */
258         action->next = a;
260         /* optimize the result */
261         *prev_ptr = action->optimizeOne();
263         return b;
266 void
267 sp_repr_free_log (Inkscape::XML::Event *log)
269         while (log) {
270                 Inkscape::XML::Event *action;
271                 action = log;
272                 log = action->next;
273                 delete action;
274         }
277 namespace {
279 template <typename T> struct ActionRelations;
281 template <>
282 struct ActionRelations<Inkscape::XML::EventAdd> {
283         typedef Inkscape::XML::EventDel Opposite;
284 };
286 template <>
287 struct ActionRelations<Inkscape::XML::EventDel> {
288         typedef Inkscape::XML::EventAdd Opposite;
289 };
291 template <typename A>
292 Inkscape::XML::Event *cancel_add_or_remove(A *action) {
293         typedef typename ActionRelations<A>::Opposite Opposite;
294         Opposite *opposite=dynamic_cast<Opposite *>(action->next);
296         if ( opposite && opposite->repr == action->repr &&
297              opposite->child == action->child &&
298              opposite->ref == action->ref )
299         {
300                 Inkscape::XML::Event *remaining=opposite->next;
302                 delete opposite;
303                 delete action;
305                 return remaining;
306         } else {
307                 return action;
308         }
313 Inkscape::XML::Event *Inkscape::XML::EventAdd::_optimizeOne() {
314         return cancel_add_or_remove(this);
317 Inkscape::XML::Event *Inkscape::XML::EventDel::_optimizeOne() {
318         return cancel_add_or_remove(this);
321 Inkscape::XML::Event *Inkscape::XML::EventChgAttr::_optimizeOne() {
322         Inkscape::XML::EventChgAttr *chg_attr=dynamic_cast<Inkscape::XML::EventChgAttr *>(this->next);
324         /* consecutive chgattrs on the same key can be combined */
325         if ( chg_attr && chg_attr->repr == this->repr &&
326              chg_attr->key == this->key )
327         {
328                 /* replace our oldval with the prior action's */
329                 this->oldval = chg_attr->oldval;
331                 /* discard the prior action */
332                 this->next = chg_attr->next;
333                 delete chg_attr;
334         }
336         return this;
339 Inkscape::XML::Event *Inkscape::XML::EventChgContent::_optimizeOne() {
340         Inkscape::XML::EventChgContent *chg_content=dynamic_cast<Inkscape::XML::EventChgContent *>(this->next);
342         /* consecutive content changes can be combined */
343         if ( chg_content && chg_content->repr == this->repr ) {
344                 /* replace our oldval with the prior action's */
345                 this->oldval = chg_content->oldval;
347                 /* get rid of the prior action*/
348                 this->next = chg_content->next;
349                 delete chg_content;
350         }
352         return this;
355 Inkscape::XML::Event *Inkscape::XML::EventChgOrder::_optimizeOne() {
356         Inkscape::XML::EventChgOrder *chg_order=dynamic_cast<Inkscape::XML::EventChgOrder *>(this->next);
358         /* consecutive chgorders for the same child may be combined or
359          * canceled out */
360         if ( chg_order && chg_order->repr == this->repr &&
361              chg_order->child == this->child )
362         {
363                 if ( chg_order->oldref == this->newref ) {
364                         /* cancel them out */
365                         Inkscape::XML::Event *after=chg_order->next;
367                         delete chg_order;
368                         delete this;
370                         return after;
371                 } else {
372                         /* combine them */
373                         this->oldref = chg_order->oldref;
375                         /* get rid of the other one */
376                         this->next = chg_order->next;
377                         delete chg_order;
379                         return this;
380                 }
381         } else {
382                 return this;
383         }
386 namespace {
388 class LogPrinter : public Inkscape::XML::NodeObserver {
389 public:
390         typedef Inkscape::XML::Node Node;
392         static LogPrinter &instance() {
393                 static LogPrinter singleton;
394                 return singleton;
395         }
397         static Glib::ustring node_to_string(Node const &node) {
398                 Glib::ustring result;
399                 char const *type_name=NULL;
400                 switch (node.type()) {
401                 case Inkscape::XML::DOCUMENT_NODE:
402                         type_name = "Document";
403                         break;
404                 case Inkscape::XML::ELEMENT_NODE:
405                         type_name = "Element";
406                         break;
407                 case Inkscape::XML::TEXT_NODE:
408                         type_name = "Text";
409                         break;
410                 case Inkscape::XML::COMMENT_NODE:
411                         type_name = "Comment";
412                         break;
413                 default:
414                         g_assert_not_reached();
415                 }
416                 char buffer[40];
417                 result.append("#<");
418                 result.append(type_name);
419                 result.append(":");
420                 snprintf(buffer, 40, "0x%p", &node);
421                 result.append(buffer);
422                 result.append(">");
424                 return result;
425         }
427         static Glib::ustring ref_to_string(Node *ref) {
428                 if (ref) {
429                         return node_to_string(*ref);
430                 } else {
431                         return "beginning";
432                 }
433         }
435         void notifyChildAdded(Node &parent, Node &child, Node *ref) {
436                 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());
437         }
439         void notifyChildRemoved(Node &parent, Node &child, Node */*ref*/) {
440                 g_warning("Event: Removed %s from %s", node_to_string(parent).c_str(), node_to_string(child).c_str());
441         }
443         void notifyChildOrderChanged(Node &parent, Node &child,
444                                      Node */*old_ref*/, Node *new_ref)
445         {
446                 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());
447         }
449         void notifyAttributeChanged(Node &node, GQuark name,
450                                     Inkscape::Util::ptr_shared<char> /*old_value*/,
451                                     Inkscape::Util::ptr_shared<char> new_value)
452         {
453                 if (new_value) {
454                         g_warning("Event: Set attribute %s to \"%s\" on %s", g_quark_to_string(name), new_value.pointer(), node_to_string(node).c_str());
455                 } else {
456                         g_warning("Event: Unset attribute %s on %s", g_quark_to_string(name), node_to_string(node).c_str());
457                 }
458         }
460         void notifyContentChanged(Node &node,
461                                   Inkscape::Util::ptr_shared<char> /*old_value*/,
462                                   Inkscape::Util::ptr_shared<char> new_value)
463         {
464                 if (new_value) {
465                         g_warning("Event: Set content of %s to \"%s\"", node_to_string(node).c_str(), new_value.pointer());
466                 } else {
467                         g_warning("Event: Unset content of %s", node_to_string(node).c_str());
468                 }
469         }
470 };
474 void
475 sp_repr_debug_print_log(Inkscape::XML::Event const *log) {
476         Inkscape::XML::replay_log_to_observer(log, LogPrinter::instance());