aboutsummaryrefslogtreecommitdiffstats
path: root/composer
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
parent711bef6798d08dcb34bc83730f11575ffa411d07 (diff)
downloadgsoc2013-evolution-9d6c76ce37be81f83d3df0cf2bfc8d81adfa18e8.tar.gz
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')
-rw-r--r--composer/ChangeLog17
-rw-r--r--composer/Makefile.am4
-rw-r--r--composer/e-icon-list.c2657
-rw-r--r--composer/e-icon-list.h178
-rw-r--r--composer/e-msg-composer-attachment-bar.c170
-rw-r--r--composer/e-msg-composer-attachment-bar.h5
-rw-r--r--composer/e-msg-composer-attachment.c3
-rw-r--r--composer/e-msg-composer-attachment.h3
-rw-r--r--composer/e-msg-composer.c47
9 files changed, 3042 insertions, 42 deletions
diff --git a/composer/ChangeLog b/composer/ChangeLog
index 021797dd20..05331c897c 100644
--- a/composer/ChangeLog
+++ b/composer/ChangeLog
@@ -1,3 +1,20 @@
+2000-10-01 Iain Holmes <iain@helixcode.com>
+
+ * e-msg-composer-attachment-bar.[ch] (update): If the attachment is
+ an image, then make a thumbnail for it.
+ Base the attachment bar on e-icon-list instead of gnome-icon-list.
+
+ * e-icon-list.[ch]: New files. These are modified versions of
+ gnome-icon-list from gnome-libs HEAD that uses gdk-pixbuf instead
+ of the evil Imlib.
+
+ * e-msg-composer-attachment.[ch]: Add a pixbuf_cache member, to
+ save us having to generate a thumbnail for the attachment every
+ time the bar changes.
+
+ * e-msg-composer.c (e_msg_composer_construct): Add dnd support for
+ files. Drag a file to the composer to add it as an attachment.
+
2000-09-28 Jeffrey Stedfast <fejj@helixcode.com>
* e-msg-composer.c (build_message): Check to see if the body has
diff --git a/composer/Makefile.am b/composer/Makefile.am
index b6a3b0760f..f6559b43a6 100644
--- a/composer/Makefile.am
+++ b/composer/Makefile.am
@@ -64,7 +64,9 @@ libcomposer_la_SOURCES = \
e-msg-composer-select-file.c \
e-msg-composer-select-file.h \
e-msg-composer.c \
- e-msg-composer.h
+ e-msg-composer.h \
+ e-icon-list.c \
+ e-icon-list.h
EXTRA_DIST = \
$(glade_DATA) \
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;
+}
+
diff --git a/composer/e-icon-list.h b/composer/e-icon-list.h
new file mode 100644
index 0000000000..43f2fa105d
--- /dev/null
+++ b/composer/e-icon-list.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 1998, 1999 Free Software Foundation
+ * Copyright (C) 2000 Red Hat, Inc.
+ * 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>
+ */
+
+#ifndef _E_ICON_LIST_H_
+#define _E_ICON_LIST_H_
+
+#include <libgnome/gnome-defs.h>
+#include <libgnomeui/gnome-canvas.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+BEGIN_GNOME_DECLS
+
+#define E_TYPE_ICON_LIST (e_icon_list_get_type ())
+#define E_ICON_LIST(obj) (GTK_CHECK_CAST ((obj), E_TYPE_ICON_LIST, EIconList))
+#define E_ICON_LIST_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_ICON_LIST, EIconListClass))
+#define E_IS_ICON_LIST(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_ICON_LIST))
+#define E_IS_ICON_LIST_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), E_TYPE_ICON_LIST))
+#define E_ICON_LIST_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), E_TYPE_ICON_LIST, EIconListClass))
+
+typedef struct _EIconList EIconList;
+typedef struct _EIconListPrivate EIconListPrivate;
+typedef struct _EIconListClass EIconListClass;
+
+typedef enum {
+ E_ICON_LIST_ICONS,
+ E_ICON_LIST_TEXT_BELOW,
+ E_ICON_LIST_TEXT_RIGHT
+} EIconListMode;
+
+/* This structure has been converted to use public and private parts. To avoid
+ * breaking binary compatibility, the slots for private fields have been
+ * replaced with padding. Please remove these fields when gnome-libs has
+ * reached another major version and it is "fine" to break binary compatibility.
+ */
+struct _EIconList {
+ GnomeCanvas canvas;
+
+ /*< private >*/
+ EIconListPrivate * _priv;
+};
+
+struct _EIconListClass {
+ GnomeCanvasClass parent_class;
+
+ void (*select_icon) (EIconList *gil, gint num, GdkEvent *event);
+ void (*unselect_icon) (EIconList *gil, gint num, GdkEvent *event);
+ gboolean (*text_changed) (EIconList *gil, gint num, const char *new_text);
+};
+
+enum {
+ E_ICON_LIST_IS_EDITABLE = 1 << 0,
+ E_ICON_LIST_STATIC_TEXT = 1 << 1
+};
+
+guint e_icon_list_get_type (void) G_GNUC_CONST;
+
+GtkWidget *e_icon_list_new (guint icon_width,
+ int flags);
+void e_icon_list_construct (EIconList *gil,
+ guint icon_width,
+ int flags);
+
+
+/* To avoid excesive recomputes during insertion/deletion */
+void e_icon_list_freeze (EIconList *gil);
+void e_icon_list_thaw (EIconList *gil);
+
+
+void e_icon_list_insert (EIconList *gil,
+ int idx,
+ const char *icon_filename,
+ const char *text);
+void e_icon_list_insert_pixbuf (EIconList *gil,
+ int idx,
+ GdkPixbuf *im,
+ const char *icon_filename,
+ const char *text);
+
+int e_icon_list_append (EIconList *gil,
+ const char *icon_filename,
+ const char *text);
+int e_icon_list_append_pixbuf (EIconList *gil,
+ GdkPixbuf *im,
+ const char *icon_filename,
+ const char *text);
+
+void e_icon_list_clear (EIconList *gil);
+void e_icon_list_remove (EIconList *gil,
+ int idx);
+
+guint e_icon_list_get_num_icons (EIconList *gil);
+
+
+/* Managing the selection */
+void e_icon_list_set_selection_mode (EIconList *gil,
+ GtkSelectionMode mode);
+void e_icon_list_select_icon (EIconList *gil,
+ int idx);
+void e_icon_list_unselect_icon (EIconList *gil,
+ int idx);
+int e_icon_list_unselect_all (EIconList *gil);
+GList * e_icon_list_get_selection (EIconList *gil);
+
+/* Setting the spacing values */
+void e_icon_list_set_icon_width (EIconList *gil,
+ int w);
+void e_icon_list_set_row_spacing (EIconList *gil,
+ int pixels);
+void e_icon_list_set_col_spacing (EIconList *gil,
+ int pixels);
+void e_icon_list_set_text_spacing (EIconList *gil,
+ int pixels);
+void e_icon_list_set_icon_border (EIconList *gil,
+ int pixels);
+void e_icon_list_set_separators (EIconList *gil,
+ const char *sep);
+/* Icon filename. */
+gchar * e_icon_list_get_icon_filename (EIconList *gil,
+ int idx);
+int e_icon_list_find_icon_from_filename (EIconList *gil,
+ const char *filename);
+
+/* Attaching information to the icons */
+void e_icon_list_set_icon_data (EIconList *gil,
+ int idx, gpointer data);
+void e_icon_list_set_icon_data_full (EIconList *gil,
+ int idx, gpointer data,
+ GtkDestroyNotify destroy);
+int e_icon_list_find_icon_from_data (EIconList *gil,
+ gpointer data);
+gpointer e_icon_list_get_icon_data (EIconList *gil,
+ int idx);
+
+/* Visibility */
+void e_icon_list_moveto (EIconList *gil,
+ int idx, double yalign);
+GtkVisibility e_icon_list_icon_is_visible (EIconList *gil,
+ int idx);
+
+int e_icon_list_get_icon_at (EIconList *gil,
+ int x, int y);
+
+int e_icon_list_get_items_per_line (EIconList *gil);
+
+END_GNOME_DECLS
+
+#endif /* _GNOME_ICON_LIST_H_ */
diff --git a/composer/e-msg-composer-attachment-bar.c b/composer/e-msg-composer-attachment-bar.c
index 8a6272d833..9dd4cf6a49 100644
--- a/composer/e-msg-composer-attachment-bar.c
+++ b/composer/e-msg-composer-attachment-bar.c
@@ -24,11 +24,16 @@
#include <gnome.h>
#include <glade/glade.h>
#include <libgnomevfs/gnome-vfs-mime-info.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk-pixbuf/gdk-pixbuf-loader.h>
+#include <gdk-pixbuf/gnome-canvas-pixbuf.h>
#include "e-msg-composer-attachment.h"
#include "e-msg-composer-attachment-bar.h"
+#include "e-icon-list.h"
#include "camel/camel-data-wrapper.h"
#include "camel/camel-stream-fs.h"
+#include "camel/camel-stream-mem.h"
#include "camel/camel-mime-part.h"
@@ -41,7 +46,7 @@
#define ICON_TEXT_SPACING 2
-static GnomeIconListClass *parent_class = NULL;
+static EIconListClass *parent_class = NULL;
struct _EMsgComposerAttachmentBarPrivate {
GList *attachments;
@@ -173,26 +178,28 @@ static void
update (EMsgComposerAttachmentBar *bar)
{
EMsgComposerAttachmentBarPrivate *priv;
- GnomeIconList *icon_list;
+ EIconList *icon_list;
GList *p;
priv = bar->priv;
- icon_list = GNOME_ICON_LIST (bar);
+ icon_list = E_ICON_LIST (bar);
- gnome_icon_list_freeze (icon_list);
+ e_icon_list_freeze (icon_list);
- gnome_icon_list_clear (icon_list);
+ e_icon_list_clear (icon_list);
/* FIXME could be faster, but we don't care. */
-
for (p = priv->attachments; p != NULL; p = p->next) {
EMsgComposerAttachment *attachment;
const gchar *icon_name, *desc;
gchar *size_string, *label, *mime_type;
GMimeContentField *content_type;
-
+ GdkPixbuf *pixbuf;
+ gboolean image = FALSE;
+
attachment = p->data;
content_type = camel_mime_part_get_content_type (attachment->body);
+
mime_type = g_strdup_printf ("%s/%s", content_type->type,
content_type->subtype);
icon_name = gnome_vfs_mime_get_value (mime_type,
@@ -204,12 +211,90 @@ update (EMsgComposerAttachmentBar *bar)
icon_name = gnome_vfs_mime_get_value ("text/plain",
"icon-filename");
+ /* Get the image out of the attachment
+ and create a thumbnail for it */
+ if (strcmp (content_type->type, "image") == 0)
+ image = TRUE;
+ else
+ image = FALSE;
+
+ if (image && attachment->pixbuf_cache == NULL) {
+ CamelDataWrapper *wrapper;
+ CamelStream *mstream;
+ GdkPixbufLoader *loader;
+ gboolean error = TRUE;
+ char tmp[4096];
+ int t;
+
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body));
+ mstream = camel_stream_mem_new ();
+
+ camel_data_wrapper_write_to_stream (wrapper, mstream);
+
+ camel_stream_reset (mstream);
+
+ /* Stream image into pixbuf loader */
+ loader = gdk_pixbuf_loader_new ();
+ do {
+ t = camel_stream_read (mstream, tmp, 4096);
+ if (t > 0) {
+ error = !gdk_pixbuf_loader_write (loader,
+ tmp, t);
+ if (error) {
+ break;
+ }
+ } else {
+ if (camel_stream_eos (mstream))
+ break;
+ error = TRUE;
+ break;
+ }
+
+ } while (!camel_stream_eos (mstream));
+
+ if (!error) {
+ int ratio, width, height;
+
+ /* Shrink pixbuf */
+ pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ if (width >= height) {
+ if (width > 48) {
+ ratio = width / 48;
+ width = 48;
+ height = height / ratio;
+ }
+ } else {
+ if (height > 48) {
+ ratio = height / 48;
+ height = 48;
+ width = width / ratio;
+ }
+ }
+
+ attachment->pixbuf_cache = gdk_pixbuf_scale_simple
+ (pixbuf,
+ width,
+ height,
+ GDK_INTERP_BILINEAR);
+ } else {
+ g_warning ("GdkPixbufLoader Error");
+ image = FALSE;
+ }
+
+ /* Destroy everything */
+ gdk_pixbuf_loader_close (loader);
+ gtk_object_destroy (GTK_OBJECT (loader));
+ camel_stream_close (mstream);
+ }
+
desc = camel_mime_part_get_description (attachment->body);
if (!desc)
desc = camel_mime_part_get_filename (attachment->body);
if (!desc)
desc = "attachment";
-
+
if (attachment->size) {
size_string = size_to_string (attachment->size);
label = g_strdup_printf ("%s (%s)", desc, size_string);
@@ -217,30 +302,36 @@ update (EMsgComposerAttachmentBar *bar)
} else
label = g_strdup (desc);
- gnome_icon_list_append (icon_list, icon_name, label);
+ if (image) {
+ e_icon_list_append_pixbuf (icon_list, attachment->pixbuf_cache, icon_name, label);
+ } else {
+ e_icon_list_append (icon_list, icon_name, label);
+ }
+
g_free (label);
}
- gnome_icon_list_thaw (icon_list);
+ e_icon_list_thaw (icon_list);
}
static void
remove_selected (EMsgComposerAttachmentBar *bar)
{
- GnomeIconList *icon_list;
+ EIconList *icon_list;
EMsgComposerAttachment *attachment;
GList *attachment_list;
GList *p;
gint num;
- icon_list = GNOME_ICON_LIST (bar);
+ icon_list = E_ICON_LIST (bar);
/* Weee! I am especially proud of this piece of cheesy code: it is
truly awful. But unless one attaches a huge number of files, it
will not be as greedy as intended. FIXME of course. */
attachment_list = NULL;
- for (p = icon_list->selection; p != NULL; p = p->next) {
+ p = e_icon_list_get_selection (icon_list);
+ for (; p != NULL; p = p->next) {
num = GPOINTER_TO_INT (p->data);
attachment = E_MSG_COMPOSER_ATTACHMENT
(g_list_nth (bar->priv->attachments, num)->data);
@@ -258,13 +349,15 @@ remove_selected (EMsgComposerAttachmentBar *bar)
static void
edit_selected (EMsgComposerAttachmentBar *bar)
{
- GnomeIconList *icon_list;
+ EIconList *icon_list;
EMsgComposerAttachment *attachment;
+ GList *selection;
gint num;
- icon_list = GNOME_ICON_LIST (bar);
+ icon_list = E_ICON_LIST (bar);
- num = GPOINTER_TO_INT (icon_list->selection->data);
+ selection = e_icon_list_get_selection (icon_list);
+ num = GPOINTER_TO_INT (selection->data);
attachment = g_list_nth (bar->priv->attachments, num)->data;
e_msg_composer_attachment_edit (attachment, GTK_WIDGET (bar));
@@ -378,7 +471,7 @@ get_icon_context_menu (EMsgComposerAttachmentBar *bar)
priv = bar->priv;
if (priv->icon_context_menu == NULL)
priv->icon_context_menu = gnome_popup_menu_new
- (icon_context_menu_info);
+ (icon_context_menu_info);
return priv->icon_context_menu;
}
@@ -447,21 +540,21 @@ button_press_event (GtkWidget *widget,
GdkEventButton *event)
{
EMsgComposerAttachmentBar *bar;
- GnomeIconList *icon_list;
+ EIconList *icon_list;
gint icon_number;
bar = E_MSG_COMPOSER_ATTACHMENT_BAR (widget);
- icon_list = GNOME_ICON_LIST (widget);
+ icon_list = E_ICON_LIST (widget);
if (event->button != 3)
return GTK_WIDGET_CLASS (parent_class)->button_press_event
- (widget, event);
+ (widget, event);
- icon_number = gnome_icon_list_get_icon_at (icon_list,
- event->x, event->y);
+ icon_number = e_icon_list_get_icon_at (icon_list,
+ event->x, event->y);
if (icon_number >= 0) {
- gnome_icon_list_select_icon (icon_list, icon_number);
+ e_icon_list_select_icon (icon_list, icon_number);
popup_icon_context_menu (bar, icon_number, event);
} else {
popup_context_menu (bar, event);
@@ -478,13 +571,13 @@ class_init (EMsgComposerAttachmentBarClass *class)
{
GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
- GnomeIconListClass *icon_list_class;
+ EIconListClass *icon_list_class;
object_class = GTK_OBJECT_CLASS (class);
widget_class = GTK_WIDGET_CLASS (class);
- icon_list_class = GNOME_ICON_LIST_CLASS (class);
+ icon_list_class = E_ICON_LIST_CLASS (class);
- parent_class = gtk_type_class (gnome_icon_list_get_type ());
+ parent_class = gtk_type_class (e_icon_list_get_type ());
object_class->destroy = destroy;
@@ -546,7 +639,7 @@ e_msg_composer_attachment_bar_get_type (void)
(GtkClassInitFunc) NULL,
};
- type = gtk_type_unique (gnome_icon_list_get_type (), &info);
+ type = gtk_type_unique (e_icon_list_get_type (), &info);
}
return type;
@@ -556,24 +649,25 @@ GtkWidget *
e_msg_composer_attachment_bar_new (GtkAdjustment *adj)
{
EMsgComposerAttachmentBar *new;
- GnomeIconList *icon_list;
+ EIconList *icon_list;
- gtk_widget_push_visual (gdk_imlib_get_visual ());
- gtk_widget_push_colormap (gdk_imlib_get_colormap ());
+ gdk_rgb_init ();
+ gtk_widget_push_visual (gdk_rgb_get_visual ());
+ gtk_widget_push_colormap (gdk_rgb_get_cmap ());
new = gtk_type_new (e_msg_composer_attachment_bar_get_type ());
gtk_widget_pop_visual ();
gtk_widget_pop_colormap ();
- icon_list = GNOME_ICON_LIST (new);
+ icon_list = E_ICON_LIST (new);
- gnome_icon_list_construct (icon_list, ICON_WIDTH, adj, 0);
+ e_icon_list_construct (icon_list, ICON_WIDTH, 0);
- gnome_icon_list_set_separators (icon_list, ICON_SEPARATORS);
- gnome_icon_list_set_row_spacing (icon_list, ICON_ROW_SPACING);
- gnome_icon_list_set_col_spacing (icon_list, ICON_COL_SPACING);
- gnome_icon_list_set_icon_border (icon_list, ICON_BORDER);
- gnome_icon_list_set_text_spacing (icon_list, ICON_TEXT_SPACING);
- gnome_icon_list_set_selection_mode (icon_list, GTK_SELECTION_MULTIPLE);
+ e_icon_list_set_separators (icon_list, ICON_SEPARATORS);
+ e_icon_list_set_row_spacing (icon_list, ICON_ROW_SPACING);
+ e_icon_list_set_col_spacing (icon_list, ICON_COL_SPACING);
+ e_icon_list_set_icon_border (icon_list, ICON_BORDER);
+ e_icon_list_set_text_spacing (icon_list, ICON_TEXT_SPACING);
+ e_icon_list_set_selection_mode (icon_list, GTK_SELECTION_MULTIPLE);
return GTK_WIDGET (new);
}
diff --git a/composer/e-msg-composer-attachment-bar.h b/composer/e-msg-composer-attachment-bar.h
index 1f331c2870..17d717cc10 100644
--- a/composer/e-msg-composer-attachment-bar.h
+++ b/composer/e-msg-composer-attachment-bar.h
@@ -25,6 +25,7 @@
#define __E_MSG_COMPOSER_ATTACHMENT_BAR_H__
#include <gnome.h>
+#include "e-icon-list.h"
#include <camel/camel-multipart.h>
#ifdef __cplusplus
@@ -47,14 +48,14 @@ extern "C" {
typedef struct _EMsgComposerAttachmentBarPrivate EMsgComposerAttachmentBarPrivate;
struct _EMsgComposerAttachmentBar {
- GnomeIconList parent;
+ EIconList parent;
EMsgComposerAttachmentBarPrivate *priv;
};
typedef struct _EMsgComposerAttachmentBar EMsgComposerAttachmentBar;
struct _EMsgComposerAttachmentBarClass {
- GnomeIconListClass parent_class;
+ EIconListClass parent_class;
void (* changed) (EMsgComposerAttachmentBar *bar);
};
diff --git a/composer/e-msg-composer-attachment.c b/composer/e-msg-composer-attachment.c
index c607b59068..7d1e61a883 100644
--- a/composer/e-msg-composer-attachment.c
+++ b/composer/e-msg-composer-attachment.c
@@ -84,6 +84,8 @@ destroy (GtkObject *object)
attachment = E_MSG_COMPOSER_ATTACHMENT (object);
camel_object_unref (CAMEL_OBJECT (attachment->body));
+ if (attachment->pixbuf_cache != NULL)
+ gdk_pixbuf_unref (attachment->pixbuf_cache);
}
@@ -129,6 +131,7 @@ init (EMsgComposerAttachment *msg_composer_attachment)
msg_composer_attachment->editor_gui = NULL;
msg_composer_attachment->body = NULL;
msg_composer_attachment->size = 0;
+ msg_composer_attachment->pixbuf_cache = NULL;
}
GtkType
diff --git a/composer/e-msg-composer-attachment.h b/composer/e-msg-composer-attachment.h
index e0cd2eb867..47bc85ece0 100644
--- a/composer/e-msg-composer-attachment.h
+++ b/composer/e-msg-composer-attachment.h
@@ -26,6 +26,7 @@
#include <gnome.h>
#include <glade/glade-xml.h>
#include <camel/camel-mime-part.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
#ifdef __cplusplus
extern "C" {
@@ -50,6 +51,8 @@ struct _EMsgComposerAttachment {
CamelMimePart *body;
gboolean guessed_type;
gulong size;
+
+ GdkPixbuf *pixbuf_cache;
};
struct _EMsgComposerAttachmentClass {
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index 1d191c8e25..5f932141b2 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -1166,6 +1166,39 @@ delete_event (GtkWidget *widget,
return TRUE;
}
+static void
+drag_data_received (EMsgComposer *composer,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ gchar *temp, *filename;
+
+ filename = g_strdup (selection->data);
+ temp = strchr (filename, '\n');
+ if (temp) {
+ if (*(temp - 1) == '\r')
+ *(temp - 1) = '\0';
+ *temp = '\0';
+ }
+
+ /* Chop the file: part off */
+ if (strncasecmp (filename, "file:", 5) == 0) {
+ temp = g_strdup (filename + 5);
+ g_free (filename);
+ filename = temp;
+ }
+
+ e_msg_composer_attachment_bar_attach
+ (E_MSG_COMPOSER_ATTACHMENT_BAR (composer->attachment_bar),
+ filename);
+
+ g_free (filename);
+}
+
static void
class_init (EMsgComposerClass *klass)
@@ -1259,7 +1292,11 @@ e_msg_composer_construct (EMsgComposer *composer)
{
GtkWidget *vbox;
BonoboObject *editor_server;
-
+
+ static GtkTargetEntry drop_types[] = {
+ {"text/uri-list", 0, 1}
+ };
+
g_return_if_fail (gtk_main_level () > 0);
gtk_window_set_default_size (GTK_WINDOW (composer),
@@ -1268,6 +1305,12 @@ e_msg_composer_construct (EMsgComposer *composer)
bonobo_win_construct (BONOBO_WIN (composer), "e-msg-composer",
_("Compose a message"));
+ /* DND support */
+ gtk_drag_dest_set (GTK_WIDGET (composer), GTK_DEST_DEFAULT_ALL,
+ drop_types, 1, GDK_ACTION_COPY);
+ gtk_signal_connect (GTK_OBJECT (composer), "drag_data_received",
+ GTK_SIGNAL_FUNC (drag_data_received), NULL);
+
composer->uih = bonobo_ui_handler_new ();
bonobo_ui_handler_set_app (composer->uih, BONOBO_WIN (composer));
@@ -1407,6 +1450,8 @@ e_msg_composer_new_with_message (CamelMimeMessage *msg)
char *text, *final_text;
guint len, i;
+ g_return_val_if_fail (gtk_main_level () > 0, NULL);
+
new = create_composer ();
if (!new)
return NULL;