diff options
| author | MenTaLguY <mental@rydia.net> | 2006-01-16 02:36:01 +0000 |
|---|---|---|
| committer | mental <mental@users.sourceforge.net> | 2006-01-16 02:36:01 +0000 |
| commit | 179fa413b047bede6e32109e2ce82437c5fb8d34 (patch) | |
| tree | a5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/document-undo.cpp | |
| download | inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip | |
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/document-undo.cpp')
| -rw-r--r-- | src/document-undo.cpp | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/src/document-undo.cpp b/src/document-undo.cpp new file mode 100644 index 000000000..226d38127 --- /dev/null +++ b/src/document-undo.cpp @@ -0,0 +1,322 @@ +#define __SP_DOCUMENT_UNDO_C__ + +/** \file + * Undo/Redo stack implementation + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * MenTaLguY <mental@rydia.net> + * + * Copyright (C) 1999-2003 authors + * Copyright (C) 2001-2002 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + * + * Using the split document model gives sodipodi a very simple and clean + * undo implementation. Whenever mutation occurs in the XML tree, + * SPObject invokes one of the five corresponding handlers of its + * container document. This writes down a generic description of the + * given action, and appends it to the recent action list, kept by the + * document. There will be as many action records as there are mutation + * events, which are all kept and processed together in the undo + * stack. Two methods exist to indicate that the given action is completed: + * + * \verbatim + void sp_document_done (SPDocument *document) + void sp_document_maybe_done (SPDocument *document, const unsigned char *key) \endverbatim + * + * Both move the recent action list into the undo stack and clear the + * list afterwards. While the first method does an unconditional push, + * the second one first checks the key of the most recent stack entry. If + * the keys are identical, the current action list is appended to the + * existing stack entry, instead of pushing it onto its own. This + * behaviour can be used to collect multi-step actions (like winding the + * Gtk spinbutton) from the UI into a single undoable step. + * + * For controls implemented by Sodipodi itself, implementing undo as a + * single step is usually done in a more efficent way. Most controls have + * the abstract model of grab, drag, release, and change user + * action. During the grab phase, all modifications are done to the + * SPObject directly - i.e. they do not change XML tree, and thus do not + * generate undo actions either. Only at the release phase (normally + * associated with releasing the mousebutton), changes are written back + * to the XML tree, thus generating only a single set of undo actions. + * (Lauris Kaplinski) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + + +#if HAVE_STRING_H +#endif + + +#if HAVE_STDLIB_H +#endif + +#include "xml/repr.h" +#include "document-private.h" +#include "inkscape.h" +#include "debug/event-tracker.h" +#include "debug/simple-event.h" + + +/* + * Undo & redo + */ +/** + * Set undo sensitivity. + * + * \note + * Since undo sensitivity needs to be nested, setting undo sensitivity + * should be done like this: + *\verbatim + gboolean saved = sp_document_get_undo_sensitive(document); + sp_document_set_undo_sensitive(document, FALSE); + ... do stuff ... + sp_document_set_undo_sensitive(document, saved); \endverbatim + */ +void +sp_document_set_undo_sensitive (SPDocument *doc, gboolean sensitive) +{ + g_assert (doc != NULL); + g_assert (doc->priv != NULL); + + if ( !(sensitive) == !(doc->priv->sensitive) ) + return; + + if (sensitive) { + sp_repr_begin_transaction (doc->rdoc); + } else { + doc->priv->partial = sp_repr_coalesce_log ( + doc->priv->partial, + sp_repr_commit_undoable (doc->rdoc) + ); + } + + doc->priv->sensitive = !!sensitive; +} + +gboolean sp_document_get_undo_sensitive(SPDocument const *document) { + g_assert(document != NULL); + g_assert(document->priv != NULL); + + return document->priv->sensitive; +} + +void +sp_document_done (SPDocument *doc) +{ + sp_document_maybe_done (doc, NULL); +} + +void +sp_document_reset_key (Inkscape::Application *inkscape, SPDesktop *desktop, GtkObject *base) +{ + SPDocument *doc = (SPDocument *) base; + doc->actionkey = NULL; +} + +void +sp_document_maybe_done (SPDocument *doc, const gchar *key) +{ + g_assert (doc != NULL); + g_assert (doc->priv != NULL); + g_assert (doc->priv->sensitive); + + doc->collectOrphans(); + + sp_document_ensure_up_to_date (doc); + + sp_document_clear_redo (doc); + + Inkscape::XML::Event *log = sp_repr_coalesce_log (doc->priv->partial, sp_repr_commit_undoable (doc->rdoc)); + doc->priv->partial = NULL; + + if (!log) { + sp_repr_begin_transaction (doc->rdoc); + return; + } + + if (key && doc->actionkey && !strcmp (key, doc->actionkey) && doc->priv->undo) { + doc->priv->undo->data = sp_repr_coalesce_log ((Inkscape::XML::Event *)doc->priv->undo->data, log); + } else { + doc->priv->undo = g_slist_prepend (doc->priv->undo, log); + doc->priv->history_size++; + doc->priv->undoStackObservers.notifyUndoCommitEvent(log); + } + + doc->actionkey = key; + + doc->virgin = FALSE; + if (!doc->rroot->attribute("sodipodi:modified")) { + doc->rroot->setAttribute("sodipodi:modified", "true"); + } + + sp_repr_begin_transaction (doc->rdoc); +} + +void +sp_document_cancel (SPDocument *doc) +{ + g_assert (doc != NULL); + g_assert (doc->priv != NULL); + g_assert (doc->priv->sensitive); + + sp_repr_rollback (doc->rdoc); + + if (doc->priv->partial) { + sp_repr_undo_log (doc->priv->partial); + sp_repr_free_log (doc->priv->partial); + doc->priv->partial = NULL; + } + + sp_repr_begin_transaction (doc->rdoc); +} + +namespace { + +void finish_incomplete_transaction(SPDocument &doc) { + SPDocumentPrivate &priv=*doc.priv; + Inkscape::XML::Event *log=sp_repr_commit_undoable(doc.rdoc); + if (log || priv.partial) { + g_warning ("Incomplete undo transaction:"); + priv.partial = sp_repr_coalesce_log(priv.partial, log); + sp_repr_debug_print_log(priv.partial); + priv.undo = g_slist_prepend(priv.undo, priv.partial); + priv.partial = NULL; + } +} + +} + +gboolean +sp_document_undo (SPDocument *doc) +{ + using Inkscape::Debug::EventTracker; + using Inkscape::Debug::SimpleEvent; + + gboolean ret; + + EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("undo"); + + g_assert (doc != NULL); + g_assert (doc->priv != NULL); + g_assert (doc->priv->sensitive); + + doc->priv->sensitive = FALSE; + + doc->actionkey = NULL; + + finish_incomplete_transaction(*doc); + + if (doc->priv->undo) { + Inkscape::XML::Event *log=(Inkscape::XML::Event *)doc->priv->undo->data; + doc->priv->undo = g_slist_remove (doc->priv->undo, log); + sp_repr_undo_log (log); + doc->priv->redo = g_slist_prepend (doc->priv->redo, log); + + doc->rroot->setAttribute("sodipodi:modified", "true"); + doc->priv->undoStackObservers.notifyUndoEvent(log); + + ret = TRUE; + } else { + ret = FALSE; + } + + sp_repr_begin_transaction (doc->rdoc); + + doc->priv->sensitive = TRUE; + + if (ret) + inkscape_external_change(); + + return ret; +} + +gboolean +sp_document_redo (SPDocument *doc) +{ + using Inkscape::Debug::EventTracker; + using Inkscape::Debug::SimpleEvent; + + gboolean ret; + + EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("redo"); + + g_assert (doc != NULL); + g_assert (doc->priv != NULL); + g_assert (doc->priv->sensitive); + + doc->priv->sensitive = FALSE; + + doc->actionkey = NULL; + + finish_incomplete_transaction(*doc); + + if (doc->priv->redo) { + Inkscape::XML::Event *log=(Inkscape::XML::Event *)doc->priv->redo->data; + doc->priv->redo = g_slist_remove (doc->priv->redo, log); + sp_repr_replay_log (log); + doc->priv->undo = g_slist_prepend (doc->priv->undo, log); + + doc->rroot->setAttribute("sodipodi:modified", "true"); + doc->priv->undoStackObservers.notifyRedoEvent(log); + + ret = TRUE; + } else { + ret = FALSE; + } + + sp_repr_begin_transaction (doc->rdoc); + + doc->priv->sensitive = TRUE; + + if (ret) + inkscape_external_change(); + + return ret; +} + +void +sp_document_clear_undo (SPDocument *doc) +{ + while (doc->priv->undo) { + GSList *current; + + current = doc->priv->undo; + doc->priv->undo = current->next; + doc->priv->history_size--; + + sp_repr_free_log ((Inkscape::XML::Event *)current->data); + g_slist_free_1 (current); + } +} + +void +sp_document_clear_redo (SPDocument *doc) +{ + while (doc->priv->redo) { + GSList *current; + + current = doc->priv->redo; + doc->priv->redo = current->next; + doc->priv->history_size--; + + sp_repr_free_log ((Inkscape::XML::Event *)current->data); + g_slist_free_1 (current); + } +} +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
