Files
evolution/widgets/text/e-text-model-uri.c
Christopher James Lahey dc8927136a Changed the license announcement at the top of these files.
2001-10-26  Christopher James Lahey  <clahey@ximian.com>

	* gal/e-paned/e-hpaned.c, gal/e-paned/e-hpaned.h,
	gal/e-paned/e-paned.c, gal/e-paned/e-paned.h,
	gal/e-paned/e-vpaned.c, gal/e-paned/e-vpaned.h,
	gal/e-text/e-completion-match.c, gal/e-text/e-completion-match.h,
	gal/e-text/e-completion-test.c, gal/e-text/e-completion-view.c,
	gal/e-text/e-completion-view.h, gal/e-text/e-completion.c,
	gal/e-text/e-completion.h, gal/e-text/e-entry-test.c,
	gal/e-text/e-entry.c, gal/e-text/e-entry.h,
	gal/e-text/e-table-text-model.c, gal/e-text/e-table-text-model.h,
	gal/e-text/e-text-model-repos.c, gal/e-text/e-text-model-repos.h,
	gal/e-text/e-text-model-test.c, gal/e-text/e-text-model-uri.c,
	gal/e-text/e-text-model-uri.h, gal/e-text/e-text-model.c,
	gal/e-text/e-text-model.h, gal/e-text/e-text.c,
	gal/e-text/e-text.h, gal/util/e-bit-array.c,
	gal/util/e-bit-array.h, gal/util/e-cache.c, gal/util/e-cache.h,
	gal/util/e-iconv.c, gal/util/e-iconv.h, gal/util/e-sorter-array.c,
	gal/util/e-sorter-array.h, gal/util/e-sorter.c,
	gal/util/e-sorter.h, gal/util/e-text-event-processor-emacs-like.c,
	gal/util/e-text-event-processor-emacs-like.h,
	gal/util/e-text-event-processor-types.h,
	gal/util/e-text-event-processor.c,
	gal/util/e-text-event-processor.h, gal/util/e-util.c,
	gal/util/e-util.h, gal/util/e-xml-utils.c, gal/util/e-xml-utils.h,
	gal/widgets/color-group.c, gal/widgets/color-group.h,
	gal/widgets/color-palette.c, gal/widgets/color-palette.h,
	gal/widgets/e-canvas-utils.c, gal/widgets/e-canvas-utils.h,
	gal/widgets/e-canvas-vbox.c, gal/widgets/e-canvas-vbox.h,
	gal/widgets/e-canvas.c, gal/widgets/e-canvas.h,
	gal/widgets/e-categories-master-list-array.c,
	gal/widgets/e-categories-master-list-array.h,
	gal/widgets/e-categories-master-list-combo.c,
	gal/widgets/e-categories-master-list-combo.h,
	gal/widgets/e-categories-master-list-dialog-model.c,
	gal/widgets/e-categories-master-list-dialog-model.h,
	gal/widgets/e-categories-master-list-dialog.c,
	gal/widgets/e-categories-master-list-dialog.h,
	gal/widgets/e-categories-master-list.c,
	gal/widgets/e-categories-master-list.h,
	gal/widgets/e-categories.c, gal/widgets/e-categories.h,
	gal/widgets/e-colors.c, gal/widgets/e-colors.h,
	gal/widgets/e-cursors.c, gal/widgets/e-cursors.h,
	gal/widgets/e-font.c, gal/widgets/e-font.h,
	gal/widgets/e-gui-utils.c, gal/widgets/e-gui-utils.h,
	gal/widgets/e-hscrollbar.c, gal/widgets/e-hscrollbar.h,
	gal/widgets/e-popup-menu.c, gal/widgets/e-popup-menu.h,
	gal/widgets/e-printable.c, gal/widgets/e-printable.h,
	gal/widgets/e-reflow-model.c, gal/widgets/e-reflow-model.h,
	gal/widgets/e-reflow-sorted.c, gal/widgets/e-reflow-sorted.h,
	gal/widgets/e-reflow.c, gal/widgets/e-reflow.h,
	gal/widgets/e-scroll-frame.c, gal/widgets/e-scroll-frame.h,
	gal/widgets/e-selection-model-array.c,
	gal/widgets/e-selection-model-array.h,
	gal/widgets/e-selection-model-simple.c,
	gal/widgets/e-selection-model-simple.h,
	gal/widgets/e-selection-model.c, gal/widgets/e-selection-model.h,
	gal/widgets/e-unicode.c, gal/widgets/e-unicode.h,
	gal/widgets/e-vscrollbar.c, gal/widgets/e-vscrollbar.h,
	gal/widgets/gtk-combo-box.c, gal/widgets/gtk-combo-box.h,
	gal/widgets/gtk-combo-stack.c, gal/widgets/gtk-combo-stack.h,
	gal/widgets/gtk-combo-text.c, gal/widgets/gtk-combo-text.h,
	gal/widgets/test-color.c, gal/widgets/test-e-font.c,
	gal/widgets/test-e-font.h, gal/widgets/test-font-loading.c,
	gal/widgets/widget-color-combo.c,
	gal/widgets/widget-color-combo.h,
	gal/widgets/widget-pixmap-combo.c,
	gal/widgets/widget-pixmap-combo.h,
	src/e-table/e-table-sorted-variable.c, tests/test-define-views.c,
	tests/test-shortcut-bar.c, tests/test-table-1.c,
	tests/test-tree-1.c, tests/test-tree-2.c, tests/test-tree-3.c,
	tests/test-unicode.c: Changed the license announcement at the top
	of these files.

svn path=/trunk/; revision=14158
2001-10-26 18:33:33 +00:00

360 lines
8.5 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* e-text-model-uri.c - a text model w/ clickable URIs
* Copyright 2000, 2001, Ximian, Inc.
*
* Authors:
* Jon Trowbridge <trow@ximian.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License, version 2, as published by the Free Software Foundation.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library 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.
*/
#include <config.h>
#include "e-text-model-uri.h"
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
#include <gtk/gtkmain.h>
#include <libgnome/gnome-url.h>
static void e_text_model_uri_class_init (ETextModelURIClass *class);
static void e_text_model_uri_init (ETextModelURI *model);
static void e_text_model_uri_destroy (GtkObject *object);
static void objectify_uris (ETextModelURI *model);
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
e_text_model_uri_get_type (void)
{
static GtkType model_uri_type = 0;
if (!model_uri_type) {
GtkTypeInfo model_uri_info = {
"ETextModelURI",
sizeof (ETextModelURI),
sizeof (ETextModelURIClass),
(GtkClassInitFunc) e_text_model_uri_class_init,
(GtkObjectInitFunc) e_text_model_uri_init,
NULL, /* reserved_1 */
NULL, /* reserved_2 */
(GtkClassInitFunc) NULL
};
model_uri_type = gtk_type_unique (e_text_model_get_type (), &model_uri_info);
}
return model_uri_type;
}
static void
e_text_model_uri_class_init (ETextModelURIClass *klass)
{
GtkObjectClass *object_class;
ETextModelClass *model_class;
object_class = (GtkObjectClass *) klass;
model_class = E_TEXT_MODEL_CLASS (klass);
parent_class = gtk_type_class (e_text_model_get_type ());
object_class->destroy = e_text_model_uri_destroy;
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;
}
static void
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 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;
static void
regex_init (void)
{
gint i;
if (regex_count != 0)
return;
while (uri_regex[regex_count]) ++regex_count;
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)
{
static gboolean objectifying = FALSE;
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;
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));
if (!changed) {
/* Check that there is a 1-1 correspondence between object positions. */
GList *jter;
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;
}
}
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);
if (E_TEXT_MODEL_CLASS(parent_class)->objectify)
E_TEXT_MODEL_CLASS(parent_class)->objectify (model);
}
static void
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)
{
ETextModelURI *model_uri = E_TEXT_MODEL_URI (model);
objectify_idle_flush (model_uri);
return g_list_length (model_uri->uris);
}
static const gchar *
e_text_model_uri_get_nth_object (ETextModel *model, gint i, gint *len)
{
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)
{
gchar *obj_str;
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 *
e_text_model_uri_new (void)
{
return E_TEXT_MODEL (gtk_type_new (e_text_model_uri_get_type ()));
}
/* $Id$ */