diff options
Diffstat (limited to 'widgets/text/e-text-model-uri.c')
-rw-r--r-- | widgets/text/e-text-model-uri.c | 273 |
1 files changed, 220 insertions, 53 deletions
diff --git a/widgets/text/e-text-model-uri.c b/widgets/text/e-text-model-uri.c index bd8dbb18e8..55b293016f 100644 --- a/widgets/text/e-text-model-uri.c +++ b/widgets/text/e-text-model-uri.c @@ -9,6 +9,8 @@ #include <config.h> #include <ctype.h> +#include <sys/types.h> +#include <regex.h> #include "e-text-model-uri.h" static void e_text_model_uri_class_init (ETextModelURIClass *class); @@ -17,10 +19,17 @@ static void e_text_model_uri_destroy (GtkObject *object); static void objectify_uris (ETextModelURI *model); -static void e_text_model_uri_set_text (ETextModel *model, gchar *text); -static const gchar *e_text_model_uri_get_nth_object (ETextModel *model, gint); +static void e_text_model_uri_objectify (ETextModel *model); +static gint e_text_model_uri_validate_pos (ETextModel *model, gint pos); +static gint e_text_model_uri_get_obj_count (ETextModel *model); +static const gchar *e_text_model_uri_get_nth_object (ETextModel *model, gint i, gint *len); static void e_text_model_uri_activate_nth_object (ETextModel *model, gint); +typedef struct _ObjInfo ObjInfo; +struct _ObjInfo { + gint offset, len; +}; + static GtkObject *parent_class; GtkType @@ -59,9 +68,13 @@ e_text_model_uri_class_init (ETextModelURIClass *klass) object_class->destroy = e_text_model_uri_destroy; - model_class->set_text = e_text_model_uri_set_text; + model_class->object_activated = e_text_model_uri_activate_nth_object; + + model_class->objectify = e_text_model_uri_objectify; + model_class->validate_pos = e_text_model_uri_validate_pos; + model_class->obj_count = e_text_model_uri_get_obj_count; model_class->get_nth_obj = e_text_model_uri_get_nth_object; - model_class->activate_nth_obj = e_text_model_uri_activate_nth_object; + } static void @@ -73,95 +86,249 @@ e_text_model_uri_init (ETextModelURI *model) static void e_text_model_uri_destroy (GtkObject *object) { + ETextModelURI *model_uri = E_TEXT_MODEL_URI (object); + GList *iter; + + if (model_uri->objectify_idle) { + gtk_idle_remove (model_uri->objectify_idle); + model_uri->objectify_idle = 0; + } + + for (iter = model_uri->uris; iter != NULL; iter = g_list_next (iter)) + g_free (iter->data); + g_list_free (model_uri->uris); + model_uri->uris = NULL; + + if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } -static gchar * -extract_uri (gchar **in_str) -{ - gchar *s = *in_str; - if (strncmp (s, "http://", 7) == 0) { - gint periods=0; - gchar *uri; +static const gchar *uri_regex[] = { + "(((news|telnet|nttp|file|http|ftp|https)://)|(www|ftp))[-A-Za-z0-9\\.]+(:[0-9]*)?/[-A-Za-z0-9_\\$\\.\\+\\!\\*\\(\\),;:@&=\\?/~\\#\\%]*[^]'\\.}>\\) ,\\\"]", + "(((news|telnet|nttp|file|http|ftp|https)://)|(www|ftp))[-A-Za-z0-9\\.]+[-A-Za-z0-9](:[0-9]*)?", + "mailto:[A-Za-z0-9_]+@[-A-Za-z0-9_]+\\.[-A-Za-z0-9\\.]+[-A-Za-z0-9]", + NULL +}; +static gint regex_count = 0; +static regex_t *regex_compiled = NULL; - s += 7; +static void +regex_init (void) +{ + gint i; - while (*s && (isalnum((gint) *s) || (*s == '.' && periods < 2))) { - if (*s == '.') - ++periods; - ++s; - } + if (regex_count != 0) + return; - uri = g_strndup (*in_str, s - *in_str); - *in_str = s; - return uri; + while (uri_regex[regex_count]) ++regex_count; - } else { - *in_str = s+1; - return NULL; + regex_compiled = g_new0 (regex_t, regex_count); + + for (i=0; i<regex_count; ++i) { + if (regcomp (®ex_compiled[i], uri_regex[i], REG_EXTENDED)) + g_error ("Bad regex?: %s", uri_regex[i]); } } + static void objectify_uris (ETextModelURI *model_uri) { - ETextModel *model = E_TEXT_MODEL (model_uri); - gchar *new_text; - gchar *src, *dest; - GList *uris = NULL; + static gboolean objectifying = FALSE; - if (model->text == NULL) + ETextModel *model = E_TEXT_MODEL (model_uri); + const gchar *txt; + GList *iter, *old_uris; + gint offset, len; + gboolean found_match; + regmatch_t match; + gboolean changed; + + if (objectifying) return; - new_text = g_new0 (gchar, strlen (model->text)+1); + objectifying = TRUE; + + if (regex_count == 0) + regex_init (); + + txt = e_text_model_get_text (model); + len = e_text_model_get_text_length (model); + + old_uris = model_uri->uris; + model_uri->uris = NULL; + + if (txt) { + offset = 0; + found_match = TRUE; + + while (offset < len && found_match) { + + gint i, so=-1, eo=-1; + + found_match = FALSE; + + for (i=0; i<regex_count; ++i) { + + if (regexec (®ex_compiled[i], txt+offset, 1, &match, 0) == 0) { + + /* Take earliest match possible. In case of a tie, take the + largest possible match. */ + if (!found_match + || match.rm_so < so + || (match.rm_so == so && match.rm_eo > eo)) { + so = match.rm_so; + eo = match.rm_eo; + } + found_match = TRUE; + } + } + + if (found_match) { + + ObjInfo *info = g_new0 (ObjInfo, 1); + info->offset = offset + so; + info->len = eo - so; + + model_uri->uris = g_list_append (model_uri->uris, info); + + offset += eo; + } + } + } + + changed = (g_list_length (old_uris) != g_list_length (model_uri->uris)); - src = model->text; - dest = new_text; + if (!changed) { + /* Check that there is a 1-1 correspondence between object positions. */ + GList *jter; - while (*src) { - gchar *uri_str; - gchar *next = src; - if ( (uri_str = extract_uri (&next)) ) { - uris = g_list_append (uris, uri_str); - *dest = '\1'; - } else { - *dest = *src; + for (iter = model_uri->uris; iter != NULL && !changed; iter = g_list_next (iter)) { + ObjInfo *info = (ObjInfo *) iter->data; + found_match = FALSE; + for (jter = old_uris; jter != NULL && !found_match; jter = g_list_next (jter)) { + ObjInfo *jnfo = (ObjInfo *) jter->data; + + if (info->offset == jnfo->offset && info->len == jnfo->len) + found_match = TRUE; + } + changed = !found_match; } - ++dest; - src = next; } - g_free (model->text); - model->text = new_text; + if (changed) + e_text_model_changed (model); + + /* Free old uris */ + for (iter = old_uris; iter != NULL; iter = g_list_next (iter)) + g_free (iter->data); + g_list_free (old_uris); + + objectifying = FALSE; +} + +static gboolean +objectify_idle_cb (gpointer ptr) +{ + ETextModelURI *model_uri = E_TEXT_MODEL_URI (ptr); + + g_assert (model_uri->objectify_idle); + objectify_uris (model_uri); + model_uri->objectify_idle = 0; + + return FALSE; +} + +static void +e_text_model_uri_objectify (ETextModel *model) +{ + ETextModelURI *model_uri = E_TEXT_MODEL_URI (model); + + if (model_uri->objectify_idle == 0) + model_uri->objectify_idle = gtk_idle_add (objectify_idle_cb, model); - /* Leaking old list */ - model_uri->uris = uris; + if (E_TEXT_MODEL_CLASS(parent_class)->objectify) + E_TEXT_MODEL_CLASS(parent_class)->objectify (model); } static void -e_text_model_uri_set_text (ETextModel *model, gchar *text) +objectify_idle_flush (ETextModelURI *model_uri) +{ + if (model_uri->objectify_idle) { + gtk_idle_remove (model_uri->objectify_idle); + model_uri->objectify_idle = 0; + objectify_uris (model_uri); + } +} + +static gint +e_text_model_uri_validate_pos (ETextModel *model, gint pos) +{ + gint obj_num; + + /* Cause us to skip over objects */ + + obj_num = e_text_model_get_object_at_offset (model, pos); + if (obj_num != -1) { + gint pos0, pos1, mp; + e_text_model_get_nth_object_bounds (model, obj_num, &pos0, &pos1); + mp = (pos0 + pos1)/2; + if (pos0 < pos && pos < mp) + pos = pos1; + else if (mp <= pos && pos < pos1) + pos = pos0; + } + + + + if (E_TEXT_MODEL_CLASS (parent_class)->validate_pos) + pos = E_TEXT_MODEL_CLASS (parent_class)->validate_pos (model, pos); + + return pos; +} + +static gint +e_text_model_uri_get_obj_count (ETextModel *model) { - if (E_TEXT_MODEL_CLASS(parent_class)->set_text) - E_TEXT_MODEL_CLASS(parent_class)->set_text (model, text); + ETextModelURI *model_uri = E_TEXT_MODEL_URI (model); + + objectify_idle_flush (model_uri); - objectify_uris (E_TEXT_MODEL_URI (model)); + return g_list_length (model_uri->uris); } static const gchar * -e_text_model_uri_get_nth_object (ETextModel *model, gint i) +e_text_model_uri_get_nth_object (ETextModel *model, gint i, gint *len) { - return (const gchar *) g_list_nth_data (E_TEXT_MODEL_URI (model)->uris, i); + ETextModelURI *model_uri = E_TEXT_MODEL_URI (model); + ObjInfo *info; + const gchar *txt; + + objectify_idle_flush (model_uri); + + txt = e_text_model_get_text (model); + + info = (ObjInfo *) g_list_nth_data (model_uri->uris, i); + g_return_val_if_fail (info != NULL, NULL); + + + if (len) + *len = info->len; + return txt + info->offset; } static void e_text_model_uri_activate_nth_object (ETextModel *model, gint i) { - const gchar *obj_str; + gchar *obj_str; - obj_str = e_text_model_get_nth_object (model, i); + objectify_idle_flush (E_TEXT_MODEL_URI (model)); + + obj_str = e_text_model_strdup_nth_object (model, i); gnome_url_show (obj_str); + g_free (obj_str); } ETextModel * |