aboutsummaryrefslogtreecommitdiffstats
path: root/composer/e-icon-list.c
diff options
context:
space:
mode:
authorIain Holmes <iain@src.gnome.org>2000-10-01 11:43:17 +0800
committerIain Holmes <iain@src.gnome.org>2000-10-01 11:43:17 +0800
commit9d6c76ce37be81f83d3df0cf2bfc8d81adfa18e8 (patch)
tree06d938871ce13b0fbc66a5396c89d9aa46490b6d /composer/e-icon-list.c
parent711bef6798d08dcb34bc83730f11575ffa411d07 (diff)
downloadgsoc2013-evolution-9d6c76ce37be81f83d3df0cf2bfc8d81adfa18e8.tar
gsoc2013-evolution-9d6c76ce37be81f83d3df0cf2bfc8d81adfa18e8.tar.gz
gsoc2013-evolution-9d6c76ce37be81f83d3df0cf2bfc8d81adfa18e8.tar.bz2
gsoc2013-evolution-9d6c76ce37be81f83d3df0cf2bfc8d81adfa18e8.tar.lz
gsoc2013-evolution-9d6c76ce37be81f83d3df0cf2bfc8d81adfa18e8.tar.xz
gsoc2013-evolution-9d6c76ce37be81f83d3df0cf2bfc8d81adfa18e8.tar.zst
gsoc2013-evolution-9d6c76ce37be81f83d3df0cf2bfc8d81adfa18e8.zip
The big eye-candy commit.
Make thumbnails out of image attachments. svn path=/trunk/; revision=5648
Diffstat (limited to 'composer/e-icon-list.c')
-rw-r--r--composer/e-icon-list.c2657
1 files changed, 2657 insertions, 0 deletions
diff --git a/composer/e-icon-list.c b/composer/e-icon-list.c
new file mode 100644
index 0000000000..114675eddd
--- /dev/null
+++ b/composer/e-icon-list.c
@@ -0,0 +1,2657 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+
+/*
+ * GnomeIconList widget - scrollable icon list
+ *
+ * Authors:
+ * Federico Mena <federico@nuclecu.unam.mx>
+ * Miguel de Icaza <miguel@nuclecu.unam.mx>
+ *
+ * Rewrote from scratch from the code written by Federico Mena
+ * <federico@nuclecu.unam.mx> to be based on a GnomeCanvas, and
+ * to support banding selection and allow inline icon renaming.
+ *
+ * Redone somewhat by Elliot to support gdk-pixbuf, and to use GArray instead of GList for item storage.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdio.h>
+#include <gtk/gtk.h>
+#include "e-icon-list.h"
+#include <libgnomeui/gnome-icon-item.h>
+#include <libgnomeui/gnome-canvas-pixbuf.h>
+#include <libgnomeui/gnome-canvas-rect-ellipse.h>
+
+
+/* Aliases to minimize screen use in my laptop */
+#define EIL(x) E_ICON_LIST(x)
+#define EIL_CLASS(x) E_ICON_LIST_CLASS(x)
+#define IS_EIL(x) E_IS_ICON_LIST(x)
+
+typedef EIconList Eil;
+typedef EIconListClass EilClass;
+
+
+/* default spacings */
+#define DEFAULT_ROW_SPACING 4
+#define DEFAULT_COL_SPACING 2
+#define DEFAULT_TEXT_SPACING 2
+#define DEFAULT_ICON_BORDER 2
+
+/* Autoscroll timeout in milliseconds */
+#define SCROLL_TIMEOUT 30
+
+
+/* Signals */
+enum {
+ SELECT_ICON,
+ UNSELECT_ICON,
+ TEXT_CHANGED,
+ LAST_SIGNAL
+};
+
+typedef enum {
+ SYNC_INSERT,
+ SYNC_REMOVE
+} SyncType;
+
+enum {
+ ARG_0,
+};
+
+static guint eil_signals[LAST_SIGNAL] = { 0 };
+
+
+static GnomeCanvasClass *parent_class;
+
+
+/* Icon structure */
+typedef struct {
+ /* Icon image and text items */
+ GnomeCanvasPixbuf *image;
+ GnomeIconTextItem *text;
+
+ /* Filename of the icon file. */
+ gchar *icon_filename;
+
+ /* User data and destroy notify function */
+ gpointer data;
+ GtkDestroyNotify destroy;
+
+ /* ID for the text item's event signal handler */
+ guint text_event_id;
+
+ /* Whether the icon is selected, and temporary storage for rubberband
+ * selections.
+ */
+ guint selected : 1;
+ guint tmp_selected : 1;
+} Icon;
+
+/* A row of icons */
+typedef struct {
+ GList *line_icons;
+ gint16 y;
+ gint16 icon_height, text_height;
+} IconLine;
+
+/* Private data of the EIconList structure */
+struct _EIconListPrivate {
+ /* List of icons */
+ GArray *icon_list;
+
+ /* List of rows of icons */
+ GList *lines;
+
+ /* Separators used to wrap the text below icons */
+ char *separators;
+
+ Icon *last_selected_icon;
+
+ /* Rubberband rectangle */
+ GnomeCanvasItem *sel_rect;
+
+ /* Saved event for a pending selection */
+ GdkEvent select_pending_event;
+
+ /* Max of the height of all the icon rows and window height */
+ int total_height;
+
+ /* Selection mode */
+ GtkSelectionMode selection_mode;
+
+ /* A list of integers with the indices of the currently selected icons */
+ GList *selection;
+
+ /* Number of icons in the list */
+ int icons;
+
+ /* Freeze count */
+ int frozen;
+
+ /* Width allocated for icons */
+ int icon_width;
+
+ /* Spacing values */
+ int row_spacing;
+ int col_spacing;
+ int text_spacing;
+ int icon_border;
+
+ /* Index and pointer to last selected icon */
+ int last_selected_idx;
+
+ /* Timeout ID for autoscrolling */
+ guint timer_tag;
+
+ /* Change the adjustment value by this amount when autoscrolling */
+ int value_diff;
+
+ /* Mouse position for autoscrolling */
+ int event_last_x;
+ int event_last_y;
+
+ /* Selection start position */
+ int sel_start_x;
+ int sel_start_y;
+
+ /* Modifier state when the selection began */
+ guint sel_state;
+
+ /* Whether the icon texts are editable */
+ guint is_editable : 1;
+
+ /* Whether the icon texts need to be copied */
+ guint static_text : 1;
+
+ /* Whether the icons need to be laid out */
+ guint dirty : 1;
+
+ /* Whether the user is performing a rubberband selection */
+ guint selecting : 1;
+
+ /* Whether editing an icon is pending after a button press */
+ guint edit_pending : 1;
+
+ /* Whether selection is pending after a button press */
+ guint select_pending : 1;
+
+ /* Whether the icon that is pending selection was selected to begin with */
+ guint select_pending_was_selected : 1;
+};
+
+
+static inline int
+icon_line_height (Eil *eil, IconLine *il)
+{
+ EIconListPrivate *priv;
+
+ priv = eil->_priv;
+
+ return il->icon_height + il->text_height + priv->row_spacing + priv->text_spacing;
+}
+
+static void
+icon_get_height (Icon *icon, int *icon_height, int *text_height)
+{
+ double d_icon_height;
+ gtk_object_get(GTK_OBJECT(icon->image), "height", &d_icon_height, NULL);
+ *icon_height = d_icon_height;
+ *text_height = icon->text->ti->height;
+}
+
+static int
+eil_get_items_per_line (Eil *eil)
+{
+ EIconListPrivate *priv;
+ int items_per_line;
+
+ priv = eil->_priv;
+
+ items_per_line = GTK_WIDGET (eil)->allocation.width / (priv->icon_width + priv->col_spacing);
+ if (items_per_line == 0)
+ items_per_line = 1;
+
+ return items_per_line;
+}
+
+/**
+ * e_icon_list_get_items_per_line:
+ * @eil: An icon list.
+ *
+ * Returns the number of icons that fit in a line or row.
+ */
+int
+e_icon_list_get_items_per_line (EIconList *eil)
+{
+ g_return_val_if_fail (eil != NULL, 1);
+ g_return_val_if_fail (IS_EIL (eil), 1);
+
+ return eil_get_items_per_line (eil);
+}
+
+static void
+eil_place_icon (Eil *eil, Icon *icon, int x, int y, int icon_height)
+{
+ EIconListPrivate *priv;
+ int x_offset, y_offset;
+ double d_icon_image_height;
+ double d_icon_image_width;
+ int icon_image_height;
+ int icon_image_width;
+
+ priv = eil->_priv;
+
+ gtk_object_get(GTK_OBJECT(icon->image), "height", &d_icon_image_height, NULL);
+ icon_image_height = d_icon_image_height;
+ g_assert(icon_image_height != 0);
+ if (icon_height > icon_image_height)
+ y_offset = (icon_height - icon_image_height) / 2;
+ else
+ y_offset = 0;
+
+ gtk_object_get(GTK_OBJECT(icon->image), "width", &d_icon_image_width, NULL);
+ icon_image_width = d_icon_image_width;
+ g_assert(icon_image_width != 0);
+ if (priv->icon_width > icon_image_width)
+ x_offset = (priv->icon_width - icon_image_width) / 2;
+ else
+ x_offset = 0;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (icon->image),
+ "x", (double) (x + x_offset),
+ "y", (double) (y + y_offset),
+ NULL);
+ gnome_icon_text_item_setxy (icon->text,
+ x,
+ y + icon_height + priv->text_spacing);
+}
+
+static void
+eil_layout_line (Eil *eil, IconLine *il)
+{
+ EIconListPrivate *priv;
+ GList *l;
+ int x;
+
+ priv = eil->_priv;
+
+ x = 0;
+ for (l = il->line_icons; l; l = l->next) {
+ Icon *icon = l->data;
+
+ eil_place_icon (eil, icon, x, il->y, il->icon_height);
+ x += priv->icon_width + priv->col_spacing;
+ }
+}
+
+static void
+eil_add_and_layout_line (Eil *eil, GList *line_icons, int y,
+ int icon_height, int text_height)
+{
+ EIconListPrivate *priv;
+ IconLine *il;
+
+ priv = eil->_priv;
+
+ il = g_new (IconLine, 1);
+ il->line_icons = line_icons;
+ il->y = y;
+ il->icon_height = icon_height;
+ il->text_height = text_height;
+
+ eil_layout_line (eil, il);
+ priv->lines = g_list_append (priv->lines, il);
+}
+
+static void
+eil_relayout_icons_at (Eil *eil, int pos, int y)
+{
+ EIconListPrivate *priv;
+ int col, row, text_height, icon_height;
+ int items_per_line, n;
+ GList *line_icons;
+
+ priv = eil->_priv;
+ items_per_line = eil_get_items_per_line (eil);
+
+ col = row = text_height = icon_height = 0;
+ line_icons = NULL;
+
+ for (n = pos; n < priv->icon_list->len; n++) {
+ Icon *icon = g_array_index(priv->icon_list, Icon*, n);
+ int ih, th;
+
+ if (!(n % items_per_line)) {
+ if (line_icons) {
+ eil_add_and_layout_line (eil, line_icons, y,
+ icon_height, text_height);
+ line_icons = NULL;
+
+ y += (icon_height + text_height
+ + priv->row_spacing + priv->text_spacing);
+ }
+
+ icon_height = 0;
+ text_height = 0;
+ }
+
+ icon_get_height (icon, &ih, &th);
+
+ icon_height = MAX (ih, icon_height);
+ text_height = MAX (th, text_height);
+
+ line_icons = g_list_append (line_icons, icon);
+ }
+
+ if (line_icons)
+ eil_add_and_layout_line (eil, line_icons, y, icon_height, text_height);
+}
+
+static void
+eil_free_line_info (Eil *eil)
+{
+ EIconListPrivate *priv;
+ GList *l;
+
+ priv = eil->_priv;
+
+ for (l = priv->lines; l; l = l->next) {
+ IconLine *il = l->data;
+
+ g_list_free (il->line_icons);
+ g_free (il);
+ }
+
+ g_list_free (priv->lines);
+ priv->lines = NULL;
+ priv->total_height = 0;
+}
+
+static void
+eil_free_line_info_from (Eil *eil, int first_line)
+{
+ EIconListPrivate *priv;
+ GList *l, *ll;
+
+ priv = eil->_priv;
+ ll = g_list_nth (priv->lines, first_line);
+
+ for (l = ll; l; l = l->next) {
+ IconLine *il = l->data;
+
+ g_list_free (il->line_icons);
+ g_free (il);
+ }
+
+ if (priv->lines) {
+ if (ll->prev)
+ ll->prev->next = NULL;
+ else
+ priv->lines = NULL;
+ }
+
+ g_list_free (ll);
+}
+
+static void
+eil_layout_from_line (Eil *eil, int line)
+{
+ EIconListPrivate *priv;
+ GList *l;
+ int height;
+
+ priv = eil->_priv;
+
+ eil_free_line_info_from (eil, line);
+
+ height = 0;
+ for (l = priv->lines; l; l = l->next) {
+ IconLine *il = l->data;
+
+ height += icon_line_height (eil, il);
+ }
+
+ eil_relayout_icons_at (eil, line * eil_get_items_per_line (eil), height);
+}
+
+static void
+eil_layout_all_icons (Eil *eil)
+{
+ EIconListPrivate *priv;
+
+ priv = eil->_priv;
+
+ if (!GTK_WIDGET_REALIZED (eil))
+ return;
+
+ eil_free_line_info (eil);
+ eil_relayout_icons_at (eil, 0, 0);
+ priv->dirty = FALSE;
+}
+
+static void
+eil_scrollbar_adjust (Eil *eil)
+{
+ EIconListPrivate *priv;
+ GtkAdjustment *adj;
+ GList *l;
+ double wx, wy, wx1, wy1, wx2, wy2;
+ int height, step_increment;
+
+ priv = eil->_priv;
+
+ if (!GTK_WIDGET_REALIZED (eil))
+ return;
+
+ height = 0;
+ step_increment = 0;
+ for (l = priv->lines; l; l = l->next) {
+ IconLine *il = l->data;
+
+ height += icon_line_height (eil, il);
+
+ if (l == priv->lines)
+ step_increment = height;
+ }
+
+ if (!step_increment)
+ step_increment = 10;
+
+ priv->total_height = MAX (height, GTK_WIDGET (eil)->allocation.height);
+
+ gnome_canvas_c2w (GNOME_CANVAS (eil), 0, 0, &wx1, &wy1);
+ gnome_canvas_c2w (GNOME_CANVAS (eil),
+ GTK_WIDGET (eil)->allocation.width,
+ priv->total_height,
+ &wx2, &wy2);
+
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (eil),
+ wx1, wy1, wx2, wy2);
+
+ wx = wy = 0;
+ gnome_canvas_window_to_world (GNOME_CANVAS (eil), 0, 0, &wx, &wy);
+
+ adj = gtk_layout_get_vadjustment (GTK_LAYOUT (eil));
+
+ adj->upper = priv->total_height;
+ adj->step_increment = step_increment;
+ adj->page_increment = GTK_WIDGET (eil)->allocation.height;
+ adj->page_size = GTK_WIDGET (eil)->allocation.height;
+
+ if (wy > adj->upper - adj->page_size)
+ wy = adj->upper - adj->page_size;
+
+ adj->value = wy;
+
+ gtk_adjustment_changed (adj);
+}
+
+/* Emits the select_icon or unselect_icon signals as appropriate */
+static void
+emit_select (Eil *eil, int sel, int i, GdkEvent *event)
+{
+ gtk_signal_emit (GTK_OBJECT (eil),
+ eil_signals[sel ? SELECT_ICON : UNSELECT_ICON],
+ i,
+ event);
+}
+
+static int
+eil_unselect_all (EIconList *eil, GdkEvent *event, gpointer keep)
+{
+ EIconListPrivate *priv;
+ Icon *icon;
+ int i, idx = 0;
+
+ g_return_val_if_fail (eil != NULL, 0);
+ g_return_val_if_fail (IS_EIL (eil), 0);
+
+ priv = eil->_priv;
+
+ for (i = 0; i < priv->icon_list->len; i++) {
+ icon = g_array_index(priv->icon_list, Icon*, i);
+
+ if (icon == keep)
+ idx = i;
+ else if (icon->selected)
+ emit_select (eil, FALSE, i, event);
+ }
+
+ return idx;
+}
+
+/**
+ * e_icon_list_unselect_all:
+ * @eil: An icon list.
+ *
+ * Returns: the number of icons in the icon list
+ */
+int
+e_icon_list_unselect_all (EIconList *eil)
+{
+ return eil_unselect_all (eil, NULL, NULL);
+}
+
+static void
+sync_selection (Eil *eil, int pos, SyncType type)
+{
+ GList *list;
+
+ for (list = eil->_priv->selection; list; list = list->next) {
+ if (GPOINTER_TO_INT (list->data) >= pos) {
+ int i = GPOINTER_TO_INT (list->data);
+
+ switch (type) {
+ case SYNC_INSERT:
+ list->data = GINT_TO_POINTER (i + 1);
+ break;
+
+ case SYNC_REMOVE:
+ list->data = GINT_TO_POINTER (i - 1);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+ }
+ }
+}
+
+static int
+eil_icon_to_index (Eil *eil, Icon *icon)
+{
+ EIconListPrivate *priv;
+ int n;
+
+ priv = eil->_priv;
+
+ for (n = 0; n < priv->icon_list->len; n++)
+ if (g_array_index(priv->icon_list, Icon*, n) == icon)
+ return n;
+
+ g_assert_not_reached ();
+ return -1; /* Shut up the compiler */
+}
+
+/* Event handler for icons when we are in SINGLE or BROWSE mode */
+static gint
+selection_one_icon_event (Eil *eil, Icon *icon, int idx, int on_text, GdkEvent *event)
+{
+ EIconListPrivate *priv;
+ GnomeIconTextItem *text;
+ int retval;
+
+ priv = eil->_priv;
+ retval = FALSE;
+
+ /* We use a separate variable and ref the object because it may be
+ * destroyed by one of the signal handlers.
+ */
+ text = icon->text;
+ gtk_object_ref (GTK_OBJECT (text));
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ priv->edit_pending = FALSE;
+ priv->select_pending = FALSE;
+
+ /* Ignore wheel mouse clicks for now */
+ if (event->button.button > 3)
+ break;
+
+ if (!icon->selected) {
+ eil_unselect_all (eil, NULL, NULL);
+ emit_select (eil, TRUE, idx, event);
+ } else {
+ if (priv->selection_mode == GTK_SELECTION_SINGLE
+ && (event->button.state & GDK_CONTROL_MASK))
+ emit_select (eil, FALSE, idx, event);
+ else if (on_text && priv->is_editable && event->button.button == 1)
+ priv->edit_pending = TRUE;
+ else
+ emit_select (eil, TRUE, idx, event);
+ }
+
+ retval = TRUE;
+ break;
+
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ /* Ignore wheel mouse clicks for now */
+ if (event->button.button > 3)
+ break;
+
+ emit_select (eil, TRUE, idx, event);
+ retval = TRUE;
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (priv->edit_pending) {
+ gnome_icon_text_item_start_editing (text);
+ priv->edit_pending = FALSE;
+ }
+
+ retval = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ /* If the click was on the text and we actually did something, stop the
+ * icon text item's own handler from executing.
+ */
+ if (on_text && retval)
+ gtk_signal_emit_stop_by_name (GTK_OBJECT (text), "event");
+
+ gtk_object_unref (GTK_OBJECT (text));
+
+ return retval;
+}
+
+/* Handles range selections when clicking on an icon */
+static void
+select_range (Eil *eil, Icon *icon, int idx, GdkEvent *event)
+{
+ EIconListPrivate *priv;
+ int a, b;
+ Icon *i;
+
+ priv = eil->_priv;
+
+ if (priv->last_selected_idx == -1) {
+ priv->last_selected_idx = idx;
+ priv->last_selected_icon = icon;
+ }
+
+ if (idx < priv->last_selected_idx) {
+ a = idx;
+ b = priv->last_selected_idx;
+ } else {
+ a = priv->last_selected_idx;
+ b = idx;
+ }
+
+ for (; a <= b; a++) {
+ i = g_array_index(priv->icon_list, Icon*, a);
+
+ if (!i->selected)
+ emit_select (eil, TRUE, a, NULL);
+ }
+
+ /* Actually notify the client of the event */
+ emit_select (eil, TRUE, idx, event);
+}
+
+/* Handles icon selection for MULTIPLE or EXTENDED selection modes */
+static void
+do_select_many (Eil *eil, Icon *icon, int idx, GdkEvent *event, int use_event)
+{
+ EIconListPrivate *priv;
+ int range, additive;
+
+ priv = eil->_priv;
+
+ range = (event->button.state & GDK_SHIFT_MASK) != 0;
+ additive = (event->button.state & GDK_CONTROL_MASK) != 0;
+
+ if (!additive) {
+ if (icon->selected)
+ eil_unselect_all (eil, NULL, icon);
+ else
+ eil_unselect_all (eil, NULL, NULL);
+ }
+
+ if (!range) {
+ if (additive)
+ emit_select (eil, !icon->selected, idx, use_event ? event : NULL);
+ else
+ emit_select (eil, TRUE, idx, use_event ? event : NULL);
+
+ priv->last_selected_idx = idx;
+ priv->last_selected_icon = icon;
+ } else
+ select_range (eil, icon, idx, use_event ? event : NULL);
+}
+
+/* Event handler for icons when we are in MULTIPLE or EXTENDED mode */
+static gint
+selection_many_icon_event (Eil *eil, Icon *icon, int idx, int on_text, GdkEvent *event)
+{
+ EIconListPrivate *priv;
+ GnomeIconTextItem *text;
+ int retval;
+ int additive, range;
+ int do_select;
+
+ priv = eil->_priv;
+ retval = FALSE;
+
+ /* We use a separate variable and ref the object because it may be
+ * destroyed by one of the signal handlers.
+ */
+ text = icon->text;
+ gtk_object_ref (GTK_OBJECT (text));
+
+ range = (event->button.state & GDK_SHIFT_MASK) != 0;
+ additive = (event->button.state & GDK_CONTROL_MASK) != 0;
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ priv->edit_pending = FALSE;
+ priv->select_pending = FALSE;
+
+ /* Ignore wheel mouse clicks for now */
+ if (event->button.button > 3)
+ break;
+
+ do_select = TRUE;
+
+ if (additive || range) {
+ if (additive && !range) {
+ priv->select_pending = TRUE;
+ priv->select_pending_event = *event;
+ priv->select_pending_was_selected = icon->selected;
+
+ /* We have to emit this so that the client will
+ * know about the click.
+ */
+ emit_select (eil, TRUE, idx, event);
+ do_select = FALSE;
+ }
+ } else if (icon->selected) {
+ priv->select_pending = TRUE;
+ priv->select_pending_event = *event;
+ priv->select_pending_was_selected = icon->selected;
+
+ if (on_text && priv->is_editable && event->button.button == 1)
+ priv->edit_pending = TRUE;
+
+ emit_select (eil, TRUE, idx, event);
+ do_select = FALSE;
+ }
+#if 0
+ } else if (icon->selected && on_text && priv->is_editable
+ && event->button.button == 1) {
+ priv->edit_pending = TRUE;
+ do_select = FALSE;
+ }
+#endif
+
+ if (do_select)
+ do_select_many (eil, icon, idx, event, TRUE);
+
+ retval = TRUE;
+ break;
+
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ /* Ignore wheel mouse clicks for now */
+ if (event->button.button > 3)
+ break;
+
+ emit_select (eil, TRUE, idx, event);
+ retval = TRUE;
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (priv->select_pending) {
+ icon->selected = priv->select_pending_was_selected;
+ do_select_many (eil, icon, idx, &priv->select_pending_event, FALSE);
+ priv->select_pending = FALSE;
+ retval = TRUE;
+ }
+
+ if (priv->edit_pending) {
+ gnome_icon_text_item_start_editing (text);
+ priv->edit_pending = FALSE;
+ retval = TRUE;
+ }
+#if 0
+ if (priv->select_pending) {
+ icon->selected = priv->select_pending_was_selected;
+ do_select_many (eil, icon, idx, &priv->select_pending_event);
+ priv->select_pending = FALSE;
+ retval = TRUE;
+ } else if (priv->edit_pending) {
+ gnome_icon_text_item_start_editing (text);
+ priv->edit_pending = FALSE;
+ retval = TRUE;
+ }
+#endif
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* If the click was on the text and we actually did something, stop the
+ * icon text item's own handler from executing.
+ */
+ if (on_text && retval)
+ gtk_signal_emit_stop_by_name (GTK_OBJECT (text), "event");
+
+ gtk_object_unref (GTK_OBJECT (text));
+
+ return retval;
+}
+
+/* Event handler for icons in the icon list */
+static gint
+icon_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data)
+{
+ Icon *icon;
+ Eil *eil;
+ EIconListPrivate *priv;
+ int idx;
+ int on_text;
+
+ icon = data;
+ eil = EIL (item->canvas);
+ priv = eil->_priv;
+ idx = eil_icon_to_index (eil, icon);
+ on_text = item == GNOME_CANVAS_ITEM (icon->text);
+
+ switch (priv->selection_mode) {
+ case GTK_SELECTION_SINGLE:
+ case GTK_SELECTION_BROWSE:
+ return selection_one_icon_event (eil, icon, idx, on_text, event);
+
+ case GTK_SELECTION_MULTIPLE:
+ case GTK_SELECTION_EXTENDED:
+ return selection_many_icon_event (eil, icon, idx, on_text, event);
+
+ default:
+ g_assert_not_reached ();
+ return FALSE; /* Shut up the compiler */
+ }
+}
+
+/* Handler for the editing_started signal of an icon text item. We block the
+ * event handler so that it will not be called while the text is being edited.
+ */
+static void
+editing_started (GnomeIconTextItem *iti, gpointer data)
+{
+ Icon *icon;
+
+ icon = data;
+ gtk_signal_handler_block (GTK_OBJECT (iti), icon->text_event_id);
+ eil_unselect_all (EIL (GNOME_CANVAS_ITEM (iti)->canvas), NULL, icon);
+}
+
+/* Handler for the editing_stopped signal of an icon text item. We unblock the
+ * event handler so that we can get events from it again.
+ */
+static void
+editing_stopped (GnomeIconTextItem *iti, gpointer data)
+{
+ Icon *icon;
+
+ icon = data;
+ gtk_signal_handler_unblock (GTK_OBJECT (iti), icon->text_event_id);
+}
+
+static gboolean
+text_changed (GnomeCanvasItem *item, Icon *icon)
+{
+ Eil *eil;
+ gboolean accept;
+ int idx;
+
+ eil = EIL (item->canvas);
+ accept = TRUE;
+
+ idx = eil_icon_to_index (eil, icon);
+ gtk_signal_emit (GTK_OBJECT (eil),
+ eil_signals[TEXT_CHANGED],
+ idx, gnome_icon_text_item_get_text (icon->text),
+ &accept);
+
+ return accept;
+}
+
+static void
+height_changed (GnomeCanvasItem *item, Icon *icon)
+{
+ Eil *eil;
+ EIconListPrivate *priv;
+ int n;
+
+ eil = EIL (item->canvas);
+ priv = eil->_priv;
+
+ if (!GTK_WIDGET_REALIZED (eil))
+ return;
+
+ if (priv->frozen) {
+ priv->dirty = TRUE;
+ return;
+ }
+
+ n = eil_icon_to_index (eil, icon);
+ eil_layout_from_line (eil, n / eil_get_items_per_line (eil));
+ eil_scrollbar_adjust (eil);
+}
+
+static Icon *
+icon_new_from_pixbuf (EIconList *eil, GdkPixbuf *im,
+ const char *icon_filename, const char *text)
+{
+ EIconListPrivate *priv;
+ GnomeCanvas *canvas;
+ GnomeCanvasGroup *group;
+ Icon *icon;
+
+ priv = eil->_priv;
+ canvas = GNOME_CANVAS (eil);
+ group = GNOME_CANVAS_GROUP (canvas->root);
+
+ icon = g_new0 (Icon, 1);
+
+ if (icon_filename)
+ icon->icon_filename = g_strdup (icon_filename);
+ else
+ icon->icon_filename = NULL;
+
+ g_print ("%d,%d\n", gdk_pixbuf_get_width (im), gdk_pixbuf_get_height (im));
+ icon->image = GNOME_CANVAS_PIXBUF (gnome_canvas_item_new (
+ group,
+ gnome_canvas_pixbuf_get_type (),
+ "x", 0.0,
+ "y", 0.0,
+ "width", (double) gdk_pixbuf_get_width (im),
+ "height", (double) gdk_pixbuf_get_height (im),
+ "pixbuf", im,
+ NULL));
+
+ icon->text = GNOME_ICON_TEXT_ITEM (gnome_canvas_item_new (
+ group,
+ gnome_icon_text_item_get_type (),
+ NULL));
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (icon->text),
+ "use_broken_event_handling", FALSE,
+ NULL);
+
+ gnome_icon_text_item_configure (icon->text,
+ 0, 0, priv->icon_width,
+ "-adobe-helvetica-medium-r-normal-*-*-120-*-*-p-*-iso8859-1",
+ text, priv->is_editable, priv->static_text);
+
+ gtk_signal_connect (GTK_OBJECT (icon->image), "event",
+ GTK_SIGNAL_FUNC (icon_event),
+ icon);
+ icon->text_event_id = gtk_signal_connect (GTK_OBJECT (icon->text), "event",
+ GTK_SIGNAL_FUNC (icon_event),
+ icon);
+
+ gtk_signal_connect (GTK_OBJECT (icon->text), "editing_started",
+ GTK_SIGNAL_FUNC (editing_started),
+ icon);
+ gtk_signal_connect (GTK_OBJECT (icon->text), "editing_stopped",
+ GTK_SIGNAL_FUNC (editing_stopped),
+ icon);
+
+ gtk_signal_connect (GTK_OBJECT (icon->text), "text_changed",
+ GTK_SIGNAL_FUNC (text_changed),
+ icon);
+ gtk_signal_connect (GTK_OBJECT (icon->text), "height_changed",
+ GTK_SIGNAL_FUNC (height_changed),
+ icon);
+
+ return icon;
+}
+
+static Icon *
+icon_new (Eil *eil, const char *icon_filename, const char *text)
+{
+ GdkPixbuf *im;
+ Icon *retval;
+
+ if (icon_filename)
+ im = gdk_pixbuf_new_from_file (icon_filename);
+ else
+ im = NULL;
+
+ retval = icon_new_from_pixbuf (eil, im, icon_filename, text);
+
+ if(im)
+ gdk_pixbuf_unref(im);
+
+ return retval;
+}
+
+static int
+icon_list_append (Eil *eil, Icon *icon)
+{
+ EIconListPrivate *priv;
+ int pos;
+
+ priv = eil->_priv;
+
+ pos = priv->icons++;
+ g_array_append_val(priv->icon_list, icon);
+
+ switch (priv->selection_mode) {
+ case GTK_SELECTION_BROWSE:
+ e_icon_list_select_icon (eil, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ if (!priv->frozen) {
+ /* FIXME: this should only layout the last line */
+ eil_layout_all_icons (eil);
+ eil_scrollbar_adjust (eil);
+ } else
+ priv->dirty = TRUE;
+
+ return priv->icons - 1;
+}
+
+static void
+icon_list_insert (Eil *eil, int pos, Icon *icon)
+{
+ EIconListPrivate *priv;
+
+ priv = eil->_priv;
+
+ if (pos == priv->icons) {
+ icon_list_append (eil, icon);
+ return;
+ }
+
+ g_array_insert_val(priv->icon_list, pos, icon);
+ priv->icons++;
+
+ switch (priv->selection_mode) {
+ case GTK_SELECTION_BROWSE:
+ e_icon_list_select_icon (eil, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ if (!priv->frozen) {
+ /* FIXME: this should only layout the lines from then one
+ * containing the Icon to the end.
+ */
+ eil_layout_all_icons (eil);
+ eil_scrollbar_adjust (eil);
+ } else
+ priv->dirty = TRUE;
+
+ sync_selection (eil, pos, SYNC_INSERT);
+}
+
+/**
+ * e_icon_list_insert_pixbuf:
+ * @eil: An icon list.
+ * @pos: Position at which the new icon should be inserted.
+ * @im: Pixbuf image with the icon image.
+ * @filename: Filename of the image file.
+ * @text: Text to be used for the icon's caption.
+ *
+ * Inserts an icon in the specified icon list. The icon is created from the
+ * specified Imlib image, and it is inserted at the @pos index.
+ */
+void
+e_icon_list_insert_pixbuf (EIconList *eil, int pos, GdkPixbuf *im,
+ const char *icon_filename, const char *text)
+{
+ Icon *icon;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+ g_return_if_fail (im != NULL);
+
+ icon = icon_new_from_pixbuf (eil, im, icon_filename, text);
+ icon_list_insert (eil, pos, icon);
+ return;
+}
+
+/**
+ * e_icon_list_insert:
+ * @eil: An icon list.
+ * @pos: Position at which the new icon should be inserted.
+ * @icon_filename: Name of the file that holds the icon's image.
+ * @text: Text to be used for the icon's caption.
+ *
+ * Inserts an icon in the specified icon list. The icon's image is loaded
+ * from the specified file, and it is inserted at the @pos index.
+ */
+void
+e_icon_list_insert (EIconList *eil, int pos, const char *icon_filename, const char *text)
+{
+ Icon *icon;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+
+ icon = icon_new (eil, icon_filename, text);
+ icon_list_insert (eil, pos, icon);
+ return;
+}
+
+/**
+ * e_icon_list_append_pixbuf:
+ * @eil: An icon list.
+ * @im: Pixbuf image with the icon image.
+ * @filename: Filename of the image file.
+ * @text: Text to be used for the icon's caption.
+ *
+ * Appends an icon to the specified icon list. The icon is created from
+ * the specified Imlib image.
+ */
+int
+e_icon_list_append_pixbuf (EIconList *eil, GdkPixbuf *im,
+ const char *icon_filename, const char *text)
+{
+ Icon *icon;
+
+ g_return_val_if_fail (eil != NULL, -1);
+ g_return_val_if_fail (IS_EIL (eil), -1);
+ g_return_val_if_fail (im != NULL, -1);
+
+ icon = icon_new_from_pixbuf (eil, im, icon_filename, text);
+ return icon_list_append (eil, icon);
+}
+
+/**
+ * e_icon_list_append:
+ * @eil: An icon list.
+ * @icon_filename: Name of the file that holds the icon's image.
+ * @text: Text to be used for the icon's caption.
+ *
+ * Appends an icon to the specified icon list. The icon's image is loaded from
+ * the specified file, and it is inserted at the @pos index.
+ */
+int
+e_icon_list_append (EIconList *eil, const char *icon_filename,
+ const char *text)
+{
+ Icon *icon;
+
+ g_return_val_if_fail (eil != NULL, -1);
+ g_return_val_if_fail (IS_EIL (eil), -1);
+
+ icon = icon_new (eil, icon_filename, text);
+ return icon_list_append (eil, icon);
+}
+
+static void
+icon_destroy (Icon *icon)
+{
+ if (icon->destroy)
+ (* icon->destroy) (icon->data);
+
+ g_free (icon->icon_filename);
+
+ gtk_object_destroy (GTK_OBJECT (icon->image));
+ gtk_object_destroy (GTK_OBJECT (icon->text));
+ g_free (icon);
+}
+
+/**
+ * e_icon_list_remove:
+ * @eil: An icon list.
+ * @pos: Index of the icon that should be removed.
+ *
+ * Removes the icon at index position @pos. If a destroy handler was specified
+ * for that icon, it will be called with the appropriate data.
+ */
+void
+e_icon_list_remove (EIconList *eil, int pos)
+{
+ EIconListPrivate *priv;
+ int was_selected;
+ Icon *icon;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+ g_return_if_fail (pos >= 0 && pos < eil->_priv->icons);
+
+ priv = eil->_priv;
+
+ was_selected = FALSE;
+
+ icon = g_array_index(priv->icon_list, Icon*, pos);
+
+ if (icon->selected) {
+ was_selected = TRUE;
+
+ switch (priv->selection_mode) {
+ case GTK_SELECTION_SINGLE:
+ case GTK_SELECTION_BROWSE:
+ case GTK_SELECTION_MULTIPLE:
+ case GTK_SELECTION_EXTENDED:
+ e_icon_list_unselect_icon (eil, pos);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ g_array_remove_index(priv->icon_list, pos);
+ priv->icons--;
+
+ sync_selection (eil, pos, SYNC_REMOVE);
+
+ if (was_selected) {
+ switch (priv->selection_mode) {
+ case GTK_SELECTION_BROWSE:
+ if (pos == priv->icons)
+ e_icon_list_select_icon (eil, pos - 1);
+ else
+ e_icon_list_select_icon (eil, pos);
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (priv->icons >= priv->last_selected_idx)
+ priv->last_selected_idx = -1;
+
+ if (priv->last_selected_icon == icon)
+ priv->last_selected_icon = NULL;
+
+ icon_destroy (icon);
+
+ if (!priv->frozen) {
+ /* FIXME: Optimize, only re-layout from pos to end */
+ eil_layout_all_icons (eil);
+ eil_scrollbar_adjust (eil);
+ } else
+ priv->dirty = TRUE;
+}
+
+/**
+ * e_icon_list_clear:
+ * @eil: An icon list.
+ *
+ * Clears the contents for the icon list by removing all the icons. If destroy
+ * handlers were specified for any of the icons, they will be called with the
+ * appropriate data.
+ */
+void
+e_icon_list_clear (EIconList *eil)
+{
+ EIconListPrivate *priv;
+ int i;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+
+ priv = eil->_priv;
+
+ for (i = 0; i < priv->icon_list->len; i++)
+ icon_destroy (g_array_index (priv->icon_list, Icon*, i));
+
+ eil_free_line_info (eil);
+
+ g_list_free (priv->selection);
+ priv->selection = NULL;
+ g_array_set_size(priv->icon_list, 0);
+ priv->icons = 0;
+ priv->last_selected_idx = -1;
+ priv->last_selected_icon = NULL;
+
+ if (!priv->frozen) {
+ eil_layout_all_icons (eil);
+ eil_scrollbar_adjust (eil);
+ } else
+ priv->dirty = TRUE;
+}
+
+static void
+eil_destroy (GtkObject *object)
+{
+ Eil *eil;
+
+ /* remember, destroy can be run multiple times! */
+
+ eil = EIL (object);
+
+ g_free (eil->_priv->separators);
+ eil->_priv->separators = NULL;
+
+ eil->_priv->frozen = 1;
+ eil->_priv->dirty = TRUE;
+ if(eil->_priv->icon_list) {
+ e_icon_list_clear (eil);
+ g_array_free(eil->_priv->icon_list, TRUE);
+ }
+ eil->_priv->icon_list = NULL;
+
+ if (eil->_priv->timer_tag != 0) {
+ gtk_timeout_remove (eil->_priv->timer_tag);
+ eil->_priv->timer_tag = 0;
+ }
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+eil_finalize (GtkObject *object)
+{
+ Eil *eil;
+
+ eil = EIL (object);
+
+ g_free (eil->_priv);
+ eil->_priv = NULL;
+
+ if (GTK_OBJECT_CLASS (parent_class)->finalize)
+ (*GTK_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+
+static void
+select_icon (Eil *eil, int pos, GdkEvent *event)
+{
+ EIconListPrivate *priv;
+ gint i;
+ Icon *icon;
+
+ priv = eil->_priv;
+
+ switch (priv->selection_mode) {
+ case GTK_SELECTION_SINGLE:
+ case GTK_SELECTION_BROWSE:
+ i = 0;
+
+ for (i = 0; i < priv->icon_list->len; i++) {
+ icon = g_array_index (priv->icon_list, Icon*, i);
+
+ if (i != pos && icon->selected)
+ emit_select (eil, FALSE, i, event);
+ }
+
+ emit_select (eil, TRUE, pos, event);
+ break;
+
+ case GTK_SELECTION_MULTIPLE:
+ case GTK_SELECTION_EXTENDED:
+ emit_select (eil, TRUE, pos, event);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * e_icon_list_select_icon:
+ * @eil: An icon list.
+ * @pos: Index of the icon to be selected.
+ *
+ * Selects the icon at the index specified by @pos.
+ */
+void
+e_icon_list_select_icon (EIconList *eil, int pos)
+{
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+ g_return_if_fail (pos >= 0 && pos < eil->_priv->icons);
+
+ select_icon (eil, pos, NULL);
+}
+
+static void
+unselect_icon (Eil *eil, int pos, GdkEvent *event)
+{
+ EIconListPrivate *priv;
+
+ priv = eil->_priv;
+
+ switch (priv->selection_mode) {
+ case GTK_SELECTION_SINGLE:
+ case GTK_SELECTION_BROWSE:
+ case GTK_SELECTION_MULTIPLE:
+ case GTK_SELECTION_EXTENDED:
+ emit_select (eil, FALSE, pos, event);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * e_icon_list_unselect_icon:
+ * @eil: An icon list.
+ * @pos: Index of the icon to be unselected.
+ *
+ * Unselects the icon at the index specified by @pos.
+ */
+void
+e_icon_list_unselect_icon (EIconList *eil, int pos)
+{
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+ g_return_if_fail (pos >= 0 && pos < eil->_priv->icons);
+
+ unselect_icon (eil, pos, NULL);
+}
+
+static void
+eil_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+ requisition->width = 1;
+ requisition->height = 1;
+}
+
+static void
+eil_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ Eil *eil;
+ EIconListPrivate *priv;
+
+ eil = EIL (widget);
+ priv = eil->_priv;
+
+ if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
+ (* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
+
+ if (priv->frozen)
+ return;
+
+ eil_layout_all_icons (eil);
+}
+
+static void
+eil_realize (GtkWidget *widget)
+{
+ Eil *eil;
+ EIconListPrivate *priv;
+ GtkStyle *style;
+
+ eil = EIL (widget);
+ priv = eil->_priv;
+
+ priv->frozen++;
+
+ if (GTK_WIDGET_CLASS (parent_class)->realize)
+ (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
+
+ priv->frozen--;
+
+ /* Change the style to use the base color as the background */
+
+ style = gtk_style_copy (gtk_widget_get_style (widget));
+ style->bg[GTK_STATE_NORMAL] = style->base[GTK_STATE_NORMAL];
+ gtk_widget_set_style (widget, style);
+
+ gdk_window_set_background (GTK_LAYOUT (eil)->bin_window,
+ &widget->style->bg[GTK_STATE_NORMAL]);
+
+ if (priv->frozen)
+ return;
+
+ if (priv->dirty) {
+ eil_layout_all_icons (eil);
+ eil_scrollbar_adjust (eil);
+ }
+}
+
+static void
+real_select_icon (Eil *eil, gint num, GdkEvent *event)
+{
+ EIconListPrivate *priv;
+ Icon *icon;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+ g_return_if_fail (num >= 0 && num < eil->_priv->icons);
+
+ priv = eil->_priv;
+
+ icon = g_array_index (priv->icon_list, Icon*, num);
+
+ if (icon->selected)
+ return;
+
+ icon->selected = TRUE;
+ gnome_icon_text_item_select (icon->text, TRUE);
+ priv->selection = g_list_append (priv->selection, GINT_TO_POINTER (num));
+}
+
+static void
+real_unselect_icon (Eil *eil, gint num, GdkEvent *event)
+{
+ EIconListPrivate *priv;
+ Icon *icon;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+ g_return_if_fail (num >= 0 && num < eil->_priv->icons);
+
+ priv = eil->_priv;
+
+ icon = g_array_index (priv->icon_list, Icon*, num);
+
+ if (!icon->selected)
+ return;
+
+ icon->selected = FALSE;
+ gnome_icon_text_item_select (icon->text, FALSE);
+ priv->selection = g_list_remove (priv->selection, GINT_TO_POINTER (num));
+}
+
+/* Saves the selection of the icon list to temporary storage */
+static void
+store_temp_selection (Eil *eil)
+{
+ EIconListPrivate *priv;
+ int i;
+ Icon *icon;
+
+ priv = eil->_priv;
+
+ for (i = 0; i < priv->icon_list->len; i++) {
+ icon = g_array_index(priv->icon_list, Icon*, i);
+
+ icon->tmp_selected = icon->selected;
+ }
+}
+
+#define gray50_width 2
+#define gray50_height 2
+static const char gray50_bits[] = {
+ 0x02, 0x01, };
+
+/* Button press handler for the icon list */
+static gint
+eil_button_press (GtkWidget *widget, GdkEventButton *event)
+{
+ Eil *eil;
+ EIconListPrivate *priv;
+ int only_one;
+ GdkBitmap *stipple;
+ double tx, ty;
+
+ eil = EIL (widget);
+ priv = eil->_priv;
+
+ /* Invoke the canvas event handler and see if an item picks up the event */
+
+ if ((* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event))
+ return TRUE;
+
+ if (!(event->type == GDK_BUTTON_PRESS
+ && event->button == 1
+ && priv->selection_mode != GTK_SELECTION_BROWSE))
+ return FALSE;
+
+ only_one = priv->selection_mode == GTK_SELECTION_SINGLE;
+
+ if (only_one || (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) == 0)
+ eil_unselect_all (eil, NULL, NULL);
+
+ if (only_one)
+ return TRUE;
+
+ if (priv->selecting)
+ return FALSE;
+
+ gnome_canvas_window_to_world (GNOME_CANVAS (eil), event->x, event->y, &tx, &ty);
+ priv->sel_start_x = tx;
+ priv->sel_start_y = ty;
+ priv->sel_state = event->state;
+ priv->selecting = TRUE;
+
+ store_temp_selection (eil);
+
+ stipple = gdk_bitmap_create_from_data (NULL, gray50_bits, gray50_width, gray50_height);
+ priv->sel_rect = gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (eil)),
+ gnome_canvas_rect_get_type (),
+ "x1", tx,
+ "y1", ty,
+ "x2", tx,
+ "y2", ty,
+ "outline_color", "black",
+ "width_pixels", 1,
+ "outline_stipple", stipple,
+ NULL);
+ gdk_bitmap_unref (stipple);
+
+ gnome_canvas_item_grab (priv->sel_rect, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+ NULL, event->time);
+
+ return TRUE;
+}
+
+/* Returns whether the specified icon is at least partially inside the specified
+ * rectangle.
+ */
+static int
+icon_is_in_area (Icon *icon, int x1, int y1, int x2, int y2)
+{
+ double ix1, iy1, ix2, iy2;
+
+ if (x1 == x2 && y1 == y2)
+ return FALSE;
+
+ gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (icon->image), &ix1, &iy1, &ix2, &iy2);
+
+ if (ix1 <= x2 && iy1 <= y2 && ix2 >= x1 && iy2 >= y1)
+ return TRUE;
+
+ gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (icon->text), &ix1, &iy1, &ix2, &iy2);
+
+ if (ix1 <= x2 && iy1 <= y2 && ix2 >= x1 && iy2 >= y1)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Updates the rubberband selection to the specified point */
+static void
+update_drag_selection (Eil *eil, int x, int y)
+{
+ EIconListPrivate *priv;
+ int x1, x2, y1, y2;
+ int i;
+ Icon *icon;
+ int additive, invert;
+
+ priv = eil->_priv;
+
+ /* Update rubberband */
+
+ if (priv->sel_start_x < x) {
+ x1 = priv->sel_start_x;
+ x2 = x;
+ } else {
+ x1 = x;
+ x2 = priv->sel_start_x;
+ }
+
+ if (priv->sel_start_y < y) {
+ y1 = priv->sel_start_y;
+ y2 = y;
+ } else {
+ y1 = y;
+ y2 = priv->sel_start_y;
+ }
+
+ if (x1 < 0)
+ x1 = 0;
+
+ if (y1 < 0)
+ y1 = 0;
+
+ if (x2 >= GTK_WIDGET (eil)->allocation.width)
+ x2 = GTK_WIDGET (eil)->allocation.width - 1;
+
+ if (y2 >= priv->total_height)
+ y2 = priv->total_height - 1;
+
+ gnome_canvas_item_set (priv->sel_rect,
+ "x1", (double) x1,
+ "y1", (double) y1,
+ "x2", (double) x2,
+ "y2", (double) y2,
+ NULL);
+
+ /* Select or unselect icons as appropriate */
+
+ additive = priv->sel_state & GDK_SHIFT_MASK;
+ invert = priv->sel_state & GDK_CONTROL_MASK;
+
+ for (i = 0; i < priv->icon_list->len; i++) {
+ icon = g_array_index(priv->icon_list, Icon*, i);
+
+ if (icon_is_in_area (icon, x1, y1, x2, y2)) {
+ if (invert) {
+ if (icon->selected == icon->tmp_selected)
+ emit_select (eil, !icon->selected, i, NULL);
+ } else if (additive) {
+ if (!icon->selected)
+ emit_select (eil, TRUE, i, NULL);
+ } else {
+ if (!icon->selected)
+ emit_select (eil, TRUE, i, NULL);
+ }
+ } else if (icon->selected != icon->tmp_selected)
+ emit_select (eil, icon->tmp_selected, i, NULL);
+ }
+}
+
+/* Button release handler for the icon list */
+static gint
+eil_button_release (GtkWidget *widget, GdkEventButton *event)
+{
+ Eil *eil;
+ EIconListPrivate *priv;
+ double x, y;
+
+ eil = EIL (widget);
+ priv = eil->_priv;
+
+ if (!priv->selecting)
+ return (* GTK_WIDGET_CLASS (parent_class)->button_release_event) (widget, event);
+
+ if (event->button != 1)
+ return FALSE;
+
+ gnome_canvas_window_to_world (GNOME_CANVAS (eil), event->x, event->y, &x, &y);
+ update_drag_selection (eil, x, y);
+ gnome_canvas_item_ungrab (priv->sel_rect, event->time);
+
+ gtk_object_destroy (GTK_OBJECT (priv->sel_rect));
+ priv->sel_rect = NULL;
+ priv->selecting = FALSE;
+
+ if (priv->timer_tag != 0) {
+ gtk_timeout_remove (priv->timer_tag);
+ priv->timer_tag = 0;
+ }
+
+ return TRUE;
+}
+
+/* Autoscroll timeout handler for the icon list */
+static gint
+scroll_timeout (gpointer data)
+{
+ Eil *eil;
+ EIconListPrivate *priv;
+ GtkAdjustment *adj;
+ double x, y;
+ int value;
+
+ eil = data;
+ priv = eil->_priv;
+
+ GDK_THREADS_ENTER ();
+
+ adj = gtk_layout_get_vadjustment (GTK_LAYOUT (eil));
+
+ value = adj->value + priv->value_diff;
+ if (value > adj->upper - adj->page_size)
+ value = adj->upper - adj->page_size;
+
+ gtk_adjustment_set_value (adj, value);
+
+ gnome_canvas_window_to_world (GNOME_CANVAS (eil),
+ priv->event_last_x, priv->event_last_y,
+ &x, &y);
+ update_drag_selection (eil, x, y);
+
+ GDK_THREADS_LEAVE();
+
+ return TRUE;
+}
+
+/* Motion event handler for the icon list */
+static gint
+eil_motion_notify (GtkWidget *widget, GdkEventMotion *event)
+{
+ Eil *eil;
+ EIconListPrivate *priv;
+ double x, y;
+
+ eil = EIL (widget);
+ priv = eil->_priv;
+
+ if (!priv->selecting)
+ return (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event);
+
+ gnome_canvas_window_to_world (GNOME_CANVAS (eil), event->x, event->y, &x, &y);
+ update_drag_selection (eil, x, y);
+
+ /* If we are out of bounds, schedule a timeout that will do the scrolling */
+
+ if (event->y < 0 || event->y > widget->allocation.height) {
+ if (priv->timer_tag == 0)
+ priv->timer_tag = gtk_timeout_add (SCROLL_TIMEOUT, scroll_timeout, eil);
+
+ if (event->y < 0)
+ priv->value_diff = event->y;
+ else
+ priv->value_diff = event->y - widget->allocation.height;
+
+ priv->event_last_x = event->x;
+ priv->event_last_y = event->y;
+
+ /* Make the steppings be relative to the mouse distance from the
+ * canvas. Also notice the timeout above is small to give a
+ * more smooth movement.
+ */
+ priv->value_diff /= 5;
+ } else if (priv->timer_tag != 0) {
+ gtk_timeout_remove (priv->timer_tag);
+ priv->timer_tag = 0;
+ }
+
+ return TRUE;
+}
+
+typedef gboolean (*xGtkSignal_BOOL__INT_POINTER) (GtkObject * object,
+ gint arg1,
+ gpointer arg2,
+ gpointer user_data);
+static void
+xgtk_marshal_BOOL__INT_POINTER (GtkObject *object, GtkSignalFunc func, gpointer func_data,
+ GtkArg *args)
+{
+ xGtkSignal_BOOL__INT_POINTER rfunc;
+ gboolean *return_val;
+
+ return_val = GTK_RETLOC_BOOL (args[2]);
+ rfunc = (xGtkSignal_BOOL__INT_POINTER) func;
+ *return_val = (*rfunc) (object,
+ GTK_VALUE_INT (args[0]),
+ GTK_VALUE_POINTER (args[1]),
+ func_data);
+}
+
+static void
+eil_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ EIconList *eil;
+
+ eil = E_ICON_LIST (object);
+
+ switch (arg_id) {
+ default:
+ break;
+ }
+}
+
+static void
+eil_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ EIconList *eil;
+ EIconListPrivate *priv;
+
+ eil = E_ICON_LIST (object);
+ priv = eil->_priv;
+
+ switch (arg_id) {
+ default:
+ arg->type = GTK_TYPE_INVALID;
+ break;
+ }
+}
+
+static void
+eil_class_init (EilClass *eil_class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkLayoutClass *layout_class;
+ GnomeCanvasClass *canvas_class;
+
+ object_class = (GtkObjectClass *) eil_class;
+ widget_class = (GtkWidgetClass *) eil_class;
+ layout_class = (GtkLayoutClass *) eil_class;
+ canvas_class = (GnomeCanvasClass *) eil_class;
+
+ parent_class = gtk_type_class (gnome_canvas_get_type ());
+
+ eil_signals[SELECT_ICON] =
+ gtk_signal_new (
+ "select_icon",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconListClass, select_icon),
+ gtk_marshal_NONE__INT_POINTER,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_INT,
+ GTK_TYPE_GDK_EVENT);
+
+ eil_signals[UNSELECT_ICON] =
+ gtk_signal_new (
+ "unselect_icon",
+ GTK_RUN_FIRST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconListClass, unselect_icon),
+ gtk_marshal_NONE__INT_POINTER,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_INT,
+ GTK_TYPE_GDK_EVENT);
+
+ eil_signals[TEXT_CHANGED] =
+ gtk_signal_new (
+ "text_changed",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconListClass, text_changed),
+ xgtk_marshal_BOOL__INT_POINTER,
+ GTK_TYPE_BOOL, 2,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER);
+
+ gtk_object_class_add_signals (object_class, eil_signals, LAST_SIGNAL);
+
+ object_class->destroy = eil_destroy;
+ object_class->finalize = eil_finalize;
+ object_class->set_arg = eil_set_arg;
+ object_class->get_arg = eil_get_arg;
+
+ widget_class->size_request = eil_size_request;
+ widget_class->size_allocate = eil_size_allocate;
+ widget_class->realize = eil_realize;
+ widget_class->button_press_event = eil_button_press;
+ widget_class->button_release_event = eil_button_release;
+ widget_class->motion_notify_event = eil_motion_notify;
+
+ eil_class->select_icon = real_select_icon;
+ eil_class->unselect_icon = real_unselect_icon;
+}
+
+static void
+eil_init (Eil *eil)
+{
+ eil->_priv = g_new0 (EIconListPrivate, 1);
+
+ eil->_priv->icon_list = g_array_new(FALSE, FALSE, sizeof(gpointer));
+ eil->_priv->row_spacing = DEFAULT_ROW_SPACING;
+ eil->_priv->col_spacing = DEFAULT_COL_SPACING;
+ eil->_priv->text_spacing = DEFAULT_TEXT_SPACING;
+ eil->_priv->icon_border = DEFAULT_ICON_BORDER;
+ eil->_priv->separators = g_strdup (" ");
+
+ eil->_priv->selection_mode = GTK_SELECTION_SINGLE;
+ eil->_priv->dirty = TRUE;
+
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (eil), 0.0, 0.0, 1000000.0, 1000000.0);
+ gnome_canvas_scroll_to (GNOME_CANVAS (eil), 0, 0);
+}
+
+/**
+ * e_icon_list_get_type:
+ *
+ * Registers the &EIconList class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Returns: The type ID of the &EIconList class.
+ */
+guint
+e_icon_list_get_type (void)
+{
+ static guint eil_type = 0;
+
+ if (!eil_type) {
+ GtkTypeInfo eil_info = {
+ "EIconList",
+ sizeof (EIconList),
+ sizeof (EIconListClass),
+ (GtkClassInitFunc) eil_class_init,
+ (GtkObjectInitFunc) eil_init,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ eil_type = gtk_type_unique (gnome_canvas_get_type (),
+ &eil_info);
+ }
+
+ return eil_type;
+}
+
+/**
+ * e_icon_list_set_icon_width:
+ * @eil: An icon list.
+ * @w: New width for the icon columns.
+ *
+ * Sets the amount of horizontal space allocated to the icons, i.e. the column
+ * width of the icon list.
+ */
+void
+e_icon_list_set_icon_width (EIconList *eil, int w)
+{
+ EIconListPrivate *priv;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+
+ priv = eil->_priv;
+
+ priv->icon_width = w;
+
+ if (priv->frozen) {
+ priv->dirty = TRUE;
+ return;
+ }
+
+ eil_layout_all_icons (eil);
+ eil_scrollbar_adjust (eil);
+}
+
+
+/**
+ * e_icon_list_construct:
+ * @eil: An icon list.
+ * @icon_width: Width for the icon columns.
+ * @flags: A combination of %E_ICON_LIST_IS_EDITABLE and %E_ICON_LIST_STATIC_TEXT.
+ *
+ * Constructor for the icon list, to be used by derived classes.
+ **/
+void
+e_icon_list_construct (EIconList *eil, guint icon_width, int flags)
+{
+ EIconListPrivate *priv;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+
+ priv = eil->_priv;
+
+ e_icon_list_set_icon_width (eil, icon_width);
+ priv->is_editable = (flags & E_ICON_LIST_IS_EDITABLE) != 0;
+ priv->static_text = (flags & E_ICON_LIST_STATIC_TEXT) != 0;
+}
+
+
+/**
+ * e_icon_list_new: [constructor]
+ * @icon_width: Width for the icon columns.
+ * @flags: A combination of %E_ICON_LIST_IS_EDITABLE and %E_ICON_LIST_STATIC_TEXT.
+ *
+ * Creates a new icon list widget. The icon columns are allocated a width of
+ * @icon_width pixels. Icon captions will be word-wrapped to this width as
+ * well.
+ *
+ * If @flags has the %E_ICON_LIST_IS_EDITABLE flag set, then the user will be
+ * able to edit the text in the icon captions, and the "text_changed" signal
+ * will be emitted when an icon's text is changed.
+ *
+ * If @flags has the %E_ICON_LIST_STATIC_TEXT flags set, then the text
+ * for the icon captions will not be copied inside the icon list; it will only
+ * store the pointers to the original text strings specified by the application.
+ * This is intended to save memory. If this flag is not set, then the text
+ * strings will be copied and managed internally.
+ *
+ * Returns: a newly-created icon list widget
+ */
+GtkWidget *
+e_icon_list_new (guint icon_width, int flags)
+{
+ Eil *eil;
+
+ gtk_widget_push_colormap (gdk_rgb_get_cmap ());
+ eil = EIL (gtk_type_new (e_icon_list_get_type ()));
+ gtk_widget_pop_colormap ();
+
+ e_icon_list_construct (eil, icon_width, flags);
+
+ return GTK_WIDGET (eil);
+}
+
+
+/**
+ * e_icon_list_freeze:
+ * @eil: An icon list.
+ *
+ * Freezes an icon list so that any changes made to it will not be
+ * reflected on the screen until it is thawed with e_icon_list_thaw().
+ * It is recommended to freeze the icon list before inserting or deleting
+ * many icons, for example, so that the layout process will only be executed
+ * once, when the icon list is finally thawed.
+ *
+ * You can call this function multiple times, but it must be balanced with the
+ * same number of calls to e_icon_list_thaw() before the changes will take
+ * effect.
+ */
+void
+e_icon_list_freeze (EIconList *eil)
+{
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+
+ eil->_priv->frozen++;
+
+ /* We hide the root so that the user will not see any changes while the
+ * icon list is doing stuff.
+ */
+
+ if (eil->_priv->frozen == 1)
+ gnome_canvas_item_hide (GNOME_CANVAS (eil)->root);
+}
+
+/**
+ * e_icon_list_thaw:
+ * @eil: An icon list.
+ *
+ * Thaws the icon list and performs any pending layout operations. This
+ * is to be used in conjunction with e_icon_list_freeze().
+ */
+void
+e_icon_list_thaw (EIconList *eil)
+{
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+ g_return_if_fail (eil->_priv->frozen > 0);
+
+ eil->_priv->frozen--;
+
+ if (eil->_priv->dirty) {
+ eil_layout_all_icons (eil);
+ eil_scrollbar_adjust (eil);
+ }
+
+ if (eil->_priv->frozen == 0)
+ gnome_canvas_item_show (GNOME_CANVAS (eil)->root);
+}
+
+/**
+ * e_icon_list_set_selection_mode
+ * @eil: An icon list.
+ * @mode: New selection mode.
+ *
+ * Sets the selection mode for an icon list. The %GTK_SELECTION_MULTIPLE and
+ * %GTK_SELECTION_EXTENDED modes are considered equivalent.
+ */
+void
+e_icon_list_set_selection_mode (EIconList *eil, GtkSelectionMode mode)
+{
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+
+ eil->_priv->selection_mode = mode;
+ eil->_priv->last_selected_idx = -1;
+ eil->_priv->last_selected_icon = NULL;
+}
+
+/**
+ * e_icon_list_set_icon_data_full:
+ * @eil: An icon list.
+ * @pos: Index of an icon.
+ * @data: User data to set on the icon.
+ * @destroy: Destroy notification handler for the icon.
+ *
+ * Associates the @data pointer to the icon at the index specified by @pos. The
+ * @destroy argument points to a function that will be called when the icon is
+ * destroyed, or NULL if no function is to be called when this happens.
+ */
+void
+e_icon_list_set_icon_data_full (EIconList *eil,
+ int pos, gpointer data,
+ GtkDestroyNotify destroy)
+{
+ Icon *icon;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+ g_return_if_fail (pos >= 0 && pos < eil->_priv->icons);
+
+ icon = g_array_index (eil->_priv->icon_list, Icon*, pos);
+ icon->data = data;
+ icon->destroy = destroy;
+}
+
+/**
+ * e_icon_list_get_icon_data:
+ * @eil: An icon list.
+ * @pos: Index of an icon.
+ * @data: User data to set on the icon.
+ *
+ * Associates the @data pointer to the icon at the index specified by @pos.
+ */
+void
+e_icon_list_set_icon_data (EIconList *eil, int pos, gpointer data)
+{
+ e_icon_list_set_icon_data_full (eil, pos, data, NULL);
+}
+
+/**
+ * e_icon_list_get_icon_data:
+ * @eil: An icon list.
+ * @pos: Index of an icon.
+ *
+ * Returns the user data pointer associated to the icon at the index specified
+ * by @pos.
+ */
+gpointer
+e_icon_list_get_icon_data (EIconList *eil, int pos)
+{
+ Icon *icon;
+
+ g_return_val_if_fail (eil != NULL, NULL);
+ g_return_val_if_fail (IS_EIL (eil), NULL);
+ g_return_val_if_fail (pos >= 0 && pos < eil->_priv->icons, NULL);
+
+ icon = g_array_index (eil->_priv->icon_list, Icon*, pos);
+ return icon->data;
+}
+
+/**
+ * e_icon_list_find_icon_from_data:
+ * @eil: An icon list.
+ * @data: Data pointer associated to an icon.
+ *
+ * Returns the index of the icon whose user data has been set to @data,
+ * or -1 if no icon has this data associated to it.
+ */
+int
+e_icon_list_find_icon_from_data (EIconList *eil, gpointer data)
+{
+ EIconListPrivate *priv;
+ int n;
+ Icon *icon;
+
+ g_return_val_if_fail (eil != NULL, -1);
+ g_return_val_if_fail (IS_EIL (eil), -1);
+
+ priv = eil->_priv;
+
+ for (n = 0; n < priv->icon_list->len; n++) {
+ icon = g_array_index(priv->icon_list, Icon*, n);
+ if (icon->data == data)
+ return n;
+ }
+
+ return -1;
+}
+
+/* Sets an integer value in the private structure of the icon list, and updates it */
+static void
+set_value (EIconList *eil, EIconListPrivate *priv, int *dest, int val)
+{
+ if (val == *dest)
+ return;
+
+ *dest = val;
+
+ if (!priv->frozen) {
+ eil_layout_all_icons (eil);
+ eil_scrollbar_adjust (eil);
+ } else
+ priv->dirty = TRUE;
+}
+
+/**
+ * e_icon_list_set_row_spacing:
+ * @eil: An icon list.
+ * @pixels: Number of pixels for inter-row spacing.
+ *
+ * Sets the spacing to be used between rows of icons.
+ */
+void
+e_icon_list_set_row_spacing (EIconList *eil, int pixels)
+{
+ EIconListPrivate *priv;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+
+ priv = eil->_priv;
+ set_value (eil, priv, &priv->row_spacing, pixels);
+}
+
+/**
+ * e_icon_list_set_col_spacing:
+ * @eil: An icon list.
+ * @pixels: Number of pixels for inter-column spacing.
+ *
+ * Sets the spacing to be used between columns of icons.
+ */
+void
+e_icon_list_set_col_spacing (EIconList *eil, int pixels)
+{
+ EIconListPrivate *priv;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+
+ priv = eil->_priv;
+ set_value (eil, priv, &priv->col_spacing, pixels);
+}
+
+/**
+ * e_icon_list_set_text_spacing:
+ * @eil: An icon list.
+ * @pixels: Number of pixels between an icon's image and its caption.
+ *
+ * Sets the spacing to be used between an icon's image and its text caption.
+ */
+void
+e_icon_list_set_text_spacing (EIconList *eil, int pixels)
+{
+ EIconListPrivate *priv;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+
+ priv = eil->_priv;
+ set_value (eil, priv, &priv->text_spacing, pixels);
+}
+
+/**
+ * e_icon_list_set_icon_border:
+ * @eil: An icon list.
+ * @pixels: Number of border pixels to be used around an icon's image.
+ *
+ * Sets the width of the border to be displayed around an icon's image. This is
+ * currently not implemented.
+ */
+void
+e_icon_list_set_icon_border (EIconList *eil, int pixels)
+{
+ EIconListPrivate *priv;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+
+ priv = eil->_priv;
+ set_value (eil, priv, &priv->icon_border, pixels);
+}
+
+/**
+ * e_icon_list_set_separators:
+ * @eil: An icon list.
+ * @sep: String with characters to be used as word separators.
+ *
+ * Sets the characters that can be used as word separators when doing
+ * word-wrapping in the icon text captions.
+ */
+void
+e_icon_list_set_separators (EIconList *eil, const char *sep)
+{
+ EIconListPrivate *priv;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+ g_return_if_fail (sep != NULL);
+
+ priv = eil->_priv;
+
+ if (priv->separators)
+ g_free (priv->separators);
+
+ priv->separators = g_strdup (sep);
+
+ if (priv->frozen) {
+ priv->dirty = TRUE;
+ return;
+ }
+
+ eil_layout_all_icons (eil);
+ eil_scrollbar_adjust (eil);
+}
+
+/**
+ * e_icon_list_moveto:
+ * @eil: An icon list.
+ * @pos: Index of an icon.
+ * @yalign: Vertical alignment of the icon.
+ *
+ * Makes the icon whose index is @pos be visible on the screen. The icon list
+ * gets scrolled so that the icon is visible. An alignment of 0.0 represents
+ * the top of the visible part of the icon list, and 1.0 represents the bottom.
+ * An icon can be centered on the icon list.
+ */
+void
+e_icon_list_moveto (EIconList *eil, int pos, double yalign)
+{
+ EIconListPrivate *priv;
+ GtkAdjustment *adj;
+ IconLine *il;
+ GList *l;
+ int i, y, uh, line;
+
+ g_return_if_fail (eil != NULL);
+ g_return_if_fail (IS_EIL (eil));
+ g_return_if_fail (pos >= 0 && pos < eil->_priv->icons);
+ g_return_if_fail (yalign >= 0.0 && yalign <= 1.0);
+
+ priv = eil->_priv;
+
+ g_return_if_fail (priv->lines != NULL);
+
+ line = pos / eil_get_items_per_line (eil);
+
+ y = 0;
+ for (i = 0, l = priv->lines; l && i < line; l = l->next, i++) {
+ il = l->data;
+ y += icon_line_height (eil, il);
+ }
+
+ il = l->data;
+
+ uh = GTK_WIDGET (eil)->allocation.height - icon_line_height (eil,il);
+ adj = gtk_layout_get_vadjustment (GTK_LAYOUT (eil));
+ gtk_adjustment_set_value (adj, y - uh * yalign);
+}
+
+/**
+ * e_icon_list_is_visible:
+ * @eil: An icon list.
+ * @pos: Index of an icon.
+ *
+ * Returns whether the icon at the index specified by @pos is visible. This
+ * will be %GTK_VISIBILITY_NONE if the icon is not visible at all,
+ * %GTK_VISIBILITY_PARTIAL if the icon is at least partially shown, and
+ * %GTK_VISIBILITY_FULL if the icon is fully visible.
+ */
+GtkVisibility
+e_icon_list_icon_is_visible (EIconList *eil, int pos)
+{
+ EIconListPrivate *priv;
+ GtkAdjustment *adj;
+ IconLine *il;
+ GList *l;
+ int line, y1, y2, i;
+
+ g_return_val_if_fail (eil != NULL, GTK_VISIBILITY_NONE);
+ g_return_val_if_fail (IS_EIL (eil), GTK_VISIBILITY_NONE);
+ g_return_val_if_fail (pos >= 0 && pos < eil->_priv->icons,
+ GTK_VISIBILITY_NONE);
+
+ priv = eil->_priv;
+
+ if (priv->lines == NULL)
+ return GTK_VISIBILITY_NONE;
+
+ line = pos / eil_get_items_per_line (eil);
+ y1 = 0;
+ for (i = 0, l = priv->lines; l && i < line; l = l->next, i++) {
+ il = l->data;
+ y1 += icon_line_height (eil, il);
+ }
+
+ y2 = y1 + icon_line_height (eil, (IconLine *) l->data);
+
+ adj = gtk_layout_get_vadjustment (GTK_LAYOUT (eil));
+
+ if (y2 < adj->value)
+ return GTK_VISIBILITY_NONE;
+
+ if (y1 > adj->value + GTK_WIDGET (eil)->allocation.height)
+ return GTK_VISIBILITY_NONE;
+
+ if (y2 <= adj->value + GTK_WIDGET (eil)->allocation.height &&
+ y1 >= adj->value)
+ return GTK_VISIBILITY_FULL;
+
+ return GTK_VISIBILITY_PARTIAL;
+}
+
+/**
+ * e_icon_list_get_icon_at:
+ * @eil: An icon list.
+ * @x: X position in the icon list window.
+ * @y: Y position in the icon list window.
+ *
+ * Returns the index of the icon that is under the specified coordinates, which
+ * are relative to the icon list's window. If there is no icon in that
+ * position, -1 is returned.
+ */
+int
+e_icon_list_get_icon_at (EIconList *eil, int x, int y)
+{
+ EIconListPrivate *priv;
+ double wx, wy;
+ double dx, dy;
+ int cx, cy;
+ int n;
+ GnomeCanvasItem *item;
+ double dist;
+
+ g_return_val_if_fail (eil != NULL, -1);
+ g_return_val_if_fail (IS_EIL (eil), -1);
+
+ priv = eil->_priv;
+
+ dx = x;
+ dy = y;
+
+ gnome_canvas_window_to_world (GNOME_CANVAS (eil), dx, dy, &wx, &wy);
+ gnome_canvas_w2c (GNOME_CANVAS (eil), wx, wy, &cx, &cy);
+
+ for (n = 0; n < priv->icon_list->len; n++) {
+ Icon *icon = g_array_index(priv->icon_list, Icon*, n);
+ GnomeCanvasItem *image = GNOME_CANVAS_ITEM (icon->image);
+ GnomeCanvasItem *text = GNOME_CANVAS_ITEM (icon->text);
+
+ if (wx >= image->x1 && wx <= image->x2 && wy >= image->y1 && wy <= image->y2) {
+ dist = (* GNOME_CANVAS_ITEM_CLASS (GTK_OBJECT (image)->klass)->point) (
+ image,
+ wx, wy,
+ cx, cy,
+ &item);
+
+ if ((int) (dist * GNOME_CANVAS (eil)->pixels_per_unit + 0.5)
+ <= GNOME_CANVAS (eil)->close_enough)
+ return n;
+ }
+
+ if (wx >= text->x1 && wx <= text->x2 && wy >= text->y1 && wy <= text->y2) {
+ dist = (* GNOME_CANVAS_ITEM_CLASS (GTK_OBJECT (text)->klass)->point) (
+ text,
+ wx, wy,
+ cx, cy,
+ &item);
+
+ if ((int) (dist * GNOME_CANVAS (eil)->pixels_per_unit + 0.5)
+ <= GNOME_CANVAS (eil)->close_enough)
+ return n;
+ }
+ }
+
+ return -1;
+}
+
+
+/**
+ * e_icon_list_get_num_icons:
+ * @eil: An icon list.
+ *
+ * Returns the number of icons in the icon list.
+ */
+guint
+e_icon_list_get_num_icons (EIconList *eil)
+{
+ g_return_val_if_fail (E_IS_ICON_LIST (eil), 0);
+
+ return eil->_priv->icons;
+}
+
+
+/**
+ * e_icon_list_get_selection:
+ * @eil: An icon list.
+ *
+ * Returns a list of integers with the indices of the currently selected icons.
+ */
+GList *
+e_icon_list_get_selection (EIconList *eil)
+{
+ g_return_val_if_fail (E_IS_ICON_LIST (eil), NULL);
+
+ return eil->_priv->selection;
+}
+
+
+/**
+ * e_icon_list_get_selection:
+ * @eil: An icon list.
+ * @idx: Index of an @icon.
+ *
+ * Returns the filename of the icon with index @idx.
+ */
+gchar *
+e_icon_list_get_icon_filename (EIconList *eil, int idx)
+{
+ Icon *icon;
+
+ g_return_val_if_fail (eil != NULL, NULL);
+ g_return_val_if_fail (IS_EIL (eil), NULL);
+ g_return_val_if_fail (idx >= 0 && idx < eil->_priv->icons, NULL);
+
+ icon = g_array_index (eil->_priv->icon_list, Icon*, idx);
+ return icon->icon_filename;
+}
+
+
+/**
+ * e_icon_list_find_icon_from_filename:
+ * @eil: An icon list.
+ * @filename: Filename of an icon.
+ *
+ * Returns the index of the icon whose filename is @filename or -1 if
+ * there is no icon with this filename.
+ */
+int
+e_icon_list_find_icon_from_filename (EIconList *eil,
+ const gchar *filename)
+{
+ EIconListPrivate *priv;
+ int n;
+ Icon *icon;
+
+ g_return_val_if_fail (eil != NULL, -1);
+ g_return_val_if_fail (IS_EIL (eil), -1);
+ g_return_val_if_fail (filename != NULL, -1);
+
+ priv = eil->_priv;
+
+ for (n = 0; n < priv->icon_list->len; n++) {
+ icon = g_array_index(priv->icon_list, Icon*, n);
+ if (!strcmp (icon->icon_filename, filename))
+ return n;
+ }
+
+ return -1;
+}
+