aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/shortcut-bar/e-icon-bar.c
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/shortcut-bar/e-icon-bar.c')
-rw-r--r--widgets/shortcut-bar/e-icon-bar.c1450
1 files changed, 1450 insertions, 0 deletions
diff --git a/widgets/shortcut-bar/e-icon-bar.c b/widgets/shortcut-bar/e-icon-bar.c
new file mode 100644
index 0000000000..6bb32ed2a2
--- /dev/null
+++ b/widgets/shortcut-bar/e-icon-bar.c
@@ -0,0 +1,1450 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Author :
+ * Damon Chaplin <damon@gtk.org>
+ *
+ * Copyright 1999, Helix Code, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * EIconBar is a subclass of GnomeCanvas for displaying a vertical column of
+ * icons and descriptions. It provides 2 views - large icons and small icons.
+ */
+
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+#include <libgnomeui/gnome-canvas-image.h>
+
+#include "e-icon-bar.h"
+#include "e-icon-bar-bg-item.h"
+#include "e-icon-bar-text-item.h"
+
+/* These are the offsets of the icons & text in both views. Note that the
+ shadow around icons is drawn in the space between items (as is the
+ horizontal bar when dragging). */
+#define E_ICON_BAR_LARGE_ICON_SPACING 8 /* Spacing between items. */
+#define E_ICON_BAR_LARGE_ICON_WIDTH 48
+#define E_ICON_BAR_LARGE_ICON_HEIGHT 48
+#define E_ICON_BAR_LARGE_ICON_TEXT_X 4
+#define E_ICON_BAR_LARGE_ICON_TEXT_Y (E_ICON_BAR_LARGE_ICON_HEIGHT + 4)
+
+#define E_ICON_BAR_SMALL_ICON_SPACING 4 /* Spacing between items. */
+#define E_ICON_BAR_SMALL_ICON_WIDTH 24
+#define E_ICON_BAR_SMALL_ICON_HEIGHT 24
+#define E_ICON_BAR_SMALL_ICON_X 4
+#define E_ICON_BAR_SMALL_ICON_TEXT_X (E_ICON_BAR_SMALL_ICON_WIDTH + 4)
+
+/* The space we leave at the top or bottom of the bar when position an item
+ while it is being edited. This is used since the EIconBar may be in a
+ EScrolledBar which may show buttons at the top or bottom. */
+#define E_ICON_BAR_V_SPACE 22
+
+/* The number of pixels the mouse has to be moved with the button down before
+ we start a drag. */
+#define E_ICON_BAR_DRAG_START_OFFSET 4
+
+/* This is the area at the top & bottom of the bar where we auto-scroll if the
+ mouse goes into during a drag-and-drop operation. */
+#define E_ICON_BAR_DRAG_AUTO_SCROLL_OFFSET 16
+
+/* This is the time between each auto-scroll, when dragging. */
+#define E_ICON_BAR_SCROLL_TIMEOUT 30
+
+/* This is the number of timeouts we skip before we start scrolling. */
+#define E_ICON_BAR_SCROLL_DELAY 12
+
+
+static void e_icon_bar_class_init (EIconBarClass *class);
+static void e_icon_bar_init (EIconBar *icon_bar);
+static void e_icon_bar_destroy (GtkObject *object);
+static void e_icon_bar_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint e_icon_bar_leave_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event);
+static gint e_icon_bar_focus_in (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint e_icon_bar_focus_out (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint e_icon_bar_key_event (GtkWidget *widget, GdkEventKey *event);
+
+static gint e_icon_bar_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time);
+static void e_icon_bar_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time);
+static void e_icon_bar_set_dragging_before_item (EIconBar *icon_bar,
+ gint before_item);
+static gboolean e_icon_bar_timeout_handler (gpointer data);
+
+static void e_icon_bar_recalc_common_positions (EIconBar *icon_bar);
+static gint e_icon_bar_recalc_item_positions (EIconBar *icon_bar);
+static void e_icon_bar_on_text_height_changed (GnomeCanvasItem *text_item,
+ EIconBar *icon_bar);
+static gint e_icon_bar_find_item (EIconBar *icon_bar,
+ GnomeCanvasItem *text_item);
+static gboolean e_icon_bar_on_item_event (GnomeCanvasItem *item,
+ GdkEvent *event,
+ EIconBar *icon_bar);
+
+static gboolean e_icon_bar_large_icons_intersects (EIconBar *icon_bar,
+ EIconBarItem *item,
+ gint x,
+ gint y);
+static gboolean e_icon_bar_large_icons_is_before (EIconBar *icon_bar,
+ EIconBarItem *item,
+ gint x,
+ gint y);
+static gboolean e_icon_bar_small_icons_intersects (EIconBar *icon_bar,
+ EIconBarItem *item,
+ gint x,
+ gint y);
+static gboolean e_icon_bar_small_icons_is_before (EIconBar *icon_bar,
+ EIconBarItem *item,
+ gint x,
+ gint y);
+static void e_icon_bar_on_text_item_editing_started (EIconBarTextItem *text_item,
+ EIconBar *icon_bar);
+static void e_icon_bar_on_text_item_editing_stopped (EIconBarTextItem *text_item,
+ EIconBar *icon_bar);
+static void e_icon_bar_ensure_edited_item_visible (EIconBar *icon_bar);
+static void e_icon_bar_update_highlight (EIconBar *icon_bar);
+
+enum
+{
+ ITEM_SELECTED,
+ ITEM_DRAGGED,
+ LAST_SIGNAL
+};
+
+static guint e_icon_bar_signals[LAST_SIGNAL] = {0};
+
+static GnomeCanvasClass *parent_class;
+
+
+GtkType
+e_icon_bar_get_type (void)
+{
+ static GtkType e_icon_bar_type = 0;
+
+ if (!e_icon_bar_type){
+ GtkTypeInfo e_icon_bar_info = {
+ "EIconBar",
+ sizeof (EIconBar),
+ sizeof (EIconBarClass),
+ (GtkClassInitFunc) e_icon_bar_class_init,
+ (GtkObjectInitFunc) e_icon_bar_init,
+ NULL, /* reserved 1 */
+ NULL, /* reserved 2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ parent_class = gtk_type_class (gnome_canvas_get_type ());
+ e_icon_bar_type = gtk_type_unique (gnome_canvas_get_type (),
+ &e_icon_bar_info);
+ }
+
+ return e_icon_bar_type;
+}
+
+
+static void
+e_icon_bar_class_init (EIconBarClass *class)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) class;
+ widget_class = (GtkWidgetClass *) class;
+
+ e_icon_bar_signals[ITEM_SELECTED] =
+ gtk_signal_new ("item_selected",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconBarClass,
+ selected_item),
+ gtk_marshal_NONE__POINTER_INT,
+ GTK_TYPE_NONE, 2, GTK_TYPE_GDK_EVENT,
+ GTK_TYPE_INT);
+ e_icon_bar_signals[ITEM_DRAGGED] =
+ gtk_signal_new ("item_dragged",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (EIconBarClass,
+ dragged_item),
+ gtk_marshal_NONE__POINTER_INT,
+ GTK_TYPE_NONE, 2, GTK_TYPE_GDK_EVENT,
+ GTK_TYPE_INT);
+
+ gtk_object_class_add_signals (object_class, e_icon_bar_signals,
+ LAST_SIGNAL);
+
+ /* Method override */
+ object_class->destroy = e_icon_bar_destroy;
+
+ widget_class->size_allocate = e_icon_bar_size_allocate;
+ widget_class->leave_notify_event = e_icon_bar_leave_notify_event;
+ widget_class->focus_in_event = e_icon_bar_focus_in;
+ widget_class->focus_out_event = e_icon_bar_focus_out;
+ widget_class->key_press_event = e_icon_bar_key_event;
+ widget_class->key_release_event = e_icon_bar_key_event;
+ widget_class->drag_motion = e_icon_bar_drag_motion;
+ widget_class->drag_leave = e_icon_bar_drag_leave;
+
+ class->selected_item = NULL;
+}
+
+
+static void
+e_icon_bar_init (EIconBar *icon_bar)
+{
+ icon_bar->view_type = E_ICON_BAR_LARGE_ICONS;
+ icon_bar->items = g_array_new (FALSE, FALSE, sizeof (EIconBarItem));
+ icon_bar->pressed_item_num = -1;
+ icon_bar->mouse_over_item_num = -1;
+ icon_bar->editing_item_num = -1;
+ icon_bar->in_drag = FALSE;
+ icon_bar->dragging_before_item_num = -1;
+ icon_bar->icon_x = 0;
+ icon_bar->icon_w = 0;
+ icon_bar->icon_h = 0;
+ icon_bar->text_x = 0;
+ icon_bar->text_w = 5;
+ icon_bar->auto_scroll_timeout_id = 0;
+
+ /* Create the background item in the canvas, which handles selections
+ and drag-and-drop. */
+ gnome_canvas_item_new (GNOME_CANVAS_GROUP (GNOME_CANVAS (icon_bar)->root),
+ e_icon_bar_bg_item_get_type (),
+ "EIconBarBgItem::icon_bar", icon_bar,
+ NULL);
+}
+
+
+/**
+ * e_icon_bar_new:
+ * @Returns: A new #EIconBar.
+ *
+ * Creates a new #EIconBar.
+ **/
+GtkWidget *
+e_icon_bar_new (void)
+{
+ GtkWidget *icon_bar;
+
+ icon_bar = GTK_WIDGET (gtk_type_new (e_icon_bar_get_type ()));
+
+ return icon_bar;
+}
+
+
+static void
+e_icon_bar_destroy (GtkObject *object)
+{
+ EIconBar *icon_bar;
+
+ icon_bar = E_ICON_BAR (object);
+
+ GTK_OBJECT_CLASS (parent_class)->destroy (object);
+
+ g_array_free (icon_bar->items, TRUE);
+
+ if (icon_bar->auto_scroll_timeout_id != 0) {
+ gtk_timeout_remove (icon_bar->auto_scroll_timeout_id);
+ icon_bar->auto_scroll_timeout_id = 0;
+ }
+}
+
+
+static void
+e_icon_bar_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ EIconBar *icon_bar;
+ gint canvas_width, canvas_height, height;
+
+ icon_bar = E_ICON_BAR (widget);
+
+ GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
+
+ canvas_width = GTK_WIDGET (icon_bar)->allocation.width;
+ canvas_height = GTK_WIDGET (icon_bar)->allocation.height;
+
+ /* Reset the y position and widths of all the items to the width of
+ the canvas, and reset the button labels, so they fit. */
+ e_icon_bar_recalc_common_positions (icon_bar);
+ height = e_icon_bar_recalc_item_positions (icon_bar);
+
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (widget),
+ 0, 0, canvas_width,
+ MAX (height, canvas_height - 1));
+
+ /* If we are editing an item, make sure it is visible. */
+ e_icon_bar_ensure_edited_item_visible (icon_bar);
+
+ GTK_LAYOUT (widget)->vadjustment->step_increment = 16;
+
+ e_icon_bar_update_highlight (icon_bar);
+}
+
+
+/* This sets all the item positions which are the same for all items in the
+ group. */
+static void
+e_icon_bar_recalc_common_positions (EIconBar *icon_bar)
+{
+ gint canvas_width;
+
+ canvas_width = GTK_WIDGET (icon_bar)->allocation.width;
+
+ if (icon_bar->view_type == E_ICON_BAR_LARGE_ICONS) {
+ icon_bar->icon_x = (canvas_width - E_ICON_BAR_LARGE_ICON_WIDTH) / 2;
+ icon_bar->icon_w = E_ICON_BAR_LARGE_ICON_WIDTH;
+ icon_bar->icon_h = E_ICON_BAR_LARGE_ICON_HEIGHT;
+
+ icon_bar->text_x = E_ICON_BAR_LARGE_ICON_TEXT_X;
+ icon_bar->text_w = MAX (canvas_width - (E_ICON_BAR_LARGE_ICON_TEXT_X * 2), 5);
+
+ icon_bar->spacing = E_ICON_BAR_LARGE_ICON_SPACING;
+ } else {
+ icon_bar->icon_x = E_ICON_BAR_SMALL_ICON_X;
+ icon_bar->icon_w = E_ICON_BAR_SMALL_ICON_WIDTH;
+ icon_bar->icon_h = E_ICON_BAR_SMALL_ICON_HEIGHT;
+
+ icon_bar->text_x = E_ICON_BAR_SMALL_ICON_TEXT_X;
+ icon_bar->text_w = MAX (canvas_width - E_ICON_BAR_SMALL_ICON_TEXT_X, 5);
+
+ icon_bar->spacing = E_ICON_BAR_SMALL_ICON_SPACING;
+ }
+}
+
+
+/* This recalculates the positions of all the items, according to the current
+ view type and the height of the text items. */
+static gint
+e_icon_bar_recalc_item_positions (EIconBar *icon_bar)
+{
+ EIconBarItem *item;
+ gint y, item_num;
+ gdouble x1, y1, x2, y2, xalign;
+ GtkJustification justify;
+ gint max_lines;
+
+ if (icon_bar->view_type == E_ICON_BAR_LARGE_ICONS) {
+ xalign = 0.5;
+ justify = GTK_JUSTIFY_CENTER;
+ max_lines = 2;
+ } else {
+ xalign = 0.0;
+ justify = GTK_JUSTIFY_LEFT;
+ max_lines = 1;
+ }
+
+ /* Now step through the items, setting the y positions. */
+ y = icon_bar->spacing;
+ for (item_num = 0; item_num < icon_bar->items->len; item_num++) {
+ item = &g_array_index (icon_bar->items,
+ EIconBarItem, item_num);
+
+ e_icon_bar_text_item_set_width (E_ICON_BAR_TEXT_ITEM (item->text),
+ icon_bar->text_w);
+
+ /* Get the text item's height. */
+ gnome_canvas_item_get_bounds (item->text, &x1, &y1, &x2, &y2);
+ item->text_width = x2 - x1;
+ item->text_height = y2 - y1;
+
+ if (icon_bar->view_type == E_ICON_BAR_LARGE_ICONS) {
+ item->icon_y = y;
+ item->text_y = y + E_ICON_BAR_LARGE_ICON_TEXT_Y;
+
+ item->item_height = E_ICON_BAR_LARGE_ICON_TEXT_Y
+ + item->text_height;
+ } else {
+ item->item_height = MAX (item->text_height, E_ICON_BAR_SMALL_ICON_HEIGHT);
+ item->icon_y = y + (item->item_height - E_ICON_BAR_SMALL_ICON_HEIGHT) / 2;
+ item->text_y = y + (item->item_height - item->text_height) / 2;
+ }
+
+ e_icon_bar_text_item_setxy (E_ICON_BAR_TEXT_ITEM (item->text),
+ icon_bar->text_x, item->text_y);
+
+ /* We need to get the bounds again, in case it has moved. */
+ gnome_canvas_item_get_bounds (item->text, &x1, &y1, &x2, &y2);
+ item->text_x = x1;
+
+ gnome_canvas_item_set (item->text,
+ "EIconBarTextItem::xalign", xalign,
+ "EIconBarTextItem::justify", justify,
+ "EIconBarTextItem::max_lines", max_lines,
+ NULL);
+
+ gnome_canvas_item_set (item->image,
+ "GnomeCanvasImage::x", (gdouble)icon_bar->icon_x,
+ "GnomeCanvasImage::y", (gdouble)item->icon_y,
+ "GnomeCanvasImage::width", (gdouble)icon_bar->icon_w,
+ "GnomeCanvasImage::height", (gdouble)icon_bar->icon_h,
+ NULL);
+
+ y += item->item_height + icon_bar->spacing;
+ }
+
+ return y;
+}
+
+
+static gint
+e_icon_bar_leave_notify_event (GtkWidget *widget, GdkEventCrossing *event)
+{
+ EIconBar *icon_bar;
+
+ icon_bar = E_ICON_BAR (widget);
+
+ GTK_WIDGET_CLASS (parent_class)->leave_notify_event (widget, event);
+
+ /* Make sure no items are highlighted. */
+ e_icon_bar_item_motion (icon_bar, -1, NULL);
+
+ return FALSE;
+}
+
+
+static gint
+e_icon_bar_focus_in (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (E_IS_ICON_BAR (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ GTK_WIDGET_CLASS (parent_class)->focus_in_event (widget, event);
+ GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+ return FALSE;
+}
+
+
+static gint
+e_icon_bar_focus_out (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (E_IS_ICON_BAR (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+ return FALSE;
+}
+
+
+/* Key event handler for the canvas.
+ FIXME: GnomeCanvas bug workaround - I needed to override this to stop the
+ canvas ignoring key events from other windows. */
+static gint
+e_icon_bar_key_event (GtkWidget *widget, GdkEventKey *event)
+{
+ GnomeCanvas *canvas;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (E_IS_ICON_BAR (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ canvas = GNOME_CANVAS (widget);
+
+ if (event->window != canvas->layout.bin_window) {
+ /* We change the window in the event struct so the canvas
+ doesn't ignore the event. Note that windows are ref-counted
+ in the event struct. */
+ if (event->window)
+ gdk_window_unref (event->window);
+ event->window = canvas->layout.bin_window;
+ gdk_window_ref (event->window);
+ }
+
+ /* These both call the same function at present, but we'll do it
+ properly just in case that changes. */
+ if (event->type == GDK_KEY_PRESS)
+ return (*GTK_WIDGET_CLASS (parent_class)->key_press_event)(widget, event);
+ else
+ return (*GTK_WIDGET_CLASS (parent_class)->key_release_event)(widget, event);
+}
+
+
+/**
+ * e_icon_bar_set_view_type:
+ * @icon_bar: An #EIconBar.
+ * @view_type: The new view type, %E_ICON_BAR_LARGE_ICONS or
+ * %E_ICON_BAR_SMALL_ICONS.
+ *
+ * Sets the view type of the #EIconBar.
+ **/
+void
+e_icon_bar_set_view_type (EIconBar *icon_bar,
+ EIconBarViewType view_type)
+{
+ g_return_if_fail (E_IS_ICON_BAR (icon_bar));
+
+ if (icon_bar->view_type == view_type)
+ return;
+
+ icon_bar->view_type = view_type;
+
+ /* Queue a resize of the canvas, so everything is put in the right
+ positions based on the new view type. */
+ gtk_widget_queue_resize (GTK_WIDGET (icon_bar));
+}
+
+
+/**
+ * e_icon_bar_add_item:
+ * @icon_bar: An #EIconBar.
+ * @image: the new item's icon.
+ * @text: the new item's text.
+ * @position: the position to place the new item, or -1 to place it last.
+ *
+ * Adds an item to the #EIconBar at the given position.
+ **/
+gint
+e_icon_bar_add_item (EIconBar *icon_bar,
+ GdkImlibImage *image,
+ gchar *text,
+ gint position)
+{
+ EIconBarItem item;
+ gfloat xalign;
+ GtkJustification justify;
+ gint max_lines, retval;
+
+ g_return_val_if_fail (E_IS_ICON_BAR (icon_bar), -1);
+ g_return_val_if_fail (text != NULL, -1);
+ g_return_val_if_fail (position >= -1, -1);
+ g_return_val_if_fail (position <= (gint)icon_bar->items->len, -1);
+
+ if (icon_bar->view_type == E_ICON_BAR_LARGE_ICONS) {
+ xalign = 0.5;
+ justify = GTK_JUSTIFY_CENTER;
+ max_lines = 2;
+ } else {
+ xalign = 0.0;
+ justify = GTK_JUSTIFY_LEFT;
+ max_lines = 1;
+ }
+
+ item.text = gnome_canvas_item_new (GNOME_CANVAS_GROUP (GNOME_CANVAS (icon_bar)->root),
+ e_icon_bar_text_item_get_type (),
+ "EIconBarTextItem::xalign", xalign,
+ "EIconBarTextItem::justify", justify,
+ "EIconBarTextItem::max_lines", max_lines,
+ NULL);
+ e_icon_bar_text_item_configure (E_ICON_BAR_TEXT_ITEM (item.text),
+ icon_bar->text_x, 0,
+ icon_bar->text_w, NULL,
+ text, FALSE);
+ gtk_signal_connect (GTK_OBJECT (item.text), "height_changed",
+ GTK_SIGNAL_FUNC (e_icon_bar_on_text_height_changed), icon_bar);
+ gtk_signal_connect (GTK_OBJECT (item.text), "event",
+ GTK_SIGNAL_FUNC (e_icon_bar_on_item_event),
+ icon_bar);
+ gtk_signal_connect (GTK_OBJECT (item.text), "editing_started",
+ GTK_SIGNAL_FUNC (e_icon_bar_on_text_item_editing_started),
+ icon_bar);
+ gtk_signal_connect (GTK_OBJECT (item.text), "editing_stopped",
+ GTK_SIGNAL_FUNC (e_icon_bar_on_text_item_editing_stopped),
+ icon_bar);
+
+ item.image = gnome_canvas_item_new (GNOME_CANVAS_GROUP (GNOME_CANVAS (icon_bar)->root),
+ gnome_canvas_image_get_type (),
+ "GnomeCanvasImage::image", image,
+ "GnomeCanvasImage::anchor", GTK_ANCHOR_NORTH_WEST,
+ "GnomeCanvasImage::width", (gdouble) icon_bar->icon_w,
+ "GnomeCanvasImage::height", (gdouble) icon_bar->icon_h,
+ NULL);
+ gtk_signal_connect (GTK_OBJECT (item.image), "event",
+ GTK_SIGNAL_FUNC (e_icon_bar_on_item_event),
+ icon_bar);
+
+ item.data = NULL;
+ item.destroy = NULL;
+
+ if (position == -1) {
+ g_array_append_val (icon_bar->items, item);
+ retval = icon_bar->items->len - 1;
+ } else {
+ g_array_insert_val (icon_bar->items, position, item);
+ retval = position;
+
+ /* FIXME: Should possibly update other indices. */
+ if (icon_bar->dragged_item_num >= position)
+ icon_bar->dragged_item_num++;
+ }
+
+ gtk_widget_queue_resize (GTK_WIDGET (icon_bar));
+
+ return retval;
+}
+
+
+/**
+ * e_icon_bar_reorder_item:
+ * @icon_bar: An #EIconBar.
+ * @item_num: The index of the item to move.
+ * @new_position: The new position of the item, which is used after the item
+ * has been removed from its current position. If @new_position is -1, the item
+ * is placed last.
+ *
+ * Moves an item to a new position within the #EIconBar.
+ **/
+void
+e_icon_bar_reorder_item (EIconBar *icon_bar,
+ gint item_num,
+ gint new_position)
+{
+ EIconBarItem tmp_item;
+
+ g_return_if_fail (E_IS_ICON_BAR (icon_bar));
+ g_return_if_fail (item_num >= 0);
+ g_return_if_fail (item_num < icon_bar->items->len);
+ g_return_if_fail (new_position >= -1);
+ g_return_if_fail (new_position < icon_bar->items->len);
+
+ tmp_item = g_array_index (icon_bar->items, EIconBarItem, item_num);
+ g_array_remove_index (icon_bar->items, item_num);
+
+ if (new_position == -1)
+ g_array_append_val (icon_bar->items, tmp_item);
+ else
+ g_array_insert_val (icon_bar->items, new_position, tmp_item);
+
+ gtk_widget_queue_resize (GTK_WIDGET (icon_bar));
+}
+
+
+/**
+ * e_icon_bar_remove_item:
+ * @icon_bar: An #EIconBar.
+ * @item_num: The index of the item to remove.
+ *
+ * Removes an item from the #EIconBar.
+ **/
+void
+e_icon_bar_remove_item (EIconBar *icon_bar,
+ gint item_num)
+{
+ EIconBarItem *item;
+
+ g_return_if_fail (E_IS_ICON_BAR (icon_bar));
+ g_return_if_fail (item_num >= 0);
+ g_return_if_fail (item_num < icon_bar->items->len);
+
+ item = &g_array_index (icon_bar->items, EIconBarItem, item_num);
+
+ if (item->destroy)
+ item->destroy (item->data);
+
+ gtk_object_destroy (GTK_OBJECT (item->text));
+ gtk_object_destroy (GTK_OBJECT (item->image));
+
+ g_array_remove_index (icon_bar->items, item_num);
+
+ gtk_widget_queue_resize (GTK_WIDGET (icon_bar));
+}
+
+
+/**
+ * e_icon_bar_get_item_image:
+ * @icon_bar: An #EIconBar.
+ * @item_num: The index of the item.
+ * @Returns: The icon of the given item.
+ *
+ * Returns the icon used for the given item.
+ **/
+GdkImlibImage*
+e_icon_bar_get_item_image (EIconBar *icon_bar,
+ gint item_num)
+{
+ EIconBarItem *item;
+ GdkImlibImage *image;
+
+ g_return_val_if_fail (E_IS_ICON_BAR (icon_bar), NULL);
+ g_return_val_if_fail (item_num >= 0, NULL);
+ g_return_val_if_fail (item_num < icon_bar->items->len, NULL);
+
+ item = &g_array_index (icon_bar->items, EIconBarItem, item_num);
+ gtk_object_get (GTK_OBJECT (item->image),
+ "GnomeCanvasImage::image", image,
+ NULL);
+ return image;
+}
+
+
+/**
+ * e_icon_bar_set_item_image:
+ * @icon_bar: An #EIconBar.
+ * @item_num: The index of the item.
+ * @image: The new icon to use for the given item.
+ *
+ * Sets the icon to use for the given item.
+ **/
+void
+e_icon_bar_set_item_image (EIconBar *icon_bar,
+ gint item_num,
+ GdkImlibImage *image)
+{
+ EIconBarItem *item;
+
+ g_return_if_fail (E_IS_ICON_BAR (icon_bar));
+ g_return_if_fail (item_num >= 0);
+ g_return_if_fail (item_num < icon_bar->items->len);
+
+ item = &g_array_index (icon_bar->items, EIconBarItem, item_num);
+ gnome_canvas_item_set (item->image,
+ "GnomeCanvasImage::image", image,
+ NULL);
+}
+
+
+/**
+ * e_icon_bar_get_item_text:
+ * @icon_bar: An #EIconBar.
+ * @item_num: The index of the item.
+ * @Returns: The text of the given item.
+ *
+ * Returns the text of the given item.
+ **/
+gchar*
+e_icon_bar_get_item_text (EIconBar *icon_bar,
+ gint item_num)
+{
+ EIconBarItem *item;
+
+ g_return_val_if_fail (E_IS_ICON_BAR (icon_bar), NULL);
+ g_return_val_if_fail (item_num >= 0, NULL);
+ g_return_val_if_fail (item_num < icon_bar->items->len, NULL);
+
+ item = &g_array_index (icon_bar->items, EIconBarItem, item_num);
+ return e_icon_bar_text_item_get_text (E_ICON_BAR_TEXT_ITEM (item->text));
+}
+
+
+/**
+ * e_icon_bar_set_item_text:
+ * @icon_bar: An #EIconBar.
+ * @item_num: The index of the item.
+ * @text: The new text for the given item.
+ *
+ * Sets the text of the given item.
+ **/
+void
+e_icon_bar_set_item_text (EIconBar *icon_bar,
+ gint item_num,
+ gchar *text)
+{
+ EIconBarItem *item;
+
+ g_return_if_fail (E_IS_ICON_BAR (icon_bar));
+ g_return_if_fail (item_num >= 0);
+ g_return_if_fail (item_num < icon_bar->items->len);
+
+ item = &g_array_index (icon_bar->items, EIconBarItem, item_num);
+ e_icon_bar_text_item_set_text (E_ICON_BAR_TEXT_ITEM (item->text),
+ text, FALSE);
+}
+
+
+/**
+ * e_icon_bar_get_item_data:
+ * @icon_bar: An #EIconBar.
+ * @item_num: The index of the item.
+ * @Returns: The user data associated with the given item.
+ *
+ * Returns the user data associated with the given item.
+ **/
+gpointer
+e_icon_bar_get_item_data (EIconBar *icon_bar,
+ gint item_num)
+{
+ EIconBarItem *item;
+
+ g_return_val_if_fail (E_IS_ICON_BAR (icon_bar), NULL);
+ g_return_val_if_fail (item_num >= 0, NULL);
+ g_return_val_if_fail (item_num < icon_bar->items->len, NULL);
+
+ item = &g_array_index (icon_bar->items, EIconBarItem, item_num);
+ return item->data;
+}
+
+
+/**
+ * e_icon_bar_set_item_data:
+ * @icon_bar: An #EIconBar.
+ * @item_num: The index of the item.
+ * @data: The user data to set for the given item.
+ *
+ * Sets the user data of the given item.
+ **/
+void
+e_icon_bar_set_item_data (EIconBar *icon_bar,
+ gint item_num,
+ gpointer data)
+{
+ e_icon_bar_set_item_data_full (icon_bar, item_num, data, NULL);
+}
+
+
+/**
+ * e_icon_bar_set_item_data_full:
+ * @icon_bar: An #EIconBar.
+ * @item_num: The index of the item.
+ * @data: The user data to set for the given item.
+ * @destroy: The function to free @data when the item is destroyed.
+ *
+ * Sets the user data of the given item, and the function to free the data
+ * when the item is destroyed.
+ **/
+void
+e_icon_bar_set_item_data_full (EIconBar *icon_bar,
+ gint item_num,
+ gpointer data,
+ GtkDestroyNotify destroy)
+{
+ EIconBarItem *item;
+
+ g_return_if_fail (E_IS_ICON_BAR (icon_bar));
+ g_return_if_fail (item_num >= 0);
+ g_return_if_fail (item_num < icon_bar->items->len);
+
+ item = &g_array_index (icon_bar->items, EIconBarItem, item_num);
+
+ if (item->destroy)
+ item->destroy (item->data);
+
+ item->data = data;
+ item->destroy = destroy;
+}
+
+
+static void
+e_icon_bar_on_text_height_changed (GnomeCanvasItem *text_item,
+ EIconBar *icon_bar)
+{
+ gtk_widget_queue_resize (GTK_WIDGET (icon_bar));
+}
+
+
+/* This returns the index of the given item, or -1 if it isn't found. */
+static gint
+e_icon_bar_find_item (EIconBar *icon_bar,
+ GnomeCanvasItem *canvas_item)
+{
+ EIconBarItem *item;
+ gint item_num;
+
+ for (item_num = 0; item_num < icon_bar->items->len; item_num++) {
+ item = &g_array_index (icon_bar->items,
+ EIconBarItem, item_num);
+
+ if (item->text == canvas_item || item->image == canvas_item) {
+ return item_num;
+ }
+ }
+
+ return -1;
+}
+
+
+/* When an item has a grab, it will get all events, so we need to use the
+ position to find the real item. */
+static gboolean
+e_icon_bar_on_item_event (GnomeCanvasItem *item,
+ GdkEvent *event,
+ EIconBar *icon_bar)
+{
+ gint item_num;
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ item_num = e_icon_bar_find_item_at_position (icon_bar,
+ event->button.x,
+ event->button.y,
+ NULL);
+ e_icon_bar_item_pressed (icon_bar, item_num, event);
+ return TRUE;
+ case GDK_BUTTON_RELEASE:
+ item_num = e_icon_bar_find_item_at_position (icon_bar,
+ event->button.x,
+ event->button.y,
+ NULL);
+ e_icon_bar_item_released (icon_bar, item_num, event);
+ return TRUE;
+ case GDK_MOTION_NOTIFY:
+ item_num = e_icon_bar_find_item_at_position (icon_bar,
+ event->motion.x,
+ event->motion.y,
+ NULL);
+ e_icon_bar_item_motion (icon_bar, item_num, event);
+ return TRUE;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+
+void
+e_icon_bar_item_pressed (EIconBar *icon_bar,
+ gint item_num,
+ GdkEvent *event)
+{
+ EIconBarItem *item;
+ gint button;
+
+ /* If we are editing an item, and a different item (or anywhere outside
+ an item) is clicked, stop the edit. If the item being edited is
+ clicked we just return, since the user may be selecting text. */
+ if (icon_bar->editing_item_num != -1) {
+ if (icon_bar->editing_item_num == item_num) {
+ item = &g_array_index (icon_bar->items, EIconBarItem,
+ icon_bar->editing_item_num);
+ if (!GTK_WIDGET_HAS_FOCUS (item->text->canvas)
+ || item->text->canvas->focused_item != item->text)
+ gnome_canvas_item_grab_focus (item->text);
+ } else {
+ e_icon_bar_stop_editing_item (icon_bar, TRUE);
+ }
+
+ return;
+ }
+
+ button = event->button.button;
+
+ if (button == 1 && item_num != -1) {
+ icon_bar->pressed_item_num = item_num;
+ icon_bar->pressed_x = event->button.x;
+ icon_bar->pressed_y = event->button.y;
+ gtk_widget_queue_draw (GTK_WIDGET (icon_bar));
+ } else if (button == 3) {
+ gtk_signal_emit (GTK_OBJECT (icon_bar),
+ e_icon_bar_signals[ITEM_SELECTED],
+ event, item_num);
+ }
+}
+
+
+void
+e_icon_bar_item_released (EIconBar *icon_bar,
+ gint item_num,
+ GdkEvent *event)
+{
+ gint button;
+
+ /* If we are editing an item, just return. */
+ if (icon_bar->editing_item_num != -1)
+ return;
+
+ button = event->button.button;
+
+ if (button == 1) {
+ if (icon_bar->pressed_item_num == icon_bar->mouse_over_item_num) {
+ gtk_signal_emit (GTK_OBJECT (icon_bar),
+ e_icon_bar_signals[ITEM_SELECTED],
+ event, item_num);
+ }
+
+ icon_bar->pressed_item_num = -1;
+ gtk_widget_queue_draw (GTK_WIDGET (icon_bar));
+ }
+}
+
+
+void
+e_icon_bar_item_motion (EIconBar *icon_bar,
+ gint item_num,
+ GdkEvent *event)
+{
+ gboolean need_redraw = TRUE;
+
+ if (event && event->motion.state & GDK_BUTTON1_MASK
+ && icon_bar->pressed_item_num != -1) {
+ if (abs (event->motion.x - icon_bar->pressed_x) > E_ICON_BAR_DRAG_START_OFFSET
+ || abs (event->motion.y - icon_bar->pressed_y) > E_ICON_BAR_DRAG_START_OFFSET) {
+ icon_bar->dragged_item_num = icon_bar->pressed_item_num;
+ gtk_signal_emit (GTK_OBJECT (icon_bar),
+ e_icon_bar_signals[ITEM_DRAGGED],
+ event, icon_bar->dragged_item_num);
+
+ /* Don't show the button as pressed while dragging. */
+ icon_bar->pressed_item_num = -1;
+ gtk_widget_queue_draw (GTK_WIDGET (icon_bar));
+ }
+
+ return;
+ }
+
+ if (icon_bar->mouse_over_item_num == item_num)
+ return;
+
+ /* If we are editing an item, items aren't highlighted so we don't
+ need a redraw. Also if an item is pressed, we only need a redraw if
+ item_num or the old mouse_over_item_num is the pressed item. */
+ if (icon_bar->editing_item_num != -1) {
+ need_redraw = FALSE;
+ } else if (icon_bar->pressed_item_num != -1) {
+ if (icon_bar->pressed_item_num != item_num
+ && icon_bar->pressed_item_num != icon_bar->mouse_over_item_num)
+ need_redraw = FALSE;
+ }
+
+ icon_bar->mouse_over_item_num = item_num;
+
+ if (need_redraw)
+ gtk_widget_queue_draw (GTK_WIDGET (icon_bar));
+}
+
+
+/* This returns the index of the item at the given position on the EIconBar,
+ or -1 if no item is found. If before_item is not NULL, it returns the
+ item which the mouse is before, or -1 (for dragging). */
+gint
+e_icon_bar_find_item_at_position (EIconBar *icon_bar,
+ gint x,
+ gint y,
+ gint *before_item)
+{
+ EIconBarItem *item;
+ gint item_num;
+
+ if (before_item)
+ *before_item = -1;
+
+ for (item_num = 0; item_num < icon_bar->items->len; item_num++) {
+ item = &g_array_index (icon_bar->items,
+ EIconBarItem, item_num);
+
+ if (icon_bar->view_type == E_ICON_BAR_LARGE_ICONS) {
+ if (e_icon_bar_large_icons_intersects (icon_bar, item,
+ x, y))
+ return item_num;
+
+ if (before_item
+ && e_icon_bar_large_icons_is_before (icon_bar,
+ item, x, y)) {
+ *before_item = item_num;
+ return -1;
+ }
+ } else {
+ if (e_icon_bar_small_icons_intersects (icon_bar, item,
+ x, y))
+ return item_num;
+
+ if (before_item
+ && e_icon_bar_small_icons_is_before (icon_bar,
+ item, x, y)) {
+ *before_item = item_num;
+ return -1;
+ }
+
+ }
+
+ }
+
+ /* If the mouse is below all the items, but inside the items' width,
+ and before_item is not NULL, we set it to the number of items, so
+ the dropped item would be added at the end. Note that this assumes
+ that the item variable points to the last item in the EIconBar. */
+ if (before_item) {
+ if (icon_bar->view_type == E_ICON_BAR_LARGE_ICONS) {
+ if (x < icon_bar->text_x
+ || x >= icon_bar->text_x + icon_bar->text_w)
+ return -1;
+
+ if (item == NULL
+ || y > item->icon_y + item->item_height)
+ *before_item = icon_bar->items->len;
+ } else {
+ if (x < icon_bar->icon_x
+ || x >= icon_bar->text_x + icon_bar->text_w)
+ return -1;
+
+ if (item == NULL) {
+ *before_item = icon_bar->items->len;
+ } else {
+ gint max_y;
+ max_y = MAX (item->icon_y + icon_bar->icon_h,
+ item->text_y + item->text_height);
+ if (y > max_y)
+ *before_item = icon_bar->items->len;
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+static gboolean
+e_icon_bar_large_icons_intersects (EIconBar *icon_bar,
+ EIconBarItem *item,
+ gint x,
+ gint y)
+{
+ if (y < item->icon_y || y >= item->text_y + item->text_height)
+ return FALSE;
+
+ if (y < item->icon_y + icon_bar->icon_h) {
+ if (x < icon_bar->icon_x
+ || x >= icon_bar->icon_x + icon_bar->icon_w)
+ return FALSE;
+ } else {
+ if (x < item->text_x
+ || x >= item->text_x + item->text_width)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+e_icon_bar_large_icons_is_before (EIconBar *icon_bar,
+ EIconBarItem *item,
+ gint x,
+ gint y)
+{
+ if (y < item->icon_y - icon_bar->spacing
+ || y >= item->icon_y)
+ return FALSE;
+
+ if (x < icon_bar->text_x || x >= icon_bar->text_x + icon_bar->text_w)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static gboolean
+e_icon_bar_small_icons_intersects (EIconBar *icon_bar,
+ EIconBarItem *item,
+ gint x,
+ gint y)
+{
+ gint min_y, max_y;
+
+ min_y = MIN (item->icon_y, item->text_y);
+ max_y = MAX (item->icon_y + icon_bar->icon_h,
+ item->text_y + item->text_height);
+
+ if (y < min_y || y >= max_y)
+ return FALSE;
+
+ if (x < icon_bar->icon_x || x >= item->text_x + item->text_width)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static gboolean
+e_icon_bar_small_icons_is_before (EIconBar *icon_bar,
+ EIconBarItem *item,
+ gint x,
+ gint y)
+{
+ gint min_y, max_y;
+
+ max_y = MIN (item->icon_y, item->text_y);
+ min_y = max_y - icon_bar->spacing;
+
+ if (y < min_y || y >= max_y)
+ return FALSE;
+
+ if (x < icon_bar->icon_x || x >= icon_bar->text_x + icon_bar->text_w)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/**
+ * e_icon_bar_start_editing_item:
+ * @icon_bar: An #EIconBar.
+ * @item_num: The index of the item.
+ *
+ * Turns the item into an editable text field so the user can rename it.
+ * Editing is stopped automatically when the user hits 'Return' or clicks
+ * outside the item. It can also be stopped explicitly by calling
+ * e_icon_bar_stop_editing_item().
+ **/
+void
+e_icon_bar_start_editing_item (EIconBar *icon_bar,
+ gint item_num)
+{
+ EIconBarItem *item;
+
+ g_return_if_fail (E_IS_ICON_BAR (icon_bar));
+ g_return_if_fail (item_num >= 0);
+ g_return_if_fail (item_num < icon_bar->items->len);
+
+ item = &g_array_index (icon_bar->items,
+ EIconBarItem, item_num);
+
+ e_icon_bar_text_item_start_editing (E_ICON_BAR_TEXT_ITEM (item->text));
+}
+
+
+/**
+ * e_icon_bar_stop_editing_item:
+ * @icon_bar: An #EIconBar.
+ * @accept: TRUE if the changes should be accepted, FALSE if the text should be
+ * changed back to its state before the editing started.
+ *
+ * Stops the editing of the items, if any were being edited.
+ **/
+void
+e_icon_bar_stop_editing_item (EIconBar *icon_bar,
+ gboolean accept)
+{
+ EIconBarItem *item;
+
+ g_return_if_fail (E_IS_ICON_BAR (icon_bar));
+
+ if (icon_bar->editing_item_num != -1) {
+ item = &g_array_index (icon_bar->items, EIconBarItem,
+ icon_bar->editing_item_num);
+ e_icon_bar_text_item_stop_editing (E_ICON_BAR_TEXT_ITEM (item->text), accept);
+ }
+}
+
+
+static void
+e_icon_bar_on_text_item_editing_started (EIconBarTextItem *text_item,
+ EIconBar *icon_bar)
+{
+ gint item_num;
+
+ item_num = e_icon_bar_find_item (icon_bar,
+ GNOME_CANVAS_ITEM (text_item));
+ g_return_if_fail (item_num != -1);
+
+ /* Turn off any highlighted item. */
+ e_icon_bar_item_motion (icon_bar, -1, NULL);
+
+ icon_bar->editing_item_num = item_num;
+
+ e_icon_bar_ensure_edited_item_visible (icon_bar);
+}
+
+
+static void
+e_icon_bar_on_text_item_editing_stopped (EIconBarTextItem *text_item,
+ EIconBar *icon_bar)
+{
+ gint item_num;
+
+ item_num = e_icon_bar_find_item (icon_bar,
+ GNOME_CANVAS_ITEM (text_item));
+ g_return_if_fail (item_num != -1);
+
+ e_icon_bar_text_item_select (text_item, FALSE);
+
+ icon_bar->editing_item_num = -1;
+
+ e_icon_bar_update_highlight (icon_bar);
+}
+
+
+static void
+e_icon_bar_ensure_edited_item_visible (EIconBar *icon_bar)
+{
+ EIconBarItem *item;
+ gint scroll_x, scroll_y, min_scroll_y, max_scroll_y, new_scroll_y;
+
+ if (icon_bar->editing_item_num == -1)
+ return;
+
+ item = &g_array_index (icon_bar->items,
+ EIconBarItem, icon_bar->editing_item_num);
+ gnome_canvas_get_scroll_offsets (GNOME_CANVAS (icon_bar),
+ &scroll_x, &scroll_y);
+
+ /* The minimum scroll y position is with the text right on the bottom
+ of the display. */
+ min_scroll_y = item->text_y + item->text_height + E_ICON_BAR_V_SPACE
+ - GTK_WIDGET (icon_bar)->allocation.height;
+ /* The maximum scroll y position is with the text at the top. */
+ max_scroll_y = item->text_y - E_ICON_BAR_V_SPACE;
+
+ new_scroll_y = MAX (scroll_y, min_scroll_y);
+ new_scroll_y = MIN (new_scroll_y, max_scroll_y);
+
+ if (new_scroll_y != scroll_y)
+ gnome_canvas_scroll_to (GNOME_CANVAS (icon_bar),
+ scroll_x, new_scroll_y);
+}
+
+
+/* This gets the mouse position and updates the highlight if necessary.
+ It is called after items are added/deleted/scrolled/edited. */
+static void
+e_icon_bar_update_highlight (EIconBar *icon_bar)
+{
+ GtkWidget *widget;
+ gint x, y, item_num;
+
+ widget = GTK_WIDGET (icon_bar);
+
+ if (!widget->window)
+ return;
+
+ gdk_window_get_pointer (widget->window, &x, &y, NULL);
+
+ if (x < 0 || y < 0
+ || x > widget->allocation.width || y > widget->allocation.height)
+ return;
+
+ item_num = e_icon_bar_find_item_at_position (icon_bar, x, y, NULL);
+ e_icon_bar_item_motion (icon_bar, item_num, NULL);
+}
+
+
+static gint
+e_icon_bar_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ EIconBar *icon_bar;
+ gint item_num, before_item, scroll_x, scroll_y;
+
+ g_return_val_if_fail (E_IS_ICON_BAR (widget), FALSE);
+
+ icon_bar = E_ICON_BAR (widget);
+
+ icon_bar->in_drag = TRUE;
+
+ /* Check if the mouse is over or between items, and if so highlight. */
+ gnome_canvas_get_scroll_offsets (GNOME_CANVAS (icon_bar),
+ &scroll_x, &scroll_y);
+ item_num = e_icon_bar_find_item_at_position (icon_bar,
+ x + scroll_x,
+ y + scroll_y,
+ &before_item);
+ e_icon_bar_item_motion (icon_bar, item_num, NULL);
+ e_icon_bar_set_dragging_before_item (icon_bar, before_item);
+
+ /* Check if the mouse is at the top or bottom of the bar, and if it is
+ scroll up/down. */
+ if (y < E_ICON_BAR_DRAG_AUTO_SCROLL_OFFSET)
+ icon_bar->scrolling_up = TRUE;
+ else if (y >= widget->allocation.height - E_ICON_BAR_DRAG_AUTO_SCROLL_OFFSET)
+ icon_bar->scrolling_up = FALSE;
+ else {
+ if (icon_bar->auto_scroll_timeout_id != 0) {
+ gtk_timeout_remove (icon_bar->auto_scroll_timeout_id);
+ icon_bar->auto_scroll_timeout_id = 0;
+ }
+ return FALSE;
+ }
+
+ if (icon_bar->auto_scroll_timeout_id == 0) {
+ icon_bar->auto_scroll_timeout_id = g_timeout_add (E_ICON_BAR_SCROLL_TIMEOUT, e_icon_bar_timeout_handler, icon_bar);
+ icon_bar->auto_scroll_delay = E_ICON_BAR_SCROLL_DELAY;
+ }
+
+ return FALSE;
+}
+
+
+static void
+e_icon_bar_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time)
+{
+ EIconBar *icon_bar;
+
+ g_return_if_fail (E_IS_ICON_BAR (widget));
+
+ icon_bar = E_ICON_BAR (widget);
+
+ icon_bar->in_drag = FALSE;
+
+ if (icon_bar->auto_scroll_timeout_id != 0) {
+ gtk_timeout_remove (icon_bar->auto_scroll_timeout_id);
+ icon_bar->auto_scroll_timeout_id = 0;
+ }
+
+ if (icon_bar->mouse_over_item_num != -1) {
+ icon_bar->mouse_over_item_num = -1;
+ gtk_widget_queue_draw (GTK_WIDGET (icon_bar));
+ }
+}
+
+
+static void
+e_icon_bar_set_dragging_before_item (EIconBar *icon_bar,
+ gint before_item)
+{
+ if (icon_bar->dragging_before_item_num == before_item)
+ return;
+
+ icon_bar->dragging_before_item_num = before_item;
+
+ gtk_widget_queue_draw (GTK_WIDGET (icon_bar));
+}
+
+
+static gboolean
+e_icon_bar_timeout_handler (gpointer data)
+{
+ EIconBar *icon_bar;
+ gint scroll_x, scroll_y, new_scroll_y;
+ GtkAdjustment *adj;
+
+ g_return_val_if_fail (E_IS_ICON_BAR (data), FALSE);
+
+ icon_bar = E_ICON_BAR (data);
+
+ GDK_THREADS_ENTER ();
+
+ if (icon_bar->auto_scroll_delay > 0) {
+ icon_bar->auto_scroll_delay--;
+ GDK_THREADS_LEAVE ();
+ return TRUE;
+ }
+
+ gnome_canvas_get_scroll_offsets (GNOME_CANVAS (icon_bar),
+ &scroll_x, &scroll_y);
+
+ adj = GTK_LAYOUT (icon_bar)->vadjustment;
+
+ if (icon_bar->scrolling_up)
+ new_scroll_y = MAX (scroll_y - adj->step_increment, 0);
+ else
+ new_scroll_y = MIN (scroll_y + adj->step_increment,
+ adj->upper - adj->page_size);
+
+ if (new_scroll_y != scroll_y)
+ gnome_canvas_scroll_to (GNOME_CANVAS (icon_bar),
+ scroll_x, new_scroll_y);
+
+ GDK_THREADS_LEAVE ();
+ return TRUE;
+}