nautilus-undo-manager.c 10.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

/* NautilusUndoManager - Undo/Redo transaction manager.
 *
 * Copyright (C) 2000 Eazel, Inc.
 *
 * Author: Gene Z. Ragan <gzr@eazel.com>
 *
 * 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>
26 27
#include "nautilus-undo-manager.h"

Ramiro Estrugo's avatar
Ramiro Estrugo committed
28 29
#include <eel/eel-gtk-macros.h>
#include <eel/eel-gtk-extensions.h>
30
#include <gtk/gtksignal.h>
31
#include <bonobo/bonobo-main.h>
32 33 34 35
#include <libnautilus/nautilus-undo-private.h>
#include "nautilus-undo-context.h"

struct NautilusUndoManagerDetails {
36 37 38 39 40 41 42 43 44 45 46
	Nautilus_Undo_Transaction 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;
47
};
48

49
enum {
50
	CHANGED,
51 52 53 54
	LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];

55
typedef struct {
56
#ifdef UIH
57
	BonoboUIHandler *handler;
58
#endif /* UIH */
59 60 61 62 63
	char *path;
	char *no_undo_menu_item_label;
	char *no_undo_menu_item_hint;
} UndoMenuHandlerConnection;

64
/* GtkObject */
65 66
static void nautilus_undo_manager_class_init (NautilusUndoManagerClass  *class);
static void nautilus_undo_manager_init       (NautilusUndoManager       *item);
67

68
EEL_BONOBO_BOILERPLATE_FULL (NautilusUndoManager,
69 70 71
			       Nautilus_Undo_Manager,
			       nautilus_undo_manager,
			       BONOBO_OBJECT_TYPE)
72

Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
73
static void
74 75 76 77 78 79 80 81 82 83 84
release_transaction (NautilusUndoManager *manager)
{
	Nautilus_Undo_Transaction transaction;

	CORBA_Environment ev;
	
	CORBA_exception_init (&ev);

	transaction = manager->details->transaction;
	manager->details->transaction = CORBA_OBJECT_NIL;
	if (!CORBA_Object_is_nil (transaction, &ev)) {
85
		bonobo_object_release_unref (transaction, &ev);
86 87 88 89 90 91 92 93 94
	}

	CORBA_exception_free (&ev);
}

static void
corba_append (PortableServer_Servant servant,
	      Nautilus_Undo_Transaction transaction,
	      CORBA_Environment *ev)
95 96
{
	NautilusUndoManager *manager;
97
	Nautilus_Undo_Transaction duplicate_transaction;
98

99
	manager = NAUTILUS_UNDO_MANAGER (bonobo_object_from_servant (bonobo_object));
100

101 102 103 104 105 106 107
	/* 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;
108
		g_return_if_fail (manager->details->num_transactions_during_undo == 1);		
109
	}
110
	
111 112 113 114 115 116 117 118 119 120 121
	g_return_if_fail (!CORBA_Object_is_nil (transaction, ev));

	/* Keep a copy of this transaction (dump the old one). */
	duplicate_transaction = CORBA_Object_duplicate (transaction, ev);
	Nautilus_Undo_Transaction_ref (duplicate_transaction, ev);
	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. */
122
	g_signal_emit (manager, signals[CHANGED], 0);
123 124 125
}

static void
126 127 128
corba_forget (PortableServer_Servant servant,
	      Nautilus_Undo_Transaction transaction,
	      CORBA_Environment *ev)
129
{
130
	NautilusUndoManager *manager;
131

132
	manager = NAUTILUS_UNDO_MANAGER (bonobo_object_from_servant (bonobo_object));
Gene Ragan's avatar
Gene Ragan committed
133

134 135 136 137 138 139 140 141 142 143 144
	/* Nothing to forget unless the item we are passed is the
	 * transaction we are currently holding.
	 */
	if (!CORBA_Object_is_equivalent (manager->details->transaction, transaction, ev)) {
		return;
	}

	/* Get rid of the transaction we are holding on to. */
	release_transaction (manager);
	
	/* Fire off signal indicating that the undo state has changed. */
145
	g_signal_emit (manager, signals[CHANGED], 0);
146
}
147

148 149 150
static void
corba_undo (PortableServer_Servant servant,
	    CORBA_Environment *ev)
151 152 153
{
	NautilusUndoManager *manager;

154
	manager = NAUTILUS_UNDO_MANAGER (bonobo_object_from_servant (bonobo_object));
155 156 157
	nautilus_undo_manager_undo (manager);
}

158
NautilusUndoManager *
159 160
nautilus_undo_manager_new (void)
{
161
	return NAUTILUS_UNDO_MANAGER (g_object_new (nautilus_undo_manager_get_type (), NULL));
162 163
}

164
static void
165
nautilus_undo_manager_init (NautilusUndoManager *manager)
166 167 168 169
{
	manager->details = g_new0 (NautilusUndoManagerDetails, 1);
}

170
void
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
171
nautilus_undo_manager_undo (NautilusUndoManager *manager)
172
{
Gene Ragan's avatar
Gene Ragan committed
173
	CORBA_Environment ev;
174
	Nautilus_Undo_Transaction transaction;
Gene Ragan's avatar
Gene Ragan committed
175

176
	g_return_if_fail (NAUTILUS_IS_UNDO_MANAGER (manager));
Gene Ragan's avatar
Gene Ragan committed
177

178
  	CORBA_exception_init (&ev);
179

180 181 182 183 184 185 186 187 188 189 190 191 192 193
	transaction = manager->details->transaction;
	manager->details->transaction = CORBA_OBJECT_NIL;
	if (!CORBA_Object_is_nil (transaction, &ev)) {
		/* 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;
		Nautilus_Undo_Transaction_undo (transaction, &ev);
194
		manager->details->undo_in_progress = FALSE;
195 196 197
		manager->details->new_transaction_is_redo = FALSE;

		/* Let go of the transaction. */
198
		bonobo_object_release_unref (transaction, &ev);
Gene Ragan's avatar
Gene Ragan committed
199

200
		/* Fire off signal indicating the undo state has changed. */
201
		g_signal_emit (manager, signals[CHANGED], 0);
202
	}
203

204
	CORBA_exception_free (&ev);
205 206 207
}

static void
208
finalize (GObject *object)
209
{
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
210 211 212
	NautilusUndoManager *manager;

	manager = NAUTILUS_UNDO_MANAGER (object);
213

214
	release_transaction (manager);
215
	
216
	EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
217
}
218 219

void
220
nautilus_undo_manager_attach (NautilusUndoManager *manager, GObject *target)
221
{
222
	g_return_if_fail (NAUTILUS_IS_UNDO_MANAGER (manager));
223
	g_return_if_fail (G_IS_OBJECT (target));
224

225
	nautilus_undo_attach_undo_manager
226
		(G_OBJECT (target),
227
		 bonobo_object_corba_objref (BONOBO_OBJECT (manager)));
228 229 230
}

void
231
nautilus_undo_manager_add_interface (NautilusUndoManager *manager, BonoboObject *object)
232
{
233
	NautilusUndoContext *context;
234

235 236
	g_return_if_fail (NAUTILUS_IS_UNDO_MANAGER (manager));
	g_return_if_fail (BONOBO_IS_OBJECT (object));
237

238 239
	context = nautilus_undo_context_new (bonobo_object_corba_objref (BONOBO_OBJECT (manager)));
	bonobo_object_add_interface (object, BONOBO_OBJECT (context));
240
}
241

242
#ifdef UIH
243
static void
244 245
update_undo_menu_item (NautilusUndoManager *manager,
		       UndoMenuHandlerConnection *connection)
246
{
247
	CORBA_Environment ev;
248
	Nautilus_Undo_MenuItem *menu_item;
249

250 251 252 253 254 255 256
	g_assert (NAUTILUS_IS_UNDO_MANAGER (manager));
	g_assert (connection != NULL);
	g_assert (BONOBO_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);
	
257 258
	CORBA_exception_init (&ev);

259
	if (CORBA_Object_is_nil (manager->details->transaction, &ev)) {
260 261
		menu_item = NULL;
	} else {
262 263 264 265 266 267 268
		if (manager->details->current_transaction_is_redo) {
			menu_item = Nautilus_Undo_Transaction__get_redo_menu_item
				(manager->details->transaction, &ev);
		} else {
			menu_item = Nautilus_Undo_Transaction__get_undo_menu_item
				(manager->details->transaction, &ev);
		}
269
	}
270

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
	bonobo_ui_handler_menu_set_sensitivity
		(connection->handler, connection->path,
		 menu_item != NULL);
	bonobo_ui_handler_menu_set_label
		(connection->handler, connection->path,
		 menu_item == NULL
		 ? connection->no_undo_menu_item_label
		 : menu_item->label);
	bonobo_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);
	
287
	CORBA_exception_free (&ev);
288 289
}

290 291
static void
undo_menu_handler_connection_free (UndoMenuHandlerConnection *connection)
292
{
293 294 295 296 297 298 299 300 301 302
	g_assert (connection != NULL);
	g_assert (BONOBO_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);
303
}
304

305 306
static void
undo_menu_handler_connection_free_cover (gpointer data)
307
{
308
	undo_menu_handler_connection_free (data);
309 310
}

311 312 313 314 315 316
void
nautilus_undo_manager_set_up_bonobo_ui_handler_undo_item (NautilusUndoManager *manager,
							  BonoboUIHandler *handler,
							  const char *path,
							  const char *no_undo_menu_item_label,
							  const char *no_undo_menu_item_hint)
317
{
318 319 320 321 322 323 324 325 326 327 328 329
	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. */
Ramiro Estrugo's avatar
Ramiro Estrugo committed
330
	eel_gtk_signal_connect_full_while_alive
331
		(GTK_OBJECT (manager), "changed",
332
		 G_CALLBACK (update_undo_menu_item), NULL,
333 334 335
		 connection, undo_menu_handler_connection_free_cover,
		 FALSE, FALSE,
		 GTK_OBJECT (handler));
336
}
337
#endif /* UIH */
338 339

static void
340
nautilus_undo_manager_class_init (NautilusUndoManagerClass *class)
341
{
342
	G_OBJECT_CLASS (class)->finalize = finalize;
343 344 345

	signals[CHANGED] = g_signal_new
		("changed",
346
		 G_TYPE_FROM_CLASS (class),
347 348 349 350 351 352 353
		 G_SIGNAL_RUN_LAST,
		 G_STRUCT_OFFSET (NautilusUndoManagerClass,
				    changed),
		 NULL, NULL,
		 gtk_marshal_NONE__NONE,
		 G_TYPE_NONE, 0);

354 355 356
	class->epv.append = corba_append;
	class->epv.forget = corba_forget;
	class->epv.undo = corba_undo;
357
}