diff options
Diffstat (limited to 'e-util/e-reflow.c')
-rw-r--r-- | e-util/e-reflow.c | 1732 |
1 files changed, 1732 insertions, 0 deletions
diff --git a/e-util/e-reflow.c b/e-util/e-reflow.c new file mode 100644 index 0000000000..0df0aad5f8 --- /dev/null +++ b/e-util/e-reflow.c @@ -0,0 +1,1732 @@ +/* -*- 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) + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-reflow.h" + +#include <math.h> +#include <string.h> + +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> + +#include "e-canvas-utils.h" +#include "e-canvas.h" +#include "e-marshal.h" +#include "e-selection-model-simple.h" +#include "e-text.h" +#include "e-unicode.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, cairo_t *cr, + gint x, gint y, gint width, gint height); +static void e_reflow_update (GnomeCanvasItem *item, const cairo_matrix_t *i2c, gint flags); +static GnomeCanvasItem *e_reflow_point (GnomeCanvasItem *item, gdouble x, gdouble y, gint cx, gint cy); +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) + +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 GHashTable * +er_create_cmp_cache (gpointer user_data) +{ + EReflow *reflow = user_data; + return e_reflow_model_create_cmp_cache (reflow->model); +} + +static gint +er_compare (gint i1, + gint i2, + GHashTable *cmp_cache, + gpointer user_data) +{ + EReflow *reflow = user_data; + return e_reflow_model_compare (reflow->model, i1, i2, cmp_cache); +} + +static gint +e_reflow_pick_line (EReflow *reflow, + gdouble 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", (gdouble) 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", (gdouble) 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; + GtkLayout *layout; + GtkAdjustment *adjustment; + gdouble page_size; + gdouble value, min_value, max_value; + EReflow *reflow = user_data; + + row = reflow->cursor_row; + if (row == -1) + return FALSE; + + layout = GTK_LAYOUT (GNOME_CANVAS_ITEM (reflow)->canvas); + adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); + + value = gtk_adjustment_get_value (adjustment); + page_size = gtk_adjustment_get_page_size (adjustment); + + if ((!reflow->items) || (!reflow->items[row])) + return TRUE; + min_value = reflow->items[row]->x2 - page_size; + max_value = reflow->items[row]->x1; + + if (value < min_value) + value = min_value; + + if (value > max_value) + value = max_value; + + if (value != gtk_adjustment_get_value (adjustment)) + gtk_adjustment_set_value (adjustment, value); + + 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", (gdouble) 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; + GtkLayout *layout; + GtkAdjustment *adjustment; + gdouble value; + gdouble page_size; + + layout = GTK_LAYOUT (GNOME_CANVAS_ITEM (reflow)->canvas); + adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); + + value = gtk_adjustment_get_value (adjustment); + page_size = gtk_adjustment_get_page_size (adjustment); + + column_width = reflow->column_width; + + first_column = value - 1 + E_REFLOW_BORDER_WIDTH; + first_column /= column_width + E_REFLOW_FULL_GUTTER; + + last_column = value + 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", (gdouble) 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; + gdouble 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 (; list && 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]) + g_object_run_dispose (G_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]) + g_object_run_dispose (G_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 { + g_object_run_dispose (G_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 (), + "width", reflow->minimum_width, + "clip", TRUE, + "use_ellipsis", TRUE, + "justification", GTK_JUSTIFY_CENTER, + "text", reflow->empty_message, + NULL); + e_canvas_item_move_absolute ( + reflow->empty_text, + reflow->minimum_width / 2, + 0); + } + } + } else { + if (reflow->empty_text) { + g_object_run_dispose (G_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 = g_object_ref (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 = g_object_ref (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); +} + +#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 property_id, + const GValue *value, + GParamSpec *pspec) +{ + GnomeCanvasItem *item; + EReflow *reflow; + + item = GNOME_CANVAS_ITEM (object); + reflow = E_REFLOW (object); + + switch (property_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 (item->flags & GNOME_CANVAS_ITEM_REALIZED) + 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 (item->flags & GNOME_CANVAS_ITEM_REALIZED) + 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)) { + GtkLayout *layout; + GtkAdjustment *adjustment; + gdouble old_width = reflow->column_width; + gdouble step_increment; + gdouble page_size; + + layout = GTK_LAYOUT (item->canvas); + adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); + page_size = gtk_adjustment_get_page_size (adjustment); + + reflow->column_width = g_value_get_double (value); + step_increment = (reflow->column_width + + E_REFLOW_FULL_GUTTER) / 2; + gtk_adjustment_set_step_increment ( + adjustment, step_increment); + gtk_adjustment_set_page_increment ( + adjustment, page_size - step_increment); + 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 property_id, + GValue *value, + GParamSpec *pspec) +{ + EReflow *reflow; + + reflow = E_REFLOW (object); + + switch (property_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, property_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; + gdouble page_increment; + gdouble step_increment; + gdouble page_size; + 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_scrollable_get_hadjustment (GTK_SCROLLABLE (item->canvas)); + +#if 0 + connect_set_adjustment (reflow); +#endif + connect_adjustment (reflow, adjustment); + + page_size = gtk_adjustment_get_page_size (adjustment); + step_increment = (reflow->column_width + E_REFLOW_FULL_GUTTER) / 2; + page_increment = page_size - step_increment; + gtk_adjustment_set_step_increment (adjustment, step_increment); + gtk_adjustment_set_page_increment (adjustment, page_increment); +} + +static void +e_reflow_unrealize (GnomeCanvasItem *item) +{ + EReflow *reflow; + + reflow = E_REFLOW (item); + + g_object_unref (reflow->arrow_cursor); + g_object_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_KEY_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; + gdouble 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->device, + button->time); + + reflow->previous_temp_column_width = -1; + reflow->need_column_resize = TRUE; + gnome_canvas_item_request_update (item); + return TRUE; + } + } + break; + case 4: + { + GtkLayout *layout; + GtkAdjustment *adjustment; + gdouble new_value; + + layout = GTK_LAYOUT (item->canvas); + adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); + new_value = gtk_adjustment_get_value (adjustment); + new_value -= gtk_adjustment_get_step_increment (adjustment); + gtk_adjustment_set_value (adjustment, new_value); + } + break; + case 5: + { + GtkLayout *layout; + GtkAdjustment *adjustment; + gdouble new_value; + gdouble page_size; + gdouble upper; + + layout = GTK_LAYOUT (item->canvas); + adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); + new_value = gtk_adjustment_get_value (adjustment); + new_value += gtk_adjustment_get_step_increment (adjustment); + upper = gtk_adjustment_get_upper (adjustment); + page_size = gtk_adjustment_get_page_size (adjustment); + if (new_value > upper - page_size) + new_value = upper - 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; + GtkLayout *layout; + gdouble value; + + layout = GTK_LAYOUT (item->canvas); + adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); + value = gtk_adjustment_get_value (adjustment); + + reflow->temp_column_width = reflow->column_width + + (button->x - reflow->start_x) / (reflow->which_column_dragged - e_reflow_pick_line (reflow, value)); + if (reflow->temp_column_width < 50) + reflow->temp_column_width = 50; + reflow->column_drag = FALSE; + if (old_width != reflow->temp_column_width) { + gdouble page_increment; + gdouble step_increment; + gdouble page_size; + + page_size = gtk_adjustment_get_page_size (adjustment); + gtk_adjustment_set_value (adjustment, value + e_reflow_pick_line (reflow, value) * (reflow->temp_column_width - reflow->column_width)); + reflow->column_width = reflow->temp_column_width; + step_increment = (reflow->column_width + E_REFLOW_FULL_GUTTER) / 2; + page_increment = page_size - step_increment; + gtk_adjustment_set_step_increment (adjustment, step_increment); + gtk_adjustment_set_page_increment (adjustment, page_increment); + 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) { + gdouble old_width = reflow->temp_column_width; + GdkEventMotion *motion = (GdkEventMotion *) event; + GtkAdjustment *adjustment; + GtkLayout *layout; + gdouble value; + + layout = GTK_LAYOUT (item->canvas); + adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); + value = gtk_adjustment_get_value (adjustment); + + reflow->temp_column_width = reflow->column_width + + (motion->x - reflow->start_x) / (reflow->which_column_dragged - e_reflow_pick_line (reflow, 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; + GdkWindow *window; + gdouble 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)); + + window = gtk_widget_get_window (GTK_WIDGET (item->canvas)); + + 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 (window, reflow->arrow_cursor); + reflow->default_cursor_shown = FALSE; + } + } else + if (!reflow->default_cursor_shown) { + gdk_window_set_cursor (window, reflow->default_cursor); + reflow->default_cursor_shown = TRUE; + } + + } + break; + case GDK_ENTER_NOTIFY: + if (!reflow->column_drag) { + GdkEventCrossing *crossing = (GdkEventCrossing *) event; + GdkWindow *window; + gdouble 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)); + + window = gtk_widget_get_window (GTK_WIDGET (item->canvas)); + + 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 (window, reflow->arrow_cursor); + reflow->default_cursor_shown = FALSE; + } + } + } + break; + case GDK_LEAVE_NOTIFY: + if (!reflow->column_drag) { + GdkEventCrossing *crossing = (GdkEventCrossing *) event; + GdkWindow *window; + gdouble 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)); + + window = gtk_widget_get_window (GTK_WIDGET (item->canvas)); + + 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 (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, + cairo_t *cr, + gint x, + gint y, + gint width, + gint height) +{ + GtkStyleContext *style_context; + GtkWidget *widget; + gint x_rect, y_rect, width_rect, height_rect; + gdouble running_width; + EReflow *reflow = E_REFLOW (item); + GdkRGBA color; + gint i; + gdouble column_width; + + if (GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->draw) + GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->draw (item, cr, x, y, width, height); + column_width = reflow->column_width; + 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); + + /* Compute first column to draw. */ + i = x; + i /= column_width + E_REFLOW_FULL_GUTTER; + running_width += i * (column_width + E_REFLOW_FULL_GUTTER); + + widget = GTK_WIDGET (item->canvas); + style_context = gtk_widget_get_style_context (widget); + + cairo_save (cr); + + gtk_style_context_get_background_color ( + style_context, GTK_STATE_FLAG_ACTIVE, &color); + gdk_cairo_set_source_rgba (cr, &color); + + for (; i < reflow->column_count; i++) { + if (running_width > x + width) + break; + x_rect = running_width; + + gtk_render_background ( + style_context, cr, + (gdouble) x_rect - x, + (gdouble) y_rect - y, + (gdouble) width_rect, + (gdouble) height_rect); + + running_width += E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH; + } + + cairo_restore (cr); + + if (reflow->column_drag) { + GtkAdjustment *adjustment; + GtkLayout *layout; + gdouble value; + gint start_line; + + layout = GTK_LAYOUT (item->canvas); + adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); + value = gtk_adjustment_get_value (adjustment); + + start_line = e_reflow_pick_line (reflow, 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; + 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); + + cairo_save (cr); + + gtk_style_context_get_color ( + style_context, GTK_STATE_FLAG_NORMAL, &color); + gdk_cairo_set_source_rgba (cr, &color); + + for (; i < reflow->column_count; i++) { + if (running_width > x + width) + break; + x_rect = running_width; + cairo_rectangle ( + cr, + x_rect - x, + y_rect - y, + width_rect - 1, + height_rect - 1); + cairo_fill (cr); + running_width += E_REFLOW_DIVIDER_WIDTH + E_REFLOW_BORDER_WIDTH + column_width + E_REFLOW_BORDER_WIDTH; + } + + cairo_restore (cr); + } +} + +static void +e_reflow_update (GnomeCanvasItem *item, + const cairo_matrix_t *i2c, + gint flags) +{ + EReflow *reflow; + gdouble 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, i2c, 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) { + GtkLayout *layout; + GtkAdjustment *adjustment; + gint x_rect, y_rect, width_rect, height_rect; + gint start_line; + gdouble running_width; + gint i; + gdouble column_width; + gdouble value; + + layout = GTK_LAYOUT (item->canvas); + adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (layout)); + value = gtk_adjustment_get_value (adjustment); + start_line = e_reflow_pick_line (reflow, value); + + 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 GnomeCanvasItem * +e_reflow_point (GnomeCanvasItem *item, + gdouble x, + gdouble y, + gint cx, + gint cy) +{ + GnomeCanvasItem *child = NULL; + + if (GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->point) + child = GNOME_CANVAS_ITEM_CLASS (e_reflow_parent_class)->point (item, x, y, cx, cy); + + return child ? child : item; +#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 (!(item->flags & 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]), + (gdouble) running_width, + (gdouble) 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 *class) +{ + GObjectClass *object_class; + GnomeCanvasItemClass *item_class; + + object_class = (GObjectClass *) class; + item_class = (GnomeCanvasItemClass *) class; + + 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; + + class->selection_event = e_reflow_selection_event_real; + class->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_TYPE_REFLOW_MODEL, + 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 | G_SIGNAL_TYPE_STATIC_SCOPE); + + 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_create_cmp_cache, 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); +} |