summaryrefslogtreecommitdiff
path: root/libcaja-private/caja-undo-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcaja-private/caja-undo-manager.c')
-rw-r--r--libcaja-private/caja-undo-manager.c320
1 files changed, 320 insertions, 0 deletions
diff --git a/libcaja-private/caja-undo-manager.c b/libcaja-private/caja-undo-manager.c
new file mode 100644
index 00000000..35951898
--- /dev/null
+++ b/libcaja-private/caja-undo-manager.c
@@ -0,0 +1,320 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* CajaUndoManager - Undo/Redo transaction manager.
+ *
+ * Copyright (C) 2000 Eazel, Inc.
+ *
+ * Author: Gene Z. Ragan <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <libcaja-private/caja-undo-manager.h>
+#include <libcaja-private/caja-undo-transaction.h>
+
+#include <eel/eel-gtk-macros.h>
+#include <eel/eel-gtk-extensions.h>
+#include <gtk/gtk.h>
+#include "caja-undo-private.h"
+
+struct CajaUndoManagerDetails
+{
+ CajaUndoTransaction *transaction;
+
+ /* These are used to tell undo from redo. */
+ gboolean current_transaction_is_redo;
+ gboolean new_transaction_is_redo;
+
+ /* These are used only so that we can complain if we get more
+ * than one transaction inside undo.
+ */
+ gboolean undo_in_progress;
+ int num_transactions_during_undo;
+};
+
+enum
+{
+ CHANGED,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL];
+
+typedef struct
+{
+#ifdef UIH
+ MateComponentUIHandler *handler;
+#endif /* UIH */
+ char *path;
+ char *no_undo_menu_item_label;
+ char *no_undo_menu_item_hint;
+} UndoMenuHandlerConnection;
+
+G_DEFINE_TYPE (CajaUndoManager,
+ caja_undo_manager,
+ G_TYPE_OBJECT)
+
+static void
+release_transaction (CajaUndoManager *manager)
+{
+ CajaUndoTransaction *transaction;
+
+ transaction = manager->details->transaction;
+ manager->details->transaction = NULL;
+ if (transaction != NULL)
+ {
+ g_object_unref (transaction);
+ }
+}
+
+void
+caja_undo_manager_append (CajaUndoManager *manager,
+ CajaUndoTransaction *transaction)
+{
+ CajaUndoTransaction *duplicate_transaction;
+
+ /* Check, complain, and ignore the passed-in transaction if we
+ * get more than one within a single undo operation. The single
+ * transaction we get during the undo operation is supposed to
+ * be the one for redoing the undo (or re-undoing the redo).
+ */
+ if (manager->details->undo_in_progress)
+ {
+ manager->details->num_transactions_during_undo += 1;
+ g_return_if_fail (manager->details->num_transactions_during_undo == 1);
+ }
+
+ g_return_if_fail (transaction != NULL);
+
+ /* Keep a copy of this transaction (dump the old one). */
+ duplicate_transaction = g_object_ref (transaction);
+ release_transaction (manager);
+ manager->details->transaction = duplicate_transaction;
+ manager->details->current_transaction_is_redo =
+ manager->details->new_transaction_is_redo;
+
+ /* Fire off signal indicating that the undo state has changed. */
+ g_signal_emit (manager, signals[CHANGED], 0);
+}
+
+void
+caja_undo_manager_forget (CajaUndoManager *manager,
+ CajaUndoTransaction *transaction)
+{
+ /* Nothing to forget unless the item we are passed is the
+ * transaction we are currently holding.
+ */
+ if (transaction != manager->details->transaction)
+ {
+ return;
+ }
+
+ /* Get rid of the transaction we are holding on to. */
+ release_transaction (manager);
+
+ /* Fire off signal indicating that the undo state has changed. */
+ g_signal_emit (manager, signals[CHANGED], 0);
+}
+
+CajaUndoManager *
+caja_undo_manager_new (void)
+{
+ return CAJA_UNDO_MANAGER (g_object_new (caja_undo_manager_get_type (), NULL));
+}
+
+static void
+caja_undo_manager_init (CajaUndoManager *manager)
+{
+ manager->details = g_new0 (CajaUndoManagerDetails, 1);
+}
+
+void
+caja_undo_manager_undo (CajaUndoManager *manager)
+{
+ CajaUndoTransaction *transaction;
+
+ g_return_if_fail (CAJA_IS_UNDO_MANAGER (manager));
+
+ transaction = manager->details->transaction;
+ manager->details->transaction = NULL;
+ if (transaction != NULL)
+ {
+ /* Perform the undo. New transactions that come in
+ * during an undo are redo transactions. New
+ * transactions that come in during a redo are undo
+ * transactions. Transactions that come in outside
+ * are always undo and never redo.
+ */
+ manager->details->new_transaction_is_redo =
+ !manager->details->current_transaction_is_redo;
+ manager->details->undo_in_progress = TRUE;
+ manager->details->num_transactions_during_undo = 0;
+ caja_undo_transaction_undo (transaction);
+ manager->details->undo_in_progress = FALSE;
+ manager->details->new_transaction_is_redo = FALSE;
+
+ /* Let go of the transaction. */
+ g_object_unref (transaction);
+
+ /* Fire off signal indicating the undo state has changed. */
+ g_signal_emit (manager, signals[CHANGED], 0);
+ }
+}
+
+static void
+finalize (GObject *object)
+{
+ CajaUndoManager *manager;
+
+ manager = CAJA_UNDO_MANAGER (object);
+
+ release_transaction (manager);
+
+ g_free (manager->details);
+
+ if (G_OBJECT_CLASS (caja_undo_manager_parent_class)->finalize)
+ {
+ (* G_OBJECT_CLASS (caja_undo_manager_parent_class)->finalize) (object);
+ }
+}
+
+void
+caja_undo_manager_attach (CajaUndoManager *manager, GObject *target)
+{
+ g_return_if_fail (CAJA_IS_UNDO_MANAGER (manager));
+ g_return_if_fail (G_IS_OBJECT (target));
+
+ caja_undo_attach_undo_manager (G_OBJECT (target), manager);
+}
+
+#ifdef UIH
+static void
+update_undo_menu_item (CajaUndoManager *manager,
+ UndoMenuHandlerConnection *connection)
+{
+ CORBA_Environment ev;
+ Caja_Undo_MenuItem *menu_item;
+
+ g_assert (CAJA_IS_UNDO_MANAGER (manager));
+ g_assert (connection != NULL);
+ g_assert (MATECOMPONENT_IS_UI_HANDLER (connection->handler));
+ g_assert (connection->path != NULL);
+ g_assert (connection->no_undo_menu_item_label != NULL);
+ g_assert (connection->no_undo_menu_item_hint != NULL);
+
+ CORBA_exception_init (&ev);
+
+ if (CORBA_Object_is_nil (manager->details->transaction, &ev))
+ {
+ menu_item = NULL;
+ }
+ else
+ {
+ if (manager->details->current_transaction_is_redo)
+ {
+ menu_item = Caja_Undo_Transaction__get_redo_menu_item
+ (manager->details->transaction, &ev);
+ }
+ else
+ {
+ menu_item = Caja_Undo_Transaction__get_undo_menu_item
+ (manager->details->transaction, &ev);
+ }
+ }
+
+ matecomponent_ui_handler_menu_set_sensitivity
+ (connection->handler, connection->path,
+ menu_item != NULL);
+ matecomponent_ui_handler_menu_set_label
+ (connection->handler, connection->path,
+ menu_item == NULL
+ ? connection->no_undo_menu_item_label
+ : menu_item->label);
+ matecomponent_ui_handler_menu_set_hint
+ (connection->handler, connection->path,
+ menu_item == NULL
+ ? connection->no_undo_menu_item_hint
+ : menu_item->hint);
+
+ CORBA_free (menu_item);
+
+ CORBA_exception_free (&ev);
+}
+
+static void
+undo_menu_handler_connection_free (UndoMenuHandlerConnection *connection)
+{
+ g_assert (connection != NULL);
+ g_assert (MATECOMPONENT_IS_UI_HANDLER (connection->handler));
+ g_assert (connection->path != NULL);
+ g_assert (connection->no_undo_menu_item_label != NULL);
+ g_assert (connection->no_undo_menu_item_hint != NULL);
+
+ g_free (connection->path);
+ g_free (connection->no_undo_menu_item_label);
+ g_free (connection->no_undo_menu_item_hint);
+ g_free (connection);
+}
+
+static void
+undo_menu_handler_connection_free_cover (gpointer data)
+{
+ undo_menu_handler_connection_free (data);
+}
+
+void
+caja_undo_manager_set_up_matecomponent_ui_handler_undo_item (CajaUndoManager *manager,
+ MateComponentUIHandler *handler,
+ const char *path,
+ const char *no_undo_menu_item_label,
+ const char *no_undo_menu_item_hint)
+{
+ UndoMenuHandlerConnection *connection;
+
+ connection = g_new (UndoMenuHandlerConnection, 1);
+ connection->handler = handler;
+ connection->path = g_strdup (path);
+ connection->no_undo_menu_item_label = g_strdup (no_undo_menu_item_label);
+ connection->no_undo_menu_item_hint = g_strdup (no_undo_menu_item_hint);
+
+ /* Set initial state of menu item. */
+ update_undo_menu_item (manager, connection);
+
+ /* Update it again whenever the changed signal is emitted. */
+ eel_gtk_signal_connect_full_while_alive
+ (GTK_OBJECT (manager), "changed",
+ G_CALLBACK (update_undo_menu_item), NULL,
+ connection, undo_menu_handler_connection_free_cover,
+ FALSE, FALSE,
+ GTK_OBJECT (handler));
+}
+#endif /* UIH */
+
+static void
+caja_undo_manager_class_init (CajaUndoManagerClass *class)
+{
+ G_OBJECT_CLASS (class)->finalize = finalize;
+
+ signals[CHANGED] = g_signal_new
+ ("changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CajaUndoManagerClass,
+ changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}