2004-11-06 [paul] 0.9.12cvs139.2
authorPaul Mangan <paul@claws-mail.org>
Sat, 6 Nov 2004 09:39:33 +0000 (09:39 +0000)
committerPaul Mangan <paul@claws-mail.org>
Sat, 6 Nov 2004 09:39:33 +0000 (09:39 +0000)
* 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 <jylefort@users.sourceforge.net>

AUTHORS
ChangeLog-gtk2.claws
PATCHSETS
configure.ac
src/textview.c
src/textview.h

diff --git a/AUTHORS b/AUTHORS
index 2ec6ca0..9ed3a28 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -210,3 +210,4 @@ contributors (beside the above; based on Changelog)
        Stephan Sachse
        Thomas Gilgin
        Guy Rouillier
+       Jean-Yves Lefort
index 0a18c95..6af87d9 100644 (file)
@@ -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 <jylefort@users.sourceforge.net>
+               
+
 2004-11-05 [colin]     0.9.12cvs139.1
 
        * src/compose.c
index e119bc6..a3b6d4b 100644 (file)
--- a/PATCHSETS
+++ b/PATCHSETS
 ( 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
index f4f32e0..f463cc1 100644 (file)
@@ -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}
index 97d8703..bfccff6 100644 (file)
@@ -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;
index 3ab32a7..9d10be5 100644 (file)
 
 #include <glib.h>
 #include <gtk/gtkwidget.h>
+#include <gtk/gtktextiter.h>
 
 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);