From 5d4895eb431adfbf2c7895d4257fa20a28f474f9 Mon Sep 17 00:00:00 2001 From: Christopher James Lahey Date: Tue, 8 May 2001 04:56:03 +0000 Subject: Added util/e-sorter-array.lo and widgets/e-reflow-model.lo. Removed 2001-05-07 Christopher James Lahey * gal/Makefile.am (libgal_la_LIBADD): Added util/e-sorter-array.lo and widgets/e-reflow-model.lo. Removed widgets/e-reflow-sorted.lo. * gal/util/Makefile.am (libutil_la_SOURCES): Added e-sorter-array.c. (libgalinclude_HEADERS): Added e-sorter-array.h. * gal/util/e-sorter-array.c, gal/util/e-sorter-array.h: A sorter for use with a single compare function to do sorting. * gal/util/e-util.c, gal/util/e-util.h (e_marshal_INT__OBJECT_POINTER): Added this marshaller. * gal/widgets/Makefile.am (libwidgets_la_SOURCES): Added e-reflow-model.c. Removed e-reflow-sorted.c. (libwidgetsinclude_HEADERS): Added e-reflow-sorted.h. Removed e-reflow-model.h. * gal/widgets/e-reflow-model.c, gal/widgets/e-reflow-model.h: Model for EReflow. Has a number of items and generates canvas items on the fly. * gal/widgets/e-reflow.c, gal/widgets/e-reflow.h: Major rewrite. This now uses a model to generate its canvas items instead of having canvas items added to it. It doesn't create the canvas items now until they will be shown on screen. svn path=/trunk/; revision=9710 --- e-util/e-sorter-array.c | 278 ++++++++++ e-util/e-sorter-array.h | 56 ++ e-util/e-util.c | 21 + e-util/e-util.h | 21 +- widgets/misc/e-reflow-model.c | 288 ++++++++++ widgets/misc/e-reflow-model.h | 83 +++ widgets/misc/e-reflow.c | 1190 +++++++++++++++++++++++++++-------------- widgets/misc/e-reflow.h | 48 +- 8 files changed, 1564 insertions(+), 421 deletions(-) create mode 100644 e-util/e-sorter-array.c create mode 100644 e-util/e-sorter-array.h create mode 100644 widgets/misc/e-reflow-model.c create mode 100644 widgets/misc/e-reflow-model.h diff --git a/e-util/e-sorter-array.c b/e-util/e-sorter-array.c new file mode 100644 index 0000000000..44cf7b0f67 --- /dev/null +++ b/e-util/e-sorter-array.c @@ -0,0 +1,278 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * e-sorter-array.c: + * + * Author: + * Christopher James Lahey + * + * (C) 2001 Ximian, Inc. + */ +#include +#include +#include +#include +#include "gal/util/e-util.h" +#include "e-sorter-array.h" + +#define d(x) + +/* The arguments we take */ +enum { + ARG_0, +}; + +#define PARENT_TYPE e_sorter_get_type() + +#define INCREMENT_AMOUNT 100 + +static ESorterClass *parent_class; + +static void esa_sort (ESorterArray *esa); +static void esa_backsort (ESorterArray *esa); + +static gint esa_model_to_sorted (ESorter *sorter, int row); +static gint esa_sorted_to_model (ESorter *sorter, int row); +static void esa_get_model_to_sorted_array (ESorter *sorter, int **array, int *count); +static void esa_get_sorted_to_model_array (ESorter *sorter, int **array, int *count); +static gboolean esa_needs_sorting (ESorter *esa); + +#define ESA_NEEDS_SORTING(esa) (((ESorterArray *) (esa))->compare != NULL) + +static int +esort_callback(const void *data1, const void *data2, gpointer user_data) +{ + ESorterArray *esa = user_data; + int ret_val; + int int1, int2; + + int1 = *(int *)data1; + int2 = *(int *)data2; + + ret_val = esa->compare (int1, int2, esa->closure); + if (ret_val != 0) + return ret_val; + + if (int1 < int2) + return -1; + if (int1 > int2) + return 1; + return 0; +} + +static void +esa_sort(ESorterArray *esa) +{ + int rows; + int i; + + if (esa->sorted) + return; + + rows = esa->rows; + + esa->sorted = g_new(int, rows); + for (i = 0; i < rows; i++) + esa->sorted[i] = i; + + if (esa->compare) + e_sort (esa->sorted, rows, sizeof(int), esort_callback, esa); +} + +static void +esa_backsort(ESorterArray *esa) +{ + int i, rows; + + if (esa->backsorted) + return; + + esa_sort(esa); + + rows = esa->rows; + + esa->backsorted = g_new0(int, rows); + + for (i = 0; i < rows; i++) { + esa->backsorted[esa->sorted[i]] = i; + } +} + + +static gint +esa_model_to_sorted (ESorter *es, int row) +{ + ESorterArray *esa = E_SORTER_ARRAY(es); + + g_return_val_if_fail(row >= 0, -1); + g_return_val_if_fail(row < esa->rows, -1); + + if (ESA_NEEDS_SORTING(es)) + esa_backsort(esa); + + if (esa->backsorted) + return esa->backsorted[row]; + else + return row; +} + +static gint +esa_sorted_to_model (ESorter *es, int row) +{ + ESorterArray *esa = (ESorterArray *) es; + + g_return_val_if_fail(row >= 0, -1); + g_return_val_if_fail(row < esa->rows, -1); + + if (ESA_NEEDS_SORTING(es)) + esa_sort(esa); + + if (esa->sorted) + return esa->sorted[row]; + else + return row; +} + +static void +esa_get_model_to_sorted_array (ESorter *es, int **array, int *count) +{ + ESorterArray *esa = E_SORTER_ARRAY(es); + if (array || count) { + esa_backsort(esa); + + if (array) + *array = esa->backsorted; + if (count) + *count = esa->rows; + } +} + +static void +esa_get_sorted_to_model_array (ESorter *es, int **array, int *count) +{ + ESorterArray *esa = E_SORTER_ARRAY(es); + if (array || count) { + esa_sort(esa); + + if (array) + *array = esa->sorted; + if (count) + *count = esa->rows; + } +} + +static gboolean +esa_needs_sorting(ESorter *es) +{ + ESorterArray *esa = E_SORTER_ARRAY(es); + return esa->compare != NULL; +} + +void +e_sorter_array_clean(ESorterArray *esa) +{ + g_free(esa->sorted); + esa->sorted = NULL; + + g_free(esa->backsorted); + esa->backsorted = NULL; +} + +void +e_sorter_array_set_count (ESorterArray *esa, int count) +{ + e_sorter_array_clean (esa); + esa->rows = count; +} + +void +e_sorter_array_append (ESorterArray *esa, int count) +{ + int i; + g_free(esa->backsorted); + esa->backsorted = NULL; + + if (esa->sorted) { + esa->sorted = g_renew(int, esa->sorted, esa->rows + count); + for (i = 0; i < count; i++) { + int value = esa->rows; + int pos; + e_bsearch (&value, esa->sorted, esa->rows, sizeof (int), esort_callback, esa, &pos, NULL); + memmove (esa->sorted + pos + 1, esa->sorted + pos, sizeof (int) * (esa->rows - pos)); + esa->sorted[pos] = value; + esa->rows ++; + } + } else { + esa->rows = count; + } +} + +ESorterArray * +e_sorter_array_construct (ESorterArray *esa, + ECompareRowsFunc compare, + gpointer closure) +{ + esa->compare = compare; + esa->closure = closure; + return esa; +} + +ESorterArray * +e_sorter_array_new (ECompareRowsFunc compare, gpointer closure) +{ + ESorterArray *esa = gtk_type_new (E_SORTER_ARRAY_TYPE); + + return e_sorter_array_construct (esa, compare, closure); +} + +static void +esa_destroy (GtkObject *object) +{ + GTK_OBJECT_CLASS (parent_class)->destroy (object); +} + +static void +esa_set_arg (GtkObject *object, GtkArg *arg, guint arg_id) +{ + switch (arg_id) { + default: + break; + } +} + +static void +esa_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) +{ + switch (arg_id) { + } +} + +static void +esa_class_init (ESorterArrayClass *klass) +{ + GtkObjectClass *object_class = GTK_OBJECT_CLASS(klass); + ESorterClass *sorter_class = E_SORTER_CLASS(klass); + + parent_class = gtk_type_class (PARENT_TYPE); + + object_class->destroy = esa_destroy; + object_class->set_arg = esa_set_arg; + object_class->get_arg = esa_get_arg; + + sorter_class->model_to_sorted = esa_model_to_sorted ; + sorter_class->sorted_to_model = esa_sorted_to_model ; + sorter_class->get_model_to_sorted_array = esa_get_model_to_sorted_array ; + sorter_class->get_sorted_to_model_array = esa_get_sorted_to_model_array ; + sorter_class->needs_sorting = esa_needs_sorting ; +} + +static void +esa_init (ESorterArray *esa) +{ + esa->rows = 0; + esa->compare = NULL; + esa->closure = NULL; + esa->sorted = NULL; + esa->backsorted = NULL; +} + +E_MAKE_TYPE(e_sorter_array, "ESorterArray", ESorterArray, esa_class_init, esa_init, PARENT_TYPE); diff --git a/e-util/e-sorter-array.h b/e-util/e-sorter-array.h new file mode 100644 index 0000000000..7f876e4148 --- /dev/null +++ b/e-util/e-sorter-array.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +#ifndef _E_SORTER_ARRAY_H_ +#define _E_SORTER_ARRAY_H_ + +#include +#include +#include +#include + +BEGIN_GNOME_DECLS + +#define E_SORTER_ARRAY_TYPE (e_sorter_array_get_type ()) +#define E_SORTER_ARRAY(o) (GTK_CHECK_CAST ((o), E_SORTER_ARRAY_TYPE, ESorterArray)) +#define E_SORTER_ARRAY_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_SORTER_ARRAY_TYPE, ESorterArrayClass)) +#define E_IS_SORTER_ARRAY(o) (GTK_CHECK_TYPE ((o), E_SORTER_ARRAY_TYPE)) +#define E_IS_SORTER_ARRAY_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_SORTER_ARRAY_TYPE)) + +#ifndef _E_COMPARE_ROWS_FUNC_H_ +#define _E_COMPARE_ROWS_FUNC_H_ +typedef int (*ECompareRowsFunc) (int row1, + int row2, + gpointer closure); +#endif + +typedef struct { + ESorter base; + + ECompareRowsFunc compare; + gpointer closure; + + /* If needs_sorting is 0, then model_to_sorted and sorted_to_model are no-ops. */ + int *sorted; + int *backsorted; + + int rows; +} ESorterArray; + +typedef struct { + ESorterClass parent_class; +} ESorterArrayClass; + +GtkType e_sorter_array_get_type (void); +ESorterArray *e_sorter_array_construct (ESorterArray *sorter, + ECompareRowsFunc compare, + gpointer closure); +ESorterArray *e_sorter_array_new (ECompareRowsFunc compare, + gpointer closure); +void e_sorter_array_clean (ESorterArray *esa); +void e_sorter_array_set_count (ESorterArray *esa, + int count); +void e_sorter_array_append (ESorterArray *esa, + int count); + +END_GNOME_DECLS + +#endif /* _E_SORTER_ARRAY_H_ */ diff --git a/e-util/e-util.c b/e-util/e-util.c index 5bbb9522b4..d7770ac3fa 100644 --- a/e-util/e-util.c +++ b/e-util/e-util.c @@ -678,6 +678,27 @@ e_marshal_NONE__POINTER_INT_INT_INT (GtkObject *object, func_data); } +typedef int (*GtkSignal_INT__OBJECT_POINTER) (GtkObject *, + GtkObject *, gpointer, + gpointer user_data); +void +e_marshal_INT__OBJECT_POINTER (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) +{ + GtkSignal_INT__OBJECT_POINTER rfunc; + int *return_val; + + rfunc = (GtkSignal_INT__OBJECT_POINTER) func; + return_val = GTK_RETLOC_INT (args[2]); + + *return_val = (*rfunc) (object, + GTK_VALUE_OBJECT (args[0]), + GTK_VALUE_POINTER (args[1]), + func_data); +} + gchar** e_strsplit (const gchar *string, const gchar *delimiter, diff --git a/e-util/e-util.h b/e-util/e-util.h index bf89b27aa1..c131676fc6 100644 --- a/e-util/e-util.h +++ b/e-util/e-util.h @@ -80,14 +80,12 @@ gchar *e_strstrcase (cons void e_filename_make_safe (gchar *string); gchar *e_format_number (gint number); gchar *e_format_number_float (gfloat number); - gboolean e_create_directory (gchar *directory); typedef int (*ESortCompareFunc) (const void *first, const void *second, gpointer closure); - void e_sort (void *base, size_t nmemb, size_t size, @@ -99,11 +97,8 @@ void e_bsearch (cons size_t size, ESortCompareFunc compare, gpointer closure, - size_t *start, - size_t *end); - - - + size_t *start, + size_t *end); void e_marshal_INT__INT_INT_POINTER (GtkObject *object, GtkSignalFunc func, gpointer func_data, @@ -192,11 +187,15 @@ void e_marshal_INT__POINTER_POINTER_POINTER_POINTER (GtkO GtkSignalFunc func, gpointer func_data, GtkArg *args); +void e_marshal_NONE__POINTER_INT_INT_INT (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); +void e_marshal_INT__OBJECT_POINTER (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args); -void e_marshal_NONE__POINTER_INT_INT_INT (GtkObject *object, - GtkSignalFunc func, - gpointer func_data, - GtkArg *args); #ifdef __cplusplus } diff --git a/widgets/misc/e-reflow-model.c b/widgets/misc/e-reflow-model.c new file mode 100644 index 0000000000..74e7b1726c --- /dev/null +++ b/widgets/misc/e-reflow-model.c @@ -0,0 +1,288 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * e-reflow-model.c: a Reflow Model + * + * Authors: + * Chris Lahey + * + * (C) 2001 Ximian, Inc. + */ +#include +#include "e-reflow-model.h" +#include + +#define ERM_CLASS(e) ((EReflowModelClass *)((GtkObject *)e)->klass) + +#define PARENT_TYPE gtk_object_get_type () + +#define d(x) + +d(static gint depth = 0); + + +static GtkObjectClass *e_reflow_model_parent_class; + +enum { + MODEL_CHANGED, + MODEL_ITEMS_INSERTED, + MODEL_ITEM_CHANGED, + 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, int width) +{ + g_return_if_fail (e_reflow_model != NULL); + g_return_if_fail (E_IS_REFLOW_MODEL (e_reflow_model)); + + ERM_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. + */ +int +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 ERM_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. + */ +int +e_reflow_model_height (EReflowModel *e_reflow_model, int 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 ERM_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, int 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 ERM_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. + */ +int +e_reflow_model_compare (EReflowModel *e_reflow_model, int n1, int 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 ERM_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, int n, GnomeCanvasItem *item) +{ + g_return_if_fail (e_reflow_model != NULL); + g_return_if_fail (E_IS_REFLOW_MODEL (e_reflow_model)); + + ERM_CLASS (e_reflow_model)->reincarnate (e_reflow_model, n, item); +} + +static void +e_reflow_model_class_init (GtkObjectClass *object_class) +{ + EReflowModelClass *klass = E_REFLOW_MODEL_CLASS(object_class); + e_reflow_model_parent_class = gtk_type_class (PARENT_TYPE); + + e_reflow_model_signals [MODEL_CHANGED] = + gtk_signal_new ("model_changed", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (EReflowModelClass, model_changed), + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, 0); + + e_reflow_model_signals [MODEL_ITEMS_INSERTED] = + gtk_signal_new ("model_items_inserted", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (EReflowModelClass, model_items_inserted), + gtk_marshal_NONE__INT_INT, + GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); + + e_reflow_model_signals [MODEL_ITEM_CHANGED] = + gtk_signal_new ("model_item_changed", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (EReflowModelClass, model_item_changed), + gtk_marshal_NONE__INT, + GTK_TYPE_NONE, 1, GTK_TYPE_INT); + + gtk_object_class_add_signals (object_class, e_reflow_model_signals, LAST_SIGNAL); + + klass->set_width = NULL; + klass->count = NULL; + klass->height = NULL; + klass->incarnate = NULL; + klass->reincarnate = NULL; + + klass->model_changed = NULL; + klass->model_items_inserted = NULL; + klass->model_item_changed = NULL; +} + + +guint +e_reflow_model_get_type (void) +{ + static guint type = 0; + + if (!type) + { + GtkTypeInfo info = + { + "EReflowModel", + sizeof (EReflowModel), + sizeof (EReflowModelClass), + (GtkClassInitFunc) e_reflow_model_class_init, + NULL, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + type = gtk_type_unique (PARENT_TYPE, &info); + } + + return type; +} + +#if d(!)0 +static void +print_tabs (void) +{ + int 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++); + gtk_signal_emit (GTK_OBJECT (e_reflow_model), + e_reflow_model_signals [MODEL_CHANGED]); + 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, int position, int 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++); + gtk_signal_emit (GTK_OBJECT (e_reflow_model), + e_reflow_model_signals [MODEL_ITEMS_INSERTED], position, count); + 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, int 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++); + gtk_signal_emit (GTK_OBJECT (e_reflow_model), + e_reflow_model_signals [MODEL_ITEM_CHANGED], n); + d(depth--); +} diff --git a/widgets/misc/e-reflow-model.h b/widgets/misc/e-reflow-model.h new file mode 100644 index 0000000000..db26335a35 --- /dev/null +++ b/widgets/misc/e-reflow-model.h @@ -0,0 +1,83 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +#ifndef _E_REFLOW_MODEL_H_ +#define _E_REFLOW_MODEL_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define E_REFLOW_MODEL_TYPE (e_reflow_model_get_type ()) +#define E_REFLOW_MODEL(o) (GTK_CHECK_CAST ((o), E_REFLOW_MODEL_TYPE, EReflowModel)) +#define E_REFLOW_MODEL_CLASS(k) (GTK_CHECK_CLASS_CAST((k), E_REFLOW_MODEL_TYPE, EReflowModelClass)) +#define E_IS_REFLOW_MODEL(o) (GTK_CHECK_TYPE ((o), E_REFLOW_MODEL_TYPE)) +#define E_IS_REFLOW_MODEL_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_REFLOW_MODEL_TYPE)) + +typedef struct { + GtkObject base; +} EReflowModel; + +typedef struct { + GtkObjectClass parent_class; + + /* + * Virtual methods + */ + void (*set_width) (EReflowModel *etm, int width); + + int (*count) (EReflowModel *etm); + int (*height) (EReflowModel *etm, int n, GnomeCanvasGroup *parent); + GnomeCanvasItem *(*incarnate) (EReflowModel *etm, int n, GnomeCanvasGroup *parent); + int (*compare) (EReflowModel *etm, int n1, int n2); + void (*reincarnate) (EReflowModel *etm, int n, GnomeCanvasItem *item); + + /* + * Signals + */ + + /* + * These all come after the change has been made. + * Major structural changes: model_changed + * Changes only in an item: item_changed + */ + void (*model_changed) (EReflowModel *etm); + void (*model_items_inserted) (EReflowModel *etm, int position, int count); + void (*model_item_changed) (EReflowModel *etm, int n); +} EReflowModelClass; + +GtkType e_reflow_model_get_type (void); + +/**/ +void e_reflow_model_set_width (EReflowModel *e_reflow_model, + int width); +int e_reflow_model_count (EReflowModel *e_reflow_model); +int e_reflow_model_height (EReflowModel *e_reflow_model, + int n, + GnomeCanvasGroup *parent); +GnomeCanvasItem *e_reflow_model_incarnate (EReflowModel *e_reflow_model, + int n, + GnomeCanvasGroup *parent); +int e_reflow_model_compare (EReflowModel *e_reflow_model, + int n1, + int n2); +void e_reflow_model_reincarnate (EReflowModel *e_reflow_model, + int n, + GnomeCanvasItem *item); + +/* + * Routines for emitting signals on the e_reflow + */ +void e_reflow_model_changed (EReflowModel *e_reflow_model); +void e_reflow_model_items_inserted (EReflowModel *e_reflow_model, + int position, + int count); +void e_reflow_model_item_changed (EReflowModel *e_reflow_model, + int n); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _E_REFLOW_MODEL_H_ */ diff --git a/widgets/misc/e-reflow.c b/widgets/misc/e-reflow.c index d67d501019..65fb2f91a6 100644 --- a/widgets/misc/e-reflow.c +++ b/widgets/misc/e-reflow.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * e-reflow.c - * Copyright (C) 2000 Helix Code, Inc. + * Copyright (C) 2000, 2001 Ximian, Inc. * Author: Chris Lahey * * This library is free software; you can redistribute it and/or @@ -21,19 +21,20 @@ */ #include + +#include "e-reflow.h" + #include #include -#include "e-reflow.h" #include "e-canvas-utils.h" #include "e-canvas.h" #include "gal/e-text/e-text.h" #include "gal/util/e-util.h" +#include +#include "e-selection-model-simple.h" + +#include -static void e_reflow_init (EReflow *reflow); -static void e_reflow_class_init (EReflowClass *klass); -static void e_reflow_set_arg (GtkObject *o, GtkArg *arg, guint arg_id); -static void e_reflow_get_arg (GtkObject *object, GtkArg *arg, guint arg_id); -static void e_reflow_destroy (GtkObject *object); static gboolean e_reflow_event (GnomeCanvasItem *item, GdkEvent *event); static void e_reflow_realize (GnomeCanvasItem *item); static void e_reflow_unrealize (GnomeCanvasItem *item); @@ -42,8 +43,7 @@ static void e_reflow_draw (GnomeCanvasItem *item, GdkDrawable *drawable, 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, int cx, int cy, GnomeCanvasItem **actual_item); static void e_reflow_reflow (GnomeCanvasItem *item, int flags); -static void e_reflow_real_add_item(EReflow *e_reflow, GnomeCanvasItem *item, gint *position); -static void set_empty(EReflow *e_reflow); +static void set_empty(EReflow *reflow); static void e_reflow_resize_children (GnomeCanvasItem *item); @@ -60,142 +60,511 @@ enum { ARG_WIDTH, ARG_HEIGHT, ARG_EMPTY_MESSAGE, + ARG_MODEL, }; -GtkType -e_reflow_get_type (void) +enum { + SELECTION_EVENT, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = {0, }; + +static gint +er_compare (int i1, int i2, gpointer user_data) { - static GtkType reflow_type = 0; + EReflow *reflow = user_data; + return e_reflow_model_compare (reflow->model, i1, i2); +} - if (!reflow_type) - { - static const GtkTypeInfo reflow_info = - { - "EReflow", - sizeof (EReflow), - sizeof (EReflowClass), - (GtkClassInitFunc) e_reflow_class_init, - (GtkObjectInitFunc) e_reflow_init, - /* reserved_1 */ NULL, - /* reserved_2 */ NULL, - (GtkClassInitFunc) NULL, - }; +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; +} - reflow_type = gtk_type_unique (gnome_canvas_group_get_type (), &reflow_info); - } +static int +er_find_item (EReflow *reflow, GnomeCanvasItem *item) +{ + int 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; + int i; + int count; + + reflow = E_REFLOW (item); - return reflow_type; + 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 void -e_reflow_class_init (EReflowClass *klass) +e_reflow_update_selection (EReflow *reflow) { - GtkObjectClass *object_class; - GnomeCanvasItemClass *item_class; + int i; + int count; + + count = reflow->count; + for (i = 0; i < count; i++) { + if (reflow->items[i]) { + gtk_object_set(GTK_OBJECT(reflow->items[i]), + "selected", e_selection_model_is_row_selected(E_SELECTION_MODEL(reflow->selection), i), + NULL); + } else if (e_selection_model_is_row_selected (E_SELECTION_MODEL (reflow->selection), i)) { + reflow->items[i] = e_reflow_model_incarnate (reflow->model, i, GNOME_CANVAS_GROUP (reflow)); + gtk_object_set (GTK_OBJECT (reflow->items[i]), + "selected", e_selection_model_is_row_selected(E_SELECTION_MODEL(reflow->selection), i), + "width", (double) reflow->column_width, + NULL); + } + } +} - object_class = (GtkObjectClass*) klass; - item_class = (GnomeCanvasItemClass *) klass; +static void +selection_changed (ESelectionModel *selection, EReflow *reflow) +{ + e_reflow_update_selection (reflow); +} - parent_class = gtk_type_class (gnome_canvas_group_get_type ()); - - gtk_object_add_arg_type ("EReflow::minimum_width", GTK_TYPE_DOUBLE, - GTK_ARG_READWRITE, ARG_MINIMUM_WIDTH); - gtk_object_add_arg_type ("EReflow::width", GTK_TYPE_DOUBLE, - GTK_ARG_READABLE, ARG_WIDTH); - gtk_object_add_arg_type ("EReflow::height", GTK_TYPE_DOUBLE, - GTK_ARG_READWRITE, ARG_HEIGHT); - gtk_object_add_arg_type ("EReflow::empty_message", GTK_TYPE_STRING, - GTK_ARG_READWRITE, ARG_EMPTY_MESSAGE); +static void +incarnate (EReflow *reflow) +{ + int column_width; + int first_column; + int last_column; + int first_cell; + int last_cell; + int i; + GtkAdjustment *adjustment = gtk_layout_get_hadjustment (GTK_LAYOUT (GNOME_CANVAS_ITEM (reflow)->canvas)); - klass->add_item = e_reflow_real_add_item; - - object_class->set_arg = e_reflow_set_arg; - object_class->get_arg = e_reflow_get_arg; - object_class->destroy = e_reflow_destroy; - - /* 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; + 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++) { + int 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)); + gtk_object_set (GTK_OBJECT (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 -e_reflow_init (EReflow *reflow) +queue_incarnate (EReflow *reflow) { - reflow->items = NULL; - reflow->columns = NULL; - reflow->column_width = 150; + if (reflow->incarnate_idle_id == 0) + reflow->incarnate_idle_id = + g_idle_add_full (25, invoke_incarnate, reflow, NULL); +} - reflow->minimum_width = 10; - reflow->width = 10; - reflow->height = 10; - reflow->idle = 0; +static void +reflow_columns (EReflow *reflow) +{ + GSList *list; + int count; + int i; + int column_count; + double running_height; + + g_free (reflow->columns); + reflow->column_count = 0; + reflow->columns = NULL; + + list = NULL; + + running_height = E_REFLOW_BORDER_WIDTH; + column_count = 1; + + count = reflow->count; + for (i = 0; i < count; i++) { + int unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i); + if (reflow->heights[unsorted] == -1) { + if (reflow->model) + reflow->heights[unsorted] = e_reflow_model_height (reflow->model, unsorted, GNOME_CANVAS_GROUP (reflow)); + else + reflow->heights[unsorted] = 0; + } + 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[2]; + } else + running_height += reflow->heights[unsorted] + E_REFLOW_BORDER_WIDTH; + } - reflow->empty_message = NULL; - reflow->empty_text = NULL; + reflow->column_count = column_count; + reflow->columns = g_new (int, column_count); + column_count --; + for (; column_count > 0; 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[0] = 0; - reflow->column_drag = FALSE; + queue_incarnate (reflow); - reflow->need_height_update = FALSE; - reflow->need_column_resize = FALSE; + reflow->need_reflow_columns = FALSE; +} - reflow->default_cursor_shown = TRUE; - reflow->arrow_cursor = NULL; - reflow->default_cursor = NULL; +static void +item_changed (EReflowModel *model, int i, EReflow *reflow) +{ + if (i < 0 || i >= reflow->count) + return; - e_canvas_item_set_reflow_callback(GNOME_CANVAS_ITEM(reflow), e_reflow_reflow); + reflow->heights[i] = -1; + if (reflow->items[i] != NULL) + e_reflow_model_reincarnate (model, i, reflow->items[i]); + e_sorter_array_clean (reflow->sorter); + reflow->need_reflow_columns = TRUE; + e_canvas_item_request_reflow(GNOME_CANVAS_ITEM (reflow)); +} + +static void +items_inserted (EReflowModel *model, int position, int count, EReflow *reflow) +{ + int i; + int 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 (int)); + memmove (reflow->items + position + count, reflow->items + position, (reflow->count - position - count) * sizeof (GnomeCanvasItem *)); + for (i = position; i < position + count; i++) { + reflow->items[i] = 0; + reflow->heights[i] = -1; + } + + 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); + reflow->need_reflow_columns = TRUE; + e_canvas_item_request_reflow(GNOME_CANVAS_ITEM (reflow)); + + g_print ("New count = %d\n", reflow->count); +} + +static void +model_changed (EReflowModel *model, EReflow *reflow) +{ + int i; + int count; + int oldcount; + + count = reflow->count; + oldcount = count; + + for (i = 0; i < count; 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] = 0; + reflow->heights[i] = -1; + } + + 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); + 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, + "font_gdk", GTK_WIDGET(GNOME_CANVAS_ITEM(reflow)->canvas)->style->font, + "fill_color", "black", + "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; + + gtk_signal_disconnect (GTK_OBJECT (reflow->model), + reflow->model_changed_id); + gtk_signal_disconnect (GTK_OBJECT (reflow->model), + reflow->model_items_inserted_id); + gtk_signal_disconnect (GTK_OBJECT (reflow->model), + reflow->model_item_changed_id); + gtk_object_unref (GTK_OBJECT (reflow->model)); + + reflow->model_changed_id = 0; + reflow->model_items_inserted_id = 0; + reflow->model_item_changed_id = 0; + reflow->model = NULL; } +static void +disconnect_selection (EReflow *reflow) +{ + if (reflow->selection == NULL) + return; + + gtk_signal_disconnect (GTK_OBJECT (reflow->selection), + reflow->selection_changed_id); + gtk_object_unref (GTK_OBJECT (reflow->selection)); + + reflow->selection_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; + gtk_object_ref (GTK_OBJECT (reflow->model)); + reflow->model_changed_id = + gtk_signal_connect (GTK_OBJECT (reflow->model), "model_changed", + GTK_SIGNAL_FUNC (model_changed), reflow); + reflow->model_items_inserted_id = + gtk_signal_connect (GTK_OBJECT (reflow->model), "model_items_inserted", + GTK_SIGNAL_FUNC (items_inserted), reflow); + reflow->model_item_changed_id = + gtk_signal_connect (GTK_OBJECT (reflow->model), "model_item_changed", + GTK_SIGNAL_FUNC (item_changed), reflow); +} + +static void +adjustment_changed (GtkAdjustment *adjustment, EReflow *reflow) +{ + incarnate (reflow); +} + +static void +disconnect_adjustment (EReflow *reflow) +{ + if (reflow->adjustment == NULL) + return; + + gtk_signal_disconnect (GTK_OBJECT (reflow->adjustment), + reflow->adjustment_changed_id); + gtk_signal_disconnect (GTK_OBJECT (reflow->adjustment), + reflow->adjustment_value_changed_id); + + gtk_object_unref (GTK_OBJECT (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 = + gtk_signal_connect (GTK_OBJECT (adjustment), "changed", + adjustment_changed, reflow); + reflow->adjustment_value_changed_id = + gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", + adjustment_changed, reflow); + gtk_object_ref (GTK_OBJECT (adjustment)); +} + +static void +set_scroll_adjustments (GtkLayout *layout, GtkAdjustment *hadj, GtkAdjustment *vadj, EReflow *reflow) +{ + connect_adjustment (reflow, hadj); +} + +static void +disconnect_set_adjustment (EReflow *reflow) +{ + gtk_signal_disconnect (GTK_OBJECT (GNOME_CANVAS_ITEM (reflow)->canvas), + reflow->set_scroll_adjustments_id); + reflow->set_scroll_adjustments_id = 0; +} + +static void +connect_set_adjustment (EReflow *reflow) +{ + reflow->set_scroll_adjustments_id = + gtk_signal_connect (GTK_OBJECT (GNOME_CANVAS_ITEM (reflow)->canvas), + "set_scroll_adjustments", + GTK_SIGNAL_FUNC (set_scroll_adjustments), reflow); +} + + + + +/* Virtual functions */ static void e_reflow_set_arg (GtkObject *o, GtkArg *arg, guint arg_id) { GnomeCanvasItem *item; - EReflow *e_reflow; + EReflow *reflow; item = GNOME_CANVAS_ITEM (o); - e_reflow = E_REFLOW (o); + reflow = E_REFLOW (o); switch (arg_id){ case ARG_HEIGHT: - e_reflow->height = GTK_VALUE_DOUBLE (*arg); + reflow->height = GTK_VALUE_DOUBLE (*arg); + reflow->need_reflow_columns = TRUE; e_canvas_item_request_reflow(item); break; case ARG_MINIMUM_WIDTH: - e_reflow->minimum_width = GTK_VALUE_DOUBLE (*arg); + reflow->minimum_width = GTK_VALUE_DOUBLE (*arg); if (GNOME_CANVAS_ITEM_REALIZED & GTK_OBJECT_FLAGS(o)) - set_empty(e_reflow); + set_empty(reflow); e_canvas_item_request_reflow(item); break; case ARG_EMPTY_MESSAGE: - g_free(e_reflow->empty_message); - e_reflow->empty_message = g_strdup(GTK_VALUE_STRING (*arg)); + g_free(reflow->empty_message); + reflow->empty_message = g_strdup(GTK_VALUE_STRING (*arg)); if (GNOME_CANVAS_ITEM_REALIZED & GTK_OBJECT_FLAGS(o)) - set_empty(e_reflow); + set_empty(reflow); + break; + case ARG_MODEL: + connect_model (reflow, (EReflowModel *) GTK_VALUE_OBJECT (*arg)); + break; } } static void e_reflow_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) { - EReflow *e_reflow; + EReflow *reflow; - e_reflow = E_REFLOW (object); + reflow = E_REFLOW (object); switch (arg_id) { case ARG_MINIMUM_WIDTH: - GTK_VALUE_DOUBLE (*arg) = e_reflow->minimum_width; + GTK_VALUE_DOUBLE (*arg) = reflow->minimum_width; break; case ARG_WIDTH: - GTK_VALUE_DOUBLE (*arg) = e_reflow->width; + GTK_VALUE_DOUBLE (*arg) = reflow->width; break; case ARG_HEIGHT: - GTK_VALUE_DOUBLE (*arg) = e_reflow->height; + GTK_VALUE_DOUBLE (*arg) = reflow->height; break; case ARG_EMPTY_MESSAGE: - GTK_VALUE_STRING (*arg) = g_strdup(e_reflow->empty_message); + GTK_VALUE_STRING (*arg) = g_strdup(reflow->empty_message); + break; + case ARG_MODEL: + GTK_VALUE_OBJECT (*arg) = (GtkObject *) reflow->model; break; default: arg->type = GTK_TYPE_INVALID; @@ -207,10 +576,25 @@ static void e_reflow_destroy (GtkObject *object) { EReflow *reflow = E_REFLOW(object); + int count; - g_list_foreach(reflow->items, (GFunc) gtk_object_unref, NULL); - g_list_free(reflow->items); - reflow->items = NULL; + count = reflow->count; + + 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 != 0) + g_source_remove (reflow->incarnate_idle_id); + + disconnect_model (reflow); + disconnect_selection (reflow); g_free(reflow->empty_message); @@ -220,33 +604,40 @@ e_reflow_destroy (GtkObject *object) static void e_reflow_realize (GnomeCanvasItem *item) { - EReflow *e_reflow; + EReflow *reflow; GnomeCanvasGroup *group; - GList *list; GtkAdjustment *adjustment; + int count; + int i; - e_reflow = E_REFLOW (item); - group = GNOME_CANVAS_GROUP( item ); + reflow = E_REFLOW (item); + group = GNOME_CANVAS_GROUP (item); if (GNOME_CANVAS_ITEM_CLASS(parent_class)->realize) (* GNOME_CANVAS_ITEM_CLASS(parent_class)->realize) (item); - e_reflow->arrow_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW); - e_reflow->default_cursor = gdk_cursor_new (GDK_LEFT_PTR); - - for(list = e_reflow->items; list; list = g_list_next(list)) { - GnomeCanvasItem *item = GNOME_CANVAS_ITEM(list->data); - gnome_canvas_item_set(item, - "width", (double) e_reflow->column_width, - NULL); + 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", (double) reflow->column_width, + NULL); } - set_empty(e_reflow); + set_empty(reflow); + reflow->need_reflow_columns = TRUE; e_canvas_item_request_reflow(item); - + adjustment = gtk_layout_get_hadjustment(GTK_LAYOUT(item->canvas)); - adjustment->step_increment = (e_reflow->column_width + E_REFLOW_FULL_GUTTER) / 2; + + connect_set_adjustment (reflow); + 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); @@ -257,71 +648,82 @@ e_reflow_realize (GnomeCanvasItem *item) static void e_reflow_unrealize (GnomeCanvasItem *item) { - EReflow *e_reflow; + EReflow *reflow; - e_reflow = E_REFLOW (item); + reflow = E_REFLOW (item); - if (!item->canvas->aa) - { - } + if (!item->canvas->aa) { + } - gdk_cursor_destroy (e_reflow->arrow_cursor); - gdk_cursor_destroy (e_reflow->default_cursor); - e_reflow->arrow_cursor = NULL; - e_reflow->default_cursor = NULL; + gdk_cursor_destroy (reflow->arrow_cursor); + gdk_cursor_destroy (reflow->default_cursor); + reflow->arrow_cursor = NULL; + reflow->default_cursor = NULL; - g_list_free (e_reflow->columns); - e_reflow->columns = NULL; + g_free (reflow->columns); + reflow->columns = NULL; - if (GNOME_CANVAS_ITEM_CLASS(parent_class)->unrealize) - (* GNOME_CANVAS_ITEM_CLASS(parent_class)->unrealize) (item); -} + disconnect_set_adjustment (reflow); + disconnect_adjustment (reflow); -static gint -e_reflow_pick_line (EReflow *e_reflow, double x) -{ - x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH; - x /= e_reflow->column_width + E_REFLOW_FULL_GUTTER; - return x; + if (GNOME_CANVAS_ITEM_CLASS(parent_class)->unrealize) + (* GNOME_CANVAS_ITEM_CLASS(parent_class)->unrealize) (item); } static gboolean e_reflow_event (GnomeCanvasItem *item, GdkEvent *event) { - EReflow *e_reflow; + EReflow *reflow; + int return_val = FALSE; - e_reflow = E_REFLOW (item); + 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) { - GList *list; - for (list = e_reflow->items; list; list = list->next) { - GnomeCanvasItem *item = GNOME_CANVAS_ITEM (list->data); + int i; + int count; + count = reflow->count; + for (i = 0; i < count; i++) { + int unsorted = e_sorter_sorted_to_model (E_SORTER (reflow->sorter), i); + GnomeCanvasItem *item = reflow->items[unsorted]; EFocus has_focus; - gtk_object_get(GTK_OBJECT(item), - "has_focus", &has_focus, - NULL); - if (has_focus) { - if (event->key.state & GDK_SHIFT_MASK) - list = list->prev; - else - list = list->next; - if (list) { - item = GNOME_CANVAS_ITEM(list->data); + if (item) { + gtk_object_get(GTK_OBJECT(item), + "has_focus", &has_focus, + NULL); + if (has_focus) { + if (event->key.state & GDK_SHIFT_MASK) { + if (i == 0) + return 0; + i--; + } else { + if (i == count - 1) + return 0; + 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 1; - } else { - return 0; } } } } +#endif break; case GDK_BUTTON_PRESS: switch(event->button.button) @@ -332,23 +734,23 @@ e_reflow_event (GnomeCanvasItem *item, GdkEvent *event) double n_x, max_x; n_x = button->x; n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH; - n_x = fmod(n_x,(e_reflow->column_width + E_REFLOW_FULL_GUTTER)); + n_x = fmod(n_x,(reflow->column_width + E_REFLOW_FULL_GUTTER)); max_x = E_REFLOW_BORDER_WIDTH; - max_x += (e_reflow->column_width + E_REFLOW_FULL_GUTTER) * e_reflow->column_count; - if ( button->y >= E_REFLOW_BORDER_WIDTH && button->y <= e_reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER && max_x > button->x ) { - e_reflow->which_column_dragged = e_reflow_pick_line(e_reflow, button->x); - e_reflow->start_x = e_reflow->which_column_dragged * (e_reflow->column_width + E_REFLOW_FULL_GUTTER) - E_REFLOW_DIVIDER_WIDTH / 2; - e_reflow->temp_column_width = e_reflow->column_width; - e_reflow->column_drag = TRUE; + max_x += (reflow->column_width + E_REFLOW_FULL_GUTTER) * reflow->column_count; + if ( button->y >= E_REFLOW_BORDER_WIDTH && button->y <= reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER && max_x > button->x ) { + 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, - e_reflow->arrow_cursor, + reflow->arrow_cursor, button->time); - e_reflow->previous_temp_column_width = -1; - e_reflow->need_column_resize = TRUE; + reflow->previous_temp_column_width = -1; + reflow->need_column_resize = TRUE; gnome_canvas_item_request_update(item); return TRUE; } @@ -375,41 +777,41 @@ e_reflow_event (GnomeCanvasItem *item, GdkEvent *event) } break; case GDK_BUTTON_RELEASE: - if (e_reflow->column_drag) { - gdouble old_width = e_reflow->column_width; + if (reflow->column_drag) { + gdouble old_width = reflow->column_width; GdkEventButton *button = (GdkEventButton *) event; GtkAdjustment *adjustment = gtk_layout_get_hadjustment(GTK_LAYOUT(item->canvas)); - e_reflow->temp_column_width = e_reflow->column_width + - (button->x - e_reflow->start_x)/(e_reflow->which_column_dragged - e_reflow_pick_line(e_reflow, adjustment->value)); - if ( e_reflow->temp_column_width < 50 ) - e_reflow->temp_column_width = 50; - e_reflow->column_drag = FALSE; - if ( old_width != e_reflow->temp_column_width ) { - gtk_adjustment_set_value(adjustment, adjustment->value + e_reflow_pick_line(e_reflow, adjustment->value) * (e_reflow->temp_column_width - e_reflow->column_width)); - e_reflow->column_width = e_reflow->temp_column_width; - adjustment->step_increment = (e_reflow->column_width + E_REFLOW_FULL_GUTTER) / 2; + 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); } - e_reflow->need_column_resize = TRUE; + 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 (e_reflow->column_drag) { - double old_width = e_reflow->temp_column_width; + 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)); - e_reflow->temp_column_width = e_reflow->column_width + - (motion->x - e_reflow->start_x)/(e_reflow->which_column_dragged - e_reflow_pick_line(e_reflow, adjustment->value)); - if (e_reflow->temp_column_width < 50) - e_reflow->temp_column_width = 50; - if (old_width != e_reflow->temp_column_width) { - e_reflow->need_column_resize = TRUE; + 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; @@ -419,53 +821,53 @@ e_reflow_event (GnomeCanvasItem *item, GdkEvent *event) n_x = motion->x; n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH; - n_x = fmod(n_x,(e_reflow->column_width + E_REFLOW_FULL_GUTTER)); + n_x = fmod(n_x,(reflow->column_width + E_REFLOW_FULL_GUTTER)); max_x = E_REFLOW_BORDER_WIDTH; - max_x += (e_reflow->column_width + E_REFLOW_FULL_GUTTER) * e_reflow->column_count; + max_x += (reflow->column_width + E_REFLOW_FULL_GUTTER) * reflow->column_count; - if ( motion->y >= E_REFLOW_BORDER_WIDTH && motion->y <= e_reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER && max_x > motion->x) { - if ( e_reflow->default_cursor_shown ) { - gdk_window_set_cursor(GTK_WIDGET(item->canvas)->window, e_reflow->arrow_cursor); - e_reflow->default_cursor_shown = FALSE; + if ( motion->y >= E_REFLOW_BORDER_WIDTH && motion->y <= reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER && max_x > motion->x) { + if ( reflow->default_cursor_shown ) { + gdk_window_set_cursor(GTK_WIDGET(item->canvas)->window, reflow->arrow_cursor); + reflow->default_cursor_shown = FALSE; } } else - if ( ! e_reflow->default_cursor_shown ) { - gdk_window_set_cursor(GTK_WIDGET(item->canvas)->window, e_reflow->default_cursor); - e_reflow->default_cursor_shown = TRUE; + 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 (!e_reflow->column_drag) { + if (!reflow->column_drag) { GdkEventCrossing *crossing = (GdkEventCrossing *) event; double n_x, max_x; n_x = crossing->x; n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH; - n_x = fmod(n_x,(e_reflow->column_width + E_REFLOW_FULL_GUTTER)); + n_x = fmod(n_x,(reflow->column_width + E_REFLOW_FULL_GUTTER)); max_x = E_REFLOW_BORDER_WIDTH; - max_x += (e_reflow->column_width + E_REFLOW_FULL_GUTTER) * e_reflow->column_count; - if ( crossing->y >= E_REFLOW_BORDER_WIDTH && crossing->y <= e_reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER && max_x > crossing->x) { - if ( e_reflow->default_cursor_shown ) { - gdk_window_set_cursor(GTK_WIDGET(item->canvas)->window, e_reflow->arrow_cursor); - e_reflow->default_cursor_shown = FALSE; + max_x += (reflow->column_width + E_REFLOW_FULL_GUTTER) * reflow->column_count; + if ( crossing->y >= E_REFLOW_BORDER_WIDTH && crossing->y <= reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER && max_x > crossing->x) { + 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 (!e_reflow->column_drag) { + 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,(e_reflow->column_width + E_REFLOW_FULL_GUTTER)); - if ( !( crossing->y >= E_REFLOW_BORDER_WIDTH && crossing->y <= e_reflow->height - E_REFLOW_BORDER_WIDTH && n_x < E_REFLOW_FULL_GUTTER ) ) { - if ( ! e_reflow->default_cursor_shown ) { - gdk_window_set_cursor(GTK_WIDGET(item->canvas)->window, e_reflow->default_cursor); - e_reflow->default_cursor_shown = TRUE; + 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; } } } @@ -473,27 +875,12 @@ e_reflow_event (GnomeCanvasItem *item, GdkEvent *event) default: break; } - - if (GNOME_CANVAS_ITEM_CLASS( parent_class )->event) + if (return_val) + return return_val; + else if (GNOME_CANVAS_ITEM_CLASS( parent_class )->event) return (* GNOME_CANVAS_ITEM_CLASS( parent_class )->event) (item, event); else - return 0; -} - -static void -e_reflow_real_add_item(EReflow *e_reflow, GnomeCanvasItem *item, gint *position) -{ - e_reflow->items = g_list_append(e_reflow->items, item); - gtk_object_ref(GTK_OBJECT(item)); - if (GTK_OBJECT_FLAGS (e_reflow) & GNOME_CANVAS_ITEM_REALIZED) { - gnome_canvas_item_set(item, - "width", (double) e_reflow->column_width, - NULL); - e_reflow_post_add_item(e_reflow, item); - e_canvas_item_request_reflow(item); - } - if (position) - *position = g_list_index(e_reflow->items, item); + return FALSE; } static void e_reflow_draw (GnomeCanvasItem *item, GdkDrawable *drawable, @@ -501,25 +888,25 @@ static void e_reflow_draw (GnomeCanvasItem *item, GdkDrawable *drawable, { int x_rect, y_rect, width_rect, height_rect; gdouble running_width; - EReflow *e_reflow = E_REFLOW(item); + EReflow *reflow = E_REFLOW(item); int i; double column_width; if (GNOME_CANVAS_ITEM_CLASS(parent_class)->draw) GNOME_CANVAS_ITEM_CLASS(parent_class)->draw (item, drawable, x, y, width, height); - column_width = e_reflow->column_width; + 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 = e_reflow->height - (E_REFLOW_BORDER_WIDTH * 2); + 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 < e_reflow->column_count; i++) { + for ( ; i < reflow->column_count; i++) { if ( running_width > x + width ) break; x_rect = running_width; @@ -536,25 +923,25 @@ static void e_reflow_draw (GnomeCanvasItem *item, GdkDrawable *drawable, height_rect); running_width += E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH; } - if (e_reflow->column_drag) { - int start_line = e_reflow_pick_line(e_reflow, + if (reflow->column_drag) { + int 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 = e_reflow->temp_column_width; + 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 = e_reflow->height - (E_REFLOW_BORDER_WIDTH * 2); + 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 < e_reflow->column_count; i++) { + for ( ; i < reflow->column_count; i++) { if ( running_width > x + width ) break; x_rect = running_width; @@ -573,10 +960,10 @@ static void e_reflow_draw (GnomeCanvasItem *item, GdkDrawable *drawable, static void e_reflow_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gint flags) { - EReflow *e_reflow; + EReflow *reflow; double x0, x1, y0, y1; - e_reflow = E_REFLOW (item); + reflow = E_REFLOW (item); if (GNOME_CANVAS_ITEM_CLASS(parent_class)->update) GNOME_CANVAS_ITEM_CLASS(parent_class)->update (item, affine, clip_path, flags); @@ -585,14 +972,14 @@ e_reflow_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gin y0 = item->y1; x1 = item->x2; y1 = item->y2; - if ( x1 < x0 + e_reflow->width ) - x1 = x0 + e_reflow->width; - if ( y1 < y0 + e_reflow->height ) - y1 = y0 + e_reflow->height; + 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 (e_reflow->need_height_update) { + if (reflow->need_height_update) { x0 = item->x1; y0 = item->y1; x1 = item->x2; @@ -607,64 +994,49 @@ e_reflow_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gin x1 = E_REFLOW(item)->height; gnome_canvas_request_redraw(item->canvas, x0, y0, x1, y1); - e_reflow->need_height_update = FALSE; - } else if (e_reflow->need_column_resize) { + reflow->need_height_update = FALSE; + } else if (reflow->need_column_resize) { int x_rect, y_rect, width_rect, height_rect; - int start_line = e_reflow_pick_line(e_reflow, + int start_line = e_reflow_pick_line(reflow, gtk_layout_get_hadjustment(GTK_LAYOUT(item->canvas))->value); gdouble running_width; int i; double column_width; - if ( e_reflow->previous_temp_column_width != -1 ) { - running_width = start_line * (e_reflow->column_width + E_REFLOW_FULL_GUTTER); - column_width = e_reflow->previous_temp_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 = e_reflow->height - (E_REFLOW_BORDER_WIDTH * 2); + height_rect = reflow->height - (E_REFLOW_BORDER_WIDTH * 2); - for ( i = 0; i < e_reflow->column_count; i++) { + 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 ( e_reflow->temp_column_width != -1 ) { - running_width = start_line * (e_reflow->column_width + E_REFLOW_FULL_GUTTER); - column_width = e_reflow->temp_column_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 = e_reflow->height - (E_REFLOW_BORDER_WIDTH * 2); + height_rect = reflow->height - (E_REFLOW_BORDER_WIDTH * 2); - for ( i = 0; i < e_reflow->column_count; i++) { + 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; } } - e_reflow->previous_temp_column_width = e_reflow->temp_column_width; - e_reflow->need_column_resize = FALSE; - } -} - -static void -e_reflow_resize_children (GnomeCanvasItem *item) -{ - GList *list; - EReflow *e_reflow; - - e_reflow = E_REFLOW (item); - for ( list = e_reflow->items; list; list = list->next ) { - GnomeCanvasItem *child = GNOME_CANVAS_ITEM(list->data); - gnome_canvas_item_set(child, - "width", (double) e_reflow->column_width, - NULL); + reflow->previous_temp_column_width = reflow->temp_column_width; + reflow->need_column_resize = FALSE; } } @@ -685,11 +1057,11 @@ e_reflow_point (GnomeCanvasItem *item, *actual_item = item; return 0; #if 0 - if (y >= E_REFLOW_BORDER_WIDTH && y <= e_reflow->height - E_REFLOW_BORDER_WIDTH) { + if (y >= E_REFLOW_BORDER_WIDTH && y <= reflow->height - E_REFLOW_BORDER_WIDTH) { float n_x; n_x = x; n_x += E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH; - n_x = fmod(n_x, (e_reflow->column_width + E_REFLOW_FULL_GUTTER)); + n_x = fmod(n_x, (reflow->column_width + E_REFLOW_FULL_GUTTER)); if (n_x < E_REFLOW_FULL_GUTTER) { *actual_item = item; return 0; @@ -700,164 +1072,194 @@ e_reflow_point (GnomeCanvasItem *item, } static void -_reflow( EReflow *e_reflow ) +e_reflow_reflow( GnomeCanvasItem *item, int flags ) { + EReflow *reflow = E_REFLOW(item); + gdouble old_width; + gdouble running_width; gdouble running_height; - GList *list; - double item_height; + int next_column; + int i; - if (e_reflow->columns) { - g_list_free (e_reflow->columns); - e_reflow->columns = NULL; + if (! (GTK_OBJECT_FLAGS (reflow) & GNOME_CANVAS_ITEM_REALIZED)) + return; + + if (reflow->need_reflow_columns) { + reflow_columns (reflow); } + + old_width = reflow->width; - e_reflow->column_count = 0; + running_width = E_REFLOW_BORDER_WIDTH; + running_height = E_REFLOW_BORDER_WIDTH; - if (e_reflow->items == NULL) { - e_reflow->columns = NULL; - e_reflow->column_count = 0; - return; - } + next_column = 1; - list = e_reflow->items; - - gtk_object_get (GTK_OBJECT(list->data), - "height", &item_height, - NULL); - running_height = E_REFLOW_BORDER_WIDTH + item_height + E_REFLOW_BORDER_WIDTH; - e_reflow->columns = g_list_append (e_reflow->columns, list); - e_reflow->column_count = 1; - - list = g_list_next(list); - - for ( ; list; list = g_list_next(list)) { - gtk_object_get (GTK_OBJECT(list->data), - "height", &item_height, - NULL); - if (running_height + item_height + E_REFLOW_BORDER_WIDTH > e_reflow->height) { - running_height = E_REFLOW_BORDER_WIDTH + item_height + E_REFLOW_BORDER_WIDTH; - e_reflow->columns = g_list_append (e_reflow->columns, list); - e_reflow->column_count ++; - } else { - running_height += item_height + E_REFLOW_BORDER_WIDTH; + for (i = 0; i < reflow->count; i++) { + int 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 (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 void -set_empty(EReflow *e_reflow) -{ - if (e_reflow->items == NULL) { - if (e_reflow->empty_text) { - if (e_reflow->empty_message) { - gnome_canvas_item_set(e_reflow->empty_text, - "width", e_reflow->minimum_width, - "text", e_reflow->empty_message, - NULL); - e_canvas_item_move_absolute(e_reflow->empty_text, - e_reflow->minimum_width / 2, - 0); - } else { - gtk_object_destroy(GTK_OBJECT(e_reflow->empty_text)); - e_reflow->empty_text = NULL; - } - } else { - if (e_reflow->empty_message) - e_reflow->empty_text = - gnome_canvas_item_new(GNOME_CANVAS_GROUP(e_reflow), - e_text_get_type(), - "anchor", GTK_ANCHOR_N, - "width", e_reflow->minimum_width, - "clip", TRUE, - "use_ellipsis", TRUE, - "font_gdk", GTK_WIDGET(GNOME_CANVAS_ITEM(e_reflow)->canvas)->style->font, - "fill_color", "black", - "justification", GTK_JUSTIFY_CENTER, - "text", e_reflow->empty_message, - "draw_background", FALSE, - NULL); - e_canvas_item_move_absolute(e_reflow->empty_text, - e_reflow->minimum_width / 2, - 0); - } - } else { - if (e_reflow->empty_text) { - gtk_object_destroy(GTK_OBJECT(e_reflow->empty_text)); - e_reflow->empty_text = NULL; +static int +e_reflow_selection_event_real (EReflow *reflow, GnomeCanvasItem *item, GdkEvent *event) +{ + int row; + int 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); + e_selection_model_do_something(reflow->selection, row, 0, event->button.state); + break; + case 3: + row = er_find_item (reflow, item); + e_selection_model_maybe_do_something(reflow->selection, row, 0, event->button.state); + break; + default: + return_val = FALSE; + break; } + 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_reflow( GnomeCanvasItem *item, int flags ) +e_reflow_class_init (EReflowClass *klass) { - EReflow *e_reflow = E_REFLOW(item); - if ( GTK_OBJECT_FLAGS( e_reflow ) & GNOME_CANVAS_ITEM_REALIZED ) { + GtkObjectClass *object_class; + GnomeCanvasItemClass *item_class; - gdouble old_width; - gdouble running_width; + object_class = (GtkObjectClass*) klass; + item_class = (GnomeCanvasItemClass *) klass; - _reflow (e_reflow); - - old_width = e_reflow->width; - - running_width = E_REFLOW_BORDER_WIDTH; + parent_class = gtk_type_class (gnome_canvas_group_get_type ()); + + gtk_object_add_arg_type ("EReflow::minimum_width", GTK_TYPE_DOUBLE, + GTK_ARG_READWRITE, ARG_MINIMUM_WIDTH); + gtk_object_add_arg_type ("EReflow::width", GTK_TYPE_DOUBLE, + GTK_ARG_READABLE, ARG_WIDTH); + gtk_object_add_arg_type ("EReflow::height", GTK_TYPE_DOUBLE, + GTK_ARG_READWRITE, ARG_HEIGHT); + gtk_object_add_arg_type ("EReflow::empty_message", GTK_TYPE_STRING, + GTK_ARG_READWRITE, ARG_EMPTY_MESSAGE); + gtk_object_add_arg_type ("EReflow::model", E_REFLOW_MODEL_TYPE, + GTK_ARG_READWRITE, ARG_MODEL); + + signals [SELECTION_EVENT] = + gtk_signal_new ("selection_event", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (EReflowClass, selection_event), + e_marshal_INT__OBJECT_POINTER, + GTK_TYPE_INT, 2, GTK_TYPE_OBJECT, GTK_TYPE_GDK_EVENT); + + gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL); + + object_class->set_arg = e_reflow_set_arg; + object_class->get_arg = e_reflow_get_arg; + object_class->destroy = e_reflow_destroy; + + /* 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; +} - if (e_reflow->items == NULL) { - } else { - GList *list; - GList *next_column; - gdouble item_height; - gdouble running_height; +static void +e_reflow_init (EReflow *reflow) +{ + reflow->model = NULL; + reflow->items = NULL; + reflow->heights = NULL; + reflow->count = 0; - running_height = E_REFLOW_BORDER_WIDTH; - - list = e_reflow->items; - gtk_object_get (GTK_OBJECT(list->data), - "height", &item_height, - NULL); - e_canvas_item_move_absolute(GNOME_CANVAS_ITEM(list->data), - (double) running_width, - (double) running_height); - running_height += item_height + E_REFLOW_BORDER_WIDTH; - next_column = g_list_next(e_reflow->columns); - list = g_list_next(list); - - for( ; list; list = g_list_next(list)) { - gtk_object_get (GTK_OBJECT(list->data), - "height", &item_height, - NULL); + reflow->columns = NULL; + reflow->column_count = 0; - if (next_column && (next_column->data == list)) { - next_column = g_list_next (next_column); - running_height = E_REFLOW_BORDER_WIDTH; - running_width += e_reflow->column_width + E_REFLOW_BORDER_WIDTH + E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH; - } - e_canvas_item_move_absolute(GNOME_CANVAS_ITEM(list->data), - (double) running_width, - (double) running_height); + reflow->empty_text = NULL; + reflow->empty_message = NULL; - running_height += item_height + E_REFLOW_BORDER_WIDTH; - } - - } - e_reflow->width = running_width + e_reflow->column_width + E_REFLOW_BORDER_WIDTH; - if ( e_reflow->width < e_reflow->minimum_width ) - e_reflow->width = e_reflow->minimum_width; - if (old_width != e_reflow->width) - e_canvas_item_request_parent_reflow(item); - } -} + reflow->minimum_width = 10; + reflow->width = 10; + reflow->height = 10; -void -e_reflow_add_item(EReflow *e_reflow, GnomeCanvasItem *item, gint *position) -{ - if (E_REFLOW_CLASS(GTK_OBJECT(e_reflow)->klass)->add_item) - (E_REFLOW_CLASS(GTK_OBJECT(e_reflow)->klass)->add_item) (e_reflow, item, position); + reflow->column_width = 150; + + reflow->column_drag = FALSE; + + reflow->need_height_update = FALSE; + reflow->need_column_resize = FALSE; + + reflow->default_cursor_shown = TRUE; + reflow->arrow_cursor = NULL; + reflow->default_cursor = NULL; + + reflow->incarnate_idle_id = 0; + + reflow->selection = E_SELECTION_MODEL (e_selection_model_simple_new()); + reflow->sorter = e_sorter_array_new (er_compare, reflow); + + gtk_object_set (GTK_OBJECT (reflow->selection), + "sorter", reflow->sorter, + NULL); + + reflow->selection_changed_id = + gtk_signal_connect(GTK_OBJECT(reflow->selection), "selection_changed", + GTK_SIGNAL_FUNC(selection_changed), reflow); + + e_canvas_item_set_reflow_callback(GNOME_CANVAS_ITEM(reflow), e_reflow_reflow); } -void -e_reflow_post_add_item(EReflow *e_reflow, GnomeCanvasItem *item) +GtkType +e_reflow_get_type (void) { - set_empty(e_reflow); + static GtkType type = 0; + + if (!type) { + static const GtkTypeInfo info = { + "EReflow", + sizeof (EReflow), + sizeof (EReflowClass), + (GtkClassInitFunc) e_reflow_class_init, + (GtkObjectInitFunc) e_reflow_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + type = gtk_type_unique (gnome_canvas_group_get_type (), &info); + } + + return type; } diff --git a/widgets/misc/e-reflow.h b/widgets/misc/e-reflow.h index 641a4af0e5..9ecbb5c529 100644 --- a/widgets/misc/e-reflow.h +++ b/widgets/misc/e-reflow.h @@ -22,6 +22,9 @@ #define __E_REFLOW_H__ #include +#include +#include +#include #ifdef __cplusplus extern "C" { @@ -46,6 +49,8 @@ extern "C" { #define E_IS_REFLOW_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_REFLOW_TYPE)) +typedef struct EReflowPriv EReflowPriv; + typedef struct _EReflow EReflow; typedef struct _EReflowClass EReflowClass; @@ -54,30 +59,50 @@ struct _EReflow GnomeCanvasGroup parent; /* item specific fields */ - GList *items; /* Of type GnomeCanvasItem */ - GList *columns; /* Of type GList of type GnomeCanvasItem (points into items) */ + EReflowModel *model; + guint model_changed_id; + guint model_items_inserted_id; + guint model_item_changed_id; + + ESelectionModel *selection; + guint selection_changed_id; + ESorterArray *sorter; + + GtkAdjustment *adjustment; + guint adjustment_changed_id; + guint adjustment_value_changed_id; + guint set_scroll_adjustments_id; + + int *heights; + GnomeCanvasItem **items; + int count; + int allocated_count; + + int *columns; gint column_count; /* Number of columnns */ GnomeCanvasItem *empty_text; gchar *empty_message; - + double minimum_width; double width; double height; - + double column_width; - int idle; + int incarnate_idle_id; /* These are all for when the column is being dragged. */ - gboolean column_drag; gdouble start_x; gint which_column_dragged; double temp_column_width; double previous_temp_column_width; + guint column_drag : 1; + guint need_height_update : 1; guint need_column_resize : 1; + guint need_reflow_columns : 1; guint default_cursor_shown : 1; GdkCursor *arrow_cursor; @@ -88,8 +113,7 @@ struct _EReflowClass { GnomeCanvasGroupClass parent_class; - /* Virtual methods. */ - void (* add_item) (EReflow *reflow, GnomeCanvasItem *item, gint *position); + int (*selection_event) (EReflow *reflow, GnomeCanvasItem *item, GdkEvent *event); }; /* @@ -98,18 +122,10 @@ struct _EReflowClass * should also do an ECanvas parent reflow request if its size * changes. */ -void e_reflow_add_item (EReflow *e_reflow, - GnomeCanvasItem *item, - gint *position); GtkType e_reflow_get_type (void); -/* Internal usage only: */ -void e_reflow_post_add_item (EReflow *e_reflow, - GnomeCanvasItem *item); - #ifdef __cplusplus } #endif /* __cplusplus */ - #endif /* __E_REFLOW_H__ */ -- cgit v1.2.3