aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/text/e-text-model-uri.c
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/text/e-text-model-uri.c')
-rw-r--r--widgets/text/e-text-model-uri.c273
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 (&regex_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 (&regex_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 *