Undo in compose window



The lack of an Undo option in the compose window has been mentioned on 
the list (and has bitten me more often than I'd like!).  The attached 
patch (against current cvs) is a first cut at such an option (single-
level).  It saves a snapshot of the buffer before any of the other menu 
items is used, and also when text is deleted using the keyboard (for 
instance, when I start typing with a block of text highlighted for 
copying, and forget that it'll be replaced!).  The Undo button restores 
the snapshot.

Restoring the snapshot loses any other changes you've made since it was 
taken, which might be a lot of typing (;-(), and there's no Redo option 
if you decide that Undo was worse than nothing.  So there's a lot of 
room for improvement!

Comments and suggestions welcome, as always.

Peter
? Changes
? autom4te-2.53.cache
? autom4te.cache
? libbalsa/stEoTSMe
? po/po2tbl.sed.in
? website/autom4te-2.53.cache
? website/autom4te.cache
Index: src/sendmsg-window.c
===================================================================
RCS file: /cvs/gnome/balsa/src/sendmsg-window.c,v
retrieving revision 1.416
diff -u -r1.416 sendmsg-window.c
--- src/sendmsg-window.c	3 Jun 2003 04:57:57 -0000	1.416
+++ src/sendmsg-window.c	3 Jun 2003 20:12:49 -0000
@@ -155,6 +155,9 @@
 /* helpers */
 static gboolean sw_do_popup(GnomeIconList * ilist, GdkEventButton * event);
 static gint sw_header_height(BalsaSendmsg * bsmsg);
+static void copy_buffer(GtkTextBuffer * src_buffer,
+                        GtkTextBuffer * dst_buffer);
+static void save_buffer(BalsaSendmsg * bsmsg);
 
 /* Standard DnD types */
 enum {
@@ -175,6 +178,7 @@
     {"x-application/x-email", 0, TARGET_EMAIL}
 };
 
+static void undo_cb(GtkWidget * widget, BalsaSendmsg * bsmsg);
 static void cut_cb(GtkWidget * widget, BalsaSendmsg * bsmsg);
 static void copy_cb(GtkWidget * widget, BalsaSendmsg * bsmsg);
 static void paste_cb(GtkWidget * widget, BalsaSendmsg * bsmsg);
@@ -235,47 +239,50 @@
 /* Cut, Copy&Paste are in our case just a placeholders because they work
    anyway */
 static GnomeUIInfo edit_menu[] = {
+#define EDIT_MENU_UNDO 0
+    GNOMEUIINFO_MENU_UNDO_ITEM(undo_cb, NULL),
+    GNOMEUIINFO_SEPARATOR,
     GNOMEUIINFO_MENU_CUT_ITEM(cut_cb, NULL),
     GNOMEUIINFO_MENU_COPY_ITEM(copy_cb, NULL),
     GNOMEUIINFO_MENU_PASTE_ITEM(paste_cb, NULL),
     GNOMEUIINFO_MENU_SELECT_ALL_ITEM(select_all_cb, NULL),
     GNOMEUIINFO_SEPARATOR,
-#define EDIT_MENU_WRAP_BODY 5
+#define EDIT_MENU_WRAP_BODY 7
     {GNOME_APP_UI_ITEM, N_("_Wrap Body"), N_("Wrap message lines"),
      (gpointer) wrap_body_cb, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
-     GDK_z, GDK_CONTROL_MASK, NULL},
+     GDK_b, GDK_CONTROL_MASK, NULL},
     GNOMEUIINFO_SEPARATOR,
-#define EDIT_MENU_ADD_SIGNATURE 7
+#define EDIT_MENU_ADD_SIGNATURE 9
     {GNOME_APP_UI_ITEM, N_("Insert Si_gnature"), NULL,
      (gpointer) insert_signature_cb, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
      GDK_g, GDK_CONTROL_MASK, NULL},
-#define EDIT_MENU_QUOTE 8
+#define EDIT_MENU_QUOTE 10
     {GNOME_APP_UI_ITEM, N_("_Quote Message(s)"), NULL,
      (gpointer) quote_messages_cb, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
      0, 0, NULL},
     GNOMEUIINFO_SEPARATOR,
-#define EDIT_MENU_REFLOW_PARA 10
+#define EDIT_MENU_REFLOW_PARA 12
     {GNOME_APP_UI_ITEM, N_("_Reflow Paragraph"), NULL,
      (gpointer) reflow_par_cb, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
      GDK_r, GDK_CONTROL_MASK, NULL},
-#define EDIT_MENU_REFLOW_MESSAGE 11
+#define EDIT_MENU_REFLOW_MESSAGE 13
     {GNOME_APP_UI_ITEM, N_("R_eflow Message"), NULL,
      (gpointer) reflow_body_cb, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
      GDK_r, GDK_CONTROL_MASK | GDK_SHIFT_MASK, NULL},
     GNOMEUIINFO_SEPARATOR,
-#define EDIT_MENU_SPELL_CHECK 13
+#define EDIT_MENU_SPELL_CHECK 15
     GNOMEUIINFO_ITEM_STOCK(N_("C_heck Spelling"), 
                            N_("Check the spelling of the message"),
                            spell_check_cb,
                            GTK_STOCK_SPELL_CHECK),
     GNOMEUIINFO_SEPARATOR,
-#define EDIT_MENU_SELECT_IDENT 15
+#define EDIT_MENU_SELECT_IDENT 17
     GNOMEUIINFO_ITEM_STOCK(N_("Select _Identity..."), 
                            N_("Select the Identity to use for the message"),
                            change_identity_dialog_cb,
                            BALSA_PIXMAP_MENU_IDENTITY),
     GNOMEUIINFO_SEPARATOR,
-#define EDIT_MENU_EDIT_GNOME 17
+#define EDIT_MENU_EDIT_GNOME 19
     GNOMEUIINFO_ITEM_STOCK(N_("_Edit with Gnome-Editor"),
                            N_("Edit the current message with "
                               "the default Gnome editor"),
@@ -718,6 +725,8 @@
     if (bsmsg->wrap_timeout_id)
         g_source_remove(bsmsg->wrap_timeout_id);
 
+    g_object_unref(bsmsg->buffer2);
+
     g_free(bsmsg);
 
     if (quit_on_close) {
@@ -869,6 +878,8 @@
     GtkTextIter start, end;
     gchar *p;
 
+    save_buffer(bsmsg);
+
     strcpy(filename, TMP_PATTERN);
     tmpfd = mkstemp(filename);
     tmp   = fdopen(tmpfd, "w+");
@@ -1610,10 +1621,12 @@
         gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
     GtkWidget *index =
 	balsa_window_find_current_index(balsa_app.main_window);
+    GList *l;
     
-    if (index) {
-	GList *node, *l = balsa_index_selected_list(BALSA_INDEX(index));
+    if (index && (l = balsa_index_selected_list(BALSA_INDEX(index)))) {
+	GList *node;
     
+        save_buffer(bsmsg);
 	for (node = l; node; node = g_list_next(node)) {
 	    LibBalsaMessage *message = node->data;
 	    GString *body = quoteBody(bsmsg, message, type);
@@ -2075,6 +2088,8 @@
                            pango_font_description_from_string
                            (balsa_app.message_font));
     buffer = gtk_text_view_get_buffer(text_view);
+    bsmsg->buffer2 =
+         gtk_text_buffer_new(gtk_text_buffer_get_tag_table(buffer));
     gtk_text_buffer_create_tag(buffer, "soft", NULL);
     gtk_text_buffer_create_tag(buffer, "url", NULL);
     gtk_text_view_set_editable(text_view, TRUE);
@@ -2330,6 +2345,7 @@
         gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
     
     if ((signature = read_signature(bsmsg)) != NULL) {
+        save_buffer(bsmsg);
 	if (bsmsg->ident->sig_separator
 	    && g_ascii_strncasecmp(signature, "--\n", 3)
 	    && g_ascii_strncasecmp(signature, "-- \n", 4)) {
@@ -2464,9 +2480,11 @@
 
     bsmsg->wrap_timeout_id = 0;
     g_signal_handler_block(buffer, bsmsg->changed_sig_id);
+    g_signal_handler_block(buffer, bsmsg->delete_range_sig_id);
     libbalsa_unwrap_buffer(buffer, &now, 1);
     libbalsa_wrap_view(text_view, balsa_app.wraplength);
     g_signal_handler_unblock(buffer, bsmsg->changed_sig_id);
+    g_signal_handler_unblock(buffer, bsmsg->delete_range_sig_id);
     gtk_text_view_scroll_to_mark(text_view,
                                  gtk_text_buffer_get_insert(buffer),
                                  0, FALSE, 0, 0);
@@ -2490,6 +2508,13 @@
 }
 
 static void
+delete_range_cb(GtkTextBuffer * buffer, GtkTextIter * iter1,
+                GtkTextIter * iter2, BalsaSendmsg * bsmsg)
+{
+    save_buffer(bsmsg);
+}
+
+static void
 set_entry_from_address_list(LibBalsaAddressEntry * address_entry,
                             GList * list)
 {
@@ -2713,6 +2738,7 @@
     fill_language_menu();
 
     gnome_app_create_menus_with_data(GNOME_APP(window), main_menu, bsmsg);
+    gtk_widget_set_sensitive(edit_menu[EDIT_MENU_UNDO].widget, FALSE);
     /*
      * `Reflow paragraph' and `Reflow message' don't seem to make much
      * sense when we're using `format=flowed'
@@ -2836,6 +2862,10 @@
         g_signal_connect(G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW
                                                            (bsmsg->text))),
                          "changed", G_CALLBACK(text_changed), bsmsg);
+    bsmsg->delete_range_sig_id =
+        g_signal_connect(gtk_text_view_get_buffer
+                         (GTK_TEXT_VIEW(bsmsg->text)), "delete-range",
+                         G_CALLBACK(delete_range_cb), bsmsg);
     if (type == SEND_CONTINUE)
 	continueBody(bsmsg, message);
     else
@@ -3113,6 +3143,8 @@
     }
 
     buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
+    save_buffer(bsmsg);
+    gtk_widget_set_sensitive(edit_menu[EDIT_MENU_UNDO].widget, TRUE);
     string = NULL;
     len = libbalsa_readfile(fl, &string);
     fclose(fl);
@@ -3585,6 +3617,43 @@
 }
 
 static void
+undo_cb(GtkWidget * widget, BalsaSendmsg * bsmsg)
+{
+    GtkTextBuffer *buffer =
+        gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
+
+    g_signal_handler_block(buffer, bsmsg->changed_sig_id);
+    g_signal_handler_block(buffer, bsmsg->delete_range_sig_id);
+    copy_buffer(bsmsg->buffer2, buffer);
+    g_signal_handler_unblock(buffer, bsmsg->changed_sig_id);
+    g_signal_handler_unblock(buffer, bsmsg->delete_range_sig_id);
+
+    gtk_widget_set_sensitive(edit_menu[EDIT_MENU_UNDO].widget, FALSE);
+}
+
+static void
+save_buffer(BalsaSendmsg * bsmsg)
+{
+    GtkTextBuffer *buffer =
+        gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
+
+    copy_buffer(buffer, bsmsg->buffer2);
+
+    gtk_widget_set_sensitive(edit_menu[EDIT_MENU_UNDO].widget, TRUE);
+}
+
+static void
+copy_buffer(GtkTextBuffer * src_buffer, GtkTextBuffer * dst_buffer)
+{
+    GtkTextIter start, end, iter;
+
+    gtk_text_buffer_get_bounds(src_buffer, &start, &end);
+    gtk_text_buffer_set_text(dst_buffer, "", 0);
+    gtk_text_buffer_get_start_iter(dst_buffer, &iter);
+    gtk_text_buffer_insert_range(dst_buffer, &iter, &start, &end);
+}
+
+static void
 clipboard_helper(BalsaSendmsg * bsmsg, gchar * signal)
 {
     guint signal_id;
@@ -3600,6 +3669,7 @@
 static void
 cut_cb(GtkWidget * widget, BalsaSendmsg * bsmsg)
 {
+    save_buffer(bsmsg);
     clipboard_helper(bsmsg, "cut-clipboard");
 }
 
@@ -3612,6 +3682,7 @@
 static void
 paste_cb(GtkWidget * widget, BalsaSendmsg * bsmsg)
 {
+    save_buffer(bsmsg);
     clipboard_helper(bsmsg, "paste-clipboard");
 }
 
@@ -3635,13 +3706,17 @@
     GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view);
     GtkTextIter start, end;
 
+    save_buffer(bsmsg);
+
     gtk_text_buffer_get_bounds(buffer, &start, &end);
 
     if (bsmsg->flow) {
         g_signal_handler_block(buffer, bsmsg->changed_sig_id);
+        g_signal_handler_block(buffer, bsmsg->delete_range_sig_id);
         libbalsa_unwrap_buffer(buffer, &start, -1);
         libbalsa_wrap_view(text_view, balsa_app.wraplength);
         g_signal_handler_unblock(buffer, bsmsg->changed_sig_id);
+        g_signal_handler_unblock(buffer, bsmsg->delete_range_sig_id);
     } else {
         GtkTextIter now;
         gint pos;
@@ -3666,13 +3741,16 @@
 }
 
 static void
-do_reflow(GtkTextView * txt, gint mode)
+do_reflow(BalsaSendmsg * bsmsg, gint mode)
 {
+    GtkTextBuffer *buffer =
+        gtk_text_view_get_buffer(GTK_TEXT_VIEW(bsmsg->text));
     gint pos;
     gchar *the_text;
-    GtkTextBuffer *buffer = gtk_text_view_get_buffer(txt);
     GtkTextIter start, end, now;
 
+    save_buffer(bsmsg);
+
     gtk_text_buffer_get_bounds(buffer, &start, &end);
     the_text = gtk_text_iter_get_text(&start, &end);
 
@@ -3686,7 +3764,8 @@
     libbalsa_insert_with_url(buffer, the_text, NULL, NULL, NULL);
     gtk_text_buffer_get_iter_at_offset(buffer, &now, pos);
     gtk_text_buffer_place_cursor(buffer, &now);
-    gtk_text_view_scroll_to_mark(txt, gtk_text_buffer_get_insert(buffer),
+    gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(bsmsg->text),
+                                 gtk_text_buffer_get_insert(buffer),
                                  0, FALSE, 0, 0);
     g_free(the_text);
 }
@@ -3694,13 +3773,13 @@
 static void
 reflow_par_cb(GtkWidget * widget, BalsaSendmsg * bsmsg)
 {
-    do_reflow(GTK_TEXT_VIEW(bsmsg->text), 0);
+    do_reflow(bsmsg, 0);
 }
 
 static void
 reflow_body_cb(GtkWidget * widget, BalsaSendmsg * bsmsg)
 {
-    do_reflow(GTK_TEXT_VIEW(bsmsg->text), -1);
+    do_reflow(bsmsg, -1);
 }
 
 /* To field "changed" signal callback. */
Index: src/sendmsg-window.h
===================================================================
RCS file: /cvs/gnome/balsa/src/sendmsg-window.h,v
retrieving revision 1.62
diff -u -r1.62 sendmsg-window.h
--- src/sendmsg-window.h	3 Jun 2003 04:57:58 -0000	1.62
+++ src/sendmsg-window.h	3 Jun 2003 20:12:49 -0000
@@ -77,6 +77,7 @@
 	                        /* the config */
 	gulong delete_sig_id;
         gulong changed_sig_id;
+        gulong delete_range_sig_id;
         guint wrap_timeout_id;
 	gboolean modified;
 	gboolean flow;          /* send format=flowed */ 
@@ -92,6 +93,8 @@
 	GtkWidget *gpg_encrypt_menu_item;
 #endif
         GtkWidget *header_table;
+
+        GtkTextBuffer *buffer2;       /* Undo buffer. */
     };
 
     BalsaSendmsg *sendmsg_window_new(GtkWidget *, LibBalsaMessage *,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]