Code

Ok, committed msgloan's patch to convert gbooleans to bools thus completing
[inkscape.git] / src / document-undo.cpp
1 #define __SP_DOCUMENT_UNDO_C__
3 /** \file
4  * Undo/Redo stack implementation
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   MenTaLguY <mental@rydia.net>
9  *
10  * Copyright (C) 1999-2003 authors
11  * Copyright (C) 2001-2002 Ximian, Inc.
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  *
15  * Using the split document model gives sodipodi a very simple and clean
16  * undo implementation. Whenever mutation occurs in the XML tree,
17  * SPObject invokes one of the five corresponding handlers of its
18  * container document. This writes down a generic description of the
19  * given action, and appends it to the recent action list, kept by the
20  * document. There will be as many action records as there are mutation
21  * events, which are all kept and processed together in the undo
22  * stack. Two methods exist to indicate that the given action is completed:
23  *
24  * \verbatim
25    void sp_document_done (SPDocument *document);
26    void sp_document_maybe_done (SPDocument *document, const unsigned char *key) \endverbatim
27  *
28  * Both move the recent action list into the undo stack and clear the
29  * list afterwards.  While the first method does an unconditional push,
30  * the second one first checks the key of the most recent stack entry. If
31  * the keys are identical, the current action list is appended to the
32  * existing stack entry, instead of pushing it onto its own.  This
33  * behaviour can be used to collect multi-step actions (like winding the
34  * Gtk spinbutton) from the UI into a single undoable step.
35  *
36  * For controls implemented by Sodipodi itself, implementing undo as a
37  * single step is usually done in a more efficent way. Most controls have
38  * the abstract model of grab, drag, release, and change user
39  * action. During the grab phase, all modifications are done to the
40  * SPObject directly - i.e. they do not change XML tree, and thus do not
41  * generate undo actions either.  Only at the release phase (normally
42  * associated with releasing the mousebutton), changes are written back
43  * to the XML tree, thus generating only a single set of undo actions.
44  * (Lauris Kaplinski)
45  */
47 #ifdef HAVE_CONFIG_H
48 # include "config.h"
49 #endif
53 #if HAVE_STRING_H
54 #endif
57 #if HAVE_STDLIB_H
58 #endif
60 #include "xml/repr.h"
61 #include "document-private.h"
62 #include "inkscape.h"
63 #include "debug/event-tracker.h"
64 #include "debug/simple-event.h"
65 #include "event.h"
68 /*
69  * Undo & redo
70  */
71 /**
72  * Set undo sensitivity.
73  *
74  * \note
75  *   Since undo sensitivity needs to be nested, setting undo sensitivity
76  *   should be done like this:
77  *\verbatim
78         bool saved = sp_document_get_undo_sensitive(document);
79         sp_document_set_undo_sensitive(document, FALSE);
80         ... do stuff ...
81         sp_document_set_undo_sensitive(document, saved);  \endverbatim
82  */
83 void
84 sp_document_set_undo_sensitive (SPDocument *doc, bool sensitive)
85 {
86         g_assert (doc != NULL);
87         g_assert (doc->priv != NULL);
89         if ( !(sensitive) == !(doc->priv->sensitive) )
90                 return;
92         if (sensitive) {
93                 sp_repr_begin_transaction (doc->rdoc);
94         } else {
95                 doc->priv->partial = sp_repr_coalesce_log (
96                         doc->priv->partial,
97                         sp_repr_commit_undoable (doc->rdoc)
98                 );
99         }
101         doc->priv->sensitive = !!sensitive;
104 bool sp_document_get_undo_sensitive(SPDocument const *document) {
105         g_assert(document != NULL);
106         g_assert(document->priv != NULL);
108         return document->priv->sensitive;
111 void
112 sp_document_done (SPDocument *doc, const unsigned int event_type, Glib::ustring event_description)
114         sp_document_maybe_done (doc, NULL, event_type, event_description);
117 void
118 sp_document_reset_key (Inkscape::Application *inkscape, SPDesktop *desktop, GtkObject *base)
120         SPDocument *doc = (SPDocument *) base;
121         doc->actionkey = NULL;
124 void
125 sp_document_maybe_done (SPDocument *doc, const gchar *key, const unsigned int event_type,
126                         Glib::ustring event_description)
128         g_assert (doc != NULL);
129         g_assert (doc->priv != NULL);
130         g_assert (doc->priv->sensitive);
132         doc->collectOrphans();
134         sp_document_ensure_up_to_date (doc);
136         sp_document_clear_redo (doc);
138         Inkscape::XML::Event *log = sp_repr_coalesce_log (doc->priv->partial, sp_repr_commit_undoable (doc->rdoc));
139         doc->priv->partial = NULL;
141         if (!log) {
142                 sp_repr_begin_transaction (doc->rdoc);
143                 return;
144         }
146         if (key && doc->actionkey && !strcmp (key, doc->actionkey) && doc->priv->undo) {
147                 doc->priv->undo->data = 
148                     new Inkscape::Event(sp_repr_coalesce_log (((Inkscape::Event *)
149                                                                doc->priv->undo->data)->event, log));
150         } else {
151                 Inkscape::Event *event = new Inkscape::Event(log, event_type, event_description);
152                 doc->priv->undo = g_slist_prepend (doc->priv->undo, event);
153                 doc->priv->history_size++;
154                 doc->priv->undoStackObservers.notifyUndoCommitEvent(event);
155         }
157         doc->actionkey = key;
159         doc->virgin = FALSE;
160         if (!doc->rroot->attribute("sodipodi:modified")) {
161                 doc->rroot->setAttribute("sodipodi:modified", "true");
162         }
164         sp_repr_begin_transaction (doc->rdoc);
166   doc->priv->commit_signal.emit();
169 void
170 sp_document_cancel (SPDocument *doc)
172         g_assert (doc != NULL);
173         g_assert (doc->priv != NULL);
174         g_assert (doc->priv->sensitive);
176         sp_repr_rollback (doc->rdoc);
178         if (doc->priv->partial) {
179                 sp_repr_undo_log (doc->priv->partial);
180                 sp_repr_free_log (doc->priv->partial);
181                 doc->priv->partial = NULL;
182         }
184         sp_repr_begin_transaction (doc->rdoc);
187 namespace {
189 void finish_incomplete_transaction(SPDocument &doc) {
190         SPDocumentPrivate &priv=*doc.priv;
191         Inkscape::XML::Event *log=sp_repr_commit_undoable(doc.rdoc);
192         if (log || priv.partial) {
193                 g_warning ("Incomplete undo transaction:");
194                 priv.partial = sp_repr_coalesce_log(priv.partial, log);
195                 sp_repr_debug_print_log(priv.partial);
196                 priv.undo = g_slist_prepend(priv.undo, priv.partial);
197                 priv.partial = NULL;
198         }
203 bool
204 sp_document_undo (SPDocument *doc)
206         using Inkscape::Debug::EventTracker;
207         using Inkscape::Debug::SimpleEvent;
209         bool ret;
211         EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("undo");
213         g_assert (doc != NULL);
214         g_assert (doc->priv != NULL);
215         g_assert (doc->priv->sensitive);
217         doc->priv->sensitive = FALSE;
219         doc->actionkey = NULL;
221         finish_incomplete_transaction(*doc);
223         if (doc->priv->undo) {
224                 Inkscape::Event *log=(Inkscape::Event *)doc->priv->undo->data;
225                 doc->priv->undo = g_slist_remove (doc->priv->undo, log);
226                 sp_repr_undo_log (log->event);
227                 doc->priv->redo = g_slist_prepend (doc->priv->redo, log);
229                 doc->rroot->setAttribute("sodipodi:modified", "true");
230                 doc->priv->undoStackObservers.notifyUndoEvent(log);
232                 ret = TRUE;
233         } else {
234                 ret = FALSE;
235         }
237         sp_repr_begin_transaction (doc->rdoc);
239         doc->priv->sensitive = TRUE;
241         if (ret)
242                 inkscape_external_change();
244         return ret;
247 bool
248 sp_document_redo (SPDocument *doc)
250         using Inkscape::Debug::EventTracker;
251         using Inkscape::Debug::SimpleEvent;
253         bool ret;
255         EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("redo");
257         g_assert (doc != NULL);
258         g_assert (doc->priv != NULL);
259         g_assert (doc->priv->sensitive);
261         doc->priv->sensitive = FALSE;
263         doc->actionkey = NULL;
265         finish_incomplete_transaction(*doc);
267         if (doc->priv->redo) {
268                 Inkscape::Event *log=(Inkscape::Event *)doc->priv->redo->data;
269                 doc->priv->redo = g_slist_remove (doc->priv->redo, log);
270                 sp_repr_replay_log (log->event);
271                 doc->priv->undo = g_slist_prepend (doc->priv->undo, log);
273                 doc->rroot->setAttribute("sodipodi:modified", "true");
274                 doc->priv->undoStackObservers.notifyRedoEvent(log);
276                 ret = TRUE;
277         } else {
278                 ret = FALSE;
279         }
281         sp_repr_begin_transaction (doc->rdoc);
283         doc->priv->sensitive = TRUE;
285         if (ret)
286                 inkscape_external_change();
288         return ret;
291 void
292 sp_document_clear_undo (SPDocument *doc)
294         while (doc->priv->undo) {
295                 GSList *current;
297                 current = doc->priv->undo;
298                 doc->priv->undo = current->next;
299                 doc->priv->history_size--;
301                 delete ((Inkscape::Event *) current->data);
302                 g_slist_free_1 (current);
303         }
306 void
307 sp_document_clear_redo (SPDocument *doc)
309         while (doc->priv->redo) {
310                 GSList *current;
312                 current = doc->priv->redo;
313                 doc->priv->redo = current->next;
314                 doc->priv->history_size--;
316                 delete ((Inkscape::Event *) current->data);
317                 g_slist_free_1 (current);
318         }
320 /*
321   Local Variables:
322   mode:c++
323   c-file-style:"stroustrup"
324   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
325   indent-tabs-mode:nil
326   fill-column:99
327   End:
328 */
329 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :