2008-02-29 Cosimo Cecchi <cosimoc@gnome.org>
[nautilus.git] / libnautilus-private / nautilus-undo-manager.c
blob2be72abc0e3fb0cef537058b7023a88799d8003d
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /* NautilusUndoManager - Undo/Redo transaction manager.
5 * Copyright (C) 2000 Eazel, Inc.
7 * Author: Gene Z. Ragan <gzr@eazel.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
25 #include <config.h>
26 #include <libnautilus-private/nautilus-undo-manager.h>
27 #include <libnautilus-private/nautilus-undo-transaction.h>
29 #include <eel/eel-gtk-macros.h>
30 #include <eel/eel-gtk-extensions.h>
31 #include <gtk/gtksignal.h>
32 #include <bonobo/bonobo-main.h>
33 #include "nautilus-undo-private.h"
35 struct NautilusUndoManagerDetails {
36 NautilusUndoTransaction *transaction;
38 /* These are used to tell undo from redo. */
39 gboolean current_transaction_is_redo;
40 gboolean new_transaction_is_redo;
42 /* These are used only so that we can complain if we get more
43 * than one transaction inside undo.
45 gboolean undo_in_progress;
46 int num_transactions_during_undo;
49 enum {
50 CHANGED,
51 LAST_SIGNAL
53 static guint signals[LAST_SIGNAL];
55 typedef struct {
56 #ifdef UIH
57 BonoboUIHandler *handler;
58 #endif /* UIH */
59 char *path;
60 char *no_undo_menu_item_label;
61 char *no_undo_menu_item_hint;
62 } UndoMenuHandlerConnection;
64 G_DEFINE_TYPE (NautilusUndoManager,
65 nautilus_undo_manager,
66 G_TYPE_OBJECT)
68 static void
69 release_transaction (NautilusUndoManager *manager)
71 NautilusUndoTransaction *transaction;
73 transaction = manager->details->transaction;
74 manager->details->transaction = NULL;
75 if (transaction != NULL) {
76 g_object_unref (transaction);
80 void
81 nautilus_undo_manager_append (NautilusUndoManager *manager,
82 NautilusUndoTransaction *transaction)
84 NautilusUndoTransaction *duplicate_transaction;
86 /* Check, complain, and ignore the passed-in transaction if we
87 * get more than one within a single undo operation. The single
88 * transaction we get during the undo operation is supposed to
89 * be the one for redoing the undo (or re-undoing the redo).
91 if (manager->details->undo_in_progress) {
92 manager->details->num_transactions_during_undo += 1;
93 g_return_if_fail (manager->details->num_transactions_during_undo == 1);
96 g_return_if_fail (transaction != NULL);
98 /* Keep a copy of this transaction (dump the old one). */
99 duplicate_transaction = g_object_ref (transaction);
100 release_transaction (manager);
101 manager->details->transaction = duplicate_transaction;
102 manager->details->current_transaction_is_redo =
103 manager->details->new_transaction_is_redo;
105 /* Fire off signal indicating that the undo state has changed. */
106 g_signal_emit (manager, signals[CHANGED], 0);
109 void
110 nautilus_undo_manager_forget (NautilusUndoManager *manager,
111 NautilusUndoTransaction *transaction)
113 /* Nothing to forget unless the item we are passed is the
114 * transaction we are currently holding.
116 if (transaction != manager->details->transaction) {
117 return;
120 /* Get rid of the transaction we are holding on to. */
121 release_transaction (manager);
123 /* Fire off signal indicating that the undo state has changed. */
124 g_signal_emit (manager, signals[CHANGED], 0);
127 NautilusUndoManager *
128 nautilus_undo_manager_new (void)
130 return NAUTILUS_UNDO_MANAGER (g_object_new (nautilus_undo_manager_get_type (), NULL));
133 static void
134 nautilus_undo_manager_init (NautilusUndoManager *manager)
136 manager->details = g_new0 (NautilusUndoManagerDetails, 1);
139 void
140 nautilus_undo_manager_undo (NautilusUndoManager *manager)
142 NautilusUndoTransaction *transaction;
144 g_return_if_fail (NAUTILUS_IS_UNDO_MANAGER (manager));
146 transaction = manager->details->transaction;
147 manager->details->transaction = NULL;
148 if (transaction != NULL) {
149 /* Perform the undo. New transactions that come in
150 * during an undo are redo transactions. New
151 * transactions that come in during a redo are undo
152 * transactions. Transactions that come in outside
153 * are always undo and never redo.
155 manager->details->new_transaction_is_redo =
156 !manager->details->current_transaction_is_redo;
157 manager->details->undo_in_progress = TRUE;
158 manager->details->num_transactions_during_undo = 0;
159 nautilus_undo_transaction_undo (transaction);
160 manager->details->undo_in_progress = FALSE;
161 manager->details->new_transaction_is_redo = FALSE;
163 /* Let go of the transaction. */
164 g_object_unref (transaction);
166 /* Fire off signal indicating the undo state has changed. */
167 g_signal_emit (manager, signals[CHANGED], 0);
171 static void
172 finalize (GObject *object)
174 NautilusUndoManager *manager;
176 manager = NAUTILUS_UNDO_MANAGER (object);
178 release_transaction (manager);
180 g_free (manager->details);
182 if (G_OBJECT_CLASS (nautilus_undo_manager_parent_class)->finalize) {
183 (* G_OBJECT_CLASS (nautilus_undo_manager_parent_class)->finalize) (object);
187 void
188 nautilus_undo_manager_attach (NautilusUndoManager *manager, GObject *target)
190 g_return_if_fail (NAUTILUS_IS_UNDO_MANAGER (manager));
191 g_return_if_fail (G_IS_OBJECT (target));
193 nautilus_undo_attach_undo_manager (G_OBJECT (target), manager);
196 #ifdef UIH
197 static void
198 update_undo_menu_item (NautilusUndoManager *manager,
199 UndoMenuHandlerConnection *connection)
201 CORBA_Environment ev;
202 Nautilus_Undo_MenuItem *menu_item;
204 g_assert (NAUTILUS_IS_UNDO_MANAGER (manager));
205 g_assert (connection != NULL);
206 g_assert (BONOBO_IS_UI_HANDLER (connection->handler));
207 g_assert (connection->path != NULL);
208 g_assert (connection->no_undo_menu_item_label != NULL);
209 g_assert (connection->no_undo_menu_item_hint != NULL);
211 CORBA_exception_init (&ev);
213 if (CORBA_Object_is_nil (manager->details->transaction, &ev)) {
214 menu_item = NULL;
215 } else {
216 if (manager->details->current_transaction_is_redo) {
217 menu_item = Nautilus_Undo_Transaction__get_redo_menu_item
218 (manager->details->transaction, &ev);
219 } else {
220 menu_item = Nautilus_Undo_Transaction__get_undo_menu_item
221 (manager->details->transaction, &ev);
225 bonobo_ui_handler_menu_set_sensitivity
226 (connection->handler, connection->path,
227 menu_item != NULL);
228 bonobo_ui_handler_menu_set_label
229 (connection->handler, connection->path,
230 menu_item == NULL
231 ? connection->no_undo_menu_item_label
232 : menu_item->label);
233 bonobo_ui_handler_menu_set_hint
234 (connection->handler, connection->path,
235 menu_item == NULL
236 ? connection->no_undo_menu_item_hint
237 : menu_item->hint);
239 CORBA_free (menu_item);
241 CORBA_exception_free (&ev);
244 static void
245 undo_menu_handler_connection_free (UndoMenuHandlerConnection *connection)
247 g_assert (connection != NULL);
248 g_assert (BONOBO_IS_UI_HANDLER (connection->handler));
249 g_assert (connection->path != NULL);
250 g_assert (connection->no_undo_menu_item_label != NULL);
251 g_assert (connection->no_undo_menu_item_hint != NULL);
253 g_free (connection->path);
254 g_free (connection->no_undo_menu_item_label);
255 g_free (connection->no_undo_menu_item_hint);
256 g_free (connection);
259 static void
260 undo_menu_handler_connection_free_cover (gpointer data)
262 undo_menu_handler_connection_free (data);
265 void
266 nautilus_undo_manager_set_up_bonobo_ui_handler_undo_item (NautilusUndoManager *manager,
267 BonoboUIHandler *handler,
268 const char *path,
269 const char *no_undo_menu_item_label,
270 const char *no_undo_menu_item_hint)
272 UndoMenuHandlerConnection *connection;
274 connection = g_new (UndoMenuHandlerConnection, 1);
275 connection->handler = handler;
276 connection->path = g_strdup (path);
277 connection->no_undo_menu_item_label = g_strdup (no_undo_menu_item_label);
278 connection->no_undo_menu_item_hint = g_strdup (no_undo_menu_item_hint);
280 /* Set initial state of menu item. */
281 update_undo_menu_item (manager, connection);
283 /* Update it again whenever the changed signal is emitted. */
284 eel_gtk_signal_connect_full_while_alive
285 (GTK_OBJECT (manager), "changed",
286 G_CALLBACK (update_undo_menu_item), NULL,
287 connection, undo_menu_handler_connection_free_cover,
288 FALSE, FALSE,
289 GTK_OBJECT (handler));
291 #endif /* UIH */
293 static void
294 nautilus_undo_manager_class_init (NautilusUndoManagerClass *class)
296 G_OBJECT_CLASS (class)->finalize = finalize;
298 signals[CHANGED] = g_signal_new
299 ("changed",
300 G_TYPE_FROM_CLASS (class),
301 G_SIGNAL_RUN_LAST,
302 G_STRUCT_OFFSET (NautilusUndoManagerClass,
303 changed),
304 NULL, NULL,
305 g_cclosure_marshal_VOID__VOID,
306 G_TYPE_NONE, 0);