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.
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
;
53 static guint signals
[LAST_SIGNAL
];
57 BonoboUIHandler
*handler
;
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
,
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
);
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);
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
) {
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
));
134 nautilus_undo_manager_init (NautilusUndoManager
*manager
)
136 manager
->details
= g_new0 (NautilusUndoManagerDetails
, 1);
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);
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
);
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
);
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
)) {
216 if (manager
->details
->current_transaction_is_redo
) {
217 menu_item
= Nautilus_Undo_Transaction__get_redo_menu_item
218 (manager
->details
->transaction
, &ev
);
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
,
228 bonobo_ui_handler_menu_set_label
229 (connection
->handler
, connection
->path
,
231 ? connection
->no_undo_menu_item_label
233 bonobo_ui_handler_menu_set_hint
234 (connection
->handler
, connection
->path
,
236 ? connection
->no_undo_menu_item_hint
239 CORBA_free (menu_item
);
241 CORBA_exception_free (&ev
);
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
);
260 undo_menu_handler_connection_free_cover (gpointer data
)
262 undo_menu_handler_connection_free (data
);
266 nautilus_undo_manager_set_up_bonobo_ui_handler_undo_item (NautilusUndoManager
*manager
,
267 BonoboUIHandler
*handler
,
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
,
289 GTK_OBJECT (handler
));
294 nautilus_undo_manager_class_init (NautilusUndoManagerClass
*class)
296 G_OBJECT_CLASS (class)->finalize
= finalize
;
298 signals
[CHANGED
] = g_signal_new
300 G_TYPE_FROM_CLASS (class),
302 G_STRUCT_OFFSET (NautilusUndoManagerClass
,
305 g_cclosure_marshal_VOID__VOID
,