? gtksourceview/.gtksourceundomanager.c.swp
Index: gtksourceview/gtksourcebuffer.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourcebuffer.c,v
retrieving revision 1.38
diff -u -r1.38 gtksourcebuffer.c
--- gtksourceview/gtksourcebuffer.c	27 Feb 2005 19:01:50 -0000	1.38
+++ gtksourceview/gtksourcebuffer.c	15 Jun 2005 16:08:29 -0000
@@ -1326,6 +1326,50 @@
 }
 
 /**
+ * gtk_source_buffer_get_undo_action
+ * @buffer: a #GtkSourceBuffer.
+ * 
+ * Returns the action the source buffer would do if undo was called.
+ * Example: "deletion of 'foobar'"
+ *
+ * Most of the time, this is used in conjunction with an "Undo" label.
+ * If the text to be displayed is too large it will be truncated to
+ * approx. 15 characters
+ *
+ * Return value: A string containing the action performed if undo is called.
+ *               The string should be freed when no longer needed.
+ **/
+gchar *
+gtk_source_buffer_get_undo_action (GtkSourceBuffer   *buffer)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
+
+	return gtk_source_undo_manager_get_undo_action (buffer->priv->undo_manager);
+}
+
+/**
+ * gtk_source_buffer_get_redo_action
+ * @buffer: a #GtkSourceBuffer.
+ * 
+ * Returns the action the source buffer would do if redo was called.
+ * Example: "typing of 'foobar'"
+ *
+ * Most of the time, this is used in conjunction with an "Redo" label
+ * If the text to be displayed is too large it will be truncated to
+ * approx. 15 characters
+ *
+ * Return value: A string containing the action performed if redo is called.
+ *               The string should be freed when no longer needed.
+ **/
+gchar *
+gtk_source_buffer_get_redo_action (GtkSourceBuffer   *buffer)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
+
+	return gtk_source_undo_manager_get_redo_action (buffer->priv->undo_manager);
+}
+
+/**
  * gtk_source_buffer_get_check_brackets:
  * @buffer: a #GtkSourceBuffer.
  * 
Index: gtksourceview/gtksourcebuffer.h
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourcebuffer.h,v
retrieving revision 1.18
diff -u -r1.18 gtksourcebuffer.h
--- gtksourceview/gtksourcebuffer.h	6 Jun 2003 08:54:36 -0000	1.18
+++ gtksourceview/gtksourcebuffer.h	15 Jun 2005 16:08:29 -0000
@@ -114,6 +114,9 @@
 void			 gtk_source_buffer_begin_not_undoable_action (GtkSourceBuffer   *buffer);
 void			 gtk_source_buffer_end_not_undoable_action   (GtkSourceBuffer   *buffer);
 
+gchar *			 gtk_source_buffer_get_undo_action (GtkSourceBuffer   *buffer);
+gchar *			 gtk_source_buffer_get_redo_action (GtkSourceBuffer   *buffer);
+
 /* marker methods. */
 GtkSourceMarker         *gtk_source_buffer_create_marker        (GtkSourceBuffer        *buffer,
 								 const gchar            *name,
Index: gtksourceview/gtksourceundomanager.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourceundomanager.c,v
retrieving revision 1.8
diff -u -r1.8 gtksourceundomanager.c
--- gtksourceview/gtksourceundomanager.c	3 Feb 2005 17:55:16 -0000	1.8
+++ gtksourceview/gtksourceundomanager.c	15 Jun 2005 16:08:30 -0000
@@ -33,10 +33,11 @@
 
 #include "gtksourceundomanager.h"
 #include "gtksourceview-marshal.h"
-
+#include "gtksourceview-i18n.h"
 
 #define DEFAULT_MAX_UNDO_LEVELS		25
 
+#define GTK_SOURCE_UNDO_ACTION_TRUNC 15
 
 typedef struct _GtkSourceUndoAction  			GtkSourceUndoAction;
 typedef struct _GtkSourceUndoInsertAction		GtkSourceUndoInsertAction;
@@ -348,20 +349,20 @@
 		if (um->priv->can_undo)
 		{
 			um->priv->can_undo = FALSE;
-			g_signal_emit (G_OBJECT (um), 
-				       undo_manager_signals [CAN_UNDO], 
-				       0, 
-				       FALSE);
 		}
+		g_signal_emit (G_OBJECT (um), 
+					undo_manager_signals [CAN_UNDO], 
+					0, 
+					um->priv->can_undo);
 
 		if (um->priv->can_redo)
 		{
 			um->priv->can_redo = FALSE;
-			g_signal_emit (G_OBJECT (um), 
-				       undo_manager_signals [CAN_REDO], 
-				       0, 
-				       FALSE);
 		}
+		g_signal_emit (G_OBJECT (um), 
+					undo_manager_signals [CAN_REDO], 
+					0, 
+					um->priv->can_redo);
 	}
 }
 
@@ -521,20 +522,20 @@
 	if (!um->priv->can_redo)
 	{
 		um->priv->can_redo = TRUE;
-		g_signal_emit (G_OBJECT (um), 
-			       undo_manager_signals [CAN_REDO], 
-			       0, 
-			       TRUE);
 	}
+	g_signal_emit (G_OBJECT (um), 
+					undo_manager_signals [CAN_REDO], 
+					0, 
+					um->priv->can_redo);
 
 	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);
 	}
+	g_signal_emit (G_OBJECT (um), 
+					undo_manager_signals [CAN_UNDO], 
+					0, 
+					um->priv->can_undo);
 }
 
 void 
@@ -614,14 +615,15 @@
 	if (um->priv->next_redo < 0)
 	{
 		um->priv->can_redo = FALSE;
-		g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, FALSE);
 	}
+	g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, um->priv->can_redo);
+	
 
 	if (!um->priv->can_undo)
 	{
 		um->priv->can_undo = TRUE;
-		g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, TRUE);
 	}
+	g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, um->priv->can_undo);
 
 }
 
@@ -796,14 +798,14 @@
 	if (!um->priv->can_undo)
 	{
 		um->priv->can_undo = TRUE;
-		g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, TRUE);
 	}
-
+	g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, um->priv->can_undo);
+	
 	if (um->priv->can_redo)
 	{
 		um->priv->can_redo = FALSE;
-		g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, FALSE);
 	}
+	g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, um->priv->can_redo);
 }
 
 static void 
@@ -906,7 +908,7 @@
  * 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.²
+ * Return Value: %TRUE is merge was sucessful, %FALSE otherwise.
  **/
 static gboolean 
 gtk_source_undo_manager_merge_action (GtkSourceUndoManager 	*um, 
@@ -1063,15 +1065,15 @@
 		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);
 		}
+		g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_REDO], 0, um->priv->can_redo);
 
 		if (um->priv->can_undo &&
-		    um->priv->next_redo >= (gint)(g_list_length (um->priv->actions) - 1))
+			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);
 		}
+		g_signal_emit (G_OBJECT (um), undo_manager_signals [CAN_UNDO], 0, um->priv->can_undo);
 	}
 }
 
@@ -1130,4 +1132,209 @@
 
 	action->modified = TRUE;
 	um->priv->modified_action = action;
+}
+
+static gchar *
+str_middle_truncate (const char *string,
+				 guint truncate_length)
+{
+	char *truncated;
+	guint length;
+	guint num_left_chars;
+	guint num_right_chars;
+
+	/* we use "â€¦" instead of "..." */
+	const char delimter[] = "â€¦";
+	const guint delimter_length = strlen (delimter);
+	const guint min_truncate_length = delimter_length + 2;
+
+	if (string == NULL) {
+		return NULL;
+	}
+
+	/* It doesnt make sense to truncate strings to less than
+	 * the size of the delimiter plus 2 characters (one on each
+	 * side)
+	 */
+	if (truncate_length < min_truncate_length) {
+		return g_strdup (string);
+	}
+
+	length = strlen (string);
+
+	/* Make sure the string is not already small enough. */
+	if (length <= truncate_length) {
+		return g_strdup (string);
+	}
+
+	/* Find the 'middle' where the truncation will occur. */
+	num_left_chars = (truncate_length - delimter_length) / 2;
+	num_right_chars = truncate_length - num_left_chars - delimter_length + 1;
+
+	truncated = g_new (char, truncate_length + 1);
+
+	strncpy (truncated, string, num_left_chars);
+	strncpy (truncated + num_left_chars, delimter, delimter_length);
+	strncpy (truncated + num_left_chars + delimter_length, string + length - num_right_chars + 1, num_right_chars);
+
+	return truncated;
+}
+
+static gboolean
+check_undo_redo_is_whitespace (gchar *action)
+{
+	gunichar unichar;
+
+	do
+	{
+		unichar = g_utf8_get_char (action);
+
+		if(!g_unichar_isspace (unichar))
+			return FALSE;
+
+		action = g_utf8_next_char (action);
+	} while ((gchar) action[0] != '\0');
+
+	return TRUE;
+}
+
+static gint
+check_undo_redo_is_ascii_char (gchar *action, gchar c)
+{
+	int i = 0;
+	int count = 0;
+
+	do
+	{
+		if(action[i++] == c)
+			count++;
+	} while ((gchar) action[i] != '\0');
+
+	return count;
+}
+
+static gchar *
+get_undo_redo_action_string (GtkSourceUndoAction *undo_action)
+{
+	gchar *text;
+
+	switch (undo_action->action_type)
+	{
+		case GTK_SOURCE_UNDO_ACTION_DELETE:
+			text = undo_action->action.delete.text;
+			break;
+
+		case GTK_SOURCE_UNDO_ACTION_INSERT:
+			text = undo_action->action.insert.text;
+			break;
+
+		default:
+			g_return_val_if_reached (NULL);
+	}
+
+	if (check_undo_redo_is_whitespace (text))
+	{
+		gint space_count;
+		gint nline_count;
+		gint tab_count;
+
+		space_count = check_undo_redo_is_ascii_char (text, ' ');
+		nline_count = check_undo_redo_is_ascii_char (text, '\n');
+		tab_count = check_undo_redo_is_ascii_char (text, '\t');
+		//g_warning ("Text: '%s' Space: %d  Lines: %d  Tabs: %d", text,space_count, nline_count, tab_count);
+
+		if (space_count > 0 && nline_count == 0 && tab_count == 0)
+			switch (undo_action->action_type)
+			{
+			    case GTK_SOURCE_UNDO_ACTION_DELETE:
+			    	return g_strdup (ngettext ("deletion of a space", "deletion of spaces", space_count));
+			    case GTK_SOURCE_UNDO_ACTION_INSERT:
+			    	return g_strdup (ngettext ("typing of a space", "typing of spaces", space_count));
+			    default:
+			    	g_return_val_if_reached (NULL);
+			}
+
+
+		if (nline_count > 0 && tab_count == 0 && space_count == 0)
+			switch (undo_action->action_type)
+			{
+			    case GTK_SOURCE_UNDO_ACTION_DELETE:
+			    	return g_strdup (ngettext ("deletion of a line feed", "deletion of line feeds", nline_count));
+			    case GTK_SOURCE_UNDO_ACTION_INSERT:
+			    	return g_strdup (ngettext ("typing of a line feed", "typing of line feeds", nline_count));
+			    default:
+			    	g_return_val_if_reached (NULL);
+			}
+
+
+		if (tab_count > 0 && nline_count == 0 && space_count == 0)
+			switch (undo_action->action_type)
+			{
+			    case GTK_SOURCE_UNDO_ACTION_DELETE:
+			    	return g_strdup (ngettext ("deletion of a tab", "deletion of tabs", tab_count));
+			    case GTK_SOURCE_UNDO_ACTION_INSERT:
+			    	return g_strdup (ngettext ("typing of a tab", "typing of tabs", tab_count));
+			    default:
+			    	g_return_val_if_reached (NULL);
+			}
+
+		switch (undo_action->action_type)
+		{
+			case GTK_SOURCE_UNDO_ACTION_DELETE:
+				return g_strdup (_("deletion of whitespace"));
+			case GTK_SOURCE_UNDO_ACTION_INSERT:
+				return g_strdup (_("typing of whitespace"));
+			default:
+				g_return_val_if_reached (NULL);
+		}
+	}
+	else
+	{
+		gchar *trunc, *action;
+
+		trunc = str_middle_truncate (text, GTK_SOURCE_UNDO_ACTION_TRUNC);
+		switch (undo_action->action_type)
+		{
+			case GTK_SOURCE_UNDO_ACTION_DELETE:
+				action = g_strdup_printf (_("deletion of '%s'"), trunc);
+				break;
+			case GTK_SOURCE_UNDO_ACTION_INSERT:
+				action = g_strdup_printf (_("typing of '%s'"), trunc);
+				break;
+			default:
+				g_return_val_if_reached (NULL);
+		}
+		g_free (trunc);
+		return action;
+	}
+}
+
+gchar *
+gtk_source_undo_manager_get_undo_action (GtkSourceUndoManager *um)
+{
+	GtkSourceUndoAction *undo_action;
+
+	g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), NULL);
+	g_return_val_if_fail (um->priv != NULL, NULL);
+	g_return_val_if_fail (um->priv->can_undo, NULL);
+
+	undo_action = g_list_nth_data (um->priv->actions, um->priv->next_redo + 1);
+	g_return_val_if_fail (undo_action != NULL, NULL);
+
+	return get_undo_redo_action_string (undo_action);
+}
+
+gchar *
+gtk_source_undo_manager_get_redo_action (GtkSourceUndoManager *um)
+{
+	GtkSourceUndoAction *undo_action;
+
+	g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), NULL);
+	g_return_val_if_fail (um->priv != NULL, NULL);
+	g_return_val_if_fail (um->priv->can_redo, NULL);
+
+	undo_action = g_list_nth_data (um->priv->actions, um->priv->next_redo);
+	g_return_val_if_fail (undo_action != NULL, NULL);
+
+	return get_undo_redo_action_string (undo_action);
 }
Index: gtksourceview/gtksourceundomanager.h
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourceundomanager.h,v
retrieving revision 1.2
diff -u -r1.2 gtksourceundomanager.h
--- gtksourceview/gtksourceundomanager.h	2 May 2003 17:27:53 -0000	1.2
+++ gtksourceview/gtksourceundomanager.h	15 Jun 2005 16:08:31 -0000
@@ -78,6 +78,8 @@
 								(GtkSourceUndoManager 	*um,
 				  	     			 gint		 	 undo_levels);
 
+gchar *			gtk_source_undo_manager_get_undo_action (GtkSourceUndoManager *um);
+gchar *			gtk_source_undo_manager_get_redo_action (GtkSourceUndoManager *um);
 #endif /* __GTK_SOURCE_UNDO_MANAGER_H__ */
 
 
Index: gtksourceview/gtksourceview.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourceview.c,v
retrieving revision 1.37
diff -u -r1.37 gtksourceview.c
--- gtksourceview/gtksourceview.c	1 Mar 2005 10:44:01 -0000	1.37
+++ gtksourceview/gtksourceview.c	15 Jun 2005 16:08:33 -0000
@@ -677,7 +677,10 @@
 {
 	GtkTextBuffer *buffer;
 	GtkWidget *menu_item;
-
+	gchar *do_action;
+	gchar *do_label;
+	gboolean can_do;
+    
 	buffer = gtk_text_view_get_buffer (text_view);
 	if (!buffer && !GTK_IS_SOURCE_BUFFER (buffer))
 		return;
@@ -688,23 +691,55 @@
 	gtk_widget_show (menu_item);
 
 	/* create redo menu_item. */
-	menu_item = gtk_image_menu_item_new_from_stock ("gtk-redo", NULL);
+	can_do = gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (buffer));
+	if (can_do)
+	{
+		do_action = gtk_source_buffer_get_redo_action (GTK_SOURCE_BUFFER (buffer));
+		do_label = g_strdup_printf (_("Redo %s"), do_action);
+
+		menu_item = gtk_image_menu_item_new_with_label (do_label);
+		gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
+		        gtk_image_new_from_stock ("gtk-redo", GTK_ICON_SIZE_MENU));
+
+		g_free (do_action);
+		g_free (do_label);
+	}
+	else
+	{
+		menu_item = gtk_image_menu_item_new_from_stock ("gtk-redo", NULL);
+	}
+
 	g_object_set_data (G_OBJECT (menu_item), "gtk-signal", "redo");
 	g_signal_connect (G_OBJECT (menu_item), "activate",
 			  G_CALLBACK (menu_item_activate_cb), text_view);
 	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
-	gtk_widget_set_sensitive (menu_item,
-				  gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (buffer)));
+	gtk_widget_set_sensitive (menu_item, can_do);
 	gtk_widget_show (menu_item);
 
 	/* create undo menu_item. */
-	menu_item = gtk_image_menu_item_new_from_stock ("gtk-undo", NULL);
+	can_do = gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (buffer));
+	if (can_do)
+	{
+		do_action = gtk_source_buffer_get_undo_action (GTK_SOURCE_BUFFER (buffer));
+		do_label = g_strdup_printf (_("Undo %s"), do_action);
+
+		menu_item = gtk_image_menu_item_new_with_label (do_label);
+		gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
+		        gtk_image_new_from_stock ("gtk-undo", GTK_ICON_SIZE_MENU));
+
+		g_free (do_action);
+		g_free (do_label);
+	}
+	else
+	{
+		menu_item = gtk_image_menu_item_new_from_stock ("gtk-undo", NULL);
+	}
+
 	g_object_set_data (G_OBJECT (menu_item), "gtk-signal", "undo");
 	g_signal_connect (G_OBJECT (menu_item), "activate",
 			  G_CALLBACK (menu_item_activate_cb), text_view);
 	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
-	gtk_widget_set_sensitive (menu_item, 
-				  gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (buffer)));
+	gtk_widget_set_sensitive (menu_item, can_do);
 	gtk_widget_show (menu_item);
 
 }
Index: tests/test-widget.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/tests/test-widget.c,v
retrieving revision 1.11
diff -u -r1.11 test-widget.c
--- tests/test-widget.c	8 Jan 2005 22:02:30 -0000	1.11
+++ tests/test-widget.c	15 Jun 2005 16:08:35 -0000
@@ -71,7 +71,10 @@
 						  gpointer         user_data);
 static void       print_preview_cb               (GtkAction       *action,
 						  gpointer         user_data);
-
+static void       undo_cb                        (GtkAction       *action,
+						  gpointer         user_data);
+static void       redo_cb                        (GtkAction       *action,
+						  gpointer         user_data); 
 static GtkWidget *create_view_window             (GtkSourceBuffer *buffer,
 						  GtkSourceView   *from);
 
@@ -79,10 +82,15 @@
 /* Actions & UI definition ---------------------------------------------------- */
 
 static GtkActionEntry buffer_action_entries[] = {
+	{ "EditMenu", NULL, "_Edit" },	
 	{ "Open", GTK_STOCK_OPEN, "_Open", "<control>O",
 	  "Open a file", G_CALLBACK (open_file_cb) },
 	{ "Quit", GTK_STOCK_QUIT, "_Quit", "<control>Q",
 	  "Exit the application", G_CALLBACK (gtk_main_quit) },
+	{ "Undo", GTK_STOCK_UNDO, "_Undo", "<control>Z",
+	  "Undo Last operation", G_CALLBACK (undo_cb) },
+	{ "Redo", GTK_STOCK_REDO, "_Redo", "<control><shift>Z",
+	  "Redo last operation", G_CALLBACK (redo_cb) },
 };
 
 static GtkActionEntry view_action_entries[] = {
@@ -160,12 +168,16 @@
 "      <separator/>"
 "      <menuitem action=\"Quit\"/>"
 "    </menu>"
+"    <menu action=\"EditMenu\">"
+"      <menuitem action=\"Undo\"/>"
+"      <menuitem action=\"Redo\"/>"
+"      <menuitem action=\"ShowLists\"/>"
+"    </menu>"
 "    <menu action=\"ViewMenu\">"
 "    </menu>"
 "  </menubar>"
 "</ui>";
 
-
 /* File loading code ----------------------------------------------------------------- */
 
 static void
@@ -568,6 +580,65 @@
 	gtk_widget_destroy (chooser);
 }
 
+static void 
+undo_cb (GtkAction *action, gpointer buffer)
+{
+	gtk_source_buffer_undo (GTK_SOURCE_BUFFER (buffer));
+}
+
+static void 
+redo_cb (GtkAction *action, gpointer buffer)
+{
+	gtk_source_buffer_redo (GTK_SOURCE_BUFFER (buffer));
+}
+
+static void
+can_undo (GtkSourceBuffer *doc, gboolean can, GtkActionGroup *group)
+{	
+	gchar *undo; 
+	gchar *label;
+	
+	gtk_action_set_sensitive (gtk_action_group_get_action (group, "Undo"), can);
+	
+	if (can)
+	{
+		undo = gtk_source_buffer_get_undo_action (doc);
+		label = g_strdup_printf ("Undo %s", undo);
+		
+		g_object_set (gtk_action_group_get_action (group, "Undo"), "label", label, NULL);
+			
+		g_free (undo);
+		g_free (label);
+	}
+	else
+	{
+		g_object_set (gtk_action_group_get_action (group, "Undo"), "label", "Undo", NULL);
+	}
+}
+
+static void
+can_redo (GtkSourceBuffer *doc, gboolean can, GtkActionGroup *group)
+{	
+	gchar *undo; 
+	gchar *label;
+	
+	gtk_action_set_sensitive (gtk_action_group_get_action (group, "Redo"), can);
+	
+	if (can)
+	{
+		undo = gtk_source_buffer_get_redo_action (doc);
+		label = g_strdup_printf ("Redo %s", undo);
+		
+		g_object_set (gtk_action_group_get_action (group, "Redo"), "label", label, NULL);
+			
+		g_free (undo);
+		g_free (label);
+	}
+	else
+	{
+		g_object_set (gtk_action_group_get_action (group, "Redo"), "label", "Redo", NULL);
+	}
+}
 
 /* View UI callbacks ------------------------------------------------------------------ */
 
@@ -948,6 +1019,17 @@
 
 	action = gtk_action_group_get_action (action_group, "TabsWidth8");
 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+	
+	/* Undo handling */
+	action_group = g_list_nth_data (groups, 1);
+	action = gtk_action_group_get_action (action_group, "Undo");
+	gtk_action_set_sensitive (action, gtk_source_buffer_can_undo (buffer));
+	
+	action = gtk_action_group_get_action (action_group, "Redo");
+	gtk_action_set_sensitive (action, gtk_source_buffer_can_redo (buffer));
+	
+	g_signal_connect (buffer, "can-undo", G_CALLBACK (can_undo), action_group);
+	g_signal_connect (buffer, "can-redo", G_CALLBACK (can_redo), action_group);
 	
 	return window;
 }
