Undo in compose window
- From: Peter Bloomfield <PeterBloomfield bellsouth net>
- To: Balsa list <balsa-list gnome org>
- Subject: Undo in compose window
- Date: Tue, 3 Jun 2003 16:24:32 -0400
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]