+++ /dev/null
-/* GtkUrl - A addon for GtkText that enables colored and clickable URLs
- * Copyright (C) 2001 Benedikt Roth <Benedikt.Roth@bratislav.de>
- * Based on code from
- * gtkspell - a spell-checking addon for GtkText
- * Copyright (c) 2000 Evan Martin.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#define GTKURL_USE_GNOME
-
-#include <gtk/gtk.h>
-#ifdef GTKURL_USE_GNOME
-#include <gnome.h>
-#endif /* GTKURL_USE_GNOME */
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-
-#include "gtkurl.h"
-
-/* FIXME? */
-static GdkColor highlight = { 0, 0, 0, 255*256 };
-
-enum {
- GTKURL_NO_URL,
- GTKURL_URL,
- GTKURL_HOST
-};
-
-
-static void entry_insert_cb(GtkText *gtktext, gchar *newtext, guint len, guint *ppos, gpointer d);
-static void entry_delete_cb(GtkText *gtktext, gint start, gint end, gpointer d);
-static gint button_press_intercept_cb(GtkText *gtktext, GdkEvent *e, gpointer d);
-
-static void popup_menu(GtkText *gtktext, GdkEventButton *eb);
-static GtkMenu *make_menu(gchar *url);
-
-static gboolean visit_url_gnome_cb( GtkWidget *widget, gpointer *data);
-static int my_poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr);
-static gboolean visit_url_cmd_cb( GtkWidget *widget, gpointer *data);
-
-static gboolean check_at(GtkText *gtktext, gint from_pos);
-static gchar *get_word_from_pos(GtkText* gtktext, gint pos, gint *pstart, gint *pend);
-static gchar *get_curword(GtkText* gtktext, gint *pstart, gint *pend);
-
-static void change_color(GtkText *gtktext, gint start, gint end, GdkColor *color);
-
-static gboolean iswordsep(gchar c);
-static gint is_url(gchar* word);
-
-
-
-void gtkurl_attach(GtkText *gtktext)
-{
- gtk_signal_connect(GTK_OBJECT(gtktext), "insert-text",
- GTK_SIGNAL_FUNC(entry_insert_cb), NULL);
- gtk_signal_connect_after(GTK_OBJECT(gtktext), "delete-text",
- GTK_SIGNAL_FUNC(entry_delete_cb), NULL);
- gtk_signal_connect(GTK_OBJECT(gtktext), "button-press-event",
- GTK_SIGNAL_FUNC(button_press_intercept_cb), NULL);
-}
-
-
-void gtkurl_detach(GtkText *gtktext)
-{
- gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
- GTK_SIGNAL_FUNC(entry_insert_cb), NULL);
- gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
- GTK_SIGNAL_FUNC(entry_delete_cb), NULL);
- gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
- GTK_SIGNAL_FUNC(button_press_intercept_cb), NULL);
-
- gtkurl_uncheck_all(gtktext);
-}
-
-
-void gtkurl_check_all(GtkText *gtktext)
-{
- guint origpos;
- guint pos = 0;
- guint len;
- float adj_value;
-
- len = gtk_text_get_length(gtktext);
-
- adj_value = gtktext->vadj->value;
- gtk_text_freeze(gtktext);
- origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
-
- while (pos < len)
- {
- while (pos < len && iswordsep(GTK_TEXT_INDEX(gtktext, pos)))
- pos++;
- while (pos < len && !iswordsep(GTK_TEXT_INDEX(gtktext, pos)))
- pos++;
- if (pos > 0)
- check_at(gtktext, pos-1);
- }
-
- gtk_text_thaw(gtktext);
- gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
-}
-
-
-void gtkurl_uncheck_all(GtkText *gtktext)
-{
- gint origpos;
- gchar *text;
- gfloat adj_value;
-
- adj_value = gtktext->vadj->value;
- gtk_text_freeze(gtktext);
- origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
- text = gtk_editable_get_chars(GTK_EDITABLE(gtktext), 0, -1);
- gtk_text_set_point(gtktext, 0);
- gtk_text_forward_delete(gtktext, gtk_text_get_length(gtktext));
- gtk_text_insert(gtktext, NULL, NULL, NULL, text, strlen(text));
- gtk_text_thaw(gtktext);
-
- gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
- gtk_adjustment_set_value(gtktext->vadj, adj_value);
-}
-
-
-static void entry_insert_cb(GtkText *gtktext, gchar *newtext, guint len, guint *ppos, gpointer d)
-{
- gint origpos;
-
- gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
- GTK_SIGNAL_FUNC(entry_insert_cb),
- NULL );
-
- gtk_text_insert(GTK_TEXT(gtktext), NULL,
- &(GTK_WIDGET(gtktext)->style->fg[0]), NULL, newtext, len);
-
- gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
- GTK_SIGNAL_FUNC(entry_insert_cb),
- NULL);
-
- gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext), "insert-text");
- *ppos += len;
-
- origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
-
- if (iswordsep(newtext[0]))
- {
- /* did we just end a word? */
- if (*ppos >= 2) check_at(gtktext, *ppos-2);
-
- /* did we just split a word? */
- if (*ppos < gtk_text_get_length(gtktext))
- check_at(gtktext, *ppos+1);
- }
- else
- {
- /* check as they type, *except* if they're typing at the end (the most
- * common case.
- */
- if (*ppos < gtk_text_get_length(gtktext) && !iswordsep(GTK_TEXT_INDEX(gtktext, *ppos)))
- check_at(gtktext, *ppos-1);
- }
-
- gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
- gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos);
-}
-
-
-static void entry_delete_cb(GtkText *gtktext, gint start, gint end, gpointer d)
-{
- gint origpos;
-
- origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
- check_at(gtktext, start-1);
- gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
- gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos);
- /* this is to *UNDO* the selection, in case they were holding shift
- * while hitting backspace. */
-}
-
-
-/* ok, this is pretty wacky:
- * we need to let the right-mouse-click go through, so it moves the cursor,
- * but we *can't* let it go through, because GtkText interprets rightclicks as
- * weird selection modifiers.
- *
- * so what do we do? forge rightclicks as leftclicks, then popup the menu.
- * HACK HACK HACK.
- */
-static gint button_press_intercept_cb(GtkText *gtktext, GdkEvent *e, gpointer d)
-{
- GdkEventButton *eb;
- gboolean retval;
-
- if (e->type != GDK_BUTTON_PRESS) return FALSE;
- eb = (GdkEventButton*) e;
-
- if (eb->button != 3)
- return FALSE;
-
- /* forge the leftclick */
- eb->button = 1;
-
- gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
- GTK_SIGNAL_FUNC(button_press_intercept_cb), d);
- gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "button-press-event",
- e, &retval);
- gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
- GTK_SIGNAL_FUNC(button_press_intercept_cb), d);
- gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext), "button-press-event");
-
- /* now do the menu wackiness */
- popup_menu(gtktext, eb);
- return TRUE;
-}
-
-
-static void popup_menu(GtkText *gtktext, GdkEventButton *eb)
-{
- gchar *buf;
-
- buf = get_curword(gtktext, NULL, NULL);
-
- gtk_menu_popup(make_menu(buf), NULL, NULL, NULL, NULL,
- eb->button, eb->time);
-}
-
-
-static GtkMenu *make_menu(gchar *url)
-{
- GtkWidget *menu, *item;
- gchar *caption;
- gchar *s = "http://";
- gchar *cmd;
-
- switch( is_url(url) )
- {
- case GTKURL_URL:
- url = g_strdup_printf("%s", url);
- break;
- case GTKURL_HOST:
- url = g_strdup_printf("%s%s", s, url);
- break;
- }
-
- menu = gtk_menu_new();
-
- caption = g_strdup_printf("%s", url);
- item = gtk_menu_item_new_with_label(caption);
- g_free(caption);
- gtk_widget_set_sensitive( GTK_WIDGET(item), FALSE);
- /* I'd like to make it so this item is never selectable, like
- * the menu titles in the GNOME panel... unfortunately, the GNOME
- * panel creates their own custom widget to do this! */
- gtk_widget_show(item);
- gtk_menu_append(GTK_MENU(menu), item);
-
- item = gtk_menu_item_new();
- gtk_widget_show(item);
- gtk_menu_append(GTK_MENU(menu), item);
-
-#ifdef GTKURL_USE_GNOME
- item = gtk_menu_item_new_with_label(_("Open with GNOME URL Handler"));
- gtk_signal_connect(GTK_OBJECT(item), "activate",
- GTK_SIGNAL_FUNC(visit_url_gnome_cb), g_strdup(url) );
- gtk_menu_append(GTK_MENU(menu), item);
- gtk_widget_show(item);
-#endif /* GTKURL_USE_GNOME */
-
- item = gtk_menu_item_new_with_label(_("Open with Netscape (Existing)"));
- cmd = g_strdup_printf("netscape -remote 'openURL(%s)'", url);
- gtk_signal_connect(GTK_OBJECT(item), "activate",
- GTK_SIGNAL_FUNC(visit_url_cmd_cb), g_strdup(cmd) );
- g_free(cmd);
- gtk_menu_append(GTK_MENU(menu), item);
- gtk_widget_show(item);
-
- item = gtk_menu_item_new_with_label(_("Open with Netscape (New Window)"));
- cmd = g_strdup_printf("netscape -remote 'openURL(%s,new-window)'", url);
- gtk_signal_connect(GTK_OBJECT(item), "activate",
- GTK_SIGNAL_FUNC(visit_url_cmd_cb), g_strdup(cmd) );
- g_free(cmd);
- gtk_menu_append(GTK_MENU(menu), item);
- gtk_widget_show(item);
-
- item = gtk_menu_item_new_with_label(_("Open with Netscape (Run New)"));
- cmd = g_strdup_printf("netscape %s", url);
- gtk_signal_connect(GTK_OBJECT(item), "activate",
- GTK_SIGNAL_FUNC(visit_url_cmd_cb), g_strdup(cmd) );
- g_free(cmd);
- gtk_menu_append(GTK_MENU(menu), item);
- gtk_widget_show(item);
-
- g_free(url);
-
- return GTK_MENU(menu);
-}
-
-
-#ifdef GTKURL_USE_GNOME
-static gboolean visit_url_gnome_cb( GtkWidget *widget, gpointer *data)
-{
- gnome_url_show((gchar *) data);
- g_free(data);
- return(TRUE);
-}
-#endif /* GTKURL_USE_GNOME */
-
-
-/* this is taken from gnome-libs 1.2.4 */
-#define POPT_ARGV_ARRAY_GROW_DELTA 5
-
-static int my_poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr)
-{
- char * buf, * bufStart, * dst;
- const char * src;
- char quote = '\0';
- int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
- char ** argv = malloc(sizeof(*argv) * argvAlloced);
- const char ** argv2;
- int argc = 0;
- int i, buflen;
-
- buflen = strlen(s) + 1;
- bufStart = buf = alloca(buflen);
- memset(buf, '\0', buflen);
-
- src = s;
- argv[argc] = buf;
-
- while (*src) {
- if (quote == *src) {
- quote = '\0';
- } else if (quote) {
- if (*src == '\\') {
- src++;
- if (!*src) {
- free(argv);
- return 1;
- }
- if (*src != quote) *buf++ = '\\';
- }
- *buf++ = *src;
- } else if (isspace(*src)) {
- if (*argv[argc]) {
- buf++, argc++;
- if (argc == argvAlloced) {
- argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
- argv = realloc(argv, sizeof(*argv) * argvAlloced);
- }
- argv[argc] = buf;
- }
- } else switch (*src) {
- case '"':
- case '\'':
- quote = *src;
- break;
- case '\\':
- src++;
- if (!*src) {
- free(argv);
- return 1;
- }
- /* fallthrough */
- default:
- *buf++ = *src;
- }
-
- src++;
- }
-
- if (strlen(argv[argc])) {
- argc++, buf++;
- }
-
- dst = malloc((argc + 1) * sizeof(*argv) + (buf - bufStart));
- argv2 = (void *) dst;
- dst += (argc + 1) * sizeof(*argv);
- memcpy(argv2, argv, argc * sizeof(*argv));
- argv2[argc] = NULL;
- memcpy(dst, bufStart, buf - bufStart);
-
- for (i = 0; i < argc; i++) {
- argv2[i] = dst + (argv[i] - bufStart);
- }
-
- free(argv);
-
- *argvPtr = (char **)argv2; /* XXX don't change the API */
- *argcPtr = argc;
-
- return 0;
-}
-
-
-static gboolean visit_url_cmd_cb( GtkWidget *widget, gpointer *data)
-{
- int pid;
- char **argv;
- int argc;
-
- if (my_poptParseArgvString ( (const char *)data, &argc, &argv) != 0)
- return -1;
-
- pid = fork ();
- if (pid == -1)
- return -1;
- if (pid == 0)
- {
- execvp (argv[0], argv);
- _exit (0);
- } else
- {
- free (argv);
- return pid;
- }
-
- g_free(data);
-
- return(TRUE);
-}
-
-
-static gboolean check_at(GtkText *gtktext, gint from_pos)
-{
- gint start, end;
- gchar *buf;
-
- if ( ! (buf = get_word_from_pos(gtktext, from_pos, &start, &end)) )
- return FALSE;
-
- if ( is_url(buf) )
- {
- if (highlight.pixel == 0)
- {
- /* add an entry for the highlight in the color map. */
- GdkColormap *gc = gtk_widget_get_colormap(GTK_WIDGET(gtktext));
- gdk_colormap_alloc_color(gc, &highlight, FALSE, TRUE);;
- }
- change_color(gtktext, start, end, &highlight);
- return(TRUE);
- }
- else
- {
- change_color(gtktext, start, end, &(GTK_WIDGET(gtktext)->style->fg[0]));
- return(FALSE);
- }
-}
-
-
-static gchar *get_word_from_pos(GtkText* gtktext, gint pos, gint *pstart, gint *pend)
-{
- GString *word = g_string_new("");
- gint start, end;
- gchar ch;
-
- if (iswordsep(GTK_TEXT_INDEX(gtktext, pos)))
- return(NULL);
-
- /* Get start and end position from the word */
- for (start = pos; start >= 0; --start)
- if (iswordsep(GTK_TEXT_INDEX(gtktext, start)))
- break;
- start++;
-
- for (end = pos; end < gtk_text_get_length(gtktext); end++)
- if (iswordsep(GTK_TEXT_INDEX(gtktext, end)) )
- break;
-
- /* Be sure to not include punctation marks etc. */
- for ( ;end>start; end-- )
- {
- ch = GTK_TEXT_INDEX(gtktext, end-1);
- if( isalpha(ch) || isdigit(ch) || ch == ':' )
- break;
- }
-
- /* Get the word (everyting between start and end */
- for (pos = start; pos < end; pos++)
- g_string_append_c( word, GTK_TEXT_INDEX(gtktext, pos) );
-
- if (pstart)
- *pstart = start;
- if (pend)
- *pend = end;
-
- return(word->str);
-}
-
-
-static gchar *get_curword(GtkText* gtktext, gint *pstart, gint *pend)
-{
- gint pos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
- return(get_word_from_pos(gtktext, pos, pstart, pend));
-}
-
-
-static void change_color(GtkText *gtktext, gint start, gint end, GdkColor *color)
-{
- gchar *newtext;
-
- /* So we don't need spaces at the very end of the text */
- if ( end == gtk_text_get_length(GTK_TEXT(gtktext))+1 )
- end--;
-
- newtext = gtk_editable_get_chars(GTK_EDITABLE(gtktext), start, end);
-
- gtk_text_freeze(gtktext);
- gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
- GTK_SIGNAL_FUNC(entry_insert_cb), NULL);
-
- gtk_text_set_point(gtktext, start);
- gtk_text_forward_delete(gtktext, end-start);
-
- if (newtext && end-start > 0)
- gtk_text_insert(gtktext, NULL, color, NULL, newtext, end-start);
-
- gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
- GTK_SIGNAL_FUNC(entry_insert_cb), NULL);
- gtk_text_thaw(gtktext);
- g_free(newtext);
-}
-
-
-static gboolean iswordsep(gchar c)
-{
-/* return !isalpha(c) && c != '\''; */
- return( isspace(c) );
-}
-
-
-static gint is_url(gchar* word)
-{
- gint len;
- if (!word)
- return GTKURL_NO_URL;
-
- len = strlen (word);
-
- if (!strncasecmp (word, "irc://", 6))
- return GTKURL_URL;
-
- if (!strncasecmp (word, "irc.", 4))
- return GTKURL_URL;
-
- if (!strncasecmp (word, "ftp.", 4))
- return GTKURL_URL;
-
- if (!strncasecmp (word, "ftp:", 4))
- return GTKURL_URL;
-
- if (!strncasecmp (word, "www.", 4))
- return GTKURL_URL;
-
- if (!strncasecmp (word, "http:", 5))
- return GTKURL_URL;
-
- if (!strncasecmp (word, "https:", 6))
- return GTKURL_URL;
-
- if (!strncasecmp (word + len - 4, ".org", 4))
- return GTKURL_HOST;
-
- if (!strncasecmp (word + len - 4, ".net", 4))
- return GTKURL_HOST;
-
- if (!strncasecmp (word + len - 4, ".com", 4))
- return GTKURL_HOST;
-
- if (!strncasecmp (word + len - 4, ".edu", 4))
- return GTKURL_HOST;
-
- return GTKURL_NO_URL;
-}