Code

updated spanish.nsh and inkscape.nsi to reflect latest file-changes
[inkscape.git] / trunk / 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()
18 #include "event.h"
19 #include "event-fns.h"
20 #include "util/reverse-list.h"
21 #include "xml/document.h"
22 #include "xml/node-observer.h"
23 #include "debug/event-tracker.h"
24 #include "debug/simple-event.h"
26 using Inkscape::Util::List;
27 using Inkscape::Util::reverse_list;
29 int Inkscape::XML::Event::_next_serial=0;
31 void
32 sp_repr_begin_transaction (Inkscape::XML::Document *doc)
33 {
34         using Inkscape::Debug::SimpleEvent;
35         using Inkscape::Debug::EventTracker;
36         using Inkscape::Debug::Event;
38         EventTracker<SimpleEvent<Event::XML> > tracker("begin-transaction");
40         g_assert(doc != NULL);
41         doc->beginTransaction();
42 }
44 void
45 sp_repr_rollback (Inkscape::XML::Document *doc)
46 {
47         using Inkscape::Debug::SimpleEvent;
48         using Inkscape::Debug::EventTracker;
49         using Inkscape::Debug::Event;
51         EventTracker<SimpleEvent<Event::XML> > tracker("rollback");
53         g_assert(doc != NULL);
54         doc->rollback();
55 }
57 void
58 sp_repr_commit (Inkscape::XML::Document *doc)
59 {
60         using Inkscape::Debug::SimpleEvent;
61         using Inkscape::Debug::EventTracker;
62         using Inkscape::Debug::Event;
64         EventTracker<SimpleEvent<Event::XML> > tracker("commit");
66         g_assert(doc != NULL);
67         doc->commit();
68 }
70 Inkscape::XML::Event *
71 sp_repr_commit_undoable (Inkscape::XML::Document *doc)
72 {
73         using Inkscape::Debug::SimpleEvent;
74         using Inkscape::Debug::EventTracker;
75         using Inkscape::Debug::Event;
77         EventTracker<SimpleEvent<Event::XML> > tracker("commit");
79         g_assert(doc != NULL);
80         return doc->commitUndoable();
81 }
83 namespace {
85 class LogPerformer : public Inkscape::XML::NodeObserver {
86 public:
87         typedef Inkscape::XML::Node Node;
89         static LogPerformer &instance() {
90                 static LogPerformer singleton;
91                 return singleton;
92         }
94         void notifyChildAdded(Node &parent, Node &child, Node *ref) {
95                 parent.addChild(&child, ref);
96         }
98         void notifyChildRemoved(Node &parent, Node &child, Node */*old_ref*/) {
99                 parent.removeChild(&child);
100         }
102         void notifyChildOrderChanged(Node &parent, Node &child,
103                                      Node */*old_ref*/, Node *new_ref)
104         {
105                 parent.changeOrder(&child, new_ref);
106         }
108         void notifyAttributeChanged(Node &node, GQuark name,
109                                     Inkscape::Util::ptr_shared<char> /*old_value*/,
110                                     Inkscape::Util::ptr_shared<char> new_value)
111         {
112                 node.setAttribute(g_quark_to_string(name), new_value);
113         }
115         void notifyContentChanged(Node &node,
116                                   Inkscape::Util::ptr_shared<char> /*old_value*/,
117                                   Inkscape::Util::ptr_shared<char> new_value)
118         {
119                 node.setContent(new_value);
120         }
121 };
125 void Inkscape::XML::undo_log_to_observer(
126         Inkscape::XML::Event const *log,
127         Inkscape::XML::NodeObserver &observer
128 ) {
129         for ( Event const *action = log ; action ; action = action->next ) {
130                 action->undoOne(observer);
131         }
134 void
135 sp_repr_undo_log (Inkscape::XML::Event *log)
137         using Inkscape::Debug::SimpleEvent;
138         using Inkscape::Debug::EventTracker;
139         using Inkscape::Debug::Event;
141         EventTracker<SimpleEvent<Event::XML> > tracker("undo-log");
143         if (log && log->repr) {
144                 g_assert(!log->repr->document()->inTransaction());
145         }
147         Inkscape::XML::undo_log_to_observer(log, LogPerformer::instance());
150 void Inkscape::XML::EventAdd::_undoOne(
151         Inkscape::XML::NodeObserver &observer
152 ) const {
153         observer.notifyChildRemoved(*this->repr, *this->child, this->ref);
156 void Inkscape::XML::EventDel::_undoOne(
157         Inkscape::XML::NodeObserver &observer
158 ) const {
159         observer.notifyChildAdded(*this->repr, *this->child, this->ref);
162 void Inkscape::XML::EventChgAttr::_undoOne(
163         Inkscape::XML::NodeObserver &observer
164 ) const {
165         observer.notifyAttributeChanged(*this->repr, this->key, this->newval, this->oldval);
168 void Inkscape::XML::EventChgContent::_undoOne(
169         Inkscape::XML::NodeObserver &observer
170 ) const {
171         observer.notifyContentChanged(*this->repr, this->newval, this->oldval);
174 void Inkscape::XML::EventChgOrder::_undoOne(
175         Inkscape::XML::NodeObserver &observer
176 ) const {
177         observer.notifyChildOrderChanged(*this->repr, *this->child, this->newref, this->oldref);
180 void Inkscape::XML::replay_log_to_observer(
181         Inkscape::XML::Event const *log,
182         Inkscape::XML::NodeObserver &observer
183 ) {
184         List<Inkscape::XML::Event const &> reversed =
185           reverse_list<Inkscape::XML::Event::ConstIterator>(log, NULL);
186         for ( ; reversed ; ++reversed ) {
187                 reversed->replayOne(observer);
188         }
191 void
192 sp_repr_replay_log (Inkscape::XML::Event *log)
194         using Inkscape::Debug::SimpleEvent;
195         using Inkscape::Debug::EventTracker;
196         using Inkscape::Debug::Event;
198         EventTracker<SimpleEvent<Event::XML> > tracker("replay-log");
200         if (log) {
201                 if (log->repr->document()) {
202                         g_assert(!log->repr->document()->inTransaction());
203                 }
204         }
206         Inkscape::XML::replay_log_to_observer(log, LogPerformer::instance());
209 void Inkscape::XML::EventAdd::_replayOne(
210         Inkscape::XML::NodeObserver &observer
211 ) const {
212         observer.notifyChildAdded(*this->repr, *this->child, this->ref);
215 void Inkscape::XML::EventDel::_replayOne(
216         Inkscape::XML::NodeObserver &observer
217 ) const {
218         observer.notifyChildRemoved(*this->repr, *this->child, this->ref);
221 void Inkscape::XML::EventChgAttr::_replayOne(
222         Inkscape::XML::NodeObserver &observer
223 ) const {
224         observer.notifyAttributeChanged(*this->repr, this->key, this->oldval, this->newval);
227 void Inkscape::XML::EventChgContent::_replayOne(
228         Inkscape::XML::NodeObserver &observer
229 ) const {
230         observer.notifyContentChanged(*this->repr, this->oldval, this->newval);
233 void Inkscape::XML::EventChgOrder::_replayOne(
234         Inkscape::XML::NodeObserver &observer
235 ) const {
236         observer.notifyChildOrderChanged(*this->repr, *this->child, this->oldref, this->newref);
239 Inkscape::XML::Event *
240 sp_repr_coalesce_log (Inkscape::XML::Event *a, Inkscape::XML::Event *b)
242         Inkscape::XML::Event *action;
243         Inkscape::XML::Event **prev_ptr;
245         if (!b) return a;
246         if (!a) return b;
248         /* find the earliest action in the second log */
249         /* (also noting the pointer that references it, so we can
250          *  replace it later) */
251         prev_ptr = &b;
252         for ( action = b ; action->next ; action = action->next ) {
253                 prev_ptr = &action->next;
254         }
256         /* add the first log after it */
257         action->next = a;
259         /* optimize the result */
260         *prev_ptr = action->optimizeOne();
262         return b;
265 void
266 sp_repr_free_log (Inkscape::XML::Event *log)
268         while (log) {
269                 Inkscape::XML::Event *action;
270                 action = log;
271                 log = action->next;
272                 delete action;
273         }
276 namespace {
278 template <typename T> struct ActionRelations;
280 template <>
281 struct ActionRelations<Inkscape::XML::EventAdd> {
282         typedef Inkscape::XML::EventDel Opposite;
283 };
285 template <>
286 struct ActionRelations<Inkscape::XML::EventDel> {
287         typedef Inkscape::XML::EventAdd Opposite;
288 };
290 template <typename A>
291 Inkscape::XML::Event *cancel_add_or_remove(A *action) {
292         typedef typename ActionRelations<A>::Opposite Opposite;
293         Opposite *opposite=dynamic_cast<Opposite *>(action->next);
295         if ( opposite && opposite->repr == action->repr &&
296              opposite->child == action->child &&
297              opposite->ref == action->ref )
298         {
299                 Inkscape::XML::Event *remaining=opposite->next;
301                 delete opposite;
302                 delete action;
304                 return remaining;
305         } else {
306                 return action;
307         }
312 Inkscape::XML::Event *Inkscape::XML::EventAdd::_optimizeOne() {
313         return cancel_add_or_remove(this);
316 Inkscape::XML::Event *Inkscape::XML::EventDel::_optimizeOne() {
317         return cancel_add_or_remove(this);
320 Inkscape::XML::Event *Inkscape::XML::EventChgAttr::_optimizeOne() {
321         Inkscape::XML::EventChgAttr *chg_attr=dynamic_cast<Inkscape::XML::EventChgAttr *>(this->next);
323         /* consecutive chgattrs on the same key can be combined */
324         if ( chg_attr && chg_attr->repr == this->repr &&
325              chg_attr->key == this->key )
326         {
327                 /* replace our oldval with the prior action's */
328                 this->oldval = chg_attr->oldval;
330                 /* discard the prior action */
331                 this->next = chg_attr->next;
332                 delete chg_attr;
333         }
335         return this;
338 Inkscape::XML::Event *Inkscape::XML::EventChgContent::_optimizeOne() {
339         Inkscape::XML::EventChgContent *chg_content=dynamic_cast<Inkscape::XML::EventChgContent *>(this->next);
341         /* consecutive content changes can be combined */
342         if ( chg_content && chg_content->repr == this->repr ) {
343                 /* replace our oldval with the prior action's */
344                 this->oldval = chg_content->oldval;
346                 /* get rid of the prior action*/
347                 this->next = chg_content->next;
348                 delete chg_content;
349         }
351         return this;
354 Inkscape::XML::Event *Inkscape::XML::EventChgOrder::_optimizeOne() {
355         Inkscape::XML::EventChgOrder *chg_order=dynamic_cast<Inkscape::XML::EventChgOrder *>(this->next);
357         /* consecutive chgorders for the same child may be combined or
358          * canceled out */
359         if ( chg_order && chg_order->repr == this->repr &&
360              chg_order->child == this->child )
361         {
362                 if ( chg_order->oldref == this->newref ) {
363                         /* cancel them out */
364                         Inkscape::XML::Event *after=chg_order->next;
366                         delete chg_order;
367                         delete this;
369                         return after;
370                 } else {
371                         /* combine them */
372                         this->oldref = chg_order->oldref;
374                         /* get rid of the other one */
375                         this->next = chg_order->next;
376                         delete chg_order;
378                         return this;
379                 }
380         } else {
381                 return this;
382         }
385 namespace {
387 class LogPrinter : public Inkscape::XML::NodeObserver {
388 public:
389         typedef Inkscape::XML::Node Node;
391         static LogPrinter &instance() {
392                 static LogPrinter singleton;
393                 return singleton;
394         }
396         static Glib::ustring node_to_string(Node const &node) {
397                 Glib::ustring result;
398                 char const *type_name=NULL;
399                 switch (node.type()) {
400                 case Inkscape::XML::DOCUMENT_NODE:
401                         type_name = "Document";
402                         break;
403                 case Inkscape::XML::ELEMENT_NODE:
404                         type_name = "Element";
405                         break;
406                 case Inkscape::XML::TEXT_NODE:
407                         type_name = "Text";
408                         break;
409                 case Inkscape::XML::COMMENT_NODE:
410                         type_name = "Comment";
411                         break;
412                 default:
413                         g_assert_not_reached();
414                 }
415                 char buffer[40];
416                 result.append("#<");
417                 result.append(type_name);
418                 result.append(":");
419                 snprintf(buffer, 40, "0x%p", &node);
420                 result.append(buffer);
421                 result.append(">");
423                 return result;
424         }
426         static Glib::ustring ref_to_string(Node *ref) {
427                 if (ref) {
428                         return node_to_string(*ref);
429                 } else {
430                         return "beginning";
431                 }
432         }
434         void notifyChildAdded(Node &parent, Node &child, Node *ref) {
435                 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());
436         }
438         void notifyChildRemoved(Node &parent, Node &child, Node */*ref*/) {
439                 g_warning("Event: Removed %s from %s", node_to_string(parent).c_str(), node_to_string(child).c_str());
440         }
442         void notifyChildOrderChanged(Node &parent, Node &child,
443                                      Node */*old_ref*/, Node *new_ref)
444         {
445                 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());
446         }
448         void notifyAttributeChanged(Node &node, GQuark name,
449                                     Inkscape::Util::ptr_shared<char> /*old_value*/,
450                                     Inkscape::Util::ptr_shared<char> new_value)
451         {
452                 if (new_value) {
453                         g_warning("Event: Set attribute %s to \"%s\" on %s", g_quark_to_string(name), new_value.pointer(), node_to_string(node).c_str());
454                 } else {
455                         g_warning("Event: Unset attribute %s on %s", g_quark_to_string(name), node_to_string(node).c_str());
456                 }
457         }
459         void notifyContentChanged(Node &node,
460                                   Inkscape::Util::ptr_shared<char> /*old_value*/,
461                                   Inkscape::Util::ptr_shared<char> new_value)
462         {
463                 if (new_value) {
464                         g_warning("Event: Set content of %s to \"%s\"", node_to_string(node).c_str(), new_value.pointer());
465                 } else {
466                         g_warning("Event: Unset content of %s", node_to_string(node).c_str());
467                 }
468         }
469 };
473 void
474 sp_repr_debug_print_log(Inkscape::XML::Event const *log) {
475         Inkscape::XML::replay_log_to_observer(log, LogPrinter::instance());