overhauling of URL detection, including channel, nick, etc 'words'

This commit is contained in:
RichardHitt 2013-01-02 14:50:26 -08:00
parent 7f2846a5bd
commit 4af624627e
10 changed files with 402 additions and 280 deletions

View File

@ -142,7 +142,7 @@ mybsearch (const void *key, void **array, size_t nmemb,
} }
void * void *
tree_find (tree *t, void *key, tree_cmp_func *cmp, void *data, int *pos) tree_find (tree *t, const void *key, tree_cmp_func *cmp, void *data, int *pos)
{ {
if (!t || !t->array) if (!t || !t->array)
return NULL; return NULL;

View File

@ -8,7 +8,7 @@ typedef int (tree_traverse_func) (const void *key, void *data);
tree *tree_new (tree_cmp_func *cmp, void *data); tree *tree_new (tree_cmp_func *cmp, void *data);
void tree_destroy (tree *t); void tree_destroy (tree *t);
void *tree_find (tree *t, void *key, tree_cmp_func *cmp, void *data, int *pos); void *tree_find (tree *t, const void *key, tree_cmp_func *cmp, void *data, int *pos);
int tree_remove (tree *t, void *key, int *pos); int tree_remove (tree *t, void *key, int *pos);
void *tree_remove_at_pos (tree *t, int pos); void *tree_remove_at_pos (tree *t, int pos);
void tree_foreach (tree *t, tree_traverse_func *func, void *data); void tree_foreach (tree *t, tree_traverse_func *func, void *data);

View File

@ -13,7 +13,7 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/ */
#include <stdio.h> #include <stdio.h>
@ -32,6 +32,13 @@
void *url_tree = NULL; void *url_tree = NULL;
GTree *url_btree = NULL; GTree *url_btree = NULL;
static int do_an_re (const char *word, int *start, int *end, int *type);
static GRegex *re_url (void);
static GRegex *re_host (void);
static GRegex *re_email (void);
static GRegex *re_nick (void);
static GRegex *re_channel (void);
static GRegex *re_path (void);
static int static int
@ -177,158 +184,38 @@ url_add (char *urltext, int len)
keep it FAST! This new version was found to be almost 3x faster than keep it FAST! This new version was found to be almost 3x faster than
2.4.4 release. */ 2.4.4 release. */
static int laststart = 0;
static int lastend = 0;
static int lasttype = 0;
int int
url_check_word (const char *word, int len) url_check_word (const char *word)
{ {
#define D(x) (x), ((sizeof (x)) - 1) laststart = lastend = lasttype = 0;
static const struct { if (do_an_re (word, &laststart, &lastend, &lasttype))
const char *s;
int len;
}
prefix[] = {
{ D("irc.") },
{ D("ftp.") },
{ D("www.") },
{ D("irc://") },
{ D("ftp://") },
{ D("http://") },
{ D("https://") },
{ D("file://") },
{ D("rtsp://") },
{ D("ut2004://") },
},
suffix[] = {
{ D(".org") },
{ D(".net") },
{ D(".com") },
{ D(".edu") },
{ D(".html") },
{ D(".info") },
{ D(".name") },
/* Some extra common suffixes.
foo.blah/baz.php etc should work now, rather than
needing http:// at the beginning. */
{ D(".php") },
{ D(".htm") },
{ D(".aero") },
{ D(".asia") },
{ D(".biz") },
{ D(".cat") },
{ D(".coop") },
{ D(".int") },
{ D(".jobs") },
{ D(".mobi") },
{ D(".museum") },
{ D(".pro") },
{ D(".tel") },
{ D(".travel") },
{ D(".xxx") },
{ D(".asp") },
{ D(".aspx") },
{ D(".shtml") },
{ D(".xml") },
};
#undef D
const char *at, *dot;
int i, dots;
/* this is pretty much the same as in logmask_is_fullpath() except with length checks and .\ for portable mode */
#ifdef WIN32
if ((len > 1 && word[0] == '\\') ||
(len > 2 && word[0] == '.' && word[1] == '\\') ||
(len > 2 && (((word[0] >= 'A' && word[0] <= 'Z') || (word[0] >= 'a' && word[0] <= 'z')) && word[1] == ':')))
#else
if (len > 1 && word[0] == '/')
#endif
{ {
return WORD_PATH; switch (lasttype)
}
if (len > 1 && word[1] == '#' && strchr("@+^%*#", word[0]))
return WORD_CHANNEL;
if ((word[0] == '#' || word[0] == '&') && word[1] != '#' && word[1] != 0)
return WORD_CHANNEL;
for (i = 0; i < G_N_ELEMENTS(prefix); i++)
{
int l;
l = prefix[i].len;
if (len > l)
{ {
int j; case WORD_NICK:
if (!isalnum (word[laststart]))
/* This is pretty much g_ascii_strncasecmp(). */ laststart++;
for (j = 0; j < l; j++) if (!userlist_find (current_sess, &word[laststart]))
{ lasttype = 0;
unsigned char c = word[j]; return lasttype;
if (tolower(c) != prefix[i].s[j]) case WORD_EMAIL:
break; if (!isalnum (word[laststart]))
} laststart++;
if (j == l) /* Fall through */
return WORD_URL; case WORD_URL:
case WORD_HOST:
case WORD_CHANNEL:
return lasttype;
default:
return 0; /* Should not occur */
} }
} }
else
at = strchr (word, '@'); /* check for email addy */ return 0;
dot = strrchr (word, '.');
if (at && dot)
{
if (at < dot)
{
if (strchr (word, '*'))
return WORD_HOST;
else
return WORD_EMAIL;
}
}
/* check if it's an IP number */
dots = 0;
for (i = 0; i < len; i++)
{
if (word[i] == '.' && i > 0)
dots++; /* allow 127.0.0.1:80 */
else if (!isdigit ((unsigned char) word[i]) && word[i] != ':')
{
dots = 0;
break;
}
}
if (dots == 3)
return WORD_HOST;
if (len > 5)
{
for (i = 0; i < G_N_ELEMENTS(suffix); i++)
{
int l;
l = suffix[i].len;
if (len > l)
{
const unsigned char *p = &word[len - l];
int j;
/* This is pretty much g_ascii_strncasecmp(). */
for (j = 0; j < l; j++)
{
if (tolower(p[j]) != suffix[i].s[j])
break;
}
if (j == l)
return WORD_HOST;
}
}
if (word[len - 3] == '.' &&
isalpha ((unsigned char) word[len - 2]) &&
isalpha ((unsigned char) word[len - 1]))
return WORD_HOST;
}
return 0;
} }
/* List of IRC commands for which contents (and thus possible URLs) /* List of IRC commands for which contents (and thus possible URLs)
@ -346,9 +233,10 @@ static char *commands[] = {
void void
url_check_line (char *buf, int len) url_check_line (char *buf, int len)
{ {
GRegex *re(void);
GMatchInfo *gmi;
char *po = buf; char *po = buf;
char *start; int i;
int i, wlen;
/* Skip over message prefix */ /* Skip over message prefix */
if (*po == ':') if (*po == ':')
@ -379,50 +267,243 @@ url_check_line (char *buf, int len)
return; return;
po++; po++;
if (buf[0] == ':' && buf[1] != 0) g_regex_match(re_url(), po, 0, &gmi);
po++; while (g_match_info_matches(gmi))
start = po;
/* check each "word" (space separated) */
while (1)
{ {
switch (po[0]) int start, end;
{
case 0:
case ' ':
case '\r':
wlen = po - start; g_match_info_fetch_pos(gmi, 0, &start, &end);
if (wlen > 2) if (po[end - 1] == '\r')
{ po[--end] = 0;
/* HACK! :( */ if (g_strstr_len (po + start, end - start, "://"))
/* This is to work around not being able to detect URLs that are at url_add(po + start, end - start);
the start of messages. */ g_match_info_next(gmi, NULL);
if (start[0] == ':')
{
start++;
wlen--;
}
if (start[0] == '+' || start[0] == '-')
{
start++;
wlen--;
}
if (wlen > 2 && url_check_word (start, wlen) == WORD_URL)
{
url_add (start, wlen);
}
}
if (po[0] == 0)
return;
po++;
start = po;
break;
default:
po++;
}
} }
g_match_info_free(gmi);
}
int
url_last (int *lstart, int *lend)
{
*lstart = laststart;
*lend = lastend;
return lasttype;
}
static int
do_an_re(const char *word,int *start, int *end, int *type)
{
typedef struct func_s {
GRegex *(*fn)(void);
int type;
} func_t;
func_t funcs[] =
{
{ re_email, WORD_EMAIL },
{ re_url, WORD_URL },
{ re_host, WORD_HOST },
{ re_channel, WORD_CHANNEL },
{ re_path, WORD_PATH },
{ re_nick, WORD_NICK }
};
GMatchInfo *gmi;
int k;
for (k = 0; k < sizeof funcs / sizeof (func_t); k++)
{
g_regex_match (funcs[k].fn(), word, 0, &gmi);
if (!g_match_info_matches (gmi))
{
g_match_info_free (gmi);
continue;
}
while (g_match_info_matches (gmi))
{
g_match_info_fetch_pos (gmi, 0, start, end);
g_match_info_next (gmi, NULL);
}
g_match_info_free (gmi);
*type = funcs[k].type;
return TRUE;
}
return FALSE;
}
/* Miscellaneous description --- */
#define DOMAIN "[-a-z0-9]+(\\.[-a-z0-9]+)*\\.[a-z]+"
#define IPADDR "[0-9]+(\\.[0-9]+){3}"
#define HOST "(" DOMAIN "|" IPADDR ")"
#define OPT_PORT "(:[1-9][0-9]{0,4})?"
GRegex *
make_re(char *grist, char *type)
{
GRegex *ret;
GError *err = NULL;
ret = g_regex_new (grist, G_REGEX_CASELESS + G_REGEX_OPTIMIZE, 0, &err);
g_free (grist);
return ret;
}
/* HOST description --- */
/* (see miscellaneous above) */
static GRegex *
re_host (void)
{
static GRegex *host_ret;
char *grist;
grist = g_strdup_printf (
"(" /* HOST */
HOST OPT_PORT
")"
);
host_ret = make_re (grist, "re_host");
return host_ret;
}
/* URL description --- */
#define SCHEME "(%s)"
#define LPAR "\\("
#define RPAR "\\)"
#define NOPARENS "[^() \t]*"
char *prefix[] = {
"irc\\.",
"ftp\\.",
"www\\.",
"irc://",
"ftp://",
"http://",
"https://",
"file://",
"rtsp://",
NULL
};
static GRegex *
re_url (void)
{
static GRegex *url_ret;
char *grist;
char *scheme;
if (url_ret) return url_ret;
scheme = g_strjoinv ("|", prefix);
grist = g_strdup_printf (
"(" /* URL or HOST */
SCHEME HOST OPT_PORT
"(" /* Optional "/path?query_string#fragment_id" */
"/" /* Must start with slash */
"("
"(" LPAR NOPARENS RPAR ")"
"|"
"(" NOPARENS ")"
")*" /* Zero or more occurrences of either of these */
"(?<![.,?!\\]])" /* Not allowed to end with these */
")?" /* Zero or one of this /path?query_string#fragment_id thing */
")"
, scheme
);
url_ret = make_re (grist, "re_url");
g_free (scheme);
return url_ret;
}
/* EMAIL description --- */
#define EMAIL "[a-z][-_a-z0-9]+@" "(" HOST ")"
static GRegex *
re_email (void)
{
static GRegex *email_ret;
char *grist;
if (email_ret) return email_ret;
grist = g_strdup_printf (
"(" /* EMAIL */
EMAIL
")"
);
email_ret = make_re (grist, "re_email");
return email_ret;
}
/* NICK description --- */
#define NICKPRE "~+!@%%&"
#define NICKHYP "-"
#define NICKLET "a-z"
#define NICKDIG "0-9"
/* Note for NICKSPE: \\\\ boils down to a single \ */
#define NICKSPE "\\[\\]\\\\`_^{|}"
#define NICK0 "[" NICKPRE "]?[" NICKLET NICKDIG "]"
#define NICK1 "[" NICKHYP NICKLET NICKDIG NICKSPE "]+"
#define NICK NICK0 NICK1
static GRegex *
re_nick (void)
{
static GRegex *nick_ret;
char *grist;
if (nick_ret) return nick_ret;
grist = g_strdup_printf (
"(" /* NICK */
NICK
")"
);
nick_ret = make_re (grist, "re_nick");
return nick_ret;
}
/* CHANNEL description --- */
#define CHANNEL "#[^ \t\a,:]+"
static GRegex *
re_channel (void)
{
static GRegex *channel_ret;
char *grist;
if (channel_ret) return channel_ret;
grist = g_strdup_printf (
"(" /* CHANNEL */
CHANNEL
")"
);
channel_ret = make_re (grist, "re_channel");
return channel_ret;
}
/* PATH description --- */
#ifdef WIN32
/* Windows path can be \ or .\ or ..\ or e.g. C: etc */
#define PATH "^(\\\\|\\.{1,2}\\\\|[a-z]:).*"
#else
/* Linux path can be / or ./ or ../ etc */
#define PATH "^(/|\\./|\\.\\./).*"
#endif
static GRegex *
re_path (void)
{
static GRegex *path_ret;
char *grist;
if (path_ret) return path_ret;
grist = g_strdup_printf (
"(" /* PATH */
PATH
")"
);
path_ret = make_re (grist, "re_path");
return path_ret;
} }

View File

@ -14,7 +14,8 @@ extern void *url_tree;
void url_clear (void); void url_clear (void);
void url_save_tree (const char *fname, const char *mode, gboolean fullpath); void url_save_tree (const char *fname, const char *mode, gboolean fullpath);
int url_check_word (const char *word, int len); int url_last (int *, int *);
int url_check_word (const char *word);
void url_check_line (char *buf, int len); void url_check_line (char *buf, int len);
#endif #endif

View File

@ -192,7 +192,7 @@ find_cmp (const char *name, struct User *user, server *serv)
} }
struct User * struct User *
userlist_find (struct session *sess, char *name) userlist_find (struct session *sess, const char *name)
{ {
int pos; int pos;

View File

@ -26,7 +26,7 @@ int userlist_add_hostname (session *sess, char *nick,
char *hostname, char *realname, char *hostname, char *realname,
char *servername, unsigned int away); char *servername, unsigned int away);
void userlist_set_away (session *sess, char *nick, unsigned int away); void userlist_set_away (session *sess, char *nick, unsigned int away);
struct User *userlist_find (session *sess, char *name); struct User *userlist_find (session *sess, const char *name);
struct User *userlist_find_global (server *serv, char *name); struct User *userlist_find_global (server *serv, char *name);
void userlist_clear (session *sess); void userlist_clear (session *sess);
void userlist_free (session *sess); void userlist_free (session *sess);

View File

@ -1058,7 +1058,7 @@ static void
fe_open_url_locale (const char *url) fe_open_url_locale (const char *url)
{ {
/* the http:// part's missing, prepend it, otherwise it won't always work */ /* the http:// part's missing, prepend it, otherwise it won't always work */
if (strchr (url, ':') == NULL && url_check_word (url, strlen (url)) != WORD_PATH) if (strchr (url, ':') == NULL && url_check_word (url) != WORD_PATH)
{ {
url = g_strdup_printf ("http://%s", url); url = g_strdup_printf ("http://%s", url);
fe_open_url_inner (url); fe_open_url_inner (url);

View File

@ -2242,20 +2242,14 @@ mg_create_topicbar (session *sess, GtkWidget *box)
/* check if a word is clickable */ /* check if a word is clickable */
static int static int
mg_word_check (GtkWidget * xtext, char *word, int len) mg_word_check (GtkWidget * xtext, char *word)
{ {
session *sess = current_sess; session *sess = current_sess;
int ret; int ret;
ret = url_check_word (word, len); /* common/url.c */ ret = url_check_word (word);
if (ret == 0) if (ret == 0 && sess->type == SESS_DIALOG)
{ return WORD_DIALOG;
if (( (word[0]=='@' || word[0]=='+' || word[0]=='%') && userlist_find (sess, word+1)) || userlist_find (sess, word))
return WORD_NICK;
if (sess->type == SESS_DIALOG)
return WORD_DIALOG;
}
return ret; return ret;
} }
@ -2266,23 +2260,28 @@ static void
mg_word_clicked (GtkWidget *xtext, char *word, GdkEventButton *even) mg_word_clicked (GtkWidget *xtext, char *word, GdkEventButton *even)
{ {
session *sess = current_sess; session *sess = current_sess;
int word_type, start, end;
char *tmp;
if (even->button == 1) /* left button */ if (word == NULL)
{ {
if (word == NULL) if (even->button == 1) /* left button */
{
mg_focus (sess); mg_focus (sess);
return; return;
} }
if ((even->state & 13) == prefs.hex_gui_url_mod) word_type = mg_word_check (xtext, word);
url_last (&start, &end);
if (even->button == 1 && (even->state & 13) == prefs.hex_gui_url_mod)
{
switch (word_type)
{ {
switch (mg_word_check (xtext, word, strlen (word))) case WORD_URL:
{ case WORD_HOST:
case WORD_URL: word[end] = 0;
case WORD_HOST: word += start;
fe_open_url (word); fe_open_url (word);
}
} }
return; return;
} }
@ -2296,7 +2295,7 @@ mg_word_clicked (GtkWidget *xtext, char *word, GdkEventButton *even)
return; return;
} }
switch (mg_word_check (xtext, word, strlen (word))) switch (word_type)
{ {
case 0: case 0:
case WORD_PATH: case WORD_PATH:
@ -2304,26 +2303,22 @@ mg_word_clicked (GtkWidget *xtext, char *word, GdkEventButton *even)
break; break;
case WORD_URL: case WORD_URL:
case WORD_HOST: case WORD_HOST:
word[end] = 0;
word += start;
menu_urlmenu (even, word); menu_urlmenu (even, word);
break; break;
case WORD_NICK: case WORD_NICK:
menu_nickmenu (sess, even, (word[0]=='@' || word[0]=='+' || word[0]=='%') ? menu_nickmenu (sess, even, word + (ispunct (*word)? 1: 0), FALSE);
word+1 : word, FALSE);
break; break;
case WORD_CHANNEL: case WORD_CHANNEL:
if (*word == '@' || *word == '+' || *word=='^' || *word=='%' || *word=='*') menu_chanmenu (sess, even, word + (ispunct (*word)? 1: 0));
word++;
menu_chanmenu (sess, even, word);
break; break;
case WORD_EMAIL: case WORD_EMAIL:
{ word[end] = 0;
char *newword = malloc (strlen (word) + 10); word += start;
if (*word == '~') tmp = g_strdup_printf("mailto:%s", word + (ispunct (*word)? 1: 0));
word++; menu_urlmenu (even, tmp);
sprintf (newword, "mailto:%s", word); g_free (tmp);
menu_urlmenu (even, newword);
free (newword);
}
break; break;
case WORD_DIALOG: case WORD_DIALOG:
menu_nickmenu (sess, even, sess->channel, FALSE); menu_nickmenu (sess, even, sess->channel, FALSE);

View File

@ -13,7 +13,7 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
* ========================================================================= * =========================================================================
* *
* xtext, the text widget used by X-Chat. * xtext, the text widget used by X-Chat.
@ -73,6 +73,7 @@
#include "../common/fe.h" #include "../common/fe.h"
#include "../common/util.h" #include "../common/util.h"
#include "../common/hexchatc.h" #include "../common/hexchatc.h"
#include "../common/url.h"
#include "fe-gtk.h" #include "fe-gtk.h"
#include "xtext.h" #include "xtext.h"
#include "fkeys.h" #include "fkeys.h"
@ -1901,7 +1902,7 @@ gtk_xtext_selection_update (GtkXText * xtext, GdkEventMotion * event, int p_y, g
static char * static char *
gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent, gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent,
int *ret_off, int *ret_len) int *ret_off, int *ret_len, GSList **slp)
{ {
textentry *ent; textentry *ent;
int offset; int offset;
@ -1950,9 +1951,9 @@ gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent,
if (ret_off) if (ret_off)
*ret_off = word - ent->str; *ret_off = word - ent->str;
if (ret_len) if (ret_len)
*ret_len = str - word; *ret_len = len; /* Length before stripping */
return gtk_xtext_strip_color (word, len, xtext->scratch_buffer, NULL, NULL, NULL, FALSE); return gtk_xtext_strip_color (word, len, xtext->scratch_buffer, NULL, NULL, slp, FALSE);
} }
#ifdef MOTION_MONITOR #ifdef MOTION_MONITOR
@ -2028,14 +2029,62 @@ gtk_xtext_check_mark_stamp (GtkXText *xtext, GdkModifierType mask)
return redraw; return redraw;
} }
static int
gtk_xtext_get_word_adjust (GtkXText *xtext, int x, int y, textentry **word_ent, int *offset, int *len)
{
GSList *slp = NULL;
unsigned char *word;
int word_type = 0;
word = gtk_xtext_get_word (xtext, x, y, word_ent, offset, len, &slp);
if (word)
{
int laststart, lastend;
word_type = xtext->urlcheck_function (GTK_WIDGET (xtext), word);
if (word_type > 0)
{
if (url_last (&laststart, &lastend))
{
int cumlen, startadj = 0, endadj = 0;
offlen_t o;
GSList *sl;
for (sl = slp, cumlen = 0; sl; sl = g_slist_next (sl))
{
o.u = GPOINTER_TO_UINT (sl->data);
startadj = o.o.off - cumlen;
cumlen += o.o.len;
if (laststart < cumlen)
break;
}
for (sl = slp, cumlen = 0; sl; sl = g_slist_next (sl))
{
o.u = GPOINTER_TO_UINT (sl->data);
endadj = o.o.off - cumlen;
cumlen += o.o.len;
if (lastend < cumlen)
break;
}
laststart += startadj;
*offset += laststart;
*len = lastend + endadj - laststart;
}
}
}
g_slist_free (slp);
return word_type;
}
static gboolean static gboolean
gtk_xtext_motion_notify (GtkWidget * widget, GdkEventMotion * event) gtk_xtext_motion_notify (GtkWidget * widget, GdkEventMotion * event)
{ {
GtkXText *xtext = GTK_XTEXT (widget); GtkXText *xtext = GTK_XTEXT (widget);
GdkModifierType mask; GdkModifierType mask;
int redraw, tmp, x, y, offset, len, line_x; int redraw, tmp, x, y, offset, len, line_x;
unsigned char *word;
textentry *word_ent; textentry *word_ent;
int word_type;
gdk_window_get_pointer (widget->window, &x, &y, &mask); gdk_window_get_pointer (widget->window, &x, &y, &mask);
@ -2104,43 +2153,40 @@ gtk_xtext_motion_notify (GtkWidget * widget, GdkEventMotion * event)
if (xtext->urlcheck_function == NULL) if (xtext->urlcheck_function == NULL)
return FALSE; return FALSE;
word = gtk_xtext_get_word (xtext, x, y, &word_ent, &offset, &len); word_type = gtk_xtext_get_word_adjust (xtext, x, y, &word_ent, &offset, &len);
if (word) if (word_type > 0)
{ {
if (xtext->urlcheck_function (GTK_WIDGET (xtext), word, len) > 0) if (!xtext->cursor_hand ||
xtext->hilight_ent != word_ent ||
xtext->hilight_start != offset ||
xtext->hilight_end != offset + len)
{ {
if (!xtext->cursor_hand || if (!xtext->cursor_hand)
xtext->hilight_ent != word_ent ||
xtext->hilight_start != offset ||
xtext->hilight_end != offset + len)
{ {
if (!xtext->cursor_hand) gdk_window_set_cursor (GTK_WIDGET (xtext)->window,
{ xtext->hand_cursor);
gdk_window_set_cursor (GTK_WIDGET (xtext)->window, xtext->cursor_hand = TRUE;
xtext->hand_cursor);
xtext->cursor_hand = TRUE;
}
/* un-render the old hilight */
if (xtext->hilight_ent)
gtk_xtext_unrender_hilight (xtext);
xtext->hilight_ent = word_ent;
xtext->hilight_start = offset;
xtext->hilight_end = offset + len;
xtext->skip_border_fills = TRUE;
xtext->render_hilights_only = TRUE;
xtext->skip_stamp = TRUE;
gtk_xtext_render_ents (xtext, word_ent, NULL);
xtext->skip_border_fills = FALSE;
xtext->render_hilights_only = FALSE;
xtext->skip_stamp = FALSE;
} }
return FALSE;
/* un-render the old hilight */
if (xtext->hilight_ent)
gtk_xtext_unrender_hilight (xtext);
xtext->hilight_ent = word_ent;
xtext->hilight_start = offset;
xtext->hilight_end = offset + len;
xtext->skip_border_fills = TRUE;
xtext->render_hilights_only = TRUE;
xtext->skip_stamp = TRUE;
gtk_xtext_render_ents (xtext, word_ent, NULL);
xtext->skip_border_fills = FALSE;
xtext->render_hilights_only = FALSE;
xtext->skip_stamp = FALSE;
} }
return FALSE;
} }
gtk_xtext_leave_notify (widget, NULL); gtk_xtext_leave_notify (widget, NULL);
@ -2280,7 +2326,7 @@ gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
if (!xtext->hilighting) if (!xtext->hilighting)
{ {
word = gtk_xtext_get_word (xtext, event->x, event->y, 0, 0, 0); word = gtk_xtext_get_word (xtext, event->x, event->y, 0, 0, 0, 0);
g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0, word ? word : NULL, event); g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0, word ? word : NULL, event);
} else } else
{ {
@ -2288,7 +2334,6 @@ gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
} }
} }
return FALSE; return FALSE;
} }
@ -2305,7 +2350,7 @@ gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
if (event->button == 3 || event->button == 2) /* right/middle click */ if (event->button == 3 || event->button == 2) /* right/middle click */
{ {
word = gtk_xtext_get_word (xtext, x, y, 0, 0, 0); word = gtk_xtext_get_word (xtext, x, y, 0, 0, 0, 0);
if (word) if (word)
{ {
g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0, g_signal_emit (G_OBJECT (xtext), xtext_signals[WORD_CLICK], 0,
@ -2322,7 +2367,7 @@ gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
if (event->type == GDK_2BUTTON_PRESS) /* WORD select */ if (event->type == GDK_2BUTTON_PRESS) /* WORD select */
{ {
gtk_xtext_check_mark_stamp (xtext, mask); gtk_xtext_check_mark_stamp (xtext, mask);
if (gtk_xtext_get_word (xtext, x, y, &ent, &offset, &len)) if (gtk_xtext_get_word (xtext, x, y, &ent, &offset, &len, 0))
{ {
if (len == 0) if (len == 0)
return FALSE; return FALSE;
@ -2343,7 +2388,7 @@ gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
if (event->type == GDK_3BUTTON_PRESS) /* LINE select */ if (event->type == GDK_3BUTTON_PRESS) /* LINE select */
{ {
gtk_xtext_check_mark_stamp (xtext, mask); gtk_xtext_check_mark_stamp (xtext, mask);
if (gtk_xtext_get_word (xtext, x, y, &ent, 0, 0)) if (gtk_xtext_get_word (xtext, x, y, &ent, 0, 0, 0))
{ {
gtk_xtext_selection_clear (xtext->buffer); gtk_xtext_selection_clear (xtext->buffer);
ent->mark_start = 0; ent->mark_start = 0;
@ -2852,7 +2897,7 @@ gtk_xtext_render_flush (GtkXText * xtext, int x, int y, unsigned char *str,
{ {
int str_width, dofill; int str_width, dofill;
GdkDrawable *pix = NULL; GdkDrawable *pix = NULL;
int dest_x, dest_y; int dest_x = 0, dest_y = 0;
if (xtext->dont_render || len < 1 || xtext->hidden) if (xtext->dont_render || len < 1 || xtext->hidden)
return 0; return 0;
@ -5904,7 +5949,7 @@ gtk_xtext_set_tint (GtkXText *xtext, int tint_red, int tint_green, int tint_blue
} }
void void
gtk_xtext_set_urlcheck_function (GtkXText *xtext, int (*urlcheck_function) (GtkWidget *, char *, int)) gtk_xtext_set_urlcheck_function (GtkXText *xtext, int (*urlcheck_function) (GtkWidget *, char *))
{ {
xtext->urlcheck_function = urlcheck_function; xtext->urlcheck_function = urlcheck_function;
} }

View File

@ -179,7 +179,7 @@ struct _GtkXText
unsigned char scratch_buffer[4096]; unsigned char scratch_buffer[4096];
void (*error_function) (int type); void (*error_function) (int type);
int (*urlcheck_function) (GtkWidget * xtext, char *word, int len); int (*urlcheck_function) (GtkWidget * xtext, char *word);
int jump_out_offset; /* point at which to stop rendering */ int jump_out_offset; /* point at which to stop rendering */
int jump_in_offset; /* "" start rendering */ int jump_in_offset; /* "" start rendering */
@ -274,7 +274,7 @@ void gtk_xtext_set_show_separator (GtkXText *xtext, gboolean show_separator);
void gtk_xtext_set_thin_separator (GtkXText *xtext, gboolean thin_separator); void gtk_xtext_set_thin_separator (GtkXText *xtext, gboolean thin_separator);
void gtk_xtext_set_time_stamp (xtext_buffer *buf, gboolean timestamp); void gtk_xtext_set_time_stamp (xtext_buffer *buf, gboolean timestamp);
void gtk_xtext_set_tint (GtkXText *xtext, int tint_red, int tint_green, int tint_blue); void gtk_xtext_set_tint (GtkXText *xtext, int tint_red, int tint_green, int tint_blue);
void gtk_xtext_set_urlcheck_function (GtkXText *xtext, int (*urlcheck_function) (GtkWidget *, char *, int)); void gtk_xtext_set_urlcheck_function (GtkXText *xtext, int (*urlcheck_function) (GtkWidget *, char *));
void gtk_xtext_set_wordwrap (GtkXText *xtext, gboolean word_wrap); void gtk_xtext_set_wordwrap (GtkXText *xtext, gboolean word_wrap);
xtext_buffer *gtk_xtext_buffer_new (GtkXText *xtext); xtext_buffer *gtk_xtext_buffer_new (GtkXText *xtext);