aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/text/e-reflow.c
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/text/e-reflow.c')
-rw-r--r--widgets/text/e-reflow.c1531
1 files changed, 1531 insertions, 0 deletions
diff --git a/widgets/text/e-reflow.c b/widgets/text/e-reflow.c
new file mode 100644
index 0000000000..705678bbf8
--- /dev/null
+++ b/widgets/text/e-reflow.c
@@ -0,0 +1,1531 @@
+/* -*- 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);
+}