diff options
Diffstat (limited to 'widgets/text/e-text-model.c')
-rw-r--r-- | widgets/text/e-text-model.c | 520 |
1 files changed, 366 insertions, 154 deletions
diff --git a/widgets/text/e-text-model.c b/widgets/text/e-text-model.c index acd08ecb04..5eccaff9fb 100644 --- a/widgets/text/e-text-model.c +++ b/widgets/text/e-text-model.c @@ -16,31 +16,40 @@ * * Author: Federico Mena <federico@nuclecu.unam.mx> */ +#undef PARANOID_DEBUGGING + #include <config.h> #include <ctype.h> +#include "e-text-model-repos.h" #include "e-text-model.h" +#define CLASS(obj) (E_TEXT_MODEL_CLASS (GTK_OBJECT (obj)->klass)) + enum { E_TEXT_MODEL_CHANGED, + E_TEXT_MODEL_REPOSITION, + E_TEXT_MODEL_OBJECT_ACTIVATED, E_TEXT_MODEL_LAST_SIGNAL }; static guint e_text_model_signals[E_TEXT_MODEL_LAST_SIGNAL] = { 0 }; -static void e_text_model_class_init (ETextModelClass *class); -static void e_text_model_init (ETextModel *model); -static void e_text_model_destroy (GtkObject *object); - -static gchar *e_text_model_real_get_text(ETextModel *model); -static void e_text_model_real_set_text(ETextModel *model, gchar *text); -static void e_text_model_real_insert(ETextModel *model, gint postion, gchar *text); -static void e_text_model_real_insert_length(ETextModel *model, gint postion, gchar *text, gint length); -static void e_text_model_real_delete(ETextModel *model, gint postion, gint length); +struct _ETextModelPrivate { + gchar *text; + gint len; +}; -static gint e_text_model_real_object_count(ETextModel *model); -static const gchar *e_text_model_real_get_nth_object(ETextModel *model, gint n); -static void e_text_model_real_activate_nth_object(ETextModel *mode, gint n); +static void e_text_model_class_init (ETextModelClass *class); +static void e_text_model_init (ETextModel *model); +static void e_text_model_destroy (GtkObject *object); +static gint e_text_model_real_validate_position (ETextModel *, gint pos); +static const gchar *e_text_model_real_get_text (ETextModel *model); +static gint e_text_model_real_get_text_length (ETextModel *model); +static void e_text_model_real_set_text (ETextModel *model, const gchar *text); +static void e_text_model_real_insert (ETextModel *model, gint postion, const gchar *text); +static void e_text_model_real_insert_length (ETextModel *model, gint postion, const gchar *text, gint length); +static void e_text_model_real_delete (ETextModel *model, gint postion, gint length); static GtkObject *parent_class; @@ -95,18 +104,45 @@ e_text_model_class_init (ETextModelClass *klass) GTK_SIGNAL_OFFSET (ETextModelClass, changed), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); + + e_text_model_signals[E_TEXT_MODEL_REPOSITION] = + gtk_signal_new ("reposition", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (ETextModelClass, reposition), + gtk_marshal_NONE__POINTER_POINTER, + GTK_TYPE_NONE, 2, + GTK_TYPE_POINTER, GTK_TYPE_POINTER); + + e_text_model_signals[E_TEXT_MODEL_OBJECT_ACTIVATED] = + gtk_signal_new ("object_activated", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (ETextModelClass, object_activated), + gtk_marshal_NONE__INT, + GTK_TYPE_NONE, 1, + GTK_TYPE_INT); gtk_object_class_add_signals (object_class, e_text_model_signals, E_TEXT_MODEL_LAST_SIGNAL); - klass->changed = NULL; - klass->get_text = e_text_model_real_get_text; - klass->set_text = e_text_model_real_set_text; - klass->insert = e_text_model_real_insert; + /* No default signal handlers. */ + klass->changed = NULL; + klass->reposition = NULL; + klass->object_activated = NULL; + + klass->validate_pos = e_text_model_real_validate_position; + + klass->get_text = e_text_model_real_get_text; + klass->get_text_len = e_text_model_real_get_text_length; + klass->set_text = e_text_model_real_set_text; + klass->insert = e_text_model_real_insert; klass->insert_length = e_text_model_real_insert_length; - klass->delete = e_text_model_real_delete; - klass->obj_count = e_text_model_real_object_count; - klass->get_nth_obj = e_text_model_real_get_nth_object; - klass->activate_nth_obj = e_text_model_real_activate_nth_object; + klass->delete = e_text_model_real_delete; + + /* We explicitly don't define default handlers for these. */ + klass->objectify = NULL; + klass->obj_count = NULL; + klass->get_nth_obj = NULL; object_class->destroy = e_text_model_destroy; } @@ -115,7 +151,9 @@ e_text_model_class_init (ETextModelClass *klass) static void e_text_model_init (ETextModel *model) { - model->text = NULL; + model->priv = g_new0 (struct _ETextModelPrivate, 1); + model->priv->text = g_strdup (""); + model->priv->len = 0; } /* Destroy handler for the text item */ @@ -129,250 +167,424 @@ e_text_model_destroy (GtkObject *object) model = E_TEXT_MODEL (object); - if (model->text) - g_free (model->text); + g_free (model->priv->text); + + g_free (model->priv); + model->priv = NULL; if (GTK_OBJECT_CLASS (parent_class)->destroy) - (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); + GTK_OBJECT_CLASS (parent_class)->destroy (object); } -static gchar * -e_text_model_real_get_text(ETextModel *model) +static gint +e_text_model_real_validate_position (ETextModel *model, gint pos) { - if (model->text) - return model->text; + gint len; + + if (pos < 0) + pos = 0; + else if (pos > ( len = e_text_model_get_text_length (model) )) + pos = len; + + return pos; +} + +static const gchar * +e_text_model_real_get_text (ETextModel *model) +{ + if (model->priv->text) + return model->priv->text; else return ""; } -static void -e_text_model_real_set_text(ETextModel *model, gchar *text) +static gint +e_text_model_real_get_text_length (ETextModel *model) { - if (model->text) - g_free(model->text); - model->text = g_strdup(text); - e_text_model_changed(model); + if (model->priv->len < 0) + model->priv->len = strlen (e_text_model_get_text (model)); + + return model->priv->len; } static void -e_text_model_real_insert(ETextModel *model, gint position, gchar *text) +e_text_model_real_set_text (ETextModel *model, const gchar *text) { - gchar *temp = g_strdup_printf("%.*s%s%s", position, model->text, text, model->text + position); - if (model->text) - g_free(model->text); - model->text = temp; - e_text_model_changed(model); + EReposAbsolute repos; + gboolean changed = FALSE; + + if (text == NULL) { + + changed = (model->priv->text != NULL); + + g_free (model->priv->text); + model->priv->text = NULL; + model->priv->len = -1; + + } else if (model->priv->text == NULL || strcmp (model->priv->text, text)) { + + g_free (model->priv->text); + model->priv->text = g_strdup (text); + model->priv->len = -1; + + changed = TRUE; + } + + if (changed) { + e_text_model_changed (model); + repos.model = model; + repos.pos = -1; + e_text_model_reposition (model, e_repos_absolute, &repos); + } } static void -e_text_model_real_insert_length(ETextModel *model, gint position, gchar *text, gint length) +e_text_model_real_insert (ETextModel *model, gint position, const gchar *text) { - gchar *temp = g_strdup_printf("%.*s%.*s%s", position, model->text, length, text, model->text + position); - if (model->text) - g_free(model->text); - model->text = temp; - e_text_model_changed(model); + EReposInsertShift repos; + gchar *temp; + gint ins_len; + + temp = g_strdup_printf ("%.*s%s%s", position, model->priv->text, text, model->priv->text + position); + ins_len = strlen (text); + + if (model->priv->text) + g_free (model->priv->text); + + model->priv->text = temp; + + if (model->priv->len >= 0) + model->priv->len += ins_len; + + e_text_model_changed (model); + + repos.model = model; + repos.pos = position; + repos.len = ins_len; + + e_text_model_reposition (model, e_repos_insert_shift, &repos); } static void -e_text_model_real_delete(ETextModel *model, gint position, gint length) +e_text_model_real_insert_length (ETextModel *model, gint position, const gchar *text, gint length) { - memmove(model->text + position, model->text + position + length, strlen(model->text + position + length) + 1); - e_text_model_changed(model); -} + EReposInsertShift repos; + gchar *temp = g_strdup_printf ("%.*s%.*s%s", position, model->priv->text, length, text, model->priv->text + position); -static gint -e_text_model_real_object_count(ETextModel *model) -{ - gint count = 0; - gchar *c = model->text; - - if (c) { - while (*c) { - if (*c == '\1') - ++count; - ++c; - } - } - return count; + if (model->priv->text) + g_free (model->priv->text); + model->priv->text = temp; + + if (model->priv->len >= 0) + model->priv->len += length; + + e_text_model_changed (model); + + repos.model = model; + repos.pos = position; + repos.len = length; + + e_text_model_reposition (model, e_repos_insert_shift, &repos); } -static const gchar * -e_text_model_real_get_nth_object(ETextModel *model, gint n) +static void +e_text_model_real_delete (ETextModel *model, gint position, gint length) { - return ""; + EReposDeleteShift repos; + + memmove (model->priv->text + position, model->priv->text + position + length, strlen (model->priv->text + position + length) + 1); + + if (model->priv->len >= 0) + model->priv->len -= length; + + e_text_model_changed (model); + + repos.model = model; + repos.pos = position; + repos.len = length; + + e_text_model_reposition (model, e_repos_delete_shift, &repos); } -static void -e_text_model_real_activate_nth_object(ETextModel *model, gint n) +void +e_text_model_changed (ETextModel *model) { - /* By default, do nothing */ + g_return_if_fail (model != NULL); + g_return_if_fail (E_IS_TEXT_MODEL (model)); + + /* + Objectify before emitting any signal. + While this method could, in theory, do pretty much anything, it is meant + for scanning objects and converting substrings into embedded objects. + */ + if (CLASS (model)->objectify) + CLASS (model)->objectify (model); + + gtk_signal_emit (GTK_OBJECT (model), + e_text_model_signals[E_TEXT_MODEL_CHANGED]); } void -e_text_model_changed(ETextModel *model) +e_text_model_reposition (ETextModel *model, ETextModelReposFn fn, gpointer repos_data) { g_return_if_fail (model != NULL); g_return_if_fail (E_IS_TEXT_MODEL (model)); + g_return_if_fail (fn != NULL); gtk_signal_emit (GTK_OBJECT (model), - e_text_model_signals [E_TEXT_MODEL_CHANGED]); + e_text_model_signals[E_TEXT_MODEL_REPOSITION], + fn, repos_data); } -gchar * -e_text_model_get_text(ETextModel *model) +gint +e_text_model_validate_position (ETextModel *model, gint pos) +{ + g_return_val_if_fail (model != NULL, 0); + g_return_val_if_fail (E_IS_TEXT_MODEL (model), 0); + + if (CLASS (model)->validate_pos) + pos = CLASS (model)->validate_pos (model, pos); + + return pos; +} + +const gchar * +e_text_model_get_text (ETextModel *model) { g_return_val_if_fail (model != NULL, NULL); g_return_val_if_fail (E_IS_TEXT_MODEL (model), NULL); - if ( E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->get_text ) - return E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->get_text(model); - else - return ""; + if (CLASS (model)->get_text) + return CLASS (model)->get_text (model); + + return ""; +} + +gint +e_text_model_get_text_length (ETextModel *model) +{ + g_return_val_if_fail (model != NULL, 0); + g_return_val_if_fail (E_IS_TEXT_MODEL (model), 0); + + if (CLASS (model)->get_text_len (model)) { + + gint len = CLASS (model)->get_text_len (model); + +#ifdef PARANOID_DEBUGGING + const gchar *str = e_text_model_get_text (model); + gint len2 = str ? strlen (str) : 0; + if (len != len) + g_error ("\"%s\" length reported as %d, not %d.", str, len, len2); +#endif + + return len; + + } else { + /* Calculate length the old-fashioned way... */ + const gchar *str = e_text_model_get_text (model); + return str ? strlen (str) : 0; + } +} + +void +e_text_model_set_text (ETextModel *model, const gchar *text) +{ + g_return_if_fail (model != NULL); + g_return_if_fail (E_IS_TEXT_MODEL (model)); + + if (CLASS (model)->set_text) + CLASS (model)->set_text (model, text); +} + +void +e_text_model_insert (ETextModel *model, gint position, const gchar *text) +{ + g_return_if_fail (model != NULL); + g_return_if_fail (E_IS_TEXT_MODEL (model)); + + if (text == NULL) + return; + + if (CLASS (model)->insert) + CLASS (model)->insert (model, position, text); } void -e_text_model_set_text(ETextModel *model, gchar *text) +e_text_model_insert_length (ETextModel *model, gint position, const gchar *text, gint length) { g_return_if_fail (model != NULL); g_return_if_fail (E_IS_TEXT_MODEL (model)); + g_return_if_fail (length >= 0); + + + if (text == NULL || length == 0) + return; - if ( E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->set_text ) - E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->set_text(model, text); + if (CLASS (model)->insert_length) + CLASS (model)->insert_length (model, position, text, length); } void -e_text_model_insert(ETextModel *model, gint position, gchar *text) +e_text_model_prepend (ETextModel *model, const gchar *text) { g_return_if_fail (model != NULL); g_return_if_fail (E_IS_TEXT_MODEL (model)); - if ( E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->insert ) - E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->insert(model, position, text); + if (text == NULL) + return; + + e_text_model_insert (model, 0, text); } void -e_text_model_insert_length(ETextModel *model, gint position, gchar *text, gint length) +e_text_model_append (ETextModel *model, const gchar *text) { g_return_if_fail (model != NULL); g_return_if_fail (E_IS_TEXT_MODEL (model)); - if ( E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->insert_length ) - E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->insert_length(model, position, text, length); + if (text == NULL) + return; + + e_text_model_insert (model, e_text_model_get_text_length (model), text); } void -e_text_model_delete(ETextModel *model, gint position, gint length) +e_text_model_delete (ETextModel *model, gint position, gint length) { + gint txt_len; + g_return_if_fail (model != NULL); g_return_if_fail (E_IS_TEXT_MODEL (model)); + g_return_if_fail (length >= 0); + + txt_len = e_text_model_get_text_length (model); + if (position + length > txt_len) + length = txt_len - position; + + if (length <= 0) + return; - if ( E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->delete ) - E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->delete(model, position, length); + if (CLASS (model)->delete) + CLASS (model)->delete (model, position, length); } gint -e_text_model_object_count(ETextModel *model) +e_text_model_object_count (ETextModel *model) { g_return_val_if_fail (model != NULL, 0); g_return_val_if_fail (E_IS_TEXT_MODEL (model), 0); - if ( E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->obj_count) - return E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->obj_count(model); - else - return 0; + if (CLASS (model)->obj_count) + return CLASS (model)->obj_count (model); + + return 0; } const gchar * -e_text_model_get_nth_object(ETextModel *model, gint n) +e_text_model_get_nth_object (ETextModel *model, gint n, gint *len) { g_return_val_if_fail (model != NULL, NULL); g_return_val_if_fail (E_IS_TEXT_MODEL (model), NULL); - g_return_val_if_fail (n >= 0, NULL); - if ( E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->get_nth_obj ) - return E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->get_nth_obj(model, n); - else - return ""; -} + if (n < 0 || n >= e_text_model_object_count (model)) + return NULL; -void -e_text_model_activate_nth_object(ETextModel *model, gint n) -{ - g_return_if_fail (model != NULL); - g_return_if_fail (E_IS_TEXT_MODEL (model)); - g_return_if_fail (n >= 0); + if (CLASS (model)->get_nth_obj) + return CLASS (model)->get_nth_obj (model, n, len); - if ( E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->activate_nth_obj ) - E_TEXT_MODEL_CLASS(GTK_OBJECT(model)->klass)->activate_nth_obj(model, n); + return NULL; } gchar * -e_text_model_strdup_expanded_text(ETextModel *model) +e_text_model_strdup_nth_object (ETextModel *model, gint n) { - gint len = 0, i, N; - gchar *expanded, *dest; - const gchar *src; + const gchar *obj; + gint len = 0; g_return_val_if_fail (model != NULL, NULL); g_return_val_if_fail (E_IS_TEXT_MODEL (model), NULL); - if (model->text == NULL) - return NULL; + obj = e_text_model_get_nth_object (model, n, &len); + + return obj ? g_strndup (obj, n) : NULL; +} - N = e_text_model_object_count (model); - if (N == 0) - return g_strdup (model->text); +void +e_text_model_get_nth_object_bounds (ETextModel *model, gint n, gint *start, gint *end) +{ + const gchar *txt = NULL, *obj = NULL; + gint len = 0; - /* First, compute the length of the expanded string. */ + g_return_if_fail (model != NULL); + g_return_if_fail (E_IS_TEXT_MODEL (model)); - len = strlen (model->text); - len -= N; /* Subtract out the \1s that signify the objects. */ + txt = e_text_model_get_text (model); + obj = e_text_model_get_nth_object (model, n, &len); - for (i=0; i<N; ++i) - len += strlen (e_text_model_get_nth_object (model ,i)); - + g_return_if_fail (obj != NULL); - /* Next, allocate and build the expanded string. */ - expanded = g_new0 (gchar, len+1); + if (start) + *start = obj - txt; + if (end) + *end = obj - txt + len; +} - src = model->text; - dest = expanded; - i = 0; - while (*src) { - if (*src == '\1') { - const gchar *src_obj; - - g_assert (i < N); - src_obj = e_text_model_get_nth_object (model, i); - - if (src_obj) { - while (*src_obj) { - *dest = *src_obj; - ++src_obj; - ++dest; - } - } - - ++src; - ++i; +gint +e_text_model_get_object_at_offset (ETextModel *model, gint offset) +{ + g_return_val_if_fail (model != NULL, -1); + g_return_val_if_fail (E_IS_TEXT_MODEL (model), -1); + + if (offset < 0 || offset >= e_text_model_get_text_length (model)) + return -1; + + /* If an optimized version has been provided, we use it. */ + if (CLASS (model)->obj_at_offset) { + + return CLASS (model)->obj_at_offset (model, offset); + + } else { + /* If not, we fake it.*/ - } else { + gint i, N, pos0, pos1; - *dest = *src; - ++src; - ++dest; + N = e_text_model_object_count (model); + for (i = 0; i < N; ++i) { + e_text_model_get_nth_object_bounds (model, i, &pos0, &pos1); + if (pos0 <= offset && offset < pos1) + return i; } + } - return expanded; + return -1; +} + +gint +e_text_model_get_object_at_pointer (ETextModel *model, const gchar *s) +{ + g_return_val_if_fail (model != NULL, -1); + g_return_val_if_fail (E_IS_TEXT_MODEL (model), -1); + g_return_val_if_fail (s != NULL, -1); + + return e_text_model_get_object_at_offset (model, s - e_text_model_get_text (model)); +} + +void +e_text_model_activate_nth_object (ETextModel *model, gint n) +{ + g_return_if_fail (model != NULL); + g_return_if_fail (E_IS_TEXT_MODEL (model)); + g_return_if_fail (n >= 0); + g_return_if_fail (n < e_text_model_object_count (model)); + + gtk_signal_emit (GTK_OBJECT (model), e_text_model_signals[E_TEXT_MODEL_OBJECT_ACTIVATED], n); } ETextModel * -e_text_model_new(void) +e_text_model_new (void) { ETextModel *model = gtk_type_new (e_text_model_get_type ()); - model->text = g_strdup(""); return model; } |