From: Colin Leroy Date: Wed, 2 Aug 2006 16:30:28 +0000 (+0000) Subject: 2006-08-02 [colin] 2.4.0cvs17 X-Git-Tag: rel_2_5_0~208 X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=commitdiff_plain;h=2c42dd3fecaa37b5da8896781ffecc357ed51279 2006-08-02 [colin] 2.4.0cvs17 * src/mainwindow.c * src/prefs_common.c * src/textview.c * src/textview.h * src/gtk/about.c Allow per-level quote hiding, Allow toggling quotes by double-clicking them Rename RemoteURI to ClickableText to reflect the fact it's used for a variety of things --- diff --git a/ChangeLog b/ChangeLog index c0c069a32..f40277d6a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2006-08-02 [colin] 2.4.0cvs17 + + * src/mainwindow.c + * src/prefs_common.c + * src/textview.c + * src/textview.h + * src/gtk/about.c + Allow per-level quote hiding, + Allow toggling quotes by double-clicking them + Rename RemoteURI to ClickableText to reflect + the fact it's used for a variety of things + 2006-08-02 [wwp] 2.4.0cvs16 * src/mainwindow.c diff --git a/PATCHSETS b/PATCHSETS index 65c81d8ab..6e9827d45 100644 --- a/PATCHSETS +++ b/PATCHSETS @@ -1708,3 +1708,4 @@ ( cvs diff -u -r 1.1.2.14 -r 1.1.2.15 src/plugins/pgpcore/passphrase.c; ) > 2.4.0cvs14.patchset ( cvs diff -u -r 1.25.2.16 -r 1.25.2.17 src/matcher_parser_parse.y; ) > 2.4.0cvs15.patchset ( cvs diff -u -r 1.274.2.130 -r 1.274.2.131 src/mainwindow.c; ) > 2.4.0cvs16.patchset +( cvs diff -u -r 1.274.2.131 -r 1.274.2.132 src/mainwindow.c; cvs diff -u -r 1.204.2.94 -r 1.204.2.95 src/prefs_common.c; cvs diff -u -r 1.96.2.129 -r 1.96.2.130 src/textview.c; cvs diff -u -r 1.12.2.11 -r 1.12.2.12 src/textview.h; cvs diff -u -r 1.4.2.34 -r 1.4.2.35 src/gtk/about.c; ) > 2.4.0cvs17.patchset diff --git a/configure.ac b/configure.ac index 04095a469..697f911f7 100644 --- a/configure.ac +++ b/configure.ac @@ -11,7 +11,7 @@ MINOR_VERSION=4 MICRO_VERSION=0 INTERFACE_AGE=0 BINARY_AGE=0 -EXTRA_VERSION=16 +EXTRA_VERSION=17 EXTRA_RELEASE= EXTRA_GTK2_VERSION= diff --git a/src/gtk/about.c b/src/gtk/about.c index 3d48a428b..ba9ab98ad 100644 --- a/src/gtk/about.c +++ b/src/gtk/about.c @@ -690,7 +690,7 @@ static void about_textview_uri_update(GtkWidget *textview, gint x, gint y) } /* compare previous hovered link and this one - (here links must be unique in text buffer otherwise RemoteURI structures should be + (here links must be unique in text buffer otherwise ClickableText structures should be used as in textview.c) */ same = (uri != NULL && uri_hover != NULL && strcmp((char*)uri, (char*)uri_hover) == 0); diff --git a/src/mainwindow.c b/src/mainwindow.c index fc23b73b1..c371fe455 100644 --- a/src/mainwindow.c +++ b/src/mainwindow.c @@ -715,7 +715,10 @@ static GtkItemFactoryEntry mainwin_entries[] = {N_("/_View/Open in new _window"), "N", open_msg_cb, 0, NULL}, {N_("/_View/Mess_age source"), "U", view_source_cb, 0, NULL}, {N_("/_View/Show all headers"), "H", show_all_header_cb, 0, ""}, - {N_("/_View/Hide quotes"), "Q", hide_quotes_cb, 0, ""}, + {N_("/_View/Quotes"), NULL, NULL, 0, ""}, + {N_("/_View/Quotes/_Hide all"), "Q", hide_quotes_cb, 1, ""}, + {N_("/_View/Quotes/Hide from level _2"),NULL, hide_quotes_cb, 2, ""}, + {N_("/_View/Quotes/Hide from level _3"),NULL, hide_quotes_cb, 3, ""}, {N_("/_View/---"), NULL, NULL, 0, ""}, {N_("/_View/_Update summary"), "U", update_summary_cb, 0, NULL}, @@ -2141,7 +2144,7 @@ void main_window_set_menu_sensitive(MainWindow *mainwin) {"/View/Go to/Parent message" , M_SINGLE_TARGET_EXIST}, {"/View/Open in new window" , M_SINGLE_TARGET_EXIST}, {"/View/Show all headers" , M_SINGLE_TARGET_EXIST}, - {"/View/Hide quotes" , M_SINGLE_TARGET_EXIST}, + {"/View/Quotes" , M_SINGLE_TARGET_EXIST}, {"/View/Message source" , M_SINGLE_TARGET_EXIST}, {"/Message/Receive/Get from current account" @@ -2274,7 +2277,12 @@ void main_window_set_menu_sensitive(MainWindow *mainwin) SET_CHECK_MENU_ACTIVE("/View/Show all headers", mainwin->messageview->mimeview->textview->show_all_headers); SET_CHECK_MENU_ACTIVE("/View/Thread view", (state & M_THREADED) != 0); - SET_CHECK_MENU_ACTIVE("/View/Hide quotes", prefs_common.hide_quotes); + if (prefs_common.hide_quotes == 1) + SET_CHECK_MENU_ACTIVE("/View/Quotes/Hide all", TRUE); + if (prefs_common.hide_quotes == 2) + SET_CHECK_MENU_ACTIVE("/View/Quotes/Hide from level 2", TRUE); + if (prefs_common.hide_quotes == 3) + SET_CHECK_MENU_ACTIVE("/View/Quotes/Hide from level 3", TRUE); #undef SET_CHECK_MENU_ACTIVE @@ -3248,16 +3256,35 @@ static void show_all_header_cb(MainWindow *mainwin, guint action, GTK_CHECK_MENU_ITEM(widget)->active); } +#define SET_CHECK_MENU_ACTIVE(path, active) \ +{ \ + menuitem = gtk_item_factory_get_widget(ifactory, path); \ + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), active); \ +} + static void hide_quotes_cb(MainWindow *mainwin, guint action, GtkWidget *widget) { + GtkWidget *menuitem; + GtkItemFactory *ifactory = mainwin->menu_factory; + if (mainwin->menu_lock_count) return; + prefs_common.hide_quotes = - GTK_CHECK_MENU_ITEM(widget)->active; + GTK_CHECK_MENU_ITEM(widget)->active ? action : 0; + + mainwin->menu_lock_count++; + SET_CHECK_MENU_ACTIVE("/View/Quotes/Hide all", FALSE); + SET_CHECK_MENU_ACTIVE("/View/Quotes/Hide from level 2", FALSE); + SET_CHECK_MENU_ACTIVE("/View/Quotes/Hide from level 3", FALSE); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), prefs_common.hide_quotes > 0); + mainwin->menu_lock_count--; + summary_display_msg_selected(mainwin->summaryview, - GTK_CHECK_MENU_ITEM(widget)->active); + mainwin->summaryview->messageview->all_headers); } +#undef SET_CHECK_MENU_ACTIVE static void mark_cb(MainWindow *mainwin, guint action, GtkWidget *widget) { summary_mark(mainwin->summaryview); diff --git a/src/prefs_common.c b/src/prefs_common.c index 18212dbb0..150bf9cf7 100644 --- a/src/prefs_common.c +++ b/src/prefs_common.c @@ -790,7 +790,7 @@ static PrefParam param[] = { NULL, NULL, NULL}, {"live_dangerously", "FALSE", &prefs_common.live_dangerously, P_BOOL, NULL, NULL, NULL}, - {"hide_quotes", "FALSE", &prefs_common.hide_quotes, P_BOOL, + {"hide_quotes", "0", &prefs_common.hide_quotes, P_INT, NULL, NULL, NULL}, {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL} diff --git a/src/textview.c b/src/textview.c index ce8135f57..99c98983a 100644 --- a/src/textview.c +++ b/src/textview.c @@ -65,7 +65,7 @@ #include "filesel.h" #include "base64.h" -struct _RemoteURI +struct _ClickableText { gchar *uri; @@ -75,6 +75,11 @@ struct _RemoteURI guint start; guint end; + + gboolean is_quote; + gint quote_level; + gboolean q_expanded; + gchar *fg_color; }; gint previousquotelevel = -1; @@ -173,12 +178,12 @@ static gboolean textview_get_uri_range (TextView *textview, GtkTextTag *tag, GtkTextIter *start_iter, GtkTextIter *end_iter); -static RemoteURI *textview_get_uri_from_range (TextView *textview, +static ClickableText *textview_get_uri_from_range (TextView *textview, GtkTextIter *iter, GtkTextTag *tag, GtkTextIter *start_iter, GtkTextIter *end_iter); -static RemoteURI *textview_get_uri (TextView *textview, +static ClickableText *textview_get_uri (TextView *textview, GtkTextIter *iter, GtkTextTag *tag); static gboolean textview_uri_button_pressed (GtkTextTag *tag, @@ -197,9 +202,11 @@ static gboolean textview_smooth_scroll_page (TextView *textview, gboolean up); static gboolean textview_uri_security_check (TextView *textview, - RemoteURI *uri); + ClickableText *uri); static void textview_uri_list_remove_all (GSList *uri_list); +static void textview_toggle_quote (TextView *textview, ClickableText *uri); + static void open_uri_cb (TextView *textview, guint action, void *data); @@ -373,7 +380,7 @@ TextView *textview_create(void) static void textview_create_tags(GtkTextView *text, TextView *textview) { GtkTextBuffer *buffer; - GtkTextTag *tag; + GtkTextTag *tag, *qtag; static PangoFontDescription *font_desc, *bold_font_desc; if (!font_desc) @@ -434,10 +441,13 @@ static void textview_create_tags(GtkTextView *text, TextView *textview) tag = gtk_text_buffer_create_tag(buffer, "link", "foreground-gdk", &uri_color, NULL); + qtag = gtk_text_buffer_create_tag(buffer, "qlink", + NULL); gtk_text_buffer_create_tag(buffer, "link-hover", - "foreground-gdk", &uri_color, "underline", PANGO_UNDERLINE_SINGLE, NULL); + g_signal_connect(G_OBJECT(qtag), "event", + G_CALLBACK(textview_uri_button_pressed), textview); g_signal_connect(G_OBJECT(tag), "event", G_CALLBACK(textview_uri_button_pressed), textview); } @@ -568,8 +578,8 @@ void textview_show_part(TextView *textview, MimeInfo *mimeinfo, FILE *fp) "header", NULL) #define TEXT_INSERT_LINK(str, fname, udata) { \ - RemoteURI *uri; \ - uri = g_new(RemoteURI, 1); \ + ClickableText *uri; \ + uri = g_new0(ClickableText, 1); \ uri->uri = g_strdup(""); \ uri->start = gtk_text_iter_get_offset(&iter); \ gtk_text_buffer_insert_with_tags_by_name \ @@ -643,7 +653,7 @@ static void textview_add_part(TextView *textview, MimeInfo *mimeinfo) GdkPixbuf *pixbuf; GError *error = NULL; gchar *filename; - RemoteURI *uri; + ClickableText *uri; gchar *uri_str; filename = procmime_get_tmp_file_name(mimeinfo); @@ -681,7 +691,7 @@ static void textview_add_part(TextView *textview, MimeInfo *mimeinfo) uri_str = g_filename_to_uri(filename, NULL, NULL); if (uri_str) { - uri = g_new(RemoteURI, 1); + uri = g_new0(ClickableText, 1); uri->uri = uri_str; uri->start = gtk_text_iter_get_offset(&iter); @@ -846,6 +856,7 @@ static void textview_write_body(TextView *textview, MimeInfo *mimeinfo) gchar buf[BUFFSIZE]; CodeConverter *conv; const gchar *charset, *p, *cmd; + GSList *cur; if (textview->messageview->forced_charset) charset = textview->messageview->forced_charset; @@ -948,6 +959,16 @@ textview_default: conv_code_converter_destroy(conv); procmime_force_encoding(0); + + for (cur = textview->uri_list; cur; cur = cur->next) { + ClickableText *uri = (ClickableText *)cur->data; + if (!uri->is_quote) + continue; + if (!prefs_common.hide_quotes || + uri->quote_level+1 < prefs_common.hide_quotes) { + textview_toggle_quote(textview, uri); + } + } } static void textview_show_html(TextView *textview, FILE *fp, @@ -1013,6 +1034,15 @@ static void textview_show_ertf(TextView *textview, FILE *fp, return; \ } +#define ADD_TXT_POS_LATER(bp_, ep_, pti_) \ + if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \ + last = last->next; \ + last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \ + last->next = NULL; \ + } else { \ + g_warning("alloc error scanning URIs\n"); \ + } + /* textview_make_clickable_parts() - colorizes clickable parts */ static void textview_make_clickable_parts(TextView *textview, const gchar *fg_tag, @@ -1108,8 +1138,8 @@ static void textview_make_clickable_parts(TextView *textview, /* insert URIs */ for (last = head.next; last != NULL; normal_text = last->ep, last = last->next) { - RemoteURI *uri; - uri = g_new(RemoteURI, 1); + ClickableText *uri; + uri = g_new0(ClickableText, 1); if (last->bp - normal_text > 0) gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, @@ -1138,6 +1168,118 @@ static void textview_make_clickable_parts(TextView *textview, g_free(mybuf); } +/* textview_make_clickable_parts() - colorizes clickable parts */ +static void textview_make_clickable_parts_later(TextView *textview, + gint start, gint end) +{ + GtkTextView *text = GTK_TEXT_VIEW(textview->text); + GtkTextBuffer *buffer = gtk_text_view_get_buffer(text); + GtkTextIter start_iter, end_iter; + gchar *mybuf; + gint offset = 0; + /* parse table - in order of priority */ + struct table { + const gchar *needle; /* token */ + + /* token search function */ + gchar *(*search) (const gchar *haystack, + const gchar *needle); + /* part parsing function */ + gboolean (*parse) (const gchar *start, + const gchar *scanpos, + const gchar **bp_, + const gchar **ep_, + gboolean hdr); + /* part to URI function */ + gchar *(*build_uri) (const gchar *bp, + const gchar *ep); + }; + + static struct table parser[] = { + {"http://", strcasestr, get_uri_part, make_uri_string}, + {"https://", strcasestr, get_uri_part, make_uri_string}, + {"ftp://", strcasestr, get_uri_part, make_uri_string}, + {"sftp://", strcasestr, get_uri_part, make_uri_string}, + {"www.", strcasestr, get_uri_part, make_http_string}, + {"mailto:", strcasestr, get_uri_part, make_uri_string}, + {"@", strcasestr, get_email_part, make_email_string} + }; + const gint PARSE_ELEMS = sizeof parser / sizeof parser[0]; + + gint n; + const gchar *walk, *bp, *ep; + + struct txtpos { + const gchar *bp, *ep; /* text position */ + gint pti; /* index in parse table */ + struct txtpos *next; /* next */ + } head = {NULL, NULL, 0, NULL}, *last = &head; + + gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start); + gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end); + mybuf = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, FALSE); + offset = gtk_text_iter_get_offset(&start_iter); + + /* parse for clickable parts, and build a list of begin and end positions */ + for (walk = mybuf, n = 0;;) { + gint last_index = PARSE_ELEMS; + gchar *scanpos = NULL; + + /* FIXME: this looks phony. scanning for anything in the parse table */ + for (n = 0; n < PARSE_ELEMS; n++) { + gchar *tmp; + + tmp = parser[n].search(walk, parser[n].needle); + if (tmp) { + if (scanpos == NULL || tmp < scanpos) { + scanpos = tmp; + last_index = n; + } + } + } + + if (scanpos) { + /* check if URI can be parsed */ + if (parser[last_index].parse(walk, scanpos, &bp, &ep, FALSE) + && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) { + ADD_TXT_POS_LATER(bp, ep, last_index); + walk = ep; + } else + walk = scanpos + + strlen(parser[last_index].needle); + } else + break; + } + + /* colorize this line */ + if (head.next) { + const gchar *normal_text = mybuf; + + /* insert URIs */ + for (last = head.next; last != NULL; + normal_text = last->ep, last = last->next) { + ClickableText *uri; + uri = g_new0(ClickableText, 1); + uri->uri = parser[last->pti].build_uri(last->bp, + last->ep); + + gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, last->bp - mybuf + offset); + gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, last->ep - mybuf + offset); + + uri->start = gtk_text_iter_get_offset(&start_iter); + + gtk_text_buffer_apply_tag_by_name(buffer, "link", &start_iter, &end_iter); + + uri->end = gtk_text_iter_get_offset(&end_iter); + uri->filename = NULL; + textview->uri_list = + g_slist_append(textview->uri_list, uri); + } + } + + g_free(mybuf); +} + #undef ADD_TXT_POS static void textview_write_line(TextView *textview, const gchar *str, @@ -1194,11 +1336,32 @@ static void textview_write_line(TextView *textview, const gchar *str, textview->is_in_signature = TRUE; } - if ( prefs_common.hide_quotes == TRUE && quotelevel > -1) { + if (quotelevel > -1) { if ( previousquotelevel != quotelevel ) { -/* textview_make_clickable_parts(textview, fg_color, "link", buf, FALSE);*/ - textview_make_clickable_parts(textview, fg_color, "link", " [...]\n", FALSE); + ClickableText *uri; + uri = g_new0(ClickableText, 1); + uri->uri = g_strdup(""); + uri->data = g_strdup(buf); + uri->start = gtk_text_iter_get_offset(&iter); + gtk_text_buffer_insert_with_tags_by_name + (buffer, &iter, " [...]", -1, + "qlink", fg_color, NULL); + uri->end = gtk_text_iter_get_offset(&iter); + uri->filename = NULL; + uri->fg_color = g_strdup(fg_color); + uri->is_quote = TRUE; + uri->quote_level = quotelevel; + textview->uri_list = + g_slist_append(textview->uri_list, uri); + gtk_text_buffer_insert(buffer, &iter, " \n", -1); + previousquotelevel = quotelevel; + } else { + GSList *last = g_slist_last(textview->uri_list); + ClickableText *lasturi = (ClickableText *)last->data; + gchar *tmp = g_strdup_printf("%s%s", (gchar *)lasturi->data, buf); + g_free(lasturi->data); + lasturi->data = tmp; } } else { textview_make_clickable_parts(textview, fg_color, "link", buf, FALSE); @@ -1215,7 +1378,7 @@ void textview_write_link(TextView *textview, const gchar *str, GtkTextIter iter; gchar buf[BUFFSIZE]; gchar *bufp; - RemoteURI *r_uri; + ClickableText *r_uri; if (!str || *str == '\0') return; @@ -1253,7 +1416,7 @@ void textview_write_link(TextView *textview, const gchar *str, if (prefs_common.enable_color) { link_color = &uri_color; } - r_uri = g_new(RemoteURI, 1); + r_uri = g_new0(ClickableText, 1); r_uri->uri = g_strdup(uri); r_uri->start = gtk_text_iter_get_offset(&iter); gtk_text_buffer_insert_with_tags_by_name @@ -1949,7 +2112,7 @@ static void textview_uri_update(TextView *textview, gint x, gint y) { GtkTextBuffer *buffer; GtkTextIter start_iter, end_iter; - RemoteURI *uri = NULL; + ClickableText *uri = NULL; buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text)); @@ -1958,7 +2121,7 @@ static void textview_uri_update(TextView *textview, gint x, gint y) GtkTextIter iter; GSList *tags; GSList *cur; - + gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(textview->text), GTK_TEXT_WINDOW_WIDGET, x, y, &bx, &by); @@ -1971,13 +2134,16 @@ static void textview_uri_update(TextView *textview, gint x, gint y) char *name; g_object_get(G_OBJECT(tag), "name", &name, NULL); - if (!strcmp(name, "link") + + if ((!strcmp(name, "link") || !strcmp(name, "qlink")) && textview_get_uri_range(textview, &iter, tag, - &start_iter, &end_iter)) + &start_iter, &end_iter)) { + uri = textview_get_uri_from_range(textview, &iter, tag, &start_iter, &end_iter); + } g_free(name); if (uri) @@ -2014,7 +2180,8 @@ static void textview_uri_update(TextView *textview, gint x, gint y) if (uri) { char *trimmed_uri; - gtk_text_buffer_apply_tag_by_name(buffer, + if (!uri->is_quote) + gtk_text_buffer_apply_tag_by_name(buffer, "link-hover", &start_iter, &end_iter); @@ -2035,14 +2202,14 @@ static gboolean textview_get_uri_range(TextView *textview, return get_tag_range(iter, tag, start_iter, end_iter); } -static RemoteURI *textview_get_uri_from_range(TextView *textview, +static ClickableText *textview_get_uri_from_range(TextView *textview, GtkTextIter *iter, GtkTextTag *tag, GtkTextIter *start_iter, GtkTextIter *end_iter) { gint start_pos, end_pos, cur_pos; - RemoteURI *uri = NULL; + ClickableText *uri = NULL; GSList *cur; start_pos = gtk_text_iter_get_offset(start_iter); @@ -2050,7 +2217,7 @@ static RemoteURI *textview_get_uri_from_range(TextView *textview, cur_pos = gtk_text_iter_get_offset(iter); for (cur = textview->uri_list; cur != NULL; cur = cur->next) { - RemoteURI *uri_ = (RemoteURI *)cur->data; + ClickableText *uri_ = (ClickableText *)cur->data; if (start_pos == uri_->start && end_pos == uri_->end) { uri = uri_; @@ -2073,12 +2240,12 @@ static RemoteURI *textview_get_uri_from_range(TextView *textview, return uri; } -static RemoteURI *textview_get_uri(TextView *textview, +static ClickableText *textview_get_uri(TextView *textview, GtkTextIter *iter, GtkTextTag *tag) { GtkTextIter start_iter, end_iter; - RemoteURI *uri = NULL; + ClickableText *uri = NULL; if (textview_get_uri_range(textview, iter, tag, &start_iter, &end_iter)) @@ -2088,12 +2255,107 @@ static RemoteURI *textview_get_uri(TextView *textview, return uri; } +static void textview_shift_uris_after(TextView *textview, gint start, gint shift) +{ + GSList *cur; + for (cur = textview->uri_list; cur; cur = cur->next) { + ClickableText *uri = (ClickableText *)cur->data; + if (uri->start <= start) + continue; + uri->start += shift; + uri->end += shift; + } +} + +static void textview_remove_uris_in(TextView *textview, gint start, gint end) +{ + GSList *cur; + for (cur = textview->uri_list; cur; ) { + ClickableText *uri = (ClickableText *)cur->data; + if (uri->start > start && uri->end < end) { + cur = cur->next; + textview->uri_list = g_slist_remove(textview->uri_list, uri); + g_free(uri->uri); + g_free(uri->filename); + if (uri->is_quote) { + g_free(uri->fg_color); + g_free(uri->data); + /* (only free data in quotes uris) */ + } + g_free(uri); + } else { + cur = cur->next; + } + + } +} + +static void textview_toggle_quote(TextView *textview, ClickableText *uri) +{ + GtkTextIter start, end; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text)); + + if (!uri->is_quote) + return; + + gtk_text_buffer_get_iter_at_offset(buffer, &start, uri->start); + gtk_text_buffer_get_iter_at_offset(buffer, &end, uri->end); + if (textview->uri_hover) + gtk_text_buffer_remove_tag_by_name(buffer, + "link-hover", + &textview->uri_hover_start_iter, + &textview->uri_hover_end_iter); + textview->uri_hover = NULL; + gtk_text_buffer_remove_tag_by_name(buffer, + "qlink", + &start, + &end); + /* when shifting URIs start and end, we have to do it per-UTF8-char + * so use g_utf8_strlen(). OTOH, when inserting in the text buffer, + * we have to pass a number of bytes, so use strlen(). disturbing. */ + if (!uri->q_expanded) { + gtk_text_buffer_get_iter_at_offset(buffer, &start, uri->start); + gtk_text_buffer_get_iter_at_offset(buffer, &end, uri->end); + textview_shift_uris_after(textview, uri->start, + g_utf8_strlen((gchar *)uri->data, -1)-strlen(" [...]\n")); + gtk_text_buffer_delete(buffer, &start, &end); + gtk_text_buffer_get_iter_at_offset(buffer, &start, uri->start); + gtk_text_buffer_insert_with_tags_by_name + (buffer, &start, (gchar *)uri->data, + strlen((gchar *)uri->data)-1, + "qlink", (gchar *)uri->fg_color, NULL); + uri->end = gtk_text_iter_get_offset(&start); + textview_make_clickable_parts_later(textview, + uri->start, uri->end); + uri->q_expanded = TRUE; + } else { + gtk_text_buffer_get_iter_at_offset(buffer, &start, uri->start); + gtk_text_buffer_get_iter_at_offset(buffer, &end, uri->end); + textview_remove_uris_in(textview, uri->start, uri->end); + textview_shift_uris_after(textview, uri->start, + strlen(" [...]\n")-g_utf8_strlen((gchar *)uri->data, -1)); + gtk_text_buffer_delete(buffer, &start, &end); + gtk_text_buffer_get_iter_at_offset(buffer, &start, uri->start); + gtk_text_buffer_insert_with_tags_by_name + (buffer, &start, " [...]", -1, + "qlink", (gchar *)uri->fg_color, NULL); + uri->end = gtk_text_iter_get_offset(&start); + uri->q_expanded = FALSE; + } + if (textview->messageview->mainwin->cursor_count == 0) { + textview_cursor_normal(textview); + } else { + textview_cursor_wait(textview); + } +} static gboolean textview_uri_button_pressed(GtkTextTag *tag, GObject *obj, GdkEvent *event, GtkTextIter *iter, TextView *textview) { GdkEventButton *bevent; - RemoteURI *uri = NULL; + ClickableText *uri = NULL; + char *tagname; + gboolean qlink = FALSE; if (!event) return FALSE; @@ -2106,10 +2368,17 @@ static gboolean textview_uri_button_pressed(GtkTextTag *tag, GObject *obj, if (!uri) return FALSE; + g_object_get(G_OBJECT(tag), "name", &tagname, NULL); + + if (!strcmp(tagname, "qlink")) + qlink = TRUE; + + g_free(tagname); + bevent = (GdkEventButton *) event; /* doubleclick: open compose / add address / browser */ - if ((event->type == GDK_BUTTON_PRESS && bevent->button == 1) || + if ((event->type == (qlink ? GDK_2BUTTON_PRESS:GDK_BUTTON_PRESS) && bevent->button == 1) || bevent->button == 2 || bevent->button == 3) { if (uri->filename && !g_ascii_strncasecmp(uri->filename, "sc://", 5)) { MimeView *mimeview = @@ -2125,6 +2394,10 @@ static gboolean textview_uri_button_pressed(GtkTextTag *tag, GObject *obj, mimeview_handle_cmd(mimeview, "sc://menu_attachment", bevent, uri->data); } return TRUE; + } else if (qlink && bevent->button == 1) { + textview_toggle_quote(textview, uri); + return TRUE; + } else if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) { if (bevent->button == 3) { g_object_set_data( @@ -2152,7 +2425,7 @@ static gboolean textview_uri_button_pressed(GtkTextTag *tag, GObject *obj, textview_uri_security_check(textview, uri) == TRUE) open_uri(uri->uri, prefs_common.uri_cmd); - else if (bevent->button == 3) { + else if (bevent->button == 3 && !qlink) { g_object_set_data( G_OBJECT(textview->link_popup_menu), "menu_button", uri); @@ -2162,7 +2435,7 @@ static gboolean textview_uri_button_pressed(GtkTextTag *tag, GObject *obj, } return TRUE; } else { - if (bevent->button == 3) { + if (bevent->button == 3 && !qlink) { g_object_set_data( G_OBJECT(textview->file_popup_menu), "menu_button", uri); @@ -2188,7 +2461,7 @@ static gboolean textview_uri_button_pressed(GtkTextTag *tag, GObject *obj, *\return gboolean TRUE if the URL is ok, or if the user chose to open * it anyway, otherwise FALSE */ -static gboolean textview_uri_security_check(TextView *textview, RemoteURI *uri) +static gboolean textview_uri_security_check(TextView *textview, ClickableText *uri) { gchar *visible_str; gboolean retval = TRUE; @@ -2250,8 +2523,13 @@ static void textview_uri_list_remove_all(GSList *uri_list) for (cur = uri_list; cur != NULL; cur = cur->next) { if (cur->data) { - g_free(((RemoteURI *)cur->data)->uri); - g_free(((RemoteURI *)cur->data)->filename); + g_free(((ClickableText *)cur->data)->uri); + g_free(((ClickableText *)cur->data)->filename); + if (((ClickableText *)cur->data)->is_quote) { + g_free(((ClickableText *)cur->data)->fg_color); + g_free(((ClickableText *)cur->data)->data); + /* (only free data in quotes uris) */ + } g_free(cur->data); } } @@ -2261,7 +2539,7 @@ static void textview_uri_list_remove_all(GSList *uri_list) static void open_uri_cb (TextView *textview, guint action, void *data) { - RemoteURI *uri = g_object_get_data(G_OBJECT(textview->link_popup_menu), + ClickableText *uri = g_object_get_data(G_OBJECT(textview->link_popup_menu), "menu_button"); if (uri == NULL) return; @@ -2275,7 +2553,7 @@ static void open_uri_cb (TextView *textview, guint action, void *data) static void open_image_cb (TextView *textview, guint action, void *data) { - RemoteURI *uri = g_object_get_data(G_OBJECT(textview->file_popup_menu), + ClickableText *uri = g_object_get_data(G_OBJECT(textview->file_popup_menu), "menu_button"); static gchar *default_cmdline = DEFAULT_IMAGE_VIEWER_CMD; @@ -2331,7 +2609,7 @@ static void open_image_cb (TextView *textview, guint action, void *data) static void save_file_cb (TextView *textview, guint action, void *data) { - RemoteURI *uri = g_object_get_data(G_OBJECT(textview->file_popup_menu), + ClickableText *uri = g_object_get_data(G_OBJECT(textview->file_popup_menu), "menu_button"); gchar *filename = NULL; gchar *filepath = NULL; @@ -2399,7 +2677,7 @@ static void save_file_cb (TextView *textview, guint action, void *data) static void copy_uri_cb (TextView *textview, guint action, void *data) { - RemoteURI *uri = g_object_get_data(G_OBJECT(textview->link_popup_menu), + ClickableText *uri = g_object_get_data(G_OBJECT(textview->link_popup_menu), "menu_button"); if (uri == NULL) return; @@ -2413,7 +2691,7 @@ static void copy_uri_cb (TextView *textview, guint action, void *data) static void add_uri_to_addrbook_cb (TextView *textview, guint action, void *data) { gchar *fromname, *fromaddress; - RemoteURI *uri = g_object_get_data(G_OBJECT(textview->mail_popup_menu), + ClickableText *uri = g_object_get_data(G_OBJECT(textview->mail_popup_menu), "menu_button"); if (uri == NULL) return; @@ -2434,7 +2712,7 @@ static void add_uri_to_addrbook_cb (TextView *textview, guint action, void *data static void mail_to_uri_cb (TextView *textview, guint action, void *data) { PrefsAccount *account = NULL; - RemoteURI *uri = g_object_get_data(G_OBJECT(textview->mail_popup_menu), + ClickableText *uri = g_object_get_data(G_OBJECT(textview->mail_popup_menu), "menu_button"); if (uri == NULL) return; @@ -2452,7 +2730,7 @@ static void mail_to_uri_cb (TextView *textview, guint action, void *data) static void copy_mail_to_uri_cb (TextView *textview, guint action, void *data) { - RemoteURI *uri = g_object_get_data(G_OBJECT(textview->mail_popup_menu), + ClickableText *uri = g_object_get_data(G_OBJECT(textview->mail_popup_menu), "menu_button"); if (uri == NULL) return; diff --git a/src/textview.h b/src/textview.h index d93f8acda..000cc89c0 100644 --- a/src/textview.h +++ b/src/textview.h @@ -29,7 +29,7 @@ #include typedef struct _TextView TextView; -typedef struct _RemoteURI RemoteURI; +typedef struct _ClickableText ClickableText; #include "messageview.h" #include "procmime.h" @@ -58,7 +58,7 @@ struct _TextView MessageView *messageview; gint last_buttonpress; - RemoteURI *uri_hover; + ClickableText *uri_hover; GtkTextIter uri_hover_start_iter; GtkTextIter uri_hover_end_iter; GtkWidget *image;