changeset 17411:674d8bc2b980

Undo/Redo in GtkImHtml from GtkSourceView. This may not be adaquate enough for us.
author Sean Egan <seanegan@gmail.com>
date Fri, 25 May 2007 21:53:49 +0000
parents fe8a1051aa0a
children 3c4811489e30
files pidgin/Makefile.am pidgin/gtkimhtml.c pidgin/gtkimhtml.h pidgin/gtksourceundomanager.c pidgin/gtksourceundomanager.h pidgin/gtksourceview-marshal.c pidgin/gtksourceview-marshal.h
diffstat 7 files changed, 1389 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/pidgin/Makefile.am	Fri May 25 19:05:47 2007 +0000
+++ b/pidgin/Makefile.am	Fri May 25 21:53:49 2007 +0000
@@ -109,6 +109,8 @@
 	gtksession.c \
 	gtksound.c \
 	gtksourceiter.c \
+	gtksourceundomanager.c \
+	gtksourceview-marshal.c \
 	gtkstatusbox.c \
 	gtkthemes.c \
 	gtkutils.c \
@@ -156,6 +158,8 @@
 	gtksession.h \
 	gtksound.h \
 	gtksourceiter.h \
+	gtksourceundomanager.h \
+	gtksourceview-marshal.h \
 	gtkstatusbox.h \
 	pidginstock.h \
 	gtkthemes.h \
--- a/pidgin/gtkimhtml.c	Fri May 25 19:05:47 2007 +0000
+++ b/pidgin/gtkimhtml.c	Fri May 25 21:53:49 2007 +0000
@@ -31,6 +31,8 @@
 #include "util.h"
 #include "gtkimhtml.h"
 #include "gtksourceiter.h"
+#include "gtksourceundomanager.h"
+#include "gtksourceview-marshal.h"
 #include <gtk/gtk.h>
 #include <glib/gerror.h>
 #include <gdk/gdkkeysyms.h>
@@ -136,6 +138,8 @@
 	CLEAR_FORMAT,
 	UPDATE_FORMAT,
 	MESSAGE_SEND,
+	UNDO,
+	REDO,
 	LAST_SIGNAL
 };
 static guint signals [LAST_SIGNAL] = { 0 };
@@ -1126,6 +1130,23 @@
 	return FALSE;
 }
 
+static void
+gtk_imhtml_undo(GtkIMHtml *imhtml) {
+	g_return_if_fail(GTK_IS_IMHTML(imhtml));
+	g_return_if_fail(imhtml->editable);
+	
+	gtk_source_undo_manager_undo(imhtml->undo_manager);
+}
+
+static void
+gtk_imhtml_redo(GtkIMHtml *imhtml) {
+	g_return_if_fail(GTK_IS_IMHTML(imhtml));
+	g_return_if_fail(imhtml->editable);
+	
+	gtk_source_undo_manager_redo(imhtml->undo_manager);
+
+}
+
 static gboolean imhtml_message_send(GtkIMHtml *imhtml)
 {
 	return FALSE;
@@ -1209,6 +1230,7 @@
 	g_queue_free(imhtml->animations);
 	g_free(imhtml->protocol_name);
 	g_free(imhtml->search_string);
+	g_object_unref(imhtml->undo_manager);
 	G_OBJECT_CLASS(parent_class)->finalize (object);
 }
 
@@ -1272,10 +1294,32 @@
 					     NULL,
 					     0, g_cclosure_marshal_VOID__VOID,
 					     G_TYPE_NONE, 0);
+        signals [UNDO] = g_signal_new ("undo",
+                        		      G_TYPE_FROM_CLASS (klass),
+		                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                		              G_STRUCT_OFFSET (GtkIMHtmlClass, undo),
+		                              NULL,
+		                              NULL,
+                		              gtksourceview_marshal_VOID__VOID,
+		                              G_TYPE_NONE,
+		                              0);
+        signals [REDO] = g_signal_new ("redo",
+                        		      G_TYPE_FROM_CLASS (klass),
+		                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		                              G_STRUCT_OFFSET (GtkIMHtmlClass, redo),
+		                              NULL,
+		                              NULL,
+		                              gtksourceview_marshal_VOID__VOID,
+		                              G_TYPE_NONE,
+		                              0);
+
+
 
 	klass->toggle_format = imhtml_toggle_format;
 	klass->message_send = imhtml_message_send;
 	klass->clear_format = imhtml_clear_formatting;
+	klass->undo = gtk_imhtml_undo;
+	klass->redo = gtk_imhtml_redo;
 
 	gobject_class->finalize = gtk_imhtml_finalize;
 	widget_class->drag_motion = gtk_text_view_drag_motion;
@@ -1300,12 +1344,17 @@
 	gtk_binding_entry_add_signal (binding_set, GDK_r, GDK_CONTROL_MASK, "format_function_clear", 0);
 	gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0, "message_send", 0);
 	gtk_binding_entry_add_signal (binding_set, GDK_Return, 0, "message_send", 0);
+        gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK, "undo", 0);
+        gtk_binding_entry_add_signal (binding_set, GDK_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0);
+        gtk_binding_entry_add_signal (binding_set, GDK_F14, 0, "undo", 0);
+
 }
 
 static void gtk_imhtml_init (GtkIMHtml *imhtml)
 {
 	GtkTextIter iter;
 	imhtml->text_buffer = gtk_text_buffer_new(NULL);
+	imhtml->undo_manager = gtk_source_undo_manager_new(imhtml->text_buffer);
 	gtk_text_buffer_get_end_iter (imhtml->text_buffer, &iter);
 	gtk_text_view_set_buffer(GTK_TEXT_VIEW(imhtml), imhtml->text_buffer);
 	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(imhtml), GTK_WRAP_WORD_CHAR);
--- a/pidgin/gtkimhtml.h	Fri May 25 19:05:47 2007 +0000
+++ b/pidgin/gtkimhtml.h	Fri May 25 21:53:49 2007 +0000
@@ -27,6 +27,7 @@
 #include <gtk/gtktextview.h>
 #include <gtk/gtktooltips.h>
 #include <gtk/gtkimage.h>
+#include "gtksourceundomanager.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -126,6 +127,7 @@
 
 	GSList *im_images;
 	GtkIMHtmlFuncs *funcs;
+	GtkSourceUndoManager *undo_manager;
 };
 
 struct _GtkIMHtmlClass {
@@ -137,6 +139,8 @@
 	void (*clear_format)(GtkIMHtml *);
 	void (*update_format)(GtkIMHtml *);
 	gboolean (*message_send)(GtkIMHtml *);
+	void (*undo)(GtkIMHtml *);
+	void (*redo)(GtkIMHtml *);
 };
 
 struct _GtkIMHtmlFontDetail {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtksourceundomanager.c	Fri May 25 21:53:49 2007 +0000
@@ -0,0 +1,1122 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gtksourceundomanager.c
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
+ * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi 
+ * Copyright (C) 2002-2005  Paolo Maggi 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, 
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gtksourceundomanager.h"
+#include "gtksourceview-marshal.h"
+
+
+#define DEFAULT_MAX_UNDO_LEVELS		25
+
+
+typedef struct _GtkSourceUndoAction  			GtkSourceUndoAction;
+typedef struct _GtkSourceUndoInsertAction		GtkSourceUndoInsertAction;
+typedef struct _GtkSourceUndoDeleteAction		GtkSourceUndoDeleteAction;
+
+typedef enum {
+	GTK_SOURCE_UNDO_ACTION_INSERT,
+	GTK_SOURCE_UNDO_ACTION_DELETE
+} GtkSourceUndoActionType;
+
+/* 
+ * We use offsets instead of GtkTextIters because the last ones
+ * require to much memory in this context without giving us any advantage.
+ */ 
+
+struct _GtkSourceUndoInsertAction
+{
+	gint   pos; 
+	gchar *text;
+	gint   length;
+	gint   chars;
+};
+
+struct _GtkSourceUndoDeleteAction
+{
+	gint   start;
+	gint   end;
+	gchar *text;
+	gboolean forward;
+};
+
+struct _GtkSourceUndoAction
+{
+	GtkSourceUndoActionType action_type;
+	
+	union {
+		GtkSourceUndoInsertAction  insert;
+		GtkSourceUndoDeleteAction  delete;
+	} action;
+
+	gint order_in_group;
+
+	/* It is TRUE whether the action can be merged with the following action. */
+	guint mergeable : 1;
+
+	/* It is TRUE whether the action is marked as "modified".
+	 * An action is marked as "modified" if it changed the 
+	 * state of the buffer from "not modified" to "modified". Only the first
+	 * action of a group can be marked as modified.
+	 * There can be a single action marked as "modified" in the actions list.
+	 */
+	guint modified  : 1;
+};
+
+/* INVALID is a pointer to an invalid action */
+#define INVALID ((void *) "IA")
+
+struct _GtkSourceUndoManagerPrivate
+{
+	GtkTextBuffer	*document;
+	
+	GList*		 actions;
+	gint 		 next_redo;	
+
+	gint 		 actions_in_current_group;
+	
+	gint		 running_not_undoable_actions;
+
+	gint		 num_of_groups;
+
+	gint		 max_undo_levels;
+	
+	guint	 	 can_undo : 1;
+	guint		 can_redo : 1;
+	
+	/* It is TRUE whether, while undoing an action of the current group (with order_in_group > 1),
+	 * the state of the buffer changed from "not modified" to "modified".
+	 */
+	guint	 	 modified_undoing_group : 1;	
+
+	/* Pointer to the action (in the action list) marked as "modified".
+	 * It is NULL when no action is marked as "modified". 
+	 * It is INVALID when the action marked as "modified" has been removed 
+	 * from the action list (freeing the list or resizing it) */
+	GtkSourceUndoAction *modified_action;
+};
+
+enum {
+	CAN_UNDO,
+	CAN_REDO,
+	LAST_SIGNAL
+};
+
+static void gtk_source_undo_manager_class_init 			(GtkSourceUndoManagerClass 	*klass);
+static void gtk_source_undo_manager_init 			(GtkSourceUndoManager 		*um);
+static void gtk_source_undo_manager_finalize 			(GObject 			*object);
+
+static void gtk_source_undo_manager_insert_text_handler 	(GtkTextBuffer 			*buffer, 
+							 	 GtkTextIter 			*pos,
+		                             		 	 const 	gchar 			*text, 
+							 	 gint 				 length, 
+							 	 GtkSourceUndoManager 		*um);
+static void gtk_source_undo_manager_delete_range_handler 	(GtkTextBuffer 			*buffer, 
+							 	 GtkTextIter 			*start,
+                        		      		 	 GtkTextIter 			*end,
+							 	 GtkSourceUndoManager 		*um);
+static void gtk_source_undo_manager_begin_user_action_handler 	(GtkTextBuffer 			*buffer, 
+								 GtkSourceUndoManager 		*um);
+static void gtk_source_undo_manager_modified_changed_handler	(GtkTextBuffer                  *buffer,
+								 GtkSourceUndoManager           *um);
+
+static void gtk_source_undo_manager_free_action_list 		(GtkSourceUndoManager 		*um);
+
+static void gtk_source_undo_manager_add_action 			(GtkSourceUndoManager 		*um, 
+		                                         	 const GtkSourceUndoAction 	*undo_action);
+static void gtk_source_undo_manager_free_first_n_actions 	(GtkSourceUndoManager 		*um, 
+								 gint 				 n);
+static void gtk_source_undo_manager_check_list_size 		(GtkSourceUndoManager 		*um);
+
+static gboolean gtk_source_undo_manager_merge_action 		(GtkSourceUndoManager 		*um, 
+		                                        	 const GtkSourceUndoAction 	*undo_action);
+
+static GObjectClass 	*parent_class 				= NULL;
+static guint 		undo_manager_signals [LAST_SIGNAL] 	= { 0 };
+
+GType
+gtk_source_undo_manager_get_type (void)
+{
+	static GType undo_manager_type = 0;
+
+  	if (undo_manager_type == 0)
+    	{
+      		static const GTypeInfo our_info =
+      		{
+        		sizeof (GtkSourceUndoManagerClass),
+        		NULL,		/* base_init */
+        		NULL,		/* base_finalize */
+        		(GClassInitFunc) gtk_source_undo_manager_class_init,
+        		NULL,           /* class_finalize */
+        		NULL,           /* class_data */
+        		sizeof (GtkSourceUndoManager),
+        		0,              /* n_preallocs */
+        		(GInstanceInitFunc) gtk_source_undo_manager_init
+      		};
+
+      		undo_manager_type = g_type_register_static (G_TYPE_OBJECT,
+                					    "GtkSourceUndoManager",
+							    &our_info,
+							    0);
+    	}
+
+	return undo_manager_type;
+}
+
+static void
+gtk_source_undo_manager_class_init (GtkSourceUndoManagerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  	parent_class = g_type_class_peek_parent (klass);
+
+  	object_class->finalize = gtk_source_undo_manager_finalize;
+
+        klass->can_undo 	= NULL;
+	klass->can_redo 	= NULL;
+	
+	undo_manager_signals[CAN_UNDO] =
+   		g_signal_new ("can_undo",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GtkSourceUndoManagerClass, can_undo),
+			      NULL, NULL,
+			      gtksourceview_marshal_VOID__BOOLEAN,
+			      G_TYPE_NONE,
+			      1,
+			      G_TYPE_BOOLEAN);
+
+	undo_manager_signals[CAN_REDO] =
+   		g_signal_new ("can_redo",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GtkSourceUndoManagerClass, can_redo),
+			      NULL, NULL,
+			      gtksourceview_marshal_VOID__BOOLEAN,
+			      G_TYPE_NONE,
+			      1,
+			      G_TYPE_BOOLEAN);
+}
+
+static void
+gtk_source_undo_manager_init (GtkSourceUndoManager *um)
+{
+	um->priv = g_new0 (GtkSourceUndoManagerPrivate, 1);
+
+	um->priv->actions = NULL;
+	um->priv->next_redo = 0;
+
+	um->priv->can_undo = FALSE;
+	um->priv->can_redo = FALSE;
+
+	um->priv->running_not_undoable_actions = 0;
+
+	um->priv->num_of_groups = 0;
+
+	um->priv->max_undo_levels = DEFAULT_MAX_UNDO_LEVELS;
+
+	um->priv->modified_action = NULL;
+
+	um->priv->modified_undoing_group = FALSE;
+}
+
+static void
+gtk_source_undo_manager_finalize (GObject *object)
+{
+	GtkSourceUndoManager *um;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (object));
+	
+   	um = GTK_SOURCE_UNDO_MANAGER (object);
+
+	g_return_if_fail (um->priv != NULL);
+
+	if (um->priv->actions != NULL)
+	{
+		gtk_source_undo_manager_free_action_list (um);
+	}
+
+	g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
+			  G_CALLBACK (gtk_source_undo_manager_delete_range_handler), 
+			  um);
+
+	g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
+			  G_CALLBACK (gtk_source_undo_manager_insert_text_handler), 
+			  um);
+	
+	g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
+			  G_CALLBACK (gtk_source_undo_manager_begin_user_action_handler), 
+			  um);
+
+	g_free (um->priv);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+GtkSourceUndoManager*
+gtk_source_undo_manager_new (GtkTextBuffer* buffer)
+{
+ 	GtkSourceUndoManager *um;
+
+	um = GTK_SOURCE_UNDO_MANAGER (g_object_new (GTK_SOURCE_TYPE_UNDO_MANAGER, NULL));
+
+	g_return_val_if_fail (um->priv != NULL, NULL);
+  	um->priv->document = buffer;
+
+	g_signal_connect (G_OBJECT (buffer), "insert_text",
+			  G_CALLBACK (gtk_source_undo_manager_insert_text_handler), 
+			  um);
+
+	g_signal_connect (G_OBJECT (buffer), "delete_range",
+			  G_CALLBACK (gtk_source_undo_manager_delete_range_handler), 
+			  um);
+
+	g_signal_connect (G_OBJECT (buffer), "begin_user_action",
+			  G_CALLBACK (gtk_source_undo_manager_begin_user_action_handler), 
+			  um);
+
+	g_signal_connect (G_OBJECT (buffer), "modified_changed",
+			  G_CALLBACK (gtk_source_undo_manager_modified_changed_handler),
+			  um);
+	return um;
+}
+
+void 
+gtk_source_undo_manager_begin_not_undoable_action (GtkSourceUndoManager *um)
+{
+	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
+	g_return_if_fail (um->priv != NULL);
+
+	++um->priv->running_not_undoable_actions;
+}
+
+static void 
+gtk_source_undo_manager_end_not_undoable_action_internal (GtkSourceUndoManager *um)
+{
+	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
+	g_return_if_fail (um->priv != NULL);
+
+	g_return_if_fail (um->priv->running_not_undoable_actions > 0);
+	
+	--um->priv->running_not_undoable_actions;
+}
+
+void 
+gtk_source_undo_manager_end_not_undoable_action (GtkSourceUndoManager *um)
+{
+	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
+	g_return_if_fail (um->priv != NULL);
+
+	gtk_source_undo_manager_end_not_undoable_action_internal (um);
+
+	if (um->priv->running_not_undoable_actions == 0)
+	{	
+		gtk_source_undo_manager_free_action_list (um);
+	
+		um->priv->next_redo = -1;	
+
+		if (um->priv->can_undo)
+		{
+			um->priv->can_undo = FALSE;
+			g_signal_emit (G_OBJECT (um), 
+				       undo_manager_signals [CAN_UNDO], 
+				       0, 
+				       FALSE);
+		}
+
+		if (um->priv->can_redo)
+		{
+			um->priv->can_redo = FALSE;
+			g_signal_emit (G_OBJECT (um), 
+				       undo_manager_signals [CAN_REDO], 
+				       0, 
+				       FALSE);
+		}
+	}
+}
+
+gboolean
+gtk_source_undo_manager_can_undo (const GtkSourceUndoManager *um)
+{
+	g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), FALSE);
+	g_return_val_if_fail (um->priv != NULL, FALSE);
+
+	return um->priv->can_undo;
+}
+
+gboolean 
+gtk_source_undo_manager_can_redo (const GtkSourceUndoManager *um)
+{
+	g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), FALSE);
+	g_return_val_if_fail (um->priv != NULL, FALSE);
+
+	return um->priv->can_redo;
+}
+
+static void
+set_cursor (GtkTextBuffer *buffer, gint cursor)
+{
+	GtkTextIter iter;
+	
+	/* Place the cursor at the requested position */
+	gtk_text_buffer_get_iter_at_offset (buffer, &iter, cursor);
+	gtk_text_buffer_place_cursor (buffer, &iter);
+}
+
+static void 
+insert_text (GtkTextBuffer *buffer, gint pos, const gchar *text, gint len)
+{
+	GtkTextIter iter;
+	
+	gtk_text_buffer_get_iter_at_offset (buffer, &iter, pos);
+	gtk_text_buffer_insert (buffer, &iter, text, len);
+}
+
+static void 
+delete_text (GtkTextBuffer *buffer, gint start, gint end)
+{
+	GtkTextIter start_iter;
+	GtkTextIter end_iter;
+
+	gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
+
+	if (end < 0)
+		gtk_text_buffer_get_end_iter (buffer, &end_iter);
+	else
+		gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
+
+	gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
+}
+
+static gchar*
+get_chars (GtkTextBuffer *buffer, gint start, gint end)
+{
+	GtkTextIter start_iter;
+	GtkTextIter end_iter;
+
+	gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
+
+	if (end < 0)
+		gtk_text_buffer_get_end_iter (buffer, &end_iter);
+	else
+		gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
+
+	return gtk_text_buffer_get_slice (buffer, &start_iter, &end_iter, TRUE);
+}
+
+void 
+gtk_source_undo_manager_undo (GtkSourceUndoManager *um)
+{
+	GtkSourceUndoAction *undo_action;
+	gboolean modified = FALSE;
+
+	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
+	g_return_if_fail (um->priv != NULL);
+	g_return_if_fail (um->priv->can_undo);
+	
+	um->priv->modified_undoing_group = FALSE;
+
+	gtk_source_undo_manager_begin_not_undoable_action (um);
+
+	do
+	{	
+		undo_action = g_list_nth_data (um->priv->actions, um->priv->next_redo + 1);
+		g_return_if_fail (undo_action != NULL);
+
+		/* undo_action->modified can be TRUE only if undo_action->order_in_group <= 1 */
+		g_return_if_fail ((undo_action->order_in_group <= 1) ||
+				  ((undo_action->order_in_group > 1) && !undo_action->modified));
+
+		if (undo_action->order_in_group <= 1)
+		{
+			/* Set modified to TRUE only if the buffer did not change its state from
+			 * "not modified" to "modified" undoing an action (with order_in_group > 1) 
+			 * in current group. */
+			modified = (undo_action->modified && !um->priv->modified_undoing_group);
+		}
+
+		switch (undo_action->action_type)
+		{
+			case GTK_SOURCE_UNDO_ACTION_DELETE:
+				insert_text (
+					um->priv->document, 
+					undo_action->action.delete.start, 
+					undo_action->action.delete.text,
+					strlen (undo_action->action.delete.text));
+
+				if (undo_action->action.delete.forward)
+					set_cursor (
+						um->priv->document, 
+						undo_action->action.delete.start);
+				else
+					set_cursor (
+						um->priv->document, 
+						undo_action->action.delete.end);
+
+				break;
+				
+			case GTK_SOURCE_UNDO_ACTION_INSERT:
+				delete_text (
+					um->priv->document, 
+					undo_action->action.insert.pos, 
+					undo_action->action.insert.pos + 
+						undo_action->action.insert.chars); 
+
+				set_cursor (
+					um->priv->document, 
+					undo_action->action.insert.pos);
+				break;
+
+			default:
+				/* Unknown action type. */
+				g_return_if_reached ();
+		}
+
+		++um->priv->next_redo;
+
+	} while (undo_action->order_in_group > 1);
+
+	if (modified)
+	{
+		--um->priv->next_redo;
+		gtk_text_buffer_set_modified (um->priv->document, FALSE);
+		++um->priv->next_redo;
+	}
+
+	gtk_source_undo_manager_end_not_undoable_action_internal (um);
+	
+	um->priv->modified_undoing_group = FALSE;
+
+	if (!um->priv->can_redo)
+	{
+		um->priv->can_redo = TRUE;
+		g_signal_emit (G_OBJECT (um), 
+			       undo_manager_signals [CAN_REDO], 
+			       0, 
+			       TRUE);
+	}
+
+	if (um->priv->next_redo >= (gint)(g_list_length (um->priv->actions) - 1))
+	{
+		um->priv->can_undo = FALSE;
+		g_signal_emit (G_OBJECT (um), 
+			       undo_manager_signals [CAN_UNDO], 
+			       0, 
+			       FALSE);
+	}
+}
+
+void 
+gtk_source_undo_manager_redo (GtkSourceUndoManager *um)
+{
+	GtkSourceUndoAction *undo_action;
+	gboolean modified = FALSE;
+
+	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
+	g_return_if_fail (um->priv != NULL);
+	g_return_if_fail (um->priv->can_redo);
+	
+	undo_action = g_list_nth_data (um->priv->actions, um->priv->next_redo);
+	g_return_if_fail (undo_action != NULL);
+
+	gtk_source_undo_manager_begin_not_undoable_action (um);
+
+	do
+	{
+		if (undo_action->modified)
+		{
+			g_return_if_fail (undo_action->order_in_group <= 1);
+			modified = TRUE;
+		}
+
+		--um->priv->next_redo;
+	
+		switch (undo_action->action_type)
+		{
+			case GTK_SOURCE_UNDO_ACTION_DELETE:
+				delete_text (
+					um->priv->document, 
+					undo_action->action.delete.start, 
+					undo_action->action.delete.end); 
+
+				set_cursor (
+					um->priv->document,
+					undo_action->action.delete.start);
+
+				break;
+				
+			case GTK_SOURCE_UNDO_ACTION_INSERT:
+				set_cursor (
+					um->priv->document,
+					undo_action->action.insert.pos);
+
+				insert_text (
+					um->priv->document, 
+					undo_action->action.insert.pos, 
+					undo_action->action.insert.text,
+					undo_action->action.insert.length);
+
+				break;
+
+			default:
+				/* Unknown action type */
+				++um->priv->next_redo;
+				g_return_if_reached ();
+		}
+
+		if (um->priv->next_redo < 0)
+			undo_action = NULL;
+		else
+			undo_action = g_list_nth_data (um->priv->actions, um->priv->next_redo);
+			
+	} while ((undo_action != NULL) && (undo_action->order_in_group > 1));
+
+	if (modified)
+	{
+		++um->priv->next_redo;
+		gtk_text_buffer_set_modified (um->priv->document, FALSE);
+		--um->priv->next_redo;
+	}
+
+	gtk_source_undo_manager_end_not_undoable_action_internal (um);
+
+	if (um->priv->next_redo < 0)
+	{
+		um->priv->can_redo = FALSE;
+		g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, FALSE);
+	}
+
+	if (!um->priv->can_undo)
+	{
+		um->priv->can_undo = TRUE;
+		g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, TRUE);
+	}
+}
+
+static void
+gtk_source_undo_action_free (GtkSourceUndoAction *action)
+{
+	if (action == NULL)
+		return;
+
+	if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
+		g_free (action->action.insert.text);
+	else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
+		g_free (action->action.delete.text);
+	else
+		g_return_if_reached ();
+
+	g_free (action);
+}
+
+static void 
+gtk_source_undo_manager_free_action_list (GtkSourceUndoManager *um)
+{
+	GList *l;
+
+	l = um->priv->actions;
+
+	while (l != NULL)
+	{
+		GtkSourceUndoAction *action = l->data;
+
+		if (action->order_in_group == 1)
+			--um->priv->num_of_groups;
+
+		if (action->modified)
+			um->priv->modified_action = INVALID;
+
+		gtk_source_undo_action_free (action);
+
+		l = g_list_next (l);
+	}
+
+	g_list_free (um->priv->actions);
+	um->priv->actions = NULL;	
+}
+
+static void 
+gtk_source_undo_manager_insert_text_handler (GtkTextBuffer 		*buffer, 
+					     GtkTextIter 		*pos,
+		                             const gchar 		*text, 
+					     gint 			 length, 
+					     GtkSourceUndoManager 	*um)
+{
+	GtkSourceUndoAction undo_action;
+	
+	if (um->priv->running_not_undoable_actions > 0)
+		return;
+
+	g_return_if_fail (strlen (text) >= (guint)length);
+	
+	undo_action.action_type = GTK_SOURCE_UNDO_ACTION_INSERT;
+
+	undo_action.action.insert.pos    = gtk_text_iter_get_offset (pos);
+	undo_action.action.insert.text   = (gchar*) text;
+	undo_action.action.insert.length = length;
+	undo_action.action.insert.chars  = g_utf8_strlen (text, length);
+
+	if ((undo_action.action.insert.chars > 1) || (g_utf8_get_char (text) == '\n'))
+
+	       	undo_action.mergeable = FALSE;
+	else
+		undo_action.mergeable = TRUE;
+
+	undo_action.modified = FALSE;
+
+	gtk_source_undo_manager_add_action (um, &undo_action);
+}
+
+static void 
+gtk_source_undo_manager_delete_range_handler (GtkTextBuffer 		*buffer, 
+					      GtkTextIter 		*start,
+                        		      GtkTextIter 		*end, 
+					      GtkSourceUndoManager 	*um)
+{
+	GtkSourceUndoAction undo_action;
+	GtkTextIter insert_iter;
+	
+	if (um->priv->running_not_undoable_actions > 0)
+		return;
+
+	undo_action.action_type = GTK_SOURCE_UNDO_ACTION_DELETE;
+
+	gtk_text_iter_order (start, end);
+
+	undo_action.action.delete.start  = gtk_text_iter_get_offset (start);
+	undo_action.action.delete.end    = gtk_text_iter_get_offset (end);
+
+	undo_action.action.delete.text   = get_chars (
+						buffer,
+						undo_action.action.delete.start,
+						undo_action.action.delete.end);
+
+	/* figure out if the user used the Delete or the Backspace key */
+	gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter,
+					  gtk_text_buffer_get_insert (buffer));
+	if (gtk_text_iter_get_offset (&insert_iter) <= undo_action.action.delete.start)
+		undo_action.action.delete.forward = TRUE;
+	else
+		undo_action.action.delete.forward = FALSE;
+
+	if (((undo_action.action.delete.end - undo_action.action.delete.start) > 1) ||
+	     (g_utf8_get_char (undo_action.action.delete.text  ) == '\n'))
+	       	undo_action.mergeable = FALSE;
+	else
+		undo_action.mergeable = TRUE;
+
+	undo_action.modified = FALSE;
+	
+	gtk_source_undo_manager_add_action (um, &undo_action);
+
+	g_free (undo_action.action.delete.text);
+
+}
+
+static void 
+gtk_source_undo_manager_begin_user_action_handler (GtkTextBuffer *buffer, GtkSourceUndoManager *um)
+{
+	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
+	g_return_if_fail (um->priv != NULL);
+
+	if (um->priv->running_not_undoable_actions > 0)
+		return;
+
+	um->priv->actions_in_current_group = 0;
+}
+
+static void
+gtk_source_undo_manager_add_action (GtkSourceUndoManager 	*um, 
+				    const GtkSourceUndoAction 	*undo_action)
+{
+	GtkSourceUndoAction* action;
+	
+	if (um->priv->next_redo >= 0)
+	{
+		gtk_source_undo_manager_free_first_n_actions (um, um->priv->next_redo + 1);
+	}
+
+	um->priv->next_redo = -1;
+
+	if (!gtk_source_undo_manager_merge_action (um, undo_action))
+	{
+		action = g_new (GtkSourceUndoAction, 1);
+		*action = *undo_action;
+
+		if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
+			action->action.insert.text = g_strdup (undo_action->action.insert.text);
+		else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
+			action->action.delete.text = g_strdup (undo_action->action.delete.text); 
+		else
+		{
+			g_free (action);
+			g_return_if_reached ();
+		}
+		
+		++um->priv->actions_in_current_group;
+		action->order_in_group = um->priv->actions_in_current_group;
+
+		if (action->order_in_group == 1)
+			++um->priv->num_of_groups;
+	
+		um->priv->actions = g_list_prepend (um->priv->actions, action);
+	}
+	
+	gtk_source_undo_manager_check_list_size (um);
+
+	if (!um->priv->can_undo)
+	{
+		um->priv->can_undo = TRUE;
+		g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, TRUE);
+	}
+
+	if (um->priv->can_redo)
+	{
+		um->priv->can_redo = FALSE;
+		g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, FALSE);
+	}
+}
+
+static void 
+gtk_source_undo_manager_free_first_n_actions (GtkSourceUndoManager	*um, 
+					      gint 			 n)
+{
+	gint i;
+
+	if (um->priv->actions == NULL)
+		return;
+
+	for (i = 0; i < n; i++)
+	{
+		GtkSourceUndoAction *action = g_list_first (um->priv->actions)->data;
+
+		if (action->order_in_group == 1)
+			--um->priv->num_of_groups;
+
+		if (action->modified)
+			um->priv->modified_action = INVALID;
+
+		gtk_source_undo_action_free (action);
+
+		um->priv->actions = g_list_delete_link (um->priv->actions,
+							um->priv->actions);
+
+		if (um->priv->actions == NULL) 
+			return;
+	}
+}
+
+static void 
+gtk_source_undo_manager_check_list_size (GtkSourceUndoManager *um)
+{
+	gint undo_levels;
+	
+	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
+	g_return_if_fail (um->priv != NULL);
+	
+	undo_levels = gtk_source_undo_manager_get_max_undo_levels (um);
+	
+	if (undo_levels < 1)
+		return;
+
+	if (um->priv->num_of_groups > undo_levels)
+	{
+		GtkSourceUndoAction *undo_action;
+		GList *last;
+		
+		last = g_list_last (um->priv->actions);
+		undo_action = (GtkSourceUndoAction*) last->data;
+			
+		do
+		{
+			GList *tmp;
+			
+			if (undo_action->order_in_group == 1)
+				--um->priv->num_of_groups;
+
+			if (undo_action->modified)
+				um->priv->modified_action = INVALID;
+
+			gtk_source_undo_action_free (undo_action);
+
+			tmp = g_list_previous (last);
+			um->priv->actions = g_list_delete_link (um->priv->actions, last);
+			last = tmp;
+			g_return_if_fail (last != NULL); 
+
+			undo_action = (GtkSourceUndoAction*) last->data;
+
+		} while ((undo_action->order_in_group > 1) || 
+			 (um->priv->num_of_groups > undo_levels));
+	}	
+}
+
+/**
+ * gtk_source_undo_manager_merge_action:
+ * @um: a #GtkSourceUndoManager. 
+ * @undo_action: a #GtkSourceUndoAction.
+ * 
+ * This function tries to merge the undo action at the top of
+ * the stack with a new undo action. So when we undo for example
+ * typing, we can undo the whole word and not each letter by itself.
+ * 
+ * Return Value: %TRUE is merge was sucessful, %FALSE otherwise.²
+ **/
+static gboolean 
+gtk_source_undo_manager_merge_action (GtkSourceUndoManager 	*um, 
+				      const GtkSourceUndoAction *undo_action)
+{
+	GtkSourceUndoAction *last_action;
+	
+	g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), FALSE);
+	g_return_val_if_fail (um->priv != NULL, FALSE);
+
+	if (um->priv->actions == NULL)
+		return FALSE;
+
+	last_action = (GtkSourceUndoAction*) g_list_nth_data (um->priv->actions, 0);
+
+	if (!last_action->mergeable)
+		return FALSE;
+
+	if ((!undo_action->mergeable) ||
+	    (undo_action->action_type != last_action->action_type))
+	{
+		last_action->mergeable = FALSE;
+		return FALSE;
+	}
+
+	if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
+	{				
+		if ((last_action->action.delete.forward != undo_action->action.delete.forward) ||
+		    ((last_action->action.delete.start != undo_action->action.delete.start) &&
+		     (last_action->action.delete.start != undo_action->action.delete.end)))
+		{
+			last_action->mergeable = FALSE;
+			return FALSE;
+		}
+		
+		if (last_action->action.delete.start == undo_action->action.delete.start)
+		{
+			gchar *str;
+			
+#define L  (last_action->action.delete.end - last_action->action.delete.start - 1)
+#define g_utf8_get_char_at(p,i) g_utf8_get_char(g_utf8_offset_to_pointer((p),(i)))
+		
+			/* Deleted with the delete key */
+			if ((g_utf8_get_char (undo_action->action.delete.text) != ' ') &&
+			    (g_utf8_get_char (undo_action->action.delete.text) != '\t') &&
+                            ((g_utf8_get_char_at (last_action->action.delete.text, L) == ' ') ||
+			     (g_utf8_get_char_at (last_action->action.delete.text, L)  == '\t')))
+			{
+				last_action->mergeable = FALSE;
+				return FALSE;
+			}
+			
+			str = g_strdup_printf ("%s%s", last_action->action.delete.text, 
+				undo_action->action.delete.text);
+			
+			g_free (last_action->action.delete.text);
+			last_action->action.delete.end += (undo_action->action.delete.end - 
+							   undo_action->action.delete.start);
+			last_action->action.delete.text = str;
+		}
+		else
+		{
+			gchar *str;
+			
+			/* Deleted with the backspace key */
+			if ((g_utf8_get_char (undo_action->action.delete.text) != ' ') &&
+			    (g_utf8_get_char (undo_action->action.delete.text) != '\t') &&
+                            ((g_utf8_get_char (last_action->action.delete.text) == ' ') ||
+			     (g_utf8_get_char (last_action->action.delete.text) == '\t')))
+			{
+				last_action->mergeable = FALSE;
+				return FALSE;
+			}
+
+			str = g_strdup_printf ("%s%s", undo_action->action.delete.text, 
+				last_action->action.delete.text);
+			
+			g_free (last_action->action.delete.text);
+			last_action->action.delete.start = undo_action->action.delete.start;
+			last_action->action.delete.text = str;
+		}
+	}
+	else if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
+	{
+		gchar* str;
+		
+#define I (last_action->action.insert.chars - 1)
+		
+		if ((undo_action->action.insert.pos != 
+		     	(last_action->action.insert.pos + last_action->action.insert.chars)) ||
+		    ((g_utf8_get_char (undo_action->action.insert.text) != ' ') &&
+		      (g_utf8_get_char (undo_action->action.insert.text) != '\t') &&
+		     ((g_utf8_get_char_at (last_action->action.insert.text, I) == ' ') ||
+		      (g_utf8_get_char_at (last_action->action.insert.text, I) == '\t')))
+		   )
+		{
+			last_action->mergeable = FALSE;
+			return FALSE;
+		}
+
+		str = g_strdup_printf ("%s%s", last_action->action.insert.text, 
+				undo_action->action.insert.text);
+		
+		g_free (last_action->action.insert.text);
+		last_action->action.insert.length += undo_action->action.insert.length;
+		last_action->action.insert.text = str;
+		last_action->action.insert.chars += undo_action->action.insert.chars;
+
+	}
+	else
+		/* Unknown action inside undo merge encountered */
+		g_return_val_if_reached (TRUE);
+		
+	return TRUE;
+}
+
+gint
+gtk_source_undo_manager_get_max_undo_levels (GtkSourceUndoManager *um)
+{
+	g_return_val_if_fail (um != NULL, 0);
+	g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), 0);
+
+	return um->priv->max_undo_levels;
+}
+
+void
+gtk_source_undo_manager_set_max_undo_levels (GtkSourceUndoManager	*um,
+				  	     gint			 max_undo_levels)
+{
+	gint old_levels;
+	
+	g_return_if_fail (um != NULL);
+	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
+
+	old_levels = um->priv->max_undo_levels;
+	um->priv->max_undo_levels = max_undo_levels;
+
+	if (max_undo_levels < 1)
+		return;
+		
+	if (old_levels > max_undo_levels)
+	{
+		/* strip redo actions first */
+		while (um->priv->next_redo >= 0 && (um->priv->num_of_groups > max_undo_levels))
+		{
+			gtk_source_undo_manager_free_first_n_actions (um, 1);
+			um->priv->next_redo--;
+		}
+		
+		/* now remove undo actions if necessary */
+		gtk_source_undo_manager_check_list_size (um);
+
+		/* emit "can_undo" and/or "can_redo" if appropiate */
+		if (um->priv->next_redo < 0 && um->priv->can_redo)
+		{
+			um->priv->can_redo = FALSE;
+			g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, FALSE);
+		}
+
+		if (um->priv->can_undo &&
+		    um->priv->next_redo >= (gint)(g_list_length (um->priv->actions) - 1))
+		{
+			um->priv->can_undo = FALSE;
+			g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, FALSE);
+		}
+	}
+}
+
+static void
+gtk_source_undo_manager_modified_changed_handler (GtkTextBuffer        *buffer,
+                                            	  GtkSourceUndoManager *um)
+{
+	GtkSourceUndoAction *action;
+	GList *list;
+	
+	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
+	g_return_if_fail (um->priv != NULL);
+
+	if (um->priv->actions == NULL)
+		return;
+
+	list = g_list_nth (um->priv->actions, um->priv->next_redo + 1);
+
+	if (list != NULL)
+		action = (GtkSourceUndoAction*) list->data;
+	else
+		action = NULL;
+
+	if (gtk_text_buffer_get_modified (buffer) == FALSE)
+	{	
+		if (action != NULL)
+			action->mergeable = FALSE;
+
+		if (um->priv->modified_action != NULL)
+		{
+			if (um->priv->modified_action != INVALID)
+				um->priv->modified_action->modified = FALSE;
+
+			um->priv->modified_action = NULL;
+		}
+
+		return;
+	}
+
+	if (action == NULL)
+	{
+		g_return_if_fail (um->priv->running_not_undoable_actions > 0);
+
+		return;
+	}
+	
+	/* gtk_text_buffer_get_modified (buffer) == TRUE */
+
+	g_return_if_fail (um->priv->modified_action == NULL);
+
+	if (action->order_in_group > 1)
+		um->priv->modified_undoing_group  = TRUE;
+
+	while (action->order_in_group > 1)
+	{
+		list = g_list_next (list);
+		g_return_if_fail (list != NULL);
+
+		action = (GtkSourceUndoAction*) list->data;
+		g_return_if_fail (action != NULL);
+	}
+
+	action->modified = TRUE;
+	um->priv->modified_action = action;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtksourceundomanager.h	Fri May 25 21:53:49 2007 +0000
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gtksourceundomanager.h
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
+ * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi 
+ * Copyright (C) 2002, 2003 Paolo Maggi 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, 
+ * Boston, MA 02111-1307, USA. * *
+ */
+ 
+#ifndef __GTK_SOURCE_UNDO_MANAGER_H__
+#define __GTK_SOURCE_UNDO_MANAGER_H__
+
+#include <gtk/gtktextbuffer.h>
+
+#define GTK_SOURCE_TYPE_UNDO_MANAGER             	(gtk_source_undo_manager_get_type ())
+#define GTK_SOURCE_UNDO_MANAGER(obj)			(GTK_CHECK_CAST ((obj), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManager))
+#define GTK_SOURCE_UNDO_MANAGER_CLASS(klass)		(GTK_CHECK_CLASS_CAST ((klass), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManagerClass))
+#define GTK_SOURCE_IS_UNDO_MANAGER(obj)			(GTK_CHECK_TYPE ((obj), GTK_SOURCE_TYPE_UNDO_MANAGER))
+#define GTK_SOURCE_IS_UNDO_MANAGER_CLASS(klass)  	(GTK_CHECK_CLASS_TYPE ((klass), GTK_SOURCE_TYPE_UNDO_MANAGER))
+#define GTK_SOURCE_UNDO_MANAGER_GET_CLASS(obj)  	(GTK_CHECK_GET_CLASS ((obj), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManagerClass))
+
+
+typedef struct _GtkSourceUndoManager        	GtkSourceUndoManager;
+typedef struct _GtkSourceUndoManagerClass 	GtkSourceUndoManagerClass;
+
+typedef struct _GtkSourceUndoManagerPrivate 	GtkSourceUndoManagerPrivate;
+
+struct _GtkSourceUndoManager
+{
+	GObject base;
+	
+	GtkSourceUndoManagerPrivate *priv;
+};
+
+struct _GtkSourceUndoManagerClass
+{
+	GObjectClass parent_class;
+
+	/* Signals */
+	void (*can_undo) (GtkSourceUndoManager *um, gboolean can_undo);
+    	void (*can_redo) (GtkSourceUndoManager *um, gboolean can_redo);
+};
+
+GType        		gtk_source_undo_manager_get_type	(void) G_GNUC_CONST;
+
+GtkSourceUndoManager* 	gtk_source_undo_manager_new 		(GtkTextBuffer 		*buffer);
+
+gboolean		gtk_source_undo_manager_can_undo	(const GtkSourceUndoManager *um);
+gboolean		gtk_source_undo_manager_can_redo 	(const GtkSourceUndoManager *um);
+
+void			gtk_source_undo_manager_undo 		(GtkSourceUndoManager 	*um);
+void			gtk_source_undo_manager_redo 		(GtkSourceUndoManager 	*um);
+
+void			gtk_source_undo_manager_begin_not_undoable_action 
+								(GtkSourceUndoManager	*um);
+void			gtk_source_undo_manager_end_not_undoable_action 
+								(GtkSourceUndoManager	*um);
+
+gint			gtk_source_undo_manager_get_max_undo_levels 
+								(GtkSourceUndoManager 	*um);
+void			gtk_source_undo_manager_set_max_undo_levels 
+								(GtkSourceUndoManager 	*um,
+				  	     			 gint		 	 undo_levels);
+
+#endif /* __GTK_SOURCE_UNDO_MANAGER_H__ */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtksourceview-marshal.c	Fri May 25 21:53:49 2007 +0000
@@ -0,0 +1,95 @@
+#include "gtksourceview-marshal.h"
+
+#include	<glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:VOID (gtksourceview-marshal.list:1) */
+
+/* VOID:BOOLEAN (gtksourceview-marshal.list:2) */
+
+/* VOID:BOXED (gtksourceview-marshal.list:3) */
+
+/* VOID:BOXED,BOXED (gtksourceview-marshal.list:4) */
+void
+gtksourceview_marshal_VOID__BOXED_BOXED (GClosure     *closure,
+                                         GValue       *return_value,
+                                         guint         n_param_values,
+                                         const GValue *param_values,
+                                         gpointer      invocation_hint,
+                                         gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__BOXED_BOXED) (gpointer     data1,
+                                                  gpointer     arg_1,
+                                                  gpointer     arg_2,
+                                                  gpointer     data2);
+  register GMarshalFunc_VOID__BOXED_BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_boxed (param_values + 1),
+            g_marshal_value_peek_boxed (param_values + 2),
+            data2);
+}
+
+/* VOID:STRING (gtksourceview-marshal.list:5) */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtksourceview-marshal.h	Fri May 25 21:53:49 2007 +0000
@@ -0,0 +1,32 @@
+
+#ifndef __gtksourceview_marshal_MARSHAL_H__
+#define __gtksourceview_marshal_MARSHAL_H__
+
+#include	<glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID:VOID (gtksourceview-marshal.list:1) */
+#define gtksourceview_marshal_VOID__VOID	g_cclosure_marshal_VOID__VOID
+
+/* VOID:BOOLEAN (gtksourceview-marshal.list:2) */
+#define gtksourceview_marshal_VOID__BOOLEAN	g_cclosure_marshal_VOID__BOOLEAN
+
+/* VOID:BOXED (gtksourceview-marshal.list:3) */
+#define gtksourceview_marshal_VOID__BOXED	g_cclosure_marshal_VOID__BOXED
+
+/* VOID:BOXED,BOXED (gtksourceview-marshal.list:4) */
+extern void gtksourceview_marshal_VOID__BOXED_BOXED (GClosure     *closure,
+                                                     GValue       *return_value,
+                                                     guint         n_param_values,
+                                                     const GValue *param_values,
+                                                     gpointer      invocation_hint,
+                                                     gpointer      marshal_data);
+
+/* VOID:STRING (gtksourceview-marshal.list:5) */
+#define gtksourceview_marshal_VOID__STRING	g_cclosure_marshal_VOID__STRING
+
+G_END_DECLS
+
+#endif /* __gtksourceview_marshal_MARSHAL_H__ */
+