From: Paul Mangan Date: Sat, 6 Nov 2004 09:39:33 +0000 (+0000) Subject: 2004-11-06 [paul] 0.9.12cvs139.2 X-Git-Tag: gtk2_win32_last_merge~109 X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=commitdiff_plain;h=d5aac843f58034041dae845b9c929c0f1cdd90be 2004-11-06 [paul] 0.9.12cvs139.2 * AUTHORS * src/textview.c * src/textview.h textview URI handling. hovering over a link displays it in the status bar, and changes the mouse pointer to a hand cursor. a link is activated with a single click. (Modified from the) patch by Jean-Yves Lefort --- diff --git a/AUTHORS b/AUTHORS index 2ec6ca0aa..9ed3a286f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -210,3 +210,4 @@ contributors (beside the above; based on Changelog) Stephan Sachse Thomas Gilgin Guy Rouillier + Jean-Yves Lefort diff --git a/ChangeLog-gtk2.claws b/ChangeLog-gtk2.claws index 0a18c95c4..6af87d90c 100644 --- a/ChangeLog-gtk2.claws +++ b/ChangeLog-gtk2.claws @@ -1,3 +1,16 @@ +2004-11-06 [paul] 0.9.12cvs139.2 + + * AUTHORS + * src/textview.c + * src/textview.h + textview URI handling. hovering over a + link displays it in the status bar, + and changes the mouse pointer to a hand + cursor. a link is activated with a single + click. (Modified from the) patch by Jean-Yves + Lefort + + 2004-11-05 [colin] 0.9.12cvs139.1 * src/compose.c diff --git a/PATCHSETS b/PATCHSETS index e119bc60f..a3b6d4b1f 100644 --- a/PATCHSETS +++ b/PATCHSETS @@ -215,3 +215,4 @@ ( cvs diff -u -r 1.2504.2.28 -r 1.2504.2.29 ChangeLog.claws; cvs diff -u -r 1.654.2.247 -r 1.654.2.248 configure.ac; cvs diff -u -r 1.53.2.7 -r 1.53.2.8 po/POTFILES.in; cvs diff -u -r 1.3.2.2 -r 1.3.2.3 src/prefs_ext_prog.c; cvs diff -u -r 1.49.2.15 -r 1.49.2.16 src/procmime.c; cvs diff -u -r 1.2.2.2 -r 1.2.2.3 tools/kdeservicemenu/README; cvs diff -u -r 1.3.2.1 -r 1.3.2.2 tools/kdeservicemenu/template_sylpheed-attach-files.desktop; cvs diff -u -r 1.3.2.1 -r 1.3.2.2 tools/kdeservicemenu/template_sylpheed-compress-attach.desktop; ) > 0.9.12cvs138.1.patchset ( cvs diff -u -r 1.382.2.57 -r 1.382.2.58 src/compose.c; cvs diff -u -r 1.49.2.16 -r 1.49.2.17 src/procmime.c; ) > 0.9.12cvs139.1.patchset ( cvs diff -u -r 1.382.2.58 -r 1.382.2.59 src/compose.c; cvs diff -u -r 1.49.2.17 -r 1.49.2.18 src/procmime.c; ) > 0.9.12cvs139.1.patchset +( cvs diff -u -r 1.100.2.5 -r 1.100.2.6 AUTHORS; cvs diff -u -r 1.96.2.25 -r 1.96.2.26 src/textview.c; cvs diff -u -r 1.12.2.2 -r 1.12.2.3 src/textview.h; ) > 0.9.12cvs139.2.patchset diff --git a/configure.ac b/configure.ac index f4f32e026..f463cc13d 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,7 @@ INTERFACE_AGE=0 BINARY_AGE=0 EXTRA_VERSION=139 EXTRA_RELEASE= -EXTRA_GTK2_VERSION=.1 +EXTRA_GTK2_VERSION=.2 if test \( $EXTRA_VERSION -eq 0 \) -o \( "x$EXTRA_RELEASE" != "x" \); then VERSION=${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}${EXTRA_RELEASE}${EXTRA_GTK2_VERSION} diff --git a/src/textview.c b/src/textview.c index 97d8703d4..bfccff662 100644 --- a/src/textview.c +++ b/src/textview.c @@ -52,8 +52,6 @@ #include "mimeview.h" #include "alertpanel.h" -typedef struct _RemoteURI RemoteURI; - struct _RemoteURI { gchar *uri; @@ -99,6 +97,8 @@ static GdkColor error_color = { #endif +static GdkCursor *hand_cursor = NULL; + #define TEXTVIEW_STATUSBAR_PUSH(textview, str) \ { \ gtk_statusbar_push(GTK_STATUSBAR(textview->messageview->statusbar), \ @@ -137,12 +137,40 @@ static GPtrArray *textview_scan_header (TextView *textview, static void textview_show_header (TextView *textview, GPtrArray *headers); -static gint textview_key_pressed (GtkWidget *widget, - GdkEventKey *event, - TextView *textview); -static gboolean textview_uri_button_pressed(GtkTextTag *tag, GObject *obj, - GdkEvent *event, GtkTextIter *iter, - TextView *textview); +static gint textview_key_pressed (GtkWidget *widget, + GdkEventKey *event, + TextView *textview); +static gboolean textview_motion_notify (GtkWidget *widget, + GdkEventMotion *motion, + TextView *textview); +static gboolean textview_leave_notify (GtkWidget *widget, + GdkEventCrossing *event, + TextView *textview); +static gboolean textview_visibility_notify (GtkWidget *widget, + GdkEventVisibility *event, + TextView *textview); +static void textview_uri_update (TextView *textview, + gint x, + gint y); +static gboolean textview_get_uri_range (TextView *textview, + GtkTextIter *iter, + GtkTextTag *tag, + GtkTextIter *start_iter, + GtkTextIter *end_iter); +static RemoteURI *textview_get_uri_from_range (TextView *textview, + GtkTextIter *iter, + GtkTextTag *tag, + GtkTextIter *start_iter, + GtkTextIter *end_iter); +static RemoteURI *textview_get_uri (TextView *textview, + GtkTextIter *iter, + GtkTextTag *tag); +static gboolean textview_uri_button_pressed (GtkTextTag *tag, + GObject *obj, + GdkEvent *event, + GtkTextIter *iter, + TextView *textview); + static void textview_smooth_scroll_do (TextView *textview, gfloat old_value, gfloat last_value, @@ -185,6 +213,7 @@ TextView *textview_create(void) /* create GtkSText widgets for single-byte and multi-byte character */ text = gtk_text_view_new(); + gtk_widget_add_events(text, GDK_LEAVE_NOTIFY_MASK); gtk_widget_show(text); gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR); @@ -211,9 +240,21 @@ TextView *textview_create(void) gtk_container_add(GTK_CONTAINER(scrolledwin), text); + if (!hand_cursor) + hand_cursor = gdk_cursor_new(GDK_HAND2); + g_signal_connect(G_OBJECT(text), "key_press_event", G_CALLBACK(textview_key_pressed), textview); + g_signal_connect(G_OBJECT(text), "motion_notify_event", + G_CALLBACK(textview_motion_notify), + textview); + g_signal_connect(G_OBJECT(text), "leave_notify_event", + G_CALLBACK(textview_leave_notify), + textview); + g_signal_connect(G_OBJECT(text), "visibility_notify_event", + G_CALLBACK(textview_visibility_notify), + textview); gtk_widget_show(scrolledwin); @@ -229,7 +270,6 @@ TextView *textview_create(void) textview->body_pos = 0; textview->show_all_headers = FALSE; textview->last_buttonpress = GDK_NOTHING; - textview->show_url_msgid = 0; return textview; } @@ -268,6 +308,9 @@ static void textview_create_tags(GtkTextView *text, TextView *textview) tag = gtk_text_buffer_create_tag(buffer, "link", "foreground-gdk", &uri_color, NULL); + gtk_text_buffer_create_tag(buffer, "link-hover", + "underline", PANGO_UNDERLINE_SINGLE, + NULL); g_signal_connect(G_OBJECT(tag), "event", G_CALLBACK(textview_uri_button_pressed), textview); @@ -1753,15 +1796,6 @@ static gint textview_key_pressed(GtkWidget *widget, GdkEventKey *event, return TRUE; } -static gint show_url_timeout_cb(gpointer data) -{ - TextView *textview = (TextView *)data; - - TEXTVIEW_STATUSBAR_POP(textview); - textview->show_url_timeout_tag = 0; - return FALSE; -} - /*! *\brief Check to see if a web URL has been disguised as a different * URL (possible with HTML email). @@ -1826,96 +1860,241 @@ static gboolean uri_security_check(RemoteURI *uri, TextView *textview) return retval; } -static gboolean textview_uri_button_pressed(GtkTextTag *tag, GObject *obj, - GdkEvent *event, GtkTextIter *iter, - TextView *textview) +static gboolean textview_motion_notify(GtkWidget *widget, + GdkEventMotion *event, + TextView *textview) { + textview_uri_update(textview, event->x, event->y); + gdk_window_get_pointer(widget->window, NULL, NULL, NULL); + + return FALSE; +} + +static gboolean textview_leave_notify(GtkWidget *widget, + GdkEventCrossing *event, + TextView *textview) +{ + textview_uri_update(textview, -1, -1); + + return FALSE; +} + +static gboolean textview_visibility_notify(GtkWidget *widget, + GdkEventVisibility *event, + TextView *textview) +{ + gint wx, wy; + + gdk_window_get_pointer(widget->window, &wx, &wy, NULL); + textview_uri_update(textview, wx, wy); + + return FALSE; +} + +static void textview_uri_update(TextView *textview, gint x, gint y) +{ + GtkTextBuffer *buffer; GtkTextIter start_iter, end_iter; - gint start_pos, end_pos; - GdkEventButton *bevent; - GSList *cur; - gchar *trimmed_uri; + RemoteURI *uri = NULL; - if (event->type != GDK_BUTTON_PRESS && event->type != GDK_2BUTTON_PRESS - && event->type != GDK_MOTION_NOTIFY) - return FALSE; + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text)); + + if (x != -1 && y != -1) { + gint bx, by; + 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); + gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(textview->text), + &iter, bx, by); + + tags = gtk_text_iter_get_tags(&iter); + for (cur = tags; cur != NULL; cur = cur->next) { + GtkTextTag *tag = cur->data; + char *name; + + g_object_get(G_OBJECT(tag), "name", &name, NULL); + if (!strcmp(name, "link") + && textview_get_uri_range(textview, &iter, tag, + &start_iter, &end_iter)) + uri = textview_get_uri_from_range(textview, + &iter, tag, + &start_iter, + &end_iter); + g_free(name); + + if (uri) + break; + } + g_slist_free(tags); + } + + if (uri != textview->uri_hover) { + GdkWindow *window; + + 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 = uri; + if (uri) { + textview->uri_hover_start_iter = start_iter; + textview->uri_hover_end_iter = end_iter; + } + + window = gtk_text_view_get_window(GTK_TEXT_VIEW(textview->text), + GTK_TEXT_WINDOW_TEXT); + gdk_window_set_cursor(window, uri ? hand_cursor : NULL); - bevent = (GdkEventButton *) event; + TEXTVIEW_STATUSBAR_POP(textview); + + if (uri) { + char *trimmed_uri; - /* get start and end positions */ - start_iter = *iter; - if(!gtk_text_iter_backward_to_tag_toggle(&start_iter, tag)) { + gtk_text_buffer_apply_tag_by_name(buffer, + "link-hover", + &start_iter, + &end_iter); + + trimmed_uri = trim_string(uri->uri, 60); + TEXTVIEW_STATUSBAR_PUSH(textview, trimmed_uri); + g_free(trimmed_uri); + } + } +} + +static gboolean textview_get_uri_range(TextView *textview, + GtkTextIter *iter, + GtkTextTag *tag, + GtkTextIter *start_iter, + GtkTextIter *end_iter) +{ + GtkTextIter _start_iter, _end_iter; + + _start_iter = *iter; + if(!gtk_text_iter_backward_to_tag_toggle(&_start_iter, tag)) { debug_print("Can't find start."); return FALSE; } - start_pos = gtk_text_iter_get_offset(&start_iter); - end_iter = *iter; - if(!gtk_text_iter_forward_to_tag_toggle(&end_iter, tag)) { + _end_iter = *iter; + if(!gtk_text_iter_forward_to_tag_toggle(&_end_iter, tag)) { debug_print("Can't find end"); return FALSE; } - end_pos = gtk_text_iter_get_offset(&end_iter); - /* search current uri */ - for (cur = textview->uri_list; cur != NULL; cur = cur->next) { - RemoteURI *uri = (RemoteURI *)cur->data; + *start_iter = _start_iter; + *end_iter = _end_iter; - if (start_pos != uri->start || end_pos != uri->end) - continue; + return TRUE; +} - trimmed_uri = trim_string(uri->uri, 60); - /* hover or single click: display url in statusbar */ - - if (event->type == GDK_MOTION_NOTIFY - || (event->type == GDK_BUTTON_PRESS && bevent->button == 1)) { - if (textview->messageview->mainwin) { - TEXTVIEW_STATUSBAR_PUSH(textview, trimmed_uri); - textview->show_url_timeout_tag = gtk_timeout_add - (4000, show_url_timeout_cb, textview); - } - return FALSE; +static RemoteURI *textview_get_uri_from_range(TextView *textview, + GtkTextIter *iter, + GtkTextTag *tag, + GtkTextIter *start_iter, + GtkTextIter *end_iter) +{ + gint start_pos, end_pos; + RemoteURI *uri = NULL; + GSList *cur; + + start_pos = gtk_text_iter_get_offset(start_iter); + end_pos = gtk_text_iter_get_offset(end_iter); + + for (cur = textview->uri_list; cur != NULL; cur = cur->next) { + RemoteURI *uri_ = (RemoteURI *)cur->data; + + if (start_pos == uri_->start && + end_pos == uri_->end) { + uri = uri_; + break; } - /* doubleclick: open compose / add address / browser */ - if ((event->type == GDK_2BUTTON_PRESS && bevent->button == 1) || - bevent->button == 2 || bevent->button == 3) { - if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) { - if (bevent->button == 3) { - gchar *fromname, *fromaddress; + } + + return uri; +} + +static RemoteURI *textview_get_uri(TextView *textview, + GtkTextIter *iter, + GtkTextTag *tag) +{ + GtkTextIter start_iter, end_iter; + RemoteURI *uri = NULL; + + if (textview_get_uri_range(textview, iter, tag, &start_iter, + &end_iter)) + uri = textview_get_uri_from_range(textview, iter, tag, + &start_iter, &end_iter); + + return uri; +} + +static gboolean textview_uri_button_pressed(GtkTextTag *tag, GObject *obj, + GdkEvent *event, GtkTextIter *iter, + TextView *textview) +{ + GdkEventButton *bevent; + RemoteURI *uri = NULL; + GSList *cur; + gchar *trimmed_uri; + + if (!event) + return FALSE; + + if (event->type != GDK_BUTTON_PRESS && event->type != GDK_2BUTTON_PRESS + && event->type != GDK_MOTION_NOTIFY) + return FALSE; + + uri = textview_get_uri(textview, iter, tag); + if (!uri) + return FALSE; + + bevent = (GdkEventButton *) event; + + /* doubleclick: open compose / add address / browser */ + if ((event->type == GDK_BUTTON_PRESS && bevent->button == 1) || + bevent->button == 2 || bevent->button == 3) { + if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) { + if (bevent->button == 3) { + gchar *fromname, *fromaddress; - /* extract url */ - fromaddress = g_strdup(uri->uri + 7); - /* Hiroyuki: please put this function in utils.c! */ - fromname = procheader_get_fromname(fromaddress); - extract_address(fromaddress); - g_message("adding from textview %s <%s>", fromname, fromaddress); - /* Add to address book - Match */ - addressbook_add_contact( fromname, fromaddress, NULL ); + /* extract url */ + fromaddress = g_strdup(uri->uri + 7); + /* Hiroyuki: please put this function in utils.c! */ + fromname = procheader_get_fromname(fromaddress); + extract_address(fromaddress); + g_message("adding from textview %s <%s>", fromname, fromaddress); + /* Add to address book - Match */ + addressbook_add_contact( fromname, fromaddress, NULL ); - g_free(fromaddress); - g_free(fromname); - } else { - PrefsAccount *account = NULL; - - if (textview->messageview && textview->messageview->msginfo && - textview->messageview->msginfo->folder) { - FolderItem *folder_item; - - folder_item = textview->messageview->msginfo->folder; - if (folder_item->prefs && folder_item->prefs->enable_default_account) - account = account_find_from_id(folder_item->prefs->default_account); - } - compose_new(account, uri->uri + 7, NULL); - } - return TRUE; + g_free(fromaddress); + g_free(fromname); } else { - if (textview_uri_security_check(textview, uri) == TRUE) - open_uri(uri->uri, - prefs_common.uri_cmd); - return TRUE; + PrefsAccount *account = NULL; + + if (textview->messageview && textview->messageview->msginfo && + textview->messageview->msginfo->folder) { + FolderItem *folder_item; + + folder_item = textview->messageview->msginfo->folder; + if (folder_item->prefs && folder_item->prefs->enable_default_account) + account = account_find_from_id(folder_item->prefs->default_account); + } + compose_new(account, uri->uri + 7, NULL); } + return TRUE; + } else { + if (textview_uri_security_check(textview, uri) == TRUE) + open_uri(uri->uri, + prefs_common.uri_cmd); + return TRUE; } - g_free(trimmed_uri); } return FALSE; diff --git a/src/textview.h b/src/textview.h index 3ab32a78f..9d10be52e 100644 --- a/src/textview.h +++ b/src/textview.h @@ -26,8 +26,10 @@ #include #include +#include typedef struct _TextView TextView; +typedef struct _RemoteURI RemoteURI; #include "messageview.h" #include "procmime.h" @@ -51,8 +53,10 @@ struct _TextView MessageView *messageview; gint last_buttonpress; - gint show_url_msgid; - gint show_url_timeout_tag; + + RemoteURI *uri_hover; + GtkTextIter uri_hover_start_iter; + GtkTextIter uri_hover_end_iter; }; TextView *textview_create (void);