aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/text
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/text')
-rw-r--r--widgets/text/Makefile.am17
-rw-r--r--widgets/text/e-reflow-model.c350
-rw-r--r--widgets/text/e-reflow-model.h108
-rw-r--r--widgets/text/e-reflow.c1534
-rw-r--r--widgets/text/e-reflow.h141
-rw-r--r--widgets/text/e-text.c4
-rw-r--r--widgets/text/gal-a11y-e-text-factory.c101
-rw-r--r--widgets/text/gal-a11y-e-text-factory.h50
-rw-r--r--widgets/text/gal-a11y-e-text.c1134
-rw-r--r--widgets/text/gal-a11y-e-text.h57
10 files changed, 3489 insertions, 7 deletions
diff --git a/widgets/text/Makefile.am b/widgets/text/Makefile.am
index ad106158d9..ee426f4791 100644
--- a/widgets/text/Makefile.am
+++ b/widgets/text/Makefile.am
@@ -15,22 +15,29 @@ privsolib_LTLIBRARIES = libetext.la
libetext_la_SOURCES = \
e-text-model-repos.c \
e-text-model.c \
- e-text.c
+ e-text.c \
+ e-reflow.c \
+ e-reflow-model.c \
+ gal-a11y-e-text-factory.c \
+ gal-a11y-e-text.c
libetextincludedir = $(privincludedir)/text
libetextinclude_HEADERS = \
e-text-model-repos.h \
e-text-model.h \
- e-text.h
+ e-text.h \
+ e-reflow.h \
+ e-reflow-model.h \
+ gal-a11y-e-text-factory.h \
+ gal-a11y-e-text.h
libetext_la_LDFLAGS = $(NO_UNDEFINED)
libetext_la_LIBADD = \
- $(WIN32_BOOTSTRAP_LIBS) \
- $(top_builddir)/e-util/libeutil.la \
$(top_builddir)/a11y/libevolution-a11y.la \
- $(top_builddir)/widgets/table/libetable.la \
+ $(top_builddir)/e-util/libeutil.la \
+ $(top_builddir)/widgets/misc/libemiscwidgets.la \
$(E_UTIL_LIBS) \
$(GNOME_PLATFORM_LIBS) \
$(REGEX_LIBS)
diff --git a/widgets/text/e-reflow-model.c b/widgets/text/e-reflow-model.c
new file mode 100644
index 0000000000..2bf5e4990d
--- /dev/null
+++ b/widgets/text/e-reflow-model.c
@@ -0,0 +1,350 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Chris Lahey <clahey@ximian.com>
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+#include <config.h>
+
+#include "e-util/e-util.h"
+
+#include "e-reflow-model.h"
+
+G_DEFINE_TYPE (EReflowModel, e_reflow_model, G_TYPE_OBJECT)
+
+#define d(x)
+
+d(static gint depth = 0;)
+
+
+enum {
+ MODEL_CHANGED,
+ COMPARISON_CHANGED,
+ MODEL_ITEMS_INSERTED,
+ MODEL_ITEM_CHANGED,
+ MODEL_ITEM_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint e_reflow_model_signals [LAST_SIGNAL] = { 0, };
+
+/**
+ * e_reflow_model_set_width:
+ * @e_reflow_model: The e-reflow-model to operate on
+ * @width: The new value for the width of each item.
+ */
+void
+e_reflow_model_set_width (EReflowModel *e_reflow_model, gint width)
+{
+ g_return_if_fail (e_reflow_model != NULL);
+ g_return_if_fail (E_IS_REFLOW_MODEL (e_reflow_model));
+
+ E_REFLOW_MODEL_GET_CLASS (e_reflow_model)->set_width (e_reflow_model, width);
+}
+
+/**
+ * e_reflow_model_count:
+ * @e_reflow_model: The e-reflow-model to operate on
+ *
+ * Returns: the number of items in the reflow model.
+ */
+gint
+e_reflow_model_count (EReflowModel *e_reflow_model)
+{
+ g_return_val_if_fail (e_reflow_model != NULL, 0);
+ g_return_val_if_fail (E_IS_REFLOW_MODEL (e_reflow_model), 0);
+
+ return E_REFLOW_MODEL_GET_CLASS (e_reflow_model)->count (e_reflow_model);
+}
+
+/**
+ * e_reflow_model_height:
+ * @e_reflow_model: The e-reflow-model to operate on
+ * @n: The item number to get the height of.
+ * @parent: The parent GnomeCanvasItem.
+ *
+ * Returns: the height of the nth item.
+ */
+gint
+e_reflow_model_height (EReflowModel *e_reflow_model, gint n, GnomeCanvasGroup *parent)
+{
+ g_return_val_if_fail (e_reflow_model != NULL, 0);
+ g_return_val_if_fail (E_IS_REFLOW_MODEL (e_reflow_model), 0);
+
+ return E_REFLOW_MODEL_GET_CLASS (e_reflow_model)->height (e_reflow_model, n, parent);
+}
+
+/**
+ * e_reflow_model_incarnate:
+ * @e_reflow_model: The e-reflow-model to operate on
+ * @n: The item to create.
+ * @parent: The parent GnomeCanvasItem to create a child of.
+ *
+ * Create a GnomeCanvasItem to represent the nth piece of data.
+ *
+ * Returns: the new GnomeCanvasItem.
+ */
+GnomeCanvasItem *
+e_reflow_model_incarnate (EReflowModel *e_reflow_model, gint n, GnomeCanvasGroup *parent)
+{
+ g_return_val_if_fail (e_reflow_model != NULL, NULL);
+ g_return_val_if_fail (E_IS_REFLOW_MODEL (e_reflow_model), NULL);
+
+ return E_REFLOW_MODEL_GET_CLASS (e_reflow_model)->incarnate (e_reflow_model, n, parent);
+}
+
+/**
+ * e_reflow_model_compare:
+ * @e_reflow_model: The e-reflow-model to operate on
+ * @n1: The first item to compare
+ * @n2: The second item to compare
+ *
+ * Compares item n1 and item n2 to see which should come first.
+ *
+ * Returns: strcmp like semantics for the comparison value.
+ */
+gint
+e_reflow_model_compare (EReflowModel *e_reflow_model, gint n1, gint n2)
+{
+#if 0
+ g_return_val_if_fail (e_reflow_model != NULL, 0);
+ g_return_val_if_fail (E_IS_REFLOW_MODEL (e_reflow_model), 0);
+#endif
+
+ return E_REFLOW_MODEL_GET_CLASS (e_reflow_model)->compare (e_reflow_model, n1, n2);
+}
+
+/**
+ * e_reflow_model_reincarnate:
+ * @e_reflow_model: The e-reflow-model to operate on
+ * @n: The item to create.
+ * @item: The item to reuse.
+ *
+ * Update item to represent the nth piece of data.
+ */
+void
+e_reflow_model_reincarnate (EReflowModel *e_reflow_model, gint n, GnomeCanvasItem *item)
+{
+ g_return_if_fail (e_reflow_model != NULL);
+ g_return_if_fail (E_IS_REFLOW_MODEL (e_reflow_model));
+
+ E_REFLOW_MODEL_GET_CLASS (e_reflow_model)->reincarnate (e_reflow_model, n, item);
+}
+
+static void
+e_reflow_model_class_init (EReflowModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ e_reflow_model_signals [MODEL_CHANGED] =
+ g_signal_new ("model_changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EReflowModelClass, model_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ e_reflow_model_signals [COMPARISON_CHANGED] =
+ g_signal_new ("comparison_changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EReflowModelClass, comparison_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ e_reflow_model_signals [MODEL_ITEMS_INSERTED] =
+ g_signal_new ("model_items_inserted",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EReflowModelClass, model_items_inserted),
+ NULL, NULL,
+ e_marshal_NONE__INT_INT,
+ G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
+
+ e_reflow_model_signals [MODEL_ITEM_CHANGED] =
+ g_signal_new ("model_item_changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EReflowModelClass, model_item_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+
+ e_reflow_model_signals [MODEL_ITEM_REMOVED] =
+ g_signal_new ("model_item_removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EReflowModelClass, model_item_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+
+ klass->set_width = NULL;
+ klass->count = NULL;
+ klass->height = NULL;
+ klass->incarnate = NULL;
+ klass->reincarnate = NULL;
+
+ klass->model_changed = NULL;
+ klass->comparison_changed = NULL;
+ klass->model_items_inserted = NULL;
+ klass->model_item_removed = NULL;
+ klass->model_item_changed = NULL;
+}
+
+static void
+e_reflow_model_init (EReflowModel *e_reflow_model)
+{
+}
+
+#if d(!)0
+static void
+print_tabs (void)
+{
+ gint i;
+ for (i = 0; i < depth; i++)
+ g_print("\t");
+}
+#endif
+
+/**
+ * e_reflow_model_changed:
+ * @e_reflow_model: the reflow model to notify of the change
+ *
+ * Use this function to notify any views of this reflow model that
+ * the contents of the reflow model have changed. This will emit
+ * the signal "model_changed" on the @e_reflow_model object.
+ *
+ * It is preferable to use the e_reflow_model_item_changed() signal to
+ * notify of smaller changes than to invalidate the entire model, as
+ * the views might have ways of caching the information they render
+ * from the model.
+ */
+void
+e_reflow_model_changed (EReflowModel *e_reflow_model)
+{
+ g_return_if_fail (e_reflow_model != NULL);
+ g_return_if_fail (E_IS_REFLOW_MODEL (e_reflow_model));
+
+ d(print_tabs());
+ d(g_print("Emitting model_changed on model 0x%p.\n", e_reflow_model));
+ d(depth++);
+ g_signal_emit (e_reflow_model,
+ e_reflow_model_signals [MODEL_CHANGED], 0);
+ d(depth--);
+}
+
+/**
+ * e_reflow_model_comparison_changed:
+ * @e_reflow_model: the reflow model to notify of the change
+ *
+ * Use this function to notify any views of this reflow model that the
+ * sorting has changed. The actual contents of the items hasn't, so
+ * there's no need to re-query the model for the heights of the
+ * individual items.
+ */
+void
+e_reflow_model_comparison_changed (EReflowModel *e_reflow_model)
+{
+ g_return_if_fail (e_reflow_model != NULL);
+ g_return_if_fail (E_IS_REFLOW_MODEL (e_reflow_model));
+
+ d(print_tabs());
+ d(g_print("Emitting comparison_changed on model 0x%p.\n", e_reflow_model));
+ d(depth++);
+ g_signal_emit (e_reflow_model,
+ e_reflow_model_signals [COMPARISON_CHANGED], 0);
+ d(depth--);
+}
+
+/**
+ * e_reflow_model_items_inserted:
+ * @e_reflow_model: The model changed.
+ * @position: The position the items were insert in.
+ * @count: The number of items inserted.
+ *
+ * Use this function to notify any views of the reflow model that a number of items have been inserted.
+ **/
+void
+e_reflow_model_items_inserted (EReflowModel *e_reflow_model, gint position, gint count)
+{
+ g_return_if_fail (e_reflow_model != NULL);
+ g_return_if_fail (E_IS_REFLOW_MODEL (e_reflow_model));
+
+ d(print_tabs());
+ d(g_print("Emitting items_inserted on model 0x%p, position=%d, count=%d.\n", e_reflow_model, position, count));
+ d(depth++);
+ g_signal_emit (e_reflow_model,
+ e_reflow_model_signals [MODEL_ITEMS_INSERTED], 0,
+ position, count);
+ d(depth--);
+}
+
+/**
+ * e_reflow_model_item_removed:
+ * @e_reflow_model: The model changed.
+ * @n: The position from which the items were removed.
+ *
+ * Use this function to notify any views of the reflow model that an
+ * item has been removed.
+ **/
+void
+e_reflow_model_item_removed (EReflowModel *e_reflow_model,
+ gint n)
+{
+ g_return_if_fail (e_reflow_model != NULL);
+ g_return_if_fail (E_IS_REFLOW_MODEL (e_reflow_model));
+
+ d(print_tabs());
+ d(g_print("Emitting item_removed on model 0x%p, n=%d.\n", e_reflow_model, n));
+ d(depth++);
+ g_signal_emit (e_reflow_model,
+ e_reflow_model_signals [MODEL_ITEM_REMOVED], 0,
+ n);
+ d(depth--);
+}
+
+
+/**
+ * e_reflow_model_item_changed:
+ * @e_reflow_model: the reflow model to notify of the change
+ * @item: the item that was changed in the model.
+ *
+ * Use this function to notify any views of the reflow model that the
+ * contents of item @item have changed in model such that the height
+ * has changed or the item needs to be reincarnated. This function
+ * will emit the "model_item_changed" signal on the @e_reflow_model
+ * object
+ */
+void
+e_reflow_model_item_changed (EReflowModel *e_reflow_model, gint n)
+{
+ g_return_if_fail (e_reflow_model != NULL);
+ g_return_if_fail (E_IS_REFLOW_MODEL (e_reflow_model));
+
+ d(print_tabs());
+ d(g_print("Emitting item_changed on model 0x%p, n=%d.\n", e_reflow_model, n));
+ d(depth++);
+ g_signal_emit (e_reflow_model,
+ e_reflow_model_signals [MODEL_ITEM_CHANGED], 0,
+ n);
+ d(depth--);
+}
diff --git a/widgets/text/e-reflow-model.h b/widgets/text/e-reflow-model.h
new file mode 100644
index 0000000000..ebbf3c1f75
--- /dev/null
+++ b/widgets/text/e-reflow-model.h
@@ -0,0 +1,108 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef _E_REFLOW_MODEL_H_
+#define _E_REFLOW_MODEL_H_
+
+#include <glib-object.h>
+#include <libgnomecanvas/gnome-canvas.h>
+
+G_BEGIN_DECLS
+
+#define E_REFLOW_MODEL_TYPE (e_reflow_model_get_type ())
+#define E_REFLOW_MODEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), E_REFLOW_MODEL_TYPE, EReflowModel))
+#define E_REFLOW_MODEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), E_REFLOW_MODEL_TYPE, EReflowModelClass))
+#define E_IS_REFLOW_MODEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_REFLOW_MODEL_TYPE))
+#define E_IS_REFLOW_MODEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), E_REFLOW_MODEL_TYPE))
+#define E_REFLOW_MODEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), E_REFLOW_MODEL_TYPE, EReflowModelClass))
+
+typedef struct {
+ GObject base;
+} EReflowModel;
+
+typedef struct {
+ GObjectClass parent_class;
+
+ /*
+ * Virtual methods
+ */
+ void (*set_width) (EReflowModel *etm, gint width);
+
+ gint (*count) (EReflowModel *etm);
+ gint (*height) (EReflowModel *etm, gint n, GnomeCanvasGroup *parent);
+ GnomeCanvasItem *(*incarnate) (EReflowModel *etm, gint n, GnomeCanvasGroup *parent);
+ gint (*compare) (EReflowModel *etm, gint n1, gint n2);
+ void (*reincarnate) (EReflowModel *etm, gint n, GnomeCanvasItem *item);
+
+ /*
+ * Signals
+ */
+
+ /*
+ * These all come after the change has been made.
+ * Major structural changes: model_changed
+ * Changes to the sorting of elements: comparison_changed
+ * Changes only in an item: item_changed
+ */
+ void (*model_changed) (EReflowModel *etm);
+ void (*comparison_changed) (EReflowModel *etm);
+ void (*model_items_inserted) (EReflowModel *etm, gint position, gint count);
+ void (*model_item_removed) (EReflowModel *etm, gint position);
+ void (*model_item_changed) (EReflowModel *etm, gint n);
+} EReflowModelClass;
+
+GType e_reflow_model_get_type (void);
+
+/**/
+void e_reflow_model_set_width (EReflowModel *e_reflow_model,
+ gint width);
+gint e_reflow_model_count (EReflowModel *e_reflow_model);
+gint e_reflow_model_height (EReflowModel *e_reflow_model,
+ gint n,
+ GnomeCanvasGroup *parent);
+GnomeCanvasItem *e_reflow_model_incarnate (EReflowModel *e_reflow_model,
+ gint n,
+ GnomeCanvasGroup *parent);
+gint e_reflow_model_compare (EReflowModel *e_reflow_model,
+ gint n1,
+ gint n2);
+void e_reflow_model_reincarnate (EReflowModel *e_reflow_model,
+ gint n,
+ GnomeCanvasItem *item);
+
+/*
+ * Routines for emitting signals on the e_reflow
+ */
+void e_reflow_model_changed (EReflowModel *e_reflow_model);
+void e_reflow_model_comparison_changed (EReflowModel *e_reflow_model);
+void e_reflow_model_items_inserted (EReflowModel *e_reflow_model,
+ gint position,
+ gint count);
+void e_reflow_model_item_removed (EReflowModel *e_reflow_model,
+ gint n);
+void e_reflow_model_item_changed (EReflowModel *e_reflow_model,
+ gint n);
+
+G_END_DECLS
+
+#endif /* _E_REFLOW_MODEL_H_ */
diff --git a/widgets/text/e-reflow.c b/widgets/text/e-reflow.c
new file mode 100644
index 0000000000..441102ac0f
--- /dev/null
+++ b/widgets/text/e-reflow.c
@@ -0,0 +1,1534 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+#include <config.h>
+
+#include <math.h>
+#include <string.h>
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "text/e-text.h"
+#include <glib/gi18n.h>
+#include "e-util/e-util.h"
+#include "e-util/e-unicode.h"
+
+#include "misc/e-canvas.h"
+#include "misc/e-canvas-utils.h"
+#include "e-reflow.h"
+#include "misc/e-selection-model-simple.h"
+
+static gboolean e_reflow_event (GnomeCanvasItem *item, GdkEvent *event);
+static void e_reflow_realize (GnomeCanvasItem *item);
+static void e_reflow_unrealize (GnomeCanvasItem *item);
+static void e_reflow_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ gint x, gint y, gint width, gint height);
+static void e_reflow_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gint flags);
+static double e_reflow_point (GnomeCanvasItem *item, double x, double y, gint cx, gint cy, GnomeCanvasItem **actual_item);
+static void e_reflow_reflow (GnomeCanvasItem *item, gint flags);
+static void set_empty(EReflow *reflow);
+
+static void e_reflow_resize_children (GnomeCanvasItem *item);
+
+#define E_REFLOW_DIVIDER_WIDTH 2
+#define E_REFLOW_BORDER_WIDTH 7
+#define E_REFLOW_FULL_GUTTER (E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH * 2)
+
+G_DEFINE_TYPE (EReflow, e_reflow, GNOME_TYPE_CANVAS_GROUP)
+
+/* The arguments we take */
+enum {
+ PROP_0,
+ PROP_MINIMUM_WIDTH,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ PROP_EMPTY_MESSAGE,
+ PROP_MODEL,
+ PROP_COLUMN_WIDTH
+};
+
+enum {
+ SELECTION_EVENT,
+ COLUMN_WIDTH_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = {0, };
+
+static gint
+er_compare (gint i1, gint i2, gpointer user_data)
+{
+ EReflow *reflow = user_data;
+ return e_reflow_model_compare (reflow->model, i1, i2);
+}
+
+static gint
+e_reflow_pick_line (EReflow *reflow, double x)
+{
+ x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH;
+ x /= reflow->column_width + E_REFLOW_FULL_GUTTER;
+ return x;
+}
+
+static gint
+er_find_item (EReflow *reflow, GnomeCanvasItem *item)
+{
+ gint i;
+ for (i = 0; i < reflow->count; i++) {
+ if (reflow->items[i] == item)
+ return i;
+ }
+ return -1;
+}
+
+static void
+e_reflow_resize_children (GnomeCanvasItem *item)
+{
+ EReflow *reflow;
+ gint i;
+ gint count;
+
+ reflow = E_REFLOW (item);
+
+ count = reflow->count;
+ for (i = 0; i < count; i++) {
+ if (reflow->items[i])
+ gnome_canvas_item_set(reflow->items[i],
+ "width", (double) reflow->column_width,
+ NULL);
+ }
+}
+
+static inline void
+e_reflow_update_selection_row (EReflow *reflow, gint row)
+{
+ if (reflow->items[row]) {
+ g_object_set(reflow->items[row],
+ "selected", e_selection_model_is_row_selected(E_SELECTION_MODEL(reflow->selection), row),
+ NULL);
+ } else if (e_selection_model_is_row_selected (E_SELECTION_MODEL (reflow->selection), row)) {
+ reflow->items[row] = e_reflow_model_incarnate (reflow->model, row, GNOME_CANVAS_GROUP (reflow));
+ g_object_set (reflow->items[row],
+ "selected", e_selection_model_is_row_selected(E_SELECTION_MODEL(reflow->selection), row),
+ "width", (double) reflow->column_width,
+ NULL);
+ }
+}
+
+static void
+e_reflow_update_selection (EReflow *reflow)
+{
+ gint i;
+ gint count;
+
+ count = reflow->count;
+ for (i = 0; i < count; i++) {
+ e_reflow_update_selection_row (reflow, i);
+ }
+}
+
+static void
+selection_changed (ESelectionModel *selection, EReflow *reflow)
+{
+ e_reflow_update_selection (reflow);
+}
+
+static void
+selection_row_changed (ESelectionModel *selection, gint row, EReflow *reflow)
+{
+ e_reflow_update_selection_row (reflow, row);
+}
+
+static gboolean
+do_adjustment (gpointer user_data)
+{
+ gint row;
+ GtkAdjustment *adj;
+ gfloat value, min_value, max_value;
+ EReflow *reflow = user_data;
+
+ row = reflow->cursor_row;
+ if (row == -1)
+ return FALSE;
+
+ adj = gtk_layout_get_hadjustment (GTK_LAYOUT (GNOME_CANVAS_ITEM (reflow)->canvas));
+ value = adj->value;
+
+ if ((!reflow->items) || (!reflow->items[row]))
+ return TRUE;
+ min_value = reflow->items[row]->x2 - adj->page_size;
+ max_value = reflow->items[row]->x1;
+
+ if (value < min_value)
+ value = min_value;
+
+ if (value > max_value)
+ value = max_value;
+
+ if (value != adj->value) {
+ adj->value = value;
+ gtk_adjustment_value_changed (adj);
+ }
+
+ reflow->do_adjustment_idle_id = 0;
+
+ return FALSE;
+}
+
+static void
+cursor_changed (ESelectionModel *selection, gint row, gint col, EReflow *reflow)
+{
+ gint count = reflow->count;
+ gint old_cursor = reflow->cursor_row;
+
+ if (old_cursor < count && old_cursor >= 0) {
+ if (reflow->items[old_cursor]) {
+ g_object_set (reflow->items[old_cursor],
+ "has_cursor", FALSE,
+ NULL);
+ }
+ }
+
+ reflow->cursor_row = row;
+
+ if (row < count && row >= 0) {
+ if (reflow->items[row]) {
+ g_object_set (reflow->items[row],
+ "has_cursor", TRUE,
+ NULL);
+ } else {
+ reflow->items[row] = e_reflow_model_incarnate (reflow->model, row, GNOME_CANVAS_GROUP (reflow));
+ g_object_set (reflow->items[row],
+ "has_cursor", TRUE,
+ "width", (double) reflow->column_width,
+ NULL);
+ }
+ }
+
+ if (reflow->do_adjustment_idle_id == 0)
+ reflow->do_adjustment_idle_id = g_idle_add (do_adjustment, reflow);
+
+}
+
+
+static void
+incarnate (EReflow *reflow)
+{
+ gint column_width;
+ gint first_column;
+ gint last_column;
+ gint first_cell;
+ gint last_cell;
+ gint i;
+ GtkAdjustment *adjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (GNOME_CANVAS_ITEM (reflow)->canvas));
+
+ column_width = reflow->column_width;
+
+ first_column = adjustment->value - 1 + E_REFLOW_BORDER_WIDTH;
+ first_column /= column_width + E_REFLOW_FULL_GUTTER;
+
+ last_column = adjustment->value + adjustment->page_size + 1 - E_REFLOW_BORDER_WIDTH - E_REFLOW_DIVIDER_WIDTH;
+ last_column /= column_width + E_REFLOW_FULL_GUTTER;
+ last_column ++;
+
+ if (first_column >= 0 && first_column < reflow->column_count)
+ first_cell = reflow->columns[first_column];
+ else
+ first_cell = 0;
+
+ if (last_column >= 0 && last_column < reflow->column_count)
+ last_cell = reflow->columns[last_column];
+ else
+ last_cell = reflow->count;
+
+ for (i = first_cell; i < last_cell; i++) {
+ gint unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i);
+ if (reflow->items[unsorted] == NULL) {
+ if (reflow->model) {
+ reflow->items[unsorted] = e_reflow_model_incarnate (reflow->model, unsorted, GNOME_CANVAS_GROUP (reflow));
+ g_object_set (reflow->items[unsorted],
+ "selected", e_selection_model_is_row_selected(E_SELECTION_MODEL(reflow->selection), unsorted),
+ "width", (double) reflow->column_width,
+ NULL);
+ }
+ }
+ }
+ reflow->incarnate_idle_id = 0;
+}
+
+static gboolean
+invoke_incarnate (gpointer user_data)
+{
+ EReflow *reflow = user_data;
+ incarnate (reflow);
+ return FALSE;
+}
+
+static void
+queue_incarnate (EReflow *reflow)
+{
+ if (reflow->incarnate_idle_id == 0)
+ reflow->incarnate_idle_id =
+ g_idle_add_full (25, invoke_incarnate, reflow, NULL);
+}
+
+static void
+reflow_columns (EReflow *reflow)
+{
+ GSList *list;
+ gint count;
+ gint start;
+ gint i;
+ gint column_count, column_start;
+ double running_height;
+
+ if (reflow->reflow_from_column <= 1) {
+ start = 0;
+ column_count = 1;
+ column_start = 0;
+ }
+ else {
+ /* we start one column before the earliest new entry,
+ so we can handle the case where the new entry is
+ inserted at the start of the column */
+ column_start = reflow->reflow_from_column - 1;
+ start = reflow->columns[column_start];
+ column_count = column_start + 1;
+ }
+
+ list = NULL;
+
+ running_height = E_REFLOW_BORDER_WIDTH;
+
+ count = reflow->count - start;
+ for (i = start; i < count; i++) {
+ gint unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i);
+ if (i != 0 && running_height + reflow->heights[unsorted] + E_REFLOW_BORDER_WIDTH > reflow->height) {
+ list = g_slist_prepend (list, GINT_TO_POINTER(i));
+ column_count ++;
+ running_height = E_REFLOW_BORDER_WIDTH * 2 + reflow->heights[unsorted];
+ } else
+ running_height += reflow->heights[unsorted] + E_REFLOW_BORDER_WIDTH;
+ }
+
+ reflow->column_count = column_count;
+ reflow->columns = g_renew (int, reflow->columns, column_count);
+ column_count --;
+
+ for (; column_count > column_start; column_count--) {
+ GSList *to_free;
+ reflow->columns[column_count] = GPOINTER_TO_INT(list->data);
+ to_free = list;
+ list = list->next;
+ g_slist_free_1 (to_free);
+ }
+ reflow->columns[column_start] = start;
+
+ queue_incarnate (reflow);
+
+ reflow->need_reflow_columns = FALSE;
+ reflow->reflow_from_column = -1;
+}
+
+static void
+item_changed (EReflowModel *model, gint i, EReflow *reflow)
+{
+ if (i < 0 || i >= reflow->count)
+ return;
+
+ reflow->heights[i] = e_reflow_model_height (reflow->model, i, GNOME_CANVAS_GROUP (reflow));
+ if (reflow->items[i] != NULL)
+ e_reflow_model_reincarnate (model, i, reflow->items[i]);
+ e_sorter_array_clean (reflow->sorter);
+ reflow->reflow_from_column = -1;
+ reflow->need_reflow_columns = TRUE;
+ e_canvas_item_request_reflow(GNOME_CANVAS_ITEM (reflow));
+}
+
+static void
+item_removed (EReflowModel *model, gint i, EReflow *reflow)
+{
+ gint c;
+ gint sorted;
+
+ if (i < 0 || i >= reflow->count)
+ return;
+
+ sorted = e_sorter_model_to_sorted (E_SORTER (reflow->sorter), i);
+ for (c = reflow->column_count - 1; c >= 0; c--) {
+ gint start_of_column = reflow->columns[c];
+
+ if (start_of_column <= sorted) {
+ if (reflow->reflow_from_column == -1
+ || reflow->reflow_from_column > c) {
+ reflow->reflow_from_column = c;
+ }
+ break;
+ }
+ }
+
+ if (reflow->items[i])
+ gtk_object_destroy (GTK_OBJECT (reflow->items[i]));
+
+ memmove (reflow->heights + i, reflow->heights + i + 1, (reflow->count - i - 1) * sizeof (gint));
+ memmove (reflow->items + i, reflow->items + i + 1, (reflow->count - i - 1) * sizeof (GnomeCanvasItem *));
+
+ reflow->count --;
+
+ reflow->heights [reflow->count] = 0;
+ reflow->items [reflow->count] = NULL;
+
+ reflow->need_reflow_columns = TRUE;
+ set_empty (reflow);
+ e_canvas_item_request_reflow(GNOME_CANVAS_ITEM (reflow));
+
+ e_sorter_array_set_count (reflow->sorter, reflow->count);
+
+ e_selection_model_simple_delete_rows (E_SELECTION_MODEL_SIMPLE (reflow->selection), i, 1);
+}
+
+static void
+items_inserted (EReflowModel *model, gint position, gint count, EReflow *reflow)
+{
+ gint i, oldcount;
+
+ if (position < 0 || position > reflow->count)
+ return;
+
+ oldcount = reflow->count;
+
+ reflow->count += count;
+
+ if (reflow->count > reflow->allocated_count) {
+ while (reflow->count > reflow->allocated_count)
+ reflow->allocated_count += 256;
+ reflow->heights = g_renew (int, reflow->heights, reflow->allocated_count);
+ reflow->items = g_renew (GnomeCanvasItem *, reflow->items, reflow->allocated_count);
+ }
+ memmove (reflow->heights + position + count, reflow->heights + position, (reflow->count - position - count) * sizeof (gint));
+ memmove (reflow->items + position + count, reflow->items + position, (reflow->count - position - count) * sizeof (GnomeCanvasItem *));
+ for (i = position; i < position + count; i++) {
+ reflow->items[i] = NULL;
+ reflow->heights[i] = e_reflow_model_height (reflow->model, i, GNOME_CANVAS_GROUP (reflow));
+ }
+
+ e_selection_model_simple_set_row_count (E_SELECTION_MODEL_SIMPLE (reflow->selection), reflow->count);
+ if (position == oldcount)
+ e_sorter_array_append (reflow->sorter, count);
+ else
+ e_sorter_array_set_count (reflow->sorter, reflow->count);
+
+ for (i = position; i < position + count; i ++) {
+ gint sorted = e_sorter_model_to_sorted (E_SORTER (reflow->sorter), i);
+ gint c;
+
+ for (c = reflow->column_count - 1; c >= 0; c--) {
+ gint start_of_column = reflow->columns[c];
+
+ if (start_of_column <= sorted) {
+ if (reflow->reflow_from_column == -1
+ || reflow->reflow_from_column > c) {
+ reflow->reflow_from_column = c;
+ }
+ break;
+ }
+ }
+ }
+
+ reflow->need_reflow_columns = TRUE;
+ set_empty (reflow);
+ e_canvas_item_request_reflow(GNOME_CANVAS_ITEM (reflow));
+}
+
+static void
+model_changed (EReflowModel *model, EReflow *reflow)
+{
+ gint i;
+ gint count;
+ gint oldcount;
+
+ count = reflow->count;
+ oldcount = count;
+
+ for (i = 0; i < count; i++) {
+ if (reflow->items[i])
+ gtk_object_destroy (GTK_OBJECT (reflow->items[i]));
+ }
+ g_free (reflow->items);
+ g_free (reflow->heights);
+ reflow->count = e_reflow_model_count (model);
+ reflow->allocated_count = reflow->count;
+ reflow->items = g_new (GnomeCanvasItem *, reflow->count);
+ reflow->heights = g_new (int, reflow->count);
+
+ count = reflow->count;
+ for (i = 0; i < count; i++) {
+ reflow->items[i] = NULL;
+ reflow->heights[i] = e_reflow_model_height (reflow->model, i, GNOME_CANVAS_GROUP (reflow));
+ }
+
+ e_selection_model_simple_set_row_count (E_SELECTION_MODEL_SIMPLE (reflow->selection), count);
+ e_sorter_array_set_count (reflow->sorter, reflow->count);
+
+ reflow->need_reflow_columns = TRUE;
+ if (oldcount > reflow->count)
+ reflow_columns (reflow);
+ set_empty (reflow);
+ e_canvas_item_request_reflow(GNOME_CANVAS_ITEM (reflow));
+}
+
+static void
+comparison_changed (EReflowModel *model, EReflow *reflow)
+{
+ e_sorter_array_clean (reflow->sorter);
+ reflow->reflow_from_column = -1;
+ reflow->need_reflow_columns = TRUE;
+ e_canvas_item_request_reflow(GNOME_CANVAS_ITEM (reflow));
+}
+
+static void
+set_empty(EReflow *reflow)
+{
+ if (reflow->count == 0) {
+ if (reflow->empty_text) {
+ if (reflow->empty_message) {
+ gnome_canvas_item_set(reflow->empty_text,
+ "width", reflow->minimum_width,
+ "text", reflow->empty_message,
+ NULL);
+ e_canvas_item_move_absolute(reflow->empty_text,
+ reflow->minimum_width / 2,
+ 0);
+ } else {
+ gtk_object_destroy(GTK_OBJECT(reflow->empty_text));
+ reflow->empty_text = NULL;
+ }
+ } else {
+ if (reflow->empty_message) {
+ reflow->empty_text =
+ gnome_canvas_item_new(GNOME_CANVAS_GROUP(reflow),
+ e_text_get_type(),
+ "anchor", GTK_ANCHOR_N,
+ "width", reflow->minimum_width,
+ "clip", TRUE,
+ "use_ellipsis", TRUE,
+ "justification", GTK_JUSTIFY_CENTER,
+ "text", reflow->empty_message,
+ "draw_background", FALSE,
+ NULL);
+ e_canvas_item_move_absolute(reflow->empty_text,
+ reflow->minimum_width / 2,
+ 0);
+ }
+ }
+ } else {
+ if (reflow->empty_text) {
+ gtk_object_destroy(GTK_OBJECT(reflow->empty_text));
+ reflow->empty_text = NULL;
+ }
+ }
+}
+
+static void
+disconnect_model (EReflow *reflow)
+{
+ if (reflow->model == NULL)
+ return;
+
+ g_signal_handler_disconnect (reflow->model,
+ reflow->model_changed_id);
+ g_signal_handler_disconnect (reflow->model,
+ reflow->comparison_changed_id);
+ g_signal_handler_disconnect (reflow->model,
+ reflow->model_items_inserted_id);
+ g_signal_handler_disconnect (reflow->model,
+ reflow->model_item_removed_id);
+ g_signal_handler_disconnect (reflow->model,
+ reflow->model_item_changed_id);
+ g_object_unref (reflow->model);
+
+ reflow->model_changed_id = 0;
+ reflow->comparison_changed_id = 0;
+ reflow->model_items_inserted_id = 0;
+ reflow->model_item_removed_id = 0;
+ reflow->model_item_changed_id = 0;
+ reflow->model = NULL;
+}
+
+static void
+disconnect_selection (EReflow *reflow)
+{
+ if (reflow->selection == NULL)
+ return;
+
+ g_signal_handler_disconnect (reflow->selection,
+ reflow->selection_changed_id);
+ g_signal_handler_disconnect (reflow->selection,
+ reflow->selection_row_changed_id);
+ g_signal_handler_disconnect (reflow->selection,
+ reflow->cursor_changed_id);
+ g_object_unref (reflow->selection);
+
+ reflow->selection_changed_id = 0;
+ reflow->selection_row_changed_id = 0;
+ reflow->cursor_changed_id = 0;
+ reflow->selection = NULL;
+}
+
+static void
+connect_model (EReflow *reflow, EReflowModel *model)
+{
+ if (reflow->model != NULL)
+ disconnect_model (reflow);
+
+ if (model == NULL)
+ return;
+
+ reflow->model = model;
+ g_object_ref (reflow->model);
+ reflow->model_changed_id =
+ g_signal_connect (reflow->model, "model_changed",
+ G_CALLBACK (model_changed), reflow);
+ reflow->comparison_changed_id =
+ g_signal_connect (reflow->model, "comparison_changed",
+ G_CALLBACK (comparison_changed), reflow);
+ reflow->model_items_inserted_id =
+ g_signal_connect (reflow->model, "model_items_inserted",
+ G_CALLBACK (items_inserted), reflow);
+ reflow->model_item_removed_id =
+ g_signal_connect (reflow->model, "model_item_removed",
+ G_CALLBACK (item_removed), reflow);
+ reflow->model_item_changed_id =
+ g_signal_connect (reflow->model, "model_item_changed",
+ G_CALLBACK (item_changed), reflow);
+ model_changed (model, reflow);
+}
+
+static void
+adjustment_changed (GtkAdjustment *adjustment, EReflow *reflow)
+{
+ queue_incarnate (reflow);
+}
+
+static void
+disconnect_adjustment (EReflow *reflow)
+{
+ if (reflow->adjustment == NULL)
+ return;
+
+ g_signal_handler_disconnect (reflow->adjustment,
+ reflow->adjustment_changed_id);
+ g_signal_handler_disconnect (reflow->adjustment,
+ reflow->adjustment_value_changed_id);
+
+ g_object_unref (reflow->adjustment);
+
+ reflow->adjustment_changed_id = 0;
+ reflow->adjustment_value_changed_id = 0;
+ reflow->adjustment = NULL;
+}
+
+static void
+connect_adjustment (EReflow *reflow, GtkAdjustment *adjustment)
+{
+ if (reflow->adjustment != NULL)
+ disconnect_adjustment (reflow);
+
+ if (adjustment == NULL)
+ return;
+
+ reflow->adjustment = adjustment;
+ reflow->adjustment_changed_id =
+ g_signal_connect (adjustment, "changed",
+ G_CALLBACK (adjustment_changed), reflow);
+ reflow->adjustment_value_changed_id =
+ g_signal_connect (adjustment, "value_changed",
+ G_CALLBACK (adjustment_changed), reflow);
+ g_object_ref (adjustment);
+}
+
+#if 0
+static void
+set_scroll_adjustments (GtkLayout *layout, GtkAdjustment *hadj, GtkAdjustment *vadj, EReflow *reflow)
+{
+ connect_adjustment (reflow, hadj);
+}
+
+static void
+connect_set_adjustment (EReflow *reflow)
+{
+ reflow->set_scroll_adjustments_id =
+ g_signal_connect (GNOME_CANVAS_ITEM (reflow)->canvas,
+ "set_scroll_adjustments",
+ G_CALLBACK (set_scroll_adjustments), reflow);
+}
+#endif
+
+static void
+disconnect_set_adjustment (EReflow *reflow)
+{
+ if (reflow->set_scroll_adjustments_id != 0) {
+ g_signal_handler_disconnect (GNOME_CANVAS_ITEM (reflow)->canvas,
+ reflow->set_scroll_adjustments_id);
+ reflow->set_scroll_adjustments_id = 0;
+ }
+}
+
+static void
+column_width_changed (EReflow *reflow)
+{
+ g_signal_emit (reflow, signals[COLUMN_WIDTH_CHANGED], 0, reflow->column_width);
+}
+
+
+
+
+/* Virtual functions */
+static void
+e_reflow_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ EReflow *reflow;
+
+ item = GNOME_CANVAS_ITEM (object);
+ reflow = E_REFLOW (object);
+
+ switch (prop_id){
+ case PROP_HEIGHT:
+ reflow->height = g_value_get_double (value);
+ reflow->need_reflow_columns = TRUE;
+ e_canvas_item_request_reflow(item);
+ break;
+ case PROP_MINIMUM_WIDTH:
+ reflow->minimum_width = g_value_get_double (value);
+ if (GNOME_CANVAS_ITEM_REALIZED & GTK_OBJECT_FLAGS(object))
+ set_empty(reflow);
+ e_canvas_item_request_reflow(item);
+ break;
+ case PROP_EMPTY_MESSAGE:
+ g_free(reflow->empty_message);
+ reflow->empty_message = g_strdup(g_value_get_string (value));
+ if (GNOME_CANVAS_ITEM_REALIZED & GTK_OBJECT_FLAGS(object))
+ set_empty(reflow);
+ break;
+ case PROP_MODEL:
+ connect_model (reflow, (EReflowModel *) g_value_get_object (value));
+ break;
+ case PROP_COLUMN_WIDTH:
+ if (reflow->column_width != g_value_get_double (value)) {
+ GtkAdjustment *adjustment = gtk_layout_get_hadjustment(GTK_LAYOUT(item->canvas));
+ double old_width = reflow->column_width;
+
+ reflow->column_width = g_value_get_double (value);
+ adjustment->step_increment = (reflow->column_width + E_REFLOW_FULL_GUTTER) / 2;
+ adjustment->page_increment = adjustment->page_size - adjustment->step_increment;
+ gtk_adjustment_changed(adjustment);
+ e_reflow_resize_children(item);
+ e_canvas_item_request_reflow(item);
+
+ reflow->need_column_resize = TRUE;
+ gnome_canvas_item_request_update(item);
+
+ if (old_width != reflow->column_width)
+ column_width_changed (reflow);
+ }
+ break;
+ }
+}
+
+static void
+e_reflow_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ EReflow *reflow;
+
+ reflow = E_REFLOW (object);
+
+ switch (prop_id) {
+ case PROP_MINIMUM_WIDTH:
+ g_value_set_double (value, reflow->minimum_width);
+ break;
+ case PROP_WIDTH:
+ g_value_set_double (value, reflow->width);
+ break;
+ case PROP_HEIGHT:
+ g_value_set_double (value, reflow->height);
+ break;
+ case PROP_EMPTY_MESSAGE:
+ g_value_set_string (value, reflow->empty_message);
+ break;
+ case PROP_MODEL:
+ g_value_set_object (value, reflow->model);
+ break;
+ case PROP_COLUMN_WIDTH:
+ g_value_set_double (value, reflow->column_width);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+e_reflow_dispose (GObject *object)
+{
+ EReflow *reflow = E_REFLOW(object);
+
+ g_free (reflow->items);
+ g_free (reflow->heights);
+ g_free (reflow->columns);
+
+ reflow->items = NULL;
+ reflow->heights = NULL;
+ reflow->columns = NULL;
+ reflow->count = 0;
+ reflow->allocated_count = 0;
+
+ if (reflow->incarnate_idle_id)
+ g_source_remove (reflow->incarnate_idle_id);
+ reflow->incarnate_idle_id = 0;
+
+ if (reflow->do_adjustment_idle_id)
+ g_source_remove (reflow->do_adjustment_idle_id);
+ reflow->do_adjustment_idle_id = 0;
+
+ disconnect_model (reflow);
+ disconnect_selection (reflow);
+
+ g_free(reflow->empty_message);
+ reflow->empty_message = NULL;
+
+ if (reflow->sorter) {
+ g_object_unref (reflow->sorter);
+ reflow->sorter = NULL;
+ }
+
+ G_OBJECT_CLASS(e_reflow_parent_class)->dispose (object);
+}
+
+static void
+e_reflow_realize (GnomeCanvasItem *item)
+{
+ EReflow *reflow;
+ GtkAdjustment *adjustment;
+ gint count;
+ gint i;
+
+ reflow = E_REFLOW (item);
+
+ if (GNOME_CANVAS_ITEM_CLASS(e_reflow_parent_class)->realize)
+ (* GNOME_CANVAS_ITEM_CLASS(e_reflow_parent_class)->realize) (item);
+
+ reflow->arrow_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
+ reflow->default_cursor = gdk_cursor_new (GDK_LEFT_PTR);
+
+ count = reflow->count;
+ for(i = 0; i < count; i++) {
+ if (reflow->items[i])
+ gnome_canvas_item_set(reflow->items[i],
+ "width", reflow->column_width,
+ NULL);
+ }
+
+ set_empty(reflow);
+
+ reflow->need_reflow_columns = TRUE;
+ e_canvas_item_request_reflow(item);
+
+ adjustment = gtk_layout_get_hadjustment(GTK_LAYOUT(item->canvas));
+
+#if 0
+ connect_set_adjustment (reflow);
+#endif
+ connect_adjustment (reflow, adjustment);
+
+ adjustment->step_increment = (reflow->column_width + E_REFLOW_FULL_GUTTER) / 2;
+ adjustment->page_increment = adjustment->page_size - adjustment->step_increment;
+ gtk_adjustment_changed(adjustment);
+
+ if (!item->canvas->aa) {
+ }
+}
+
+static void
+e_reflow_unrealize (GnomeCanvasItem *item)
+{
+ EReflow *reflow;
+
+ reflow = E_REFLOW (item);
+
+ if (!item->canvas->aa) {
+ }
+
+ gdk_cursor_unref (reflow->arrow_cursor);
+ gdk_cursor_unref (reflow->default_cursor);
+ reflow->arrow_cursor = NULL;
+ reflow->default_cursor = NULL;
+
+ g_free (reflow->columns);
+ reflow->columns = NULL;
+
+ disconnect_set_adjustment (reflow);
+ disconnect_adjustment (reflow);
+
+ if (GNOME_CANVAS_ITEM_CLASS(e_reflow_parent_class)->unrealize)
+ (* GNOME_CANVAS_ITEM_CLASS(e_reflow_parent_class)->unrealize) (item);
+}
+
+static gboolean
+e_reflow_event (GnomeCanvasItem *item, GdkEvent *event)
+{
+ EReflow *reflow;
+ gint return_val = FALSE;
+
+ reflow = E_REFLOW (item);
+
+ switch( event->type )
+ {
+ case GDK_KEY_PRESS:
+ return_val = e_selection_model_key_press(reflow->selection, (GdkEventKey *) event);
+ break;
+#if 0
+ if (event->key.keyval == GDK_Tab ||
+ event->key.keyval == GDK_KP_Tab ||
+ event->key.keyval == GDK_ISO_Left_Tab) {
+ gint i;
+ gint count;
+ count = reflow->count;
+ for (i = 0; i < count; i++) {
+ gint unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i);
+ GnomeCanvasItem *item = reflow->items[unsorted];
+ EFocus has_focus;
+ if (item) {
+ g_object_get(item,
+ "has_focus", &has_focus,
+ NULL);
+ if (has_focus) {
+ if (event->key.state & GDK_SHIFT_MASK) {
+ if (i == 0)
+ return FALSE;
+ i--;
+ } else {
+ if (i == count - 1)
+ return FALSE;
+ i++;
+ }
+
+ unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i);
+ if (reflow->items[unsorted] == NULL) {
+ reflow->items[unsorted] = e_reflow_model_incarnate (reflow->model, unsorted, GNOME_CANVAS_GROUP (reflow));
+ }
+
+ item = reflow->items[unsorted];
+ gnome_canvas_item_set(item,
+ "has_focus", (event->key.state & GDK_SHIFT_MASK) ? E_FOCUS_END : E_FOCUS_START,
+ NULL);
+ return TRUE;
+ }
+ }
+ }
+ }
+#endif
+ case GDK_BUTTON_PRESS:
+ switch(event->button.button)
+ {
+ case 1:
+ {
+ GdkEventButton *button = (GdkEventButton *) event;
+ double n_x;
+ n_x = button->x;
+ n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH;
+ n_x = fmod(n_x,(reflow->column_width + E_REFLOW_FULL_GUTTER));
+
+ if ( button->y >= E_REFLOW_BORDER_WIDTH && button->y <= reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER ) {
+ /* don't allow to drag the first line*/
+ if (e_reflow_pick_line(reflow, button->x) == 0)
+ return TRUE;
+ reflow->which_column_dragged = e_reflow_pick_line(reflow, button->x);
+ reflow->start_x = reflow->which_column_dragged * (reflow->column_width + E_REFLOW_FULL_GUTTER) - E_REFLOW_DIVIDER_WIDTH / 2;
+ reflow->temp_column_width = reflow->column_width;
+ reflow->column_drag = TRUE;
+
+ gnome_canvas_item_grab (item,
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK,
+ reflow->arrow_cursor,
+ button->time);
+
+ reflow->previous_temp_column_width = -1;
+ reflow->need_column_resize = TRUE;
+ gnome_canvas_item_request_update(item);
+ return TRUE;
+ }
+ }
+ break;
+ case 4:
+ {
+ GtkAdjustment *adjustment = gtk_layout_get_hadjustment(GTK_LAYOUT(item->canvas));
+ gdouble new_value = adjustment->value;
+ new_value -= adjustment->step_increment;
+ gtk_adjustment_set_value(adjustment, new_value);
+ }
+ break;
+ case 5:
+ {
+ GtkAdjustment *adjustment = gtk_layout_get_hadjustment(GTK_LAYOUT(item->canvas));
+ gdouble new_value = adjustment->value;
+ new_value += adjustment->step_increment;
+ if ( new_value > adjustment->upper - adjustment->page_size )
+ new_value = adjustment->upper - adjustment->page_size;
+ gtk_adjustment_set_value(adjustment, new_value);
+ }
+ break;
+ }
+ break;
+ case GDK_BUTTON_RELEASE:
+ if (reflow->column_drag) {
+ gdouble old_width = reflow->column_width;
+ GdkEventButton *button = (GdkEventButton *) event;
+ GtkAdjustment *adjustment = gtk_layout_get_hadjustment(GTK_LAYOUT(item->canvas));
+ reflow->temp_column_width = reflow->column_width +
+ (button->x - reflow->start_x)/(reflow->which_column_dragged - e_reflow_pick_line(reflow, adjustment->value));
+ if ( reflow->temp_column_width < 50 )
+ reflow->temp_column_width = 50;
+ reflow->column_drag = FALSE;
+ if ( old_width != reflow->temp_column_width ) {
+ gtk_adjustment_set_value(adjustment, adjustment->value + e_reflow_pick_line(reflow, adjustment->value) * (reflow->temp_column_width - reflow->column_width));
+ reflow->column_width = reflow->temp_column_width;
+ adjustment->step_increment = (reflow->column_width + E_REFLOW_FULL_GUTTER) / 2;
+ adjustment->page_increment = adjustment->page_size - adjustment->step_increment;
+ gtk_adjustment_changed(adjustment);
+ e_reflow_resize_children(item);
+ e_canvas_item_request_reflow(item);
+ gnome_canvas_request_redraw(item->canvas, 0, 0, reflow->width, reflow->height);
+ column_width_changed (reflow);
+ }
+ reflow->need_column_resize = TRUE;
+ gnome_canvas_item_request_update(item);
+ gnome_canvas_item_ungrab (item, button->time);
+ return TRUE;
+ }
+ break;
+ case GDK_MOTION_NOTIFY:
+ if (reflow->column_drag) {
+ double old_width = reflow->temp_column_width;
+ GdkEventMotion *motion = (GdkEventMotion *) event;
+ GtkAdjustment *adjustment = gtk_layout_get_hadjustment(GTK_LAYOUT(item->canvas));
+ reflow->temp_column_width = reflow->column_width +
+ (motion->x - reflow->start_x)/(reflow->which_column_dragged - e_reflow_pick_line(reflow, adjustment->value));
+ if (reflow->temp_column_width < 50)
+ reflow->temp_column_width = 50;
+ if (old_width != reflow->temp_column_width) {
+ reflow->need_column_resize = TRUE;
+ gnome_canvas_item_request_update(item);
+ }
+ return TRUE;
+ } else {
+ GdkEventMotion *motion = (GdkEventMotion *) event;
+ double n_x;
+
+ n_x = motion->x;
+ n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH;
+ n_x = fmod(n_x,(reflow->column_width + E_REFLOW_FULL_GUTTER));
+
+ if ( motion->y >= E_REFLOW_BORDER_WIDTH && motion->y <= reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER ) {
+ if ( reflow->default_cursor_shown ) {
+ gdk_window_set_cursor(GTK_WIDGET(item->canvas)->window, reflow->arrow_cursor);
+ reflow->default_cursor_shown = FALSE;
+ }
+ } else
+ if ( ! reflow->default_cursor_shown ) {
+ gdk_window_set_cursor(GTK_WIDGET(item->canvas)->window, reflow->default_cursor);
+ reflow->default_cursor_shown = TRUE;
+ }
+
+ }
+ break;
+ case GDK_ENTER_NOTIFY:
+ if (!reflow->column_drag) {
+ GdkEventCrossing *crossing = (GdkEventCrossing *) event;
+ double n_x;
+ n_x = crossing->x;
+ n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH;
+ n_x = fmod(n_x,(reflow->column_width + E_REFLOW_FULL_GUTTER));
+
+ if ( crossing->y >= E_REFLOW_BORDER_WIDTH && crossing->y <= reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER ) {
+ if ( reflow->default_cursor_shown ) {
+ gdk_window_set_cursor(GTK_WIDGET(item->canvas)->window, reflow->arrow_cursor);
+ reflow->default_cursor_shown = FALSE;
+ }
+ }
+ }
+ break;
+ case GDK_LEAVE_NOTIFY:
+ if (!reflow->column_drag) {
+ GdkEventCrossing *crossing = (GdkEventCrossing *) event;
+ double n_x;
+ n_x = crossing->x;
+ n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH;
+ n_x = fmod(n_x,(reflow->column_width + E_REFLOW_FULL_GUTTER));
+ if ( !( crossing->y >= E_REFLOW_BORDER_WIDTH && crossing->y <= reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER ) ) {
+ if ( ! reflow->default_cursor_shown ) {
+ gdk_window_set_cursor(GTK_WIDGET(item->canvas)->window, reflow->default_cursor);
+ reflow->default_cursor_shown = TRUE;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if (return_val)
+ return return_val;
+ else if (GNOME_CANVAS_ITEM_CLASS( e_reflow_parent_class )->event)
+ return (* GNOME_CANVAS_ITEM_CLASS( e_reflow_parent_class )->event) (item, event);
+ else
+ return FALSE;
+}
+
+static void e_reflow_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+ gint x, gint y, gint width, gint height)
+{
+ gint x_rect, y_rect, width_rect, height_rect;
+ gdouble running_width;
+ EReflow *reflow = E_REFLOW(item);
+ gint i;
+ double column_width;
+
+ if (GNOME_CANVAS_ITEM_CLASS(e_reflow_parent_class)->draw)
+ GNOME_CANVAS_ITEM_CLASS(e_reflow_parent_class)->draw (item, drawable, x, y, width, height);
+ column_width = reflow->column_width;
+ running_width = E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH;
+ x_rect = running_width;
+ y_rect = E_REFLOW_BORDER_WIDTH;
+ width_rect = E_REFLOW_DIVIDER_WIDTH;
+ height_rect = reflow->height - (E_REFLOW_BORDER_WIDTH * 2);
+
+ /* Compute first column to draw. */
+ i = x;
+ i /= column_width + E_REFLOW_FULL_GUTTER;
+ running_width += i * (column_width + E_REFLOW_FULL_GUTTER);
+
+ for (; i < reflow->column_count; i++) {
+ if ( running_width > x + width )
+ break;
+ x_rect = running_width;
+ gtk_paint_flat_box(GTK_WIDGET(item->canvas)->style,
+ drawable,
+ GTK_STATE_ACTIVE,
+ GTK_SHADOW_NONE,
+ NULL,
+ GTK_WIDGET(item->canvas),
+ "reflow",
+ x_rect - x,
+ y_rect - y,
+ width_rect,
+ height_rect);
+ running_width += E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH;
+ }
+ if (reflow->column_drag) {
+ gint start_line = e_reflow_pick_line(reflow,
+ gtk_layout_get_hadjustment(GTK_LAYOUT(item->canvas))->value);
+ i = x - start_line * (column_width + E_REFLOW_FULL_GUTTER);
+ running_width = start_line * (column_width + E_REFLOW_FULL_GUTTER);
+ column_width = reflow->temp_column_width;
+ running_width -= start_line * (column_width + E_REFLOW_FULL_GUTTER);
+ i += start_line * (column_width + E_REFLOW_FULL_GUTTER);
+ running_width += E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH;
+ x_rect = running_width;
+ y_rect = E_REFLOW_BORDER_WIDTH;
+ width_rect = E_REFLOW_DIVIDER_WIDTH;
+ height_rect = reflow->height - (E_REFLOW_BORDER_WIDTH * 2);
+
+ /* Compute first column to draw. */
+ i /= column_width + E_REFLOW_FULL_GUTTER;
+ running_width += i * (column_width + E_REFLOW_FULL_GUTTER);
+
+ for (; i < reflow->column_count; i++) {
+ if ( running_width > x + width )
+ break;
+ x_rect = running_width;
+ gdk_draw_rectangle(drawable,
+ GTK_WIDGET(item->canvas)->style->fg_gc[GTK_STATE_NORMAL],
+ TRUE,
+ x_rect - x,
+ y_rect - y,
+ width_rect - 1,
+ height_rect - 1);
+ running_width += E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH;
+ }
+ }
+}
+
+static void
+e_reflow_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gint flags)
+{
+ EReflow *reflow;
+ double x0, x1, y0, y1;
+
+ reflow = E_REFLOW (item);
+
+ if (GNOME_CANVAS_ITEM_CLASS(e_reflow_parent_class)->update)
+ GNOME_CANVAS_ITEM_CLASS(e_reflow_parent_class)->update (item, affine, clip_path, flags);
+
+ x0 = item->x1;
+ y0 = item->y1;
+ x1 = item->x2;
+ y1 = item->y2;
+ if ( x1 < x0 + reflow->width )
+ x1 = x0 + reflow->width;
+ if ( y1 < y0 + reflow->height )
+ y1 = y0 + reflow->height;
+ item->x2 = x1;
+ item->y2 = y1;
+
+ if (reflow->need_height_update) {
+ x0 = item->x1;
+ y0 = item->y1;
+ x1 = item->x2;
+ y1 = item->y2;
+ if ( x0 > 0 )
+ x0 = 0;
+ if ( y0 > 0 )
+ y0 = 0;
+ if ( x1 < E_REFLOW(item)->width )
+ x1 = E_REFLOW(item)->width;
+ if ( x1 < E_REFLOW(item)->height )
+ x1 = E_REFLOW(item)->height;
+
+ gnome_canvas_request_redraw(item->canvas, x0, y0, x1, y1);
+ reflow->need_height_update = FALSE;
+ } else if (reflow->need_column_resize) {
+ gint x_rect, y_rect, width_rect, height_rect;
+ gint start_line = e_reflow_pick_line(reflow,
+ gtk_layout_get_hadjustment(GTK_LAYOUT(item->canvas))->value);
+ gdouble running_width;
+ gint i;
+ double column_width;
+
+ if ( reflow->previous_temp_column_width != -1 ) {
+ running_width = start_line * (reflow->column_width + E_REFLOW_FULL_GUTTER);
+ column_width = reflow->previous_temp_column_width;
+ running_width -= start_line * (column_width + E_REFLOW_FULL_GUTTER);
+ running_width += E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH;
+ y_rect = E_REFLOW_BORDER_WIDTH;
+ width_rect = E_REFLOW_DIVIDER_WIDTH;
+ height_rect = reflow->height - (E_REFLOW_BORDER_WIDTH * 2);
+
+ for ( i = 0; i < reflow->column_count; i++) {
+ x_rect = running_width;
+ gnome_canvas_request_redraw(item->canvas, x_rect, y_rect, x_rect + width_rect, y_rect + height_rect);
+ running_width += E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH;
+ }
+ }
+
+ if ( reflow->temp_column_width != -1 ) {
+ running_width = start_line * (reflow->column_width + E_REFLOW_FULL_GUTTER);
+ column_width = reflow->temp_column_width;
+ running_width -= start_line * (column_width + E_REFLOW_FULL_GUTTER);
+ running_width += E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH;
+ y_rect = E_REFLOW_BORDER_WIDTH;
+ width_rect = E_REFLOW_DIVIDER_WIDTH;
+ height_rect = reflow->height - (E_REFLOW_BORDER_WIDTH * 2);
+
+ for ( i = 0; i < reflow->column_count; i++) {
+ x_rect = running_width;
+ gnome_canvas_request_redraw(item->canvas, x_rect, y_rect, x_rect + width_rect, y_rect + height_rect);
+ running_width += E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH;
+ }
+ }
+
+ reflow->previous_temp_column_width = reflow->temp_column_width;
+ reflow->need_column_resize = FALSE;
+ }
+}
+
+static double
+e_reflow_point (GnomeCanvasItem *item,
+ double x, double y, gint cx, gint cy,
+ GnomeCanvasItem **actual_item)
+{
+ double distance = 1;
+
+ *actual_item = NULL;
+
+ if (GNOME_CANVAS_ITEM_CLASS(e_reflow_parent_class)->point)
+ distance = GNOME_CANVAS_ITEM_CLASS(e_reflow_parent_class)->point (item, x, y, cx, cy, actual_item);
+ if ((gint) (distance * item->canvas->pixels_per_unit + 0.5) <= item->canvas->close_enough && *actual_item)
+ return distance;
+
+ *actual_item = item;
+ return 0;
+#if 0
+ if (y >= E_REFLOW_BORDER_WIDTH && y <= reflow->height - E_REFLOW_BORDER_WIDTH) {
+ gfloat n_x;
+ n_x = x;
+ n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH;
+ n_x = fmod(n_x, (reflow->column_width + E_REFLOW_FULL_GUTTER));
+ if (n_x < E_REFLOW_FULL_GUTTER) {
+ *actual_item = item;
+ return 0;
+ }
+ }
+ return distance;
+#endif
+}
+
+static void
+e_reflow_reflow( GnomeCanvasItem *item, gint flags )
+{
+ EReflow *reflow = E_REFLOW(item);
+ gdouble old_width;
+ gdouble running_width;
+ gdouble running_height;
+ gint next_column;
+ gint i;
+
+ if (! (GTK_OBJECT_FLAGS (reflow) & GNOME_CANVAS_ITEM_REALIZED))
+ return;
+
+ if (reflow->need_reflow_columns) {
+ reflow_columns (reflow);
+ }
+
+ old_width = reflow->width;
+
+ running_width = E_REFLOW_BORDER_WIDTH;
+ running_height = E_REFLOW_BORDER_WIDTH;
+
+ next_column = 1;
+
+ for (i = 0; i < reflow->count; i++) {
+ gint unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i);
+ if (next_column < reflow->column_count && i == reflow->columns[next_column]) {
+ running_height = E_REFLOW_BORDER_WIDTH;
+ running_width += reflow->column_width + E_REFLOW_FULL_GUTTER;
+ next_column ++;
+ }
+
+ if (unsorted >= 0 && reflow->items[unsorted]) {
+ e_canvas_item_move_absolute(GNOME_CANVAS_ITEM(reflow->items[unsorted]),
+ (double) running_width,
+ (double) running_height);
+ running_height += reflow->heights[unsorted] + E_REFLOW_BORDER_WIDTH;
+ }
+ }
+ reflow->width = running_width + reflow->column_width + E_REFLOW_BORDER_WIDTH;
+ if ( reflow->width < reflow->minimum_width )
+ reflow->width = reflow->minimum_width;
+ if (old_width != reflow->width)
+ e_canvas_item_request_parent_reflow(item);
+}
+
+static gint
+e_reflow_selection_event_real (EReflow *reflow, GnomeCanvasItem *item, GdkEvent *event)
+{
+ gint row;
+ gint return_val = TRUE;
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ switch (event->button.button) {
+ case 1: /* Fall through. */
+ case 2:
+ row = er_find_item (reflow, item);
+ if (event->button.button == 1) {
+ reflow->maybe_did_something =
+ e_selection_model_maybe_do_something(reflow->selection, row, 0, event->button.state);
+ reflow->maybe_in_drag = TRUE;
+ } else {
+ e_selection_model_do_something(reflow->selection, row, 0, event->button.state);
+ }
+ break;
+ case 3:
+ row = er_find_item (reflow, item);
+ e_selection_model_right_click_down(reflow->selection, row, 0, 0);
+ break;
+ default:
+ return_val = FALSE;
+ break;
+ }
+ break;
+ case GDK_BUTTON_RELEASE:
+ if (event->button.button == 1) {
+ if (reflow->maybe_in_drag) {
+ reflow->maybe_in_drag = FALSE;
+ if (!reflow->maybe_did_something) {
+ row = er_find_item (reflow, item);
+ e_selection_model_do_something(reflow->selection, row, 0, event->button.state);
+ }
+ }
+ }
+ break;
+ case GDK_KEY_PRESS:
+ return_val = e_selection_model_key_press(reflow->selection, (GdkEventKey *) event);
+ break;
+ default:
+ return_val = FALSE;
+ break;
+ }
+
+ return return_val;
+}
+
+static void
+e_reflow_class_init (EReflowClass *klass)
+{
+ GObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ object_class = (GObjectClass*) klass;
+ item_class = (GnomeCanvasItemClass *) klass;
+
+ object_class->set_property = e_reflow_set_property;
+ object_class->get_property = e_reflow_get_property;
+ object_class->dispose = e_reflow_dispose;
+
+ /* GnomeCanvasItem method overrides */
+ item_class->event = e_reflow_event;
+ item_class->realize = e_reflow_realize;
+ item_class->unrealize = e_reflow_unrealize;
+ item_class->draw = e_reflow_draw;
+ item_class->update = e_reflow_update;
+ item_class->point = e_reflow_point;
+
+ klass->selection_event = e_reflow_selection_event_real;
+ klass->column_width_changed = NULL;
+
+ g_object_class_install_property (object_class, PROP_MINIMUM_WIDTH,
+ g_param_spec_double ("minimum_width",
+ _( "Minimum width" ),
+ _( "Minimum Width" ),
+ 0.0, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_WIDTH,
+ g_param_spec_double ("width",
+ _( "Width" ),
+ _( "Width" ),
+ 0.0, G_MAXDOUBLE, 0.0,
+ G_PARAM_READABLE));
+
+
+ g_object_class_install_property (object_class, PROP_HEIGHT,
+ g_param_spec_double ("height",
+ _( "Height" ),
+ _( "Height" ),
+ 0.0, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_EMPTY_MESSAGE,
+ g_param_spec_string ("empty_message",
+ _( "Empty message" ),
+ _( "Empty message" ),
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_MODEL,
+ g_param_spec_object ("model",
+ _( "Reflow model" ),
+ _( "Reflow model" ),
+ E_REFLOW_MODEL_TYPE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_COLUMN_WIDTH,
+ g_param_spec_double ("column_width",
+ _( "Column width" ),
+ _( "Column width" ),
+ 0.0, G_MAXDOUBLE, 150.0,
+ G_PARAM_READWRITE));
+
+ signals [SELECTION_EVENT] =
+ g_signal_new ("selection_event",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EReflowClass, selection_event),
+ NULL, NULL,
+ e_marshal_INT__OBJECT_BOXED,
+ G_TYPE_INT, 2, G_TYPE_OBJECT,
+ GDK_TYPE_EVENT);
+
+ signals [COLUMN_WIDTH_CHANGED] =
+ g_signal_new ("column_width_changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EReflowClass, column_width_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__DOUBLE,
+ G_TYPE_NONE, 1, G_TYPE_DOUBLE);
+}
+
+static void
+e_reflow_init (EReflow *reflow)
+{
+ reflow->model = NULL;
+ reflow->items = NULL;
+ reflow->heights = NULL;
+ reflow->count = 0;
+
+ reflow->columns = NULL;
+ reflow->column_count = 0;
+
+ reflow->empty_text = NULL;
+ reflow->empty_message = NULL;
+
+ reflow->minimum_width = 10;
+ reflow->width = 10;
+ reflow->height = 10;
+
+ reflow->column_width = 150;
+
+ reflow->column_drag = FALSE;
+
+ reflow->need_height_update = FALSE;
+ reflow->need_column_resize = FALSE;
+ reflow->need_reflow_columns = FALSE;
+
+ reflow->maybe_did_something = FALSE;
+ reflow->maybe_in_drag = FALSE;
+
+ reflow->default_cursor_shown = TRUE;
+ reflow->arrow_cursor = NULL;
+ reflow->default_cursor = NULL;
+
+ reflow->cursor_row = -1;
+
+ reflow->incarnate_idle_id = 0;
+ reflow->do_adjustment_idle_id = 0;
+ reflow->set_scroll_adjustments_id = 0;
+
+ reflow->selection = E_SELECTION_MODEL (e_selection_model_simple_new());
+ reflow->sorter = e_sorter_array_new (er_compare, reflow);
+
+ g_object_set (reflow->selection,
+ "sorter", reflow->sorter,
+ NULL);
+
+ reflow->selection_changed_id =
+ g_signal_connect(reflow->selection, "selection_changed",
+ G_CALLBACK (selection_changed), reflow);
+ reflow->selection_row_changed_id =
+ g_signal_connect(reflow->selection, "selection_row_changed",
+ G_CALLBACK (selection_row_changed), reflow);
+ reflow->cursor_changed_id =
+ g_signal_connect(reflow->selection, "cursor_changed",
+ G_CALLBACK (cursor_changed), reflow);
+
+ e_canvas_item_set_reflow_callback(GNOME_CANVAS_ITEM(reflow), e_reflow_reflow);
+}
diff --git a/widgets/text/e-reflow.h b/widgets/text/e-reflow.h
new file mode 100644
index 0000000000..234dbc16a2
--- /dev/null
+++ b/widgets/text/e-reflow.h
@@ -0,0 +1,141 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Chris Lahey <clahey@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef __E_REFLOW_H__
+#define __E_REFLOW_H__
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <text/e-reflow-model.h>
+#include <misc/e-selection-model.h>
+#include <e-util/e-sorter-array.h>
+
+G_BEGIN_DECLS
+
+/* EReflow - A canvas item container.
+ *
+ * The following arguments are available:
+ *
+ * name type read/write description
+ * --------------------------------------------------------------------------------
+ * minimum_width double RW minimum width of the reflow. width >= minimum_width
+ * width double R width of the reflow
+ * height double RW height of the reflow
+ */
+
+#define E_REFLOW_TYPE (e_reflow_get_type ())
+#define E_REFLOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_REFLOW_TYPE, EReflow))
+#define E_REFLOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_REFLOW_TYPE, EReflowClass))
+#define E_IS_REFLOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_REFLOW_TYPE))
+#define E_IS_REFLOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), E_REFLOW_TYPE))
+
+
+typedef struct EReflowPriv EReflowPriv;
+
+typedef struct _EReflow EReflow;
+typedef struct _EReflowClass EReflowClass;
+
+struct _EReflow
+{
+ GnomeCanvasGroup parent;
+
+ /* item specific fields */
+ EReflowModel *model;
+ guint model_changed_id;
+ guint comparison_changed_id;
+ guint model_items_inserted_id;
+ guint model_item_removed_id;
+ guint model_item_changed_id;
+
+ ESelectionModel *selection;
+ guint selection_changed_id;
+ guint selection_row_changed_id;
+ guint cursor_changed_id;
+ ESorterArray *sorter;
+
+ GtkAdjustment *adjustment;
+ guint adjustment_changed_id;
+ guint adjustment_value_changed_id;
+ guint set_scroll_adjustments_id;
+
+ gint *heights;
+ GnomeCanvasItem **items;
+ gint count;
+ gint allocated_count;
+
+ gint *columns;
+ gint column_count; /* Number of columnns */
+
+ GnomeCanvasItem *empty_text;
+ gchar *empty_message;
+
+ double minimum_width;
+ double width;
+ double height;
+
+ double column_width;
+
+ gint incarnate_idle_id;
+ gint do_adjustment_idle_id;
+
+ /* These are all for when the column is being dragged. */
+ gdouble start_x;
+ gint which_column_dragged;
+ double temp_column_width;
+ double previous_temp_column_width;
+
+ gint cursor_row;
+
+ gint reflow_from_column;
+
+ guint column_drag : 1;
+
+ guint need_height_update : 1;
+ guint need_column_resize : 1;
+ guint need_reflow_columns : 1;
+
+ guint default_cursor_shown : 1;
+
+ guint maybe_did_something : 1;
+ guint maybe_in_drag : 1;
+ GdkCursor *arrow_cursor;
+ GdkCursor *default_cursor;
+};
+
+struct _EReflowClass
+{
+ GnomeCanvasGroupClass parent_class;
+
+ gint (*selection_event) (EReflow *reflow, GnomeCanvasItem *item, GdkEvent *event);
+ void (*column_width_changed) (EReflow *reflow, double width);
+};
+
+/*
+ * To be added to a reflow, an item must have the argument "width" as
+ * a Read/Write argument and "height" as a Read Only argument. It
+ * should also do an ECanvas parent reflow request if its size
+ * changes.
+ */
+GType e_reflow_get_type (void);
+
+G_END_DECLS
+
+#endif /* __E_REFLOW_H__ */
diff --git a/widgets/text/e-text.c b/widgets/text/e-text.c
index 0b2f84cf8e..7c542d8f5f 100644
--- a/widgets/text/e-text.c
+++ b/widgets/text/e-text.c
@@ -46,10 +46,10 @@
#include <gtk/gtk.h>
#include <libgnomecanvas/gnome-canvas-rect-ellipse.h>
-#include "a11y/e-text/gal-a11y-e-text.h"
+#include "gal-a11y-e-text.h"
#include "misc/e-canvas.h"
#include "misc/e-canvas-utils.h"
-#include "misc/e-unicode.h"
+#include "e-util/e-unicode.h"
#include <glib/gi18n.h>
#include "e-util/e-text-event-processor-emacs-like.h"
#include "e-util/e-util.h"
diff --git a/widgets/text/gal-a11y-e-text-factory.c b/widgets/text/gal-a11y-e-text-factory.c
new file mode 100644
index 0000000000..2df9241014
--- /dev/null
+++ b/widgets/text/gal-a11y-e-text-factory.c
@@ -0,0 +1,101 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Christopher James Lahey <clahey@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include <config.h>
+#include "text/e-text.h"
+#include "gal-a11y-e-text-factory.h"
+#include "gal-a11y-e-text.h"
+
+#define CS_CLASS(factory) (G_TYPE_INSTANCE_GET_CLASS ((factory), C_TYPE_STREAM, GalA11yETextFactoryClass))
+static AtkObjectFactoryClass *parent_class;
+#define PARENT_TYPE (ATK_TYPE_OBJECT_FACTORY)
+
+/* Static functions */
+
+static GType
+gal_a11y_e_text_factory_get_accessible_type (void)
+{
+ return GAL_A11Y_TYPE_E_TEXT;
+}
+
+static AtkObject*
+gal_a11y_e_text_factory_create_accessible (GObject *obj)
+{
+ AtkObject *atk_object;
+
+ g_return_val_if_fail (E_IS_TEXT (obj), NULL);
+
+ atk_object = g_object_new (GAL_A11Y_TYPE_E_TEXT, NULL);
+ atk_object_initialize (atk_object, obj);
+
+ return atk_object;
+}
+
+static void
+gal_a11y_e_text_factory_class_init (GalA11yETextFactoryClass *klass)
+{
+ AtkObjectFactoryClass *factory_class = ATK_OBJECT_FACTORY_CLASS (klass);
+
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ factory_class->create_accessible = gal_a11y_e_text_factory_create_accessible;
+ factory_class->get_accessible_type = gal_a11y_e_text_factory_get_accessible_type;
+}
+
+static void
+gal_a11y_e_text_factory_init (GalA11yETextFactory *factory)
+{
+}
+
+/**
+ * gal_a11y_e_text_factory_get_type:
+ * @void:
+ *
+ * Registers the &GalA11yETextFactory class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &GalA11yETextFactory class.
+ **/
+GType
+gal_a11y_e_text_factory_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ GTypeInfo info = {
+ sizeof (GalA11yETextFactoryClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gal_a11y_e_text_factory_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GalA11yETextFactory),
+ 0,
+ (GInstanceInitFunc) gal_a11y_e_text_factory_init,
+ NULL /* value_text */
+ };
+
+ type = g_type_register_static (PARENT_TYPE, "GalA11yETextFactory", &info, 0);
+ }
+
+ return type;
+}
diff --git a/widgets/text/gal-a11y-e-text-factory.h b/widgets/text/gal-a11y-e-text-factory.h
new file mode 100644
index 0000000000..df7638f64f
--- /dev/null
+++ b/widgets/text/gal-a11y-e-text-factory.h
@@ -0,0 +1,50 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Christopher James Lahey <clahey@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef __GAL_A11Y_E_TEXT_FACTORY_H__
+#define __GAL_A11Y_E_TEXT_FACTORY_H__
+
+#include <glib-object.h>
+#include <atk/atkobjectfactory.h>
+
+#define GAL_A11Y_TYPE_E_TEXT_FACTORY (gal_a11y_e_text_factory_get_type ())
+#define GAL_A11Y_E_TEXT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAL_A11Y_TYPE_E_TEXT_FACTORY, GalA11yETextFactory))
+#define GAL_A11Y_E_TEXT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAL_A11Y_TYPE_E_TEXT_FACTORY, GalA11yETextFactoryClass))
+#define GAL_A11Y_IS_E_TEXT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAL_A11Y_TYPE_E_TEXT_FACTORY))
+#define GAL_A11Y_IS_E_TEXT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAL_A11Y_TYPE_E_TEXT_FACTORY))
+
+typedef struct _GalA11yETextFactory GalA11yETextFactory;
+typedef struct _GalA11yETextFactoryClass GalA11yETextFactoryClass;
+
+struct _GalA11yETextFactory {
+ AtkObjectFactory object;
+};
+
+struct _GalA11yETextFactoryClass {
+ AtkObjectFactoryClass parent_class;
+};
+
+
+/* Standard Glib function */
+GType gal_a11y_e_text_factory_get_type (void);
+
+#endif /* ! __GAL_A11Y_E_TEXT_FACTORY_H__ */
diff --git a/widgets/text/gal-a11y-e-text.c b/widgets/text/gal-a11y-e-text.c
new file mode 100644
index 0000000000..99fb4e8a94
--- /dev/null
+++ b/widgets/text/gal-a11y-e-text.c
@@ -0,0 +1,1134 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Christopher James Lahey <clahey@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "a11y/gal-a11y-util.h"
+#include "text/e-text.h"
+#include "text/e-text-model-repos.h"
+
+#include "gal-a11y-e-text.h"
+#include "gal-a11y-e-text-factory.h"
+
+#define CS_CLASS(a11y) (G_TYPE_INSTANCE_GET_CLASS ((a11y), C_TYPE_STREAM, GalA11yETextClass))
+static GObjectClass *parent_class;
+static AtkComponentIface *component_parent_iface;
+static GType parent_type;
+static gint priv_offset;
+static GQuark quark_accessible_object = 0;
+#define GET_PRIVATE(object) ((GalA11yETextPrivate *) (((gchar *) object) + priv_offset))
+#define PARENT_TYPE (parent_type)
+
+struct _GalA11yETextPrivate {
+ gint dummy;
+};
+
+static void
+et_dispose (GObject *object)
+{
+ if (parent_class->dispose)
+ parent_class->dispose (object);
+}
+
+/* Static functions */
+
+static void
+et_get_extents (AtkComponent *component,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coord_type)
+{
+ EText *item = E_TEXT (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (component)));
+ double real_width;
+ double real_height;
+ gint fake_width;
+ gint fake_height;
+
+ if (component_parent_iface &&
+ component_parent_iface->get_extents)
+ component_parent_iface->get_extents (component,
+ x,
+ y,
+ &fake_width,
+ &fake_height,
+ coord_type);
+
+ g_object_get (item,
+ "text_width", &real_width,
+ "text_height", &real_height,
+ NULL);
+
+ if (width)
+ *width = real_width;
+ if (height)
+ *height = real_height;
+}
+
+static const gchar *
+et_get_full_text (AtkText *text)
+{
+ EText *etext = E_TEXT (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text)));
+ ETextModel *model;
+ const gchar *full_text;
+
+ g_object_get (etext, "model", &model, NULL);
+
+ full_text = e_text_model_get_text (model);
+
+ return full_text;
+}
+
+static void
+et_set_full_text (AtkEditableText *text,
+ const gchar *full_text)
+{
+ EText *etext = E_TEXT (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text)));
+ ETextModel *model;
+
+ g_object_get (etext, "model", &model, NULL);
+
+ e_text_model_set_text (model, full_text);
+}
+
+static gchar *
+et_get_text (AtkText *text,
+ gint start_offset,
+ gint end_offset)
+{
+ gint start, end, real_start, real_end, len;
+ const gchar *full_text = et_get_full_text (text);
+ if (full_text == NULL)
+ return NULL;
+ len = g_utf8_strlen (full_text, -1);
+
+ start = MIN (MAX (0, start_offset), len);
+ end = MIN (MAX (-1, end_offset), len);
+
+ if (end_offset == -1)
+ end = strlen (full_text);
+ else
+ end = g_utf8_offset_to_pointer (full_text, end) - full_text;
+
+ start = g_utf8_offset_to_pointer (full_text, start) - full_text;
+
+ real_start = MIN (start, end);
+ real_end = MAX (start, end);
+
+ return g_strndup (full_text + real_start, real_end - real_start);
+}
+
+static gboolean
+is_a_seperator (gunichar c)
+{
+ return g_unichar_ispunct(c) || g_unichar_isspace(c);
+}
+
+static gint
+find_word_start (const gchar *text,
+ gint begin_offset,
+ gint step)
+{
+ gint offset;
+ gchar *at_offset;
+ gunichar current, previous;
+ gint len;
+
+ offset = begin_offset;
+ len = g_utf8_strlen (text, -1);
+
+ while (offset > 0 && offset < len) {
+ at_offset = g_utf8_offset_to_pointer (text, offset);
+ current = g_utf8_get_char_validated (at_offset, -1);
+ at_offset = g_utf8_offset_to_pointer (text, offset-1);
+ previous = g_utf8_get_char_validated (at_offset, -1);
+ if ((! is_a_seperator (current)) && is_a_seperator (previous))
+ break;
+ offset += step;
+ }
+
+ return offset;
+}
+
+static gint
+find_word_end (const gchar *text,
+ gint begin_offset,
+ gint step)
+{
+ gint offset;
+ gchar *at_offset;
+ gunichar current, previous;
+ gint len;
+
+ offset = begin_offset;
+ len = g_utf8_strlen (text, -1);
+
+ while (offset > 0 && offset < len) {
+ at_offset = g_utf8_offset_to_pointer (text, offset);
+ current = g_utf8_get_char_validated (at_offset, -1);
+ at_offset = g_utf8_offset_to_pointer (text, offset-1);
+ previous = g_utf8_get_char_validated (at_offset, -1);
+ if (is_a_seperator (current) && (! is_a_seperator (previous)))
+ break;
+ offset += step;
+ }
+
+ return offset;
+}
+
+static gint
+find_sentence_start (const gchar *text,
+ gint begin_offset,
+ gint step)
+{
+ gint offset, last_word_end, len;
+ gchar *at_offset;
+ gunichar ch;
+ gint i;
+
+ offset = find_word_start (text, begin_offset, step);
+ len = g_utf8_strlen (text, -1);
+
+ while (offset>0 && offset <len) {
+ last_word_end = find_word_end (text, offset - 1, -1);
+ if (last_word_end == 0)
+ break;
+ for (i = last_word_end; i < offset; i++) {
+ at_offset = g_utf8_offset_to_pointer (text, i);
+ ch = g_utf8_get_char_validated (at_offset, -1);
+ if (ch == '.' || ch == '!' || ch == '?')
+ return offset;
+ }
+
+ offset = find_word_start (text, offset + step, step);
+ }
+
+ return offset;
+}
+
+static gint
+find_sentence_end (const gchar *text,
+ gint begin_offset,
+ gint step)
+{
+ gint offset;
+ gchar *at_offset;
+ gunichar previous;
+ gint len;
+
+ offset = begin_offset;
+ len = g_utf8_strlen (text, -1);
+
+ while (offset > 0 && offset < len) {
+ at_offset = g_utf8_offset_to_pointer (text, offset - 1);
+ previous = g_utf8_get_char_validated (at_offset, -1);
+ if (previous == '.' || previous == '!' || previous == '?')
+ break;
+ offset += step;
+ }
+
+ return offset;
+}
+
+static gint
+find_line_start (const gchar *text,
+ gint begin_offset,
+ gint step)
+{
+ gint offset;
+ gchar *at_offset;
+ gunichar previous;
+ gint len;
+
+ offset = begin_offset;
+ len = g_utf8_strlen (text, -1);
+
+ while (offset > 0 && offset < len) {
+ at_offset = g_utf8_offset_to_pointer (text, offset - 1);
+ previous = g_utf8_get_char_validated (at_offset, -1);
+ if (previous == '\n' || previous == '\r')
+ break;
+ offset += step;
+ }
+
+ return offset;
+}
+
+static gint
+find_line_end (const gchar *text,
+ gint begin_offset,
+ gint step)
+{
+ gint offset;
+ gchar *at_offset;
+ gunichar current;
+ gint len;
+
+ offset = begin_offset;
+ len = g_utf8_strlen (text, -1);
+
+ while (offset >= 0 && offset < len) {
+ at_offset = g_utf8_offset_to_pointer (text, offset);
+ current = g_utf8_get_char_validated (at_offset, -1);
+ if (current == '\n' || current == '\r')
+ break;
+ offset += step;
+ }
+
+ return offset;
+}
+
+static gchar *
+et_get_text_after_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset)
+{
+ gint start, end, len;
+ const gchar *full_text = et_get_full_text (text);
+ g_return_val_if_fail (full_text, NULL);
+
+ switch (boundary_type)
+ {
+ case ATK_TEXT_BOUNDARY_CHAR:
+ start = offset + 1;
+ end = offset + 2;
+ break;
+ case ATK_TEXT_BOUNDARY_WORD_START:
+ start = find_word_start (full_text, offset + 1, 1);
+ end = find_word_start (full_text, start + 1, 1);
+ break;
+ case ATK_TEXT_BOUNDARY_WORD_END:
+ start = find_word_end (full_text, offset + 1, 1);
+ end = find_word_end (full_text, start + 1, 1);
+ break;
+ case ATK_TEXT_BOUNDARY_SENTENCE_START:
+ start = find_sentence_start (full_text, offset + 1, 1);
+ end = find_sentence_start (full_text, start + 1, 1);
+ break;
+ case ATK_TEXT_BOUNDARY_SENTENCE_END:
+ start = find_sentence_end (full_text, offset + 1, 1);
+ end = find_sentence_end (full_text, start + 1, 1);
+ break;
+ case ATK_TEXT_BOUNDARY_LINE_START:
+ start = find_line_start (full_text, offset + 1, 1);
+ end = find_line_start (full_text, start + 1, 1);
+ break;
+ case ATK_TEXT_BOUNDARY_LINE_END:
+ start = find_line_end (full_text, offset + 1, 1);
+ end = find_line_end (full_text, start + 1, 1);
+ break;
+ default:
+ return NULL;
+ }
+
+ len = g_utf8_strlen (full_text, -1);
+ if (start_offset)
+ *start_offset = MIN (MAX (0, start), len);
+ if (end_offset)
+ *end_offset = MIN (MAX (0, end), len);
+ return et_get_text (text, start, end);
+}
+
+static gchar *
+et_get_text_at_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset)
+{
+ gint start, end, len;
+ const gchar *full_text = et_get_full_text (text);
+ g_return_val_if_fail (full_text, NULL);
+
+ switch (boundary_type)
+ {
+ case ATK_TEXT_BOUNDARY_CHAR:
+ start = offset;
+ end = offset + 1;
+ break;
+ case ATK_TEXT_BOUNDARY_WORD_START:
+ start = find_word_start (full_text, offset - 1, -1);
+ end = find_word_start (full_text, offset, 1);
+ break;
+ case ATK_TEXT_BOUNDARY_WORD_END:
+ start = find_word_end (full_text, offset, -1);
+ end = find_word_end (full_text, offset + 1, 1);
+ break;
+ case ATK_TEXT_BOUNDARY_SENTENCE_START:
+ start = find_sentence_start (full_text, offset - 1, -1);
+ end = find_sentence_start (full_text, offset, 1);
+ break;
+ case ATK_TEXT_BOUNDARY_SENTENCE_END:
+ start = find_sentence_end (full_text, offset, -1);
+ end = find_sentence_end (full_text, offset + 1, 1);
+ break;
+ case ATK_TEXT_BOUNDARY_LINE_START:
+ start = find_line_start (full_text, offset - 1, -1);
+ end = find_line_start (full_text, offset, 1);
+ break;
+ case ATK_TEXT_BOUNDARY_LINE_END:
+ start = find_line_end (full_text, offset, -1);
+ end = find_line_end (full_text, offset + 1, 1);
+ break;
+ default:
+ return NULL;
+ }
+
+ len = g_utf8_strlen (full_text, -1);
+ if (start_offset)
+ *start_offset = MIN (MAX (0, start), len);
+ if (end_offset)
+ *end_offset = MIN (MAX (0, end), len);
+ return et_get_text (text, start, end);
+}
+
+static gunichar
+et_get_character_at_offset (AtkText *text,
+ gint offset)
+{
+ const gchar *full_text = et_get_full_text (text);
+ gchar *at_offset;
+
+ at_offset = g_utf8_offset_to_pointer (full_text, offset);
+ return g_utf8_get_char_validated (at_offset, -1);
+}
+
+
+static gchar *
+et_get_text_before_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset)
+{
+ gint start, end, len;
+ const gchar *full_text = et_get_full_text (text);
+ g_return_val_if_fail (full_text, NULL);
+
+ switch (boundary_type)
+ {
+ case ATK_TEXT_BOUNDARY_CHAR:
+ start = offset - 1;
+ end = offset;
+ break;
+ case ATK_TEXT_BOUNDARY_WORD_START:
+ end = find_word_start (full_text, offset - 1, -1);
+ start = find_word_start (full_text, end - 1, -1);
+ break;
+ case ATK_TEXT_BOUNDARY_WORD_END:
+ end = find_word_end (full_text, offset, -1);
+ start = find_word_end (full_text, end - 1, -1);
+ break;
+ case ATK_TEXT_BOUNDARY_SENTENCE_START:
+ end = find_sentence_start (full_text, offset, -1);
+ start = find_sentence_start (full_text, end - 1, -1);
+ break;
+ case ATK_TEXT_BOUNDARY_SENTENCE_END:
+ end = find_sentence_end (full_text, offset, -1);
+ start = find_sentence_end (full_text, end - 1, -1);
+ break;
+ case ATK_TEXT_BOUNDARY_LINE_START:
+ end = find_line_start (full_text, offset, -1);
+ start = find_line_start (full_text, end - 1, -1);
+ break;
+ case ATK_TEXT_BOUNDARY_LINE_END:
+ end = find_line_end (full_text, offset, -1);
+ start = find_line_end (full_text, end - 1, -1);
+ break;
+ default:
+ return NULL;
+ }
+
+ len = g_utf8_strlen (full_text, -1);
+ if (start_offset)
+ *start_offset = MIN (MAX (0, start), len);
+ if (end_offset)
+ *end_offset = MIN (MAX (0, end), len);
+ return et_get_text (text, start, end);
+}
+
+static gint
+et_get_caret_offset (AtkText *text)
+{
+ GObject *obj;
+ EText *etext;
+ gint offset;
+
+ g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE(text), -1);
+ obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+ if (obj == NULL)
+ return -1;
+
+ g_return_val_if_fail (E_IS_TEXT (obj), -1);
+ etext = E_TEXT (obj);
+
+ g_object_get (etext, "cursor_pos", &offset, NULL);
+ return offset;
+}
+
+
+static AtkAttributeSet*
+et_get_run_attributes (AtkText *text,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset)
+{
+ /* Unimplemented */
+ return NULL;
+}
+
+
+static AtkAttributeSet*
+et_get_default_attributes (AtkText *text)
+{
+ /* Unimplemented */
+ return NULL;
+}
+
+
+static void
+et_get_character_extents (AtkText *text,
+ gint offset,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coords)
+{
+ GObject *obj;
+ EText *etext;
+ GnomeCanvas *canvas;
+ gint x_widget, y_widget, x_window, y_window;
+ GdkWindow *window;
+ GtkWidget *widget;
+ PangoRectangle pango_pos;
+
+ g_return_if_fail (ATK_IS_GOBJECT_ACCESSIBLE(text));
+ obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+ if (obj == NULL)
+ return;
+ g_return_if_fail (E_IS_TEXT (obj));
+ etext = E_TEXT(obj);
+ canvas = GNOME_CANVAS_ITEM(etext)->canvas;
+ widget = GTK_WIDGET(canvas);
+ window = widget->window;
+ gdk_window_get_origin (window, &x_widget, &y_widget);
+
+ pango_layout_index_to_pos (etext->layout, offset, &pango_pos);
+ pango_pos.x = PANGO_PIXELS (pango_pos.x);
+ pango_pos.y = PANGO_PIXELS (pango_pos.y);
+ pango_pos.width = (pango_pos.width + PANGO_SCALE / 2) / PANGO_SCALE;
+ pango_pos.height = (pango_pos.height + PANGO_SCALE / 2) / PANGO_SCALE;
+
+ *x = pango_pos.x + x_widget;
+ *y = pango_pos.y + y_widget;
+
+ *width = pango_pos.width;
+ *height = pango_pos.height;
+
+ if (etext->draw_borders) {
+ *x += 3; /*BORDER_INDENT;*/
+ *y += 3; /*BORDER_INDENT;*/
+ }
+
+ *x += etext->xofs;
+ *y += etext->yofs;
+
+ if (etext->editing) {
+ *x -= etext->xofs_edit;
+ *y -= etext->yofs_edit;
+ }
+
+ *x += etext->cx;
+ *y += etext->cy;
+
+ if (coords == ATK_XY_WINDOW) {
+ window = gdk_window_get_toplevel (window);
+ gdk_window_get_origin (window, &x_window, &y_window);
+ *x -= x_window;
+ *y -= y_window;
+ }
+ else if (coords == ATK_XY_SCREEN) {
+ }
+ else {
+ *x = 0;
+ *y = 0;
+ *height = 0;
+ *width = 0;
+ }
+}
+
+
+static gint
+et_get_character_count (AtkText *text)
+{
+ const gchar *full_text = et_get_full_text (text);
+
+ return g_utf8_strlen (full_text, -1);
+}
+
+
+static gint
+et_get_offset_at_point (AtkText *text,
+ gint x,
+ gint y,
+ AtkCoordType coords)
+{
+ GObject *obj;
+ EText *etext;
+ GnomeCanvas *canvas;
+ gint x_widget, y_widget, x_window, y_window;
+ GdkWindow *window;
+ GtkWidget *widget;
+ gint index;
+ gint trailing;
+
+ g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE(text), -1);
+ obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+ if (obj == NULL)
+ return -1;
+ g_return_val_if_fail (E_IS_TEXT (obj), -1);
+ etext = E_TEXT(obj);
+ canvas = GNOME_CANVAS_ITEM(etext)->canvas;
+ widget = GTK_WIDGET(canvas);
+ window = widget->window;
+ gdk_window_get_origin (window, &x_widget, &y_widget);
+
+ if (coords == ATK_XY_SCREEN) {
+ x = x - x_widget;
+ y = y - y_widget;
+ }
+ else if (coords == ATK_XY_WINDOW) {
+ window = gdk_window_get_toplevel (window);
+ gdk_window_get_origin (window, &x_window, &y_window);
+ x = x - x_widget + x_window;
+ y = y - y_widget + y_window;
+ }
+ else
+ return -1;
+
+ if (etext->draw_borders) {
+ x -= 3; /*BORDER_INDENT;*/
+ y -= 3; /*BORDER_INDENT;*/
+ }
+
+ x -= etext->xofs;
+ y -= etext->yofs;
+
+ if (etext->editing) {
+ x += etext->xofs_edit;
+ y += etext->yofs_edit;
+ }
+
+ x -= etext->cx;
+ y -= etext->cy;
+
+ pango_layout_xy_to_index (etext->layout,
+ x * PANGO_SCALE - PANGO_SCALE / 2,
+ y * PANGO_SCALE - PANGO_SCALE / 2,
+ &index,
+ &trailing);
+
+ return g_utf8_pointer_to_offset (etext->text, etext->text + index + trailing);
+}
+
+
+static gint
+et_get_n_selections (AtkText *text)
+{
+ EText *etext = E_TEXT (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text)));
+ if (etext->selection_start !=
+ etext->selection_end)
+ return 1;
+ return 0;
+}
+
+
+static gchar *
+et_get_selection (AtkText *text,
+ gint selection_num,
+ gint *start_offset,
+ gint *end_offset)
+{
+ gint start, end, real_start, real_end, len;
+ EText *etext;
+ if (selection_num == 0) {
+ const gchar *full_text = et_get_full_text (text);
+ if (full_text == NULL)
+ return NULL;
+ len = g_utf8_strlen (full_text, -1);
+ etext = E_TEXT (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text)));
+ start = MIN (etext->selection_start, etext->selection_end);
+ end = MAX (etext->selection_start, etext->selection_end);
+ start = MIN (MAX (0, start), len);
+ end = MIN (MAX (0, end), len);
+ if (start != end) {
+ if (start_offset)
+ *start_offset = start;
+ if (end_offset)
+ *end_offset = end;
+ real_start = g_utf8_offset_to_pointer (full_text, start) - full_text;
+ real_end = g_utf8_offset_to_pointer (full_text, end) - full_text;
+ return g_strndup (full_text + real_start, real_end - real_start);
+ }
+ }
+
+ return NULL;
+}
+
+
+static gboolean
+et_add_selection (AtkText *text,
+ gint start_offset,
+ gint end_offset)
+{
+ GObject *obj;
+ EText *etext;
+
+ g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text), FALSE);
+ obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+ if (obj == NULL)
+ return FALSE;
+ g_return_val_if_fail (E_IS_TEXT (obj), FALSE);
+ etext = E_TEXT (obj);
+
+ g_return_val_if_fail (start_offset >= 0, FALSE);
+ g_return_val_if_fail (start_offset >= -1, FALSE);
+ if (end_offset == -1)
+ end_offset = et_get_character_count (text);
+
+ if (start_offset != end_offset) {
+ gint real_start, real_end;
+ real_start = MIN (start_offset, end_offset);
+ real_end = MAX (start_offset, end_offset);
+ etext->selection_start = real_start;
+ etext->selection_end = real_end;
+
+ gnome_canvas_item_grab_focus (GNOME_CANVAS_ITEM (etext));
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (etext));
+
+ g_signal_emit_by_name (ATK_OBJECT (text), "text_selection_changed");
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+et_remove_selection (AtkText *text,
+ gint selection_num)
+{
+ GObject *obj;
+ EText *etext;
+
+ g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text), FALSE);
+ obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+ if (obj == NULL)
+ return FALSE;
+ g_return_val_if_fail (E_IS_TEXT (obj), FALSE);
+ etext = E_TEXT (obj);
+
+ if (selection_num == 0
+ && etext->selection_start != etext->selection_end) {
+ etext->selection_end = etext->selection_start;
+ g_signal_emit_by_name (ATK_OBJECT(text), "text_selection_changed");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+et_set_selection (AtkText *text,
+ gint selection_num,
+ gint start_offset,
+ gint end_offset)
+{
+ GObject *obj;
+
+ g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text), FALSE);
+ obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+ if (obj == NULL)
+ return FALSE;
+ g_return_val_if_fail (E_IS_TEXT (obj), FALSE);
+ if (selection_num == 0)
+ return et_add_selection (text, start_offset, end_offset);
+ return FALSE;
+}
+
+
+static gboolean
+et_set_caret_offset (AtkText *text,
+ gint offset)
+{
+ GObject *obj;
+ EText *etext;
+
+ g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text), FALSE);
+ obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+ if (obj == NULL)
+ return FALSE;
+
+ g_return_val_if_fail (E_IS_TEXT (obj), FALSE);
+ etext = E_TEXT (obj);
+
+ if (offset < -1)
+ return FALSE;
+ else {
+ ETextEventProcessorCommand command;
+
+ if (offset == -1)
+ offset = et_get_character_count (text);
+
+ command.action = E_TEP_MOVE;
+ command.position = E_TEP_VALUE;
+ command.value = offset;
+ command.time = GDK_CURRENT_TIME;
+ g_signal_emit_by_name (etext->tep, "command", &command);
+ return TRUE;
+ }
+}
+
+static gboolean
+et_set_run_attributes (AtkEditableText *text,
+ AtkAttributeSet *attrib_set,
+ gint start_offset,
+ gint end_offset)
+{
+ /* Unimplemented */
+ return FALSE;
+}
+
+static void
+et_set_text_contents (AtkEditableText *text,
+ const gchar *string)
+{
+ et_set_full_text (text, string);
+}
+
+static void
+et_insert_text (AtkEditableText *text,
+ const gchar *string,
+ gint length,
+ gint *position)
+{
+ /* Utf8 unimplemented */
+ gchar *result;
+
+ const gchar *full_text = et_get_full_text (ATK_TEXT (text));
+ if (full_text == NULL)
+ return;
+
+ result = g_strdup_printf ("%.*s%.*s%s", *position, full_text, length, string, full_text + *position);
+
+ et_set_full_text (text, result);
+
+ *position += length;
+
+ g_free (result);
+}
+
+static void
+et_copy_text (AtkEditableText *text,
+ gint start_pos,
+ gint end_pos)
+{
+ GObject *obj;
+ EText *etext;
+
+ g_return_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text));
+ obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+ if (obj == NULL)
+ return;
+
+ g_return_if_fail (E_IS_TEXT (obj));
+ etext = E_TEXT (obj);
+
+ if (start_pos != end_pos) {
+ etext->selection_start = start_pos;
+ etext->selection_end = end_pos;
+ e_text_copy_clipboard (etext);
+ }
+}
+
+static void
+et_delete_text (AtkEditableText *text,
+ gint start_pos,
+ gint end_pos)
+{
+ GObject *obj;
+ EText *etext;
+
+ g_return_if_fail (ATK_IS_GOBJECT_ACCESSIBLE(text));
+ obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+ if (obj == NULL)
+ return;
+
+ g_return_if_fail (E_IS_TEXT (obj));
+ etext = E_TEXT (obj);
+
+ etext->selection_start = start_pos;
+ etext->selection_end = end_pos;
+
+ e_text_delete_selection (etext);
+}
+
+static void
+et_cut_text (AtkEditableText *text,
+ gint start_pos,
+ gint end_pos)
+{
+ et_copy_text (text, start_pos, end_pos);
+ et_delete_text (text, start_pos, end_pos);
+}
+
+static void
+et_paste_text (AtkEditableText *text,
+ gint position)
+{
+ GObject *obj;
+ EText *etext;
+
+ g_return_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text));
+ obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+ if (obj == NULL)
+ return;
+
+ g_return_if_fail (E_IS_TEXT (obj));
+ etext = E_TEXT (obj);
+
+ g_object_set (etext, "cursor_pos", position, NULL);
+ e_text_paste_clipboard (etext);
+}
+
+static void
+et_atk_component_iface_init (AtkComponentIface *iface)
+{
+ iface->get_extents = et_get_extents;
+}
+
+static void
+et_atk_text_iface_init (AtkTextIface *iface)
+{
+ iface->get_text = et_get_text;
+ iface->get_text_after_offset = et_get_text_after_offset;
+ iface->get_text_at_offset = et_get_text_at_offset;
+ iface->get_character_at_offset = et_get_character_at_offset;
+ iface->get_text_before_offset = et_get_text_before_offset;
+ iface->get_caret_offset = et_get_caret_offset;
+ iface->get_run_attributes = et_get_run_attributes;
+ iface->get_default_attributes = et_get_default_attributes;
+ iface->get_character_extents = et_get_character_extents;
+ iface->get_character_count = et_get_character_count;
+ iface->get_offset_at_point = et_get_offset_at_point;
+ iface->get_n_selections = et_get_n_selections;
+ iface->get_selection = et_get_selection;
+ iface->add_selection = et_add_selection;
+ iface->remove_selection = et_remove_selection;
+ iface->set_selection = et_set_selection;
+ iface->set_caret_offset = et_set_caret_offset;
+}
+
+static void
+et_atk_editable_text_iface_init (AtkEditableTextIface *iface)
+{
+ iface->set_run_attributes = et_set_run_attributes;
+ iface->set_text_contents = et_set_text_contents;
+ iface->insert_text = et_insert_text;
+ iface->copy_text = et_copy_text;
+ iface->cut_text = et_cut_text;
+ iface->delete_text = et_delete_text;
+ iface->paste_text = et_paste_text;
+}
+
+static void
+_et_reposition_cb (ETextModel *model,
+ ETextModelReposFn fn,
+ gpointer repos_data,
+ gpointer user_data)
+{
+ AtkObject *accessible;
+ AtkText *text;
+
+ accessible = ATK_OBJECT (user_data);
+ text = ATK_TEXT (accessible);
+
+ if (fn == e_repos_delete_shift) {
+ EReposDeleteShift *info = (EReposDeleteShift *) repos_data;
+ g_signal_emit_by_name (text, "text-changed::delete", info->pos, info->len);
+ }
+ else if (fn == e_repos_insert_shift) {
+ EReposInsertShift *info = (EReposInsertShift *) repos_data;
+ g_signal_emit_by_name (text, "text-changed::insert", info->pos, info->len);
+ }
+}
+
+static void
+_et_command_cb (ETextEventProcessor *tep,
+ ETextEventProcessorCommand *command,
+ gpointer user_data)
+{
+ AtkObject *accessible;
+ AtkText *text;
+
+ accessible = ATK_OBJECT (user_data);
+ text = ATK_TEXT (accessible);
+
+ switch (command->action) {
+ case E_TEP_MOVE:
+ g_signal_emit_by_name (text, "text-caret-moved", et_get_caret_offset (text));
+ break;
+ case E_TEP_SELECT:
+ g_signal_emit_by_name (text, "text-selection-changed");
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+et_real_initialize (AtkObject *obj,
+ gpointer data)
+{
+ EText *etext;
+
+ ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
+
+ g_return_if_fail (GAL_A11Y_IS_E_TEXT (obj));
+ g_return_if_fail (E_IS_TEXT (data));
+
+ etext = E_TEXT (data);
+
+ /* Set up signal callbacks */
+ g_signal_connect (etext->model, "reposition",
+ G_CALLBACK (_et_reposition_cb), obj);
+
+ if (etext->tep)
+ g_signal_connect_after (etext->tep, "command",
+ (GCallback) _et_command_cb, obj);
+
+ obj->role = ATK_ROLE_TEXT;
+}
+
+static void
+et_class_init (GalA11yETextClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
+
+ quark_accessible_object = g_quark_from_static_string ("gtk-accessible-object");
+ parent_class = g_type_class_ref (PARENT_TYPE);
+ component_parent_iface = g_type_interface_peek(parent_class, ATK_TYPE_COMPONENT);
+ object_class->dispose = et_dispose;
+ atk_class->initialize = et_real_initialize;
+}
+
+static void
+et_init (GalA11yEText *a11y)
+{
+#if 0
+ GalA11yETextPrivate *priv;
+
+ priv = GET_PRIVATE (a11y);
+#endif
+}
+
+/**
+ * gal_a11y_e_text_get_type:
+ * @void:
+ *
+ * Registers the &GalA11yEText class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &GalA11yEText class.
+ **/
+GType
+gal_a11y_e_text_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ AtkObjectFactory *factory;
+
+ GTypeInfo info = {
+ sizeof (GalA11yETextClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) et_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GalA11yEText),
+ 0,
+ (GInstanceInitFunc) et_init,
+ NULL /* value_text */
+ };
+
+ static const GInterfaceInfo atk_component_info = {
+ (GInterfaceInitFunc) et_atk_component_iface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+ static const GInterfaceInfo atk_text_info = {
+ (GInterfaceInitFunc) et_atk_text_iface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+ static const GInterfaceInfo atk_editable_text_info = {
+ (GInterfaceInitFunc) et_atk_editable_text_iface_init,
+ (GInterfaceFinalizeFunc) NULL,
+ NULL
+ };
+
+ factory = atk_registry_get_factory (atk_get_default_registry (), GNOME_TYPE_CANVAS_ITEM);
+ parent_type = atk_object_factory_get_accessible_type (factory);
+
+ type = gal_a11y_type_register_static_with_private (PARENT_TYPE, "GalA11yEText", &info, 0,
+ sizeof (GalA11yETextPrivate), &priv_offset);
+
+ g_type_add_interface_static (type, ATK_TYPE_COMPONENT, &atk_component_info);
+ g_type_add_interface_static (type, ATK_TYPE_TEXT, &atk_text_info);
+ g_type_add_interface_static (type, ATK_TYPE_EDITABLE_TEXT, &atk_editable_text_info);
+ }
+
+ return type;
+}
+
+void
+gal_a11y_e_text_init (void)
+{
+ if (atk_get_root ())
+ atk_registry_set_factory_type (atk_get_default_registry (),
+ E_TYPE_TEXT,
+ gal_a11y_e_text_factory_get_type ());
+
+}
+
diff --git a/widgets/text/gal-a11y-e-text.h b/widgets/text/gal-a11y-e-text.h
new file mode 100644
index 0000000000..6a5bb80de0
--- /dev/null
+++ b/widgets/text/gal-a11y-e-text.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Christopher James Lahey <clahey@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef __GAL_A11Y_E_TEXT_H__
+#define __GAL_A11Y_E_TEXT_H__
+
+#include <glib-object.h>
+#include <table/e-table-item.h>
+
+#define GAL_A11Y_TYPE_E_TEXT (gal_a11y_e_text_get_type ())
+#define GAL_A11Y_E_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAL_A11Y_TYPE_E_TEXT, GalA11yEText))
+#define GAL_A11Y_E_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAL_A11Y_TYPE_E_TEXT, GalA11yETextClass))
+#define GAL_A11Y_IS_E_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAL_A11Y_TYPE_E_TEXT))
+#define GAL_A11Y_IS_E_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAL_A11Y_TYPE_E_TEXT))
+
+typedef struct _GalA11yEText GalA11yEText;
+typedef struct _GalA11yETextClass GalA11yETextClass;
+typedef struct _GalA11yETextPrivate GalA11yETextPrivate;
+
+/* This struct should actually be larger as this isn't what we derive from.
+ * The GalA11yETextPrivate comes right after the parent class structure.
+ **/
+struct _GalA11yEText {
+ AtkObject object;
+};
+
+struct _GalA11yETextClass {
+ AtkObject parent_class;
+};
+
+
+/* Standard Glib function */
+GType gal_a11y_e_text_get_type (void);
+
+void gal_a11y_e_text_init (void);
+
+#endif /* ! __GAL_A11Y_E_TEXT_H__ */