Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
event.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Repr transaction logging
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * MenTaLguY <mental@rydia.net>
8 *
9 * Copyright (C) 2004-2005 MenTaLguY
10 * Copyright (C) 1999-2003 authors
11 * Copyright (C) 2001-2002 Ximian, Inc.
12 * g++ port Copyright (C) 2003 Nathan Hurst
13 *
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
17#include <glib.h> // g_assert()
18#include <cstdio>
19
20#include "event.h"
21#include "event-fns.h"
22#include "xml/document.h"
23#include "xml/node-observer.h"
24#include "debug/event-tracker.h"
25#include "debug/simple-event.h"
26
28
29void
31{
35
36 EventTracker<SimpleEvent<Event::XML> > tracker("begin-transaction");
37
38 g_assert(doc != nullptr);
39 doc->beginTransaction();
40}
41
42void
44{
48
49 EventTracker<SimpleEvent<Event::XML> > tracker("rollback");
50
51 g_assert(doc != nullptr);
52 doc->rollback();
53}
54
55void
57{
61
62 EventTracker<SimpleEvent<Event::XML> > tracker("commit");
63
64 g_assert(doc != nullptr);
65 doc->commit();
66}
67
70{
74
75 EventTracker<SimpleEvent<Event::XML> > tracker("commit");
76
77 g_assert(doc != nullptr);
78 return doc->commitUndoable();
79}
80
81namespace {
82
83class LogPerformer : public Inkscape::XML::NodeObserver {
84public:
85 typedef Inkscape::XML::Node Node;
86
87 static LogPerformer &instance() {
88 static LogPerformer singleton;
89 return singleton;
90 }
91
92 void notifyChildAdded(Node &parent, Node &child, Node *ref) override {
93 parent.addChild(&child, ref);
94 }
95
96 void notifyChildRemoved(Node &parent, Node &child, Node */*old_ref*/) override {
97 parent.removeChild(&child);
98 }
99
100 void notifyChildOrderChanged(Node &parent, Node &child,
101 Node */*old_ref*/, Node *new_ref) override
102 {
103 parent.changeOrder(&child, new_ref);
104 }
105
106 void notifyAttributeChanged(Node &node, GQuark name,
107 Inkscape::Util::ptr_shared /*old_value*/,
108 Inkscape::Util::ptr_shared new_value) override
109 {
110 node.setAttribute(g_quark_to_string(name), new_value);
111 }
112
113 void notifyContentChanged(Node &node,
114 Inkscape::Util::ptr_shared /*old_value*/,
115 Inkscape::Util::ptr_shared new_value) override
116 {
117 node.setContent(new_value);
118 }
119
120 void notifyElementNameChanged(Node& node, GQuark /*old_value*/, GQuark new_value) override
121 {
122 node.setCodeUnsafe(new_value);
123 }
124};
125
126}
127
131) {
132 for ( Event const *action = log ; action ; action = action->next ) {
133 action->undoOne(observer);
134 }
135}
136
138{
142
143 EventTracker<SimpleEvent<Event::XML> > tracker("undo-log");
144
145 if (log) {
146 if (log->repr) {
147 g_assert(!log->repr->document()->inTransaction());
148 }
149 }
150
151 Inkscape::XML::undo_log_to_observer(log, LogPerformer::instance());
152}
153
156) const {
157 observer.notifyChildRemoved(*this->repr, *this->child, this->ref);
158}
159
162) const {
163 observer.notifyChildAdded(*this->repr, *this->child, this->ref);
164}
165
168) const {
169 observer.notifyAttributeChanged(*this->repr, this->key, this->newval, this->oldval);
170}
171
174) const {
175 observer.notifyContentChanged(*this->repr, this->newval, this->oldval);
176}
177
180) const {
181 observer.notifyChildOrderChanged(*this->repr, *this->child, this->newref, this->oldref);
182}
183
186) const {
187 observer.notifyElementNameChanged(*this->repr, this->new_name, this->old_name);
188}
189
193) {
194 std::vector<Inkscape::XML::Event const *> r;
195 while (log) {
196 r.push_back(log);
197 log = log->next;
198 }
199 for ( auto reversed = r.rbegin(); reversed != r.rend(); ++reversed ) {
200 (*reversed)->replayOne(observer);
201 }
202}
203
204void
206{
210
211 EventTracker<SimpleEvent<Event::XML> > tracker("replay-log");
212
213 if (log) {
214 if (log->repr->document()) {
215 g_assert(!log->repr->document()->inTransaction());
216 }
217 }
218
219 Inkscape::XML::replay_log_to_observer(log, LogPerformer::instance());
220}
221
224) const {
225 observer.notifyChildAdded(*this->repr, *this->child, this->ref);
226}
227
230) const {
231 observer.notifyChildRemoved(*this->repr, *this->child, this->ref);
232}
233
236) const {
237 observer.notifyAttributeChanged(*this->repr, this->key, this->oldval, this->newval);
238}
239
242) const {
243 observer.notifyContentChanged(*this->repr, this->oldval, this->newval);
244}
245
248) const {
249 observer.notifyChildOrderChanged(*this->repr, *this->child, this->oldref, this->newref);
250}
251
254) const {
255 observer.notifyElementNameChanged(*this->repr, this->old_name, this->new_name);
256}
257
260{
261 Inkscape::XML::Event *action;
262 Inkscape::XML::Event **prev_ptr;
263
264 if (!b) return a;
265 if (!a) return b;
266
267 /* find the earliest action in the second log */
268 /* (also noting the pointer that references it, so we can
269 * replace it later) */
270 prev_ptr = &b;
271 for ( action = b ; action->next ; action = action->next ) {
272 prev_ptr = &action->next;
273 }
274
275 /* add the first log after it */
276 action->next = a;
277
278 /* optimize the result */
279 *prev_ptr = action->optimizeOne();
280
281 return b;
282}
283
284void
286{
287 while (log) {
288 Inkscape::XML::Event *action;
289 action = log;
290 log = action->next;
291 delete action;
292 }
293}
294
295namespace {
296
297template <typename T> struct ActionRelations;
298
299template <>
300struct ActionRelations<Inkscape::XML::EventAdd> {
301 typedef Inkscape::XML::EventDel Opposite;
302};
303
304template <>
305struct ActionRelations<Inkscape::XML::EventDel> {
306 typedef Inkscape::XML::EventAdd Opposite;
307};
308
309template <typename A>
310Inkscape::XML::Event *cancel_add_or_remove(A *action) {
311 typedef typename ActionRelations<A>::Opposite Opposite;
312 Opposite *opposite=dynamic_cast<Opposite *>(action->next);
313
314 bool OK = false;
315 if (opposite){
316 if (opposite->repr == action->repr &&
317 opposite->child == action->child &&
318 opposite->ref == action->ref ) {
319 OK = true;
320 }
321 }
322 if (OK){
323 Inkscape::XML::Event *remaining=opposite->next;
324
325 delete opposite;
326 delete action;
327
328 return remaining;
329 } else {
330 return action;
331 }
332}
333}
334
336 return cancel_add_or_remove(this);
337}
338
340 return cancel_add_or_remove(this);
341}
342
344 Inkscape::XML::EventChgAttr *chg_attr=dynamic_cast<Inkscape::XML::EventChgAttr *>(this->next);
345
346 /* consecutive chgattrs on the same key can be combined */
347 if ( chg_attr) {
348 if ( chg_attr->repr == this->repr &&
349 chg_attr->key == this->key )
350 {
351 /* replace our oldval with the prior action's */
352 this->oldval = chg_attr->oldval;
353
354 /* discard the prior action */
355 this->next = chg_attr->next;
356 delete chg_attr;
357 }
358 }
359
360 return this;
361}
362
364 Inkscape::XML::EventChgContent *chg_content=dynamic_cast<Inkscape::XML::EventChgContent *>(this->next);
365
366 /* consecutive content changes can be combined */
367 if (chg_content) {
368 if (chg_content->repr == this->repr ) {
369 /* replace our oldval with the prior action's */
370 this->oldval = chg_content->oldval;
371
372 /* get rid of the prior action*/
373 this->next = chg_content->next;
374 delete chg_content;
375 }
376 }
377
378 return this;
379}
380
382 Inkscape::XML::EventChgOrder *chg_order=dynamic_cast<Inkscape::XML::EventChgOrder *>(this->next);
383
384 /* consecutive chgorders for the same child may be combined or
385 * canceled out */
386 bool OK = false;
387 if (chg_order) {
388 if (chg_order->repr == this->repr &&
389 chg_order->child == this->child ){
390 OK = true;
391 }
392 }
393 if (OK) {
394 if ( chg_order->oldref == this->newref ) {
395 /* cancel them out */
396 Inkscape::XML::Event *after=chg_order->next;
397
398 delete chg_order;
399 delete this;
400
401 return after;
402 } else {
403 /* combine them */
404 this->oldref = chg_order->oldref;
405
406 /* get rid of the other one */
407 this->next = chg_order->next;
408 delete chg_order;
409
410 return this;
411 }
412 } else {
413 return this;
414 }
415}
416
418 auto next_chg_element_name = dynamic_cast<Inkscape::XML::EventChgElementName*>(this->next);
419 if (next_chg_element_name && next_chg_element_name->repr == this->repr) {
420 // Combine name changes to the same element.
421 this->old_name = next_chg_element_name->old_name;
422 this->next = next_chg_element_name->next;
423 delete next_chg_element_name;
424 }
425 return this;
426}
427
428namespace {
429
430class LogPrinter : public Inkscape::XML::NodeObserver {
431public:
433
434 static LogPrinter &instance() {
435 static LogPrinter singleton;
436 return singleton;
437 }
438
439 static Glib::ustring node_to_string(Node const &node) {
440 Glib::ustring result;
441 char const *type_name=nullptr;
442 switch (node.type()) {
444 type_name = "Document";
445 break;
447 type_name = "Element";
448 break;
450 type_name = "Text";
451 break;
453 type_name = "Comment";
454 break;
455 default:
456 g_assert_not_reached();
457 }
458 char buffer[40];
459 result.append("#");
460 if (node.attribute("id")) {
461 result.append(node.attribute("id"));
462 }
463 result.append("<");
464 result.append(type_name);
465 result.append(":");
466 snprintf(buffer, 40, "0x%p", &node);
467 result.append(buffer);
468 result.append(">");
469
470 return result;
471 }
472
473 static Glib::ustring ref_to_string(Node *ref) {
474 if (ref) {
475 return node_to_string(*ref);
476 } else {
477 return "beginning";
478 }
479 }
480
481 void notifyChildAdded(Node &parent, Node &child, Node *ref) override {
482 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());
483 }
484
485 void notifyChildRemoved(Node &parent, Node &child, Node */*ref*/) override {
486 g_warning("Event: Removed %s from %s", node_to_string(parent).c_str(), node_to_string(child).c_str());
487 }
488
489 void notifyChildOrderChanged(Node &parent, Node &child,
490 Node */*old_ref*/, Node *new_ref) override
491 {
492 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());
493 }
494
495 void notifyAttributeChanged(Node &node, GQuark name,
496 Inkscape::Util::ptr_shared /*old_value*/,
497 Inkscape::Util::ptr_shared new_value) override
498 {
499 if (new_value) {
500 g_warning("Event: Set attribute %s to \"%s\" on %s", g_quark_to_string(name), new_value.pointer(), node_to_string(node).c_str());
501 } else {
502 g_warning("Event: Unset attribute %s on %s", g_quark_to_string(name), node_to_string(node).c_str());
503 }
504 }
505
506 void notifyContentChanged(Node &node,
507 Inkscape::Util::ptr_shared /*old_value*/,
508 Inkscape::Util::ptr_shared new_value) override
509 {
510 if (new_value) {
511 g_warning("Event: Set content of %s to \"%s\"", node_to_string(node).c_str(), new_value.pointer());
512 } else {
513 g_warning("Event: Unset content of %s", node_to_string(node).c_str());
514 }
515 }
516
517 void notifyElementNameChanged(Node& node, GQuark old_value, GQuark new_value) override
518 {
519 g_warning("Event: Changed name of %s from %s to %s\n",
520 node_to_string(node).c_str(), g_quark_to_string(old_value), g_quark_to_string(new_value));
521 }
522};
523
524}
525
529
char const * pointer() const
Definition share.h:33
Object representing child addition.
Definition event.h:124
void _undoOne(NodeObserver &observer) const override
Definition event.cpp:154
void _replayOne(NodeObserver &observer) const override
Definition event.cpp:222
Event * _optimizeOne() override
Definition event.cpp:335
Object representing attribute change.
Definition event.h:162
Inkscape::Util::ptr_shared oldval
Value of the attribute before the change.
Definition event.h:174
void _undoOne(NodeObserver &observer) const override
Definition event.cpp:166
GQuark key
GQuark corresponding to the changed attribute's name.
Definition event.h:172
Event * _optimizeOne() override
Definition event.cpp:343
void _replayOne(NodeObserver &observer) const override
Definition event.cpp:234
Object representing content change.
Definition event.h:187
Inkscape::Util::ptr_shared oldval
Content of the node before the change.
Definition event.h:196
void _undoOne(NodeObserver &observer) const override
Definition event.cpp:172
Event * _optimizeOne() override
Definition event.cpp:363
void _replayOne(NodeObserver &observer) const override
Definition event.cpp:240
Object representing element name change.
Definition event.h:231
void _replayOne(NodeObserver &observer) const override
Definition event.cpp:252
void _undoOne(NodeObserver &observer) const override
Definition event.cpp:184
Event * _optimizeOne() override
Definition event.cpp:417
GQuark old_name
GQuark corresponding to the old element name.
Definition event.h:237
Object representing child order change.
Definition event.h:209
Node * oldref
The node after which the relocated node was in the sibling order before the change,...
Definition event.h:218
Node * child
The node that was relocated in sibling order.
Definition event.h:216
void _replayOne(NodeObserver &observer) const override
Definition event.cpp:246
Event * _optimizeOne() override
Definition event.cpp:381
void _undoOne(NodeObserver &observer) const override
Definition event.cpp:178
Object representing child removal.
Definition event.h:143
void _replayOne(NodeObserver &observer) const override
Definition event.cpp:228
Event * _optimizeOne() override
Definition event.cpp:339
void _undoOne(NodeObserver &observer) const override
Definition event.cpp:160
Enumeration of all XML event types.
Definition event.h:53
Event * optimizeOne()
If possible, combine this event with the next to reduce memory use.
Definition event.h:90
static int _next_serial
Definition event.h:118
Event * next
Pointer to the next event in the event chain.
Definition event.h:63
Node * repr
Pointer to the node that was the object of the event.
Definition event.h:75
Interface for XML node observers.
virtual void notifyElementNameChanged(Node &node, GQuark old_name, GQuark new_name)
Element name change callback.
virtual void notifyAttributeChanged(Node &node, GQuark name, Util::ptr_shared old_value, Util::ptr_shared new_value)
Attribute change callback.
virtual void notifyContentChanged(Node &node, Util::ptr_shared old_content, Util::ptr_shared new_content)
Content change callback.
virtual void notifyChildOrderChanged(Node &node, Node &child, Node *old_prev, Node *new_prev)
Child order change callback.
virtual void notifyChildRemoved(Node &node, Node &child, Node *prev)
Child removal callback.
virtual void notifyChildAdded(Node &node, Node &child, Node *prev)
Child addition callback.
Interface for refcounted XML nodes.
Definition node.h:80
virtual void setCodeUnsafe(int code)=0
Set the integer GQuark code for the name of the node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual void setContent(char const *value)=0
Set the content of a text or comment node.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
virtual NodeType type() const =0
Get the type of the node.
NodeObserver const * observer
Css & result
static char const *const parent
Definition dir-util.cpp:70
TODO: insert short description here.
void sp_repr_replay_log(Inkscape::XML::Event *log)
Definition event.cpp:205
void sp_repr_debug_print_log(Inkscape::XML::Event const *log)
Definition event.cpp:526
Inkscape::XML::Event * sp_repr_coalesce_log(Inkscape::XML::Event *a, Inkscape::XML::Event *b)
Definition event.cpp:259
void sp_repr_free_log(Inkscape::XML::Event *log)
Definition event.cpp:285
void sp_repr_undo_log(Inkscape::XML::Event *log)
Definition event.cpp:137
Inkscape::XML::Event * sp_repr_commit_undoable(Inkscape::XML::Document *doc)
Definition event.cpp:69
void sp_repr_commit(Inkscape::XML::Document *doc)
Definition event.cpp:56
void sp_repr_rollback(Inkscape::XML::Document *doc)
Definition event.cpp:43
void sp_repr_begin_transaction(Inkscape::XML::Document *doc)
Definition event.cpp:30
Inkscape::XML::Node * node
void undo_log_to_observer(Event const *log, NodeObserver &observer)
Definition event.cpp:128
@ DOCUMENT_NODE
Top-level document node. Do not confuse with the root node.
@ COMMENT_NODE
Comment node, e.g. <!– some comment –>.
@ ELEMENT_NODE
Regular element node, e.g. <group />.
@ TEXT_NODE
Text node, e.g. "Some text" in <group>Some text</group> is represented by a text node.
void replay_log_to_observer(Event const *log, NodeObserver &observer)
Definition event.cpp:190
Helper class to stream background task notifications as a series of messages.
Interface for XML node observers.
static cairo_user_data_key_t key
Piecewise< SBasis > log(Interval in)
Definition pw-funcs.cpp:37
Ocnode * child[8]
Definition quantize.cpp:33
Ocnode ** ref
Definition quantize.cpp:32
guint32 GQuark
Interface for XML documents.
Definition document.h:43
virtual Event * commitUndoable()=0
Commit a transaction and store the events for later use.
virtual void commit()=0
Commit a transaction and discard change data.
virtual void rollback()=0
Restore the state of the document prior to the transaction.
virtual void beginTransaction()=0
Begin a transaction and start recording changes.
Glib::ustring name
Definition toolbars.cpp:55
Interface for XML documents.