[nautilus/wip/antoniof/gnome-42: 62/72] Revert "general: Remove canvas view"




commit f0ed057f7a27cacdf9cfa80432e64046685f113a
Author: Ondrej Holy <oholy redhat com>
Date:   Fri Feb 11 14:31:50 2022 +0100

    Revert "general: Remove canvas view"
    
    This reverts commit 2d1deaac2dd12b0ba16446bfbf3498b266e60338.

 eel/eel-canvas.c                     | 4153 ++++++++++++++++++++++
 eel/eel-canvas.h                     |  497 +++
 eel/meson.build                      |    2 +
 po/POTFILES.in                       |    6 +
 src/meson.build                      |   13 +
 src/nautilus-canvas-container.c      | 6360 ++++++++++++++++++++++++++++++++++
 src/nautilus-canvas-container.h      |  295 ++
 src/nautilus-canvas-dnd.c            | 1833 ++++++++++
 src/nautilus-canvas-dnd.h            |   56 +
 src/nautilus-canvas-item.c           | 2760 +++++++++++++++
 src/nautilus-canvas-item.h           |   90 +
 src/nautilus-canvas-private.h        |  223 ++
 src/nautilus-canvas-view-container.c |  377 ++
 src/nautilus-canvas-view-container.h |   35 +
 src/nautilus-canvas-view.c           | 1646 +++++++++
 src/nautilus-canvas-view.h           |   45 +
 src/nautilus-debug.c                 |    1 +
 src/nautilus-debug.h                 |   25 +-
 src/nautilus-dnd.c                   |    1 +
 src/nautilus-files-view.c            |    1 +
 src/nautilus-selection-canvas-item.c |  554 +++
 src/nautilus-selection-canvas-item.h |   62 +
 src/resources/css/Adwaita.css        |   12 +
 23 files changed, 19035 insertions(+), 12 deletions(-)
---
diff --git a/eel/eel-canvas.c b/eel/eel-canvas.c
new file mode 100644
index 000000000..559114f8f
--- /dev/null
+++ b/eel/eel-canvas.c
@@ -0,0 +1,4153 @@
+/*
+ * Copyright (C) 1997, 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,
+ * see <http://www.gnu.org/licenses/>.
+ */
+/*
+ *  @NOTATION@
+ */
+/*
+ * EelCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * EelCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Raph Levien <raph gimp org>
+ */
+
+/*
+ * TO-DO list for the canvas:
+ *
+ * - Allow to specify whether EelCanvasImage sizes are in units or pixels (scale or don't scale).
+ *
+ * - GC put functions for items.
+ *
+ * - Widget item (finish it).
+ *
+ * - GList *eel_canvas_gimme_all_items_contained_in_this_area (EelCanvas *canvas, Rectangle area);
+ *
+ * - Retrofit all the primitive items with microtile support.
+ *
+ * - Curve support for line item.
+ *
+ * - Arc item (Havoc has it; to be integrated in EelCanvasEllipse).
+ *
+ * - Sane font handling API.
+ *
+ * - Get_arg methods for items:
+ *   - How to fetch the outline width and know whether it is in pixels or units?
+ */
+
+#include <config.h>
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+#include <cairo-gobject.h>
+#include "eel-canvas.h"
+
+static void eel_canvas_request_update (EelCanvas *canvas);
+static void group_add (EelCanvasGroup *group,
+                       EelCanvasItem  *item);
+static void group_remove (EelCanvasGroup *group,
+                          EelCanvasItem  *item);
+static void redraw_and_repick_if_mapped (EelCanvasItem *item);
+
+/*** EelCanvasItem ***/
+
+/* Some convenience stuff */
+#define GCI_UPDATE_MASK (EEL_CANVAS_UPDATE_REQUESTED | EEL_CANVAS_UPDATE_DEEP)
+
+enum
+{
+    ITEM_PROP_0,
+    ITEM_PROP_VISIBLE
+};
+
+enum
+{
+    ITEM_DESTROY,
+    ITEM_EVENT,
+    ITEM_LAST_SIGNAL
+};
+
+static void eel_canvas_item_class_init (EelCanvasItemClass *klass);
+static void eel_canvas_item_init (EelCanvasItem *item);
+static int  emit_event (EelCanvas *canvas,
+                        GdkEvent  *event);
+
+static guint item_signals[ITEM_LAST_SIGNAL];
+
+static GObjectClass *item_parent_class;
+
+static gpointer accessible_item_parent_class;
+static gpointer accessible_parent_class;
+
+
+/**
+ * eel_canvas_item_get_type:
+ *
+ * Registers the &EelCanvasItem class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &EelCanvasItem class.
+ **/
+GType
+eel_canvas_item_get_type (void)
+{
+    static GType canvas_item_type = 0;
+
+    if (!canvas_item_type)
+    {
+        static const GTypeInfo canvas_item_info =
+        {
+            sizeof (EelCanvasItemClass),
+            (GBaseInitFunc) NULL,
+            (GBaseFinalizeFunc) NULL,
+            (GClassInitFunc) eel_canvas_item_class_init,
+            NULL,                       /* class_finalize */
+            NULL,                       /* class_data */
+            sizeof (EelCanvasItem),
+            0,                          /* n_preallocs */
+            (GInstanceInitFunc) eel_canvas_item_init
+        };
+
+        canvas_item_type = g_type_register_static (G_TYPE_INITIALLY_UNOWNED,
+                                                   "EelCanvasItem",
+                                                   &canvas_item_info,
+                                                   0);
+    }
+
+    return canvas_item_type;
+}
+
+/* Object initialization function for EelCanvasItem */
+static void
+eel_canvas_item_init (EelCanvasItem *item)
+{
+    item->flags |= EEL_CANVAS_ITEM_VISIBLE;
+}
+
+/* Performs post-creation operations on a canvas item (adding it to its parent
+ * group, etc.)
+ */
+static void
+item_post_create_setup (EelCanvasItem *item)
+{
+    group_add (EEL_CANVAS_GROUP (item->parent), item);
+
+    redraw_and_repick_if_mapped (item);
+}
+
+/**
+ * eel_canvas_item_new:
+ * @parent: The parent group for the new item.
+ * @type: The object type of the item.
+ * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
+ * used to configure the item.  For example, "fill_color", "black",
+ * "width_units", 5.0, NULL.
+ * @Varargs:
+ *
+ * Creates a new canvas item with @parent as its parent group.  The item is
+ * created at the top of its parent's stack, and starts up as visible.  The item
+ * is of the specified @type, for example, it can be
+ * eel_canvas_rect_get_type().  The list of object arguments/value pairs is
+ * used to configure the item.
+ *
+ * Return value: The newly-created item.
+ **/
+EelCanvasItem *
+eel_canvas_item_new (EelCanvasGroup *parent,
+                     GType           type,
+                     const gchar    *first_arg_name,
+                     ...)
+{
+    EelCanvasItem *item;
+    va_list args;
+
+    g_return_val_if_fail (EEL_IS_CANVAS_GROUP (parent), NULL);
+    g_return_val_if_fail (g_type_is_a (type, eel_canvas_item_get_type ()), NULL);
+
+    item = EEL_CANVAS_ITEM (g_object_new (type, NULL));
+
+    item->parent = EEL_CANVAS_ITEM (parent);
+    item->canvas = item->parent->canvas;
+
+    va_start (args, first_arg_name);
+    g_object_set_valist (G_OBJECT (item), first_arg_name, args);
+    va_end (args);
+
+    item_post_create_setup (item);
+
+    return item;
+}
+
+/* Set_property handler for canvas items */
+static void
+eel_canvas_item_set_property (GObject      *gobject,
+                              guint         param_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+    EelCanvasItem *item;
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (gobject));
+
+    item = EEL_CANVAS_ITEM (gobject);
+
+    switch (param_id)
+    {
+        case ITEM_PROP_VISIBLE:
+        {
+            if (g_value_get_boolean (value))
+            {
+                eel_canvas_item_show (item);
+            }
+            else
+            {
+                eel_canvas_item_hide (item);
+            }
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+        }
+        break;
+    }
+}
+
+/* Get_property handler for canvas items */
+static void
+eel_canvas_item_get_property (GObject    *gobject,
+                              guint       param_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+    EelCanvasItem *item;
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (gobject));
+
+    item = EEL_CANVAS_ITEM (gobject);
+
+    switch (param_id)
+    {
+        case ITEM_PROP_VISIBLE:
+        {
+            g_value_set_boolean (value, item->flags & EEL_CANVAS_ITEM_VISIBLE);
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+        }
+        break;
+    }
+}
+
+static void
+redraw_and_repick_if_mapped (EelCanvasItem *item)
+{
+    if (item->flags & EEL_CANVAS_ITEM_MAPPED)
+    {
+        eel_canvas_item_request_redraw (item);
+        item->canvas->need_repick = TRUE;
+    }
+}
+
+/* Dispose handler for canvas items */
+static void
+eel_canvas_item_dispose (GObject *object)
+{
+    EelCanvasItem *item;
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (object));
+
+    item = EEL_CANVAS_ITEM (object);
+
+    if (item->canvas)
+    {
+        eel_canvas_item_request_redraw (item);
+
+        /* Make the canvas forget about us */
+
+        if (item == item->canvas->current_item)
+        {
+            item->canvas->current_item = NULL;
+            item->canvas->need_repick = TRUE;
+        }
+
+        if (item == item->canvas->new_current_item)
+        {
+            item->canvas->new_current_item = NULL;
+            item->canvas->need_repick = TRUE;
+        }
+
+        eel_canvas_item_ungrab (item);
+
+        if (item == item->canvas->focused_item)
+        {
+            item->canvas->focused_item = NULL;
+        }
+
+        /* Normal destroy stuff */
+
+        if (item->flags & EEL_CANVAS_ITEM_MAPPED)
+        {
+            (*EEL_CANVAS_ITEM_GET_CLASS (item)->unmap)(item);
+        }
+
+        if (item->flags & EEL_CANVAS_ITEM_REALIZED)
+        {
+            (*EEL_CANVAS_ITEM_GET_CLASS (item)->unrealize)(item);
+        }
+
+        if (item->parent)
+        {
+            group_remove (EEL_CANVAS_GROUP (item->parent), item);
+        }
+
+        item->canvas = NULL;
+    }
+
+    g_object_set_data (object, "in-destruction", GINT_TO_POINTER (1));
+    g_signal_emit (object, item_signals[ITEM_DESTROY], 0);
+
+    g_object_set_data (object, "in-destruction", NULL);
+
+    G_OBJECT_CLASS (item_parent_class)->dispose (object);
+}
+
+void
+eel_canvas_item_destroy (EelCanvasItem *item)
+{
+    if (g_object_get_data (G_OBJECT (item), "in-destruction") == NULL)
+    {
+        g_object_run_dispose (G_OBJECT (item));
+    }
+}
+
+/* Realize handler for canvas items */
+static void
+eel_canvas_item_realize (EelCanvasItem *item)
+{
+    if (item->parent && !(item->parent->flags & EEL_CANVAS_ITEM_REALIZED))
+    {
+        (*EEL_CANVAS_ITEM_GET_CLASS (item->parent)->realize)(item->parent);
+    }
+
+    if (item->parent == NULL && !gtk_widget_get_realized (GTK_WIDGET (item->canvas)))
+    {
+        gtk_widget_realize (GTK_WIDGET (item->canvas));
+    }
+
+    item->flags |= EEL_CANVAS_ITEM_REALIZED;
+
+    eel_canvas_item_request_update (item);
+}
+
+/* Unrealize handler for canvas items */
+static void
+eel_canvas_item_unrealize (EelCanvasItem *item)
+{
+    if (item->flags & EEL_CANVAS_ITEM_MAPPED)
+    {
+        (*EEL_CANVAS_ITEM_GET_CLASS (item)->unmap)(item);
+    }
+
+    item->flags &= ~(EEL_CANVAS_ITEM_REALIZED);
+}
+
+/* Map handler for canvas items */
+static void
+eel_canvas_item_map (EelCanvasItem *item)
+{
+    item->flags |= EEL_CANVAS_ITEM_MAPPED;
+}
+
+/* Unmap handler for canvas items */
+static void
+eel_canvas_item_unmap (EelCanvasItem *item)
+{
+    item->flags &= ~(EEL_CANVAS_ITEM_MAPPED);
+}
+
+/* Update handler for canvas items */
+static void
+eel_canvas_item_update (EelCanvasItem *item,
+                        double         i2w_dx,
+                        double         i2w_dy,
+                        int            flags)
+{
+    item->flags &= ~(EEL_CANVAS_ITEM_NEED_UPDATE);
+    item->flags &= ~(EEL_CANVAS_ITEM_NEED_DEEP_UPDATE);
+}
+
+/*
+ * This routine invokes the update method of the item
+ * Please notice, that we take parent to canvas pixel matrix as argument
+ * unlike virtual method ::update, whose argument is item 2 canvas pixel
+ * matrix
+ *
+ * I will try to force somewhat meaningful naming for affines (Lauris)
+ * General naming rule is FROM2TO, where FROM and TO are abbreviations
+ * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
+ * I hope that this helps to keep track of what really happens
+ *
+ */
+
+static void
+eel_canvas_item_invoke_update (EelCanvasItem *item,
+                               double         i2w_dx,
+                               double         i2w_dy,
+                               int            flags)
+{
+    int child_flags;
+
+    child_flags = flags;
+
+    /* apply object flags to child flags */
+    child_flags &= ~EEL_CANVAS_UPDATE_REQUESTED;
+
+    if (item->flags & EEL_CANVAS_ITEM_NEED_UPDATE)
+    {
+        child_flags |= EEL_CANVAS_UPDATE_REQUESTED;
+    }
+
+    if (item->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)
+    {
+        child_flags |= EEL_CANVAS_UPDATE_DEEP;
+    }
+
+    if (child_flags & GCI_UPDATE_MASK)
+    {
+        if (EEL_CANVAS_ITEM_GET_CLASS (item)->update)
+        {
+            EEL_CANVAS_ITEM_GET_CLASS (item)->update (item, i2w_dx, i2w_dy, child_flags);
+        }
+    }
+
+    /* If this fail you probably forgot to chain up to
+     * EelCanvasItem::update from a derived class */
+    g_return_if_fail (!(item->flags & EEL_CANVAS_ITEM_NEED_UPDATE));
+}
+
+/*
+ * This routine invokes the point method of the item.
+ * The arguments x, y should be in the parent item local coordinates.
+ */
+
+static double
+eel_canvas_item_invoke_point (EelCanvasItem  *item,
+                              double          x,
+                              double          y,
+                              int             cx,
+                              int             cy,
+                              EelCanvasItem **actual_item)
+{
+    /* Calculate x & y in item local coordinates */
+
+    if (EEL_CANVAS_ITEM_GET_CLASS (item)->point)
+    {
+        return EEL_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
+    }
+
+    return 1e18;
+}
+
+/**
+ * eel_canvas_item_set:
+ * @item: A canvas item.
+ * @first_arg_name: The list of object argument name/value pairs used to configure the item.
+ * @Varargs:
+ *
+ * Configures a canvas item.  The arguments in the item are set to the specified
+ * values, and the item is repainted as appropriate.
+ **/
+void
+eel_canvas_item_set (EelCanvasItem *item,
+                     const gchar   *first_arg_name,
+                     ...)
+{
+    va_list args;
+
+    va_start (args, first_arg_name);
+    g_object_set_valist (G_OBJECT (item), first_arg_name, args);
+    va_end (args);
+
+    item->canvas->need_repick = TRUE;
+}
+
+/**
+ * eel_canvas_item_move:
+ * @item: A canvas item.
+ * @dx: Horizontal offset.
+ * @dy: Vertical offset.
+ *
+ * Moves a canvas item by creating an affine transformation matrix for
+ * translation by using the specified values. This happens in item
+ * local coordinate system, so if you have nontrivial transform, it
+ * most probably does not do, what you want.
+ **/
+void
+eel_canvas_item_move (EelCanvasItem *item,
+                      double         dx,
+                      double         dy)
+{
+    g_return_if_fail (item != NULL);
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+    if (!EEL_CANVAS_ITEM_GET_CLASS (item)->translate)
+    {
+        g_warning ("Item type %s does not implement translate method.\n",
+                   g_type_name (G_OBJECT_TYPE (item)));
+        return;
+    }
+
+    (*EEL_CANVAS_ITEM_GET_CLASS (item)->translate)(item, dx, dy);
+
+    if (item->flags & EEL_CANVAS_ITEM_MAPPED)
+    {
+        item->canvas->need_repick = TRUE;
+    }
+
+    if (!(item->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE))
+    {
+        item->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
+        if (item->parent != NULL)
+        {
+            eel_canvas_item_request_update (item->parent);
+        }
+        else
+        {
+            eel_canvas_request_update (item->canvas);
+        }
+    }
+}
+
+static void
+eel_canvas_queue_resize (EelCanvas *canvas)
+{
+    if (gtk_widget_is_drawable (GTK_WIDGET (canvas)))
+    {
+        gtk_widget_queue_resize (GTK_WIDGET (canvas));
+    }
+}
+
+/* Convenience function to reorder items in a group's child list.  This puts the
+ * specified link after the "before" link. Returns TRUE if the list was changed.
+ */
+static gboolean
+put_item_after (GList *link,
+                GList *before)
+{
+    EelCanvasGroup *parent;
+
+    if (link == before)
+    {
+        return FALSE;
+    }
+
+    parent = EEL_CANVAS_GROUP (EEL_CANVAS_ITEM (link->data)->parent);
+
+    if (before == NULL)
+    {
+        if (link == parent->item_list)
+        {
+            return FALSE;
+        }
+
+        link->prev->next = link->next;
+
+        if (link->next)
+        {
+            link->next->prev = link->prev;
+        }
+        else
+        {
+            parent->item_list_end = link->prev;
+        }
+
+        link->prev = before;
+        link->next = parent->item_list;
+        link->next->prev = link;
+        parent->item_list = link;
+    }
+    else
+    {
+        if ((link == parent->item_list_end) && (before == parent->item_list_end->prev))
+        {
+            return FALSE;
+        }
+
+        if (link->next)
+        {
+            link->next->prev = link->prev;
+        }
+
+        if (link->prev)
+        {
+            link->prev->next = link->next;
+        }
+        else
+        {
+            parent->item_list = link->next;
+            parent->item_list->prev = NULL;
+        }
+
+        link->prev = before;
+        link->next = before->next;
+
+        link->prev->next = link;
+
+        if (link->next)
+        {
+            link->next->prev = link;
+        }
+        else
+        {
+            parent->item_list_end = link;
+        }
+    }
+    return TRUE;
+}
+
+
+/**
+ * eel_canvas_item_raise:
+ * @item: A canvas item.
+ * @positions: Number of steps to raise the item.
+ *
+ * Raises the item in its parent's stack by the specified number of positions.
+ * If the number of positions is greater than the distance to the top of the
+ * stack, then the item is put at the top.
+ **/
+void
+eel_canvas_item_raise (EelCanvasItem *item,
+                       int            positions)
+{
+    GList *link, *before;
+    EelCanvasGroup *parent;
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+    g_return_if_fail (positions >= 0);
+
+    if (!item->parent || positions == 0)
+    {
+        return;
+    }
+
+    parent = EEL_CANVAS_GROUP (item->parent);
+    link = g_list_find (parent->item_list, item);
+    g_assert (link != NULL);
+
+    for (before = link; positions && before; positions--)
+    {
+        before = before->next;
+    }
+
+    if (!before)
+    {
+        before = parent->item_list_end;
+    }
+
+    if (put_item_after (link, before))
+    {
+        redraw_and_repick_if_mapped (item);
+    }
+}
+
+
+/**
+ * eel_canvas_item_lower:
+ * @item: A canvas item.
+ * @positions: Number of steps to lower the item.
+ *
+ * Lowers the item in its parent's stack by the specified number of positions.
+ * If the number of positions is greater than the distance to the bottom of the
+ * stack, then the item is put at the bottom.
+ **/
+void
+eel_canvas_item_lower (EelCanvasItem *item,
+                       int            positions)
+{
+    GList *link, *before;
+    EelCanvasGroup *parent;
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+    g_return_if_fail (positions >= 1);
+
+    if (!item->parent)
+    {
+        return;
+    }
+
+    parent = EEL_CANVAS_GROUP (item->parent);
+    link = g_list_find (parent->item_list, item);
+    g_assert (link != NULL);
+
+    if (link->prev)
+    {
+        for (before = link->prev; positions && before; positions--)
+        {
+            before = before->prev;
+        }
+    }
+    else
+    {
+        before = NULL;
+    }
+
+    if (put_item_after (link, before))
+    {
+        redraw_and_repick_if_mapped (item);
+    }
+}
+
+
+/**
+ * eel_canvas_item_raise_to_top:
+ * @item: A canvas item.
+ *
+ * Raises an item to the top of its parent's stack.
+ **/
+void
+eel_canvas_item_raise_to_top (EelCanvasItem *item)
+{
+    GList *link;
+    EelCanvasGroup *parent;
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+    if (!item->parent)
+    {
+        return;
+    }
+
+    parent = EEL_CANVAS_GROUP (item->parent);
+    link = g_list_find (parent->item_list, item);
+    g_assert (link != NULL);
+
+    if (put_item_after (link, parent->item_list_end))
+    {
+        redraw_and_repick_if_mapped (item);
+    }
+}
+
+
+/**
+ * eel_canvas_item_lower_to_bottom:
+ * @item: A canvas item.
+ *
+ * Lowers an item to the bottom of its parent's stack.
+ **/
+void
+eel_canvas_item_lower_to_bottom (EelCanvasItem *item)
+{
+    GList *link;
+    EelCanvasGroup *parent;
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+    if (!item->parent)
+    {
+        return;
+    }
+
+    parent = EEL_CANVAS_GROUP (item->parent);
+    link = g_list_find (parent->item_list, item);
+    g_assert (link != NULL);
+
+    if (put_item_after (link, NULL))
+    {
+        redraw_and_repick_if_mapped (item);
+    }
+}
+
+/**
+ * eel_canvas_item_send_behind:
+ * @item: A canvas item.
+ * @behind_item: The canvas item to put item behind, or NULL
+ *
+ * Moves item to a in the position in the stacking order so that
+ * it is placed immediately below behind_item, or at the top if
+ * behind_item is NULL.
+ **/
+void
+eel_canvas_item_send_behind (EelCanvasItem *item,
+                             EelCanvasItem *behind_item)
+{
+    GList *item_list;
+    int item_position, behind_position;
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+    if (behind_item == NULL)
+    {
+        eel_canvas_item_raise_to_top (item);
+        return;
+    }
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (behind_item));
+    g_return_if_fail (item->parent == behind_item->parent);
+
+    item_list = EEL_CANVAS_GROUP (item->parent)->item_list;
+
+    item_position = g_list_index (item_list, item);
+    g_assert (item_position != -1);
+    behind_position = g_list_index (item_list, behind_item);
+    g_assert (behind_position != -1);
+    g_assert (item_position != behind_position);
+
+    if (item_position == behind_position - 1)
+    {
+        return;
+    }
+
+    if (item_position < behind_position)
+    {
+        eel_canvas_item_raise (item, (behind_position - 1) - item_position);
+    }
+    else
+    {
+        eel_canvas_item_lower (item, item_position - behind_position);
+    }
+}
+
+/**
+ * eel_canvas_item_show:
+ * @item: A canvas item.
+ *
+ * Shows a canvas item.  If the item was already shown, then no action is taken.
+ **/
+void
+eel_canvas_item_show (EelCanvasItem *item)
+{
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+    if (!(item->flags & EEL_CANVAS_ITEM_VISIBLE))
+    {
+        item->flags |= EEL_CANVAS_ITEM_VISIBLE;
+
+        if (!(item->flags & EEL_CANVAS_ITEM_REALIZED))
+        {
+            (*EEL_CANVAS_ITEM_GET_CLASS (item)->realize)(item);
+        }
+
+        if (item->parent != NULL)
+        {
+            if (!(item->flags & EEL_CANVAS_ITEM_MAPPED) &&
+                item->parent->flags & EEL_CANVAS_ITEM_MAPPED)
+            {
+                (*EEL_CANVAS_ITEM_GET_CLASS (item)->map)(item);
+            }
+        }
+        else
+        {
+            if (!(item->flags & EEL_CANVAS_ITEM_MAPPED) &&
+                gtk_widget_get_mapped (GTK_WIDGET (item->canvas)))
+            {
+                (*EEL_CANVAS_ITEM_GET_CLASS (item)->map)(item);
+            }
+        }
+
+        redraw_and_repick_if_mapped (item);
+        eel_canvas_queue_resize (item->canvas);
+    }
+}
+
+
+/**
+ * eel_canvas_item_hide:
+ * @item: A canvas item.
+ *
+ * Hides a canvas item.  If the item was already hidden, then no action is
+ * taken.
+ **/
+void
+eel_canvas_item_hide (EelCanvasItem *item)
+{
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+    if (item->flags & EEL_CANVAS_ITEM_VISIBLE)
+    {
+        item->flags &= ~EEL_CANVAS_ITEM_VISIBLE;
+
+        redraw_and_repick_if_mapped (item);
+
+        if (item->flags & EEL_CANVAS_ITEM_MAPPED)
+        {
+            (*EEL_CANVAS_ITEM_GET_CLASS (item)->unmap)(item);
+        }
+
+        eel_canvas_queue_resize (item->canvas);
+
+        /* No need to unrealize when we just want to hide */
+    }
+}
+
+
+/*
+ * Prepare the window for grabbing, i.e. show it.
+ */
+static void
+seat_grab_prepare_window (GdkSeat   *seat,
+                          GdkWindow *window,
+                          gpointer   user_data)
+{
+    gdk_window_show (window);
+}
+
+/**
+ * eel_canvas_item_grab:
+ * @item: A canvas item.
+ * @event_mask: Mask of events that will be sent to this item.
+ * @cursor: If non-NULL, the cursor that will be used while the grab is active.
+ * @event: The event, triggering the grab, if any.
+ *
+ * Specifies that all events that match the specified event mask should be sent
+ * to the specified item, and also grabs the seat by calling gdk_seat_grab().
+ * If @cursor is not NULL, then that cursor is used while the grab is active.
+ *
+ * Return value: If an item was already grabbed, it returns %GDK_GRAB_ALREADY_GRABBED. If
+ * the specified item was hidden by calling eel_canvas_item_hide(), then it
+ * returns %GDK_GRAB_NOT_VIEWABLE. Else, it returns the result of calling
+ * gdk_seat_grab().
+ **/
+GdkGrabStatus
+eel_canvas_item_grab (EelCanvasItem  *item,
+                      GdkEventMask    event_mask,
+                      GdkCursor      *cursor,
+                      const GdkEvent *event)
+{
+    GdkGrabStatus retval;
+    GdkDisplay *display;
+    GdkSeat *seat;
+
+    g_return_val_if_fail (EEL_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
+    g_return_val_if_fail (gtk_widget_get_mapped (GTK_WIDGET (item->canvas)),
+                          GDK_GRAB_NOT_VIEWABLE);
+
+    if (item->canvas->grabbed_item)
+    {
+        return GDK_GRAB_ALREADY_GRABBED;
+    }
+
+    if (!(item->flags & EEL_CANVAS_ITEM_MAPPED))
+    {
+        return GDK_GRAB_NOT_VIEWABLE;
+    }
+
+    display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
+    seat = gdk_display_get_default_seat (display);
+
+    retval = gdk_seat_grab (seat,
+                            gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas)),
+                            GDK_SEAT_CAPABILITY_ALL_POINTING,
+                            FALSE,
+                            cursor,
+                            event,
+                            seat_grab_prepare_window,
+                            NULL);
+
+    if (retval != GDK_GRAB_SUCCESS)
+    {
+        return retval;
+    }
+
+    item->canvas->grabbed_item = item;
+    item->canvas->grabbed_event_mask = event_mask;
+    item->canvas->current_item = item;     /* So that events go to the grabbed item */
+
+    return retval;
+}
+
+
+/**
+ * eel_canvas_item_ungrab:
+ * @item: A canvas item that holds a grab.
+ *
+ * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
+ * seat.
+ **/
+void
+eel_canvas_item_ungrab (EelCanvasItem *item)
+{
+    GdkDisplay *display;
+    GdkSeat *seat;
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+    if (item->canvas->grabbed_item != item)
+    {
+        return;
+    }
+
+    display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
+    seat = gdk_display_get_default_seat (display);
+
+    item->canvas->grabbed_item = NULL;
+    gdk_seat_ungrab (seat);
+}
+
+/**
+ * eel_canvas_item_i2w:
+ * @item: A canvas item.
+ * @x: X coordinate to convert (input/output value).
+ * @y: Y coordinate to convert (input/output value).
+ *
+ * Converts a coordinate pair from item-relative coordinates to world
+ * coordinates.
+ **/
+void
+eel_canvas_item_i2w (EelCanvasItem *item,
+                     double        *x,
+                     double        *y)
+{
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+    g_return_if_fail (x != NULL);
+    g_return_if_fail (y != NULL);
+
+    item = item->parent;
+    while (item)
+    {
+        if (EEL_IS_CANVAS_GROUP (item))
+        {
+            *x += EEL_CANVAS_GROUP (item)->xpos;
+            *y += EEL_CANVAS_GROUP (item)->ypos;
+        }
+
+        item = item->parent;
+    }
+}
+
+/* Returns whether the item is an inferior of or is equal to the parent. */
+static int
+is_descendant (EelCanvasItem *item,
+               EelCanvasItem *parent)
+{
+    for (; item; item = item->parent)
+    {
+        if (item == parent)
+        {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+/**
+ * eel_canvas_item_grab_focus:
+ * @item: A canvas item.
+ *
+ * Makes the specified item take the keyboard focus, so all keyboard events will
+ * be sent to it.  If the canvas widget itself did not have the focus, it grabs
+ * it as well.
+ **/
+static void
+eel_canvas_item_grab_focus (EelCanvasItem *item)
+{
+    EelCanvasItem *focused_item;
+    GdkEvent ev;
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+    g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)));
+
+    focused_item = item->canvas->focused_item;
+
+    if (focused_item)
+    {
+        ev.focus_change.type = GDK_FOCUS_CHANGE;
+        ev.focus_change.window = gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas));
+        ev.focus_change.send_event = FALSE;
+        ev.focus_change.in = FALSE;
+
+        emit_event (item->canvas, &ev);
+    }
+
+    item->canvas->focused_item = item;
+    gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
+
+    if (focused_item)
+    {
+        ev.focus_change.type = GDK_FOCUS_CHANGE;
+        ev.focus_change.window = gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas));
+        ev.focus_change.send_event = FALSE;
+        ev.focus_change.in = TRUE;
+
+        emit_event (item->canvas, &ev);
+    }
+}
+
+
+/**
+ * eel_canvas_item_get_bounds:
+ * @item: A canvas item.
+ * @x1: Leftmost edge of the bounding box (return value).
+ * @y1: Upper edge of the bounding box (return value).
+ * @x2: Rightmost edge of the bounding box (return value).
+ * @y2: Lower edge of the bounding box (return value).
+ *
+ * Queries the bounding box of a canvas item.  The bounds are returned in the
+ * coordinate system of the item's parent.
+ **/
+void
+eel_canvas_item_get_bounds (EelCanvasItem *item,
+                            double        *x1,
+                            double        *y1,
+                            double        *x2,
+                            double        *y2)
+{
+    double tx1, ty1, tx2, ty2;
+
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+    tx1 = ty1 = tx2 = ty2 = 0.0;
+
+    /* Get the item's bounds in its coordinate system */
+
+    if (EEL_CANVAS_ITEM_GET_CLASS (item)->bounds)
+    {
+        (*EEL_CANVAS_ITEM_GET_CLASS (item)->bounds)(item, &tx1, &ty1, &tx2, &ty2);
+    }
+
+    /* Return the values */
+
+    if (x1)
+    {
+        *x1 = tx1;
+    }
+
+    if (y1)
+    {
+        *y1 = ty1;
+    }
+
+    if (x2)
+    {
+        *x2 = tx2;
+    }
+
+    if (y2)
+    {
+        *y2 = ty2;
+    }
+}
+
+
+/**
+ * eel_canvas_item_request_update
+ * @item: A canvas item.
+ *
+ * To be used only by item implementations.  Requests that the canvas queue an
+ * update for the specified item.
+ **/
+void
+eel_canvas_item_request_update (EelCanvasItem *item)
+{
+    if (NULL == item->canvas)
+    {
+        return;
+    }
+
+    g_return_if_fail (!item->canvas->doing_update);
+
+    if (item->flags & EEL_CANVAS_ITEM_NEED_UPDATE)
+    {
+        return;
+    }
+
+    item->flags |= EEL_CANVAS_ITEM_NEED_UPDATE;
+
+    if (item->parent != NULL)
+    {
+        /* Recurse up the tree */
+        eel_canvas_item_request_update (item->parent);
+    }
+    else
+    {
+        /* Have reached the top of the tree, make sure the update call gets scheduled. */
+        eel_canvas_request_update (item->canvas);
+    }
+}
+
+/**
+ * eel_canvas_item_request_update
+ * @item: A canvas item.
+ *
+ * Convenience function that informs a canvas that the specified item needs
+ * to be repainted. To be used by item implementations
+ **/
+void
+eel_canvas_item_request_redraw (EelCanvasItem *item)
+{
+    if (item->flags & EEL_CANVAS_ITEM_MAPPED)
+    {
+        eel_canvas_request_redraw (item->canvas,
+                                   item->x1, item->y1,
+                                   item->x2 + 1, item->y2 + 1);
+    }
+}
+
+
+
+/*** EelCanvasGroup ***/
+
+
+enum
+{
+    GROUP_PROP_0,
+    GROUP_PROP_X,
+    GROUP_PROP_Y
+};
+
+
+static void eel_canvas_group_class_init (EelCanvasGroupClass *klass);
+static void eel_canvas_group_init (EelCanvasGroup *group);
+static void eel_canvas_group_set_property (GObject      *object,
+                                           guint         param_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec);
+static void eel_canvas_group_get_property (GObject    *object,
+                                           guint       param_id,
+                                           GValue     *value,
+                                           GParamSpec *pspec);
+
+static void eel_canvas_group_destroy (EelCanvasItem *object);
+
+static void   eel_canvas_group_update (EelCanvasItem *item,
+                                       double         i2w_dx,
+                                       double         i2w_dy,
+                                       int            flags);
+static void   eel_canvas_group_unrealize (EelCanvasItem *item);
+static void   eel_canvas_group_map (EelCanvasItem *item);
+static void   eel_canvas_group_unmap (EelCanvasItem *item);
+static void   eel_canvas_group_draw (EelCanvasItem  *item,
+                                     cairo_t        *cr,
+                                     cairo_region_t *region);
+static double eel_canvas_group_point (EelCanvasItem  *item,
+                                      double          x,
+                                      double          y,
+                                      int             cx,
+                                      int             cy,
+                                      EelCanvasItem **actual_item);
+static void   eel_canvas_group_translate (EelCanvasItem *item,
+                                          double         dx,
+                                          double         dy);
+static void   eel_canvas_group_bounds (EelCanvasItem *item,
+                                       double        *x1,
+                                       double        *y1,
+                                       double        *x2,
+                                       double        *y2);
+
+
+static EelCanvasItemClass *group_parent_class;
+
+
+/**
+ * eel_canvas_group_get_type:
+ *
+ * Registers the &EelCanvasGroup class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &EelCanvasGroup class.
+ **/
+GType
+eel_canvas_group_get_type (void)
+{
+    static GType group_type = 0;
+
+    if (!group_type)
+    {
+        static const GTypeInfo group_info =
+        {
+            sizeof (EelCanvasGroupClass),
+            (GBaseInitFunc) NULL,
+            (GBaseFinalizeFunc) NULL,
+            (GClassInitFunc) eel_canvas_group_class_init,
+            NULL,                       /* class_finalize */
+            NULL,                       /* class_data */
+            sizeof (EelCanvasGroup),
+            0,                          /* n_preallocs */
+            (GInstanceInitFunc) eel_canvas_group_init
+        };
+
+        group_type = g_type_register_static (eel_canvas_item_get_type (),
+                                             "EelCanvasGroup",
+                                             &group_info,
+                                             0);
+    }
+
+    return group_type;
+}
+
+/* Class initialization function for EelCanvasGroupClass */
+static void
+eel_canvas_group_class_init (EelCanvasGroupClass *klass)
+{
+    GObjectClass *gobject_class;
+    EelCanvasItemClass *item_class;
+
+    gobject_class = (GObjectClass *) klass;
+    item_class = (EelCanvasItemClass *) klass;
+
+    group_parent_class = g_type_class_peek_parent (klass);
+
+    gobject_class->set_property = eel_canvas_group_set_property;
+    gobject_class->get_property = eel_canvas_group_get_property;
+
+    g_object_class_install_property
+        (gobject_class, GROUP_PROP_X,
+        g_param_spec_double ("x",
+                             _("X"),
+                             _("X"),
+                             -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+                             G_PARAM_READWRITE));
+    g_object_class_install_property
+        (gobject_class, GROUP_PROP_Y,
+        g_param_spec_double ("y",
+                             _("Y"),
+                             _("Y"),
+                             -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+                             G_PARAM_READWRITE));
+
+    item_class->destroy = eel_canvas_group_destroy;
+    item_class->update = eel_canvas_group_update;
+    item_class->unrealize = eel_canvas_group_unrealize;
+    item_class->map = eel_canvas_group_map;
+    item_class->unmap = eel_canvas_group_unmap;
+    item_class->draw = eel_canvas_group_draw;
+    item_class->point = eel_canvas_group_point;
+    item_class->translate = eel_canvas_group_translate;
+    item_class->bounds = eel_canvas_group_bounds;
+}
+
+/* Object initialization function for EelCanvasGroup */
+static void
+eel_canvas_group_init (EelCanvasGroup *group)
+{
+    group->xpos = 0.0;
+    group->ypos = 0.0;
+}
+
+/* Set_property handler for canvas groups */
+static void
+eel_canvas_group_set_property (GObject      *gobject,
+                               guint         param_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+    EelCanvasItem *item;
+    EelCanvasGroup *group;
+    double old;
+    gboolean moved;
+
+    g_return_if_fail (EEL_IS_CANVAS_GROUP (gobject));
+
+    item = EEL_CANVAS_ITEM (gobject);
+    group = EEL_CANVAS_GROUP (gobject);
+
+    moved = FALSE;
+    switch (param_id)
+    {
+        case GROUP_PROP_X:
+        {
+            old = group->xpos;
+            group->xpos = g_value_get_double (value);
+            if (old != group->xpos)
+            {
+                moved = TRUE;
+            }
+        }
+        break;
+
+        case GROUP_PROP_Y:
+        {
+            old = group->ypos;
+            group->ypos = g_value_get_double (value);
+            if (old != group->ypos)
+            {
+                moved = TRUE;
+            }
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+        }
+        break;
+    }
+
+    if (moved)
+    {
+        item->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
+        if (item->parent != NULL)
+        {
+            eel_canvas_item_request_update (item->parent);
+        }
+        else
+        {
+            eel_canvas_request_update (item->canvas);
+        }
+    }
+}
+
+/* Get_property handler for canvas groups */
+static void
+eel_canvas_group_get_property (GObject    *gobject,
+                               guint       param_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+    EelCanvasGroup *group;
+
+    g_return_if_fail (EEL_IS_CANVAS_GROUP (gobject));
+
+    group = EEL_CANVAS_GROUP (gobject);
+
+    switch (param_id)
+    {
+        case GROUP_PROP_X:
+        {
+            g_value_set_double (value, group->xpos);
+        }
+        break;
+
+        case GROUP_PROP_Y:
+        {
+            g_value_set_double (value, group->ypos);
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+        }
+        break;
+    }
+}
+
+/* Destroy handler for canvas groups */
+static void
+eel_canvas_group_destroy (EelCanvasItem *object)
+{
+    EelCanvasGroup *group;
+    EelCanvasItem *child;
+    GList *list;
+
+    g_return_if_fail (EEL_IS_CANVAS_GROUP (object));
+
+    group = EEL_CANVAS_GROUP (object);
+
+    list = group->item_list;
+    while (list)
+    {
+        child = list->data;
+        list = list->next;
+
+        eel_canvas_item_destroy (child);
+    }
+
+    if (EEL_CANVAS_ITEM_CLASS (group_parent_class)->destroy)
+    {
+        (*EEL_CANVAS_ITEM_CLASS (group_parent_class)->destroy)(object);
+    }
+}
+
+/* Update handler for canvas groups */
+static void
+eel_canvas_group_update (EelCanvasItem *item,
+                         double         i2w_dx,
+                         double         i2w_dy,
+                         int            flags)
+{
+    EelCanvasGroup *group;
+    GList *list;
+    EelCanvasItem *i;
+    double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
+    gboolean first = TRUE;
+
+    group = EEL_CANVAS_GROUP (item);
+
+    (*group_parent_class->update)(item, i2w_dx, i2w_dy, flags);
+
+    bbox_x0 = 0;
+    bbox_y0 = 0;
+    bbox_x1 = 0;
+    bbox_y1 = 0;
+
+    for (list = group->item_list; list; list = list->next)
+    {
+        i = list->data;
+
+        eel_canvas_item_invoke_update (i, i2w_dx + group->xpos, i2w_dy + group->ypos, flags);
+
+        if (first)
+        {
+            first = FALSE;
+            bbox_x0 = i->x1;
+            bbox_y0 = i->y1;
+            bbox_x1 = i->x2;
+            bbox_y1 = i->y2;
+        }
+        else
+        {
+            bbox_x0 = MIN (bbox_x0, i->x1);
+            bbox_y0 = MIN (bbox_y0, i->y1);
+            bbox_x1 = MAX (bbox_x1, i->x2);
+            bbox_y1 = MAX (bbox_y1, i->y2);
+        }
+    }
+    item->x1 = bbox_x0;
+    item->y1 = bbox_y0;
+    item->x2 = bbox_x1;
+    item->y2 = bbox_y1;
+}
+
+/* Unrealize handler for canvas groups */
+static void
+eel_canvas_group_unrealize (EelCanvasItem *item)
+{
+    EelCanvasGroup *group;
+    GList *list;
+    EelCanvasItem *i;
+
+    group = EEL_CANVAS_GROUP (item);
+
+    /* Unmap group before children to avoid flash */
+    if (item->flags & EEL_CANVAS_ITEM_MAPPED)
+    {
+        (*EEL_CANVAS_ITEM_GET_CLASS (item)->unmap)(item);
+    }
+
+    for (list = group->item_list; list; list = list->next)
+    {
+        i = list->data;
+
+        if (i->flags & EEL_CANVAS_ITEM_REALIZED)
+        {
+            (*EEL_CANVAS_ITEM_GET_CLASS (i)->unrealize)(i);
+        }
+    }
+
+    (*group_parent_class->unrealize)(item);
+}
+
+/* Map handler for canvas groups */
+static void
+eel_canvas_group_map (EelCanvasItem *item)
+{
+    EelCanvasGroup *group;
+    GList *list;
+    EelCanvasItem *i;
+
+    group = EEL_CANVAS_GROUP (item);
+
+    for (list = group->item_list; list; list = list->next)
+    {
+        i = list->data;
+
+        if (i->flags & EEL_CANVAS_ITEM_VISIBLE &&
+            !(i->flags & EEL_CANVAS_ITEM_MAPPED))
+        {
+            if (!(i->flags & EEL_CANVAS_ITEM_REALIZED))
+            {
+                (*EEL_CANVAS_ITEM_GET_CLASS (i)->realize)(i);
+            }
+
+            (*EEL_CANVAS_ITEM_GET_CLASS (i)->map)(i);
+        }
+    }
+
+    (*group_parent_class->map)(item);
+}
+
+/* Unmap handler for canvas groups */
+static void
+eel_canvas_group_unmap (EelCanvasItem *item)
+{
+    EelCanvasGroup *group;
+    GList *list;
+    EelCanvasItem *i;
+
+    group = EEL_CANVAS_GROUP (item);
+
+    for (list = group->item_list; list; list = list->next)
+    {
+        i = list->data;
+
+        if (i->flags & EEL_CANVAS_ITEM_MAPPED)
+        {
+            (*EEL_CANVAS_ITEM_GET_CLASS (i)->unmap)(i);
+        }
+    }
+
+    (*group_parent_class->unmap)(item);
+}
+
+/* Draw handler for canvas groups */
+static void
+eel_canvas_group_draw (EelCanvasItem  *item,
+                       cairo_t        *cr,
+                       cairo_region_t *region)
+{
+    EelCanvasGroup *group;
+    GList *list;
+    EelCanvasItem *child = NULL;
+
+    group = EEL_CANVAS_GROUP (item);
+
+    for (list = group->item_list; list; list = list->next)
+    {
+        child = list->data;
+
+        if ((child->flags & EEL_CANVAS_ITEM_MAPPED) &&
+            (EEL_CANVAS_ITEM_GET_CLASS (child)->draw))
+        {
+            GdkRectangle child_rect;
+
+            child_rect.x = child->x1;
+            child_rect.y = child->y1;
+            child_rect.width = child->x2 - child->x1 + 1;
+            child_rect.height = child->y2 - child->y1 + 1;
+
+            if (cairo_region_contains_rectangle (region, &child_rect) != CAIRO_REGION_OVERLAP_OUT)
+            {
+                EEL_CANVAS_ITEM_GET_CLASS (child)->draw (child, cr, region);
+            }
+        }
+    }
+}
+
+/* Point handler for canvas groups */
+static double
+eel_canvas_group_point (EelCanvasItem  *item,
+                        double          x,
+                        double          y,
+                        int             cx,
+                        int             cy,
+                        EelCanvasItem **actual_item)
+{
+    EelCanvasGroup *group;
+    GList *list;
+    EelCanvasItem *child, *point_item;
+    int x1, y1, x2, y2;
+    double gx, gy;
+    double dist, best;
+    int has_point;
+
+    group = EEL_CANVAS_GROUP (item);
+
+    x1 = cx - item->canvas->close_enough;
+    y1 = cy - item->canvas->close_enough;
+    x2 = cx + item->canvas->close_enough;
+    y2 = cy + item->canvas->close_enough;
+
+    best = 0.0;
+    *actual_item = NULL;
+
+    gx = x - group->xpos;
+    gy = y - group->ypos;
+
+    dist = 0.0;     /* keep gcc happy */
+
+    for (list = group->item_list; list; list = list->next)
+    {
+        child = list->data;
+
+        if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1))
+        {
+            continue;
+        }
+
+        point_item = NULL;         /* cater for incomplete item implementations */
+
+        if ((child->flags & EEL_CANVAS_ITEM_MAPPED)
+            && EEL_CANVAS_ITEM_GET_CLASS (child)->point)
+        {
+            dist = eel_canvas_item_invoke_point (child, gx, gy, cx, cy, &point_item);
+            has_point = TRUE;
+        }
+        else
+        {
+            has_point = FALSE;
+        }
+
+        if (has_point
+            && point_item
+            && ((int) (dist * item->canvas->pixels_per_unit + 0.5)
+                <= item->canvas->close_enough))
+        {
+            best = dist;
+            *actual_item = point_item;
+        }
+    }
+
+    return best;
+}
+
+static void
+eel_canvas_group_translate (EelCanvasItem *item,
+                            double         dx,
+                            double         dy)
+{
+    EelCanvasGroup *group;
+
+    group = EEL_CANVAS_GROUP (item);
+
+    group->xpos += dx;
+    group->ypos += dy;
+}
+
+/* Bounds handler for canvas groups */
+static void
+eel_canvas_group_bounds (EelCanvasItem *item,
+                         double        *x1,
+                         double        *y1,
+                         double        *x2,
+                         double        *y2)
+{
+    EelCanvasGroup *group;
+    EelCanvasItem *child;
+    GList *list;
+    double tx1, ty1, tx2, ty2;
+    double minx, miny, maxx, maxy;
+    int set;
+
+    group = EEL_CANVAS_GROUP (item);
+
+    /* Get the bounds of the first visible item */
+
+    child = NULL;     /* Unnecessary but eliminates a warning. */
+
+    set = FALSE;
+
+    for (list = group->item_list; list; list = list->next)
+    {
+        child = list->data;
+
+        if (child->flags & EEL_CANVAS_ITEM_MAPPED)
+        {
+            set = TRUE;
+            eel_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
+            break;
+        }
+    }
+
+    /* If there were no visible items, return an empty bounding box */
+
+    if (!set)
+    {
+        *x1 = *y1 = *x2 = *y2 = 0.0;
+        return;
+    }
+
+    /* Now we can grow the bounds using the rest of the items */
+
+    list = list->next;
+
+    for (; list; list = list->next)
+    {
+        child = list->data;
+
+        if (!(child->flags & EEL_CANVAS_ITEM_MAPPED))
+        {
+            continue;
+        }
+
+        eel_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
+
+        if (tx1 < minx)
+        {
+            minx = tx1;
+        }
+
+        if (ty1 < miny)
+        {
+            miny = ty1;
+        }
+
+        if (tx2 > maxx)
+        {
+            maxx = tx2;
+        }
+
+        if (ty2 > maxy)
+        {
+            maxy = ty2;
+        }
+    }
+
+    /* Make the bounds be relative to our parent's coordinate system */
+
+    if (item->parent)
+    {
+        minx += group->xpos;
+        miny += group->ypos;
+        maxx += group->xpos;
+        maxy += group->ypos;
+    }
+
+    *x1 = minx;
+    *y1 = miny;
+    *x2 = maxx;
+    *y2 = maxy;
+}
+
+/* Adds an item to a group */
+static void
+group_add (EelCanvasGroup *group,
+           EelCanvasItem  *item)
+{
+    g_object_ref_sink (item);
+
+    if (!group->item_list)
+    {
+        group->item_list = g_list_append (group->item_list, item);
+        group->item_list_end = group->item_list;
+    }
+    else
+    {
+        group->item_list_end = g_list_append (group->item_list_end, item)->next;
+    }
+
+    if (item->flags & EEL_CANVAS_ITEM_VISIBLE &&
+        group->item.flags & EEL_CANVAS_ITEM_MAPPED)
+    {
+        if (!(item->flags & EEL_CANVAS_ITEM_REALIZED))
+        {
+            (*EEL_CANVAS_ITEM_GET_CLASS (item)->realize)(item);
+        }
+
+        if (!(item->flags & EEL_CANVAS_ITEM_MAPPED))
+        {
+            (*EEL_CANVAS_ITEM_GET_CLASS (item)->map)(item);
+        }
+    }
+
+    if (item->flags & EEL_CANVAS_ITEM_VISIBLE)
+    {
+        eel_canvas_queue_resize (EEL_CANVAS_ITEM (group)->canvas);
+    }
+}
+
+/* Removes an item from a group */
+static void
+group_remove (EelCanvasGroup *group,
+              EelCanvasItem  *item)
+{
+    GList *children;
+
+    g_return_if_fail (EEL_IS_CANVAS_GROUP (group));
+    g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+    for (children = group->item_list; children; children = children->next)
+    {
+        if (children->data == item)
+        {
+            if (item->flags & EEL_CANVAS_ITEM_MAPPED)
+            {
+                (*EEL_CANVAS_ITEM_GET_CLASS (item)->unmap)(item);
+            }
+
+            if (item->flags & EEL_CANVAS_ITEM_REALIZED)
+            {
+                (*EEL_CANVAS_ITEM_GET_CLASS (item)->unrealize)(item);
+            }
+
+            if (item->flags & EEL_CANVAS_ITEM_VISIBLE)
+            {
+                eel_canvas_queue_resize (item->canvas);
+            }
+
+            /* Unparent the child */
+
+            item->parent = NULL;
+            /* item->canvas = NULL; */
+            g_object_unref (G_OBJECT (item));
+
+            /* Remove it from the list */
+
+            if (children == group->item_list_end)
+            {
+                group->item_list_end = children->prev;
+            }
+
+            group->item_list = g_list_remove_link (group->item_list, children);
+            g_list_free (children);
+            break;
+        }
+    }
+}
+
+
+/*** EelCanvas ***/
+
+
+static void eel_canvas_class_init (EelCanvasClass *klass);
+static void eel_canvas_init (EelCanvas *canvas);
+static void eel_canvas_destroy (GtkWidget *object);
+static void eel_canvas_map (GtkWidget *widget);
+static void eel_canvas_unmap (GtkWidget *widget);
+static void eel_canvas_realize (GtkWidget *widget);
+static void eel_canvas_unrealize (GtkWidget *widget);
+static void eel_canvas_size_allocate (GtkWidget     *widget,
+                                      GtkAllocation *allocation);
+static gint eel_canvas_button (GtkWidget      *widget,
+                               GdkEventButton *event);
+static gint eel_canvas_motion (GtkWidget      *widget,
+                               GdkEventMotion *event);
+static gint eel_canvas_draw (GtkWidget *widget,
+                             cairo_t   *cr);
+static gint eel_canvas_key (GtkWidget   *widget,
+                            GdkEventKey *event);
+static gint eel_canvas_crossing (GtkWidget        *widget,
+                                 GdkEventCrossing *event);
+static gint eel_canvas_focus_in (GtkWidget     *widget,
+                                 GdkEventFocus *event);
+static gint eel_canvas_focus_out (GtkWidget     *widget,
+                                  GdkEventFocus *event);
+static void eel_canvas_request_update_real (EelCanvas *canvas);
+static GtkLayoutClass *canvas_parent_class;
+
+/**
+ * eel_canvas_get_type:
+ *
+ * Registers the &EelCanvas class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &EelCanvas class.
+ **/
+GType
+eel_canvas_get_type (void)
+{
+    static GType canvas_type = 0;
+
+    if (!canvas_type)
+    {
+        static const GTypeInfo canvas_info =
+        {
+            sizeof (EelCanvasClass),
+            (GBaseInitFunc) NULL,
+            (GBaseFinalizeFunc) NULL,
+            (GClassInitFunc) eel_canvas_class_init,
+            NULL,                       /* class_finalize */
+            NULL,                       /* class_data */
+            sizeof (EelCanvas),
+            0,                          /* n_preallocs */
+            (GInstanceInitFunc) eel_canvas_init
+        };
+
+        canvas_type = g_type_register_static (gtk_layout_get_type (),
+                                              "EelCanvas",
+                                              &canvas_info,
+                                              0);
+    }
+
+    return canvas_type;
+}
+
+static void
+eel_canvas_get_property (GObject    *object,
+                         guint       prop_id,
+                         GValue     *value,
+                         GParamSpec *pspec)
+{
+    switch (prop_id)
+    {
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+        break;
+    }
+}
+
+static void
+eel_canvas_set_property (GObject      *object,
+                         guint         prop_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
+{
+    switch (prop_id)
+    {
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+        break;
+    }
+}
+
+static void
+eel_canvas_accessible_adjustment_changed (GtkAdjustment *adjustment,
+                                          gpointer       data)
+{
+    AtkObject *atk_obj;
+
+    /* The scrollbars have changed */
+    atk_obj = ATK_OBJECT (data);
+
+    g_signal_emit_by_name (atk_obj, "visible-data-changed");
+}
+
+static void
+eel_canvas_accessible_initialize (AtkObject *obj,
+                                  gpointer   data)
+{
+    EelCanvas *canvas = data;
+
+    if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize != NULL)
+    {
+        ATK_OBJECT_CLASS (accessible_parent_class)->initialize (obj, data);
+    }
+
+    gtk_accessible_set_widget (GTK_ACCESSIBLE (obj), GTK_WIDGET (data));
+    g_signal_connect (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)),
+                      "value-changed",
+                      G_CALLBACK (eel_canvas_accessible_adjustment_changed),
+                      obj);
+    g_signal_connect (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)),
+                      "value-changed",
+                      G_CALLBACK (eel_canvas_accessible_adjustment_changed),
+                      obj);
+
+    obj->role = ATK_ROLE_LAYERED_PANE;
+}
+
+static gint
+eel_canvas_accessible_get_n_children (AtkObject *obj)
+{
+    GtkAccessible *accessible;
+    GtkWidget *widget;
+    EelCanvas *canvas;
+    EelCanvasGroup *root_group;
+
+    accessible = GTK_ACCESSIBLE (obj);
+    widget = gtk_accessible_get_widget (accessible);
+
+    if (widget == NULL)
+    {
+        return 0;
+    }
+
+    g_return_val_if_fail (EEL_IS_CANVAS (widget), 0);
+
+    canvas = EEL_CANVAS (widget);
+    root_group = eel_canvas_root (canvas);
+    g_return_val_if_fail (root_group, 0);
+
+    return 1;
+}
+
+static AtkObject *
+eel_canvas_accessible_ref_child (AtkObject *obj,
+                                 gint       i)
+{
+    GtkAccessible *accessible;
+    GtkWidget *widget;
+    EelCanvas *canvas;
+    EelCanvasGroup *root_group;
+    AtkObject *atk_object;
+
+    /* Canvas only has one child, so return NULL if index is non zero */
+    if (i != 0)
+    {
+        return NULL;
+    }
+
+    accessible = GTK_ACCESSIBLE (obj);
+    widget = gtk_accessible_get_widget (accessible);
+
+    if (widget == NULL)
+    {
+        return NULL;
+    }
+
+    canvas = EEL_CANVAS (widget);
+    root_group = eel_canvas_root (canvas);
+    g_return_val_if_fail (root_group, NULL);
+
+    atk_object = atk_gobject_accessible_for_object (G_OBJECT (root_group));
+
+    return g_object_ref (atk_object);
+}
+
+static void
+eel_canvas_accessible_class_init (EelCanvasAccessibleClass *klass)
+{
+    AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
+
+    accessible_parent_class = g_type_class_peek_parent (klass);
+
+    atk_class->initialize = eel_canvas_accessible_initialize;
+    atk_class->get_n_children = eel_canvas_accessible_get_n_children;
+    atk_class->ref_child = eel_canvas_accessible_ref_child;
+}
+
+static void
+eel_canvas_accessible_init (EelCanvasAccessible *accessible)
+{
+}
+
+G_DEFINE_TYPE (EelCanvasAccessible, eel_canvas_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE)
+
+/* Class initialization function for EelCanvasClass */
+static void
+eel_canvas_class_init (EelCanvasClass *klass)
+{
+    GObjectClass *gobject_class;
+    GtkWidgetClass *widget_class;
+
+    gobject_class = (GObjectClass *) klass;
+    widget_class = (GtkWidgetClass *) klass;
+
+    canvas_parent_class = g_type_class_peek_parent (klass);
+
+    gobject_class->set_property = eel_canvas_set_property;
+    gobject_class->get_property = eel_canvas_get_property;
+
+    widget_class->destroy = eel_canvas_destroy;
+    widget_class->map = eel_canvas_map;
+    widget_class->unmap = eel_canvas_unmap;
+    widget_class->realize = eel_canvas_realize;
+    widget_class->unrealize = eel_canvas_unrealize;
+    widget_class->size_allocate = eel_canvas_size_allocate;
+    widget_class->button_press_event = eel_canvas_button;
+    widget_class->button_release_event = eel_canvas_button;
+    widget_class->motion_notify_event = eel_canvas_motion;
+    widget_class->draw = eel_canvas_draw;
+    widget_class->key_press_event = eel_canvas_key;
+    widget_class->key_release_event = eel_canvas_key;
+    widget_class->enter_notify_event = eel_canvas_crossing;
+    widget_class->leave_notify_event = eel_canvas_crossing;
+    widget_class->focus_in_event = eel_canvas_focus_in;
+    widget_class->focus_out_event = eel_canvas_focus_out;
+
+    klass->request_update = eel_canvas_request_update_real;
+
+    gtk_widget_class_set_accessible_type (widget_class, eel_canvas_accessible_get_type ());
+}
+
+/* Callback used when the root item of a canvas is destroyed.  The user should
+ * never ever do this, so we panic if this happens.
+ */
+static void
+panic_root_destroyed (GtkWidget *object,
+                      gpointer   data)
+{
+    g_error ("Eeeek, root item %p of canvas %p was destroyed!", object, data);
+}
+
+/* Object initialization function for EelCanvas */
+static void
+eel_canvas_init (EelCanvas *canvas)
+{
+    guint width, height;
+    gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE);
+
+    gtk_widget_set_redraw_on_allocate (GTK_WIDGET (canvas), FALSE);
+
+    canvas->scroll_x1 = 0.0;
+    canvas->scroll_y1 = 0.0;
+    gtk_layout_get_size (GTK_LAYOUT (canvas),
+                         &width, &height);
+    canvas->scroll_x2 = width;
+    canvas->scroll_y2 = height;
+
+    canvas->pixels_per_unit = 1.0;
+
+    canvas->pick_event.type = GDK_LEAVE_NOTIFY;
+    canvas->pick_event.crossing.x = 0;
+    canvas->pick_event.crossing.y = 0;
+
+    gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (canvas), NULL);
+    gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (canvas), NULL);
+
+    /* Create the root item as a special case */
+
+    canvas->root = EEL_CANVAS_ITEM (g_object_new (eel_canvas_group_get_type (), NULL));
+    canvas->root->canvas = canvas;
+
+    g_object_ref_sink (canvas->root);
+
+    canvas->root_destroy_id = g_signal_connect (G_OBJECT (canvas->root),
+                                                "destroy", G_CALLBACK (panic_root_destroyed), canvas);
+
+    canvas->need_repick = TRUE;
+    canvas->doing_update = FALSE;
+}
+
+/* Convenience function to remove the idle handler of a canvas */
+static void
+remove_idle (EelCanvas *canvas)
+{
+    if (canvas->idle_id == 0)
+    {
+        return;
+    }
+
+    g_source_remove (canvas->idle_id);
+    canvas->idle_id = 0;
+}
+
+/* Removes the transient state of the canvas (idle handler, grabs). */
+static void
+shutdown_transients (EelCanvas *canvas)
+{
+    /* We turn off the need_redraw flag, since if the canvas is mapped again
+     * it will request a redraw anyways.  We do not turn off the need_update
+     * flag, though, because updates are not queued when the canvas remaps
+     * itself.
+     */
+    if (canvas->need_redraw)
+    {
+        canvas->need_redraw = FALSE;
+    }
+
+    if (canvas->grabbed_item)
+    {
+        eel_canvas_item_ungrab (canvas->grabbed_item);
+    }
+
+    remove_idle (canvas);
+}
+
+/* Destroy handler for EelCanvas */
+static void
+eel_canvas_destroy (GtkWidget *object)
+{
+    EelCanvas *canvas;
+
+    g_return_if_fail (EEL_IS_CANVAS (object));
+
+    /* remember, destroy can be run multiple times! */
+
+    canvas = EEL_CANVAS (object);
+
+    g_clear_signal_handler (&canvas->root_destroy_id, G_OBJECT (canvas->root));
+    if (canvas->root)
+    {
+        EelCanvasItem *root = canvas->root;
+        canvas->root = NULL;
+        eel_canvas_item_destroy (root);
+        g_object_unref (root);
+    }
+
+    shutdown_transients (canvas);
+
+    if (GTK_WIDGET_CLASS (canvas_parent_class)->destroy)
+    {
+        (*GTK_WIDGET_CLASS (canvas_parent_class)->destroy)(object);
+    }
+}
+
+/**
+ * eel_canvas_new:
+ * @void:
+ *
+ * Creates a new empty canvas.  If you wish to use the
+ * &EelCanvasImage item inside this canvas, then you must push the gdk_imlib
+ * visual and colormap before calling this function, and they can be popped
+ * afterwards.
+ *
+ * Return value: A newly-created canvas.
+ **/
+GtkWidget *
+eel_canvas_new (void)
+{
+    return GTK_WIDGET (g_object_new (eel_canvas_get_type (), NULL));
+}
+
+/* Map handler for the canvas */
+static void
+eel_canvas_map (GtkWidget *widget)
+{
+    EelCanvas *canvas;
+
+    g_return_if_fail (EEL_IS_CANVAS (widget));
+
+    /* Normal widget mapping stuff */
+
+    if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
+    {
+        (*GTK_WIDGET_CLASS (canvas_parent_class)->map)(widget);
+    }
+
+    canvas = EEL_CANVAS (widget);
+
+    /* Map items */
+
+    if (canvas->root->flags & EEL_CANVAS_ITEM_VISIBLE &&
+        !(canvas->root->flags & EEL_CANVAS_ITEM_MAPPED) &&
+        EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
+    {
+        (*EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map)(canvas->root);
+    }
+}
+
+/* Unmap handler for the canvas */
+static void
+eel_canvas_unmap (GtkWidget *widget)
+{
+    EelCanvas *canvas;
+
+    g_return_if_fail (EEL_IS_CANVAS (widget));
+
+    canvas = EEL_CANVAS (widget);
+
+    shutdown_transients (canvas);
+
+    /* Unmap items */
+
+    if (EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
+    {
+        (*EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)(canvas->root);
+    }
+
+    /* Normal widget unmapping stuff */
+
+    if (GTK_WIDGET_CLASS (canvas_parent_class)->unmap)
+    {
+        (*GTK_WIDGET_CLASS (canvas_parent_class)->unmap)(widget);
+    }
+}
+
+/* Realize handler for the canvas */
+static void
+eel_canvas_realize (GtkWidget *widget)
+{
+    EelCanvas *canvas;
+
+    g_return_if_fail (EEL_IS_CANVAS (widget));
+
+    /* Normal widget realization stuff */
+
+    if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
+    {
+        (*GTK_WIDGET_CLASS (canvas_parent_class)->realize)(widget);
+    }
+
+    canvas = EEL_CANVAS (widget);
+
+    gdk_window_set_events (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)),
+                           (gdk_window_get_events (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
+                            | GDK_EXPOSURE_MASK
+                            | GDK_BUTTON_PRESS_MASK
+                            | GDK_BUTTON_RELEASE_MASK
+                            | GDK_POINTER_MOTION_MASK
+                            | GDK_KEY_PRESS_MASK
+                            | GDK_KEY_RELEASE_MASK
+                            | GDK_ENTER_NOTIFY_MASK
+                            | GDK_LEAVE_NOTIFY_MASK
+                            | GDK_FOCUS_CHANGE_MASK));
+
+    /* Create our own temporary pixmap gc and realize all the items */
+
+    (*EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->realize)(canvas->root);
+}
+
+/* Unrealize handler for the canvas */
+static void
+eel_canvas_unrealize (GtkWidget *widget)
+{
+    EelCanvas *canvas;
+
+    g_return_if_fail (EEL_IS_CANVAS (widget));
+
+    canvas = EEL_CANVAS (widget);
+
+    shutdown_transients (canvas);
+
+    /* Unrealize items and parent widget */
+
+    (*EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize)(canvas->root);
+
+    if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
+    {
+        (*GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)(widget);
+    }
+}
+
+/* Handles scrolling of the canvas.  Adjusts the scrolling and zooming offset to
+ * keep as much as possible of the canvas scrolling region in view.
+ */
+static void
+scroll_to (EelCanvas *canvas,
+           int        cx,
+           int        cy)
+{
+    int scroll_width, scroll_height;
+    int right_limit, bottom_limit;
+    int old_zoom_xofs, old_zoom_yofs;
+    int changed_x = FALSE, changed_y = FALSE;
+    int canvas_width, canvas_height;
+    GtkAllocation allocation;
+    GtkAdjustment *vadjustment, *hadjustment;
+    guint width, height;
+
+    gtk_widget_get_allocation (GTK_WIDGET (canvas), &allocation);
+    canvas_width = allocation.width;
+    canvas_height = allocation.height;
+
+    scroll_width = floor ((canvas->scroll_x2 - canvas->scroll_x1) * canvas->pixels_per_unit + 0.5);
+    scroll_height = floor ((canvas->scroll_y2 - canvas->scroll_y1) * canvas->pixels_per_unit + 0.5);
+
+    right_limit = scroll_width - canvas_width;
+    bottom_limit = scroll_height - canvas_height;
+
+    old_zoom_xofs = canvas->zoom_xofs;
+    old_zoom_yofs = canvas->zoom_yofs;
+
+    if (right_limit < 0)
+    {
+        cx = 0;
+        if (canvas->center_scroll_region)
+        {
+            canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
+            scroll_width = canvas_width;
+        }
+        else
+        {
+            canvas->zoom_xofs = 0;
+        }
+    }
+    else if (cx < 0)
+    {
+        cx = 0;
+        canvas->zoom_xofs = 0;
+    }
+    else if (cx > right_limit)
+    {
+        cx = right_limit;
+        canvas->zoom_xofs = 0;
+    }
+    else
+    {
+        canvas->zoom_xofs = 0;
+    }
+
+    if (bottom_limit < 0)
+    {
+        cy = 0;
+        if (canvas->center_scroll_region)
+        {
+            canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
+            scroll_height = canvas_height;
+        }
+        else
+        {
+            canvas->zoom_yofs = 0;
+        }
+    }
+    else if (cy < 0)
+    {
+        cy = 0;
+        canvas->zoom_yofs = 0;
+    }
+    else if (cy > bottom_limit)
+    {
+        cy = bottom_limit;
+        canvas->zoom_yofs = 0;
+    }
+    else
+    {
+        canvas->zoom_yofs = 0;
+    }
+
+    if ((canvas->zoom_xofs != old_zoom_xofs) || (canvas->zoom_yofs != old_zoom_yofs))
+    {
+        /* This can only occur, if either canvas size or widget size changes
+         * So I think we can request full redraw here
+         * More stuff - we have to mark root as needing fresh affine (Lauris)
+         */
+        if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE))
+        {
+            canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
+            eel_canvas_request_update (canvas);
+        }
+        gtk_widget_queue_draw (GTK_WIDGET (canvas));
+    }
+
+    hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
+    vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
+
+    if (((int) gtk_adjustment_get_value (hadjustment)) != cx)
+    {
+        gtk_adjustment_set_value (hadjustment, cx);
+        changed_x = TRUE;
+    }
+
+    if (((int) gtk_adjustment_get_value (vadjustment)) != cy)
+    {
+        gtk_adjustment_set_value (vadjustment, cy);
+        changed_y = TRUE;
+    }
+
+    gtk_layout_get_size (&canvas->layout, &width, &height);
+    if ((scroll_width != (int) width) || (scroll_height != (int) height))
+    {
+        gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
+    }
+
+    /* Signal GtkLayout that it should do a redraw. */
+    if (changed_x)
+    {
+        g_signal_emit_by_name (hadjustment, "value-changed");
+    }
+    if (changed_y)
+    {
+        g_signal_emit_by_name (vadjustment, "value-changed");
+    }
+}
+
+/* Size allocation handler for the canvas */
+static void
+eel_canvas_size_allocate (GtkWidget     *widget,
+                          GtkAllocation *allocation)
+{
+    EelCanvas *canvas;
+    GtkAdjustment *vadjustment, *hadjustment;
+
+    g_return_if_fail (EEL_IS_CANVAS (widget));
+    g_return_if_fail (allocation != NULL);
+
+    if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)
+    {
+        (*GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)(widget, allocation);
+    }
+
+    canvas = EEL_CANVAS (widget);
+
+    /* Recenter the view, if appropriate */
+
+    hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
+    vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
+
+    gtk_adjustment_set_page_size (hadjustment, allocation->width);
+    gtk_adjustment_set_page_increment (hadjustment, allocation->width / 2);
+
+    gtk_adjustment_set_page_size (vadjustment, allocation->height);
+    gtk_adjustment_set_page_increment (vadjustment, allocation->height / 2);
+
+    scroll_to (canvas,
+               gtk_adjustment_get_value (hadjustment),
+               gtk_adjustment_get_value (vadjustment));
+
+    g_signal_emit_by_name (hadjustment, "changed");
+    g_signal_emit_by_name (vadjustment, "changed");
+}
+
+/* Emits an event for an item in the canvas, be it the current item, grabbed
+ * item, or focused item, as appropriate.
+ */
+
+static int
+emit_event (EelCanvas *canvas,
+            GdkEvent  *event)
+{
+    GdkEvent ev;
+    gint finished;
+    EelCanvasItem *item;
+    EelCanvasItem *parent;
+    guint mask;
+
+    /* Could be an old pick event */
+    if (!gtk_widget_get_realized (GTK_WIDGET (canvas)))
+    {
+        return FALSE;
+    }
+
+    /* Perform checks for grabbed items */
+
+    if (canvas->grabbed_item &&
+        !is_descendant (canvas->current_item, canvas->grabbed_item))
+    {
+        return FALSE;
+    }
+
+    if (canvas->grabbed_item)
+    {
+        switch (event->type)
+        {
+            case GDK_ENTER_NOTIFY:
+            {
+                mask = GDK_ENTER_NOTIFY_MASK;
+            }
+            break;
+
+            case GDK_LEAVE_NOTIFY:
+            {
+                mask = GDK_LEAVE_NOTIFY_MASK;
+            }
+            break;
+
+            case GDK_MOTION_NOTIFY:
+            {
+                mask = GDK_POINTER_MOTION_MASK;
+            }
+            break;
+
+            case GDK_BUTTON_PRESS:
+            case GDK_2BUTTON_PRESS:
+            case GDK_3BUTTON_PRESS:
+            {
+                mask = GDK_BUTTON_PRESS_MASK;
+            }
+            break;
+
+            case GDK_BUTTON_RELEASE:
+            {
+                mask = GDK_BUTTON_RELEASE_MASK;
+            }
+            break;
+
+            case GDK_KEY_PRESS:
+            {
+                mask = GDK_KEY_PRESS_MASK;
+            }
+            break;
+
+            case GDK_KEY_RELEASE:
+            {
+                mask = GDK_KEY_RELEASE_MASK;
+            }
+            break;
+
+            default:
+            {
+                mask = 0;
+            }
+            break;
+        }
+
+        if (!(mask & canvas->grabbed_event_mask))
+        {
+            return FALSE;
+        }
+    }
+
+    /* Convert to world coordinates -- we have two cases because of diferent
+     * offsets of the fields in the event structures.
+     */
+
+    ev = *event;
+
+    switch (ev.type)
+    {
+        case GDK_ENTER_NOTIFY:
+        case GDK_LEAVE_NOTIFY:
+        {
+            eel_canvas_window_to_world (canvas,
+                                        ev.crossing.x, ev.crossing.y,
+                                        &ev.crossing.x, &ev.crossing.y);
+        }
+        break;
+
+        case GDK_MOTION_NOTIFY:
+        case GDK_BUTTON_PRESS:
+        case GDK_2BUTTON_PRESS:
+        case GDK_3BUTTON_PRESS:
+        case GDK_BUTTON_RELEASE:
+        {
+            eel_canvas_window_to_world (canvas,
+                                        ev.motion.x, ev.motion.y,
+                                        &ev.motion.x, &ev.motion.y);
+        }
+        break;
+
+        default:
+        {
+        }
+        break;
+    }
+
+    /* Choose where we send the event */
+
+    item = canvas->current_item;
+
+    if (canvas->focused_item
+        && ((event->type == GDK_KEY_PRESS) ||
+            (event->type == GDK_KEY_RELEASE) ||
+            (event->type == GDK_FOCUS_CHANGE)))
+    {
+        item = canvas->focused_item;
+    }
+
+    /* The event is propagated up the hierarchy (for if someone connected to
+     * a group instead of a leaf event), and emission is stopped if a
+     * handler returns TRUE, just like for GtkWidget events.
+     */
+
+    finished = FALSE;
+
+    while (item && !finished)
+    {
+        g_object_ref (item);
+
+        g_signal_emit (
+            G_OBJECT (item), item_signals[ITEM_EVENT], 0,
+            &ev, &finished);
+
+        parent = item->parent;
+        g_object_unref (item);
+
+        item = parent;
+    }
+
+    return finished;
+}
+
+/* Re-picks the current item in the canvas, based on the event's coordinates.
+ * Also emits enter/leave events for items as appropriate.
+ */
+static int
+pick_current_item (EelCanvas *canvas,
+                   GdkEvent  *event)
+{
+    int button_down;
+    double x, y;
+    int cx, cy;
+    int retval;
+
+    retval = FALSE;
+
+    /* If a button is down, we'll perform enter and leave events on the
+     * current item, but not enter on any other item.  This is more or less
+     * like X pointer grabbing for canvas items.
+     */
+    button_down = canvas->state & (GDK_BUTTON1_MASK
+                                   | GDK_BUTTON2_MASK
+                                   | GDK_BUTTON3_MASK
+                                   | GDK_BUTTON4_MASK
+                                   | GDK_BUTTON5_MASK);
+    if (!button_down)
+    {
+        canvas->left_grabbed_item = FALSE;
+    }
+
+    /* Save the event in the canvas.  This is used to synthesize enter and
+     * leave events in case the current item changes.  It is also used to
+     * re-pick the current item if the current one gets deleted.  Also,
+     * synthesize an enter event.
+     */
+    if (event != &canvas->pick_event)
+    {
+        if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE))
+        {
+            /* these fields have the same offsets in both types of events */
+
+            canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
+            canvas->pick_event.crossing.window = event->motion.window;
+            canvas->pick_event.crossing.send_event = event->motion.send_event;
+            canvas->pick_event.crossing.subwindow = NULL;
+            canvas->pick_event.crossing.x = event->motion.x;
+            canvas->pick_event.crossing.y = event->motion.y;
+            canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
+            canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
+            canvas->pick_event.crossing.focus = FALSE;
+            canvas->pick_event.crossing.state = event->motion.state;
+
+            /* these fields don't have the same offsets in both types of events */
+
+            if (event->type == GDK_MOTION_NOTIFY)
+            {
+                canvas->pick_event.crossing.x_root = event->motion.x_root;
+                canvas->pick_event.crossing.y_root = event->motion.y_root;
+            }
+            else
+            {
+                canvas->pick_event.crossing.x_root = event->button.x_root;
+                canvas->pick_event.crossing.y_root = event->button.y_root;
+            }
+        }
+        else
+        {
+            canvas->pick_event = *event;
+        }
+    }
+
+    /* Don't do anything else if this is a recursive call */
+
+    if (canvas->in_repick)
+    {
+        return retval;
+    }
+
+    /* LeaveNotify means that there is no current item, so we don't look for one */
+
+    if (canvas->pick_event.type != GDK_LEAVE_NOTIFY)
+    {
+        /* these fields don't have the same offsets in both types of events */
+
+        if (canvas->pick_event.type == GDK_ENTER_NOTIFY)
+        {
+            x = canvas->pick_event.crossing.x;
+            y = canvas->pick_event.crossing.y;
+        }
+        else
+        {
+            x = canvas->pick_event.motion.x;
+            y = canvas->pick_event.motion.y;
+        }
+
+        /* canvas pixel coords */
+
+        cx = (int) (x + 0.5);
+        cy = (int) (y + 0.5);
+
+        /* world coords */
+        eel_canvas_c2w (canvas, cx, cy, &x, &y);
+
+        /* find the closest item */
+        if (canvas->root->flags & EEL_CANVAS_ITEM_MAPPED)
+        {
+            eel_canvas_item_invoke_point (canvas->root, x, y, cx, cy,
+                                          &canvas->new_current_item);
+        }
+        else
+        {
+            canvas->new_current_item = NULL;
+        }
+    }
+    else
+    {
+        canvas->new_current_item = NULL;
+    }
+
+    if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item)
+    {
+        return retval;         /* current item did not change */
+    }
+    /* Synthesize events for old and new current items */
+
+    if ((canvas->new_current_item != canvas->current_item)
+        && (canvas->current_item != NULL)
+        && !canvas->left_grabbed_item)
+    {
+        GdkEvent new_event;
+
+        new_event = canvas->pick_event;
+        new_event.type = GDK_LEAVE_NOTIFY;
+
+        new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
+        new_event.crossing.subwindow = NULL;
+        canvas->in_repick = TRUE;
+        retval = emit_event (canvas, &new_event);
+        canvas->in_repick = FALSE;
+    }
+
+    /* new_current_item may have been set to NULL during the call to emit_event() above */
+
+    if ((canvas->new_current_item != canvas->current_item) && button_down)
+    {
+        canvas->current_item = canvas->new_current_item;
+        canvas->left_grabbed_item = TRUE;
+        return retval;
+    }
+
+    /* Handle the rest of cases */
+
+    canvas->left_grabbed_item = FALSE;
+    canvas->current_item = canvas->new_current_item;
+
+    if (canvas->current_item != NULL)
+    {
+        GdkEvent new_event;
+
+        new_event = canvas->pick_event;
+        new_event.type = GDK_ENTER_NOTIFY;
+        new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
+        new_event.crossing.subwindow = NULL;
+        retval = emit_event (canvas, &new_event);
+    }
+
+    return retval;
+}
+
+/* Button event handler for the canvas */
+static gint
+eel_canvas_button (GtkWidget      *widget,
+                   GdkEventButton *event)
+{
+    EelCanvas *canvas;
+    int mask;
+    int retval;
+
+    g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    retval = FALSE;
+
+    canvas = EEL_CANVAS (widget);
+
+    /* Don't handle extra mouse button events */
+    if (event->button > 5)
+    {
+        return FALSE;
+    }
+
+    /*
+     * dispatch normally regardless of the event's window if an item has
+     * has a pointer grab in effect
+     */
+    if (!canvas->grabbed_item && event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
+    {
+        return retval;
+    }
+
+    switch (event->button)
+    {
+        case 1:
+        {
+            mask = GDK_BUTTON1_MASK;
+        }
+        break;
+
+        case 2:
+        {
+            mask = GDK_BUTTON2_MASK;
+        }
+        break;
+
+        case 3:
+        {
+            mask = GDK_BUTTON3_MASK;
+        }
+        break;
+
+        case 4:
+        {
+            mask = GDK_BUTTON4_MASK;
+        }
+        break;
+
+        case 5:
+        {
+            mask = GDK_BUTTON5_MASK;
+        }
+        break;
+
+        default:
+        {
+            mask = 0;
+        }
+    }
+
+    switch (event->type)
+    {
+        case GDK_BUTTON_PRESS:
+        case GDK_2BUTTON_PRESS:
+        case GDK_3BUTTON_PRESS:
+        {
+            /* Pick the current item as if the button were not pressed, and
+             * then process the event.
+             */
+            event->state ^= mask;
+            canvas->state = event->state;
+            pick_current_item (canvas, (GdkEvent *) event);
+            event->state ^= mask;
+            canvas->state = event->state;
+            retval = emit_event (canvas, (GdkEvent *) event);
+        }
+        break;
+
+        case GDK_BUTTON_RELEASE:
+        {
+            /* Process the event as if the button were pressed, then repick
+             * after the button has been released
+             */
+            canvas->state = event->state;
+            retval = emit_event (canvas, (GdkEvent *) event);
+            event->state ^= mask;
+            canvas->state = event->state;
+            pick_current_item (canvas, (GdkEvent *) event);
+            event->state ^= mask;
+        }
+        break;
+
+        default:
+        {
+            g_assert_not_reached ();
+        }
+    }
+
+    return retval;
+}
+
+/* Motion event handler for the canvas */
+static gint
+eel_canvas_motion (GtkWidget      *widget,
+                   GdkEventMotion *event)
+{
+    EelCanvas *canvas;
+
+    g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    canvas = EEL_CANVAS (widget);
+
+    if (event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
+    {
+        return FALSE;
+    }
+
+    canvas->state = event->state;
+    pick_current_item (canvas, (GdkEvent *) event);
+    return emit_event (canvas, (GdkEvent *) event);
+}
+
+/* Key event handler for the canvas */
+static gint
+eel_canvas_key (GtkWidget   *widget,
+                GdkEventKey *event)
+{
+    EelCanvas *canvas;
+
+    g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    canvas = EEL_CANVAS (widget);
+
+    if (emit_event (canvas, (GdkEvent *) event))
+    {
+        return TRUE;
+    }
+    if (event->type == GDK_KEY_RELEASE)
+    {
+        return GTK_WIDGET_CLASS (canvas_parent_class)->key_release_event (widget, event);
+    }
+    else
+    {
+        return GTK_WIDGET_CLASS (canvas_parent_class)->key_press_event (widget, event);
+    }
+}
+
+
+/* Crossing event handler for the canvas */
+static gint
+eel_canvas_crossing (GtkWidget        *widget,
+                     GdkEventCrossing *event)
+{
+    EelCanvas *canvas;
+
+    g_return_val_if_fail (EEL_IS_CANVAS (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    canvas = EEL_CANVAS (widget);
+
+    if (event->window != gtk_layout_get_bin_window (GTK_LAYOUT (canvas)))
+    {
+        return FALSE;
+    }
+
+    canvas->state = event->state;
+    return pick_current_item (canvas, (GdkEvent *) event);
+}
+
+/* Focus in handler for the canvas */
+static gint
+eel_canvas_focus_in (GtkWidget     *widget,
+                     GdkEventFocus *event)
+{
+    EelCanvas *canvas;
+
+    canvas = EEL_CANVAS (widget);
+
+    if (canvas->focused_item)
+    {
+        return emit_event (canvas, (GdkEvent *) event);
+    }
+    else
+    {
+        return FALSE;
+    }
+}
+
+/* Focus out handler for the canvas */
+static gint
+eel_canvas_focus_out (GtkWidget     *widget,
+                      GdkEventFocus *event)
+{
+    EelCanvas *canvas;
+
+    canvas = EEL_CANVAS (widget);
+
+    if (canvas->focused_item)
+    {
+        return emit_event (canvas, (GdkEvent *) event);
+    }
+    else
+    {
+        return FALSE;
+    }
+}
+
+
+static cairo_region_t *
+eel_cairo_get_clip_region (cairo_t *cr)
+{
+    cairo_rectangle_list_t *list;
+    cairo_region_t *region;
+    int i;
+
+    list = cairo_copy_clip_rectangle_list (cr);
+    if (list->status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
+    {
+        cairo_rectangle_int_t clip_rect;
+
+        cairo_rectangle_list_destroy (list);
+
+        if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
+        {
+            return NULL;
+        }
+        return cairo_region_create_rectangle (&clip_rect);
+    }
+
+
+    region = cairo_region_create ();
+    for (i = list->num_rectangles - 1; i >= 0; --i)
+    {
+        cairo_rectangle_t *rect = &list->rectangles[i];
+        cairo_rectangle_int_t clip_rect;
+
+        clip_rect.x = floor (rect->x);
+        clip_rect.y = floor (rect->y);
+        clip_rect.width = ceil (rect->x + rect->width) - clip_rect.x;
+        clip_rect.height = ceil (rect->y + rect->height) - clip_rect.y;
+
+        if (cairo_region_union_rectangle (region, &clip_rect) != CAIRO_STATUS_SUCCESS)
+        {
+            cairo_region_destroy (region);
+            region = NULL;
+            break;
+        }
+    }
+
+    cairo_rectangle_list_destroy (list);
+    return region;
+}
+
+/* Expose handler for the canvas */
+static gboolean
+eel_canvas_draw (GtkWidget *widget,
+                 cairo_t   *cr)
+{
+    EelCanvas *canvas = EEL_CANVAS (widget);
+    GdkWindow *bin_window;
+    cairo_region_t *region;
+
+    if (!gdk_cairo_get_clip_rectangle (cr, NULL))
+    {
+        return FALSE;
+    }
+
+    bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
+
+    if (!gtk_cairo_should_draw_window (cr, bin_window))
+    {
+        return FALSE;
+    }
+
+    cairo_save (cr);
+
+    gtk_cairo_transform_to_window (cr, widget, bin_window);
+
+    region = eel_cairo_get_clip_region (cr);
+    if (region == NULL)
+    {
+        cairo_restore (cr);
+        return FALSE;
+    }
+
+#ifdef VERBOSE
+    g_print ("Draw\n");
+#endif
+    /* If there are any outstanding items that need updating, do them now */
+    if (canvas->idle_id)
+    {
+        g_source_remove (canvas->idle_id);
+        canvas->idle_id = 0;
+    }
+    if (canvas->need_update)
+    {
+        g_return_val_if_fail (!canvas->doing_update, FALSE);
+
+        canvas->doing_update = TRUE;
+        eel_canvas_item_invoke_update (canvas->root, 0, 0, 0);
+
+        g_return_val_if_fail (canvas->doing_update, FALSE);
+
+        canvas->doing_update = FALSE;
+
+        canvas->need_update = FALSE;
+    }
+
+    if (canvas->root->flags & EEL_CANVAS_ITEM_MAPPED)
+    {
+        EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->draw (canvas->root, cr, region);
+    }
+
+    cairo_restore (cr);
+
+    /* Chain up to get exposes on child widgets */
+    if (GTK_WIDGET_CLASS (canvas_parent_class)->draw)
+    {
+        GTK_WIDGET_CLASS (canvas_parent_class)->draw (widget, cr);
+    }
+
+    cairo_region_destroy (region);
+    return FALSE;
+}
+
+static void
+do_update (EelCanvas *canvas)
+{
+    /* Cause the update if necessary */
+
+update_again:
+    if (canvas->need_update)
+    {
+        g_return_if_fail (!canvas->doing_update);
+
+        canvas->doing_update = TRUE;
+        eel_canvas_item_invoke_update (canvas->root, 0, 0, 0);
+
+        g_return_if_fail (canvas->doing_update);
+
+        canvas->doing_update = FALSE;
+
+        canvas->need_update = FALSE;
+    }
+
+    /* Pick new current item */
+
+    while (canvas->need_repick)
+    {
+        canvas->need_repick = FALSE;
+        pick_current_item (canvas, &canvas->pick_event);
+    }
+
+    /* it is possible that during picking we emitted an event in which
+     *  the user then called some function which then requested update
+     *  of something.  Without this we'd be left in a state where
+     *  need_update would have been left TRUE and the canvas would have
+     *  been left unpainted. */
+    if (canvas->need_update)
+    {
+        goto update_again;
+    }
+}
+
+/* Idle handler for the canvas.  It deals with pending updates and redraws. */
+static gint
+idle_handler (gpointer data)
+{
+    EelCanvas *canvas;
+
+    canvas = EEL_CANVAS (data);
+    do_update (canvas);
+
+    /* Reset idle id */
+    canvas->idle_id = 0;
+
+    return FALSE;
+}
+
+/* Convenience function to add an idle handler to a canvas */
+static void
+add_idle (EelCanvas *canvas)
+{
+    if (!canvas->idle_id)
+    {
+        /* We let the update idle handler have higher priority
+         * than the redraw idle handler so the canvas state
+         * will be updated during the expose event.  canvas in
+         * expose_event.
+         */
+        canvas->idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW - 20,
+                                           idle_handler, canvas, NULL);
+    }
+}
+
+/**
+ * eel_canvas_root:
+ * @canvas: A canvas.
+ *
+ * Queries the root group of a canvas.
+ *
+ * Return value: The root group of the specified canvas.
+ **/
+EelCanvasGroup *
+eel_canvas_root (EelCanvas *canvas)
+{
+    g_return_val_if_fail (EEL_IS_CANVAS (canvas), NULL);
+
+    return EEL_CANVAS_GROUP (canvas->root);
+}
+
+
+/**
+ * eel_canvas_set_scroll_region:
+ * @canvas: A canvas.
+ * @x1: Leftmost limit of the scrolling region.
+ * @y1: Upper limit of the scrolling region.
+ * @x2: Rightmost limit of the scrolling region.
+ * @y2: Lower limit of the scrolling region.
+ *
+ * Sets the scrolling region of a canvas to the specified rectangle.  The canvas
+ * will then be able to scroll only within this region.  The view of the canvas
+ * is adjusted as appropriate to display as much of the new region as possible.
+ **/
+void
+eel_canvas_set_scroll_region (EelCanvas *canvas,
+                              double     x1,
+                              double     y1,
+                              double     x2,
+                              double     y2)
+{
+    double wxofs, wyofs;
+    int xofs, yofs;
+    GtkAdjustment *vadjustment, *hadjustment;
+
+    g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+    if ((canvas->scroll_x1 == x1) && (canvas->scroll_y1 == y1) &&
+        (canvas->scroll_x2 == x2) && (canvas->scroll_y2 == y2))
+    {
+        return;
+    }
+
+    /*
+     * Set the new scrolling region.  If possible, do not move the visible contents of the
+     * canvas.
+     */
+    hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
+    vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
+
+    eel_canvas_c2w (canvas,
+                    gtk_adjustment_get_value (hadjustment) + canvas->zoom_xofs,
+                    gtk_adjustment_get_value (vadjustment) + canvas->zoom_yofs,
+                    /*canvas->zoom_xofs,
+                     *   canvas->zoom_yofs,*/
+                    &wxofs, &wyofs);
+
+    canvas->scroll_x1 = x1;
+    canvas->scroll_y1 = y1;
+    canvas->scroll_x2 = x2;
+    canvas->scroll_y2 = y2;
+
+    eel_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
+
+    scroll_to (canvas, xofs, yofs);
+
+    canvas->need_repick = TRUE;
+
+    if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE))
+    {
+        canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
+        eel_canvas_request_update (canvas);
+    }
+}
+
+
+/**
+ * eel_canvas_get_scroll_region:
+ * @canvas: A canvas.
+ * @x1: Leftmost limit of the scrolling region (return value).
+ * @y1: Upper limit of the scrolling region (return value).
+ * @x2: Rightmost limit of the scrolling region (return value).
+ * @y2: Lower limit of the scrolling region (return value).
+ *
+ * Queries the scrolling region of a canvas.
+ **/
+void
+eel_canvas_get_scroll_region (EelCanvas *canvas,
+                              double    *x1,
+                              double    *y1,
+                              double    *x2,
+                              double    *y2)
+{
+    g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+    if (x1)
+    {
+        *x1 = canvas->scroll_x1;
+    }
+
+    if (y1)
+    {
+        *y1 = canvas->scroll_y1;
+    }
+
+    if (x2)
+    {
+        *x2 = canvas->scroll_x2;
+    }
+
+    if (y2)
+    {
+        *y2 = canvas->scroll_y2;
+    }
+}
+
+/**
+ * eel_canvas_set_pixels_per_unit:
+ * @canvas: A canvas.
+ * @n: The number of pixels that correspond to one canvas unit.
+ *
+ * Sets the zooming factor of a canvas by specifying the number of pixels that
+ * correspond to one canvas unit.
+ **/
+void
+eel_canvas_set_pixels_per_unit (EelCanvas *canvas,
+                                double     n)
+{
+    GtkWidget *widget;
+    double cx, cy;
+    int x1, y1;
+    int center_x, center_y;
+    GdkWindow *window;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+    GtkAllocation allocation;
+    GtkAdjustment *vadjustment, *hadjustment;
+
+    g_return_if_fail (EEL_IS_CANVAS (canvas));
+    g_return_if_fail (n > EEL_CANVAS_EPSILON);
+
+    widget = GTK_WIDGET (canvas);
+
+    gtk_widget_get_allocation (widget, &allocation);
+    center_x = allocation.width / 2;
+    center_y = allocation.height / 2;
+
+    /* Find the coordinates of the screen center in units. */
+    hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
+    vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
+    cx = (gtk_adjustment_get_value (hadjustment) + center_x) / canvas->pixels_per_unit + canvas->scroll_x1 + 
canvas->zoom_xofs;
+    cy = (gtk_adjustment_get_value (vadjustment) + center_y) / canvas->pixels_per_unit + canvas->scroll_y1 + 
canvas->zoom_yofs;
+
+    /* Now calculate the new offset of the upper left corner. (round not truncate) */
+    x1 = ((cx - canvas->scroll_x1) * n) - center_x + .5;
+    y1 = ((cy - canvas->scroll_y1) * n) - center_y + .5;
+
+    canvas->pixels_per_unit = n;
+
+    if (!(canvas->root->flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE))
+    {
+        canvas->root->flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
+        eel_canvas_request_update (canvas);
+    }
+
+    /* Map a background None window over the bin_window to avoid
+     * scrolling the window scroll causing exposes.
+     */
+    window = NULL;
+    if (gtk_widget_get_mapped (widget))
+    {
+        attributes.window_type = GDK_WINDOW_CHILD;
+        gtk_widget_get_allocation (widget, &allocation);
+        attributes.x = allocation.x;
+        attributes.y = allocation.y;
+        attributes.width = allocation.width;
+        attributes.height = allocation.height;
+        attributes.wclass = GDK_INPUT_OUTPUT;
+        attributes.visual = gtk_widget_get_visual (widget);
+        attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
+
+        attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+
+        window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                 &attributes, attributes_mask);
+        gdk_window_set_user_data (window, widget);
+
+        gdk_window_show (window);
+    }
+
+    scroll_to (canvas, x1, y1);
+
+    /* If we created a an overlapping background None window, remove it how.
+     *
+     * TODO: We would like to temporarily set the bin_window background to
+     * None to avoid clearing the bin_window to the background, but gdk doesn't
+     * expose enought to let us do this, so we get a flash-effect here. At least
+     * it looks better than scroll + expose.
+     */
+    if (window != NULL)
+    {
+        gdk_window_hide (window);
+        gdk_window_set_user_data (window, NULL);
+        gdk_window_destroy (window);
+    }
+
+    canvas->need_repick = TRUE;
+}
+
+/**
+ * eel_canvas_get_scroll_offsets:
+ * @canvas: A canvas.
+ * @cx: Horizontal scrolling offset (return value).
+ * @cy: Vertical scrolling offset (return value).
+ *
+ * Queries the scrolling offsets of a canvas.  The values are returned in canvas
+ * pixel units.
+ **/
+void
+eel_canvas_get_scroll_offsets (EelCanvas *canvas,
+                               int       *cx,
+                               int       *cy)
+{
+    GtkAdjustment *vadjustment, *hadjustment;
+
+    g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+    hadjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
+    vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
+
+    if (cx)
+    {
+        *cx = gtk_adjustment_get_value (hadjustment);
+    }
+
+    if (cy)
+    {
+        *cy = gtk_adjustment_get_value (vadjustment);
+    }
+}
+
+/* Queues an update of the canvas */
+static void
+eel_canvas_request_update (EelCanvas *canvas)
+{
+    EEL_CANVAS_GET_CLASS (canvas)->request_update (canvas);
+}
+
+static void
+eel_canvas_request_update_real (EelCanvas *canvas)
+{
+    canvas->need_update = TRUE;
+    add_idle (canvas);
+}
+
+/**
+ * eel_canvas_request_redraw:
+ * @canvas: A canvas.
+ * @x1: Leftmost coordinate of the rectangle to be redrawn.
+ * @y1: Upper coordinate of the rectangle to be redrawn.
+ * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
+ * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
+ *
+ * Convenience function that informs a canvas that the specified rectangle needs
+ * to be repainted.  The rectangle includes @x1 and @y1, but not @x2 and @y2.
+ * To be used only by item implementations.
+ **/
+void
+eel_canvas_request_redraw (EelCanvas *canvas,
+                           int        x1,
+                           int        y1,
+                           int        x2,
+                           int        y2)
+{
+    GdkRectangle bbox;
+
+    g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+    if (!gtk_widget_is_drawable (GTK_WIDGET (canvas))
+        || (x1 >= x2) || (y1 >= y2))
+    {
+        return;
+    }
+
+    bbox.x = x1;
+    bbox.y = y1;
+    bbox.width = x2 - x1;
+    bbox.height = y2 - y1;
+
+    gdk_window_invalidate_rect (gtk_layout_get_bin_window (GTK_LAYOUT (canvas)),
+                                &bbox, FALSE);
+}
+
+/**
+ * eel_canvas_w2c:
+ * @canvas: A canvas.
+ * @wx: World X coordinate.
+ * @wy: World Y coordinate.
+ * @cx: X pixel coordinate (return value).
+ * @cy: Y pixel coordinate (return value).
+ *
+ * Converts world coordinates into canvas pixel coordinates.
+ **/
+void
+eel_canvas_w2c (EelCanvas *canvas,
+                double     wx,
+                double     wy,
+                int       *cx,
+                int       *cy)
+{
+    double zoom;
+
+    g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+    zoom = canvas->pixels_per_unit;
+
+    if (cx)
+    {
+        *cx = floor ((wx - canvas->scroll_x1) * zoom + canvas->zoom_xofs + 0.5);
+    }
+    if (cy)
+    {
+        *cy = floor ((wy - canvas->scroll_y1) * zoom + canvas->zoom_yofs + 0.5);
+    }
+}
+
+/**
+ * eel_canvas_w2c:
+ * @canvas: A canvas.
+ * @world: rectangle in world coordinates.
+ * @canvas: rectangle in canvase coordinates.
+ *
+ * Converts rectangles in world coordinates into canvas pixel coordinates.
+ **/
+void
+eel_canvas_w2c_rect_d (EelCanvas *canvas,
+                       double    *x1,
+                       double    *y1,
+                       double    *x2,
+                       double    *y2)
+{
+    eel_canvas_w2c_d (canvas,
+                      *x1, *y1,
+                      x1, y1);
+    eel_canvas_w2c_d (canvas,
+                      *x2, *y2,
+                      x2, y2);
+}
+
+
+
+/**
+ * eel_canvas_w2c_d:
+ * @canvas: A canvas.
+ * @wx: World X coordinate.
+ * @wy: World Y coordinate.
+ * @cx: X pixel coordinate (return value).
+ * @cy: Y pixel coordinate (return value).
+ *
+ * Converts world coordinates into canvas pixel coordinates.  This version
+ * produces coordinates in floating point coordinates, for greater precision.
+ **/
+void
+eel_canvas_w2c_d (EelCanvas *canvas,
+                  double     wx,
+                  double     wy,
+                  double    *cx,
+                  double    *cy)
+{
+    double zoom;
+
+    g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+    zoom = canvas->pixels_per_unit;
+
+    if (cx)
+    {
+        *cx = (wx - canvas->scroll_x1) * zoom + canvas->zoom_xofs;
+    }
+    if (cy)
+    {
+        *cy = (wy - canvas->scroll_y1) * zoom + canvas->zoom_yofs;
+    }
+}
+
+
+/**
+ * eel_canvas_c2w:
+ * @canvas: A canvas.
+ * @cx: Canvas pixel X coordinate.
+ * @cy: Canvas pixel Y coordinate.
+ * @wx: X world coordinate (return value).
+ * @wy: Y world coordinate (return value).
+ *
+ * Converts canvas pixel coordinates to world coordinates.
+ **/
+void
+eel_canvas_c2w (EelCanvas *canvas,
+                int        cx,
+                int        cy,
+                double    *wx,
+                double    *wy)
+{
+    double zoom;
+
+    g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+    zoom = canvas->pixels_per_unit;
+
+    if (wx)
+    {
+        *wx = (cx - canvas->zoom_xofs) / zoom + canvas->scroll_x1;
+    }
+    if (wy)
+    {
+        *wy = (cy - canvas->zoom_yofs) / zoom + canvas->scroll_y1;
+    }
+}
+
+
+/**
+ * eel_canvas_window_to_world:
+ * @canvas: A canvas.
+ * @winx: Window-relative X coordinate.
+ * @winy: Window-relative Y coordinate.
+ * @worldx: X world coordinate (return value).
+ * @worldy: Y world coordinate (return value).
+ *
+ * Converts window-relative coordinates into world coordinates.  You can use
+ * this when you need to convert mouse coordinates into world coordinates, for
+ * example.
+ * Window coordinates are really the same as canvas coordinates now, but this
+ * function is here for backwards compatibility reasons.
+ **/
+void
+eel_canvas_window_to_world (EelCanvas *canvas,
+                            double     winx,
+                            double     winy,
+                            double    *worldx,
+                            double    *worldy)
+{
+    g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+    if (worldx)
+    {
+        *worldx = canvas->scroll_x1 + ((winx - canvas->zoom_xofs)
+                                       / canvas->pixels_per_unit);
+    }
+
+    if (worldy)
+    {
+        *worldy = canvas->scroll_y1 + ((winy - canvas->zoom_yofs)
+                                       / canvas->pixels_per_unit);
+    }
+}
+
+
+/**
+ * eel_canvas_world_to_window:
+ * @canvas: A canvas.
+ * @worldx: World X coordinate.
+ * @worldy: World Y coordinate.
+ * @winx: X window-relative coordinate.
+ * @winy: Y window-relative coordinate.
+ *
+ * Converts world coordinates into window-relative coordinates.
+ * Window coordinates are really the same as canvas coordinates now, but this
+ * function is here for backwards compatibility reasons.
+ **/
+void
+eel_canvas_world_to_window (EelCanvas *canvas,
+                            double     worldx,
+                            double     worldy,
+                            double    *winx,
+                            double    *winy)
+{
+    g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+    if (winx)
+    {
+        *winx = (canvas->pixels_per_unit) * (worldx - canvas->scroll_x1) + canvas->zoom_xofs;
+    }
+
+    if (winy)
+    {
+        *winy = (canvas->pixels_per_unit) * (worldy - canvas->scroll_y1) + canvas->zoom_yofs;
+    }
+}
+
+static gboolean
+boolean_handled_accumulator (GSignalInvocationHint *ihint,
+                             GValue                *return_accu,
+                             const GValue          *handler_return,
+                             gpointer               dummy)
+{
+    gboolean continue_emission;
+    gboolean signal_handled;
+
+    signal_handled = g_value_get_boolean (handler_return);
+    g_value_set_boolean (return_accu, signal_handled);
+    continue_emission = !signal_handled;
+
+    return continue_emission;
+}
+
+static void
+eel_canvas_item_accessible_get_item_extents (EelCanvasItem *item,
+                                             GdkRectangle  *rect)
+{
+    double bx1, bx2, by1, by2;
+    gint scroll_x, scroll_y;
+    gint x1, x2, y1, y2;
+
+    eel_canvas_item_get_bounds (item, &bx1, &by1, &bx2, &by2);
+    eel_canvas_w2c_rect_d (item->canvas, &bx1, &by1, &bx2, &by2);
+    eel_canvas_get_scroll_offsets (item->canvas, &scroll_x, &scroll_y);
+    x1 = floor (bx1 + .5);
+    y1 = floor (by1 + .5);
+    x2 = floor (bx2 + .5);
+    y2 = floor (by2 + .5);
+    rect->x = x1 - scroll_x;
+    rect->y = y1 - scroll_y;
+    rect->width = x2 - x1;
+    rect->height = y2 - y1;
+}
+
+static gboolean
+eel_canvas_item_accessible_is_item_in_window (EelCanvasItem *item,
+                                              GdkRectangle  *rect)
+{
+    GtkWidget *widget;
+    gboolean retval;
+
+    widget = GTK_WIDGET (item->canvas);
+    if (gtk_widget_get_window (widget))
+    {
+        int window_width, window_height;
+
+        gdk_window_get_geometry (gtk_widget_get_window (widget), NULL, NULL,
+                                 &window_width, &window_height);
+        /*
+         * Check whether rectangles intersect
+         */
+        if (rect->x + rect->width < 0 ||
+            rect->y + rect->height < 0 ||
+            rect->x > window_width ||
+            rect->y > window_height)
+        {
+            retval = FALSE;
+        }
+        else
+        {
+            retval = TRUE;
+        }
+    }
+    else
+    {
+        retval = FALSE;
+    }
+    return retval;
+}
+
+
+static void
+eel_canvas_item_accessible_get_extents (AtkComponent *component,
+                                        gint         *x,
+                                        gint         *y,
+                                        gint         *width,
+                                        gint         *height,
+                                        AtkCoordType  coord_type)
+{
+    AtkGObjectAccessible *atk_gobj;
+    GObject *obj;
+    EelCanvasItem *item;
+    gint window_x, window_y;
+    gint toplevel_x, toplevel_y;
+    GdkRectangle rect;
+    GdkWindow *window;
+    GtkWidget *canvas;
+
+    atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+    obj = atk_gobject_accessible_get_object (atk_gobj);
+
+    if (obj == NULL)
+    {
+        /* item is defunct */
+        return;
+    }
+
+    /* Get the CanvasItem */
+    item = EEL_CANVAS_ITEM (obj);
+
+    /* If this item has no parent canvas, something's broken */
+    g_return_if_fail (GTK_IS_WIDGET (item->canvas));
+
+    eel_canvas_item_accessible_get_item_extents (item, &rect);
+    *width = rect.width;
+    *height = rect.height;
+    if (!eel_canvas_item_accessible_is_item_in_window (item, &rect))
+    {
+        *x = G_MININT;
+        *y = G_MININT;
+        return;
+    }
+
+    canvas = GTK_WIDGET (item->canvas);
+    window = gtk_widget_get_parent_window (canvas);
+    gdk_window_get_origin (window, &window_x, &window_y);
+    *x = rect.x + window_x;
+    *y = rect.y + window_y;
+    if (coord_type == ATK_XY_WINDOW)
+    {
+        window = gdk_window_get_toplevel (gtk_widget_get_window (canvas));
+        gdk_window_get_origin (window, &toplevel_x, &toplevel_y);
+        *x -= toplevel_x;
+        *y -= toplevel_y;
+    }
+    return;
+}
+
+static gint
+eel_canvas_item_accessible_get_mdi_zorder (AtkComponent *component)
+{
+    AtkGObjectAccessible *atk_gobj;
+    GObject *g_obj;
+    EelCanvasItem *item;
+
+    atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+    g_obj = atk_gobject_accessible_get_object (atk_gobj);
+    if (g_obj == NULL)
+    {
+        /* Object is defunct */
+        return -1;
+    }
+
+    item = EEL_CANVAS_ITEM (g_obj);
+    if (item->parent)
+    {
+        return g_list_index (EEL_CANVAS_GROUP (item->parent)->item_list, item);
+    }
+    else
+    {
+        g_return_val_if_fail (item->canvas->root == item, -1);
+        return 0;
+    }
+}
+
+static gboolean
+eel_canvas_item_accessible_grab_focus (AtkComponent *component)
+{
+    AtkGObjectAccessible *atk_gobj;
+    GObject *obj;
+    EelCanvasItem *item;
+    GtkWidget *toplevel;
+
+    atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+    obj = atk_gobject_accessible_get_object (atk_gobj);
+
+    item = EEL_CANVAS_ITEM (obj);
+    if (item == NULL)
+    {
+        /* item is defunct */
+        return FALSE;
+    }
+
+    eel_canvas_item_grab_focus (item);
+    toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->canvas));
+    if (gtk_widget_is_toplevel (toplevel))
+    {
+        gtk_window_present (GTK_WINDOW (toplevel));
+    }
+
+    return TRUE;
+}
+
+static void
+eel_canvas_item_accessible_component_interface_init (AtkComponentIface *iface)
+{
+    g_return_if_fail (iface != NULL);
+
+    iface->get_extents = eel_canvas_item_accessible_get_extents;
+    iface->get_mdi_zorder = eel_canvas_item_accessible_get_mdi_zorder;
+    iface->grab_focus = eel_canvas_item_accessible_grab_focus;
+}
+
+static gboolean
+eel_canvas_item_accessible_is_item_on_screen (EelCanvasItem *item)
+{
+    GdkRectangle rect;
+
+    eel_canvas_item_accessible_get_item_extents (item, &rect);
+    return eel_canvas_item_accessible_is_item_in_window (item, &rect);
+}
+
+static void
+eel_canvas_item_accessible_initialize (AtkObject *obj,
+                                       gpointer   data)
+{
+    if (ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize != NULL)
+    {
+        ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize (obj, data);
+    }
+    g_object_set_data (G_OBJECT (obj), "atk-component-layer",
+                       GINT_TO_POINTER (ATK_LAYER_MDI));
+}
+
+static AtkStateSet *
+eel_canvas_item_accessible_ref_state_set (AtkObject *accessible)
+{
+    AtkGObjectAccessible *atk_gobj;
+    GObject *obj;
+    EelCanvasItem *item;
+    AtkStateSet *state_set;
+
+    state_set = ATK_OBJECT_CLASS (accessible_item_parent_class)->ref_state_set (accessible);
+    atk_gobj = ATK_GOBJECT_ACCESSIBLE (accessible);
+    obj = atk_gobject_accessible_get_object (atk_gobj);
+
+    item = EEL_CANVAS_ITEM (obj);
+    if (item == NULL)
+    {
+        atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
+    }
+    else
+    {
+        if (item->flags & EEL_CANVAS_ITEM_VISIBLE)
+        {
+            atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
+
+            if (eel_canvas_item_accessible_is_item_on_screen (item))
+            {
+                atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
+            }
+        }
+        if (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)))
+        {
+            atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
+
+            if (item->canvas->focused_item == item)
+            {
+                atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
+            }
+        }
+    }
+
+    return state_set;
+}
+
+static void
+eel_canvas_item_accessible_class_init (EelCanvasItemAccessibleClass *klass)
+{
+    AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
+
+    accessible_item_parent_class = g_type_class_peek_parent (klass);
+
+    atk_class->initialize = eel_canvas_item_accessible_initialize;
+    atk_class->ref_state_set = eel_canvas_item_accessible_ref_state_set;
+}
+
+static void
+eel_canvas_item_accessible_init (EelCanvasItemAccessible *self)
+{
+}
+
+G_DEFINE_TYPE_WITH_CODE (EelCanvasItemAccessible,
+                         eel_canvas_item_accessible,
+                         ATK_TYPE_GOBJECT_ACCESSIBLE,
+                         G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT,
+                                                eel_canvas_item_accessible_component_interface_init));
+
+static GType eel_canvas_item_accessible_factory_get_type (void);
+
+typedef AtkObjectFactory EelCanvasItemAccessibleFactory;
+typedef AtkObjectFactoryClass EelCanvasItemAccessibleFactoryClass;
+G_DEFINE_TYPE (EelCanvasItemAccessibleFactory, eel_canvas_item_accessible_factory,
+               ATK_TYPE_OBJECT_FACTORY)
+
+static GType
+eel_canvas_item_accessible_factory_get_accessible_type (void)
+{
+    return eel_canvas_item_accessible_get_type ();
+}
+
+static AtkObject *
+eel_canvas_item_accessible_factory_create_accessible (GObject *for_object)
+{
+    AtkObject *accessible;
+
+    accessible = g_object_new (eel_canvas_item_accessible_get_type (), NULL);
+    atk_object_initialize (accessible, for_object);
+    return accessible;
+}
+
+static void
+eel_canvas_item_accessible_factory_init (EelCanvasItemAccessibleFactory *self)
+{
+}
+
+static void
+eel_canvas_item_accessible_factory_class_init (AtkObjectFactoryClass *klass)
+{
+    klass->create_accessible = eel_canvas_item_accessible_factory_create_accessible;
+    klass->get_accessible_type = eel_canvas_item_accessible_factory_get_accessible_type;
+}
+
+/* Class initialization function for EelCanvasItemClass */
+static void
+eel_canvas_item_class_init (EelCanvasItemClass *klass)
+{
+    GObjectClass *gobject_class = (GObjectClass *) klass;
+
+    item_parent_class = g_type_class_peek_parent (klass);
+
+    gobject_class->set_property = eel_canvas_item_set_property;
+    gobject_class->get_property = eel_canvas_item_get_property;
+    gobject_class->dispose = eel_canvas_item_dispose;
+
+    g_object_class_install_property
+        (gobject_class, ITEM_PROP_VISIBLE,
+        g_param_spec_boolean ("visible", NULL, NULL,
+                              TRUE,
+                              G_PARAM_READWRITE));
+
+    item_signals[ITEM_EVENT] =
+        g_signal_new ("event",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (EelCanvasItemClass, event),
+                      boolean_handled_accumulator, NULL,
+                      g_cclosure_marshal_generic,
+                      G_TYPE_BOOLEAN, 1,
+                      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+    item_signals[ITEM_DESTROY] =
+        g_signal_new ("destroy",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+                      G_STRUCT_OFFSET (EelCanvasItemClass, destroy),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+
+    klass->realize = eel_canvas_item_realize;
+    klass->unrealize = eel_canvas_item_unrealize;
+    klass->map = eel_canvas_item_map;
+    klass->unmap = eel_canvas_item_unmap;
+    klass->update = eel_canvas_item_update;
+
+    atk_registry_set_factory_type (atk_get_default_registry (),
+                                   EEL_TYPE_CANVAS_ITEM,
+                                   eel_canvas_item_accessible_factory_get_type ());
+}
diff --git a/eel/eel-canvas.h b/eel/eel-canvas.h
new file mode 100644
index 000000000..f406ca0a4
--- /dev/null
+++ b/eel/eel-canvas.h
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 1997, 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,
+ * see <http://www.gnu.org/licenses/>.
+ */
+/*
+  @NOTATION@
+ */
+/* EelCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * EelCanvas is basically a port of the Tk toolkit's most excellent canvas
+ * widget.  Tk is copyrighted by the Regents of the University of California,
+ * Sun Microsystems, and other parties.
+ *
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Raph Levien <raph gimp org>
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <gtk/gtk-a11y.h>
+#include <gdk/gdk.h>
+#include <stdarg.h>
+
+G_BEGIN_DECLS
+
+
+/* "Small" value used by canvas stuff */
+#define EEL_CANVAS_EPSILON 1e-10
+
+
+/* Macros for building colors that fit in a 32-bit integer.  The values are in
+ * [0, 255].
+ */
+
+#define EEL_CANVAS_COLOR(r, g, b) ((((int) (r) & 0xff) << 24)  \
+                                    | (((int) (g) & 0xff) << 16)       \
+                                    | (((int) (b) & 0xff) << 8)        \
+                                    | 0xff)
+
+#define EEL_CANVAS_COLOR_A(r, g, b, a) ((((int) (r) & 0xff) << 24)     \
+                                         | (((int) (g) & 0xff) << 16)  \
+                                         | (((int) (b) & 0xff) << 8)   \
+                                         | ((int) (a) & 0xff))
+
+
+typedef struct _EelCanvas           EelCanvas;
+typedef struct _EelCanvasClass      EelCanvasClass;
+typedef struct _EelCanvasItem       EelCanvasItem;
+typedef struct _EelCanvasItemClass  EelCanvasItemClass;
+typedef struct _EelCanvasGroup      EelCanvasGroup;
+typedef struct _EelCanvasGroupClass EelCanvasGroupClass;
+
+
+/* EelCanvasItem - base item class for canvas items
+ *
+ * All canvas items are derived from EelCanvasItem.  The only information a
+ * EelCanvasItem contains is its parent canvas, its parent canvas item group,
+ * and its bounding box in world coordinates.
+ *
+ * Items inside a canvas are organized in a tree of EelCanvasItemGroup nodes
+ * and EelCanvasItem leaves.  Each canvas has a single root group, which can
+ * be obtained with the eel_canvas_get_root() function.
+ *
+ * The abstract EelCanvasItem class does not have any configurable or
+ * queryable attributes.
+ */
+
+/* Object flags for items */
+enum {
+       EEL_CANVAS_ITEM_REALIZED         = 1 << 4,
+       EEL_CANVAS_ITEM_MAPPED           = 1 << 5,
+       EEL_CANVAS_ITEM_ALWAYS_REDRAW    = 1 << 6,
+       EEL_CANVAS_ITEM_VISIBLE          = 1 << 7,
+       EEL_CANVAS_ITEM_NEED_UPDATE      = 1 << 8,
+       EEL_CANVAS_ITEM_NEED_DEEP_UPDATE = 1 << 9
+};
+
+/* Update flags for items */
+enum {
+       EEL_CANVAS_UPDATE_REQUESTED  = 1 << 0,
+       EEL_CANVAS_UPDATE_DEEP       = 1 << 1
+};
+
+#define EEL_TYPE_CANVAS_ITEM            (eel_canvas_item_get_type ())
+#define EEL_CANVAS_ITEM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEL_TYPE_CANVAS_ITEM, 
EelCanvasItem))
+#define EEL_CANVAS_ITEM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EEL_TYPE_CANVAS_ITEM, 
EelCanvasItemClass))
+#define EEL_IS_CANVAS_ITEM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEL_TYPE_CANVAS_ITEM))
+#define EEL_IS_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEL_TYPE_CANVAS_ITEM))
+#define EEL_CANVAS_ITEM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EEL_TYPE_CANVAS_ITEM, 
EelCanvasItemClass))
+
+
+struct _EelCanvasItem {
+       GInitiallyUnowned object;
+
+       /* Parent canvas for this item */
+       EelCanvas *canvas;
+
+       /* Parent canvas group for this item (a EelCanvasGroup) */
+       EelCanvasItem *parent;
+
+       /* Bounding box for this item (in canvas coordinates) */
+       double x1, y1, x2, y2;
+
+       /* Object flags */
+       guint flags;
+};
+
+struct _EelCanvasItemClass {
+       GInitiallyUnownedClass parent_class;
+
+       void (* destroy) (EelCanvasItem *item);
+
+       /* Tell the item to update itself.  The flags are from the update flags
+        * defined above.  The item should update its internal state from its
+        * queued state, and recompute and request its repaint area. The
+        * update method also recomputes the bounding box of the item.
+        */
+       void (* update) (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags);
+
+       /* Realize an item -- create GCs, etc. */
+       void (* realize) (EelCanvasItem *item);
+
+       /* Unrealize an item */
+       void (* unrealize) (EelCanvasItem *item);
+
+       /* Map an item - normally only need by items with their own GdkWindows */
+       void (* map) (EelCanvasItem *item);
+
+       /* Unmap an item */
+       void (* unmap) (EelCanvasItem *item);
+
+       /* Draw an item of this type.  (x, y) are the upper-left canvas pixel
+        * coordinates of the drawable, a temporary pixmap, where things get
+        * drawn.  (width, height) are the dimensions of the drawable.
+        */
+       void (* draw) (EelCanvasItem *item, cairo_t *cr, cairo_region_t *region);
+
+       /* Calculate the distance from an item to the specified point.  It also
+         * returns a canvas item which is the item itself in the case of the
+         * object being an actual leaf item, or a child in case of the object
+         * being a canvas group.  (cx, cy) are the canvas pixel coordinates that
+         * correspond to the item-relative coordinates (x, y).
+        */
+       double (* point) (EelCanvasItem *item, double x, double y, int cx, int cy,
+                         EelCanvasItem **actual_item);
+
+       void (* translate) (EelCanvasItem *item, double dx, double dy);
+       
+       /* Fetch the item's bounding box (need not be exactly tight).  This
+        * should be in item-relative coordinates.
+        */
+       void (* bounds) (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+       /* Signal: an event ocurred for an item of this type.  The (x, y)
+        * coordinates are in the canvas world coordinate system.
+        */
+       gboolean (* event)                (EelCanvasItem *item, GdkEvent *event);
+
+       /* Reserved for future expansion */
+       gpointer spare_vmethods [4];
+};
+
+
+/* Standard Gtk function */
+GType eel_canvas_item_get_type (void) G_GNUC_CONST;
+
+/* Create a canvas item using the standard Gtk argument mechanism.  The item is
+ * automatically inserted at the top of the specified canvas group.  The last
+ * argument must be a NULL pointer.
+ */
+EelCanvasItem *eel_canvas_item_new (EelCanvasGroup *parent, GType type,
+                                   const gchar *first_arg_name, ...);
+
+void eel_canvas_item_destroy (EelCanvasItem *item);
+
+/* Configure an item using the standard Gtk argument mechanism.  The last
+ * argument must be a NULL pointer.
+ */
+void eel_canvas_item_set (EelCanvasItem *item, const gchar *first_arg_name, ...);
+
+/* Move an item by the specified amount */
+void eel_canvas_item_move (EelCanvasItem *item, double dx, double dy);
+
+/* Raise an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void eel_canvas_item_raise (EelCanvasItem *item, int positions);
+
+/* Lower an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void eel_canvas_item_lower (EelCanvasItem *item, int positions);
+
+/* Raise an item to the top of its parent group's z-order. */
+void eel_canvas_item_raise_to_top (EelCanvasItem *item);
+
+/* Lower an item to the bottom of its parent group's z-order */
+void eel_canvas_item_lower_to_bottom (EelCanvasItem *item);
+
+/* Send an item behind another item */
+void eel_canvas_item_send_behind (EelCanvasItem *item,
+                                 EelCanvasItem *behind_item);
+
+
+/* Show an item (make it visible).  If the item is already shown, it has no
+ * effect.
+ */
+void eel_canvas_item_show (EelCanvasItem *item);
+
+/* Hide an item (make it invisible).  If the item is already invisible, it has
+ * no effect.
+ */
+void eel_canvas_item_hide (EelCanvasItem *item);
+
+/* Grab the seat for the specified item.  Only the events in event_mask will be
+ * reported. If cursor is non-NULL, it will be used during the duration of the
+ * grab. event is the event, triggering the grab. Returns the same values as gdk_seat_grab().
+ */
+GdkGrabStatus eel_canvas_item_grab (EelCanvasItem *item,
+                                   GdkEventMask event_mask,
+                                   GdkCursor *cursor,
+                                   const GdkEvent* event);
+
+/* Ungrabs the seat -- the specified item must be the same that was passed to
+ * eel_canvas_item_grab().
+ */
+void eel_canvas_item_ungrab (EelCanvasItem *item);
+
+/* These functions convert from a coordinate system to another.  "w" is world
+ * coordinates and "i" is item coordinates.
+ */
+void eel_canvas_item_i2w (EelCanvasItem *item, double *x, double *y);
+
+/* Fetch the bounding box of the item.  The bounding box may not be exactly
+ * tight, but the canvas items will do the best they can.  The returned bounding
+ * box is in the coordinate system of the item's parent.
+ */
+void eel_canvas_item_get_bounds (EelCanvasItem *item,
+                                double *x1, double *y1, double *x2, double *y2);
+
+/* Request that the update method eventually get called.  This should be used
+ * only by item implementations.
+ */
+void eel_canvas_item_request_update (EelCanvasItem *item);
+
+/* Request a redraw of the bounding box of the canvas item */
+void eel_canvas_item_request_redraw (EelCanvasItem *item);
+
+/* EelCanvasGroup - a group of canvas items
+ *
+ * A group is a node in the hierarchical tree of groups/items inside a canvas.
+ * Groups serve to give a logical structure to the items.
+ *
+ * Consider a circuit editor application that uses the canvas for its schematic
+ * display.  Hierarchically, there would be canvas groups that contain all the
+ * components needed for an "adder", for example -- this includes some logic
+ * gates as well as wires.  You can move stuff around in a convenient way by
+ * doing a eel_canvas_item_move() of the hierarchical groups -- to move an
+ * adder, simply move the group that represents the adder.
+ *
+ * The following arguments are available:
+ *
+ * name                type            read/write      description
+ * --------------------------------------------------------------------------------
+ * x           double          RW              X coordinate of group's origin
+ * y           double          RW              Y coordinate of group's origin
+ */
+
+
+#define EEL_TYPE_CANVAS_GROUP            (eel_canvas_group_get_type ())
+#define EEL_CANVAS_GROUP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEL_TYPE_CANVAS_GROUP, 
EelCanvasGroup))
+#define EEL_CANVAS_GROUP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EEL_TYPE_CANVAS_GROUP, 
EelCanvasGroupClass))
+#define EEL_IS_CANVAS_GROUP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEL_TYPE_CANVAS_GROUP))
+#define EEL_IS_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEL_TYPE_CANVAS_GROUP))
+#define EEL_CANVAS_GROUP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EEL_TYPE_CANVAS_GROUP, 
EelCanvasGroupClass))
+
+
+struct _EelCanvasGroup {
+       EelCanvasItem item;
+
+       double xpos, ypos;
+       
+       /* Children of the group */
+       GList *item_list;
+       GList *item_list_end;
+};
+
+struct _EelCanvasGroupClass {
+       EelCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType eel_canvas_group_get_type (void) G_GNUC_CONST;
+
+
+/*** EelCanvas ***/
+
+
+#define EEL_TYPE_CANVAS            (eel_canvas_get_type ())
+#define EEL_CANVAS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEL_TYPE_CANVAS, EelCanvas))
+#define EEL_CANVAS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EEL_TYPE_CANVAS, EelCanvasClass))
+#define EEL_IS_CANVAS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEL_TYPE_CANVAS))
+#define EEL_IS_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEL_TYPE_CANVAS))
+#define EEL_CANVAS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EEL_TYPE_CANVAS, EelCanvasClass))
+
+
+struct _EelCanvas {
+       GtkLayout layout;
+
+       /* Root canvas group */
+       EelCanvasItem *root;
+
+       /* The item containing the mouse pointer, or NULL if none */
+       EelCanvasItem *current_item;
+
+       /* Item that is about to become current (used to track deletions and such) */
+       EelCanvasItem *new_current_item;
+
+       /* Item that holds a pointer grab, or NULL if none */
+       EelCanvasItem *grabbed_item;
+
+       /* If non-NULL, the currently focused item */
+       EelCanvasItem *focused_item;
+
+       /* Event on which selection of current item is based */
+       GdkEvent pick_event;
+
+       /* Scrolling region */
+       double scroll_x1, scroll_y1;
+       double scroll_x2, scroll_y2;
+
+       /* Scaling factor to be used for display */
+       double pixels_per_unit;
+
+       /* Idle handler ID */
+       guint idle_id;
+
+       /* Signal handler ID for destruction of the root item */
+       gulong root_destroy_id;
+
+       /* Internal pixel offsets when zoomed out */
+       int zoom_xofs, zoom_yofs;
+
+       /* Last known modifier state, for deferred repick when a button is down */
+       int state;
+
+       /* Event mask specified when grabbing an item */
+       guint grabbed_event_mask;
+
+       /* Tolerance distance for picking items */
+       int close_enough;
+
+       /* Whether the canvas should center the canvas in the middle of
+        * the window if the scroll region is smaller than the window */
+       unsigned int center_scroll_region : 1;
+
+       /* Whether items need update at next idle loop iteration */
+       unsigned int need_update : 1;
+
+       /* Are we in the midst of an update */
+       unsigned int doing_update : 1;
+
+       /* Whether the canvas needs redrawing at the next idle loop iteration */
+       unsigned int need_redraw : 1;
+
+       /* Whether current item will be repicked at next idle loop iteration */
+       unsigned int need_repick : 1;
+
+       /* For use by internal pick_current_item() function */
+       unsigned int left_grabbed_item : 1;
+
+       /* For use by internal pick_current_item() function */
+       unsigned int in_repick : 1;
+};
+
+struct _EelCanvasClass {
+       GtkLayoutClass parent_class;
+
+       /* Private Virtual methods for groping the canvas inside bonobo */
+       void (* request_update) (EelCanvas *canvas);
+
+       /* Reserved for future expansion */
+       gpointer spare_vmethods [4];
+};
+
+
+/* Standard Gtk function */
+GType eel_canvas_get_type (void) G_GNUC_CONST;
+
+/* Creates a new canvas.  You should check that the canvas is created with the
+ * proper visual and colormap.  Any visual will do unless you intend to insert
+ * gdk_imlib images into it, in which case you should use the gdk_imlib visual.
+ *
+ * You should call eel_canvas_set_scroll_region() soon after calling this
+ * function to set the desired scrolling limits for the canvas.
+ */
+GtkWidget *eel_canvas_new (void);
+
+/* Returns the root canvas item group of the canvas */
+EelCanvasGroup *eel_canvas_root (EelCanvas *canvas);
+
+/* Sets the limits of the scrolling region, in world coordinates */
+void eel_canvas_set_scroll_region (EelCanvas *canvas,
+                                  double x1, double y1, double x2, double y2);
+
+/* Gets the limits of the scrolling region, in world coordinates */
+void eel_canvas_get_scroll_region (EelCanvas *canvas,
+                                  double *x1, double *y1, double *x2, double *y2);
+
+/* Sets the number of pixels that correspond to one unit in world coordinates */
+void eel_canvas_set_pixels_per_unit (EelCanvas *canvas, double n);
+
+/* Returns the scroll offsets of the canvas in canvas pixel coordinates.  You
+ * can specify NULL for any of the values, in which case that value will not be
+ * queried.
+ */
+void eel_canvas_get_scroll_offsets (EelCanvas *canvas, int *cx, int *cy);
+
+/* For use only by item type implementations.  Request that the canvas
+ * eventually redraw the specified region, specified in canvas pixel
+ * coordinates.  The region contains (x1, y1) but not (x2, y2).
+ */
+void eel_canvas_request_redraw (EelCanvas *canvas, int x1, int y1, int x2, int y2);
+
+/* These functions convert from a coordinate system to another.  "w" is world
+ * coordinates, "c" is canvas pixel coordinates (pixel coordinates that are
+ * (0,0) for the upper-left scrolling limit and something else for the
+ * lower-left scrolling limit).
+ */
+void eel_canvas_w2c_rect_d (EelCanvas *canvas,
+                           double *x1, double *y1,
+                           double *x2, double *y2);
+void eel_canvas_w2c (EelCanvas *canvas, double wx, double wy, int *cx, int *cy);
+void eel_canvas_w2c_d (EelCanvas *canvas, double wx, double wy, double *cx, double *cy);
+void eel_canvas_c2w (EelCanvas *canvas, int cx, int cy, double *wx, double *wy);
+
+/* This function takes in coordinates relative to the GTK_LAYOUT
+ * (canvas)->bin_window and converts them to world coordinates.
+ * These days canvas coordinates and window coordinates are the same, but
+ * these are left for backwards compat reasons.
+ */
+void eel_canvas_window_to_world (EelCanvas *canvas,
+                                double winx, double winy, double *worldx, double *worldy);
+
+/* This is the inverse of eel_canvas_window_to_world() */
+void eel_canvas_world_to_window (EelCanvas *canvas,
+                                double worldx, double worldy, double *winx, double *winy);
+
+/* Accessible implementation */
+GType eel_canvas_accessible_get_type (void);
+
+typedef struct _EelCanvasAccessible EelCanvasAccessible;
+struct _EelCanvasAccessible
+{
+       GtkContainerAccessible parent;
+};
+
+typedef struct _EelCanvasAccessibleClass EelCanvasAccessibleClass;
+struct _EelCanvasAccessibleClass
+{
+       GtkContainerAccessibleClass parent_class;
+};
+
+GType eel_canvas_item_accessible_get_type (void);
+
+typedef struct _EelCanvasItemAccessible EelCanvasItemAccessible;
+struct _EelCanvasItemAccessible
+{
+       GtkAccessible parent;
+};
+
+typedef struct _EelCanvasItemAccessibleClass EelCanvasItemAccessibleClass;
+struct _EelCanvasItemAccessibleClass
+{
+       GtkAccessibleClass parent_class;
+};
+
+G_END_DECLS
\ No newline at end of file
diff --git a/eel/meson.build b/eel/meson.build
index 8f88a9bad..e5fa7465d 100644
--- a/eel/meson.build
+++ b/eel/meson.build
@@ -1,6 +1,8 @@
 libeel_2_sources = [
   'eel-art-extensions.h',
   'eel-art-extensions.c',
+  'eel-canvas.h',
+  'eel-canvas.c',
   'eel-debug.h',
   'eel-debug.c',
   'eel-glib-extensions.h',
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7c1b6a31b..6b7fdf593 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -4,6 +4,7 @@ data/nautilus-autorun-software.desktop.in
 data/org.gnome.Nautilus.appdata.xml.in.in
 data/org.gnome.Nautilus.desktop.in.in
 data/org.gnome.nautilus.gschema.xml
+eel/eel-canvas.c
 eel/eel-gtk-extensions.c
 eel/eel-stock-dialogs.c
 eel/eel-vfs-extensions.c
@@ -22,6 +23,11 @@ src/nautilus-autorun-software.c
 src/nautilus-batch-rename-dialog.c
 src/nautilus-batch-rename-dialog.h
 src/nautilus-bookmark.c
+src/nautilus-canvas-container.c
+src/nautilus-canvas-dnd.c
+src/nautilus-canvas-item.c
+src/nautilus-canvas-view.c
+src/nautilus-canvas-view-container.c
 src/nautilus-clipboard.c
 src/nautilus-column-chooser.c
 src/nautilus-column-utilities.c
diff --git a/src/meson.build b/src/meson.build
index 20865fff4..dac467d54 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -72,6 +72,10 @@ libnautilus_sources = [
   'nautilus-application.h',
   'nautilus-bookmark-list.c',
   'nautilus-bookmark-list.h',
+  'nautilus-canvas-view.c',
+  'nautilus-canvas-view.h',
+  'nautilus-canvas-view-container.c',
+  'nautilus-canvas-view-container.h',
   'nautilus-dbus-manager.c',
   'nautilus-dbus-manager.h',
   'nautilus-error-reporting.c',
@@ -143,6 +147,13 @@ libnautilus_sources = [
   'nautilus-x-content-bar.h',
   'nautilus-bookmark.c',
   'nautilus-bookmark.h',
+  'nautilus-canvas-container.c',
+  'nautilus-canvas-container.h',
+  'nautilus-canvas-dnd.c',
+  'nautilus-canvas-dnd.h',
+  'nautilus-canvas-item.c',
+  'nautilus-canvas-item.h',
+  'nautilus-canvas-private.h',
   'nautilus-clipboard.c',
   'nautilus-clipboard.h',
   'nautilus-column-chooser.c',
@@ -223,6 +234,8 @@ libnautilus_sources = [
   'nautilus-search-engine-simple.h',
   'nautilus-search-hit.c',
   'nautilus-search-hit.h',
+  'nautilus-selection-canvas-item.c',
+  'nautilus-selection-canvas-item.h',
   'nautilus-signaller.h',
   'nautilus-signaller.c',
   'nautilus-query.c',
diff --git a/src/nautilus-canvas-container.c b/src/nautilus-canvas-container.c
new file mode 100644
index 000000000..44b637166
--- /dev/null
+++ b/src/nautilus-canvas-container.c
@@ -0,0 +1,6360 @@
+/* nautilus-canvas-container.c - Canvas container widget.
+ *
+ *  Copyright (C) 1999, 2000 Free Software Foundation
+ *  Copyright (C) 2000, 2001 Eazel, Inc.
+ *  Copyright (C) 2002, 2003 Red Hat, Inc.
+ *
+ *  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,
+ *  see <http://www.gnu.org/licenses/>.
+ *
+ *  Authors: Ettore Perazzoli <ettore gnu org>,
+ *  Darin Adler <darin bentspoon com>
+ */
+
+#include "nautilus-canvas-container.h"
+
+#include <atk/atkaction.h>
+#include <eel/eel-art-extensions.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-vfs-extensions.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#define DEBUG_FLAG NAUTILUS_DEBUG_CANVAS_CONTAINER
+#include "nautilus-debug.h"
+
+#include "nautilus-canvas-private.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-icon-info.h"
+#include "nautilus-lib-self-check-functions.h"
+#include "nautilus-selection-canvas-item.h"
+
+/* Interval for updating the rubberband selection, in milliseconds.  */
+#define RUBBERBAND_TIMEOUT_INTERVAL 10
+
+#define RUBBERBAND_SCROLL_THRESHOLD 5
+
+/* Initial unpositioned icon value */
+#define ICON_UNPOSITIONED_VALUE -1
+
+/* Timeout for making the icon currently selected for keyboard operation visible.
+ * If this is 0, you can get into trouble with extra scrolling after holding
+ * down the arrow key for awhile when there are many items.
+ */
+#define KEYBOARD_ICON_REVEAL_TIMEOUT 10
+
+#define CONTEXT_MENU_TIMEOUT_INTERVAL 500
+
+/* Maximum amount of milliseconds the mouse button is allowed to stay down
+ * and still be considered a click.
+ */
+#define MAX_CLICK_TIME 1500
+
+/* Button assignments. */
+#define DRAG_BUTTON 1
+#define RUBBERBAND_BUTTON 1
+#define MIDDLE_BUTTON 2
+#define CONTEXTUAL_MENU_BUTTON 3
+#define DRAG_MENU_BUTTON 2
+
+/* Maximum size (pixels) allowed for icons at the standard zoom level. */
+#define MINIMUM_IMAGE_SIZE 24
+#define MAXIMUM_IMAGE_SIZE 96
+
+#define ICON_PAD_LEFT 4
+#define ICON_PAD_RIGHT 4
+#define ICON_PAD_TOP 4
+#define ICON_PAD_BOTTOM 4
+
+#define CONTAINER_PAD_LEFT 4
+#define CONTAINER_PAD_RIGHT 4
+#define CONTAINER_PAD_TOP 4
+#define CONTAINER_PAD_BOTTOM 4
+
+/* Width of a "grid unit". Canvas items will always take up one or more
+ * grid units, rounding up their size relative to the unit width.
+ * So with an 80px grid unit, a 100px canvas item would take two grid units,
+ * where a 76px canvas item would only take one.
+ * Canvas items are then centered in the extra available space.
+ * Keep in sync with MAX_TEXT_WIDTH at nautilus-canvas-item.
+ */
+#define SMALL_ICON_GRID_WIDTH 124
+#define STANDARD_ICON_GRID_WIDTH 112
+#define LARGE_ICON_GRID_WIDTH 106
+#define LARGER_ICON_GRID_WIDTH 128
+
+/* Copied from NautilusCanvasContainer */
+#define NAUTILUS_CANVAS_CONTAINER_SEARCH_DIALOG_TIMEOUT 5
+
+/* Copied from NautilusFile */
+#define UNDEFINED_TIME ((time_t) (-1))
+
+enum
+{
+    ACTION_ACTIVATE,
+    ACTION_MENU,
+    LAST_ACTION
+};
+
+typedef struct
+{
+    GList *selection;
+    char *action_descriptions[LAST_ACTION];
+} NautilusCanvasContainerAccessiblePrivate;
+
+static GType         nautilus_canvas_container_accessible_get_type (void);
+static void          preview_selected_items (NautilusCanvasContainer *container);
+static void          activate_selected_items (NautilusCanvasContainer *container);
+static void          activate_selected_items_alternate (NautilusCanvasContainer *container,
+                                                        NautilusCanvasIcon      *icon);
+static NautilusCanvasIcon *get_first_selected_icon (NautilusCanvasContainer *container);
+static NautilusCanvasIcon *get_nth_selected_icon (NautilusCanvasContainer *container,
+                                                  int                      index);
+static gboolean      has_multiple_selection (NautilusCanvasContainer *container);
+static gboolean      all_selected (NautilusCanvasContainer *container);
+static gboolean      has_selection (NautilusCanvasContainer *container);
+static void          icon_destroy (NautilusCanvasContainer *container,
+                                   NautilusCanvasIcon      *icon);
+static gboolean      finish_adding_new_icons (NautilusCanvasContainer *container);
+static inline void   icon_get_bounding_box (NautilusCanvasIcon           *icon,
+                                            int                          *x1_return,
+                                            int                          *y1_return,
+                                            int                          *x2_return,
+                                            int                          *y2_return,
+                                            NautilusCanvasItemBoundsUsage usage);
+static void          handle_hadjustment_changed (GtkAdjustment           *adjustment,
+                                                 NautilusCanvasContainer *container);
+static void          handle_vadjustment_changed (GtkAdjustment           *adjustment,
+                                                 NautilusCanvasContainer *container);
+static GList *nautilus_canvas_container_get_selected_icons (NautilusCanvasContainer *container);
+static void          nautilus_canvas_container_update_visible_icons (NautilusCanvasContainer *container);
+static void          reveal_icon (NautilusCanvasContainer *container,
+                                  NautilusCanvasIcon      *icon);
+
+static void          nautilus_canvas_container_set_rtl_positions (NautilusCanvasContainer *container);
+static double        get_mirror_x_position (NautilusCanvasContainer *container,
+                                            NautilusCanvasIcon      *icon,
+                                            double                   x);
+static void         text_ellipsis_limit_changed_container_callback (gpointer callback_data);
+
+static int compare_icons_horizontal (NautilusCanvasContainer *container,
+                                     NautilusCanvasIcon      *icon_a,
+                                     NautilusCanvasIcon      *icon_b);
+
+static int compare_icons_vertical (NautilusCanvasContainer *container,
+                                   NautilusCanvasIcon      *icon_a,
+                                   NautilusCanvasIcon      *icon_b);
+
+static void schedule_redo_layout (NautilusCanvasContainer *container);
+
+static const char *nautilus_canvas_container_accessible_action_names[] =
+{
+    "activate",
+    "menu",
+    NULL
+};
+
+static const char *nautilus_canvas_container_accessible_action_descriptions[] =
+{
+    "Activate selected items",
+    "Popup context menu",
+    NULL
+};
+
+G_DEFINE_TYPE (NautilusCanvasContainer, nautilus_canvas_container, EEL_TYPE_CANVAS);
+
+/* The NautilusCanvasContainer signals.  */
+enum
+{
+    ACTIVATE,
+    ACTIVATE_ALTERNATE,
+    ACTIVATE_PREVIEWER,
+    BAND_SELECT_STARTED,
+    BAND_SELECT_ENDED,
+    BUTTON_PRESS,
+    CONTEXT_CLICK_BACKGROUND,
+    CONTEXT_CLICK_SELECTION,
+    MIDDLE_CLICK,
+    GET_CONTAINER_URI,
+    GET_ICON_URI,
+    GET_ICON_ACTIVATION_URI,
+    GET_ICON_DROP_TARGET_URI,
+    ICON_RENAME_STARTED,
+    ICON_RENAME_ENDED,
+    ICON_STRETCH_STARTED,
+    ICON_STRETCH_ENDED,
+    MOVE_COPY_ITEMS,
+    HANDLE_NETSCAPE_URL,
+    HANDLE_URI_LIST,
+    HANDLE_TEXT,
+    HANDLE_RAW,
+    HANDLE_HOVER,
+    SELECTION_CHANGED,
+    ICON_ADDED,
+    ICON_REMOVED,
+    CLEARED,
+    LAST_SIGNAL
+};
+
+typedef struct
+{
+    int **icon_grid;
+    int *grid_memory;
+    int num_rows;
+    int num_columns;
+    gboolean tight;
+} PlacementGrid;
+
+static guint signals[LAST_SIGNAL];
+
+/* Functions dealing with NautilusIcons.  */
+
+static void
+icon_free (NautilusCanvasIcon *icon)
+{
+    /* Destroy this icon item; the parent will unref it. */
+    eel_canvas_item_destroy (EEL_CANVAS_ITEM (icon->item));
+    g_free (icon);
+}
+
+static gboolean
+icon_is_positioned (const NautilusCanvasIcon *icon)
+{
+    return icon->x != ICON_UNPOSITIONED_VALUE && icon->y != ICON_UNPOSITIONED_VALUE;
+}
+
+
+/* x, y are the top-left coordinates of the icon. */
+static void
+icon_set_position (NautilusCanvasIcon *icon,
+                   double              x,
+                   double              y)
+{
+    if (icon->x == x && icon->y == y)
+    {
+        return;
+    }
+
+    if (icon->x == ICON_UNPOSITIONED_VALUE)
+    {
+        icon->x = 0;
+    }
+    if (icon->y == ICON_UNPOSITIONED_VALUE)
+    {
+        icon->y = 0;
+    }
+
+    eel_canvas_item_move (EEL_CANVAS_ITEM (icon->item),
+                          x - icon->x,
+                          y - icon->y);
+
+    icon->x = x;
+    icon->y = y;
+}
+
+static guint
+nautilus_canvas_container_get_grid_size_for_zoom_level (NautilusCanvasZoomLevel zoom_level)
+{
+    switch (zoom_level)
+    {
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL:
+        {
+            return SMALL_ICON_GRID_WIDTH;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_STANDARD:
+        {
+            return STANDARD_ICON_GRID_WIDTH;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGE:
+        {
+            return LARGE_ICON_GRID_WIDTH;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER:
+        {
+            return LARGER_ICON_GRID_WIDTH;
+        }
+        break;
+
+        default:
+        {
+            g_return_val_if_reached (STANDARD_ICON_GRID_WIDTH);
+        }
+        break;
+    }
+}
+
+guint
+nautilus_canvas_container_get_icon_size_for_zoom_level (NautilusCanvasZoomLevel zoom_level)
+{
+    switch (zoom_level)
+    {
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL:
+        {
+            return NAUTILUS_CANVAS_ICON_SIZE_SMALL;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_STANDARD:
+        {
+            return NAUTILUS_CANVAS_ICON_SIZE_STANDARD;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGE:
+        {
+            return NAUTILUS_CANVAS_ICON_SIZE_LARGE;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER:
+        {
+            return NAUTILUS_CANVAS_ICON_SIZE_LARGER;
+        }
+        break;
+
+        default:
+        {
+            g_return_val_if_reached (NAUTILUS_CANVAS_ICON_SIZE_STANDARD);
+        }
+        break;
+    }
+}
+
+static void
+icon_get_size (NautilusCanvasContainer *container,
+               NautilusCanvasIcon      *icon,
+               guint                   *size)
+{
+    if (size != NULL)
+    {
+        *size = MAX (nautilus_canvas_container_get_icon_size_for_zoom_level (container->details->zoom_level),
+                     NAUTILUS_CANVAS_ICON_SIZE_SMALL);
+    }
+}
+
+static void
+icon_raise (NautilusCanvasIcon *icon)
+{
+    EelCanvasItem *item, *band;
+
+    item = EEL_CANVAS_ITEM (icon->item);
+    band = NAUTILUS_CANVAS_CONTAINER (item->canvas)->details->rubberband_info.selection_rectangle;
+
+    eel_canvas_item_send_behind (item, band);
+}
+
+static void
+icon_toggle_selected (NautilusCanvasContainer *container,
+                      NautilusCanvasIcon      *icon)
+{
+    icon->is_selected = !icon->is_selected;
+    if (icon->is_selected)
+    {
+        container->details->selection = g_list_prepend (container->details->selection, icon->data);
+        container->details->selection_needs_resort = TRUE;
+    }
+    else
+    {
+        container->details->selection = g_list_remove (container->details->selection, icon->data);
+    }
+
+    eel_canvas_item_set (EEL_CANVAS_ITEM (icon->item),
+                         "highlighted_for_selection", (gboolean) icon->is_selected,
+                         NULL);
+
+    /* Raise each newly-selected icon to the front as it is selected. */
+    if (icon->is_selected)
+    {
+        icon_raise (icon);
+    }
+}
+
+/* Select an icon. Return TRUE if selection has changed. */
+static gboolean
+icon_set_selected (NautilusCanvasContainer *container,
+                   NautilusCanvasIcon      *icon,
+                   gboolean                 select)
+{
+    if (select == icon->is_selected)
+    {
+        return FALSE;
+    }
+
+    icon_toggle_selected (container, icon);
+    g_assert (select == icon->is_selected);
+    return TRUE;
+}
+
+static inline void
+icon_get_bounding_box (NautilusCanvasIcon            *icon,
+                       int                           *x1_return,
+                       int                           *y1_return,
+                       int                           *x2_return,
+                       int                           *y2_return,
+                       NautilusCanvasItemBoundsUsage  usage)
+{
+    double x1, y1, x2, y2;
+
+    if (usage == BOUNDS_USAGE_FOR_DISPLAY)
+    {
+        eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon->item),
+                                    &x1, &y1, &x2, &y2);
+    }
+    else if (usage == BOUNDS_USAGE_FOR_LAYOUT)
+    {
+        nautilus_canvas_item_get_bounds_for_layout (icon->item,
+                                                    &x1, &y1, &x2, &y2);
+    }
+    else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM)
+    {
+        nautilus_canvas_item_get_bounds_for_entire_item (icon->item,
+                                                         &x1, &y1, &x2, &y2);
+    }
+    else
+    {
+        g_assert_not_reached ();
+    }
+
+    if (x1_return != NULL)
+    {
+        *x1_return = x1;
+    }
+
+    if (y1_return != NULL)
+    {
+        *y1_return = y1;
+    }
+
+    if (x2_return != NULL)
+    {
+        *x2_return = x2;
+    }
+
+    if (y2_return != NULL)
+    {
+        *y2_return = y2;
+    }
+}
+
+/* Utility functions for NautilusCanvasContainer.  */
+
+gboolean
+nautilus_canvas_container_scroll (NautilusCanvasContainer *container,
+                                  int                      delta_x,
+                                  int                      delta_y)
+{
+    GtkAdjustment *hadj, *vadj;
+    int old_h_value, old_v_value;
+
+    hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
+    vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
+
+    /* Store the old ajustment values so we can tell if we
+     * ended up actually scrolling. We may not have in a case
+     * where the resulting value got pinned to the adjustment
+     * min or max.
+     */
+    old_h_value = gtk_adjustment_get_value (hadj);
+    old_v_value = gtk_adjustment_get_value (vadj);
+
+    gtk_adjustment_set_value (hadj, gtk_adjustment_get_value (hadj) + delta_x);
+    gtk_adjustment_set_value (vadj, gtk_adjustment_get_value (vadj) + delta_y);
+
+    /* return TRUE if we did scroll */
+    return gtk_adjustment_get_value (hadj) != old_h_value || gtk_adjustment_get_value (vadj) != old_v_value;
+}
+
+static void
+pending_icon_to_reveal_destroy_callback (NautilusCanvasItem      *item,
+                                         NautilusCanvasContainer *container)
+{
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
+    g_assert (container->details->pending_icon_to_reveal != NULL);
+    g_assert (container->details->pending_icon_to_reveal->item == item);
+
+    container->details->pending_icon_to_reveal = NULL;
+}
+
+static NautilusCanvasIcon *
+get_pending_icon_to_reveal (NautilusCanvasContainer *container)
+{
+    return container->details->pending_icon_to_reveal;
+}
+
+static void
+set_pending_icon_to_reveal (NautilusCanvasContainer *container,
+                            NautilusCanvasIcon      *icon)
+{
+    NautilusCanvasIcon *old_icon;
+
+    old_icon = container->details->pending_icon_to_reveal;
+
+    if (icon == old_icon)
+    {
+        return;
+    }
+
+    if (old_icon != NULL)
+    {
+        g_signal_handlers_disconnect_by_func
+            (old_icon->item,
+            G_CALLBACK (pending_icon_to_reveal_destroy_callback),
+            container);
+    }
+
+    if (icon != NULL)
+    {
+        g_signal_connect (icon->item, "destroy",
+                          G_CALLBACK (pending_icon_to_reveal_destroy_callback),
+                          container);
+    }
+
+    container->details->pending_icon_to_reveal = icon;
+}
+
+static void
+item_get_canvas_bounds (EelCanvasItem *item,
+                        EelIRect      *bounds)
+{
+    EelDRect world_rect;
+
+    eel_canvas_item_get_bounds (item,
+                                &world_rect.x0,
+                                &world_rect.y0,
+                                &world_rect.x1,
+                                &world_rect.y1);
+    eel_canvas_item_i2w (item->parent,
+                         &world_rect.x0,
+                         &world_rect.y0);
+    eel_canvas_item_i2w (item->parent,
+                         &world_rect.x1,
+                         &world_rect.y1);
+
+    world_rect.x0 -= ICON_PAD_LEFT + ICON_PAD_RIGHT;
+    world_rect.x1 += ICON_PAD_LEFT + ICON_PAD_RIGHT;
+
+    world_rect.y0 -= ICON_PAD_TOP + ICON_PAD_BOTTOM;
+    world_rect.y1 += ICON_PAD_TOP + ICON_PAD_BOTTOM;
+
+    eel_canvas_w2c (item->canvas,
+                    world_rect.x0,
+                    world_rect.y0,
+                    &bounds->x0,
+                    &bounds->y0);
+    eel_canvas_w2c (item->canvas,
+                    world_rect.x1,
+                    world_rect.y1,
+                    &bounds->x1,
+                    &bounds->y1);
+}
+
+static void
+icon_get_row_and_column_bounds (NautilusCanvasContainer *container,
+                                NautilusCanvasIcon      *icon,
+                                EelIRect                *bounds)
+{
+    GList *p;
+    NautilusCanvasIcon *one_icon;
+    EelIRect one_bounds;
+
+    item_get_canvas_bounds (EEL_CANVAS_ITEM (icon->item), bounds);
+
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        one_icon = p->data;
+
+        if (icon == one_icon)
+        {
+            continue;
+        }
+
+        if (compare_icons_horizontal (container, icon, one_icon) == 0)
+        {
+            item_get_canvas_bounds (EEL_CANVAS_ITEM (one_icon->item), &one_bounds);
+            bounds->x0 = MIN (bounds->x0, one_bounds.x0);
+            bounds->x1 = MAX (bounds->x1, one_bounds.x1);
+        }
+
+        if (compare_icons_vertical (container, icon, one_icon) == 0)
+        {
+            item_get_canvas_bounds (EEL_CANVAS_ITEM (one_icon->item), &one_bounds);
+            bounds->y0 = MIN (bounds->y0, one_bounds.y0);
+            bounds->y1 = MAX (bounds->y1, one_bounds.y1);
+        }
+    }
+}
+
+static void
+reveal_icon (NautilusCanvasContainer *container,
+             NautilusCanvasIcon      *icon)
+{
+    GtkAllocation allocation;
+    GtkAdjustment *hadj, *vadj;
+    EelIRect bounds;
+
+    if (!icon_is_positioned (icon))
+    {
+        set_pending_icon_to_reveal (container, icon);
+        return;
+    }
+
+    set_pending_icon_to_reveal (container, NULL);
+
+    gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
+
+    hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
+    vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
+
+    /* ensure that we reveal the entire row/column */
+    icon_get_row_and_column_bounds (container, icon, &bounds);
+
+    if (bounds.y0 < gtk_adjustment_get_value (vadj))
+    {
+        gtk_adjustment_set_value (vadj, bounds.y0);
+    }
+    else if (bounds.y1 > gtk_adjustment_get_value (vadj) + allocation.height)
+    {
+        gtk_adjustment_set_value
+            (vadj, bounds.y1 - allocation.height);
+    }
+
+    if (bounds.x0 < gtk_adjustment_get_value (hadj))
+    {
+        gtk_adjustment_set_value (hadj, bounds.x0);
+    }
+    else if (bounds.x1 > gtk_adjustment_get_value (hadj) + allocation.width)
+    {
+        gtk_adjustment_set_value
+            (hadj, bounds.x1 - allocation.width);
+    }
+}
+
+static void
+process_pending_icon_to_reveal (NautilusCanvasContainer *container)
+{
+    NautilusCanvasIcon *pending_icon_to_reveal;
+
+    pending_icon_to_reveal = get_pending_icon_to_reveal (container);
+
+    if (pending_icon_to_reveal != NULL)
+    {
+        reveal_icon (container, pending_icon_to_reveal);
+    }
+}
+
+static gboolean
+keyboard_icon_reveal_timeout_callback (gpointer data)
+{
+    NautilusCanvasContainer *container;
+    NautilusCanvasIcon *icon;
+
+    container = NAUTILUS_CANVAS_CONTAINER (data);
+    icon = container->details->keyboard_icon_to_reveal;
+
+    g_assert (icon != NULL);
+
+    /* Only reveal the icon if it's still the keyboard focus or if
+     * it's still selected. Someone originally thought we should
+     * cancel this reveal if the user manages to sneak a direct
+     * scroll in before the timeout fires, but we later realized
+     * this wouldn't actually be an improvement
+     * (see bugzilla.gnome.org 40612).
+     */
+    if (icon == container->details->focus
+        || icon->is_selected)
+    {
+        reveal_icon (container, icon);
+    }
+    container->details->keyboard_icon_reveal_timer_id = 0;
+
+    return FALSE;
+}
+
+static void
+unschedule_keyboard_icon_reveal (NautilusCanvasContainer *container)
+{
+    NautilusCanvasContainerDetails *details;
+
+    details = container->details;
+
+    if (details->keyboard_icon_reveal_timer_id != 0)
+    {
+        g_source_remove (details->keyboard_icon_reveal_timer_id);
+    }
+}
+
+static void
+schedule_keyboard_icon_reveal (NautilusCanvasContainer *container,
+                               NautilusCanvasIcon      *icon)
+{
+    NautilusCanvasContainerDetails *details;
+
+    details = container->details;
+
+    unschedule_keyboard_icon_reveal (container);
+
+    details->keyboard_icon_to_reveal = icon;
+    details->keyboard_icon_reveal_timer_id
+        = g_timeout_add (KEYBOARD_ICON_REVEAL_TIMEOUT,
+                         keyboard_icon_reveal_timeout_callback,
+                         container);
+}
+
+static void inline
+emit_atk_object_notify_focused (NautilusCanvasIcon *icon,
+                                gboolean            focused)
+{
+    AtkObject *atk_object = atk_gobject_accessible_for_object (G_OBJECT (icon->item));
+    atk_object_notify_state_change (atk_object, ATK_STATE_FOCUSED, focused);
+}
+
+static void
+clear_focus (NautilusCanvasContainer *container)
+{
+    if (container->details->focus != NULL)
+    {
+        if (container->details->keyboard_focus)
+        {
+            eel_canvas_item_set (EEL_CANVAS_ITEM (container->details->focus->item),
+                                 "highlighted_as_keyboard_focus", 0,
+                                 NULL);
+        }
+        else
+        {
+            emit_atk_object_notify_focused (container->details->focus, FALSE);
+        }
+    }
+
+    container->details->focus = NULL;
+}
+
+/* Set @icon as the icon currently focused for accessibility. */
+static void
+set_focus (NautilusCanvasContainer *container,
+           NautilusCanvasIcon      *icon,
+           gboolean                 keyboard_focus)
+{
+    g_assert (icon != NULL);
+
+    if (icon == container->details->focus)
+    {
+        return;
+    }
+
+    clear_focus (container);
+
+    container->details->focus = icon;
+    container->details->keyboard_focus = keyboard_focus;
+
+    if (keyboard_focus)
+    {
+        eel_canvas_item_set (EEL_CANVAS_ITEM (container->details->focus->item),
+                             "highlighted_as_keyboard_focus", 1,
+                             NULL);
+    }
+    else
+    {
+        emit_atk_object_notify_focused (container->details->focus, TRUE);
+    }
+}
+
+static void
+set_keyboard_rubberband_start (NautilusCanvasContainer *container,
+                               NautilusCanvasIcon      *icon)
+{
+    container->details->keyboard_rubberband_start = icon;
+}
+
+static void
+clear_keyboard_rubberband_start (NautilusCanvasContainer *container)
+{
+    container->details->keyboard_rubberband_start = NULL;
+}
+
+/* carbon-copy of eel_canvas_group_bounds(), but
+ * for NautilusCanvasContainerItems it returns the
+ * bounds for the “entire item”.
+ */
+static void
+get_icon_bounds_for_canvas_bounds (EelCanvasGroup                *group,
+                                   double                        *x1,
+                                   double                        *y1,
+                                   double                        *x2,
+                                   double                        *y2,
+                                   NautilusCanvasItemBoundsUsage  usage)
+{
+    EelCanvasItem *child;
+    GList *list;
+    double tx1, ty1, tx2, ty2;
+    double minx, miny, maxx, maxy;
+    int set;
+
+    /* Get the bounds of the first visible item */
+
+    child = NULL;     /* Unnecessary but eliminates a warning. */
+
+    set = FALSE;
+
+    for (list = group->item_list; list; list = list->next)
+    {
+        child = list->data;
+
+        if (!NAUTILUS_IS_CANVAS_ITEM (child))
+        {
+            continue;
+        }
+
+        if (child->flags & EEL_CANVAS_ITEM_VISIBLE)
+        {
+            set = TRUE;
+            if (!NAUTILUS_IS_CANVAS_ITEM (child) ||
+                usage == BOUNDS_USAGE_FOR_DISPLAY)
+            {
+                eel_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
+            }
+            else if (usage == BOUNDS_USAGE_FOR_LAYOUT)
+            {
+                nautilus_canvas_item_get_bounds_for_layout (NAUTILUS_CANVAS_ITEM (child),
+                                                            &minx, &miny, &maxx, &maxy);
+            }
+            else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM)
+            {
+                nautilus_canvas_item_get_bounds_for_entire_item (NAUTILUS_CANVAS_ITEM (child),
+                                                                 &minx, &miny, &maxx, &maxy);
+            }
+            else
+            {
+                g_assert_not_reached ();
+            }
+            break;
+        }
+    }
+
+    /* If there were no visible items, return an empty bounding box */
+
+    if (!set)
+    {
+        *x1 = *y1 = *x2 = *y2 = 0.0;
+        return;
+    }
+
+    /* Now we can grow the bounds using the rest of the items */
+
+    list = list->next;
+
+    for (; list; list = list->next)
+    {
+        child = list->data;
+
+        if (!NAUTILUS_IS_CANVAS_ITEM (child))
+        {
+            continue;
+        }
+
+        if (!(child->flags & EEL_CANVAS_ITEM_VISIBLE))
+        {
+            continue;
+        }
+
+        if (!NAUTILUS_IS_CANVAS_ITEM (child) ||
+            usage == BOUNDS_USAGE_FOR_DISPLAY)
+        {
+            eel_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
+        }
+        else if (usage == BOUNDS_USAGE_FOR_LAYOUT)
+        {
+            nautilus_canvas_item_get_bounds_for_layout (NAUTILUS_CANVAS_ITEM (child),
+                                                        &tx1, &ty1, &tx2, &ty2);
+        }
+        else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM)
+        {
+            nautilus_canvas_item_get_bounds_for_entire_item (NAUTILUS_CANVAS_ITEM (child),
+                                                             &tx1, &ty1, &tx2, &ty2);
+        }
+        else
+        {
+            g_assert_not_reached ();
+        }
+
+        if (tx1 < minx)
+        {
+            minx = tx1;
+        }
+
+        if (ty1 < miny)
+        {
+            miny = ty1;
+        }
+
+        if (tx2 > maxx)
+        {
+            maxx = tx2;
+        }
+
+        if (ty2 > maxy)
+        {
+            maxy = ty2;
+        }
+    }
+
+    /* Make the bounds be relative to our parent's coordinate system */
+
+    if (EEL_CANVAS_ITEM (group)->parent)
+    {
+        minx += group->xpos;
+        miny += group->ypos;
+        maxx += group->xpos;
+        maxy += group->ypos;
+    }
+
+    if (x1 != NULL)
+    {
+        *x1 = minx;
+    }
+
+    if (y1 != NULL)
+    {
+        *y1 = miny;
+    }
+
+    if (x2 != NULL)
+    {
+        *x2 = maxx;
+    }
+
+    if (y2 != NULL)
+    {
+        *y2 = maxy;
+    }
+}
+
+static void
+get_all_icon_bounds (NautilusCanvasContainer       *container,
+                     double                        *x1,
+                     double                        *y1,
+                     double                        *x2,
+                     double                        *y2,
+                     NautilusCanvasItemBoundsUsage  usage)
+{
+    /* FIXME bugzilla.gnome.org 42477: Do we have to do something about the rubberband
+     * here? Any other non-icon items?
+     */
+    get_icon_bounds_for_canvas_bounds (EEL_CANVAS_GROUP (EEL_CANVAS (container)->root),
+                                       x1, y1, x2, y2, usage);
+}
+
+void
+nautilus_canvas_container_update_scroll_region (NautilusCanvasContainer *container)
+{
+    double x1, y1, x2, y2;
+    double pixels_per_unit;
+    GtkAdjustment *hadj, *vadj;
+    float step_increment;
+    GtkAllocation allocation;
+
+    pixels_per_unit = EEL_CANVAS (container)->pixels_per_unit;
+
+    get_all_icon_bounds (container, &x1, &y1, &x2, &y2, BOUNDS_USAGE_FOR_ENTIRE_ITEM);
+
+    /* Add border at the "end"of the layout (i.e. after the icons), to
+     * ensure we get some space when scrolled to the end.
+     */
+    y2 += ICON_PAD_BOTTOM + CONTAINER_PAD_BOTTOM;
+
+    /* Auto-layout assumes a 0, 0 scroll origin and at least allocation->width.
+     * Then we lay out to the right or to the left, so
+     * x can be < 0 and > allocation */
+    gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
+    x1 = MIN (x1, 0);
+    x2 = MAX (x2, allocation.width / pixels_per_unit);
+    y1 = 0;
+
+    x2 -= 1;
+    x2 = MAX (x1, x2);
+
+    y2 -= 1;
+    y2 = MAX (y1, y2);
+
+    eel_canvas_set_scroll_region (EEL_CANVAS (container), x1, y1, x2, y2);
+
+    hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
+    vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
+
+    /* Scroll by 1/4 icon each time you click. */
+    step_increment = nautilus_canvas_container_get_icon_size_for_zoom_level
+                         (container->details->zoom_level) / 4;
+    if (gtk_adjustment_get_step_increment (hadj) != step_increment)
+    {
+        gtk_adjustment_set_step_increment (hadj, step_increment);
+    }
+    if (gtk_adjustment_get_step_increment (vadj) != step_increment)
+    {
+        gtk_adjustment_set_step_increment (vadj, step_increment);
+    }
+}
+
+static void
+cache_icon_positions (NautilusCanvasContainer *container)
+{
+    GList *l;
+    gint idx;
+    NautilusCanvasIcon *icon;
+
+    for (l = container->details->icons, idx = 0; l != NULL; l = l->next)
+    {
+        icon = l->data;
+        icon->position = idx++;
+    }
+}
+
+static int
+compare_icons_data (gconstpointer a,
+                    gconstpointer b,
+                    gpointer      canvas_container)
+{
+    NautilusCanvasContainerClass *klass;
+    NautilusCanvasIconData *data_a, *data_b;
+
+    data_a = (NautilusCanvasIconData *) a;
+    data_b = (NautilusCanvasIconData *) b;
+    klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (canvas_container);
+
+    return klass->compare_icons (canvas_container, data_a, data_b);
+}
+
+static int
+compare_icons (gconstpointer a,
+               gconstpointer b,
+               gpointer      canvas_container)
+{
+    NautilusCanvasContainerClass *klass;
+    const NautilusCanvasIcon *icon_a, *icon_b;
+
+    icon_a = a;
+    icon_b = b;
+    klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (canvas_container);
+
+    return klass->compare_icons (canvas_container, icon_a->data, icon_b->data);
+}
+
+static void
+sort_selection (NautilusCanvasContainer *container)
+{
+    container->details->selection = g_list_sort_with_data (container->details->selection,
+                                                           compare_icons_data,
+                                                           container);
+    container->details->selection_needs_resort = FALSE;
+}
+
+static void
+sort_icons (NautilusCanvasContainer  *container,
+            GList                   **icons)
+{
+    NautilusCanvasContainerClass *klass;
+
+    klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
+    g_assert (klass->compare_icons != NULL);
+
+    *icons = g_list_sort_with_data (*icons, compare_icons, container);
+}
+
+static void
+resort (NautilusCanvasContainer *container)
+{
+    sort_icons (container, &container->details->icons);
+    sort_selection (container);
+    cache_icon_positions (container);
+}
+
+typedef struct
+{
+    double width;
+    double height;
+    double x_offset;
+    double y_offset;
+} IconPositions;
+
+static void
+lay_down_one_line (NautilusCanvasContainer *container,
+                   GList                   *line_start,
+                   GList                   *line_end,
+                   double                   y,
+                   double                   max_height,
+                   GArray                  *positions,
+                   gboolean                 whole_text)
+{
+    GList *p;
+    NautilusCanvasIcon *icon;
+    double x, ltr_icon_x, icon_x, y_offset;
+    IconPositions *position;
+    int i;
+    gboolean is_rtl;
+
+    is_rtl = nautilus_canvas_container_is_layout_rtl (container);
+
+    /* Lay out the icons along the baseline. */
+    x = ICON_PAD_LEFT;
+    i = 0;
+    for (p = line_start; p != line_end; p = p->next)
+    {
+        icon = p->data;
+
+        position = &g_array_index (positions, IconPositions, i++);
+        ltr_icon_x = x + position->x_offset;
+        icon_x = is_rtl ? get_mirror_x_position (container, icon, ltr_icon_x) : ltr_icon_x;
+        y_offset = position->y_offset;
+
+        icon_set_position (icon, icon_x, y + y_offset);
+        nautilus_canvas_item_set_entire_text (icon->item, whole_text);
+
+        icon->saved_ltr_x = is_rtl ? ltr_icon_x : icon->x;
+
+        x += position->width;
+    }
+}
+
+static void
+lay_down_icons_horizontal (NautilusCanvasContainer *container,
+                           GList                   *icons,
+                           double                   start_y)
+{
+    GList *p, *line_start;
+    NautilusCanvasIcon *icon;
+    double canvas_width, y;
+    double available_width;
+    GArray *positions;
+    IconPositions *position;
+    EelDRect bounds;
+    EelDRect icon_bounds;
+    double max_height_above, max_height_below;
+    double height_above, height_below;
+    double line_width;
+    double min_grid_width;
+    double grid_width;
+    double num_columns;
+    double icon_width, icon_size;
+    int i;
+    GtkAllocation allocation;
+
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    /* We can't get the right allocation if the size hasn't been allocated yet */
+    g_return_if_fail (container->details->has_been_allocated);
+
+    if (icons == NULL)
+    {
+        return;
+    }
+
+    positions = g_array_new (FALSE, FALSE, sizeof (IconPositions));
+    gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
+
+    /* Lay out icons a line at a time. */
+    canvas_width = CANVAS_WIDTH (container, allocation);
+    min_grid_width = nautilus_canvas_container_get_grid_size_for_zoom_level (container->details->zoom_level);
+    icon_size = nautilus_canvas_container_get_icon_size_for_zoom_level (container->details->zoom_level);
+
+    /* Subtracting 1.0 adds some room for error to prevent the jitter due to
+     * the code not being able to decide how many columns should be there, as
+     * "double" is not perfectly precise and increasing the size of the the
+     * window by one pixel could well make it so that the space taken by the
+     * icons and the padding is actually greater than the canvas with by like
+     * 0.01, causing an entire column to be dropped unnecessarily. This fix is
+     * adapted from Nemo.
+     */
+    available_width = MAX (1.0, canvas_width - ICON_PAD_LEFT - ICON_PAD_RIGHT - 1.0);
+    num_columns = MAX (1.0, floor (available_width / min_grid_width));
+
+    if (g_list_nth (icons, num_columns) != NULL)
+    {
+        grid_width = available_width / num_columns;
+    }
+    else
+    {
+        /* It does not look good when the icons jump around when new columns are
+         * added or removed to the grid while there is only one line. It does
+         * not look good either when the icons do not move at all when the
+         * window is resized.
+         *
+         * To do this, we first compute the maximum extra fraction we can add to
+         * the grid width. Adding this much, however, would simply distribute
+         * the icons evenly, which looks bad when there's a wide window with
+         * only a few icons.
+         *
+         * To fix this, we need to apply a function to the fraction which never
+         * makes it larger and instead makes its growth slow down quickly but
+         * smoothly as the window gets wider and wider. Here's the function used
+         * by this code:
+         *
+         * f(x) = ∜(x + 1) - 1
+         *
+         * The +1 and -1 are there to skip the 0 to 1 part of ∜ where it makes
+         * the number larger.
+         */
+
+        double num_icons = MAX (1.0, g_list_length (icons));
+
+        double used_width = num_icons * min_grid_width;
+        double unused_width = available_width - used_width;
+
+        double max_extra_fraction = (unused_width / num_icons) / min_grid_width;
+        double extra_fraction = pow (max_extra_fraction + 1.0, 1.0 / 4.0) - 1.0;
+
+        grid_width = min_grid_width * (1 + extra_fraction);
+    }
+
+    grid_width = MAX (min_grid_width, grid_width);
+
+    line_width = 0;
+    line_start = icons;
+    y = start_y + CONTAINER_PAD_TOP;
+    i = 0;
+
+    max_height_above = 0;
+    max_height_below = 0;
+    for (p = icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+
+        /* Assume it's only one level hierarchy to avoid costly affine calculations */
+        nautilus_canvas_item_get_bounds_for_layout (icon->item,
+                                                    &bounds.x0, &bounds.y0,
+                                                    &bounds.x1, &bounds.y1);
+
+        /* Normalize the icon width to the grid unit.
+         * Use the icon size for this zoom level too in the calculation, since
+         * the actual bounds might be smaller - e.g. because we have a very
+         * narrow thumbnail.
+         */
+        icon_width = ceil (MAX ((bounds.x1 - bounds.x0), icon_size) / grid_width) * grid_width;
+
+        /* Calculate size above/below baseline */
+        icon_bounds = nautilus_canvas_item_get_icon_rectangle (icon->item);
+        height_above = icon_bounds.y1 - bounds.y0;
+        height_below = bounds.y1 - icon_bounds.y1;
+
+        /* If this icon doesn't fit, it's time to lay out the line that's queued up. */
+        if (line_start != p && line_width + icon_width >= canvas_width)
+        {
+            /* Advance to the baseline. */
+            y += ICON_PAD_TOP + max_height_above;
+
+            lay_down_one_line (container, line_start, p, y, max_height_above, positions, FALSE);
+
+            /* Advance to next line. */
+            y += max_height_below + ICON_PAD_BOTTOM;
+
+            line_width = 0;
+            line_start = p;
+            i = 0;
+
+            max_height_above = height_above;
+            max_height_below = height_below;
+        }
+        else
+        {
+            if (height_above > max_height_above)
+            {
+                max_height_above = height_above;
+            }
+            if (height_below > max_height_below)
+            {
+                max_height_below = height_below;
+            }
+        }
+
+        g_array_set_size (positions, i + 1);
+        position = &g_array_index (positions, IconPositions, i++);
+        position->width = icon_width;
+        position->height = icon_bounds.y1 - icon_bounds.y0;
+
+        position->x_offset = (icon_width - (icon_bounds.x1 - icon_bounds.x0)) / 2;
+        position->y_offset = icon_bounds.y0 - icon_bounds.y1;
+
+        /* Add this icon. */
+        line_width += icon_width;
+    }
+
+    /* Lay down that last line of icons. */
+    if (line_start != NULL)
+    {
+        /* Advance to the baseline. */
+        y += ICON_PAD_TOP + max_height_above;
+
+        lay_down_one_line (container, line_start, NULL, y, max_height_above, positions, FALSE);
+    }
+
+    g_array_free (positions, TRUE);
+}
+
+static double
+get_mirror_x_position (NautilusCanvasContainer *container,
+                       NautilusCanvasIcon      *icon,
+                       double                   x)
+{
+    EelDRect icon_bounds;
+    GtkAllocation allocation;
+
+    gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
+    icon_bounds = nautilus_canvas_item_get_icon_rectangle (icon->item);
+
+    return CANVAS_WIDTH (container, allocation) - x - (icon_bounds.x1 - icon_bounds.x0);
+}
+
+static void
+nautilus_canvas_container_set_rtl_positions (NautilusCanvasContainer *container)
+{
+    GList *l;
+    NautilusCanvasIcon *icon;
+    double x;
+
+    if (!container->details->icons)
+    {
+        return;
+    }
+
+    for (l = container->details->icons; l != NULL; l = l->next)
+    {
+        icon = l->data;
+        x = get_mirror_x_position (container, icon, icon->saved_ltr_x);
+        icon_set_position (icon, x, icon->y);
+    }
+}
+
+static void
+lay_down_icons (NautilusCanvasContainer *container,
+                GList                   *icons,
+                double                   start_y)
+{
+    lay_down_icons_horizontal (container, icons, start_y);
+}
+
+static void
+redo_layout_internal (NautilusCanvasContainer *container)
+{
+    gboolean layout_possible;
+
+    layout_possible = finish_adding_new_icons (container);
+    if (!layout_possible)
+    {
+        schedule_redo_layout (container);
+        return;
+    }
+
+    if (container->details->needs_resort)
+    {
+        resort (container);
+        container->details->needs_resort = FALSE;
+    }
+    lay_down_icons (container, container->details->icons, 0);
+
+    if (nautilus_canvas_container_is_layout_rtl (container))
+    {
+        nautilus_canvas_container_set_rtl_positions (container);
+    }
+
+    nautilus_canvas_container_update_scroll_region (container);
+
+    process_pending_icon_to_reveal (container);
+    nautilus_canvas_container_update_visible_icons (container);
+}
+
+static gboolean
+redo_layout_callback (gpointer callback_data)
+{
+    NautilusCanvasContainer *container;
+
+    container = NAUTILUS_CANVAS_CONTAINER (callback_data);
+    redo_layout_internal (container);
+    container->details->idle_id = 0;
+
+    return FALSE;
+}
+
+static void
+unschedule_redo_layout (NautilusCanvasContainer *container)
+{
+    if (container->details->idle_id != 0)
+    {
+        g_source_remove (container->details->idle_id);
+        container->details->idle_id = 0;
+    }
+}
+
+static void
+schedule_redo_layout (NautilusCanvasContainer *container)
+{
+    if (container->details->idle_id == 0
+        && container->details->has_been_allocated)
+    {
+        container->details->idle_id = g_idle_add
+                                          (redo_layout_callback, container);
+    }
+}
+
+static void
+redo_layout (NautilusCanvasContainer *container)
+{
+    unschedule_redo_layout (container);
+    /* We can't lay out if the size hasn't been allocated yet; wait for it to
+     * be and then we will be called again from size_allocate ()
+     */
+    if (container->details->has_been_allocated)
+    {
+        redo_layout_internal (container);
+    }
+}
+
+/* Container-level icon handling functions.  */
+
+static gboolean
+button_event_modifies_selection (GdkEventButton *event)
+{
+    return (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
+}
+
+/* invalidate the cached label sizes for all the icons */
+static void
+invalidate_label_sizes (NautilusCanvasContainer *container)
+{
+    GList *p;
+    NautilusCanvasIcon *icon;
+
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+
+        nautilus_canvas_item_invalidate_label_size (icon->item);
+    }
+}
+
+static gboolean
+select_range (NautilusCanvasContainer *container,
+              NautilusCanvasIcon      *icon1,
+              NautilusCanvasIcon      *icon2,
+              gboolean                 unselect_outside_range)
+{
+    gboolean selection_changed;
+    GList *p;
+    NautilusCanvasIcon *icon;
+    NautilusCanvasIcon *unmatched_icon;
+    gboolean select;
+
+    selection_changed = FALSE;
+
+    unmatched_icon = NULL;
+    select = FALSE;
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+
+        if (unmatched_icon == NULL)
+        {
+            if (icon == icon1)
+            {
+                unmatched_icon = icon2;
+                select = TRUE;
+            }
+            else if (icon == icon2)
+            {
+                unmatched_icon = icon1;
+                select = TRUE;
+            }
+        }
+
+        if (select || unselect_outside_range)
+        {
+            selection_changed |= icon_set_selected
+                                     (container, icon, select);
+        }
+
+        if (unmatched_icon != NULL && icon == unmatched_icon)
+        {
+            select = FALSE;
+        }
+    }
+    return selection_changed;
+}
+
+
+static gboolean
+select_one_unselect_others (NautilusCanvasContainer *container,
+                            NautilusCanvasIcon      *icon_to_select)
+{
+    gboolean selection_changed;
+    GList *p;
+    NautilusCanvasIcon *icon;
+
+    selection_changed = FALSE;
+
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+
+        selection_changed |= icon_set_selected
+                                 (container, icon, icon == icon_to_select);
+    }
+
+    if (selection_changed && icon_to_select != NULL)
+    {
+        reveal_icon (container, icon_to_select);
+    }
+    return selection_changed;
+}
+
+static gboolean
+unselect_all (NautilusCanvasContainer *container)
+{
+    return select_one_unselect_others (container, NULL);
+}
+
+/* Implementation of rubberband selection.  */
+static void
+rubberband_select (NautilusCanvasContainer *container,
+                   const EelDRect          *current_rect)
+{
+    GList *p;
+    gboolean selection_changed, is_in, canvas_rect_calculated;
+    NautilusCanvasIcon *icon;
+    EelIRect canvas_rect;
+    EelCanvas *canvas;
+
+    selection_changed = FALSE;
+    canvas_rect_calculated = FALSE;
+
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+
+        if (!canvas_rect_calculated)
+        {
+            /* Only do this calculation once, since all the canvas items
+             * we are interating are in the same coordinate space
+             */
+            canvas = EEL_CANVAS_ITEM (icon->item)->canvas;
+            eel_canvas_w2c (canvas,
+                            current_rect->x0,
+                            current_rect->y0,
+                            &canvas_rect.x0,
+                            &canvas_rect.y0);
+            eel_canvas_w2c (canvas,
+                            current_rect->x1,
+                            current_rect->y1,
+                            &canvas_rect.x1,
+                            &canvas_rect.y1);
+            canvas_rect_calculated = TRUE;
+        }
+
+        is_in = nautilus_canvas_item_hit_test_rectangle (icon->item, canvas_rect);
+
+        selection_changed |= icon_set_selected
+                                 (container, icon,
+                                 is_in ^ icon->was_selected_before_rubberband);
+    }
+
+    if (selection_changed)
+    {
+        g_signal_emit (container,
+                       signals[SELECTION_CHANGED], 0);
+    }
+}
+
+static int
+rubberband_timeout_callback (gpointer data)
+{
+    NautilusCanvasContainer *container;
+    GtkWidget *widget;
+    NautilusCanvasRubberbandInfo *band_info;
+    int x, y;
+    double x1, y1, x2, y2;
+    double world_x, world_y;
+    int x_scroll, y_scroll;
+    int adj_x, adj_y;
+    gboolean adj_changed;
+    GtkAllocation allocation;
+
+    EelDRect selection_rect;
+
+    widget = GTK_WIDGET (data);
+    container = NAUTILUS_CANVAS_CONTAINER (data);
+    band_info = &container->details->rubberband_info;
+
+    g_assert (band_info->timer_id != 0);
+
+    adj_changed = FALSE;
+    gtk_widget_get_allocation (widget, &allocation);
+
+    adj_x = gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
+    if (adj_x != band_info->last_adj_x)
+    {
+        band_info->last_adj_x = adj_x;
+        adj_changed = TRUE;
+    }
+
+    adj_y = gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));
+    if (adj_y != band_info->last_adj_y)
+    {
+        band_info->last_adj_y = adj_y;
+        adj_changed = TRUE;
+    }
+
+    gdk_window_get_device_position (gtk_widget_get_window (widget),
+                                    band_info->device,
+                                    &x, &y, NULL);
+
+    if (x < RUBBERBAND_SCROLL_THRESHOLD)
+    {
+        x_scroll = x - RUBBERBAND_SCROLL_THRESHOLD;
+        x = 0;
+    }
+    else if (x >= allocation.width - RUBBERBAND_SCROLL_THRESHOLD)
+    {
+        x_scroll = x - allocation.width + RUBBERBAND_SCROLL_THRESHOLD + 1;
+        x = allocation.width - 1;
+    }
+    else
+    {
+        x_scroll = 0;
+    }
+
+    if (y < RUBBERBAND_SCROLL_THRESHOLD)
+    {
+        y_scroll = y - RUBBERBAND_SCROLL_THRESHOLD;
+        y = 0;
+    }
+    else if (y >= allocation.height - RUBBERBAND_SCROLL_THRESHOLD)
+    {
+        y_scroll = y - allocation.height + RUBBERBAND_SCROLL_THRESHOLD + 1;
+        y = allocation.height - 1;
+    }
+    else
+    {
+        y_scroll = 0;
+    }
+
+    if (y_scroll == 0 && x_scroll == 0
+        && (int) band_info->prev_x == x && (int) band_info->prev_y == y && !adj_changed)
+    {
+        return TRUE;
+    }
+
+    nautilus_canvas_container_scroll (container, x_scroll, y_scroll);
+
+    /* Remember to convert from widget to scrolled window coords */
+    eel_canvas_window_to_world (EEL_CANVAS (container),
+                                x + gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE 
(container))),
+                                y + gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE 
(container))),
+                                &world_x, &world_y);
+
+    if (world_x < band_info->start_x)
+    {
+        x1 = world_x;
+        x2 = band_info->start_x;
+    }
+    else
+    {
+        x1 = band_info->start_x;
+        x2 = world_x;
+    }
+
+    if (world_y < band_info->start_y)
+    {
+        y1 = world_y;
+        y2 = band_info->start_y;
+    }
+    else
+    {
+        y1 = band_info->start_y;
+        y2 = world_y;
+    }
+
+    /* Don't let the area of the selection rectangle be empty.
+     * Aside from the fact that it would be funny when the rectangle disappears,
+     * this also works around a crash in libart that happens sometimes when a
+     * zero height rectangle is passed.
+     */
+    x2 = MAX (x1 + 1, x2);
+    y2 = MAX (y1 + 1, y2);
+
+    eel_canvas_item_set
+        (band_info->selection_rectangle,
+        "x1", x1, "y1", y1,
+        "x2", x2, "y2", y2,
+        NULL);
+
+    selection_rect.x0 = x1;
+    selection_rect.y0 = y1;
+    selection_rect.x1 = x2;
+    selection_rect.y1 = y2;
+
+    rubberband_select (container,
+                       &selection_rect);
+
+    band_info->prev_x = x;
+    band_info->prev_y = y;
+
+    return TRUE;
+}
+
+static void
+stop_rubberbanding (NautilusCanvasContainer *container,
+                    GdkEventButton          *event);
+
+static void
+start_rubberbanding (NautilusCanvasContainer *container,
+                     GdkEventButton          *event)
+{
+    AtkObject *accessible;
+    NautilusCanvasContainerDetails *details;
+    NautilusCanvasRubberbandInfo *band_info;
+    GList *p;
+    NautilusCanvasIcon *icon;
+
+    details = container->details;
+    band_info = &details->rubberband_info;
+
+    if (band_info->active)
+    {
+        g_debug ("Canceling active rubberband by device %s", gdk_device_get_name (band_info->device));
+        stop_rubberbanding (container, NULL);
+    }
+
+    g_signal_emit (container,
+                   signals[BAND_SELECT_STARTED], 0);
+
+    band_info->device = event->device;
+
+    for (p = details->icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+        icon->was_selected_before_rubberband = icon->is_selected;
+    }
+
+    eel_canvas_window_to_world
+        (EEL_CANVAS (container), event->x, event->y,
+        &band_info->start_x, &band_info->start_y);
+
+    band_info->selection_rectangle = eel_canvas_item_new
+                                         (eel_canvas_root
+                                             (EEL_CANVAS (container)),
+                                         NAUTILUS_TYPE_SELECTION_CANVAS_ITEM,
+                                         "x1", band_info->start_x,
+                                         "y1", band_info->start_y,
+                                         "x2", band_info->start_x,
+                                         "y2", band_info->start_y,
+                                         NULL);
+
+    accessible = atk_gobject_accessible_for_object
+                     (G_OBJECT (band_info->selection_rectangle));
+    atk_object_set_name (accessible, "selection");
+    atk_object_set_description (accessible, _("The selection rectangle"));
+
+    band_info->prev_x = event->x - gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE 
(container)));
+    band_info->prev_y = event->y - gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE 
(container)));
+
+    band_info->active = TRUE;
+
+    if (band_info->timer_id == 0)
+    {
+        band_info->timer_id = g_timeout_add
+                                  (RUBBERBAND_TIMEOUT_INTERVAL,
+                                  rubberband_timeout_callback,
+                                  container);
+    }
+
+    eel_canvas_item_grab (band_info->selection_rectangle,
+                          (GDK_POINTER_MOTION_MASK
+                           | GDK_BUTTON_RELEASE_MASK
+                           | GDK_SCROLL_MASK),
+                          NULL,
+                          (GdkEvent *) event);
+}
+
+static void
+stop_rubberbanding (NautilusCanvasContainer *container,
+                    GdkEventButton          *event)
+{
+    NautilusCanvasRubberbandInfo *band_info;
+    GList *icons;
+    gboolean enable_animation;
+
+    band_info = &container->details->rubberband_info;
+
+    if (event != NULL && event->device != band_info->device)
+    {
+        return;
+    }
+
+    g_assert (band_info->timer_id != 0);
+    g_source_remove (band_info->timer_id);
+    band_info->timer_id = 0;
+
+    band_info->active = FALSE;
+
+    band_info->device = NULL;
+
+    g_object_get (gtk_settings_get_default (), "gtk-enable-animations", &enable_animation, NULL);
+
+    /* Destroy this canvas item; the parent will unref it. */
+    eel_canvas_item_ungrab (band_info->selection_rectangle);
+    eel_canvas_item_lower_to_bottom (band_info->selection_rectangle);
+    eel_canvas_item_destroy (band_info->selection_rectangle);
+    band_info->selection_rectangle = NULL;
+
+    /* if only one item has been selected, use it as range
+     * selection base (cf. handle_icon_button_press) */
+    icons = nautilus_canvas_container_get_selected_icons (container);
+    if (g_list_length (icons) == 1)
+    {
+        container->details->range_selection_base_icon = icons->data;
+    }
+    g_list_free (icons);
+
+    g_signal_emit (container,
+                   signals[BAND_SELECT_ENDED], 0);
+}
+
+/* Keyboard navigation.  */
+
+typedef gboolean (*IsBetterCanvasFunction) (NautilusCanvasContainer *container,
+                                            NautilusCanvasIcon      *start_icon,
+                                            NautilusCanvasIcon      *best_so_far,
+                                            NautilusCanvasIcon      *candidate,
+                                            void                    *data);
+
+static NautilusCanvasIcon *
+find_best_icon (NautilusCanvasContainer *container,
+                NautilusCanvasIcon      *start_icon,
+                IsBetterCanvasFunction   function,
+                void                    *data)
+{
+    GList *p;
+    NautilusCanvasIcon *best, *candidate;
+
+    best = NULL;
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        candidate = p->data;
+
+        if (candidate != start_icon)
+        {
+            if ((*function)(container, start_icon, best, candidate, data))
+            {
+                best = candidate;
+            }
+        }
+    }
+    return best;
+}
+
+static NautilusCanvasIcon *
+find_best_selected_icon (NautilusCanvasContainer *container,
+                         NautilusCanvasIcon      *start_icon,
+                         IsBetterCanvasFunction   function,
+                         void                    *data)
+{
+    GList *p;
+    NautilusCanvasIcon *best, *candidate;
+
+    best = NULL;
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        candidate = p->data;
+
+        if (candidate != start_icon && candidate->is_selected)
+        {
+            if ((*function)(container, start_icon, best, candidate, data))
+            {
+                best = candidate;
+            }
+        }
+    }
+    return best;
+}
+
+static int
+compare_icons_by_uri (NautilusCanvasContainer *container,
+                      NautilusCanvasIcon      *icon_a,
+                      NautilusCanvasIcon      *icon_b)
+{
+    char *uri_a, *uri_b;
+    int result;
+
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
+    g_assert (icon_a != NULL);
+    g_assert (icon_b != NULL);
+    g_assert (icon_a != icon_b);
+
+    uri_a = nautilus_canvas_container_get_icon_uri (container, icon_a);
+    uri_b = nautilus_canvas_container_get_icon_uri (container, icon_b);
+    result = strcmp (uri_a, uri_b);
+    g_assert (result != 0);
+    g_free (uri_a);
+    g_free (uri_b);
+
+    return result;
+}
+
+static int
+get_cmp_point_x (NautilusCanvasContainer *container,
+                 EelDRect                 icon_rect)
+{
+    return (icon_rect.x0 + icon_rect.x1) / 2;
+}
+
+static int
+get_cmp_point_y (NautilusCanvasContainer *container,
+                 EelDRect                 icon_rect)
+{
+    return icon_rect.y1;
+}
+
+
+static int
+compare_icons_horizontal (NautilusCanvasContainer *container,
+                          NautilusCanvasIcon      *icon_a,
+                          NautilusCanvasIcon      *icon_b)
+{
+    EelDRect world_rect;
+    int ax, bx;
+
+    world_rect = nautilus_canvas_item_get_icon_rectangle (icon_a->item);
+    eel_canvas_w2c
+        (EEL_CANVAS (container),
+        get_cmp_point_x (container, world_rect),
+        get_cmp_point_y (container, world_rect),
+        &ax,
+        NULL);
+    world_rect = nautilus_canvas_item_get_icon_rectangle (icon_b->item);
+    eel_canvas_w2c
+        (EEL_CANVAS (container),
+        get_cmp_point_x (container, world_rect),
+        get_cmp_point_y (container, world_rect),
+        &bx,
+        NULL);
+
+    if (ax < bx)
+    {
+        return -1;
+    }
+    if (ax > bx)
+    {
+        return +1;
+    }
+    return 0;
+}
+
+static int
+compare_icons_vertical (NautilusCanvasContainer *container,
+                        NautilusCanvasIcon      *icon_a,
+                        NautilusCanvasIcon      *icon_b)
+{
+    EelDRect world_rect;
+    int ay, by;
+
+    world_rect = nautilus_canvas_item_get_icon_rectangle (icon_a->item);
+    eel_canvas_w2c
+        (EEL_CANVAS (container),
+        get_cmp_point_x (container, world_rect),
+        get_cmp_point_y (container, world_rect),
+        NULL,
+        &ay);
+    world_rect = nautilus_canvas_item_get_icon_rectangle (icon_b->item);
+    eel_canvas_w2c
+        (EEL_CANVAS (container),
+        get_cmp_point_x (container, world_rect),
+        get_cmp_point_y (container, world_rect),
+        NULL,
+        &by);
+
+    if (ay < by)
+    {
+        return -1;
+    }
+    if (ay > by)
+    {
+        return +1;
+    }
+    return 0;
+}
+
+static int
+compare_icons_horizontal_first (NautilusCanvasContainer *container,
+                                NautilusCanvasIcon      *icon_a,
+                                NautilusCanvasIcon      *icon_b)
+{
+    EelDRect world_rect;
+    int ax, ay, bx, by;
+
+    world_rect = nautilus_canvas_item_get_icon_rectangle (icon_a->item);
+    eel_canvas_w2c
+        (EEL_CANVAS (container),
+        get_cmp_point_x (container, world_rect),
+        get_cmp_point_y (container, world_rect),
+        &ax,
+        &ay);
+    world_rect = nautilus_canvas_item_get_icon_rectangle (icon_b->item);
+    eel_canvas_w2c
+        (EEL_CANVAS (container),
+        get_cmp_point_x (container, world_rect),
+        get_cmp_point_y (container, world_rect),
+        &bx,
+        &by);
+
+    if (ax < bx)
+    {
+        return -1;
+    }
+    if (ax > bx)
+    {
+        return +1;
+    }
+    if (ay < by)
+    {
+        return -1;
+    }
+    if (ay > by)
+    {
+        return +1;
+    }
+    return compare_icons_by_uri (container, icon_a, icon_b);
+}
+
+static int
+compare_icons_vertical_first (NautilusCanvasContainer *container,
+                              NautilusCanvasIcon      *icon_a,
+                              NautilusCanvasIcon      *icon_b)
+{
+    EelDRect world_rect;
+    int ax, ay, bx, by;
+
+    world_rect = nautilus_canvas_item_get_icon_rectangle (icon_a->item);
+    eel_canvas_w2c
+        (EEL_CANVAS (container),
+        get_cmp_point_x (container, world_rect),
+        get_cmp_point_y (container, world_rect),
+        &ax,
+        &ay);
+    world_rect = nautilus_canvas_item_get_icon_rectangle (icon_b->item);
+    eel_canvas_w2c
+        (EEL_CANVAS (container),
+        get_cmp_point_x (container, world_rect),
+        get_cmp_point_y (container, world_rect),
+        &bx,
+        &by);
+
+    if (ay < by)
+    {
+        return -1;
+    }
+    if (ay > by)
+    {
+        return +1;
+    }
+    if (ax < bx)
+    {
+        return -1;
+    }
+    if (ax > bx)
+    {
+        return +1;
+    }
+    return compare_icons_by_uri (container, icon_a, icon_b);
+}
+
+static gboolean
+leftmost_in_top_row (NautilusCanvasContainer *container,
+                     NautilusCanvasIcon      *start_icon,
+                     NautilusCanvasIcon      *best_so_far,
+                     NautilusCanvasIcon      *candidate,
+                     void                    *data)
+{
+    if (best_so_far == NULL)
+    {
+        return TRUE;
+    }
+    return compare_icons_vertical_first (container, best_so_far, candidate) > 0;
+}
+
+static gboolean
+rightmost_in_top_row (NautilusCanvasContainer *container,
+                      NautilusCanvasIcon      *start_icon,
+                      NautilusCanvasIcon      *best_so_far,
+                      NautilusCanvasIcon      *candidate,
+                      void                    *data)
+{
+    if (best_so_far == NULL)
+    {
+        return TRUE;
+    }
+    return compare_icons_vertical (container, best_so_far, candidate) > 0;
+    return compare_icons_horizontal (container, best_so_far, candidate) < 0;
+}
+
+static gboolean
+rightmost_in_bottom_row (NautilusCanvasContainer *container,
+                         NautilusCanvasIcon      *start_icon,
+                         NautilusCanvasIcon      *best_so_far,
+                         NautilusCanvasIcon      *candidate,
+                         void                    *data)
+{
+    if (best_so_far == NULL)
+    {
+        return TRUE;
+    }
+    return compare_icons_vertical_first (container, best_so_far, candidate) < 0;
+}
+
+static int
+compare_with_start_row (NautilusCanvasContainer *container,
+                        NautilusCanvasIcon      *icon)
+{
+    EelCanvasItem *item;
+
+    item = EEL_CANVAS_ITEM (icon->item);
+
+    if (container->details->arrow_key_start_y < item->y1)
+    {
+        return -1;
+    }
+    if (container->details->arrow_key_start_y > item->y2)
+    {
+        return +1;
+    }
+    return 0;
+}
+
+static int
+compare_with_start_column (NautilusCanvasContainer *container,
+                           NautilusCanvasIcon      *icon)
+{
+    EelCanvasItem *item;
+
+    item = EEL_CANVAS_ITEM (icon->item);
+
+    if (container->details->arrow_key_start_x < item->x1)
+    {
+        return -1;
+    }
+    if (container->details->arrow_key_start_x > item->x2)
+    {
+        return +1;
+    }
+    return 0;
+}
+
+static gboolean
+same_row_right_side_leftmost (NautilusCanvasContainer *container,
+                              NautilusCanvasIcon      *start_icon,
+                              NautilusCanvasIcon      *best_so_far,
+                              NautilusCanvasIcon      *candidate,
+                              void                    *data)
+{
+    /* Candidates not on the start row do not qualify. */
+    if (compare_with_start_row (container, candidate) != 0)
+    {
+        return FALSE;
+    }
+
+    /* Candidates that are farther right lose out. */
+    if (best_so_far != NULL)
+    {
+        if (compare_icons_horizontal_first (container,
+                                            best_so_far,
+                                            candidate) < 0)
+        {
+            return FALSE;
+        }
+    }
+
+    /* Candidate to the left of the start do not qualify. */
+    if (compare_icons_horizontal_first (container,
+                                        candidate,
+                                        start_icon) <= 0)
+    {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+same_row_left_side_rightmost (NautilusCanvasContainer *container,
+                              NautilusCanvasIcon      *start_icon,
+                              NautilusCanvasIcon      *best_so_far,
+                              NautilusCanvasIcon      *candidate,
+                              void                    *data)
+{
+    /* Candidates not on the start row do not qualify. */
+    if (compare_with_start_row (container, candidate) != 0)
+    {
+        return FALSE;
+    }
+
+    /* Candidates that are farther left lose out. */
+    if (best_so_far != NULL)
+    {
+        if (compare_icons_horizontal_first (container,
+                                            best_so_far,
+                                            candidate) > 0)
+        {
+            return FALSE;
+        }
+    }
+
+    /* Candidate to the right of the start do not qualify. */
+    if (compare_icons_horizontal_first (container,
+                                        candidate,
+                                        start_icon) >= 0)
+    {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+next_row_leftmost (NautilusCanvasContainer *container,
+                   NautilusCanvasIcon      *start_icon,
+                   NautilusCanvasIcon      *best_so_far,
+                   NautilusCanvasIcon      *candidate,
+                   void                    *data)
+{
+    /* sort out icons that are not below the current row */
+    if (compare_with_start_row (container, candidate) >= 0)
+    {
+        return FALSE;
+    }
+
+    if (best_so_far != NULL)
+    {
+        if (compare_icons_vertical_first (container,
+                                          best_so_far,
+                                          candidate) > 0)
+        {
+            /* candidate is above best choice, but below the current row */
+            return TRUE;
+        }
+
+        if (compare_icons_horizontal_first (container,
+                                            best_so_far,
+                                            candidate) > 0)
+        {
+            return TRUE;
+        }
+    }
+
+    return best_so_far == NULL;
+}
+
+static gboolean
+next_row_rightmost (NautilusCanvasContainer *container,
+                    NautilusCanvasIcon      *start_icon,
+                    NautilusCanvasIcon      *best_so_far,
+                    NautilusCanvasIcon      *candidate,
+                    void                    *data)
+{
+    /* sort out icons that are not below the current row */
+    if (compare_with_start_row (container, candidate) >= 0)
+    {
+        return FALSE;
+    }
+
+    if (best_so_far != NULL)
+    {
+        if (compare_icons_vertical_first (container,
+                                          best_so_far,
+                                          candidate) > 0)
+        {
+            /* candidate is above best choice, but below the current row */
+            return TRUE;
+        }
+
+        if (compare_icons_horizontal_first (container,
+                                            best_so_far,
+                                            candidate) < 0)
+        {
+            return TRUE;
+        }
+    }
+
+    return best_so_far == NULL;
+}
+
+static gboolean
+previous_row_rightmost (NautilusCanvasContainer *container,
+                        NautilusCanvasIcon      *start_icon,
+                        NautilusCanvasIcon      *best_so_far,
+                        NautilusCanvasIcon      *candidate,
+                        void                    *data)
+{
+    /* sort out icons that are not above the current row */
+    if (compare_with_start_row (container, candidate) <= 0)
+    {
+        return FALSE;
+    }
+
+    if (best_so_far != NULL)
+    {
+        if (compare_icons_vertical_first (container,
+                                          best_so_far,
+                                          candidate) < 0)
+        {
+            /* candidate is below the best choice, but above the current row */
+            return TRUE;
+        }
+
+        if (compare_icons_horizontal_first (container,
+                                            best_so_far,
+                                            candidate) < 0)
+        {
+            return TRUE;
+        }
+    }
+
+    return best_so_far == NULL;
+}
+
+static gboolean
+same_column_above_lowest (NautilusCanvasContainer *container,
+                          NautilusCanvasIcon      *start_icon,
+                          NautilusCanvasIcon      *best_so_far,
+                          NautilusCanvasIcon      *candidate,
+                          void                    *data)
+{
+    /* Candidates not on the start column do not qualify. */
+    if (compare_with_start_column (container, candidate) != 0)
+    {
+        return FALSE;
+    }
+
+    /* Candidates that are higher lose out. */
+    if (best_so_far != NULL)
+    {
+        if (compare_icons_vertical_first (container,
+                                          best_so_far,
+                                          candidate) > 0)
+        {
+            return FALSE;
+        }
+    }
+
+    /* Candidates below the start do not qualify. */
+    if (compare_icons_vertical_first (container,
+                                      candidate,
+                                      start_icon) >= 0)
+    {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+same_column_below_highest (NautilusCanvasContainer *container,
+                           NautilusCanvasIcon      *start_icon,
+                           NautilusCanvasIcon      *best_so_far,
+                           NautilusCanvasIcon      *candidate,
+                           void                    *data)
+{
+    /* Candidates not on the start column do not qualify. */
+    if (compare_with_start_column (container, candidate) != 0)
+    {
+        return FALSE;
+    }
+
+    /* Candidates that are lower lose out. */
+    if (best_so_far != NULL)
+    {
+        if (compare_icons_vertical_first (container,
+                                          best_so_far,
+                                          candidate) < 0)
+        {
+            return FALSE;
+        }
+    }
+
+    /* Candidates above the start do not qualify. */
+    if (compare_icons_vertical_first (container,
+                                      candidate,
+                                      start_icon) <= 0)
+    {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+closest_in_90_degrees (NautilusCanvasContainer *container,
+                       NautilusCanvasIcon      *start_icon,
+                       NautilusCanvasIcon      *best_so_far,
+                       NautilusCanvasIcon      *candidate,
+                       void                    *data)
+{
+    EelDRect world_rect;
+    int x, y;
+    int dx, dy;
+    int dist;
+    int *best_dist;
+
+
+    world_rect = nautilus_canvas_item_get_icon_rectangle (candidate->item);
+    eel_canvas_w2c
+        (EEL_CANVAS (container),
+        get_cmp_point_x (container, world_rect),
+        get_cmp_point_y (container, world_rect),
+        &x,
+        &y);
+
+    dx = x - container->details->arrow_key_start_x;
+    dy = y - container->details->arrow_key_start_y;
+
+    switch (container->details->arrow_key_direction)
+    {
+        case GTK_DIR_UP:
+        {
+            if (dy > 0 ||
+                ABS (dx) > ABS (dy))
+            {
+                return FALSE;
+            }
+        }
+        break;
+
+        case GTK_DIR_DOWN:
+        {
+            if (dy < 0 ||
+                ABS (dx) > ABS (dy))
+            {
+                return FALSE;
+            }
+        }
+        break;
+
+        case GTK_DIR_LEFT:
+        {
+            if (dx > 0 ||
+                ABS (dy) > ABS (dx))
+            {
+                return FALSE;
+            }
+        }
+        break;
+
+        case GTK_DIR_RIGHT:
+        {
+            if (dx < 0 ||
+                ABS (dy) > ABS (dx))
+            {
+                return FALSE;
+            }
+        }
+        break;
+
+        default:
+        {
+            g_assert_not_reached ();
+        }
+        break;
+    }
+
+    dist = dx * dx + dy * dy;
+    best_dist = data;
+
+    if (best_so_far == NULL)
+    {
+        *best_dist = dist;
+        return TRUE;
+    }
+
+    if (dist < *best_dist)
+    {
+        *best_dist = dist;
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static EelDRect
+get_rubberband (NautilusCanvasIcon *icon1,
+                NautilusCanvasIcon *icon2)
+{
+    EelDRect rect1;
+    EelDRect rect2;
+    EelDRect ret;
+
+    eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon1->item),
+                                &rect1.x0, &rect1.y0,
+                                &rect1.x1, &rect1.y1);
+    eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon2->item),
+                                &rect2.x0, &rect2.y0,
+                                &rect2.x1, &rect2.y1);
+
+    eel_drect_union (&ret, &rect1, &rect2);
+
+    return ret;
+}
+
+static void
+keyboard_move_to (NautilusCanvasContainer *container,
+                  NautilusCanvasIcon      *icon,
+                  NautilusCanvasIcon      *from,
+                  GdkEventKey             *event)
+{
+    if (icon == NULL)
+    {
+        return;
+    }
+
+    set_focus (container, icon, TRUE);
+
+    if (event != NULL &&
+        (event->state & GDK_CONTROL_MASK) != 0 &&
+        (event->state & GDK_SHIFT_MASK) == 0)
+    {
+        clear_keyboard_rubberband_start (container);
+    }
+    else if (event != NULL &&
+             (event->state & GDK_CONTROL_MASK) != 0 &&
+             (event->state & GDK_SHIFT_MASK) != 0)
+    {
+        /* Do rubberband selection */
+        EelDRect rect;
+
+        if (from && !container->details->keyboard_rubberband_start)
+        {
+            set_keyboard_rubberband_start (container, from);
+        }
+
+        if (icon && container->details->keyboard_rubberband_start)
+        {
+            rect = get_rubberband (container->details->keyboard_rubberband_start,
+                                   icon);
+            rubberband_select (container, &rect);
+        }
+    }
+    else if (event != NULL &&
+             (event->state & GDK_CONTROL_MASK) == 0 &&
+             (event->state & GDK_SHIFT_MASK) != 0)
+    {
+        /* Select range */
+        NautilusCanvasIcon *start_icon;
+
+        start_icon = container->details->range_selection_base_icon;
+        if (start_icon == NULL || !start_icon->is_selected)
+        {
+            start_icon = icon;
+            container->details->range_selection_base_icon = icon;
+        }
+
+        if (select_range (container, start_icon, icon, TRUE))
+        {
+            g_signal_emit (container,
+                           signals[SELECTION_CHANGED], 0);
+        }
+    }
+    else
+    {
+        /* Select icon. */
+        clear_keyboard_rubberband_start (container);
+
+        container->details->range_selection_base_icon = icon;
+        if (select_one_unselect_others (container, icon))
+        {
+            g_signal_emit (container,
+                           signals[SELECTION_CHANGED], 0);
+        }
+    }
+    schedule_keyboard_icon_reveal (container, icon);
+}
+
+static void
+keyboard_home (NautilusCanvasContainer *container,
+               GdkEventKey             *event)
+{
+    NautilusCanvasIcon *from;
+    NautilusCanvasIcon *to;
+
+    /* Home selects the first canvas.
+     * Control-Home sets the keyboard focus to the first canvas.
+     */
+
+    from = find_best_selected_icon (container, NULL,
+                                    rightmost_in_bottom_row,
+                                    NULL);
+    to = find_best_icon (container, NULL, leftmost_in_top_row, NULL);
+
+    keyboard_move_to (container, to, from, event);
+}
+
+static void
+keyboard_end (NautilusCanvasContainer *container,
+              GdkEventKey             *event)
+{
+    NautilusCanvasIcon *to;
+    NautilusCanvasIcon *from;
+
+    /* End selects the last canvas.
+     * Control-End sets the keyboard focus to the last canvas.
+     */
+    from = find_best_selected_icon (container, NULL,
+                                    leftmost_in_top_row,
+                                    NULL);
+    to = find_best_icon (container, NULL, rightmost_in_bottom_row, NULL);
+
+    keyboard_move_to (container, to, from, event);
+}
+
+static void
+record_arrow_key_start (NautilusCanvasContainer *container,
+                        NautilusCanvasIcon      *icon,
+                        GtkDirectionType         direction)
+{
+    EelDRect world_rect;
+
+    world_rect = nautilus_canvas_item_get_icon_rectangle (icon->item);
+    eel_canvas_w2c
+        (EEL_CANVAS (container),
+        get_cmp_point_x (container, world_rect),
+        get_cmp_point_y (container, world_rect),
+        &container->details->arrow_key_start_x,
+        &container->details->arrow_key_start_y);
+    container->details->arrow_key_direction = direction;
+}
+
+static void
+keyboard_arrow_key (NautilusCanvasContainer *container,
+                    GdkEventKey             *event,
+                    GtkDirectionType         direction,
+                    IsBetterCanvasFunction   better_start,
+                    IsBetterCanvasFunction   empty_start,
+                    IsBetterCanvasFunction   better_destination,
+                    IsBetterCanvasFunction   better_destination_fallback,
+                    IsBetterCanvasFunction   better_destination_fallback_fallback,
+                    IsBetterCanvasFunction   better_destination_manual)
+{
+    NautilusCanvasIcon *from;
+    NautilusCanvasIcon *to;
+    int data;
+
+    /* Chose the icon to start with.
+     * If we have a keyboard focus, start with it.
+     * Otherwise, use the single selected icon.
+     * If there's multiple selection, use the icon farthest toward the end.
+     */
+
+    from = container->details->focus;
+
+    if (from == NULL)
+    {
+        if (has_multiple_selection (container))
+        {
+            if (all_selected (container))
+            {
+                from = find_best_selected_icon
+                           (container, NULL,
+                           empty_start, NULL);
+            }
+            else
+            {
+                from = find_best_selected_icon
+                           (container, NULL,
+                           better_start, NULL);
+            }
+        }
+        else
+        {
+            from = get_first_selected_icon (container);
+        }
+    }
+
+    /* If there's no icon, select the icon farthest toward the end.
+     * If there is an icon, select the next icon based on the arrow direction.
+     */
+    if (from == NULL)
+    {
+        to = from = find_best_icon
+                        (container, NULL,
+                        empty_start, NULL);
+    }
+    else
+    {
+        record_arrow_key_start (container, from, direction);
+
+        to = find_best_icon
+                 (container, from,
+                 better_destination,
+                 &data);
+
+        /* Wrap around to next/previous row/column */
+        if (to == NULL &&
+            better_destination_fallback != NULL)
+        {
+            to = find_best_icon
+                     (container, from,
+                     better_destination_fallback,
+                     &data);
+        }
+
+        /* With a layout like
+         * 1 2 3
+         * 4
+         * (horizontal layout)
+         *
+         * or
+         *
+         * 1 4
+         * 2
+         * 3
+         * (vertical layout)
+         *
+         * * pressing down for any of 1,2,3 (horizontal layout)
+         * * pressing right for any of 1,2,3 (vertical layout)
+         *
+         * Should select 4.
+         */
+        if (to == NULL &&
+            better_destination_fallback_fallback != NULL)
+        {
+            to = find_best_icon
+                     (container, from,
+                     better_destination_fallback_fallback,
+                     &data);
+        }
+
+        if (to == NULL)
+        {
+            to = from;
+        }
+    }
+
+    keyboard_move_to (container, to, from, event);
+}
+
+static gboolean
+is_rectangle_selection_event (GdkEventKey *event)
+{
+    return event != NULL &&
+           (event->state & GDK_CONTROL_MASK) != 0 &&
+           (event->state & GDK_SHIFT_MASK) != 0;
+}
+
+static void
+keyboard_right (NautilusCanvasContainer *container,
+                GdkEventKey             *event)
+{
+    IsBetterCanvasFunction fallback;
+
+    fallback = NULL;
+    if (!is_rectangle_selection_event (event))
+    {
+        fallback = next_row_leftmost;
+    }
+
+    /* Right selects the next icon in the same row.
+     * Control-Right sets the keyboard focus to the next icon in the same row.
+     */
+    keyboard_arrow_key (container,
+                        event,
+                        GTK_DIR_RIGHT,
+                        rightmost_in_bottom_row,
+                        nautilus_canvas_container_is_layout_rtl (container) ?
+                        rightmost_in_top_row : leftmost_in_top_row,
+                        same_row_right_side_leftmost,
+                        fallback,
+                        NULL,
+                        closest_in_90_degrees);
+}
+
+static void
+keyboard_left (NautilusCanvasContainer *container,
+               GdkEventKey             *event)
+{
+    IsBetterCanvasFunction fallback;
+
+    fallback = NULL;
+    if (!is_rectangle_selection_event (event))
+    {
+        fallback = previous_row_rightmost;
+    }
+
+    /* Left selects the next icon in the same row.
+     * Control-Left sets the keyboard focus to the next icon in the same row.
+     */
+    keyboard_arrow_key (container,
+                        event,
+                        GTK_DIR_LEFT,
+                        rightmost_in_bottom_row,
+                        nautilus_canvas_container_is_layout_rtl (container) ?
+                        rightmost_in_top_row : leftmost_in_top_row,
+                        same_row_left_side_rightmost,
+                        fallback,
+                        NULL,
+                        closest_in_90_degrees);
+}
+
+static void
+keyboard_down (NautilusCanvasContainer *container,
+               GdkEventKey             *event)
+{
+    IsBetterCanvasFunction next_row_fallback;
+
+    next_row_fallback = NULL;
+    if (gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL)
+    {
+        next_row_fallback = next_row_leftmost;
+    }
+    else
+    {
+        next_row_fallback = next_row_rightmost;
+    }
+
+    /* Down selects the next icon in the same column.
+     * Control-Down sets the keyboard focus to the next icon in the same column.
+     */
+    keyboard_arrow_key (container,
+                        event,
+                        GTK_DIR_DOWN,
+                        rightmost_in_bottom_row,
+                        nautilus_canvas_container_is_layout_rtl (container) ?
+                        rightmost_in_top_row : leftmost_in_top_row,
+                        same_column_below_highest,
+                        NULL,
+                        next_row_fallback,
+                        closest_in_90_degrees);
+}
+
+static void
+keyboard_up (NautilusCanvasContainer *container,
+             GdkEventKey             *event)
+{
+    /* Up selects the next icon in the same column.
+     * Control-Up sets the keyboard focus to the next icon in the same column.
+     */
+    keyboard_arrow_key (container,
+                        event,
+                        GTK_DIR_UP,
+                        rightmost_in_bottom_row,
+                        nautilus_canvas_container_is_layout_rtl (container) ?
+                        rightmost_in_top_row : leftmost_in_top_row,
+                        same_column_above_lowest,
+                        NULL,
+                        NULL,
+                        closest_in_90_degrees);
+}
+
+void
+nautilus_canvas_container_preview_selection_event (NautilusCanvasContainer *container,
+                                                   GtkDirectionType         direction)
+{
+    if (direction == GTK_DIR_UP)
+    {
+        keyboard_up (container, NULL);
+    }
+    else if (direction == GTK_DIR_DOWN)
+    {
+        keyboard_down (container, NULL);
+    }
+    else if (direction == GTK_DIR_LEFT)
+    {
+        keyboard_left (container, NULL);
+    }
+    else if (direction == GTK_DIR_RIGHT)
+    {
+        keyboard_right (container, NULL);
+    }
+}
+
+static void
+keyboard_space (NautilusCanvasContainer *container,
+                GdkEventKey             *event)
+{
+    NautilusCanvasIcon *icon;
+
+    if (!has_selection (container) &&
+        container->details->focus != NULL)
+    {
+        keyboard_move_to (container,
+                          container->details->focus,
+                          NULL, NULL);
+    }
+    else if ((event->state & GDK_CONTROL_MASK) != 0 &&
+             (event->state & GDK_SHIFT_MASK) == 0)
+    {
+        /* Control-space toggles the selection state of the current icon. */
+        if (container->details->focus != NULL)
+        {
+            icon_toggle_selected (container, container->details->focus);
+            g_signal_emit (container, signals[SELECTION_CHANGED], 0);
+            if (container->details->focus->is_selected)
+            {
+                container->details->range_selection_base_icon = container->details->focus;
+            }
+        }
+        else
+        {
+            icon = find_best_selected_icon (container,
+                                            NULL,
+                                            leftmost_in_top_row,
+                                            NULL);
+            if (icon == NULL)
+            {
+                icon = find_best_icon (container,
+                                       NULL,
+                                       leftmost_in_top_row,
+                                       NULL);
+            }
+            if (icon != NULL)
+            {
+                set_focus (container, icon, TRUE);
+            }
+        }
+    }
+    else if ((event->state & GDK_SHIFT_MASK) != 0)
+    {
+        activate_selected_items_alternate (container, NULL);
+    }
+    else
+    {
+        preview_selected_items (container);
+    }
+}
+
+static void
+destroy (GtkWidget *object)
+{
+    NautilusCanvasContainer *container;
+
+    container = NAUTILUS_CANVAS_CONTAINER (object);
+
+    nautilus_canvas_container_clear (container);
+
+    if (container->details->rubberband_info.timer_id != 0)
+    {
+        g_source_remove (container->details->rubberband_info.timer_id);
+        container->details->rubberband_info.timer_id = 0;
+    }
+
+    if (container->details->idle_id != 0)
+    {
+        g_source_remove (container->details->idle_id);
+        container->details->idle_id = 0;
+    }
+
+    if (container->details->align_idle_id != 0)
+    {
+        g_source_remove (container->details->align_idle_id);
+        container->details->align_idle_id = 0;
+    }
+
+    if (container->details->selection_changed_id != 0)
+    {
+        g_source_remove (container->details->selection_changed_id);
+        container->details->selection_changed_id = 0;
+    }
+
+    if (container->details->size_allocation_count_id != 0)
+    {
+        g_source_remove (container->details->size_allocation_count_id);
+        container->details->size_allocation_count_id = 0;
+    }
+
+    GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->destroy (object);
+}
+
+static void
+finalize (GObject *object)
+{
+    NautilusCanvasContainerDetails *details;
+
+    details = NAUTILUS_CANVAS_CONTAINER (object)->details;
+
+    g_signal_handlers_disconnect_by_func (nautilus_icon_view_preferences,
+                                          text_ellipsis_limit_changed_container_callback,
+                                          object);
+
+    g_hash_table_destroy (details->icon_set);
+    details->icon_set = NULL;
+
+    g_free (details->font);
+
+    if (details->a11y_item_action_queue != NULL)
+    {
+        while (!g_queue_is_empty (details->a11y_item_action_queue))
+        {
+            g_free (g_queue_pop_head (details->a11y_item_action_queue));
+        }
+        g_queue_free (details->a11y_item_action_queue);
+    }
+    if (details->a11y_item_action_idle_handler != 0)
+    {
+        g_source_remove (details->a11y_item_action_idle_handler);
+    }
+
+    g_free (details);
+
+    G_OBJECT_CLASS (nautilus_canvas_container_parent_class)->finalize (object);
+}
+
+/* GtkWidget methods.  */
+
+static gboolean
+clear_size_allocation_count (gpointer data)
+{
+    NautilusCanvasContainer *container;
+
+    container = NAUTILUS_CANVAS_CONTAINER (data);
+
+    container->details->size_allocation_count_id = 0;
+    container->details->size_allocation_count = 0;
+
+    return FALSE;
+}
+
+static void
+size_allocate (GtkWidget     *widget,
+               GtkAllocation *allocation)
+{
+    NautilusCanvasContainer *container;
+    gboolean need_layout_redone;
+    GtkAllocation wid_allocation;
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    need_layout_redone = !container->details->has_been_allocated;
+    gtk_widget_get_allocation (widget, &wid_allocation);
+
+    if (allocation->width != wid_allocation.width)
+    {
+        need_layout_redone = TRUE;
+    }
+
+    if (allocation->height != wid_allocation.height)
+    {
+        need_layout_redone = TRUE;
+    }
+
+    /* Under some conditions we can end up in a loop when size allocating.
+     * This happens when the icons don't fit without a scrollbar, but fits
+     * when a scrollbar is added (bug #129963 for details).
+     * We keep track of this looping by increasing a counter in size_allocate
+     * and clearing it in a high-prio idle (the only way to detect the loop is
+     * done).
+     * When we've done at more than two iterations (with/without scrollbar)
+     * we terminate this looping by not redoing the layout when the width
+     * is wider than the current one (i.e when removing the scrollbar).
+     */
+    if (container->details->size_allocation_count_id == 0)
+    {
+        container->details->size_allocation_count_id =
+            g_idle_add_full (G_PRIORITY_HIGH,
+                             clear_size_allocation_count,
+                             container, NULL);
+    }
+    container->details->size_allocation_count++;
+    if (container->details->size_allocation_count > 2 &&
+        allocation->width >= wid_allocation.width)
+    {
+        need_layout_redone = FALSE;
+    }
+
+    GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->size_allocate (widget, allocation);
+
+    container->details->has_been_allocated = TRUE;
+
+    if (need_layout_redone)
+    {
+        redo_layout (container);
+    }
+}
+
+static GtkSizeRequestMode
+get_request_mode (GtkWidget *widget)
+{
+    /* Don't trade size at all, since we get whatever we get anyway. */
+    return GTK_SIZE_REQUEST_CONSTANT_SIZE;
+}
+
+/* We need to implement these since the GtkScrolledWindow uses them
+ *  to guess whether to show scrollbars or not, and if we don't report
+ *  anything it'll tend to get it wrong causing double calls
+ *  to size_allocate (at different sizes) during its size allocation. */
+static void
+get_prefered_width (GtkWidget *widget,
+                    gint      *minimum_size,
+                    gint      *natural_size)
+{
+    EelCanvasGroup *root;
+    double x1, x2;
+    int cx1, cx2;
+    int width;
+
+    root = eel_canvas_root (EEL_CANVAS (widget));
+    eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (root),
+                                &x1, NULL, &x2, NULL);
+    eel_canvas_w2c (EEL_CANVAS (widget), x1, 0, &cx1, NULL);
+    eel_canvas_w2c (EEL_CANVAS (widget), x2, 0, &cx2, NULL);
+
+    width = cx2 - cx1;
+    if (natural_size)
+    {
+        *natural_size = width;
+    }
+    if (minimum_size)
+    {
+        *minimum_size = width;
+    }
+}
+
+static void
+get_prefered_height (GtkWidget *widget,
+                     gint      *minimum_size,
+                     gint      *natural_size)
+{
+    EelCanvasGroup *root;
+    double y1, y2;
+    int cy1, cy2;
+    int height;
+
+    root = eel_canvas_root (EEL_CANVAS (widget));
+    eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (root),
+                                NULL, &y1, NULL, &y2);
+    eel_canvas_w2c (EEL_CANVAS (widget), 0, y1, NULL, &cy1);
+    eel_canvas_w2c (EEL_CANVAS (widget), 0, y2, NULL, &cy2);
+
+    height = cy2 - cy1;
+    if (natural_size)
+    {
+        *natural_size = height;
+    }
+    if (minimum_size)
+    {
+        *minimum_size = height;
+    }
+}
+
+static void
+realize (GtkWidget *widget)
+{
+    GtkAdjustment *vadj, *hadj;
+    NautilusCanvasContainer *container;
+
+    GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->realize (widget);
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    /* Set up DnD.  */
+    nautilus_canvas_dnd_init (container);
+
+    hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (widget));
+    g_signal_connect (hadj, "value-changed",
+                      G_CALLBACK (handle_hadjustment_changed), widget);
+
+    vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (widget));
+    g_signal_connect (vadj, "value-changed",
+                      G_CALLBACK (handle_vadjustment_changed), widget);
+}
+
+static void
+unrealize (GtkWidget *widget)
+{
+    NautilusCanvasContainer *container;
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    nautilus_canvas_dnd_fini (container);
+
+    GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->unrealize (widget);
+}
+
+static void
+nautilus_canvas_container_request_update_all_internal (NautilusCanvasContainer *container,
+                                                       gboolean                 invalidate_labels)
+{
+    GList *node;
+    NautilusCanvasIcon *icon;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    for (node = container->details->icons; node != NULL; node = node->next)
+    {
+        icon = node->data;
+
+        if (invalidate_labels)
+        {
+            nautilus_canvas_item_invalidate_label (icon->item);
+        }
+
+        nautilus_canvas_container_update_icon (container, icon);
+    }
+
+    container->details->needs_resort = TRUE;
+    redo_layout (container);
+}
+
+static void
+style_updated (GtkWidget *widget)
+{
+    NautilusCanvasContainer *container;
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->style_updated (widget);
+
+    if (gtk_widget_get_realized (widget))
+    {
+        nautilus_canvas_container_request_update_all_internal (container, TRUE);
+    }
+}
+
+static gboolean
+button_press_event (GtkWidget      *widget,
+                    GdkEventButton *event)
+{
+    NautilusCanvasContainer *container;
+    gboolean selection_changed;
+    gboolean return_value;
+    gboolean clicked_on_icon;
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+    container->details->button_down_time = event->time;
+
+    /* Forget about the old keyboard selection now that we've started mousing. */
+    clear_keyboard_rubberband_start (container);
+
+    if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS)
+    {
+        /* We use our own double-click detection. */
+        return TRUE;
+    }
+
+    /* Invoke the canvas event handler and see if an item picks up the event. */
+    clicked_on_icon = GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->button_press_event (widget, 
event);
+
+    if (!gtk_widget_has_focus (widget))
+    {
+        gtk_widget_grab_focus (widget);
+    }
+
+    if (clicked_on_icon)
+    {
+        return TRUE;
+    }
+
+    clear_focus (container);
+
+    if (event->button == DRAG_BUTTON &&
+        event->type == GDK_BUTTON_PRESS)
+    {
+        /* Clear the last click icon for double click */
+        container->details->double_click_icon[1] = container->details->double_click_icon[0];
+        container->details->double_click_icon[0] = NULL;
+    }
+
+    /* Button 1 does rubber banding. */
+    if (event->button == RUBBERBAND_BUTTON)
+    {
+        if (!button_event_modifies_selection (event))
+        {
+            selection_changed = unselect_all (container);
+            if (selection_changed)
+            {
+                g_signal_emit (container,
+                               signals[SELECTION_CHANGED], 0);
+            }
+        }
+
+        start_rubberbanding (container, event);
+        return TRUE;
+    }
+
+    /* Prevent multi-button weirdness such as bug 6181 */
+    if (container->details->rubberband_info.active)
+    {
+        return TRUE;
+    }
+
+    /* Button 2 may be passed to the window manager. */
+    if (event->button == MIDDLE_BUTTON)
+    {
+        selection_changed = unselect_all (container);
+        if (selection_changed)
+        {
+            g_signal_emit (container, signals[SELECTION_CHANGED], 0);
+        }
+        g_signal_emit (widget, signals[MIDDLE_CLICK], 0, event);
+        return TRUE;
+    }
+
+    /* Button 3 does a contextual menu. */
+    if (event->button == CONTEXTUAL_MENU_BUTTON)
+    {
+        selection_changed = unselect_all (container);
+        if (selection_changed)
+        {
+            g_signal_emit (container, signals[SELECTION_CHANGED], 0);
+        }
+        g_signal_emit (widget, signals[CONTEXT_CLICK_BACKGROUND], 0, event);
+        return TRUE;
+    }
+
+    /* Otherwise, we emit a button_press message. */
+    g_signal_emit (widget,
+                   signals[BUTTON_PRESS], 0, event,
+                   &return_value);
+    return return_value;
+}
+
+static void
+nautilus_canvas_container_did_not_drag (NautilusCanvasContainer *container,
+                                        GdkEventButton          *event)
+{
+    NautilusCanvasContainerDetails *details;
+    gboolean selection_changed;
+    static gint64 last_click_time = 0;
+    static gint click_count = 0;
+    gint double_click_time;
+    gint64 current_time;
+
+    details = container->details;
+
+    if (details->icon_selected_on_button_down &&
+        ((event->state & GDK_CONTROL_MASK) != 0 ||
+         (event->state & GDK_SHIFT_MASK) == 0))
+    {
+        if (button_event_modifies_selection (event))
+        {
+            details->range_selection_base_icon = NULL;
+            icon_toggle_selected (container, details->drag_icon);
+            g_signal_emit (container,
+                           signals[SELECTION_CHANGED], 0);
+        }
+        else
+        {
+            details->range_selection_base_icon = details->drag_icon;
+            selection_changed = select_one_unselect_others
+                                    (container, details->drag_icon);
+
+            if (selection_changed)
+            {
+                g_signal_emit (container,
+                               signals[SELECTION_CHANGED], 0);
+            }
+        }
+    }
+
+    if (details->drag_icon != NULL &&
+        (details->single_click_mode ||
+         event->button == MIDDLE_BUTTON))
+    {
+        /* Determine click count */
+        g_object_get (G_OBJECT (gtk_widget_get_settings (GTK_WIDGET (container))),
+                      "gtk-double-click-time", &double_click_time,
+                      NULL);
+        current_time = g_get_monotonic_time ();
+        if (current_time - last_click_time < double_click_time * 1000)
+        {
+            click_count++;
+        }
+        else
+        {
+            click_count = 0;
+        }
+
+        /* Stash time for next compare */
+        last_click_time = current_time;
+
+        /* If single-click mode, activate the selected icons, unless modifying
+         * the selection or pressing for a very long time, or double clicking.
+         */
+
+
+        if (click_count == 0 &&
+            event->time - details->button_down_time < MAX_CLICK_TIME &&
+            !button_event_modifies_selection (event))
+        {
+            /* It's a tricky UI issue whether this should activate
+             * just the clicked item (as if it were a link), or all
+             * the selected items (as if you were issuing an "activate
+             * selection" command). For now, we're trying the activate
+             * entire selection version to see how it feels. Note that
+             * NautilusList goes the other way because its "links" seem
+             * much more link-like.
+             */
+            if (event->button == MIDDLE_BUTTON)
+            {
+                activate_selected_items_alternate (container, NULL);
+            }
+            else
+            {
+                activate_selected_items (container);
+            }
+        }
+    }
+}
+
+static gboolean
+clicked_within_double_click_interval (NautilusCanvasContainer *container)
+{
+    static gint64 last_click_time = 0;
+    static gint click_count = 0;
+    gint double_click_time;
+    gint64 current_time;
+
+    /* Determine click count */
+    g_object_get (G_OBJECT (gtk_widget_get_settings (GTK_WIDGET (container))),
+                  "gtk-double-click-time", &double_click_time,
+                  NULL);
+    current_time = g_get_monotonic_time ();
+    if (current_time - last_click_time < double_click_time * 1000)
+    {
+        click_count++;
+    }
+    else
+    {
+        click_count = 0;
+    }
+
+    /* Stash time for next compare */
+    last_click_time = current_time;
+
+    /* Only allow double click */
+    if (click_count == 1)
+    {
+        click_count = 0;
+        return TRUE;
+    }
+    else
+    {
+        return FALSE;
+    }
+}
+
+static void
+clear_drag_state (NautilusCanvasContainer *container)
+{
+    container->details->drag_icon = NULL;
+    container->details->drag_state = DRAG_STATE_INITIAL;
+}
+
+static gboolean
+button_release_event (GtkWidget      *widget,
+                      GdkEventButton *event)
+{
+    NautilusCanvasContainer *container;
+    NautilusCanvasContainerDetails *details;
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+    details = container->details;
+
+    if (event->button == RUBBERBAND_BUTTON && details->rubberband_info.active)
+    {
+        stop_rubberbanding (container, event);
+        return TRUE;
+    }
+
+    if (event->button == details->drag_button)
+    {
+        details->drag_button = 0;
+
+        switch (details->drag_state)
+        {
+            case DRAG_STATE_MOVE_OR_COPY:
+            {
+                if (!details->drag_started)
+                {
+                    nautilus_canvas_container_did_not_drag (container, event);
+                }
+                else
+                {
+                    nautilus_canvas_dnd_end_drag (container);
+                    DEBUG ("Ending drag from canvas container");
+                }
+            }
+            break;
+
+            default:
+            {
+            }
+            break;
+        }
+
+        clear_drag_state (container);
+        return TRUE;
+    }
+
+    return GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->button_release_event (widget, event);
+}
+
+static int
+motion_notify_event (GtkWidget      *widget,
+                     GdkEventMotion *event)
+{
+    NautilusCanvasContainer *container;
+    NautilusCanvasContainerDetails *details;
+    double world_x, world_y;
+    int canvas_x, canvas_y;
+    GdkDragAction actions;
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+    details = container->details;
+
+    if (details->drag_button != 0)
+    {
+        switch (details->drag_state)
+        {
+            case DRAG_STATE_MOVE_OR_COPY:
+            {
+                if (details->drag_started)
+                {
+                    break;
+                }
+
+                eel_canvas_window_to_world
+                    (EEL_CANVAS (container), event->x, event->y, &world_x, &world_y);
+
+                if (gtk_drag_check_threshold (widget,
+                                              details->drag_x,
+                                              details->drag_y,
+                                              world_x,
+                                              world_y))
+                {
+                    details->drag_started = TRUE;
+                    details->drag_state = DRAG_STATE_MOVE_OR_COPY;
+
+                    eel_canvas_w2c (EEL_CANVAS (container),
+                                    details->drag_x,
+                                    details->drag_y,
+                                    &canvas_x,
+                                    &canvas_y);
+
+                    actions = GDK_ACTION_COPY
+                              | GDK_ACTION_MOVE
+                              | GDK_ACTION_LINK
+                              | GDK_ACTION_ASK;
+
+                    nautilus_canvas_dnd_begin_drag (container,
+                                                    actions,
+                                                    details->drag_button,
+                                                    event,
+                                                    canvas_x,
+                                                    canvas_y);
+                    DEBUG ("Beginning drag from canvas container");
+                }
+            }
+            break;
+
+            default:
+            {
+            }
+            break;
+        }
+    }
+
+    return GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->motion_notify_event (widget, event);
+}
+
+static void
+nautilus_canvas_container_get_icon_text (NautilusCanvasContainer  *container,
+                                         NautilusCanvasIconData   *data,
+                                         char                    **editable_text,
+                                         char                    **additional_text,
+                                         gboolean                  include_invisible)
+{
+    NautilusCanvasContainerClass *klass;
+
+    klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
+    g_assert (klass->get_icon_text != NULL);
+
+    klass->get_icon_text (container, data, editable_text, additional_text, include_invisible);
+}
+
+static gboolean
+handle_popups (NautilusCanvasContainer *container,
+               GdkEvent                *event,
+               const char              *signal)
+{
+    /* ensure we clear the drag state before showing the menu */
+    clear_drag_state (container);
+
+    g_signal_emit_by_name (container, signal, event);
+
+    return TRUE;
+}
+
+static int
+key_press_event (GtkWidget   *widget,
+                 GdkEventKey *event)
+{
+    NautilusCanvasContainer *container;
+    gboolean handled;
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+    handled = FALSE;
+
+    switch (event->keyval)
+    {
+        case GDK_KEY_Home:
+        case GDK_KEY_KP_Home:
+        {
+            keyboard_home (container, event);
+            handled = TRUE;
+        }
+        break;
+
+        case GDK_KEY_End:
+        case GDK_KEY_KP_End:
+        {
+            keyboard_end (container, event);
+            handled = TRUE;
+        }
+        break;
+
+        case GDK_KEY_Left:
+        case GDK_KEY_KP_Left:
+        {
+            /* Don't eat Alt-Left, as that is used for history browsing */
+            if ((event->state & GDK_MOD1_MASK) == 0)
+            {
+                keyboard_left (container, event);
+                handled = TRUE;
+            }
+        }
+        break;
+
+        case GDK_KEY_Up:
+        case GDK_KEY_KP_Up:
+        {
+            /* Don't eat Alt-Up, as that is used for alt-shift-Up */
+            if ((event->state & GDK_MOD1_MASK) == 0)
+            {
+                keyboard_up (container, event);
+                handled = TRUE;
+            }
+        }
+        break;
+
+        case GDK_KEY_Right:
+        case GDK_KEY_KP_Right:
+        {
+            /* Don't eat Alt-Right, as that is used for history browsing */
+            if ((event->state & GDK_MOD1_MASK) == 0)
+            {
+                keyboard_right (container, event);
+                handled = TRUE;
+            }
+        }
+        break;
+
+        case GDK_KEY_Down:
+        case GDK_KEY_KP_Down:
+        {
+            /* Don't eat Alt-Down, as that is used for Open */
+            if ((event->state & GDK_MOD1_MASK) == 0)
+            {
+                keyboard_down (container, event);
+                handled = TRUE;
+            }
+        }
+        break;
+
+        case GDK_KEY_space:
+        {
+            keyboard_space (container, event);
+            handled = TRUE;
+        }
+        break;
+
+        case GDK_KEY_F10:
+        {
+            /* handle Ctrl+F10 because we want to display the
+             * background popup even if something is selected.
+             * The other cases are handled by the "popup-menu" GtkWidget signal.
+             */
+            if (event->state & GDK_CONTROL_MASK)
+            {
+                handled = handle_popups (container, (GdkEvent *) event,
+                                         "context_click_background");
+            }
+        }
+        break;
+
+        case GDK_KEY_v:
+        {
+            /* Eat Control + v to not enable type ahead */
+            if ((event->state & GDK_CONTROL_MASK) != 0)
+            {
+                handled = TRUE;
+            }
+        }
+        break;
+
+        default:
+        {
+        }
+        break;
+    }
+
+    if (!handled)
+    {
+        handled = GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->key_press_event (widget, event);
+    }
+
+    return handled;
+}
+
+static void
+grab_notify_cb  (GtkWidget *widget,
+                 gboolean   was_grabbed)
+{
+    NautilusCanvasContainer *container;
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    if (container->details->rubberband_info.active &&
+        !was_grabbed)
+    {
+        /* we got a (un)grab-notify during rubberband.
+         * This happens when a new modal dialog shows
+         * up (e.g. authentication or an error). Stop
+         * the rubberbanding so that we can handle the
+         * dialog. */
+        stop_rubberbanding (container, NULL);
+    }
+}
+
+static void
+text_ellipsis_limit_changed_container_callback (gpointer callback_data)
+{
+    NautilusCanvasContainer *container;
+
+    container = NAUTILUS_CANVAS_CONTAINER (callback_data);
+    invalidate_label_sizes (container);
+    schedule_redo_layout (container);
+}
+
+static GObject *
+nautilus_canvas_container_constructor (GType                  type,
+                                       guint                  n_construct_params,
+                                       GObjectConstructParam *construct_params)
+{
+    NautilusCanvasContainer *container;
+    GObject *object;
+
+    object = G_OBJECT_CLASS (nautilus_canvas_container_parent_class)->constructor
+                 (type,
+                 n_construct_params,
+                 construct_params);
+
+    container = NAUTILUS_CANVAS_CONTAINER (object);
+    g_signal_connect_swapped (nautilus_icon_view_preferences,
+                              "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT,
+                              G_CALLBACK (text_ellipsis_limit_changed_container_callback),
+                              container);
+
+    return object;
+}
+
+/* Initialization.  */
+
+static void
+nautilus_canvas_container_class_init (NautilusCanvasContainerClass *class)
+{
+    GtkWidgetClass *widget_class;
+
+    G_OBJECT_CLASS (class)->constructor = nautilus_canvas_container_constructor;
+    G_OBJECT_CLASS (class)->finalize = finalize;
+
+    /* Signals.  */
+
+    signals[SELECTION_CHANGED]
+        = g_signal_new ("selection-changed",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         selection_changed),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__VOID,
+                        G_TYPE_NONE, 0);
+    signals[BUTTON_PRESS]
+        = g_signal_new ("button-press",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         button_press),
+                        NULL, NULL,
+                        g_cclosure_marshal_generic,
+                        G_TYPE_BOOLEAN, 1,
+                        GDK_TYPE_EVENT);
+    signals[ACTIVATE]
+        = g_signal_new ("activate",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         activate),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__POINTER,
+                        G_TYPE_NONE, 1,
+                        G_TYPE_POINTER);
+    signals[ACTIVATE_ALTERNATE]
+        = g_signal_new ("activate-alternate",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         activate_alternate),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__POINTER,
+                        G_TYPE_NONE, 1,
+                        G_TYPE_POINTER);
+    signals[ACTIVATE_PREVIEWER]
+        = g_signal_new ("activate-previewer",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         activate_previewer),
+                        NULL, NULL,
+                        g_cclosure_marshal_generic,
+                        G_TYPE_NONE, 2,
+                        G_TYPE_POINTER, G_TYPE_POINTER);
+    signals[CONTEXT_CLICK_SELECTION]
+        = g_signal_new ("context-click-selection",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         context_click_selection),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__POINTER,
+                        G_TYPE_NONE, 1,
+                        G_TYPE_POINTER);
+    signals[CONTEXT_CLICK_BACKGROUND]
+        = g_signal_new ("context-click-background",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         context_click_background),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__POINTER,
+                        G_TYPE_NONE, 1,
+                        G_TYPE_POINTER);
+    signals[MIDDLE_CLICK]
+        = g_signal_new ("middle-click",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         middle_click),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__POINTER,
+                        G_TYPE_NONE, 1,
+                        G_TYPE_POINTER);
+    signals[GET_ICON_URI]
+        = g_signal_new ("get-icon-uri",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         get_icon_uri),
+                        NULL, NULL,
+                        g_cclosure_marshal_generic,
+                        G_TYPE_STRING, 1,
+                        G_TYPE_POINTER);
+    signals[GET_ICON_ACTIVATION_URI]
+        = g_signal_new ("get-icon-activation-uri",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         get_icon_activation_uri),
+                        NULL, NULL,
+                        g_cclosure_marshal_generic,
+                        G_TYPE_STRING, 1,
+                        G_TYPE_POINTER);
+    signals[GET_ICON_DROP_TARGET_URI]
+        = g_signal_new ("get-icon-drop-target-uri",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         get_icon_drop_target_uri),
+                        NULL, NULL,
+                        g_cclosure_marshal_generic,
+                        G_TYPE_STRING, 1,
+                        G_TYPE_POINTER);
+    signals[MOVE_COPY_ITEMS]
+        = g_signal_new ("move-copy-items",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         move_copy_items),
+                        NULL, NULL,
+                        g_cclosure_marshal_generic,
+                        G_TYPE_NONE, 3,
+                        G_TYPE_POINTER,
+                        G_TYPE_POINTER,
+                        GDK_TYPE_DRAG_ACTION);
+    signals[HANDLE_NETSCAPE_URL]
+        = g_signal_new ("handle-netscape-url",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         handle_netscape_url),
+                        NULL, NULL,
+                        g_cclosure_marshal_generic,
+                        G_TYPE_NONE, 3,
+                        G_TYPE_STRING,
+                        G_TYPE_STRING,
+                        GDK_TYPE_DRAG_ACTION);
+    signals[HANDLE_URI_LIST]
+        = g_signal_new ("handle-uri-list",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         handle_uri_list),
+                        NULL, NULL,
+                        g_cclosure_marshal_generic,
+                        G_TYPE_NONE, 3,
+                        G_TYPE_STRING,
+                        G_TYPE_STRING,
+                        GDK_TYPE_DRAG_ACTION);
+    signals[HANDLE_TEXT]
+        = g_signal_new ("handle-text",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         handle_text),
+                        NULL, NULL,
+                        g_cclosure_marshal_generic,
+                        G_TYPE_NONE, 3,
+                        G_TYPE_STRING,
+                        G_TYPE_STRING,
+                        GDK_TYPE_DRAG_ACTION);
+    signals[HANDLE_RAW]
+        = g_signal_new ("handle-raw",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         handle_raw),
+                        NULL, NULL,
+                        g_cclosure_marshal_generic,
+                        G_TYPE_NONE, 5,
+                        G_TYPE_POINTER,
+                        G_TYPE_INT,
+                        G_TYPE_STRING,
+                        G_TYPE_STRING,
+                        GDK_TYPE_DRAG_ACTION);
+    signals[HANDLE_HOVER] =
+        g_signal_new ("handle-hover",
+                      G_TYPE_FROM_CLASS (class),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                       handle_hover),
+                      NULL, NULL,
+                      g_cclosure_marshal_generic,
+                      G_TYPE_NONE, 1,
+                      G_TYPE_STRING);
+    signals[GET_CONTAINER_URI]
+        = g_signal_new ("get-container-uri",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         get_container_uri),
+                        NULL, NULL,
+                        g_cclosure_marshal_generic,
+                        G_TYPE_STRING, 0);
+    signals[BAND_SELECT_STARTED]
+        = g_signal_new ("band-select-started",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         band_select_started),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__VOID,
+                        G_TYPE_NONE, 0);
+    signals[BAND_SELECT_ENDED]
+        = g_signal_new ("band-select-ended",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         band_select_ended),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__VOID,
+                        G_TYPE_NONE, 0);
+    signals[ICON_ADDED]
+        = g_signal_new ("icon-added",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         icon_added),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__POINTER,
+                        G_TYPE_NONE, 1, G_TYPE_POINTER);
+    signals[ICON_REMOVED]
+        = g_signal_new ("icon-removed",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         icon_removed),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__POINTER,
+                        G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+    signals[CLEARED]
+        = g_signal_new ("cleared",
+                        G_TYPE_FROM_CLASS (class),
+                        G_SIGNAL_RUN_LAST,
+                        G_STRUCT_OFFSET (NautilusCanvasContainerClass,
+                                         cleared),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__VOID,
+                        G_TYPE_NONE, 0);
+
+    /* GtkWidget class.  */
+
+    widget_class = GTK_WIDGET_CLASS (class);
+    widget_class->destroy = destroy;
+    widget_class->size_allocate = size_allocate;
+    widget_class->get_request_mode = get_request_mode;
+    widget_class->get_preferred_width = get_prefered_width;
+    widget_class->get_preferred_height = get_prefered_height;
+    widget_class->realize = realize;
+    widget_class->unrealize = unrealize;
+    widget_class->button_press_event = button_press_event;
+    widget_class->button_release_event = button_release_event;
+    widget_class->motion_notify_event = motion_notify_event;
+    widget_class->key_press_event = key_press_event;
+    widget_class->style_updated = style_updated;
+    widget_class->grab_notify = grab_notify_cb;
+
+    gtk_widget_class_set_accessible_type (widget_class, nautilus_canvas_container_accessible_get_type ());
+}
+
+static void
+update_selected (NautilusCanvasContainer *container)
+{
+    GList *node;
+    NautilusCanvasIcon *icon;
+
+    for (node = container->details->icons; node != NULL; node = node->next)
+    {
+        icon = node->data;
+        if (icon->is_selected)
+        {
+            eel_canvas_item_request_update (EEL_CANVAS_ITEM (icon->item));
+        }
+    }
+}
+
+static void
+handle_has_focus_changed (GObject    *object,
+                          GParamSpec *pspec,
+                          gpointer    user_data)
+{
+    update_selected (NAUTILUS_CANVAS_CONTAINER (object));
+}
+
+static void
+handle_scale_factor_changed (GObject    *object,
+                             GParamSpec *pspec,
+                             gpointer    user_data)
+{
+    nautilus_canvas_container_request_update_all_internal (NAUTILUS_CANVAS_CONTAINER (object),
+                                                           TRUE);
+}
+
+
+
+static int text_ellipsis_limits[NAUTILUS_CANVAS_ZOOM_LEVEL_N_ENTRIES];
+
+static gboolean
+get_text_ellipsis_limit_for_zoom (char       **strs,
+                                  const char  *zoom_level,
+                                  int         *limit)
+{
+    char **p;
+    char *str;
+    gboolean success;
+
+    success = FALSE;
+
+    /* default */
+    *limit = 3;
+
+    if (zoom_level != NULL)
+    {
+        str = g_strdup_printf ("%s:%%d", zoom_level);
+    }
+    else
+    {
+        str = g_strdup ("%d");
+    }
+
+    if (strs != NULL)
+    {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+        for (p = strs; *p != NULL; p++)
+        {
+            if (sscanf (*p, str, limit))
+            {
+                success = TRUE;
+            }
+        }
+#pragma GCC diagnostic pop
+    }
+
+    g_free (str);
+
+    return success;
+}
+
+static const char *zoom_level_names[] =
+{
+    "small",
+    "standard",
+    "large",
+};
+
+static void
+text_ellipsis_limit_changed_callback (gpointer callback_data)
+{
+    char **pref;
+    unsigned int i;
+    int one_limit;
+
+    pref = g_settings_get_strv (nautilus_icon_view_preferences,
+                                NAUTILUS_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT);
+
+    /* set default */
+    get_text_ellipsis_limit_for_zoom (pref, NULL, &one_limit);
+    for (i = 0; i < NAUTILUS_CANVAS_ZOOM_LEVEL_N_ENTRIES; i++)
+    {
+        text_ellipsis_limits[i] = one_limit;
+    }
+
+    /* override for each zoom level */
+    for (i = 0; i < G_N_ELEMENTS (zoom_level_names); i++)
+    {
+        if (get_text_ellipsis_limit_for_zoom (pref,
+                                              zoom_level_names[i],
+                                              &one_limit))
+        {
+            text_ellipsis_limits[i] = one_limit;
+        }
+    }
+
+    g_strfreev (pref);
+}
+
+static void
+nautilus_canvas_container_init (NautilusCanvasContainer *container)
+{
+    NautilusCanvasContainerDetails *details;
+    static gboolean setup_prefs = FALSE;
+
+    details = g_new0 (NautilusCanvasContainerDetails, 1);
+
+    details->icon_set = g_hash_table_new (g_direct_hash, g_direct_equal);
+    details->zoom_level = NAUTILUS_CANVAS_ZOOM_LEVEL_STANDARD;
+
+    container->details = details;
+
+    g_signal_connect (container, "notify::has-focus",
+                      G_CALLBACK (handle_has_focus_changed), NULL);
+    g_signal_connect (container, "notify::scale-factor",
+                      G_CALLBACK (handle_scale_factor_changed), NULL);
+
+    if (!setup_prefs)
+    {
+        g_signal_connect_swapped (nautilus_icon_view_preferences,
+                                  "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT,
+                                  G_CALLBACK (text_ellipsis_limit_changed_callback),
+                                  NULL);
+        text_ellipsis_limit_changed_callback (NULL);
+
+        setup_prefs = TRUE;
+    }
+}
+
+typedef struct
+{
+    NautilusCanvasContainer *container;
+    GdkEventButton *event;
+} ContextMenuParameters;
+
+static gboolean
+handle_canvas_double_click (NautilusCanvasContainer *container,
+                            NautilusCanvasIcon      *icon,
+                            GdkEventButton          *event)
+{
+    NautilusCanvasContainerDetails *details;
+
+    if (event->button != DRAG_BUTTON)
+    {
+        return FALSE;
+    }
+
+    details = container->details;
+
+    if (!details->single_click_mode &&
+        clicked_within_double_click_interval (container) &&
+        details->double_click_icon[0] == details->double_click_icon[1] &&
+        details->double_click_button[0] == details->double_click_button[1])
+    {
+        details->double_clicked = TRUE;
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/* NautilusCanvasIcon event handling.  */
+
+/* Conceptually, pressing button 1 together with CTRL or SHIFT toggles
+ * selection of a single icon without affecting the other icons;
+ * without CTRL or SHIFT, it selects a single icon and un-selects all
+ * the other icons.  But in this latter case, the de-selection should
+ * only happen when the button is released if the icon is already
+ * selected, because the user might select multiple icons and drag all
+ * of them by doing a simple click-drag.
+ */
+
+static gboolean
+handle_canvas_button_press (NautilusCanvasContainer *container,
+                            NautilusCanvasIcon      *icon,
+                            GdkEventButton          *event)
+{
+    NautilusCanvasContainerDetails *details;
+
+    details = container->details;
+
+    if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS)
+    {
+        return TRUE;
+    }
+
+    if (event->button != DRAG_BUTTON
+        && event->button != CONTEXTUAL_MENU_BUTTON
+        && event->button != DRAG_MENU_BUTTON)
+    {
+        return TRUE;
+    }
+
+    if ((event->button == DRAG_BUTTON) &&
+        event->type == GDK_BUTTON_PRESS)
+    {
+        /* The next double click has to be on this icon */
+        details->double_click_icon[1] = details->double_click_icon[0];
+        details->double_click_icon[0] = icon;
+
+        details->double_click_button[1] = details->double_click_button[0];
+        details->double_click_button[0] = event->button;
+    }
+
+    if (handle_canvas_double_click (container, icon, event))
+    {
+        /* Double clicking does not trigger a D&D action. */
+        details->drag_button = 0;
+        details->drag_icon = NULL;
+        return TRUE;
+    }
+
+    if (event->button == DRAG_BUTTON
+        || event->button == DRAG_MENU_BUTTON)
+    {
+        details->drag_button = event->button;
+        details->drag_icon = icon;
+        details->drag_x = event->x;
+        details->drag_y = event->y;
+        details->drag_state = DRAG_STATE_MOVE_OR_COPY;
+        details->drag_started = FALSE;
+    }
+
+    /* Modify the selection as appropriate. Selection is modified
+     * the same way for contextual menu as it would be without.
+     */
+    details->icon_selected_on_button_down = icon->is_selected;
+
+    if ((event->button == DRAG_BUTTON || event->button == MIDDLE_BUTTON) &&
+        (event->state & GDK_SHIFT_MASK) != 0)
+    {
+        NautilusCanvasIcon *start_icon;
+
+        set_focus (container, icon, FALSE);
+
+        start_icon = details->range_selection_base_icon;
+        if (start_icon == NULL || !start_icon->is_selected)
+        {
+            start_icon = icon;
+            details->range_selection_base_icon = icon;
+        }
+        if (select_range (container, start_icon, icon,
+                          (event->state & GDK_CONTROL_MASK) == 0))
+        {
+            g_signal_emit (container,
+                           signals[SELECTION_CHANGED], 0);
+        }
+    }
+    else if (!details->icon_selected_on_button_down)
+    {
+        set_focus (container, icon, FALSE);
+
+        details->range_selection_base_icon = icon;
+        if (button_event_modifies_selection (event))
+        {
+            icon_toggle_selected (container, icon);
+            g_signal_emit (container,
+                           signals[SELECTION_CHANGED], 0);
+        }
+        else
+        {
+            select_one_unselect_others (container, icon);
+            g_signal_emit (container,
+                           signals[SELECTION_CHANGED], 0);
+        }
+    }
+
+    if (event->button == CONTEXTUAL_MENU_BUTTON)
+    {
+        clear_drag_state (container);
+
+        g_signal_emit (container,
+                       signals[CONTEXT_CLICK_SELECTION], 0,
+                       event);
+    }
+
+
+    return TRUE;
+}
+
+static int
+item_event_callback (EelCanvasItem *item,
+                     GdkEvent      *event,
+                     gpointer       data)
+{
+    NautilusCanvasContainer *container;
+    NautilusCanvasIcon *icon;
+    GdkEventButton *event_button;
+
+    container = NAUTILUS_CANVAS_CONTAINER (data);
+
+    icon = NAUTILUS_CANVAS_ITEM (item)->user_data;
+    g_assert (icon != NULL);
+
+    event_button = &event->button;
+
+    switch (event->type)
+    {
+        case GDK_MOTION_NOTIFY:
+        {
+            return FALSE;
+        }
+
+        case GDK_BUTTON_PRESS:
+        {
+            container->details->double_clicked = FALSE;
+            if (handle_canvas_button_press (container, icon, event_button))
+            {
+                /* Stop the event from being passed along further. Returning
+                 * TRUE ain't enough.
+                 */
+                return TRUE;
+            }
+            return FALSE;
+        }
+
+        case GDK_BUTTON_RELEASE:
+        {
+            if (event_button->button == DRAG_BUTTON
+                && container->details->double_clicked)
+            {
+                if (!button_event_modifies_selection (event_button))
+                {
+                    activate_selected_items (container);
+                }
+                else if ((event_button->state & GDK_CONTROL_MASK) == 0 &&
+                         (event_button->state & GDK_SHIFT_MASK) != 0)
+                {
+                    activate_selected_items_alternate (container, icon);
+                }
+            }
+            /* fall through */
+        }
+
+        default:
+        {
+            container->details->double_clicked = FALSE;
+            return FALSE;
+        }
+        break;
+    }
+}
+
+GtkWidget *
+nautilus_canvas_container_new (void)
+{
+    return gtk_widget_new (NAUTILUS_TYPE_CANVAS_CONTAINER, NULL);
+}
+
+/* Clear all of the icons in the container. */
+void
+nautilus_canvas_container_clear (NautilusCanvasContainer *container)
+{
+    NautilusCanvasContainerDetails *details;
+    GList *p;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    details = container->details;
+
+    if (details->icons == NULL)
+    {
+        return;
+    }
+
+    clear_focus (container);
+    clear_keyboard_rubberband_start (container);
+    unschedule_keyboard_icon_reveal (container);
+    set_pending_icon_to_reveal (container, NULL);
+    details->drop_target = NULL;
+
+    for (p = details->icons; p != NULL; p = p->next)
+    {
+        icon_free (p->data);
+    }
+    g_list_free (details->icons);
+    details->icons = NULL;
+    g_list_free (details->new_icons);
+    details->new_icons = NULL;
+    g_list_free (details->selection);
+    details->selection = NULL;
+
+    g_hash_table_destroy (details->icon_set);
+    details->icon_set = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+    nautilus_canvas_container_update_scroll_region (container);
+}
+
+gboolean
+nautilus_canvas_container_is_empty (NautilusCanvasContainer *container)
+{
+    return container->details->icons == NULL;
+}
+
+NautilusCanvasIconData *
+nautilus_canvas_container_get_first_visible_icon (NautilusCanvasContainer *container)
+{
+    GList *l;
+    NautilusCanvasIcon *icon, *best_icon;
+    double x, y;
+    double x1, y1, x2, y2;
+    double *pos, best_pos;
+    double hadj_v, vadj_v, h_page_size;
+    gboolean better_icon;
+    gboolean compare_lt;
+
+    hadj_v = gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
+    vadj_v = gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));
+    h_page_size = gtk_adjustment_get_page_size (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
+
+    if (nautilus_canvas_container_is_layout_rtl (container))
+    {
+        x = hadj_v + h_page_size - ICON_PAD_LEFT - 1;
+        y = vadj_v;
+    }
+    else
+    {
+        x = hadj_v;
+        y = vadj_v;
+    }
+
+    eel_canvas_c2w (EEL_CANVAS (container),
+                    x, y,
+                    &x, &y);
+
+    l = container->details->icons;
+    best_icon = NULL;
+    best_pos = 0;
+    while (l != NULL)
+    {
+        icon = l->data;
+
+        if (icon_is_positioned (icon))
+        {
+            eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon->item),
+                                        &x1, &y1, &x2, &y2);
+
+            compare_lt = FALSE;
+            pos = &y1;
+            better_icon = y2 > y + ICON_PAD_TOP;
+            if (better_icon)
+            {
+                if (best_icon == NULL)
+                {
+                    better_icon = TRUE;
+                }
+                else if (compare_lt)
+                {
+                    better_icon = best_pos < *pos;
+                }
+                else
+                {
+                    better_icon = best_pos > *pos;
+                }
+
+                if (better_icon)
+                {
+                    best_icon = icon;
+                    best_pos = *pos;
+                }
+            }
+        }
+
+        l = l->next;
+    }
+
+    return best_icon ? best_icon->data : NULL;
+}
+
+NautilusCanvasIconData *
+nautilus_canvas_container_get_focused_icon (NautilusCanvasContainer *container)
+{
+    NautilusCanvasIcon *icon;
+
+    icon = container->details->focus;
+
+    if (icon != NULL)
+    {
+        return icon->data;
+    }
+
+    return NULL;
+}
+
+/* puts the icon at the top of the screen */
+void
+nautilus_canvas_container_scroll_to_canvas (NautilusCanvasContainer *container,
+                                            NautilusCanvasIconData  *data)
+{
+    GList *l;
+    NautilusCanvasIcon *icon;
+    GtkAdjustment *vadj;
+    EelIRect bounds;
+    GtkAllocation allocation;
+
+    vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
+    gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
+
+    /* We need to force a relayout now if there are updates queued
+     * since we need the final positions */
+    nautilus_canvas_container_layout_now (container);
+
+    l = container->details->icons;
+    while (l != NULL)
+    {
+        icon = l->data;
+
+        if (icon->data == data &&
+            icon_is_positioned (icon))
+        {
+            /* ensure that we reveal the entire row/column */
+            icon_get_row_and_column_bounds (container, icon, &bounds);
+
+            gtk_adjustment_set_value (vadj, bounds.y0);
+        }
+
+        l = l->next;
+    }
+}
+
+/* Call a function for all the icons. */
+typedef struct
+{
+    NautilusCanvasCallback callback;
+    gpointer callback_data;
+} CallbackAndData;
+
+static void
+call_canvas_callback (gpointer data,
+                      gpointer callback_data)
+{
+    NautilusCanvasIcon *icon;
+    CallbackAndData *callback_and_data;
+
+    icon = data;
+    callback_and_data = callback_data;
+    (*callback_and_data->callback)(icon->data, callback_and_data->callback_data);
+}
+
+void
+nautilus_canvas_container_for_each (NautilusCanvasContainer *container,
+                                    NautilusCanvasCallback   callback,
+                                    gpointer                 callback_data)
+{
+    CallbackAndData callback_and_data;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    callback_and_data.callback = callback;
+    callback_and_data.callback_data = callback_data;
+
+    g_list_foreach (container->details->icons,
+                    call_canvas_callback, &callback_and_data);
+}
+
+static int
+selection_changed_at_idle_callback (gpointer data)
+{
+    NautilusCanvasContainer *container;
+
+    container = NAUTILUS_CANVAS_CONTAINER (data);
+
+    g_signal_emit (container,
+                   signals[SELECTION_CHANGED], 0);
+
+    container->details->selection_changed_id = 0;
+    return FALSE;
+}
+
+/* utility routine to remove a single icon from the container */
+
+static void
+icon_destroy (NautilusCanvasContainer *container,
+              NautilusCanvasIcon      *icon)
+{
+    NautilusCanvasContainerDetails *details;
+    gboolean was_selected;
+    NautilusCanvasIcon *icon_to_focus;
+    GList *item;
+
+    details = container->details;
+
+    item = g_list_find (details->icons, icon);
+    item = item->next ? item->next : item->prev;
+    icon_to_focus = (item != NULL) ? item->data : NULL;
+
+    details->icons = g_list_remove (details->icons, icon);
+    details->new_icons = g_list_remove (details->new_icons, icon);
+    details->selection = g_list_remove (details->selection, icon->data);
+    g_hash_table_remove (details->icon_set, icon->data);
+
+    was_selected = icon->is_selected;
+
+    if (details->focus == icon ||
+        details->focus == NULL)
+    {
+        if (icon_to_focus != NULL)
+        {
+            set_focus (container, icon_to_focus, TRUE);
+        }
+        else
+        {
+            clear_focus (container);
+        }
+    }
+
+    if (details->keyboard_rubberband_start == icon)
+    {
+        clear_keyboard_rubberband_start (container);
+    }
+
+    if (details->keyboard_icon_to_reveal == icon)
+    {
+        unschedule_keyboard_icon_reveal (container);
+    }
+    if (details->drag_icon == icon)
+    {
+        clear_drag_state (container);
+    }
+    if (details->drop_target == icon)
+    {
+        details->drop_target = NULL;
+    }
+    if (details->range_selection_base_icon == icon)
+    {
+        details->range_selection_base_icon = NULL;
+    }
+    if (details->pending_icon_to_reveal == icon)
+    {
+        set_pending_icon_to_reveal (container, NULL);
+    }
+
+    icon_free (icon);
+
+    if (was_selected)
+    {
+        /* Coalesce multiple removals causing multiple selection_changed events */
+        details->selection_changed_id = g_idle_add (selection_changed_at_idle_callback, container);
+    }
+}
+
+/* activate any selected items in the container */
+static void
+activate_selected_items (NautilusCanvasContainer *container)
+{
+    GList *selection;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    selection = nautilus_canvas_container_get_selection (container);
+    if (selection != NULL)
+    {
+        g_signal_emit (container,
+                       signals[ACTIVATE], 0,
+                       selection);
+    }
+    g_list_free (selection);
+}
+
+static void
+preview_selected_items (NautilusCanvasContainer *container)
+{
+    GList *selection;
+    GArray *locations;
+    gint idx;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    selection = nautilus_canvas_container_get_selection (container);
+    locations = nautilus_canvas_container_get_selected_icon_locations (container);
+
+    for (idx = 0; idx < locations->len; idx++)
+    {
+        GdkPoint *point = &(g_array_index (locations, GdkPoint, idx));
+        gint scroll_x, scroll_y;
+
+        eel_canvas_get_scroll_offsets (EEL_CANVAS (container),
+                                       &scroll_x, &scroll_y);
+
+        point->x -= scroll_x;
+        point->y -= scroll_y;
+    }
+
+    if (selection != NULL)
+    {
+        g_signal_emit (container,
+                       signals[ACTIVATE_PREVIEWER], 0,
+                       selection, locations);
+    }
+    g_list_free (selection);
+    g_array_unref (locations);
+}
+
+static void
+activate_selected_items_alternate (NautilusCanvasContainer *container,
+                                   NautilusCanvasIcon      *icon)
+{
+    GList *selection;
+
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    if (icon != NULL)
+    {
+        selection = g_list_prepend (NULL, icon->data);
+    }
+    else
+    {
+        selection = nautilus_canvas_container_get_selection (container);
+    }
+    if (selection != NULL)
+    {
+        g_signal_emit (container,
+                       signals[ACTIVATE_ALTERNATE], 0,
+                       selection);
+    }
+    g_list_free (selection);
+}
+
+static NautilusIconInfo *
+nautilus_canvas_container_get_icon_images (NautilusCanvasContainer *container,
+                                           NautilusCanvasIconData  *data,
+                                           int                      size,
+                                           gboolean                 for_drag_accept)
+{
+    NautilusCanvasContainerClass *klass;
+
+    klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
+    g_assert (klass->get_icon_images != NULL);
+
+    return klass->get_icon_images (container, data, size, for_drag_accept);
+}
+
+static void
+nautilus_canvas_container_prioritize_thumbnailing (NautilusCanvasContainer *container,
+                                                   NautilusCanvasIcon      *icon)
+{
+    NautilusCanvasContainerClass *klass;
+
+    klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
+    g_assert (klass->prioritize_thumbnailing != NULL);
+
+    klass->prioritize_thumbnailing (container, icon->data);
+}
+
+static void
+nautilus_canvas_container_update_visible_icons (NautilusCanvasContainer *container)
+{
+    GtkAdjustment *vadj, *hadj;
+    double min_y, max_y;
+    double min_x, max_x;
+    double x0, y0, x1, y1;
+    GList *node;
+    NautilusCanvasIcon *icon;
+    gboolean visible;
+    GtkAllocation allocation;
+
+    hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
+    vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
+    gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
+
+    min_x = gtk_adjustment_get_value (hadj);
+    max_x = min_x + allocation.width;
+
+    min_y = gtk_adjustment_get_value (vadj);
+    max_y = min_y + allocation.height;
+
+    eel_canvas_c2w (EEL_CANVAS (container),
+                    min_x, min_y, &min_x, &min_y);
+    eel_canvas_c2w (EEL_CANVAS (container),
+                    max_x, max_y, &max_x, &max_y);
+
+    /* Do the iteration in reverse to get the render-order from top to
+     * bottom for the prioritized thumbnails.
+     */
+    for (node = g_list_last (container->details->icons); node != NULL; node = node->prev)
+    {
+        icon = node->data;
+
+        if (icon_is_positioned (icon))
+        {
+            eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon->item),
+                                        &x0,
+                                        &y0,
+                                        &x1,
+                                        &y1);
+            eel_canvas_item_i2w (EEL_CANVAS_ITEM (icon->item)->parent,
+                                 &x0,
+                                 &y0);
+            eel_canvas_item_i2w (EEL_CANVAS_ITEM (icon->item)->parent,
+                                 &x1,
+                                 &y1);
+
+            visible = y1 >= min_y && y0 <= max_y;
+
+            if (visible)
+            {
+                nautilus_canvas_item_set_is_visible (icon->item, TRUE);
+                nautilus_canvas_container_prioritize_thumbnailing (container,
+                                                                   icon);
+            }
+            else
+            {
+                nautilus_canvas_item_set_is_visible (icon->item, FALSE);
+            }
+        }
+    }
+}
+
+static void
+handle_vadjustment_changed (GtkAdjustment           *adjustment,
+                            NautilusCanvasContainer *container)
+{
+    nautilus_canvas_container_update_visible_icons (container);
+}
+
+static void
+handle_hadjustment_changed (GtkAdjustment           *adjustment,
+                            NautilusCanvasContainer *container)
+{
+    nautilus_canvas_container_update_visible_icons (container);
+}
+
+
+void
+nautilus_canvas_container_update_icon (NautilusCanvasContainer *container,
+                                       NautilusCanvasIcon      *icon)
+{
+    NautilusCanvasContainerDetails *details;
+    guint icon_size;
+    guint min_image_size, max_image_size;
+    NautilusIconInfo *icon_info;
+    GdkPixbuf *pixbuf;
+    char *editable_text, *additional_text;
+
+    if (icon == NULL)
+    {
+        return;
+    }
+
+    details = container->details;
+
+    /* compute the maximum size based on the scale factor */
+    min_image_size = MINIMUM_IMAGE_SIZE * EEL_CANVAS (container)->pixels_per_unit;
+    max_image_size = MAX (MAXIMUM_IMAGE_SIZE * EEL_CANVAS (container)->pixels_per_unit, 
NAUTILUS_ICON_MAXIMUM_SIZE);
+
+    /* Get the appropriate images for the file. */
+    icon_get_size (container, icon, &icon_size);
+
+    icon_size = MAX (icon_size, min_image_size);
+    icon_size = MIN (icon_size, max_image_size);
+
+    DEBUG ("Icon size, getting for size %d", icon_size);
+
+    /* Get the icons. */
+    icon_info = nautilus_canvas_container_get_icon_images (container, icon->data, icon_size,
+                                                           icon == details->drop_target);
+
+    pixbuf = nautilus_icon_info_get_pixbuf (icon_info);
+    g_object_unref (icon_info);
+
+    nautilus_canvas_container_get_icon_text (container,
+                                             icon->data,
+                                             &editable_text,
+                                             &additional_text,
+                                             FALSE);
+
+    eel_canvas_item_set (EEL_CANVAS_ITEM (icon->item),
+                         "editable_text", editable_text,
+                         "additional_text", additional_text,
+                         "highlighted_for_drop", icon == details->drop_target,
+                         NULL);
+
+    nautilus_canvas_item_set_image (icon->item, pixbuf);
+
+    /* Let the pixbufs go. */
+    g_object_unref (pixbuf);
+
+    g_free (editable_text);
+    g_free (additional_text);
+}
+
+static void
+finish_adding_icon (NautilusCanvasContainer *container,
+                    NautilusCanvasIcon      *icon)
+{
+    nautilus_canvas_container_update_icon (container, icon);
+    eel_canvas_item_show (EEL_CANVAS_ITEM (icon->item));
+
+    g_signal_connect_object (icon->item, "event",
+                             G_CALLBACK (item_event_callback), container, 0);
+
+    g_signal_emit (container, signals[ICON_ADDED], 0, icon->data);
+}
+
+static gboolean
+finish_adding_new_icons (NautilusCanvasContainer *container)
+{
+    GList *p, *new_icons;
+
+    new_icons = container->details->new_icons;
+    container->details->new_icons = NULL;
+    container->details->is_populating_container = g_list_length (new_icons) ==
+                                                  g_hash_table_size (container->details->icon_set);
+
+    /* Position most icons (not unpositioned manual-layout icons). */
+    new_icons = g_list_reverse (new_icons);
+    for (p = new_icons; p != NULL; p = p->next)
+    {
+        finish_adding_icon (container, p->data);
+    }
+    g_list_free (new_icons);
+
+    return TRUE;
+}
+
+/**
+ * nautilus_canvas_container_add:
+ * @container: A NautilusCanvasContainer
+ * @data: Icon data.
+ *
+ * Add icon to represent @data to container.
+ * Returns FALSE if there was already such an icon.
+ **/
+gboolean
+nautilus_canvas_container_add (NautilusCanvasContainer *container,
+                               NautilusCanvasIconData  *data)
+{
+    NautilusCanvasContainerDetails *details;
+    NautilusCanvasIcon *icon;
+    EelCanvasItem *band, *item;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), FALSE);
+    g_return_val_if_fail (data != NULL, FALSE);
+
+    details = container->details;
+
+    if (g_hash_table_lookup (details->icon_set, data) != NULL)
+    {
+        return FALSE;
+    }
+
+    /* Create the new icon, including the canvas item. */
+    icon = g_new0 (NautilusCanvasIcon, 1);
+    icon->data = data;
+    icon->x = ICON_UNPOSITIONED_VALUE;
+    icon->y = ICON_UNPOSITIONED_VALUE;
+
+    /* Whether the saved icon position should only be used
+     * if the previous icon position is free. If the position
+     * is occupied, another position near the last one will
+     */
+    icon->item = NAUTILUS_CANVAS_ITEM
+                     (eel_canvas_item_new (EEL_CANVAS_GROUP (EEL_CANVAS (container)->root),
+                                           nautilus_canvas_item_get_type (),
+                                           "visible", FALSE,
+                                           NULL));
+    icon->item->user_data = icon;
+
+    /* Make sure the icon is under the selection_rectangle */
+    item = EEL_CANVAS_ITEM (icon->item);
+    band = NAUTILUS_CANVAS_CONTAINER (item->canvas)->details->rubberband_info.selection_rectangle;
+    if (band)
+    {
+        eel_canvas_item_send_behind (item, band);
+    }
+
+    /* Put it on both lists. */
+    details->icons = g_list_prepend (details->icons, icon);
+    details->new_icons = g_list_prepend (details->new_icons, icon);
+
+    g_hash_table_insert (details->icon_set, data, icon);
+
+    details->needs_resort = TRUE;
+
+    /* Run an idle function to add the icons. */
+    schedule_redo_layout (container);
+
+    return TRUE;
+}
+
+void
+nautilus_canvas_container_layout_now (NautilusCanvasContainer *container)
+{
+    container->details->in_layout_now = TRUE;
+    if (container->details->idle_id != 0)
+    {
+        unschedule_redo_layout (container);
+        redo_layout_internal (container);
+    }
+
+    /* Also need to make sure we're properly resized, for instance
+     * newly added files may trigger a change in the size allocation and
+     * thus toggle scrollbars on */
+    gtk_container_check_resize (GTK_CONTAINER (gtk_widget_get_parent (GTK_WIDGET (container))));
+    container->details->in_layout_now = FALSE;
+}
+
+/**
+ * nautilus_canvas_container_remove:
+ * @container: A NautilusCanvasContainer.
+ * @data: Icon data.
+ *
+ * Remove the icon with this data.
+ **/
+gboolean
+nautilus_canvas_container_remove (NautilusCanvasContainer *container,
+                                  NautilusCanvasIconData  *data)
+{
+    NautilusCanvasIcon *icon;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), FALSE);
+    g_return_val_if_fail (data != NULL, FALSE);
+
+    icon = g_hash_table_lookup (container->details->icon_set, data);
+
+    if (icon == NULL)
+    {
+        return FALSE;
+    }
+
+    icon_destroy (container, icon);
+    schedule_redo_layout (container);
+
+    g_signal_emit (container, signals[ICON_REMOVED], 0, icon);
+
+    return TRUE;
+}
+
+/**
+ * nautilus_canvas_container_request_update:
+ * @container: A NautilusCanvasContainer.
+ * @data: Icon data.
+ *
+ * Update the icon with this data.
+ **/
+void
+nautilus_canvas_container_request_update (NautilusCanvasContainer *container,
+                                          NautilusCanvasIconData  *data)
+{
+    NautilusCanvasIcon *icon;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+    g_return_if_fail (data != NULL);
+
+    icon = g_hash_table_lookup (container->details->icon_set, data);
+
+    if (icon != NULL)
+    {
+        nautilus_canvas_container_update_icon (container, icon);
+        container->details->needs_resort = TRUE;
+        schedule_redo_layout (container);
+    }
+}
+
+/* zooming */
+
+NautilusCanvasZoomLevel
+nautilus_canvas_container_get_zoom_level (NautilusCanvasContainer *container)
+{
+    return container->details->zoom_level;
+}
+
+void
+nautilus_canvas_container_set_zoom_level (NautilusCanvasContainer *container,
+                                          int                      new_level)
+{
+    NautilusCanvasContainerDetails *details;
+    int pinned_level;
+    double pixels_per_unit;
+
+    details = container->details;
+
+    pinned_level = new_level;
+    if (pinned_level < NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL)
+    {
+        pinned_level = NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL;
+    }
+    else if (pinned_level > NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER)
+    {
+        pinned_level = NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER;
+    }
+
+    if (pinned_level == details->zoom_level)
+    {
+        return;
+    }
+
+    details->zoom_level = pinned_level;
+
+    pixels_per_unit = (double) nautilus_canvas_container_get_icon_size_for_zoom_level (pinned_level)
+                      / NAUTILUS_CANVAS_ICON_SIZE_STANDARD;
+    eel_canvas_set_pixels_per_unit (EEL_CANVAS (container), pixels_per_unit);
+
+    nautilus_canvas_container_request_update_all_internal (container, TRUE);
+}
+
+/**
+ * nautilus_canvas_container_request_update_all:
+ * For each icon, synchronizes the displayed information (image, text) with the
+ * information from the model.
+ *
+ * @container: An canvas container.
+ **/
+void
+nautilus_canvas_container_request_update_all (NautilusCanvasContainer *container)
+{
+    nautilus_canvas_container_request_update_all_internal (container, FALSE);
+}
+
+/**
+ * nautilus_canvas_container_reveal:
+ * Change scroll position as necessary to reveal the specified item.
+ */
+void
+nautilus_canvas_container_reveal (NautilusCanvasContainer *container,
+                                  NautilusCanvasIconData  *data)
+{
+    NautilusCanvasIcon *icon;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+    g_return_if_fail (data != NULL);
+
+    icon = g_hash_table_lookup (container->details->icon_set, data);
+
+    if (icon != NULL)
+    {
+        reveal_icon (container, icon);
+    }
+}
+
+/**
+ * nautilus_canvas_container_get_selection:
+ * @container: An canvas container.
+ *
+ * Get a list of the icons currently selected in @container.
+ *
+ * Return value: A GList of the programmer-specified data associated to each
+ * selected icon, or NULL if no canvas is selected.  The caller is expected to
+ * free the list when it is not needed anymore.
+ **/
+GList *
+nautilus_canvas_container_get_selection (NautilusCanvasContainer *container)
+{
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), NULL);
+
+    if (container->details->selection_needs_resort)
+    {
+        sort_selection (container);
+    }
+
+    return g_list_copy (container->details->selection);
+}
+
+static GList *
+nautilus_canvas_container_get_selected_icons (NautilusCanvasContainer *container)
+{
+    GList *list, *p;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), NULL);
+
+    list = NULL;
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        NautilusCanvasIcon *icon;
+
+        icon = p->data;
+        if (icon->is_selected)
+        {
+            list = g_list_prepend (list, icon);
+        }
+    }
+
+    return g_list_reverse (list);
+}
+
+/**
+ * nautilus_canvas_container_invert_selection:
+ * @container: An canvas container.
+ *
+ * Inverts the selection in @container.
+ *
+ **/
+void
+nautilus_canvas_container_invert_selection (NautilusCanvasContainer *container)
+{
+    GList *p;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        NautilusCanvasIcon *icon;
+
+        icon = p->data;
+        icon_toggle_selected (container, icon);
+    }
+
+    g_signal_emit (container, signals[SELECTION_CHANGED], 0);
+}
+
+
+/* Returns an array of GdkPoints of locations of the icons. */
+static GArray *
+nautilus_canvas_container_get_icon_locations (NautilusCanvasContainer *container,
+                                              GList                   *icons)
+{
+    GArray *result;
+    GList *node;
+    int index;
+
+    result = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
+    result = g_array_set_size (result, g_list_length (icons));
+
+    for (index = 0, node = icons; node != NULL; index++, node = node->next)
+    {
+        g_array_index (result, GdkPoint, index).x =
+            ((NautilusCanvasIcon *) node->data)->x;
+        g_array_index (result, GdkPoint, index).y =
+            ((NautilusCanvasIcon *) node->data)->y;
+    }
+
+    return result;
+}
+
+/* Returns a GdkRectangle of the icon. The bounding box is adjusted with the
+ * pixels_per_unit already, so they are the final positions on the canvas */
+GdkRectangle *
+nautilus_canvas_container_get_icon_bounding_box (NautilusCanvasContainer *container,
+                                                 NautilusCanvasIconData  *data)
+{
+    NautilusCanvasIcon *icon;
+    int x1, x2, y1, y2;
+    GdkRectangle *bounding_box;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), NULL);
+    g_return_val_if_fail (data != NULL, NULL);
+
+    icon = g_hash_table_lookup (container->details->icon_set, data);
+    icon_get_bounding_box (icon,
+                           &x1, &y1, &x2, &y2,
+                           BOUNDS_USAGE_FOR_DISPLAY);
+    bounding_box = g_malloc0 (sizeof (GdkRectangle));
+    bounding_box->x = x1 * EEL_CANVAS (container)->pixels_per_unit;
+    bounding_box->width = (x2 - x1) * EEL_CANVAS (container)->pixels_per_unit;
+    bounding_box->y = y1 * EEL_CANVAS (container)->pixels_per_unit;
+    bounding_box->height = (y2 - y1) * EEL_CANVAS (container)->pixels_per_unit;
+
+    return bounding_box;
+}
+
+/**
+ * nautilus_canvas_container_get_selected_icon_locations:
+ * @container: An canvas container widget.
+ *
+ * Returns an array of GdkPoints of locations of the selected icons.
+ **/
+GArray *
+nautilus_canvas_container_get_selected_icon_locations (NautilusCanvasContainer *container)
+{
+    GArray *result;
+    GList *icons;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), NULL);
+
+    icons = nautilus_canvas_container_get_selected_icons (container);
+    result = nautilus_canvas_container_get_icon_locations (container, icons);
+    g_list_free (icons);
+
+    return result;
+}
+
+/**
+ * nautilus_canvas_container_select_all:
+ * @container: An canvas container widget.
+ *
+ * Select all the icons in @container at once.
+ **/
+void
+nautilus_canvas_container_select_all (NautilusCanvasContainer *container)
+{
+    gboolean selection_changed;
+    GList *p;
+    NautilusCanvasIcon *icon;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    selection_changed = FALSE;
+
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+
+        selection_changed |= icon_set_selected (container, icon, TRUE);
+    }
+
+    if (selection_changed)
+    {
+        g_signal_emit (container,
+                       signals[SELECTION_CHANGED], 0);
+    }
+}
+
+/**
+ * nautilus_canvas_container_select_first:
+ * @container: An canvas container widget.
+ *
+ * Select the first icon in @container.
+ **/
+void
+nautilus_canvas_container_select_first (NautilusCanvasContainer *container)
+{
+    gboolean selection_changed;
+    NautilusCanvasIcon *icon;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    selection_changed = FALSE;
+
+    if (container->details->needs_resort)
+    {
+        resort (container);
+        container->details->needs_resort = FALSE;
+    }
+
+    icon = g_list_nth_data (container->details->icons, 0);
+    if (icon)
+    {
+        selection_changed |= icon_set_selected (container, icon, TRUE);
+    }
+
+    if (selection_changed)
+    {
+        g_signal_emit (container,
+                       signals[SELECTION_CHANGED], 0);
+    }
+}
+
+/**
+ * nautilus_canvas_container_set_selection:
+ * @container: An canvas container widget.
+ * @selection: A list of NautilusCanvasIconData *.
+ *
+ * Set the selection to exactly the icons in @container which have
+ * programmer data matching one of the items in @selection.
+ **/
+void
+nautilus_canvas_container_set_selection (NautilusCanvasContainer *container,
+                                         GList                   *selection)
+{
+    gboolean selection_changed;
+    GHashTable *hash;
+    GList *p;
+    gboolean res;
+    NautilusCanvasIcon *icon, *selected_icon;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    selection_changed = FALSE;
+    selected_icon = NULL;
+
+    hash = g_hash_table_new (NULL, NULL);
+    for (p = selection; p != NULL; p = p->next)
+    {
+        g_hash_table_insert (hash, p->data, p->data);
+    }
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+
+        res = icon_set_selected
+                  (container, icon,
+                  g_hash_table_lookup (hash, icon->data) != NULL);
+        selection_changed |= res;
+
+        if (res)
+        {
+            selected_icon = icon;
+        }
+    }
+    g_hash_table_destroy (hash);
+
+    if (selection_changed)
+    {
+        /* if only one item has been selected, use it as range
+         * selection base (cf. handle_canvas_button_press) */
+        if (g_list_length (selection) == 1)
+        {
+            container->details->range_selection_base_icon = selected_icon;
+        }
+
+        g_signal_emit (container,
+                       signals[SELECTION_CHANGED], 0);
+    }
+}
+
+/**
+ * nautilus_canvas_container_select_list_unselect_others.
+ * @container: An canvas container widget.
+ * @selection: A list of NautilusCanvasIcon *.
+ *
+ * Set the selection to exactly the icons in @selection.
+ **/
+void
+nautilus_canvas_container_select_list_unselect_others (NautilusCanvasContainer *container,
+                                                       GList                   *selection)
+{
+    gboolean selection_changed;
+    GHashTable *hash;
+    GList *p;
+    NautilusCanvasIcon *icon;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    selection_changed = FALSE;
+
+    hash = g_hash_table_new (NULL, NULL);
+    for (p = selection; p != NULL; p = p->next)
+    {
+        g_hash_table_insert (hash, p->data, p->data);
+    }
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+
+        selection_changed |= icon_set_selected
+                                 (container, icon,
+                                 g_hash_table_lookup (hash, icon) != NULL);
+    }
+    g_hash_table_destroy (hash);
+
+    if (selection_changed)
+    {
+        g_signal_emit (container,
+                       signals[SELECTION_CHANGED], 0);
+    }
+}
+
+/**
+ * nautilus_canvas_container_unselect_all:
+ * @container: An canvas container widget.
+ *
+ * Deselect all the icons in @container.
+ **/
+void
+nautilus_canvas_container_unselect_all (NautilusCanvasContainer *container)
+{
+    if (unselect_all (container))
+    {
+        g_signal_emit (container,
+                       signals[SELECTION_CHANGED], 0);
+    }
+}
+
+/**
+ * nautilus_canvas_container_get_icon_by_uri:
+ * @container: An canvas container widget.
+ * @uri: The uri of an canvas to find.
+ *
+ * Locate an icon, given the URI. The URI must match exactly.
+ * Later we may have to have some way of figuring out if the
+ * URI specifies the same object that does not require an exact match.
+ **/
+NautilusCanvasIcon *
+nautilus_canvas_container_get_icon_by_uri (NautilusCanvasContainer *container,
+                                           const char              *uri)
+{
+    NautilusCanvasContainerDetails *details;
+    GList *p;
+
+    /* Eventually, we must avoid searching the entire canvas list,
+     *  but it's OK for now.
+     *  A hash table mapping uri to canvas is one possibility.
+     */
+
+    details = container->details;
+
+    for (p = details->icons; p != NULL; p = p->next)
+    {
+        NautilusCanvasIcon *icon;
+        char *icon_uri;
+        gboolean is_match;
+
+        icon = p->data;
+
+        icon_uri = nautilus_canvas_container_get_icon_uri
+                       (container, icon);
+        is_match = strcmp (uri, icon_uri) == 0;
+        g_free (icon_uri);
+
+        if (is_match)
+        {
+            return icon;
+        }
+    }
+
+    return NULL;
+}
+
+static NautilusCanvasIcon *
+get_nth_selected_icon (NautilusCanvasContainer *container,
+                       int                      index)
+{
+    GList *p;
+    NautilusCanvasIcon *icon;
+    int selection_count;
+
+    g_assert (index > 0);
+
+    /* Find the nth selected icon. */
+    selection_count = 0;
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+        if (icon->is_selected)
+        {
+            if (++selection_count == index)
+            {
+                return icon;
+            }
+        }
+    }
+    return NULL;
+}
+
+static NautilusCanvasIcon *
+get_first_selected_icon (NautilusCanvasContainer *container)
+{
+    return get_nth_selected_icon (container, 1);
+}
+
+static gboolean
+has_multiple_selection (NautilusCanvasContainer *container)
+{
+    return get_nth_selected_icon (container, 2) != NULL;
+}
+
+static gboolean
+all_selected (NautilusCanvasContainer *container)
+{
+    GList *p;
+    NautilusCanvasIcon *icon;
+
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+        if (!icon->is_selected)
+        {
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+
+static gboolean
+has_selection (NautilusCanvasContainer *container)
+{
+    return get_nth_selected_icon (container, 1) != NULL;
+}
+
+char *
+nautilus_canvas_container_get_icon_uri (NautilusCanvasContainer *container,
+                                        NautilusCanvasIcon      *icon)
+{
+    char *uri;
+
+    uri = NULL;
+    g_signal_emit (container,
+                   signals[GET_ICON_URI], 0,
+                   icon->data,
+                   &uri);
+    return uri;
+}
+
+char *
+nautilus_canvas_container_get_icon_activation_uri (NautilusCanvasContainer *container,
+                                                   NautilusCanvasIcon      *icon)
+{
+    char *uri;
+
+    uri = NULL;
+    g_signal_emit (container,
+                   signals[GET_ICON_ACTIVATION_URI], 0,
+                   icon->data,
+                   &uri);
+    return uri;
+}
+
+char *
+nautilus_canvas_container_get_icon_drop_target_uri (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIcon      *icon)
+{
+    char *uri;
+
+    uri = NULL;
+    g_signal_emit (container,
+                   signals[GET_ICON_DROP_TARGET_URI], 0,
+                   icon->data,
+                   &uri);
+    return uri;
+}
+
+/* Re-sort, switching to automatic layout if it was in manual layout. */
+void
+nautilus_canvas_container_sort (NautilusCanvasContainer *container)
+{
+    container->details->needs_resort = TRUE;
+    redo_layout (container);
+}
+
+void
+nautilus_canvas_container_set_single_click_mode (NautilusCanvasContainer *container,
+                                                 gboolean                 single_click_mode)
+{
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    container->details->single_click_mode = single_click_mode;
+}
+
+/* handle theme changes */
+
+void
+nautilus_canvas_container_set_font (NautilusCanvasContainer *container,
+                                    const char              *font)
+{
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    if (g_strcmp0 (container->details->font, font) == 0)
+    {
+        return;
+    }
+
+    g_free (container->details->font);
+    container->details->font = g_strdup (font);
+
+    nautilus_canvas_container_request_update_all_internal (container, TRUE);
+    gtk_widget_queue_draw (GTK_WIDGET (container));
+}
+
+/**
+ * nautilus_canvas_container_get_icon_description
+ * @container: An canvas container widget.
+ * @data: Icon data
+ *
+ * Gets the description for the icon. This function may return NULL.
+ **/
+char *
+nautilus_canvas_container_get_icon_description (NautilusCanvasContainer *container,
+                                                NautilusCanvasIconData  *data)
+{
+    NautilusCanvasContainerClass *klass;
+
+    klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
+
+    if (klass->get_icon_description)
+    {
+        return klass->get_icon_description (container, data);
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
+/**
+ * nautilus_canvas_container_set_highlighted_for_clipboard
+ * @container: An canvas container widget.
+ * @data: Canvas Data associated with all icons that should be highlighted.
+ *        Others will be unhighlighted.
+ **/
+void
+nautilus_canvas_container_set_highlighted_for_clipboard (NautilusCanvasContainer *container,
+                                                         GList                   *clipboard_canvas_data)
+{
+    GList *l;
+    NautilusCanvasIcon *icon;
+    gboolean highlighted_for_clipboard;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    for (l = container->details->icons; l != NULL; l = l->next)
+    {
+        icon = l->data;
+        highlighted_for_clipboard = (g_list_find (clipboard_canvas_data, icon->data) != NULL);
+
+        eel_canvas_item_set (EEL_CANVAS_ITEM (icon->item),
+                             "highlighted-for-clipboard", highlighted_for_clipboard,
+                             NULL);
+    }
+}
+
+/* NautilusCanvasContainerAccessible */
+typedef struct
+{
+    EelCanvasAccessible parent;
+    NautilusCanvasContainerAccessiblePrivate *priv;
+} NautilusCanvasContainerAccessible;
+
+typedef EelCanvasAccessibleClass NautilusCanvasContainerAccessibleClass;
+
+#define GET_ACCESSIBLE_PRIV(o) ((NautilusCanvasContainerAccessible *) o)->priv
+
+/* AtkAction interface */
+static gboolean
+nautilus_canvas_container_accessible_do_action (AtkAction *accessible,
+                                                int        i)
+{
+    GtkWidget *widget;
+    NautilusCanvasContainer *container;
+    GList *selection;
+
+    g_return_val_if_fail (i < LAST_ACTION, FALSE);
+
+    widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
+    if (!widget)
+    {
+        return FALSE;
+    }
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+    switch (i)
+    {
+        case ACTION_ACTIVATE:
+        {
+            selection = nautilus_canvas_container_get_selection (container);
+
+            if (selection)
+            {
+                g_signal_emit_by_name (container, "activate", selection);
+                g_list_free (selection);
+            }
+        }
+        break;
+
+        case ACTION_MENU:
+        {
+            handle_popups (container, NULL, "context_click_background");
+        }
+        break;
+
+        default:
+        {
+            g_warning ("Invalid action passed to NautilusCanvasContainerAccessible::do_action");
+            return FALSE;
+        }
+        break;
+    }
+    return TRUE;
+}
+
+static int
+nautilus_canvas_container_accessible_get_n_actions (AtkAction *accessible)
+{
+    return LAST_ACTION;
+}
+
+static const char *
+nautilus_canvas_container_accessible_action_get_description (AtkAction *accessible,
+                                                             int        i)
+{
+    NautilusCanvasContainerAccessiblePrivate *priv;
+
+    g_assert (i < LAST_ACTION);
+
+    priv = GET_ACCESSIBLE_PRIV (accessible);
+
+    if (priv->action_descriptions[i])
+    {
+        return priv->action_descriptions[i];
+    }
+    else
+    {
+        return nautilus_canvas_container_accessible_action_descriptions[i];
+    }
+}
+
+static const char *
+nautilus_canvas_container_accessible_action_get_name (AtkAction *accessible,
+                                                      int        i)
+{
+    g_assert (i < LAST_ACTION);
+
+    return nautilus_canvas_container_accessible_action_names[i];
+}
+
+static const char *
+nautilus_canvas_container_accessible_action_get_keybinding (AtkAction *accessible,
+                                                            int        i)
+{
+    g_assert (i < LAST_ACTION);
+
+    return NULL;
+}
+
+static gboolean
+nautilus_canvas_container_accessible_action_set_description (AtkAction  *accessible,
+                                                             int         i,
+                                                             const char *description)
+{
+    NautilusCanvasContainerAccessiblePrivate *priv;
+
+    g_assert (i < LAST_ACTION);
+
+    priv = GET_ACCESSIBLE_PRIV (accessible);
+
+    if (priv->action_descriptions[i])
+    {
+        g_free (priv->action_descriptions[i]);
+    }
+    priv->action_descriptions[i] = g_strdup (description);
+
+    return FALSE;
+}
+
+static void
+nautilus_canvas_container_accessible_action_interface_init (AtkActionIface *iface)
+{
+    iface->do_action = nautilus_canvas_container_accessible_do_action;
+    iface->get_n_actions = nautilus_canvas_container_accessible_get_n_actions;
+    iface->get_description = nautilus_canvas_container_accessible_action_get_description;
+    iface->get_name = nautilus_canvas_container_accessible_action_get_name;
+    iface->get_keybinding = nautilus_canvas_container_accessible_action_get_keybinding;
+    iface->set_description = nautilus_canvas_container_accessible_action_set_description;
+}
+
+/* AtkSelection interface */
+
+static void
+nautilus_canvas_container_accessible_update_selection (AtkObject *accessible)
+{
+    NautilusCanvasContainer *container;
+    NautilusCanvasContainerAccessiblePrivate *priv;
+
+    container = NAUTILUS_CANVAS_CONTAINER (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
+    priv = GET_ACCESSIBLE_PRIV (accessible);
+
+    if (priv->selection)
+    {
+        g_list_free (priv->selection);
+        priv->selection = NULL;
+    }
+
+    priv->selection = nautilus_canvas_container_get_selected_icons (container);
+}
+
+static void
+nautilus_canvas_container_accessible_selection_changed_cb (NautilusCanvasContainer *container,
+                                                           gpointer                 data)
+{
+    g_signal_emit_by_name (data, "selection-changed");
+}
+
+static void
+nautilus_canvas_container_accessible_icon_added_cb (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData  *icon_data,
+                                                    gpointer                 data)
+{
+    NautilusCanvasIcon *icon;
+    AtkObject *atk_parent;
+    AtkObject *atk_child;
+
+    /* We don't want to emit children_changed signals during any type of load. */
+    if (!container->details->in_layout_now || container->details->is_populating_container)
+    {
+        return;
+    }
+
+    icon = g_hash_table_lookup (container->details->icon_set, icon_data);
+    if (icon)
+    {
+        atk_parent = ATK_OBJECT (data);
+        atk_child = atk_gobject_accessible_for_object
+                        (G_OBJECT (icon->item));
+
+        g_signal_emit_by_name (atk_parent, "children-changed::add",
+                               icon->position, atk_child, NULL);
+    }
+}
+
+static void
+nautilus_canvas_container_accessible_icon_removed_cb (NautilusCanvasContainer *container,
+                                                      NautilusCanvasIconData  *icon_data,
+                                                      gpointer                 data)
+{
+    NautilusCanvasIcon *icon;
+    AtkObject *atk_parent;
+    AtkObject *atk_child;
+
+    icon = g_hash_table_lookup (container->details->icon_set, icon_data);
+    if (icon)
+    {
+        atk_parent = ATK_OBJECT (data);
+        atk_child = atk_gobject_accessible_for_object
+                        (G_OBJECT (icon->item));
+
+        g_signal_emit_by_name (atk_parent, "children-changed::remove",
+                               icon->position, atk_child, NULL);
+    }
+}
+
+static void
+nautilus_canvas_container_accessible_cleared_cb (NautilusCanvasContainer *container,
+                                                 gpointer                 data)
+{
+    g_signal_emit_by_name (data, "children-changed", 0, NULL, NULL);
+}
+
+static gboolean
+nautilus_canvas_container_accessible_add_selection (AtkSelection *accessible,
+                                                    int           i)
+{
+    GtkWidget *widget;
+    NautilusCanvasContainer *container;
+    GList *l;
+    GList *selection;
+    NautilusCanvasIcon *icon;
+
+    widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
+    if (!widget)
+    {
+        return FALSE;
+    }
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    l = g_list_nth (container->details->icons, i);
+    if (l)
+    {
+        icon = l->data;
+
+        selection = nautilus_canvas_container_get_selection (container);
+        selection = g_list_prepend (selection,
+                                    icon->data);
+        nautilus_canvas_container_set_selection (container, selection);
+
+        g_list_free (selection);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+nautilus_canvas_container_accessible_clear_selection (AtkSelection *accessible)
+{
+    GtkWidget *widget;
+    NautilusCanvasContainer *container;
+
+    widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
+    if (!widget)
+    {
+        return FALSE;
+    }
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    nautilus_canvas_container_unselect_all (container);
+
+    return TRUE;
+}
+
+static AtkObject *
+nautilus_canvas_container_accessible_ref_selection (AtkSelection *accessible,
+                                                    int           i)
+{
+    NautilusCanvasContainerAccessiblePrivate *priv;
+    AtkObject *atk_object;
+    GList *item;
+    NautilusCanvasIcon *icon;
+
+    nautilus_canvas_container_accessible_update_selection (ATK_OBJECT (accessible));
+    priv = GET_ACCESSIBLE_PRIV (accessible);
+
+    item = (g_list_nth (priv->selection, i));
+
+    if (item)
+    {
+        icon = item->data;
+        atk_object = atk_gobject_accessible_for_object (G_OBJECT (icon->item));
+        if (atk_object)
+        {
+            g_object_ref (atk_object);
+        }
+
+        return atk_object;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
+static int
+nautilus_canvas_container_accessible_get_selection_count (AtkSelection *accessible)
+{
+    NautilusCanvasContainerAccessiblePrivate *priv;
+    int count;
+
+    priv = GET_ACCESSIBLE_PRIV (accessible);
+    nautilus_canvas_container_accessible_update_selection (ATK_OBJECT (accessible));
+    count = g_list_length (priv->selection);
+
+    return count;
+}
+
+static gboolean
+nautilus_canvas_container_accessible_is_child_selected (AtkSelection *accessible,
+                                                        int           i)
+{
+    NautilusCanvasContainer *container;
+    GList *l;
+    NautilusCanvasIcon *icon;
+    GtkWidget *widget;
+
+    widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
+    if (!widget)
+    {
+        return FALSE;
+    }
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    l = g_list_nth (container->details->icons, i);
+    if (l)
+    {
+        icon = l->data;
+        return icon->is_selected;
+    }
+    return FALSE;
+}
+
+static gboolean
+nautilus_canvas_container_accessible_remove_selection (AtkSelection *accessible,
+                                                       int           i)
+{
+    NautilusCanvasContainerAccessiblePrivate *priv;
+    NautilusCanvasContainer *container;
+    GList *l;
+    GList *selection;
+    NautilusCanvasIcon *icon;
+    GtkWidget *widget;
+
+    widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
+    if (!widget)
+    {
+        return FALSE;
+    }
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+    nautilus_canvas_container_accessible_update_selection (ATK_OBJECT (accessible));
+
+    priv = GET_ACCESSIBLE_PRIV (accessible);
+    l = g_list_nth (priv->selection, i);
+    if (l)
+    {
+        icon = l->data;
+
+        selection = nautilus_canvas_container_get_selection (container);
+        selection = g_list_remove (selection, icon->data);
+        nautilus_canvas_container_set_selection (container, selection);
+
+        g_list_free (selection);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+nautilus_canvas_container_accessible_select_all_selection (AtkSelection *accessible)
+{
+    NautilusCanvasContainer *container;
+    GtkWidget *widget;
+
+    widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
+    if (!widget)
+    {
+        return FALSE;
+    }
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    nautilus_canvas_container_select_all (container);
+
+    return TRUE;
+}
+
+void
+nautilus_canvas_container_widget_to_file_operation_position (NautilusCanvasContainer *container,
+                                                             GdkPoint                *position)
+{
+    double x, y;
+
+    g_return_if_fail (position != NULL);
+
+    x = position->x;
+    y = position->y;
+
+    eel_canvas_window_to_world (EEL_CANVAS (container), x, y, &x, &y);
+
+    position->x = (int) x;
+    position->y = (int) y;
+
+    /* ensure that we end up in the middle of the icon */
+    position->x -= nautilus_canvas_container_get_icon_size_for_zoom_level (container->details->zoom_level) / 
2;
+    position->y -= nautilus_canvas_container_get_icon_size_for_zoom_level (container->details->zoom_level) / 
2;
+}
+
+static void
+nautilus_canvas_container_accessible_selection_interface_init (AtkSelectionIface *iface)
+{
+    iface->add_selection = nautilus_canvas_container_accessible_add_selection;
+    iface->clear_selection = nautilus_canvas_container_accessible_clear_selection;
+    iface->ref_selection = nautilus_canvas_container_accessible_ref_selection;
+    iface->get_selection_count = nautilus_canvas_container_accessible_get_selection_count;
+    iface->is_child_selected = nautilus_canvas_container_accessible_is_child_selected;
+    iface->remove_selection = nautilus_canvas_container_accessible_remove_selection;
+    iface->select_all_selection = nautilus_canvas_container_accessible_select_all_selection;
+}
+
+
+static gint
+nautilus_canvas_container_accessible_get_n_children (AtkObject *accessible)
+{
+    NautilusCanvasContainer *container;
+    GtkWidget *widget;
+    gint i;
+
+    widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
+    if (!widget)
+    {
+        return FALSE;
+    }
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    i = g_hash_table_size (container->details->icon_set);
+
+    return i;
+}
+
+static AtkObject *
+nautilus_canvas_container_accessible_ref_child (AtkObject *accessible,
+                                                int        i)
+{
+    AtkObject *atk_object;
+    NautilusCanvasContainer *container;
+    GList *item;
+    NautilusCanvasIcon *icon;
+    GtkWidget *widget;
+
+    widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
+    if (!widget)
+    {
+        return NULL;
+    }
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    item = (g_list_nth (container->details->icons, i));
+
+    if (item)
+    {
+        icon = item->data;
+
+        atk_object = atk_gobject_accessible_for_object (G_OBJECT (icon->item));
+        g_object_ref (atk_object);
+
+        return atk_object;
+    }
+    return NULL;
+}
+
+G_DEFINE_TYPE_WITH_CODE (NautilusCanvasContainerAccessible, nautilus_canvas_container_accessible,
+                         eel_canvas_accessible_get_type (),
+                         G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, 
nautilus_canvas_container_accessible_action_interface_init)
+                         G_IMPLEMENT_INTERFACE (ATK_TYPE_SELECTION, 
nautilus_canvas_container_accessible_selection_interface_init))
+
+static void
+nautilus_canvas_container_accessible_initialize (AtkObject *accessible,
+                                                 gpointer   data)
+{
+    NautilusCanvasContainer *container;
+
+    if (ATK_OBJECT_CLASS (nautilus_canvas_container_accessible_parent_class)->initialize)
+    {
+        ATK_OBJECT_CLASS (nautilus_canvas_container_accessible_parent_class)->initialize (accessible, data);
+    }
+
+    if (GTK_IS_ACCESSIBLE (accessible))
+    {
+        nautilus_canvas_container_accessible_update_selection
+            (ATK_OBJECT (accessible));
+
+        container = NAUTILUS_CANVAS_CONTAINER (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
+        g_signal_connect (container, "selection-changed",
+                          G_CALLBACK (nautilus_canvas_container_accessible_selection_changed_cb),
+                          accessible);
+        g_signal_connect (container, "icon-added",
+                          G_CALLBACK (nautilus_canvas_container_accessible_icon_added_cb),
+                          accessible);
+        g_signal_connect (container, "icon-removed",
+                          G_CALLBACK (nautilus_canvas_container_accessible_icon_removed_cb),
+                          accessible);
+        g_signal_connect (container, "cleared",
+                          G_CALLBACK (nautilus_canvas_container_accessible_cleared_cb),
+                          accessible);
+    }
+}
+
+static void
+nautilus_canvas_container_accessible_finalize (GObject *object)
+{
+    NautilusCanvasContainerAccessiblePrivate *priv;
+    int i;
+
+    priv = GET_ACCESSIBLE_PRIV (object);
+
+    if (priv->selection)
+    {
+        g_list_free (priv->selection);
+    }
+
+    for (i = 0; i < LAST_ACTION; i++)
+    {
+        if (priv->action_descriptions[i])
+        {
+            g_free (priv->action_descriptions[i]);
+        }
+    }
+
+    G_OBJECT_CLASS (nautilus_canvas_container_accessible_parent_class)->finalize (object);
+}
+
+static void
+nautilus_canvas_container_accessible_init (NautilusCanvasContainerAccessible *self)
+{
+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nautilus_canvas_container_accessible_get_type (),
+                                              NautilusCanvasContainerAccessiblePrivate);
+}
+
+static void
+nautilus_canvas_container_accessible_class_init (NautilusCanvasContainerAccessibleClass *klass)
+{
+    AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+    gobject_class->finalize = nautilus_canvas_container_accessible_finalize;
+
+    atk_class->get_n_children = nautilus_canvas_container_accessible_get_n_children;
+    atk_class->ref_child = nautilus_canvas_container_accessible_ref_child;
+    atk_class->initialize = nautilus_canvas_container_accessible_initialize;
+
+    g_type_class_add_private (klass, sizeof (NautilusCanvasContainerAccessiblePrivate));
+}
+
+gboolean
+nautilus_canvas_container_is_layout_rtl (NautilusCanvasContainer *container)
+{
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), 0);
+
+    return (gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL);
+}
+
+int
+nautilus_canvas_container_get_max_layout_lines_for_pango (NautilusCanvasContainer *container)
+{
+    int limit;
+
+    limit = text_ellipsis_limits[container->details->zoom_level];
+
+    if (limit <= 0)
+    {
+        return G_MININT;
+    }
+
+    return -limit;
+}
+
+int
+nautilus_canvas_container_get_max_layout_lines (NautilusCanvasContainer *container)
+{
+    int limit;
+
+    limit = text_ellipsis_limits[container->details->zoom_level];
+
+    if (limit <= 0)
+    {
+        return G_MAXINT;
+    }
+
+    return limit;
+}
diff --git a/src/nautilus-canvas-container.h b/src/nautilus-canvas-container.h
new file mode 100644
index 000000000..7955cf34d
--- /dev/null
+++ b/src/nautilus-canvas-container.h
@@ -0,0 +1,295 @@
+
+/* gnome-canvas-container.h - Canvas container widget.
+
+   Copyright (C) 1999, 2000 Free Software Foundation
+   Copyright (C) 2000 Eazel, Inc.
+
+   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,
+   see <http://www.gnu.org/licenses/>.
+
+   Authors: Ettore Perazzoli <ettore gnu org>, Darin Adler <darin bentspoon com>
+*/
+
+#pragma once
+
+#include <eel/eel-canvas.h>
+
+#include "nautilus-types.h"
+
+#define NAUTILUS_TYPE_CANVAS_CONTAINER nautilus_canvas_container_get_type()
+#define NAUTILUS_CANVAS_CONTAINER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_CANVAS_CONTAINER, NautilusCanvasContainer))
+#define NAUTILUS_CANVAS_CONTAINER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_CANVAS_CONTAINER, NautilusCanvasContainerClass))
+#define NAUTILUS_IS_CANVAS_CONTAINER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_CANVAS_CONTAINER))
+#define NAUTILUS_IS_CANVAS_CONTAINER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_CANVAS_CONTAINER))
+#define NAUTILUS_CANVAS_CONTAINER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), NAUTILUS_TYPE_CANVAS_CONTAINER, NautilusCanvasContainerClass))
+
+
+#define NAUTILUS_CANVAS_ICON_DATA(pointer) \
+       ((NautilusCanvasIconData *) (pointer))
+
+typedef struct NautilusCanvasIconData NautilusCanvasIconData;
+
+typedef void (* NautilusCanvasCallback) (NautilusCanvasIconData *icon_data,
+                                        gpointer callback_data);
+
+typedef struct {
+       int x;
+       int y;
+       double scale;
+} NautilusCanvasPosition;
+
+#define        NAUTILUS_CANVAS_CONTAINER_TYPESELECT_FLUSH_DELAY 1000000
+
+typedef struct _NautilusCanvasContainer        NautilusCanvasContainer;
+typedef struct  NautilusCanvasContainerDetails NautilusCanvasContainerDetails;
+
+struct _NautilusCanvasContainer {
+       EelCanvas canvas;
+       NautilusCanvasContainerDetails *details;
+};
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (NautilusCanvasContainer, g_object_unref)
+
+typedef struct {
+       EelCanvasClass parent_slot;
+
+       /* Operations on the container. */
+       int          (* button_press)             (NautilusCanvasContainer *container,
+                                                  GdkEventButton *event);
+       void         (* context_click_background) (NautilusCanvasContainer *container,
+                                                  GdkEventButton *event);
+       void         (* middle_click)             (NautilusCanvasContainer *container,
+                                                  GdkEventButton *event);
+
+       /* Operations on icons. */
+       void         (* activate)                 (NautilusCanvasContainer *container,
+                                                  NautilusCanvasIconData *data);
+       void         (* activate_alternate)       (NautilusCanvasContainer *container,
+                                                  NautilusCanvasIconData *data);
+       void         (* activate_previewer)       (NautilusCanvasContainer *container,
+                                                  GList *files,
+                                                  GArray *locations);
+       void         (* context_click_selection)  (NautilusCanvasContainer *container,
+                                                  GdkEventButton *event);
+       void         (* move_copy_items)          (NautilusCanvasContainer *container,
+                                                  const GList *item_uris,
+                                                  const char *target_uri,
+                                                  GdkDragAction action,
+                                                  int x,
+                                                  int y);
+       void         (* handle_netscape_url)      (NautilusCanvasContainer *container,
+                                                  const char *url,
+                                                  const char *target_uri,
+                                                  GdkDragAction action,
+                                                  int x,
+                                                  int y);
+       void         (* handle_uri_list)          (NautilusCanvasContainer *container,
+                                                  const char *uri_list,
+                                                  const char *target_uri,
+                                                  GdkDragAction action,
+                                                  int x,
+                                                  int y);
+       void         (* handle_text)              (NautilusCanvasContainer *container,
+                                                  const char *text,
+                                                  const char *target_uri,
+                                                  GdkDragAction action,
+                                                  int x,
+                                                  int y);
+       void         (* handle_raw)               (NautilusCanvasContainer *container,
+                                                  char *raw_data,
+                                                  int length,
+                                                  const char *target_uri,
+                                                  const char *direct_save_uri,
+                                                  GdkDragAction action,
+                                                  int x,
+                                                  int y);
+       void         (* handle_hover)             (NautilusCanvasContainer *container,
+                                                  const char *target_uri);
+
+       /* Queries on the container for subclass/client.
+        * These must be implemented. The default "do nothing" is not good enough.
+        */
+       char *       (* get_container_uri)        (NautilusCanvasContainer *container);
+
+       /* Queries on icons for subclass/client.
+        * These must be implemented. The default "do nothing" is not
+        * good enough, these are _not_ signals.
+        */
+       NautilusIconInfo *(* get_icon_images)     (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *data,
+                                                    int canvas_size,
+                                                    gboolean for_drag_accept);
+       void         (* get_icon_text)            (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *data,
+                                                    char **editable_text,
+                                                    char **additional_text,
+                                                    gboolean include_invisible);
+       char *       (* get_icon_description)     (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *data);
+       int          (* compare_icons)            (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *canvas_a,
+                                                    NautilusCanvasIconData *canvas_b);
+       int          (* compare_icons_by_name)    (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *canvas_a,
+                                                    NautilusCanvasIconData *canvas_b);
+       void         (* prioritize_thumbnailing)  (NautilusCanvasContainer *container,
+                                                  NautilusCanvasIconData *data);
+
+       /* Queries on icons for subclass/client.
+        * These must be implemented => These are signals !
+        * The default "do nothing" is not good enough.
+        */
+       gboolean     (* get_stored_icon_position) (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *data,
+                                                    NautilusCanvasPosition *position);
+       char *       (* get_icon_uri)             (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *data);
+       char *       (* get_icon_activation_uri)  (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *data);
+       char *       (* get_icon_drop_target_uri) (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *data);
+
+       /* If canvas data is NULL, the layout timestamp of the container should be retrieved.
+        * That is the time when the container displayed a fully loaded directory with
+        * all canvas positions assigned.
+        *
+        * If canvas data is not NULL, the position timestamp of the canvas should be retrieved.
+        * That is the time when the file (i.e. canvas data payload) was last displayed in a
+        * fully loaded directory with all canvas positions assigned.
+        */
+       gboolean     (* get_stored_layout_timestamp) (NautilusCanvasContainer *container,
+                                                     NautilusCanvasIconData *data,
+                                                     time_t *time);
+       /* If canvas data is NULL, the layout timestamp of the container should be stored.
+        * If canvas data is not NULL, the position timestamp of the container should be stored.
+        */
+       gboolean     (* store_layout_timestamp) (NautilusCanvasContainer *container,
+                                                NautilusCanvasIconData *data,
+                                                const time_t *time);
+
+       /* Notifications for the whole container. */
+       void         (* band_select_started)      (NautilusCanvasContainer *container);
+       void         (* band_select_ended)        (NautilusCanvasContainer *container);
+       void         (* selection_changed)        (NautilusCanvasContainer *container);
+       void         (* layout_changed)           (NautilusCanvasContainer *container);
+
+       /* Notifications for icons. */
+       void         (* icon_position_changed)    (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *data,
+                                                    const NautilusCanvasPosition *position);
+       int          (* preview)                  (NautilusCanvasContainer *container,
+                                                  NautilusCanvasIconData *data,
+                                                  gboolean start_flag);
+        void         (* icon_added)               (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *data);
+        void         (* icon_removed)             (NautilusCanvasContainer *container,
+                                                    NautilusCanvasIconData *data);
+        void         (* cleared)                  (NautilusCanvasContainer *container);
+       gboolean     (* start_interactive_search) (NautilusCanvasContainer *container);
+} NautilusCanvasContainerClass;
+
+/* GtkObject */
+GType             nautilus_canvas_container_get_type                      (void);
+GtkWidget *       nautilus_canvas_container_new                           (void);
+
+
+/* adding, removing, and managing icons */
+void              nautilus_canvas_container_clear                         (NautilusCanvasContainer  *view);
+gboolean          nautilus_canvas_container_add                           (NautilusCanvasContainer  *view,
+                                                                          NautilusCanvasIconData       
*data);
+void              nautilus_canvas_container_layout_now                    (NautilusCanvasContainer 
*container);
+gboolean          nautilus_canvas_container_remove                        (NautilusCanvasContainer  *view,
+                                                                          NautilusCanvasIconData       
*data);
+void              nautilus_canvas_container_for_each                      (NautilusCanvasContainer  *view,
+                                                                          NautilusCanvasCallback    callback,
+                                                                          gpointer                
callback_data);
+void              nautilus_canvas_container_request_update                (NautilusCanvasContainer  *view,
+                                                                          NautilusCanvasIconData       
*data);
+void              nautilus_canvas_container_request_update_all            (NautilusCanvasContainer  
*container);
+void              nautilus_canvas_container_reveal                        (NautilusCanvasContainer  
*container,
+                                                                          NautilusCanvasIconData       
*data);
+gboolean          nautilus_canvas_container_is_empty                      (NautilusCanvasContainer  
*container);
+NautilusCanvasIconData *nautilus_canvas_container_get_first_visible_icon        (NautilusCanvasContainer  
*container);
+NautilusCanvasIconData *nautilus_canvas_container_get_focused_icon              (NautilusCanvasContainer  
*container);
+GdkRectangle      *nautilus_canvas_container_get_icon_bounding_box          (NautilusCanvasContainer  
*container,
+                                                                            NautilusCanvasIconData       
*data);
+void              nautilus_canvas_container_scroll_to_canvas                (NautilusCanvasContainer  
*container,
+                                                                            NautilusCanvasIconData       
*data);
+
+void              nautilus_canvas_container_begin_loading                 (NautilusCanvasContainer  
*container);
+void              nautilus_canvas_container_end_loading                   (NautilusCanvasContainer  
*container,
+                                                                          gboolean                
all_icons_added);
+
+void              nautilus_canvas_container_sort                          (NautilusCanvasContainer  
*container);
+void              nautilus_canvas_container_freeze_icon_positions         (NautilusCanvasContainer  
*container);
+
+int               nautilus_canvas_container_get_max_layout_lines           (NautilusCanvasContainer  
*container);
+int               nautilus_canvas_container_get_max_layout_lines_for_pango (NautilusCanvasContainer  
*container);
+
+void              nautilus_canvas_container_set_highlighted_for_clipboard (NautilusCanvasContainer  
*container,
+                                                                          GList                  
*clipboard_canvas_data);
+
+/* operations on all icons */
+void              nautilus_canvas_container_unselect_all                  (NautilusCanvasContainer  *view);
+void              nautilus_canvas_container_select_all                    (NautilusCanvasContainer  *view);
+
+
+void              nautilus_canvas_container_select_first                  (NautilusCanvasContainer  *view);
+
+void              nautilus_canvas_container_preview_selection_event       (NautilusCanvasContainer  *view,
+                                                                           GtkDirectionType          
direction);
+
+/* operations on the selection */
+GList     *       nautilus_canvas_container_get_selection                 (NautilusCanvasContainer  *view);
+void                     nautilus_canvas_container_invert_selection                            
(NautilusCanvasContainer  *view);
+void              nautilus_canvas_container_set_selection                 (NautilusCanvasContainer  *view,
+                                                                          GList                  *selection);
+GArray    *       nautilus_canvas_container_get_selected_icon_locations   (NautilusCanvasContainer  *view);
+
+/* options */
+NautilusCanvasZoomLevel nautilus_canvas_container_get_zoom_level                (NautilusCanvasContainer  
*view);
+void              nautilus_canvas_container_set_zoom_level                (NautilusCanvasContainer  *view,
+                                                                          int                     
new_zoom_level);
+void              nautilus_canvas_container_set_single_click_mode         (NautilusCanvasContainer  
*container,
+                                                                          gboolean                
single_click_mode);
+void              nautilus_canvas_container_enable_linger_selection       (NautilusCanvasContainer  *view,
+                                                                          gboolean                enable);
+void              nautilus_canvas_container_set_font                      (NautilusCanvasContainer  
*container,
+                                                                          const char             *font); 
+void              nautilus_canvas_container_set_margins                   (NautilusCanvasContainer  
*container,
+                                                                          int                     
left_margin,
+                                                                          int                     
right_margin,
+                                                                          int                     top_margin,
+                                                                          int                     
bottom_margin);
+char*             nautilus_canvas_container_get_icon_description          (NautilusCanvasContainer  
*container,
+                                                                            NautilusCanvasIconData       
*data);
+
+gboolean         nautilus_canvas_container_is_layout_rtl                       (NautilusCanvasContainer  
*container);
+
+gboolean          nautilus_canvas_container_get_store_layout_timestamps   (NautilusCanvasContainer  
*container);
+
+void              nautilus_canvas_container_widget_to_file_operation_position (NautilusCanvasContainer 
*container,
+                                                                              GdkPoint              
*position);
+guint             nautilus_canvas_container_get_icon_size_for_zoom_level (NautilusCanvasZoomLevel 
zoom_level);
+
+#define CANVAS_WIDTH(container,allocation) (allocation.width           \
+                                           /  EEL_CANVAS (container)->pixels_per_unit)
+
+#define CANVAS_HEIGHT(container,allocation) (allocation.height         \
+                                            / EEL_CANVAS (container)->pixels_per_unit)
diff --git a/src/nautilus-canvas-dnd.c b/src/nautilus-canvas-dnd.c
new file mode 100644
index 000000000..9522f321c
--- /dev/null
+++ b/src/nautilus-canvas-dnd.c
@@ -0,0 +1,1833 @@
+/* nautilus-canvas-dnd.c - Drag & drop handling for the canvas container widget.
+ *
+ *  Copyright (C) 1999, 2000 Free Software Foundation
+ *  Copyright (C) 2000 Eazel, Inc.
+ *
+ *  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,
+ *  see <http://www.gnu.org/licenses/>.
+ *
+ *  Authors: Ettore Perazzoli <ettore gnu org>,
+ *           Darin Adler <darin bentspoon com>,
+ *           Andy Hertzfeld <andy eazel com>
+ *           Pavel Cisler <pavel eazel com>
+ *
+ *
+ *  XDS support: Benedikt Meurer <benny xfce org> (adapted by Amos Brocco <amos brocco unifr ch>)
+ *
+ */
+
+
+#include <config.h>
+#include <math.h>
+#include <src/nautilus-window.h>
+
+#include "nautilus-canvas-dnd.h"
+
+#include "nautilus-canvas-private.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-metadata.h"
+#include "nautilus-selection-canvas-item.h"
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-graphic-effects.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-stock-dialogs.h>
+#include <eel/eel-string.h>
+#include <eel/eel-vfs-extensions.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "nautilus-file-utilities.h"
+#include "nautilus-file-changes-queue.h"
+#include <stdio.h>
+#include <string.h>
+
+#define DEBUG_FLAG NAUTILUS_DEBUG_CANVAS_CONTAINER
+#include "nautilus-debug.h"
+
+static const GtkTargetEntry drag_types [] =
+{
+    { NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
+    { NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
+};
+
+static const GtkTargetEntry drop_types [] =
+{
+    { NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
+    /* prefer "_NETSCAPE_URL" over "text/uri-list" to satisfy web browsers. */
+    { NAUTILUS_ICON_DND_NETSCAPE_URL_TYPE, 0, NAUTILUS_ICON_DND_NETSCAPE_URL },
+    /* prefer XDS over "text/uri-list" */
+    { NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, 0, NAUTILUS_ICON_DND_XDNDDIRECTSAVE },     /* XDS Protocol Type 
*/
+    { NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
+    { NAUTILUS_ICON_DND_RAW_TYPE, 0, NAUTILUS_ICON_DND_RAW },
+    /* Must be last: */
+    { NAUTILUS_ICON_DND_ROOTWINDOW_DROP_TYPE, 0, NAUTILUS_ICON_DND_ROOTWINDOW_DROP }
+};
+static void     stop_dnd_highlight (GtkWidget *widget);
+static void     dnd_highlight_queue_redraw (GtkWidget *widget);
+
+static GtkTargetList *drop_types_list = NULL;
+static GtkTargetList *drop_types_list_root = NULL;
+
+static char *nautilus_canvas_container_find_drop_target (NautilusCanvasContainer *container,
+                                                         GdkDragContext          *context,
+                                                         int                      x,
+                                                         int                      y,
+                                                         gboolean                *icon_hit);
+
+static EelCanvasItem *
+create_selection_shadow (NautilusCanvasContainer *container,
+                         GList                   *list)
+{
+    EelCanvasGroup *group;
+    EelCanvas *canvas;
+    int max_x, max_y;
+    int min_x, min_y;
+    GList *p;
+    GtkAllocation allocation;
+
+    if (list == NULL)
+    {
+        return NULL;
+    }
+
+    /* if we're only dragging a single item, don't worry about the shadow */
+    if (list->next == NULL)
+    {
+        return NULL;
+    }
+
+    canvas = EEL_CANVAS (container);
+    gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
+
+    /* Creating a big set of rectangles in the canvas can be expensive, so
+     *  we try to be smart and only create the maximum number of rectangles
+     *  that we will need, in the vertical/horizontal directions.  */
+
+    max_x = allocation.width;
+    min_x = -max_x;
+
+    max_y = allocation.height;
+    min_y = -max_y;
+
+    /* Create a group, so that it's easier to move all the items around at
+     *  once.  */
+    group = EEL_CANVAS_GROUP
+                (eel_canvas_item_new (EEL_CANVAS_GROUP (canvas->root),
+                                      eel_canvas_group_get_type (),
+                                      NULL));
+
+    for (p = list; p != NULL; p = p->next)
+    {
+        NautilusDragSelectionItem *item;
+        int x1, y1, x2, y2;
+
+        item = p->data;
+
+        if (!item->got_icon_position)
+        {
+            continue;
+        }
+
+        x1 = item->icon_x;
+        y1 = item->icon_y;
+        x2 = x1 + item->icon_width;
+        y2 = y1 + item->icon_height;
+
+        if (x2 >= min_x && x1 <= max_x && y2 >= min_y && y1 <= max_y)
+        {
+            eel_canvas_item_new
+                (group,
+                NAUTILUS_TYPE_SELECTION_CANVAS_ITEM,
+                "x1", (double) x1,
+                "y1", (double) y1,
+                "x2", (double) x2,
+                "y2", (double) y2,
+                NULL);
+        }
+    }
+
+    return EEL_CANVAS_ITEM (group);
+}
+
+/* Set the affine instead of the x and y position.
+ * Simple, and setting x and y was broken at one point.
+ */
+static void
+set_shadow_position (EelCanvasItem *shadow,
+                     double         x,
+                     double         y)
+{
+    eel_canvas_item_set (shadow,
+                         "x", x, "y", y,
+                         NULL);
+}
+
+
+/* Source-side handling of the drag. */
+
+/* iteration glue struct */
+typedef struct
+{
+    gpointer iterator_context;
+    NautilusDragEachSelectedItemDataGet iteratee;
+    gpointer iteratee_data;
+} CanvasGetDataBinderContext;
+
+static void
+canvas_rect_world_to_widget (EelCanvas *canvas,
+                             EelDRect  *world_rect,
+                             EelIRect  *widget_rect)
+{
+    EelDRect window_rect;
+    GtkAdjustment *hadj, *vadj;
+
+    hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas));
+    vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas));
+
+    eel_canvas_world_to_window (canvas,
+                                world_rect->x0, world_rect->y0,
+                                &window_rect.x0, &window_rect.y0);
+    eel_canvas_world_to_window (canvas,
+                                world_rect->x1, world_rect->y1,
+                                &window_rect.x1, &window_rect.y1);
+    widget_rect->x0 = (int) window_rect.x0 - gtk_adjustment_get_value (hadj);
+    widget_rect->y0 = (int) window_rect.y0 - gtk_adjustment_get_value (vadj);
+    widget_rect->x1 = (int) window_rect.x1 - gtk_adjustment_get_value (hadj);
+    widget_rect->y1 = (int) window_rect.y1 - gtk_adjustment_get_value (vadj);
+}
+
+static void
+canvas_widget_to_world (EelCanvas *canvas,
+                        double     widget_x,
+                        double     widget_y,
+                        double    *world_x,
+                        double    *world_y)
+{
+    eel_canvas_window_to_world (canvas,
+                                widget_x + gtk_adjustment_get_value (gtk_scrollable_get_hadjustment 
(GTK_SCROLLABLE (canvas))),
+                                widget_y + gtk_adjustment_get_value (gtk_scrollable_get_vadjustment 
(GTK_SCROLLABLE (canvas))),
+                                world_x, world_y);
+}
+
+static gboolean
+icon_get_data_binder (NautilusCanvasIcon *icon,
+                      gpointer            data)
+{
+    CanvasGetDataBinderContext *context;
+    EelDRect world_rect;
+    EelIRect widget_rect;
+    char *uri;
+    NautilusCanvasContainer *container;
+    NautilusFile *file;
+
+    context = (CanvasGetDataBinderContext *) data;
+
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (context->iterator_context));
+
+    container = NAUTILUS_CANVAS_CONTAINER (context->iterator_context);
+
+    world_rect = nautilus_canvas_item_get_icon_rectangle (icon->item);
+
+    canvas_rect_world_to_widget (EEL_CANVAS (container), &world_rect, &widget_rect);
+
+    uri = nautilus_canvas_container_get_icon_uri (container, icon);
+    file = nautilus_file_get_by_uri (uri);
+    g_free (uri);
+    uri = nautilus_canvas_container_get_icon_activation_uri (container, icon);
+
+    if (uri == NULL)
+    {
+        g_warning ("no URI for one of the iterated icons");
+        nautilus_file_unref (file);
+        return TRUE;
+    }
+
+    widget_rect = eel_irect_offset_by (widget_rect,
+                                       -container->details->dnd_info->drag_info.start_x,
+                                       -container->details->dnd_info->drag_info.start_y);
+
+    widget_rect = eel_irect_scale_by (widget_rect,
+                                      1 / EEL_CANVAS (container)->pixels_per_unit);
+
+    /* pass the uri, mouse-relative x/y and icon width/height */
+    context->iteratee (uri,
+                       (int) widget_rect.x0,
+                       (int) widget_rect.y0,
+                       widget_rect.x1 - widget_rect.x0,
+                       widget_rect.y1 - widget_rect.y0,
+                       context->iteratee_data);
+
+    g_free (uri);
+    nautilus_file_unref (file);
+
+    return TRUE;
+}
+
+typedef gboolean (*CanvasContainerEachFunc)(NautilusCanvasIcon *,
+                                            gpointer);
+
+/* Iterate over each selected icon in a NautilusCanvasContainer,
+ * calling each_function on each.
+ */
+static void
+nautilus_canvas_container_each_selected_icon (NautilusCanvasContainer *container,
+                                              CanvasContainerEachFunc  each_function,
+                                              gpointer                 data)
+{
+    GList *p;
+    NautilusCanvasIcon *icon;
+
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        icon = p->data;
+        if (!icon->is_selected)
+        {
+            continue;
+        }
+        if (!each_function (icon, data))
+        {
+            return;
+        }
+    }
+}
+
+/* Adaptor function used with nautilus_canvas_container_each_selected_icon
+ * to help iterate over all selected items, passing uris, x, y, w and h
+ * values to the iteratee
+ */
+static void
+each_icon_get_data_binder (NautilusDragEachSelectedItemDataGet iteratee,
+                           gpointer                            iterator_context,
+                           gpointer                            data)
+{
+    CanvasGetDataBinderContext context;
+    NautilusCanvasContainer *container;
+
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (iterator_context));
+    container = NAUTILUS_CANVAS_CONTAINER (iterator_context);
+
+    context.iterator_context = iterator_context;
+    context.iteratee = iteratee;
+    context.iteratee_data = data;
+    nautilus_canvas_container_each_selected_icon (container, icon_get_data_binder, &context);
+}
+
+/* Called when the data for drag&drop is needed */
+static void
+drag_data_get_callback (GtkWidget        *widget,
+                        GdkDragContext   *context,
+                        GtkSelectionData *selection_data,
+                        guint             info,
+                        guint32           time,
+                        gpointer          data)
+{
+    NautilusDragInfo *drag_info;
+
+    g_assert (widget != NULL);
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (widget));
+    g_return_if_fail (context != NULL);
+
+    /* Call common function from nautilus-drag that set's up
+     * the selection data in the right format. Pass it means to
+     * iterate all the selected icons.
+     */
+    drag_info = &(NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info->drag_info);
+    nautilus_drag_drag_data_get_from_cache (drag_info->selection_cache, context, selection_data, info, time);
+}
+
+
+/* Target-side handling of the drag.  */
+
+static void
+nautilus_canvas_container_position_shadow (NautilusCanvasContainer *container,
+                                           int                      x,
+                                           int                      y)
+{
+    EelCanvasItem *shadow;
+    double world_x, world_y;
+
+    shadow = container->details->dnd_info->shadow;
+    if (shadow == NULL)
+    {
+        return;
+    }
+
+    canvas_widget_to_world (EEL_CANVAS (container), x, y,
+                            &world_x, &world_y);
+
+    set_shadow_position (shadow, world_x, world_y);
+    eel_canvas_item_show (shadow);
+}
+
+static void
+stop_cache_selection_list (NautilusDragInfo *drag_info)
+{
+    if (drag_info->file_list_info_handler)
+    {
+        nautilus_file_list_cancel_call_when_ready (drag_info->file_list_info_handler);
+        drag_info->file_list_info_handler = NULL;
+    }
+}
+
+static void
+cache_selection_list (NautilusDragInfo *drag_info)
+{
+    GList *files;
+
+    files = nautilus_drag_file_list_from_selection_list (drag_info->selection_list);
+    nautilus_file_list_call_when_ready (files,
+                                        NAUTILUS_FILE_ATTRIBUTE_INFO,
+                                        drag_info->file_list_info_handler,
+                                        NULL, NULL);
+
+    g_list_free_full (files, g_object_unref);
+}
+
+static void
+nautilus_canvas_container_dropped_canvas_feedback (GtkWidget        *widget,
+                                                   GtkSelectionData *data,
+                                                   int               x,
+                                                   int               y)
+{
+    NautilusCanvasContainer *container;
+    NautilusCanvasDndInfo *dnd_info;
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+    dnd_info = container->details->dnd_info;
+
+    /* Delete old selection list. */
+    stop_cache_selection_list (&dnd_info->drag_info);
+    nautilus_drag_destroy_selection_list (dnd_info->drag_info.selection_list);
+    dnd_info->drag_info.selection_list = NULL;
+
+    /* Delete old shadow if any. */
+    if (dnd_info->shadow != NULL)
+    {
+        /* FIXME bugzilla.gnome.org 42484:
+         * Is a destroy really sufficient here? Who does the unref? */
+        eel_canvas_item_destroy (dnd_info->shadow);
+    }
+
+    /* Build the selection list and the shadow. */
+    dnd_info->drag_info.selection_list = nautilus_drag_build_selection_list (data);
+    cache_selection_list (&dnd_info->drag_info);
+    dnd_info->shadow = create_selection_shadow (container, dnd_info->drag_info.selection_list);
+    nautilus_canvas_container_position_shadow (container, x, y);
+}
+
+static char *
+get_direct_save_filename (GdkDragContext *context)
+{
+    guchar *prop_text;
+    gint prop_len;
+
+    if (!gdk_property_get (gdk_drag_context_get_source_window (context), gdk_atom_intern 
(NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE),
+                           gdk_atom_intern ("text/plain", FALSE), 0, 1024, FALSE, NULL, NULL,
+                           &prop_len, &prop_text))
+    {
+        return NULL;
+    }
+
+    /* Zero-terminate the string */
+    prop_text = g_realloc (prop_text, prop_len + 1);
+    prop_text[prop_len] = '\0';
+
+    /* Verify that the file name provided by the source is valid */
+    if (*prop_text == '\0' ||
+        strchr ((const gchar *) prop_text, G_DIR_SEPARATOR) != NULL)
+    {
+        DEBUG ("Invalid filename provided by XDS drag site");
+        g_free (prop_text);
+        return NULL;
+    }
+
+    return (gchar *) prop_text;
+}
+
+static void
+set_direct_save_uri (GtkWidget        *widget,
+                     GdkDragContext   *context,
+                     NautilusDragInfo *drag_info,
+                     int               x,
+                     int               y)
+{
+    GFile *base, *child;
+    char *filename, *drop_target;
+    gchar *uri;
+
+    drag_info->got_drop_data_type = TRUE;
+    drag_info->data_type = NAUTILUS_ICON_DND_XDNDDIRECTSAVE;
+
+    uri = NULL;
+
+    filename = get_direct_save_filename (context);
+    drop_target = nautilus_canvas_container_find_drop_target (NAUTILUS_CANVAS_CONTAINER (widget),
+                                                              context, x, y, NULL);
+
+    if (drop_target && eel_uri_is_trash (drop_target))
+    {
+        g_free (drop_target);
+        drop_target = NULL;         /* Cannot save to trash ...*/
+    }
+
+    if (filename != NULL && drop_target != NULL)
+    {
+        /* Resolve relative path */
+        base = g_file_new_for_uri (drop_target);
+        child = g_file_get_child (base, filename);
+        uri = g_file_get_uri (child);
+        g_object_unref (base);
+        g_object_unref (child);
+
+        /* Change the uri property */
+        gdk_property_change (gdk_drag_context_get_source_window (context),
+                             gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE),
+                             gdk_atom_intern ("text/plain", FALSE), 8,
+                             GDK_PROP_MODE_REPLACE, (const guchar *) uri,
+                             strlen (uri));
+
+        drag_info->direct_save_uri = uri;
+    }
+
+    g_free (filename);
+    g_free (drop_target);
+}
+
+/* FIXME bugzilla.gnome.org 47445: Needs to become a shared function */
+static void
+get_data_on_first_target_we_support (GtkWidget      *widget,
+                                     GdkDragContext *context,
+                                     guint32         time,
+                                     int             x,
+                                     int             y)
+{
+    GtkTargetList *list;
+    GdkAtom target;
+
+    if (drop_types_list == NULL)
+    {
+        drop_types_list = gtk_target_list_new (drop_types,
+                                               G_N_ELEMENTS (drop_types) - 1);
+        gtk_target_list_add_text_targets (drop_types_list, NAUTILUS_ICON_DND_TEXT);
+    }
+    if (drop_types_list_root == NULL)
+    {
+        drop_types_list_root = gtk_target_list_new (drop_types,
+                                                    G_N_ELEMENTS (drop_types));
+        gtk_target_list_add_text_targets (drop_types_list_root, NAUTILUS_ICON_DND_TEXT);
+    }
+
+    list = drop_types_list;
+
+    target = gtk_drag_dest_find_target (widget, context, list);
+    if (target != GDK_NONE)
+    {
+        guint info;
+        NautilusDragInfo *drag_info;
+        gboolean found;
+
+        drag_info = &(NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info->drag_info);
+
+        found = gtk_target_list_find (list, target, &info);
+        g_assert (found);
+
+        /* Don't get_data for destructive ops */
+        if ((info == NAUTILUS_ICON_DND_ROOTWINDOW_DROP ||
+             info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE) &&
+            !drag_info->drop_occurred)
+        {
+            /* We can't call get_data here, because that would
+             *  make the source execute the rootwin action or the direct save */
+            drag_info->got_drop_data_type = TRUE;
+            drag_info->data_type = info;
+        }
+        else
+        {
+            if (info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE)
+            {
+                set_direct_save_uri (widget, context, drag_info, x, y);
+            }
+            gtk_drag_get_data (GTK_WIDGET (widget), context,
+                               target, time);
+        }
+    }
+}
+
+static void
+nautilus_canvas_container_ensure_drag_data (NautilusCanvasContainer *container,
+                                            GdkDragContext          *context,
+                                            guint32                  time)
+{
+    NautilusCanvasDndInfo *dnd_info;
+
+    dnd_info = container->details->dnd_info;
+
+    if (!dnd_info->drag_info.got_drop_data_type)
+    {
+        get_data_on_first_target_we_support (GTK_WIDGET (container), context, time, 0, 0);
+    }
+}
+
+static void
+drag_end_callback (GtkWidget      *widget,
+                   GdkDragContext *context,
+                   gpointer        data)
+{
+    NautilusCanvasContainer *container;
+    NautilusCanvasDndInfo *dnd_info;
+    NautilusWindow *window;
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+    window = NAUTILUS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (container)));
+    dnd_info = container->details->dnd_info;
+
+    stop_cache_selection_list (&dnd_info->drag_info);
+    nautilus_drag_destroy_selection_list (dnd_info->drag_info.selection_list);
+    nautilus_drag_destroy_selection_list (dnd_info->drag_info.selection_cache);
+    nautilus_drag_destroy_selection_list (container->details->dnd_source_info->selection_cache);
+    dnd_info->drag_info.selection_list = NULL;
+    dnd_info->drag_info.selection_cache = NULL;
+    container->details->dnd_source_info->selection_cache = NULL;
+
+    nautilus_window_end_dnd (window, context);
+}
+
+static NautilusCanvasIcon *
+nautilus_canvas_container_item_at (NautilusCanvasContainer *container,
+                                   int                      x,
+                                   int                      y)
+{
+    GList *p;
+    int size;
+    EelDRect point;
+    EelIRect canvas_point;
+
+    /* build the hit-test rectangle. Base the size on the scale factor to ensure that it is
+     * non-empty even at the smallest scale factor
+     */
+
+    size = MAX (1, 1 + (1 / EEL_CANVAS (container)->pixels_per_unit));
+    point.x0 = x;
+    point.y0 = y;
+    point.x1 = x + size;
+    point.y1 = y + size;
+
+    for (p = container->details->icons; p != NULL; p = p->next)
+    {
+        NautilusCanvasIcon *icon;
+        icon = p->data;
+
+        eel_canvas_w2c (EEL_CANVAS (container),
+                        point.x0,
+                        point.y0,
+                        &canvas_point.x0,
+                        &canvas_point.y0);
+        eel_canvas_w2c (EEL_CANVAS (container),
+                        point.x1,
+                        point.y1,
+                        &canvas_point.x1,
+                        &canvas_point.y1);
+        if (nautilus_canvas_item_hit_test_rectangle (icon->item, canvas_point))
+        {
+            return icon;
+        }
+    }
+
+    return NULL;
+}
+
+static char *
+get_container_uri (NautilusCanvasContainer *container)
+{
+    char *uri;
+
+    /* get the URI associated with the container */
+    uri = NULL;
+    g_signal_emit_by_name (container, "get-container-uri", &uri);
+    return uri;
+}
+
+static gboolean
+nautilus_canvas_container_selection_items_local (NautilusCanvasContainer *container,
+                                                 GList                   *items)
+{
+    char *container_uri_string;
+    gboolean result;
+
+    /* must have at least one item */
+    g_assert (items);
+
+    /* get the URI associated with the container */
+    container_uri_string = get_container_uri (container);
+
+    result = nautilus_drag_items_local (container_uri_string, items);
+
+    g_free (container_uri_string);
+
+    return result;
+}
+
+/* handle dropped url */
+static void
+receive_dropped_netscape_url (NautilusCanvasContainer *container,
+                              const char              *encoded_url,
+                              GdkDragContext          *context,
+                              int                      x,
+                              int                      y)
+{
+    char *drop_target;
+
+    if (encoded_url == NULL)
+    {
+        return;
+    }
+
+    drop_target = nautilus_canvas_container_find_drop_target (container, context, x, y, NULL);
+
+    g_signal_emit_by_name (container, "handle-netscape-url",
+                           encoded_url,
+                           drop_target,
+                           gdk_drag_context_get_selected_action (context));
+
+    g_free (drop_target);
+}
+
+/* handle dropped uri list */
+static void
+receive_dropped_uri_list (NautilusCanvasContainer *container,
+                          const char              *uri_list,
+                          GdkDragContext          *context,
+                          int                      x,
+                          int                      y)
+{
+    char *drop_target;
+
+    if (uri_list == NULL)
+    {
+        return;
+    }
+
+    drop_target = nautilus_canvas_container_find_drop_target (container, context, x, y, NULL);
+
+    g_signal_emit_by_name (container, "handle-uri-list",
+                           uri_list,
+                           drop_target,
+                           gdk_drag_context_get_selected_action (context));
+
+    g_free (drop_target);
+}
+
+/* handle dropped text */
+static void
+receive_dropped_text (NautilusCanvasContainer *container,
+                      const char              *text,
+                      GdkDragContext          *context,
+                      int                      x,
+                      int                      y)
+{
+    char *drop_target;
+
+    if (text == NULL)
+    {
+        return;
+    }
+
+    drop_target = nautilus_canvas_container_find_drop_target (container, context, x, y, NULL);
+
+    g_signal_emit_by_name (container, "handle-text",
+                           text,
+                           drop_target,
+                           gdk_drag_context_get_selected_action (context));
+
+    g_free (drop_target);
+}
+
+/* handle dropped raw data */
+static void
+receive_dropped_raw (NautilusCanvasContainer *container,
+                     const char              *raw_data,
+                     int                      length,
+                     const char              *direct_save_uri,
+                     GdkDragContext          *context,
+                     int                      x,
+                     int                      y)
+{
+    char *drop_target;
+
+    if (raw_data == NULL)
+    {
+        return;
+    }
+
+    drop_target = nautilus_canvas_container_find_drop_target (container, context, x, y, NULL);
+
+    g_signal_emit_by_name (container, "handle-raw",
+                           raw_data,
+                           length,
+                           drop_target,
+                           direct_save_uri,
+                           gdk_drag_context_get_selected_action (context));
+
+    g_free (drop_target);
+}
+
+static int
+auto_scroll_timeout_callback (gpointer data)
+{
+    NautilusCanvasContainer *container;
+    GtkWidget *widget;
+    float x_scroll_delta, y_scroll_delta;
+    GdkRectangle exposed_area;
+    GtkAllocation allocation;
+
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (data));
+    widget = GTK_WIDGET (data);
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+
+    if (container->details->dnd_info->drag_info.waiting_to_autoscroll
+        && container->details->dnd_info->drag_info.start_auto_scroll_in > g_get_monotonic_time ())
+    {
+        /* not yet */
+        return TRUE;
+    }
+
+    container->details->dnd_info->drag_info.waiting_to_autoscroll = FALSE;
+
+    nautilus_drag_autoscroll_calculate_delta (widget, &x_scroll_delta, &y_scroll_delta);
+    if (x_scroll_delta == 0 && y_scroll_delta == 0)
+    {
+        /* no work */
+        return TRUE;
+    }
+
+    /* Clear the old dnd highlight frame */
+    dnd_highlight_queue_redraw (widget);
+
+    if (!nautilus_canvas_container_scroll (container, (int) x_scroll_delta, (int) y_scroll_delta))
+    {
+        /* the scroll value got pinned to a min or max adjustment value,
+         * we ended up not scrolling
+         */
+        return TRUE;
+    }
+
+    /* Make sure the dnd highlight frame is redrawn */
+    dnd_highlight_queue_redraw (widget);
+
+    /* update cached drag start offsets */
+    container->details->dnd_info->drag_info.start_x -= x_scroll_delta;
+    container->details->dnd_info->drag_info.start_y -= y_scroll_delta;
+
+    /* Due to a glitch in GtkLayout, whe need to do an explicit draw of the exposed
+     * area.
+     * Calculate the size of the area we need to draw
+     */
+    gtk_widget_get_allocation (widget, &allocation);
+    exposed_area.x = allocation.x;
+    exposed_area.y = allocation.y;
+    exposed_area.width = allocation.width;
+    exposed_area.height = allocation.height;
+
+    if (x_scroll_delta > 0)
+    {
+        exposed_area.x = exposed_area.width - x_scroll_delta;
+    }
+    else if (x_scroll_delta < 0)
+    {
+        exposed_area.width = -x_scroll_delta;
+    }
+
+    if (y_scroll_delta > 0)
+    {
+        exposed_area.y = exposed_area.height - y_scroll_delta;
+    }
+    else if (y_scroll_delta < 0)
+    {
+        exposed_area.height = -y_scroll_delta;
+    }
+
+    /* offset it to 0, 0 */
+    exposed_area.x -= allocation.x;
+    exposed_area.y -= allocation.y;
+
+    gtk_widget_queue_draw_area (widget,
+                                exposed_area.x,
+                                exposed_area.y,
+                                exposed_area.width,
+                                exposed_area.height);
+
+    return TRUE;
+}
+
+static void
+set_up_auto_scroll_if_needed (NautilusCanvasContainer *container)
+{
+    nautilus_drag_autoscroll_start (&container->details->dnd_info->drag_info,
+                                    GTK_WIDGET (container),
+                                    auto_scroll_timeout_callback,
+                                    container);
+}
+
+static void
+stop_auto_scroll (NautilusCanvasContainer *container)
+{
+    nautilus_drag_autoscroll_stop (&container->details->dnd_info->drag_info);
+}
+
+static void
+handle_nonlocal_move (NautilusCanvasContainer *container,
+                      GdkDragAction            action,
+                      const char              *target_uri,
+                      gboolean                 icon_hit)
+{
+    GList *source_uris, *p;
+    gboolean free_target_uri;
+
+    if (container->details->dnd_info->drag_info.selection_list == NULL)
+    {
+        return;
+    }
+
+    source_uris = NULL;
+    for (p = container->details->dnd_info->drag_info.selection_list; p != NULL; p = p->next)
+    {
+        /* do a shallow copy of all the uri strings of the copied files */
+        source_uris = g_list_prepend (source_uris, ((NautilusDragSelectionItem *) p->data)->uri);
+    }
+    source_uris = g_list_reverse (source_uris);
+
+    free_target_uri = FALSE;
+
+    /* start the copy */
+    g_signal_emit_by_name (container, "move-copy-items",
+                           source_uris,
+                           target_uri,
+                           action);
+
+    if (free_target_uri)
+    {
+        g_free ((char *) target_uri);
+    }
+
+    g_list_free (source_uris);
+}
+
+static char *
+nautilus_canvas_container_find_drop_target (NautilusCanvasContainer *container,
+                                            GdkDragContext          *context,
+                                            int                      x,
+                                            int                      y,
+                                            gboolean                *icon_hit)
+{
+    NautilusCanvasIcon *drop_target_icon;
+    double world_x, world_y;
+    NautilusFile *file;
+    char *icon_uri;
+    char *container_uri;
+
+    if (icon_hit)
+    {
+        *icon_hit = FALSE;
+    }
+
+    if (!container->details->dnd_info->drag_info.got_drop_data_type)
+    {
+        return NULL;
+    }
+
+    canvas_widget_to_world (EEL_CANVAS (container), x, y, &world_x, &world_y);
+
+    /* FIXME bugzilla.gnome.org 42485:
+     * These "can_accept_items" tests need to be done by
+     * the canvas view, not here. This file is not supposed to know
+     * that the target is a file.
+     */
+
+    /* Find the item we hit with our drop, if any */
+    drop_target_icon = nautilus_canvas_container_item_at (container, world_x, world_y);
+    if (drop_target_icon != NULL)
+    {
+        icon_uri = nautilus_canvas_container_get_icon_uri (container, drop_target_icon);
+        if (icon_uri != NULL)
+        {
+            file = nautilus_file_get_by_uri (icon_uri);
+
+            if (!nautilus_drag_can_accept_info (file,
+                                                container->details->dnd_info->drag_info.data_type,
+                                                container->details->dnd_info->drag_info.selection_list))
+            {
+                /* the item we dropped our selection on cannot accept the items,
+                 * do the same thing as if we just dropped the items on the canvas
+                 */
+                drop_target_icon = NULL;
+            }
+
+            g_free (icon_uri);
+            nautilus_file_unref (file);
+        }
+    }
+
+    if (drop_target_icon == NULL)
+    {
+        if (icon_hit)
+        {
+            *icon_hit = FALSE;
+        }
+
+        container_uri = get_container_uri (container);
+
+        if (container_uri != NULL)
+        {
+            gboolean can;
+            file = nautilus_file_get_by_uri (container_uri);
+            can = nautilus_drag_can_accept_info (file,
+                                                 container->details->dnd_info->drag_info.data_type,
+                                                 container->details->dnd_info->drag_info.selection_list);
+            g_object_unref (file);
+            if (!can)
+            {
+                g_free (container_uri);
+                container_uri = NULL;
+            }
+        }
+
+        return container_uri;
+    }
+
+    if (icon_hit)
+    {
+        *icon_hit = TRUE;
+    }
+    return nautilus_canvas_container_get_icon_drop_target_uri (container, drop_target_icon);
+}
+
+static void
+nautilus_canvas_container_receive_dropped_icons (NautilusCanvasContainer *container,
+                                                 GdkDragContext          *context,
+                                                 int                      x,
+                                                 int                      y)
+{
+    char *drop_target;
+    gboolean local_move_only;
+    double world_x, world_y;
+    gboolean icon_hit;
+    GdkDragAction action, real_action;
+
+    drop_target = NULL;
+
+    if (container->details->dnd_info->drag_info.selection_list == NULL)
+    {
+        return;
+    }
+
+    real_action = gdk_drag_context_get_selected_action (context);
+
+    if (real_action == GDK_ACTION_ASK)
+    {
+        action = GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK;
+        real_action = nautilus_drag_drop_action_ask (GTK_WIDGET (container), action);
+    }
+
+    if (real_action > 0)
+    {
+        eel_canvas_window_to_world (EEL_CANVAS (container),
+                                    x + gtk_adjustment_get_value (gtk_scrollable_get_hadjustment 
(GTK_SCROLLABLE (container))),
+                                    y + gtk_adjustment_get_value (gtk_scrollable_get_vadjustment 
(GTK_SCROLLABLE (container))),
+                                    &world_x, &world_y);
+
+        drop_target = nautilus_canvas_container_find_drop_target (container,
+                                                                  context, x, y, &icon_hit);
+
+        local_move_only = FALSE;
+        if (!icon_hit && real_action == GDK_ACTION_MOVE)
+        {
+            local_move_only = nautilus_canvas_container_selection_items_local
+                                  (container, container->details->dnd_info->drag_info.selection_list);
+        }
+
+        /* If the move is local, there is nothing to do. */
+        if (!local_move_only)
+        {
+            handle_nonlocal_move (container, real_action, drop_target, icon_hit);
+        }
+    }
+
+    g_free (drop_target);
+    stop_cache_selection_list (&container->details->dnd_info->drag_info);
+    nautilus_drag_destroy_selection_list (container->details->dnd_info->drag_info.selection_list);
+    container->details->dnd_info->drag_info.selection_list = NULL;
+}
+
+NautilusDragInfo *
+nautilus_canvas_dnd_get_drag_source_data (NautilusCanvasContainer *container,
+                                          GdkDragContext          *context)
+{
+    return container->details->dnd_source_info;
+}
+
+static void
+nautilus_canvas_container_get_drop_action (NautilusCanvasContainer *container,
+                                           GdkDragContext          *context,
+                                           int                      x,
+                                           int                      y,
+                                           int                     *action)
+{
+    char *drop_target;
+    gboolean icon_hit;
+    double world_x, world_y;
+
+    icon_hit = FALSE;
+    if (!container->details->dnd_info->drag_info.got_drop_data_type)
+    {
+        /* drag_data_received_callback didn't get called yet */
+        return;
+    }
+
+    /* find out if we're over an canvas */
+    canvas_widget_to_world (EEL_CANVAS (container), x, y, &world_x, &world_y);
+    *action = 0;
+
+    drop_target = nautilus_canvas_container_find_drop_target (container,
+                                                              context, x, y, &icon_hit);
+    if (drop_target == NULL)
+    {
+        return;
+    }
+
+    /* case out on the type of object being dragged */
+    switch (container->details->dnd_info->drag_info.data_type)
+    {
+        case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
+        {
+            if (container->details->dnd_info->drag_info.selection_list != NULL)
+            {
+                nautilus_drag_default_drop_action_for_icons (context, drop_target,
+                                                             
container->details->dnd_info->drag_info.selection_list,
+                                                             0,
+                                                             action);
+            }
+        }
+        break;
+
+        case NAUTILUS_ICON_DND_URI_LIST:
+        {
+            *action = nautilus_drag_default_drop_action_for_uri_list (context, drop_target);
+        }
+        break;
+
+        case NAUTILUS_ICON_DND_NETSCAPE_URL:
+        {
+            *action = nautilus_drag_default_drop_action_for_netscape_url (context);
+        }
+        break;
+
+        case NAUTILUS_ICON_DND_ROOTWINDOW_DROP:
+        {
+            *action = gdk_drag_context_get_suggested_action (context);
+        }
+        break;
+
+        case NAUTILUS_ICON_DND_TEXT:
+        case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
+        case NAUTILUS_ICON_DND_RAW:
+        {
+            *action = GDK_ACTION_COPY;
+        }
+        break;
+    }
+
+    g_free (drop_target);
+}
+
+static void
+set_drop_target (NautilusCanvasContainer *container,
+                 NautilusCanvasIcon      *icon)
+{
+    NautilusCanvasIcon *old_icon;
+
+    /* Check if current drop target changed, update icon drop
+     * higlight if needed.
+     */
+    old_icon = container->details->drop_target;
+    if (icon == old_icon)
+    {
+        return;
+    }
+
+    /* Remember the new drop target for the next round. */
+    container->details->drop_target = icon;
+    nautilus_canvas_container_update_icon (container, old_icon);
+    nautilus_canvas_container_update_icon (container, icon);
+}
+
+static void
+nautilus_canvas_dnd_update_drop_target (NautilusCanvasContainer *container,
+                                        GdkDragContext          *context,
+                                        int                      x,
+                                        int                      y)
+{
+    NautilusCanvasIcon *icon;
+    NautilusFile *file;
+    double world_x, world_y;
+    char *uri;
+
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    canvas_widget_to_world (EEL_CANVAS (container), x, y, &world_x, &world_y);
+
+    /* Find the item we hit with our drop, if any. */
+    icon = nautilus_canvas_container_item_at (container, world_x, world_y);
+
+    /* FIXME bugzilla.gnome.org 42485:
+     * These "can_accept_items" tests need to be done by
+     * the canvas view, not here. This file is not supposed to know
+     * that the target is a file.
+     */
+
+    /* Find if target canvas accepts our drop. */
+    if (icon != NULL)
+    {
+        uri = nautilus_canvas_container_get_icon_uri (container, icon);
+        file = nautilus_file_get_by_uri (uri);
+        g_free (uri);
+
+        if (!nautilus_drag_can_accept_info (file,
+                                            container->details->dnd_info->drag_info.data_type,
+                                            container->details->dnd_info->drag_info.selection_list))
+        {
+            icon = NULL;
+        }
+
+        nautilus_file_unref (file);
+    }
+
+    set_drop_target (container, icon);
+}
+
+static void
+remove_hover_timer (NautilusCanvasDndInfo *dnd_info)
+{
+    if (dnd_info->hover_id != 0)
+    {
+        g_source_remove (dnd_info->hover_id);
+        dnd_info->hover_id = 0;
+    }
+}
+
+static void
+nautilus_canvas_container_free_drag_data (NautilusCanvasContainer *container)
+{
+    NautilusCanvasDndInfo *dnd_info;
+
+    dnd_info = container->details->dnd_info;
+
+    dnd_info->drag_info.got_drop_data_type = FALSE;
+
+    if (dnd_info->shadow != NULL)
+    {
+        eel_canvas_item_destroy (dnd_info->shadow);
+        dnd_info->shadow = NULL;
+    }
+
+    if (dnd_info->drag_info.selection_data != NULL)
+    {
+        gtk_selection_data_free (dnd_info->drag_info.selection_data);
+        dnd_info->drag_info.selection_data = NULL;
+    }
+
+    if (dnd_info->drag_info.direct_save_uri != NULL)
+    {
+        g_free (dnd_info->drag_info.direct_save_uri);
+        dnd_info->drag_info.direct_save_uri = NULL;
+    }
+
+    g_free (dnd_info->target_uri);
+    dnd_info->target_uri = NULL;
+
+    remove_hover_timer (dnd_info);
+}
+
+static void
+drag_leave_callback (GtkWidget      *widget,
+                     GdkDragContext *context,
+                     guint32         time,
+                     gpointer        data)
+{
+    NautilusCanvasDndInfo *dnd_info;
+
+    dnd_info = NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info;
+
+    if (dnd_info->shadow != NULL)
+    {
+        eel_canvas_item_hide (dnd_info->shadow);
+    }
+
+    stop_dnd_highlight (widget);
+
+    set_drop_target (NAUTILUS_CANVAS_CONTAINER (widget), NULL);
+    stop_auto_scroll (NAUTILUS_CANVAS_CONTAINER (widget));
+    nautilus_canvas_container_free_drag_data (NAUTILUS_CANVAS_CONTAINER (widget));
+}
+
+static void
+drag_begin_callback (GtkWidget      *widget,
+                     GdkDragContext *context,
+                     gpointer        data)
+{
+    NautilusCanvasContainer *container;
+    NautilusDragInfo *drag_info;
+    NautilusWindow *window;
+    cairo_surface_t *surface;
+    double x1, y1, x2, y2, winx, winy;
+    int x_offset, y_offset;
+    int start_x, start_y;
+    GList *dragged_files;
+    double sx, sy;
+
+    container = NAUTILUS_CANVAS_CONTAINER (widget);
+    window = NAUTILUS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (container)));
+
+    start_x = container->details->dnd_info->drag_info.start_x +
+              gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
+    start_y = container->details->dnd_info->drag_info.start_y +
+              gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));
+
+    /* create a pixmap and mask to drag with */
+    surface = nautilus_canvas_item_get_drag_surface (container->details->drag_icon->item);
+
+    /* compute the image's offset */
+    eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (container->details->drag_icon->item),
+                                &x1, &y1, &x2, &y2);
+    eel_canvas_world_to_window (EEL_CANVAS (container),
+                                x1, y1, &winx, &winy);
+    x_offset = start_x - winx;
+    y_offset = start_y - winy;
+
+    cairo_surface_get_device_scale (surface, &sx, &sy);
+    cairo_surface_set_device_offset (surface,
+                                     -x_offset * sx,
+                                     -y_offset * sy);
+    gtk_drag_set_icon_surface (context, surface);
+    cairo_surface_destroy (surface);
+
+    /* cache the data at the beginning since the view may change */
+    drag_info = &(container->details->dnd_info->drag_info);
+    drag_info->selection_cache = nautilus_drag_create_selection_cache (widget,
+                                                                       each_icon_get_data_binder);
+
+    container->details->dnd_source_info->selection_cache = nautilus_drag_create_selection_cache (widget,
+                                                                                                 
each_icon_get_data_binder);
+
+    dragged_files = nautilus_drag_file_list_from_selection_list (drag_info->selection_cache);
+    if (nautilus_file_list_are_all_folders (dragged_files))
+    {
+        nautilus_window_start_dnd (window, context);
+    }
+    g_list_free_full (dragged_files, g_object_unref);
+}
+
+void
+nautilus_canvas_dnd_begin_drag (NautilusCanvasContainer *container,
+                                GdkDragAction            actions,
+                                int                      button,
+                                GdkEventMotion          *event,
+                                int                      start_x,
+                                int                      start_y)
+{
+    NautilusCanvasDndInfo *dnd_info;
+    NautilusDragInfo *dnd_source_info;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+    g_return_if_fail (event != NULL);
+
+    dnd_info = container->details->dnd_info;
+    container->details->dnd_source_info = g_new0 (NautilusDragInfo, 1);
+    dnd_source_info = container->details->dnd_source_info;
+    g_return_if_fail (dnd_info != NULL);
+
+    /* Notice that the event is in bin_window coordinates, because of
+     *  the way the canvas handles events.
+     */
+    dnd_info->drag_info.start_x = start_x -
+                                  gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE 
(container)));
+    dnd_info->drag_info.start_y = start_y -
+                                  gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE 
(container)));
+
+    dnd_source_info->source_actions = actions;
+    /* start the drag */
+    gtk_drag_begin_with_coordinates (GTK_WIDGET (container),
+                                     dnd_info->drag_info.target_list,
+                                     actions,
+                                     button,
+                                     (GdkEvent *) event,
+                                     dnd_info->drag_info.start_x,
+                                     dnd_info->drag_info.start_y);
+}
+
+static gboolean
+drag_highlight_draw (GtkWidget *widget,
+                     cairo_t   *cr,
+                     gpointer   user_data)
+{
+    gint width, height;
+    GdkWindow *window;
+    GtkStyleContext *style;
+
+    window = gtk_widget_get_window (widget);
+    width = gdk_window_get_width (window);
+    height = gdk_window_get_height (window);
+
+    style = gtk_widget_get_style_context (widget);
+
+    gtk_style_context_save (style);
+    gtk_style_context_add_class (style, GTK_STYLE_CLASS_DND);
+    gtk_style_context_set_state (style, GTK_STATE_FLAG_FOCUSED);
+
+    gtk_render_frame (style,
+                      cr,
+                      0, 0, width, height);
+
+    gtk_style_context_restore (style);
+
+    return FALSE;
+}
+
+/* Queue a redraw of the dnd highlight rect */
+static void
+dnd_highlight_queue_redraw (GtkWidget *widget)
+{
+    NautilusCanvasDndInfo *dnd_info;
+    int width, height;
+    GtkAllocation allocation;
+
+    dnd_info = NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info;
+
+    if (!dnd_info->highlighted)
+    {
+        return;
+    }
+
+    gtk_widget_get_allocation (widget, &allocation);
+    width = allocation.width;
+    height = allocation.height;
+
+    /* we don't know how wide the shadow is exactly,
+     * so we expose a 10-pixel wide border
+     */
+    gtk_widget_queue_draw_area (widget,
+                                0, 0,
+                                width, 10);
+    gtk_widget_queue_draw_area (widget,
+                                0, 0,
+                                10, height);
+    gtk_widget_queue_draw_area (widget,
+                                0, height - 10,
+                                width, 10);
+    gtk_widget_queue_draw_area (widget,
+                                width - 10, 0,
+                                10, height);
+}
+
+static void
+start_dnd_highlight (GtkWidget *widget)
+{
+    NautilusCanvasDndInfo *dnd_info;
+
+    dnd_info = NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info;
+
+    if (!dnd_info->highlighted)
+    {
+        dnd_info->highlighted = TRUE;
+        g_signal_connect_after (widget, "draw",
+                                G_CALLBACK (drag_highlight_draw),
+                                NULL);
+        dnd_highlight_queue_redraw (widget);
+    }
+}
+
+static void
+stop_dnd_highlight (GtkWidget *widget)
+{
+    NautilusCanvasDndInfo *dnd_info;
+
+    dnd_info = NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info;
+
+    if (dnd_info->highlighted)
+    {
+        g_signal_handlers_disconnect_by_func (widget,
+                                              drag_highlight_draw,
+                                              NULL);
+        dnd_highlight_queue_redraw (widget);
+        dnd_info->highlighted = FALSE;
+    }
+}
+
+static gboolean
+hover_timer (gpointer user_data)
+{
+    NautilusCanvasContainer *container = user_data;
+    NautilusCanvasDndInfo *dnd_info;
+
+    dnd_info = container->details->dnd_info;
+
+    dnd_info->hover_id = 0;
+
+    g_signal_emit_by_name (container, "handle-hover", dnd_info->target_uri);
+
+    return FALSE;
+}
+
+static void
+check_hover_timer (NautilusCanvasContainer *container,
+                   const char              *uri)
+{
+    NautilusCanvasDndInfo *dnd_info;
+
+    dnd_info = container->details->dnd_info;
+
+    if (g_strcmp0 (uri, dnd_info->target_uri) == 0)
+    {
+        return;
+    }
+
+    remove_hover_timer (dnd_info);
+
+    g_clear_pointer (&dnd_info->target_uri, g_free);
+
+    if (uri != NULL)
+    {
+        dnd_info->target_uri = g_strdup (uri);
+        dnd_info->hover_id = g_timeout_add (HOVER_TIMEOUT, hover_timer, container);
+    }
+}
+
+static gboolean
+drag_motion_callback (GtkWidget      *widget,
+                      GdkDragContext *context,
+                      int             x,
+                      int             y,
+                      guint32         time)
+{
+    int action;
+
+    nautilus_canvas_container_ensure_drag_data (NAUTILUS_CANVAS_CONTAINER (widget), context, time);
+    nautilus_canvas_container_position_shadow (NAUTILUS_CANVAS_CONTAINER (widget), x, y);
+    nautilus_canvas_dnd_update_drop_target (NAUTILUS_CANVAS_CONTAINER (widget), context, x, y);
+    set_up_auto_scroll_if_needed (NAUTILUS_CANVAS_CONTAINER (widget));
+    /* Find out what the drop actions are based on our drag selection and
+     * the drop target.
+     */
+    action = 0;
+    nautilus_canvas_container_get_drop_action (NAUTILUS_CANVAS_CONTAINER (widget), context, x, y,
+                                               &action);
+    if (action != 0)
+    {
+        char *uri;
+        uri = nautilus_canvas_container_find_drop_target (NAUTILUS_CANVAS_CONTAINER (widget),
+                                                          context, x, y, NULL);
+        check_hover_timer (NAUTILUS_CANVAS_CONTAINER (widget), uri);
+        g_free (uri);
+        start_dnd_highlight (widget);
+    }
+    else
+    {
+        remove_hover_timer (NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info);
+    }
+
+    gdk_drag_status (context, action, time);
+
+    return TRUE;
+}
+
+static gboolean
+drag_drop_callback (GtkWidget      *widget,
+                    GdkDragContext *context,
+                    int             x,
+                    int             y,
+                    guint32         time,
+                    gpointer        data)
+{
+    NautilusCanvasDndInfo *dnd_info;
+
+    dnd_info = NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info;
+
+    /* tell the drag_data_received callback that
+     *  the drop occurred and that it can actually
+     *  process the actions.
+     *  make sure it is going to be called at least once.
+     */
+    dnd_info->drag_info.drop_occurred = TRUE;
+
+    get_data_on_first_target_we_support (widget, context, time, x, y);
+
+    return TRUE;
+}
+
+void
+nautilus_canvas_dnd_end_drag (NautilusCanvasContainer *container)
+{
+    NautilusCanvasDndInfo *dnd_info;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    dnd_info = container->details->dnd_info;
+    g_return_if_fail (dnd_info != NULL);
+    stop_auto_scroll (container);
+    /* Do nothing.
+     * Can that possibly be right?
+     */
+}
+
+/** this callback is called in 2 cases.
+ *   It is called upon drag_motion events to get the actual data
+ *   In that case, it just makes sure it gets the data.
+ *   It is called upon drop_drop events to execute the actual
+ *   actions on the received action. In that case, it actually first makes sure
+ *   that we have got the data then processes it.
+ */
+
+static void
+drag_data_received_callback (GtkWidget        *widget,
+                             GdkDragContext   *context,
+                             int               x,
+                             int               y,
+                             GtkSelectionData *data,
+                             guint             info,
+                             guint32           time,
+                             gpointer          user_data)
+{
+    NautilusDragInfo *drag_info;
+    guchar *tmp;
+    const guchar *tmp_raw;
+    int length;
+    gboolean success;
+
+    drag_info = &(NAUTILUS_CANVAS_CONTAINER (widget)->details->dnd_info->drag_info);
+
+    drag_info->got_drop_data_type = TRUE;
+    drag_info->data_type = info;
+
+    switch (info)
+    {
+        case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
+        {
+            nautilus_canvas_container_dropped_canvas_feedback (widget, data, x, y);
+        }
+        break;
+
+        case NAUTILUS_ICON_DND_URI_LIST:
+        case NAUTILUS_ICON_DND_TEXT:
+        case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
+        case NAUTILUS_ICON_DND_RAW:
+        {
+            /* Save the data so we can do the actual work on drop. */
+            if (drag_info->selection_data != NULL)
+            {
+                gtk_selection_data_free (drag_info->selection_data);
+            }
+            drag_info->selection_data = gtk_selection_data_copy (data);
+        }
+        break;
+
+        /* Netscape keeps sending us the data, even though we accept the first drag */
+        case NAUTILUS_ICON_DND_NETSCAPE_URL:
+        {
+            if (drag_info->selection_data != NULL)
+            {
+                gtk_selection_data_free (drag_info->selection_data);
+                drag_info->selection_data = gtk_selection_data_copy (data);
+            }
+        }
+        break;
+
+        case NAUTILUS_ICON_DND_ROOTWINDOW_DROP:
+        {
+            /* Do nothing, this won't even happen, since we don't want to call get_data twice */
+        }
+        break;
+    }
+
+    /* this is the second use case of this callback.
+     * we have to do the actual work for the drop.
+     */
+    if (drag_info->drop_occurred)
+    {
+        success = FALSE;
+        switch (info)
+        {
+            case NAUTILUS_ICON_DND_GNOME_ICON_LIST:
+            {
+                nautilus_canvas_container_receive_dropped_icons
+                    (NAUTILUS_CANVAS_CONTAINER (widget),
+                    context, x, y);
+            }
+            break;
+
+            case NAUTILUS_ICON_DND_NETSCAPE_URL:
+            {
+                receive_dropped_netscape_url
+                    (NAUTILUS_CANVAS_CONTAINER (widget),
+                    (char *) gtk_selection_data_get_data (data), context, x, y);
+                success = TRUE;
+            }
+            break;
+
+            case NAUTILUS_ICON_DND_URI_LIST:
+            {
+                receive_dropped_uri_list
+                    (NAUTILUS_CANVAS_CONTAINER (widget),
+                    (char *) gtk_selection_data_get_data (data), context, x, y);
+                success = TRUE;
+            }
+            break;
+
+            case NAUTILUS_ICON_DND_TEXT:
+            {
+                tmp = gtk_selection_data_get_text (data);
+                receive_dropped_text
+                    (NAUTILUS_CANVAS_CONTAINER (widget),
+                    (char *) tmp, context, x, y);
+                success = TRUE;
+                g_free (tmp);
+            }
+            break;
+
+            case NAUTILUS_ICON_DND_RAW:
+            {
+                length = gtk_selection_data_get_length (data);
+                tmp_raw = gtk_selection_data_get_data (data);
+                receive_dropped_raw
+                    (NAUTILUS_CANVAS_CONTAINER (widget),
+                    (const gchar *) tmp_raw, length, drag_info->direct_save_uri,
+                    context, x, y);
+                success = TRUE;
+            }
+            break;
+
+            case NAUTILUS_ICON_DND_ROOTWINDOW_DROP:
+            {
+                /* Do nothing, everything is done by the sender */
+            }
+            break;
+
+            case NAUTILUS_ICON_DND_XDNDDIRECTSAVE:
+            {
+                const guchar *selection_data;
+                gint selection_length;
+                gint selection_format;
+
+                selection_data = gtk_selection_data_get_data (drag_info->selection_data);
+                selection_length = gtk_selection_data_get_length (drag_info->selection_data);
+                selection_format = gtk_selection_data_get_format (drag_info->selection_data);
+
+                if (selection_format == 8 &&
+                    selection_length == 1 &&
+                    selection_data[0] == 'F')
+                {
+                    gtk_drag_get_data (widget, context,
+                                       gdk_atom_intern (NAUTILUS_ICON_DND_RAW_TYPE,
+                                                        FALSE),
+                                       time);
+                    return;
+                }
+                else if (selection_format == 8 &&
+                         selection_length == 1 &&
+                         selection_data[0] == 'F' &&
+                         drag_info->direct_save_uri != NULL)
+                {
+                    GFile *location;
+
+                    location = g_file_new_for_uri (drag_info->direct_save_uri);
+
+                    nautilus_file_changes_queue_file_added (location);
+                    g_object_unref (location);
+                    nautilus_file_changes_consume_changes (TRUE);
+                    success = TRUE;
+                }
+            }     /* NAUTILUS_ICON_DND_XDNDDIRECTSAVE */
+            break;
+        }
+        gtk_drag_finish (context, success, FALSE, time);
+
+        nautilus_canvas_container_free_drag_data (NAUTILUS_CANVAS_CONTAINER (widget));
+
+        set_drop_target (NAUTILUS_CANVAS_CONTAINER (widget), NULL);
+
+        /* reinitialise it for the next dnd */
+        drag_info->drop_occurred = FALSE;
+    }
+}
+
+void
+nautilus_canvas_dnd_init (NautilusCanvasContainer *container)
+{
+    GtkTargetList *targets;
+    int n_elements;
+
+    g_return_if_fail (container != NULL);
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+
+    container->details->dnd_info = g_new0 (NautilusCanvasDndInfo, 1);
+    nautilus_drag_init (&container->details->dnd_info->drag_info,
+                        drag_types, G_N_ELEMENTS (drag_types), TRUE);
+
+    /* Set up the widget as a drag destination.
+     * (But not a source, as drags starting from this widget will be
+     * implemented by dealing with events manually.)
+     */
+    n_elements = G_N_ELEMENTS (drop_types) - 1;
+    gtk_drag_dest_set (GTK_WIDGET (container),
+                       0,
+                       drop_types, n_elements,
+                       GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
+
+    targets = gtk_drag_dest_get_target_list (GTK_WIDGET (container));
+    gtk_target_list_add_text_targets (targets, NAUTILUS_ICON_DND_TEXT);
+
+
+    /* Messages for outgoing drag. */
+    g_signal_connect (container, "drag-begin",
+                      G_CALLBACK (drag_begin_callback), NULL);
+    g_signal_connect (container, "drag-data-get",
+                      G_CALLBACK (drag_data_get_callback), NULL);
+    g_signal_connect (container, "drag-end",
+                      G_CALLBACK (drag_end_callback), NULL);
+
+    /* Messages for incoming drag. */
+    g_signal_connect (container, "drag-data-received",
+                      G_CALLBACK (drag_data_received_callback), NULL);
+    g_signal_connect (container, "drag-motion",
+                      G_CALLBACK (drag_motion_callback), NULL);
+    g_signal_connect (container, "drag-drop",
+                      G_CALLBACK (drag_drop_callback), NULL);
+    g_signal_connect (container, "drag-leave",
+                      G_CALLBACK (drag_leave_callback), NULL);
+}
+
+void
+nautilus_canvas_dnd_fini (NautilusCanvasContainer *container)
+{
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
+
+    if (container->details->dnd_info != NULL)
+    {
+        stop_auto_scroll (container);
+
+        nautilus_drag_finalize (&container->details->dnd_info->drag_info);
+        container->details->dnd_info = NULL;
+    }
+}
diff --git a/src/nautilus-canvas-dnd.h b/src/nautilus-canvas-dnd.h
new file mode 100644
index 000000000..0e8b980e3
--- /dev/null
+++ b/src/nautilus-canvas-dnd.h
@@ -0,0 +1,56 @@
+
+/* nautilus-canvas-dnd.h - Drag & drop handling for the canvas container widget.
+
+   Copyright (C) 1999, 2000 Free Software Foundation
+   Copyright (C) 2000 Eazel, Inc.
+
+   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,
+   see <http://www.gnu.org/licenses/>.
+
+   Authors: Ettore Perazzoli <ettore gnu org>,
+            Darin Adler <darin bentspoon com>,
+           Andy Hertzfeld <andy eazel com>
+*/
+
+#pragma once
+
+#include "nautilus-canvas-container.h"
+#include "nautilus-dnd.h"
+
+/* DnD-related information. */
+typedef struct {
+       /* inherited drag info context */
+       NautilusDragInfo drag_info;
+
+       gboolean highlighted;
+       char *target_uri;
+
+       /* Shadow for the icons being dragged.  */
+       EelCanvasItem *shadow;
+       guint hover_id;
+} NautilusCanvasDndInfo;
+
+
+void   nautilus_canvas_dnd_init                  (NautilusCanvasContainer *container);
+void   nautilus_canvas_dnd_fini                  (NautilusCanvasContainer *container);
+void   nautilus_canvas_dnd_begin_drag            (NautilusCanvasContainer *container,
+                                                 GdkDragAction          actions,
+                                                 gint                   button,
+                                                 GdkEventMotion        *event,
+                                                 int                    start_x,
+                                                 int                    start_y);
+void   nautilus_canvas_dnd_end_drag              (NautilusCanvasContainer *container);
+
+NautilusDragInfo* nautilus_canvas_dnd_get_drag_source_data (NautilusCanvasContainer *container,
+                                                            GdkDragContext          *context);
diff --git a/src/nautilus-canvas-item.c b/src/nautilus-canvas-item.c
new file mode 100644
index 000000000..762c8832a
--- /dev/null
+++ b/src/nautilus-canvas-item.c
@@ -0,0 +1,2760 @@
+/* Nautilus - Canvas item class for canvas container.
+ *
+ * Copyright (C) 2000 Eazel, Inc
+ *
+ * Author: Andy Hertzfeld <andy eazel com>
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <math.h>
+#include "nautilus-canvas-item.h"
+
+#include <glib/gi18n.h>
+
+#include "nautilus-file-utilities.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-canvas-private.h"
+#include <eel/eel-art-extensions.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-graphic-effects.h>
+#include <eel/eel-string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n.h>
+#include <atk/atkimage.h>
+#include <atk/atkcomponent.h>
+#include <atk/atknoopobject.h>
+#include <stdio.h>
+#include <string.h>
+
+/* gap between bottom of icon and start of text box */
+#define LABEL_OFFSET 1
+#define LABEL_LINE_SPACING 0
+
+/* Text padding */
+#define TEXT_BACK_PADDING_X 4
+#define TEXT_BACK_PADDING_Y 1
+
+/* Width of the label, keep in sync with ICON_GRID_WIDTH at nautilus-canvas-container.c */
+#define MAX_TEXT_WIDTH_SMALL 116
+#define MAX_TEXT_WIDTH_STANDARD 104
+#define MAX_TEXT_WIDTH_LARGE 98
+#define MAX_TEXT_WIDTH_LARGER 100
+
+/* special text height handling
+ * each item has three text height variables:
+ *  + text_height: actual height of the displayed (i.e. on-screen) PangoLayout.
+ *  + text_height_for_layout: height used in canvas grid layout algorithms.
+ *                    “sane amount” of text.
+ *   “sane amount“ as of
+ *      + hard-coded to three lines in text-below-icon mode.
+ *
+ *  This layout height is used by grid layout algorithms, even
+ *  though the actually displayed and/or requested text size may be larger
+ *  and overlap adjacent icons, if an icon is selected.
+ *
+ *  + text_height_for_entire_text: height needed to display the entire PangoLayout,
+ *    if it wasn't ellipsized.
+ */
+
+/* Private part of the NautilusCanvasItem structure. */
+struct NautilusCanvasItemDetails
+{
+    /* The image, text, font. */
+    double x, y;
+    GdkPixbuf *pixbuf;
+    cairo_surface_t *rendered_surface;
+    char *editable_text;                /* Text that can be modified by a renaming function */
+    char *additional_text;              /* Text that cannot be modifed, such as file size, etc. */
+
+    /* Size of the text at current font. */
+    int text_dx;
+    int text_width;
+
+    /* actual size required for rendering the text to display */
+    int text_height;
+    /* actual size that would be required for rendering the entire text if it wasn't ellipsized */
+    int text_height_for_entire_text;
+    /* actual size needed for rendering a “sane amount” of text */
+    int text_height_for_layout;
+
+    int editable_text_height;
+
+    /* whether the entire text must always be visible. In that case,
+     * text_height_for_layout will always be equal to text_height.
+     * Used for the last line of a line-wise icon layout. */
+    guint entire_text : 1;
+
+    /* Highlight state. */
+    guint is_highlighted_for_selection : 1;
+    guint is_highlighted_as_keyboard_focus : 1;
+    guint is_highlighted_for_drop : 1;
+    guint is_highlighted_for_clipboard : 1;
+    guint is_prelit : 1;
+
+    guint rendered_is_highlighted_for_selection : 1;
+    guint rendered_is_highlighted_for_drop : 1;
+    guint rendered_is_highlighted_for_clipboard : 1;
+    guint rendered_is_prelit : 1;
+    guint rendered_is_focused : 1;
+
+    guint bounds_cached : 1;
+
+    guint is_visible : 1;
+
+    /* Cached PangoLayouts. Only used if the icon is visible */
+    PangoLayout *editable_text_layout;
+    PangoLayout *additional_text_layout;
+
+    /* Cached rectangle in canvas coordinates */
+    EelIRect icon_rect;
+    EelIRect text_rect;
+
+    EelIRect bounds_cache;
+    EelIRect bounds_cache_for_layout;
+    EelIRect bounds_cache_for_entire_item;
+
+    GdkWindow *cursor_window;
+
+    GString *text;
+};
+
+/* Object argument IDs. */
+enum
+{
+    PROP_0,
+    PROP_EDITABLE_TEXT,
+    PROP_ADDITIONAL_TEXT,
+    PROP_HIGHLIGHTED_FOR_SELECTION,
+    PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
+    PROP_HIGHLIGHTED_FOR_DROP,
+    PROP_HIGHLIGHTED_FOR_CLIPBOARD
+};
+
+typedef enum
+{
+    RIGHT_SIDE,
+    BOTTOM_SIDE,
+    LEFT_SIDE,
+    TOP_SIDE
+} RectangleSide;
+
+static GType nautilus_canvas_item_accessible_factory_get_type (void);
+
+G_DEFINE_TYPE (NautilusCanvasItem, nautilus_canvas_item, EEL_TYPE_CANVAS_ITEM)
+
+/* private */
+static void     get_icon_rectangle (NautilusCanvasItem *item,
+                                    EelIRect           *rect);
+static PangoLayout *get_label_layout (PangoLayout       **layout,
+                                      NautilusCanvasItem *item,
+                                      const char         *text);
+
+static void       nautilus_canvas_item_ensure_bounds_up_to_date (NautilusCanvasItem *canvas_item);
+
+/* Object initialization function for the canvas item. */
+static void
+nautilus_canvas_item_init (NautilusCanvasItem *canvas_item)
+{
+    canvas_item->details = G_TYPE_INSTANCE_GET_PRIVATE ((canvas_item), NAUTILUS_TYPE_CANVAS_ITEM, 
NautilusCanvasItemDetails);
+    nautilus_canvas_item_invalidate_label_size (canvas_item);
+}
+
+static void
+nautilus_canvas_item_finalize (GObject *object)
+{
+    NautilusCanvasItemDetails *details;
+
+    g_assert (NAUTILUS_IS_CANVAS_ITEM (object));
+
+    details = NAUTILUS_CANVAS_ITEM (object)->details;
+
+    if (details->cursor_window != NULL)
+    {
+        gdk_window_set_cursor (details->cursor_window, NULL);
+        g_object_unref (details->cursor_window);
+    }
+
+    if (details->pixbuf != NULL)
+    {
+        g_object_unref (details->pixbuf);
+    }
+
+    if (details->text != NULL)
+    {
+        g_string_free (details->text, TRUE);
+        details->text = NULL;
+    }
+
+    g_free (details->editable_text);
+    g_free (details->additional_text);
+
+    if (details->rendered_surface != NULL)
+    {
+        cairo_surface_destroy (details->rendered_surface);
+    }
+
+    if (details->editable_text_layout != NULL)
+    {
+        g_object_unref (details->editable_text_layout);
+    }
+
+    if (details->additional_text_layout != NULL)
+    {
+        g_object_unref (details->additional_text_layout);
+    }
+
+    G_OBJECT_CLASS (nautilus_canvas_item_parent_class)->finalize (object);
+}
+
+/* Currently we require pixbufs in this format (for hit testing).
+ * Perhaps gdk-pixbuf will be changed so it can do the hit testing
+ * and we won't have this requirement any more.
+ */
+static gboolean
+pixbuf_is_acceptable (GdkPixbuf *pixbuf)
+{
+    return gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB
+           && ((!gdk_pixbuf_get_has_alpha (pixbuf)
+                && gdk_pixbuf_get_n_channels (pixbuf) == 3)
+               || (gdk_pixbuf_get_has_alpha (pixbuf)
+                   && gdk_pixbuf_get_n_channels (pixbuf) == 4))
+           && gdk_pixbuf_get_bits_per_sample (pixbuf) == 8;
+}
+
+static void
+nautilus_canvas_item_invalidate_bounds_cache (NautilusCanvasItem *item)
+{
+    item->details->bounds_cached = FALSE;
+}
+
+/* invalidate the text width and height cached in the item details. */
+void
+nautilus_canvas_item_invalidate_label_size (NautilusCanvasItem *item)
+{
+    if (item->details->editable_text_layout != NULL)
+    {
+        pango_layout_context_changed (item->details->editable_text_layout);
+    }
+    if (item->details->additional_text_layout != NULL)
+    {
+        pango_layout_context_changed (item->details->additional_text_layout);
+    }
+    nautilus_canvas_item_invalidate_bounds_cache (item);
+    item->details->text_width = -1;
+    item->details->text_height = -1;
+    item->details->text_height_for_layout = -1;
+    item->details->text_height_for_entire_text = -1;
+    item->details->editable_text_height = -1;
+}
+
+/* Set property handler for the canvas item. */
+static void
+nautilus_canvas_item_set_property (GObject      *object,
+                                   guint         property_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+    NautilusCanvasItem *item;
+    NautilusCanvasItemDetails *details;
+    AtkObject *accessible;
+    gboolean is_rename;
+
+    item = NAUTILUS_CANVAS_ITEM (object);
+    accessible = atk_gobject_accessible_for_object (G_OBJECT (item));
+    details = item->details;
+
+    switch (property_id)
+    {
+        case PROP_EDITABLE_TEXT:
+        {
+            if (g_strcmp0 (details->editable_text,
+                           g_value_get_string (value)) == 0)
+            {
+                return;
+            }
+
+            is_rename = details->editable_text != NULL;
+            g_free (details->editable_text);
+            details->editable_text = g_strdup (g_value_get_string (value));
+            if (details->text)
+            {
+                details->text = g_string_assign (details->text, details->editable_text);
+
+                if (is_rename)
+                {
+                    g_object_notify (G_OBJECT (accessible), "accessible-name");
+                }
+            }
+
+            nautilus_canvas_item_invalidate_label_size (item);
+            if (details->editable_text_layout)
+            {
+                g_object_unref (details->editable_text_layout);
+                details->editable_text_layout = NULL;
+            }
+        }
+        break;
+
+        case PROP_ADDITIONAL_TEXT:
+        {
+            if (g_strcmp0 (details->additional_text,
+                           g_value_get_string (value)) == 0)
+            {
+                return;
+            }
+
+            g_free (details->additional_text);
+            details->additional_text = g_strdup (g_value_get_string (value));
+
+            nautilus_canvas_item_invalidate_label_size (item);
+            if (details->additional_text_layout)
+            {
+                g_object_unref (details->additional_text_layout);
+                details->additional_text_layout = NULL;
+            }
+        }
+        break;
+
+        case PROP_HIGHLIGHTED_FOR_SELECTION:
+        {
+            if (!details->is_highlighted_for_selection == !g_value_get_boolean (value))
+            {
+                return;
+            }
+            details->is_highlighted_for_selection = g_value_get_boolean (value);
+            nautilus_canvas_item_invalidate_label_size (item);
+
+            atk_object_notify_state_change (accessible, ATK_STATE_SELECTED,
+                                            details->is_highlighted_for_selection);
+        }
+        break;
+
+        case PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS:
+        {
+            if (!details->is_highlighted_as_keyboard_focus == !g_value_get_boolean (value))
+            {
+                return;
+            }
+            details->is_highlighted_as_keyboard_focus = g_value_get_boolean (value);
+
+            atk_object_notify_state_change (accessible, ATK_STATE_FOCUSED,
+                                            details->is_highlighted_as_keyboard_focus);
+        }
+        break;
+
+        case PROP_HIGHLIGHTED_FOR_DROP:
+        {
+            if (!details->is_highlighted_for_drop == !g_value_get_boolean (value))
+            {
+                return;
+            }
+            details->is_highlighted_for_drop = g_value_get_boolean (value);
+        }
+        break;
+
+        case PROP_HIGHLIGHTED_FOR_CLIPBOARD:
+        {
+            if (!details->is_highlighted_for_clipboard == !g_value_get_boolean (value))
+            {
+                return;
+            }
+            details->is_highlighted_for_clipboard = g_value_get_boolean (value);
+        }
+        break;
+
+        default:
+        {
+            g_warning ("nautilus_canvas_item_set_property on unknown argument");
+            return;
+        }
+    }
+
+    eel_canvas_item_request_update (EEL_CANVAS_ITEM (object));
+}
+
+/* Get property handler for the canvas item */
+static void
+nautilus_canvas_item_get_property (GObject    *object,
+                                   guint       property_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+    NautilusCanvasItemDetails *details;
+
+    details = NAUTILUS_CANVAS_ITEM (object)->details;
+
+    switch (property_id)
+    {
+        case PROP_EDITABLE_TEXT:
+        {
+            g_value_set_string (value, details->editable_text);
+        }
+        break;
+
+        case PROP_ADDITIONAL_TEXT:
+        {
+            g_value_set_string (value, details->additional_text);
+        }
+        break;
+
+        case PROP_HIGHLIGHTED_FOR_SELECTION:
+        {
+            g_value_set_boolean (value, details->is_highlighted_for_selection);
+        }
+        break;
+
+        case PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS:
+        {
+            g_value_set_boolean (value, details->is_highlighted_as_keyboard_focus);
+        }
+        break;
+
+        case PROP_HIGHLIGHTED_FOR_DROP:
+        {
+            g_value_set_boolean (value, details->is_highlighted_for_drop);
+        }
+        break;
+
+        case PROP_HIGHLIGHTED_FOR_CLIPBOARD:
+        {
+            g_value_set_boolean (value, details->is_highlighted_for_clipboard);
+        }
+        break;
+
+        default:
+        {
+            g_warning ("invalid property %d", property_id);
+        }
+        break;
+    }
+}
+
+static void
+get_scaled_icon_size (NautilusCanvasItem *item,
+                      gint               *width,
+                      gint               *height)
+{
+    EelCanvas *canvas;
+    GdkPixbuf *pixbuf = NULL;
+    gint scale;
+
+    if (item != NULL)
+    {
+        canvas = EEL_CANVAS_ITEM (item)->canvas;
+        scale = gtk_widget_get_scale_factor (GTK_WIDGET (canvas));
+        pixbuf = item->details->pixbuf;
+    }
+
+    if (width)
+    {
+        *width = (pixbuf == NULL) ? 0 : (gdk_pixbuf_get_width (pixbuf) / scale);
+    }
+    if (height)
+    {
+        *height = (pixbuf == NULL) ? 0 : (gdk_pixbuf_get_height (pixbuf) / scale);
+    }
+}
+
+void
+nautilus_canvas_item_set_image (NautilusCanvasItem *item,
+                                GdkPixbuf          *image)
+{
+    NautilusCanvasItemDetails *details;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_ITEM (item));
+    g_return_if_fail (image == NULL || pixbuf_is_acceptable (image));
+
+    details = item->details;
+    if (details->pixbuf == image)
+    {
+        return;
+    }
+
+    if (image != NULL)
+    {
+        g_object_ref (image);
+    }
+    if (details->pixbuf != NULL)
+    {
+        g_object_unref (details->pixbuf);
+    }
+    if (details->rendered_surface != NULL)
+    {
+        cairo_surface_destroy (details->rendered_surface);
+        details->rendered_surface = NULL;
+    }
+
+    details->pixbuf = image;
+
+    nautilus_canvas_item_invalidate_bounds_cache (item);
+    eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
+}
+
+/* Recomputes the bounding box of a canvas item.
+ * This is a generic implementation that could be used for any canvas item
+ * class, it has no assumptions about how the item is used.
+ */
+static void
+recompute_bounding_box (NautilusCanvasItem *canvas_item,
+                        double              i2w_dx,
+                        double              i2w_dy)
+{
+    /* The bounds stored in the item is the same as what get_bounds
+     * returns, except it's in canvas coordinates instead of the item's
+     * parent's coordinates.
+     */
+
+    EelCanvasItem *item;
+    EelDRect bounds_rect;
+
+    item = EEL_CANVAS_ITEM (canvas_item);
+
+    eel_canvas_item_get_bounds (item,
+                                &bounds_rect.x0, &bounds_rect.y0,
+                                &bounds_rect.x1, &bounds_rect.y1);
+
+    bounds_rect.x0 += i2w_dx;
+    bounds_rect.y0 += i2w_dy;
+    bounds_rect.x1 += i2w_dx;
+    bounds_rect.y1 += i2w_dy;
+    eel_canvas_w2c_d (item->canvas,
+                      bounds_rect.x0, bounds_rect.y0,
+                      &item->x1, &item->y1);
+    eel_canvas_w2c_d (item->canvas,
+                      bounds_rect.x1, bounds_rect.y1,
+                      &item->x2, &item->y2);
+}
+
+static EelIRect
+compute_text_rectangle (const NautilusCanvasItem      *item,
+                        EelIRect                       icon_rectangle,
+                        gboolean                       canvas_coords,
+                        NautilusCanvasItemBoundsUsage  usage)
+{
+    EelIRect text_rectangle;
+    double pixels_per_unit;
+    double text_width, text_height, text_height_for_layout, text_height_for_entire_text, real_text_height;
+
+    pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+    if (canvas_coords)
+    {
+        text_width = item->details->text_width;
+        text_height = item->details->text_height;
+        text_height_for_layout = item->details->text_height_for_layout;
+        text_height_for_entire_text = item->details->text_height_for_entire_text;
+    }
+    else
+    {
+        text_width = item->details->text_width / pixels_per_unit;
+        text_height = item->details->text_height / pixels_per_unit;
+        text_height_for_layout = item->details->text_height_for_layout / pixels_per_unit;
+        text_height_for_entire_text = item->details->text_height_for_entire_text / pixels_per_unit;
+    }
+
+    text_rectangle.x0 = (icon_rectangle.x0 + icon_rectangle.x1) / 2 - (int) text_width / 2;
+    text_rectangle.y0 = icon_rectangle.y1;
+    text_rectangle.x1 = text_rectangle.x0 + text_width;
+
+    if (usage == BOUNDS_USAGE_FOR_LAYOUT)
+    {
+        real_text_height = text_height_for_layout;
+    }
+    else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM)
+    {
+        real_text_height = text_height_for_entire_text;
+    }
+    else if (usage == BOUNDS_USAGE_FOR_DISPLAY)
+    {
+        real_text_height = text_height;
+    }
+    else
+    {
+        g_assert_not_reached ();
+    }
+
+    text_rectangle.y1 = text_rectangle.y0 + real_text_height + LABEL_OFFSET / pixels_per_unit;
+
+    return text_rectangle;
+}
+
+static EelIRect
+get_current_canvas_bounds (EelCanvasItem *item)
+{
+    EelIRect bounds;
+
+    g_assert (EEL_IS_CANVAS_ITEM (item));
+
+    bounds.x0 = item->x1;
+    bounds.y0 = item->y1;
+    bounds.x1 = item->x2;
+    bounds.y1 = item->y2;
+
+    return bounds;
+}
+
+void
+nautilus_canvas_item_update_bounds (NautilusCanvasItem *item,
+                                    double              i2w_dx,
+                                    double              i2w_dy)
+{
+    EelIRect before, after;
+    EelCanvasItem *canvas_item;
+
+    canvas_item = EEL_CANVAS_ITEM (item);
+
+    /* Compute new bounds. */
+    before = get_current_canvas_bounds (canvas_item);
+    recompute_bounding_box (item, i2w_dx, i2w_dy);
+    after = get_current_canvas_bounds (canvas_item);
+
+    /* If the bounds didn't change, we are done. */
+    if (eel_irect_equal (before, after))
+    {
+        return;
+    }
+
+    /* Update canvas and text rect cache */
+    get_icon_rectangle (item, &item->details->icon_rect);
+    item->details->text_rect = compute_text_rectangle (item, item->details->icon_rect,
+                                                       TRUE, BOUNDS_USAGE_FOR_DISPLAY);
+
+    /* queue a redraw. */
+    eel_canvas_request_redraw (canvas_item->canvas,
+                               before.x0, before.y0,
+                               before.x1 + 1, before.y1 + 1);
+}
+
+/* Update handler for the canvas canvas item. */
+static void
+nautilus_canvas_item_update (EelCanvasItem *item,
+                             double         i2w_dx,
+                             double         i2w_dy,
+                             gint           flags)
+{
+    nautilus_canvas_item_update_bounds (NAUTILUS_CANVAS_ITEM (item), i2w_dx, i2w_dy);
+
+    eel_canvas_item_request_redraw (EEL_CANVAS_ITEM (item));
+
+    EEL_CANVAS_ITEM_CLASS (nautilus_canvas_item_parent_class)->update (item, i2w_dx, i2w_dy, flags);
+}
+
+/* Rendering */
+static gboolean
+in_single_click_mode (void)
+{
+    int click_policy;
+
+    click_policy = g_settings_get_enum (nautilus_preferences,
+                                        NAUTILUS_PREFERENCES_CLICK_POLICY);
+
+    return click_policy == NAUTILUS_CLICK_POLICY_SINGLE;
+}
+
+
+/* Keep these for a bit while we work on performance of draw_or_measure_label_text. */
+/*
+ #define PERFORMANCE_TEST_DRAW_DISABLE
+ #define PERFORMANCE_TEST_MEASURE_DISABLE
+ */
+
+/* This gets the size of the layout from the position of the layout.
+ * This means that if the layout is right aligned we get the full width
+ * of the layout, not just the width of the text snippet on the right side
+ */
+static void
+layout_get_full_size (PangoLayout *layout,
+                      int         *width,
+                      int         *height,
+                      int         *dx)
+{
+    PangoRectangle logical_rect;
+    int the_width, total_width;
+
+    pango_layout_get_extents (layout, NULL, &logical_rect);
+    the_width = (logical_rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
+    total_width = (logical_rect.x + logical_rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
+
+    if (width != NULL)
+    {
+        *width = the_width;
+    }
+
+    if (height != NULL)
+    {
+        *height = (logical_rect.height + PANGO_SCALE / 2) / PANGO_SCALE;
+    }
+
+    if (dx != NULL)
+    {
+        *dx = total_width - the_width;
+    }
+}
+
+static void
+layout_get_size_for_layout (PangoLayout *layout,
+                            int          max_layout_line_count,
+                            int          height_for_entire_text,
+                            int         *height_for_layout)
+{
+    PangoLayoutIter *iter;
+    PangoRectangle logical_rect;
+    int i;
+
+    /* only use the first max_layout_line_count lines for the gridded auto layout */
+    if (pango_layout_get_line_count (layout) <= max_layout_line_count)
+    {
+        *height_for_layout = height_for_entire_text;
+    }
+    else
+    {
+        *height_for_layout = 0;
+        iter = pango_layout_get_iter (layout);
+        for (i = 0; i < max_layout_line_count; i++)
+        {
+            pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
+            *height_for_layout += (logical_rect.height + PANGO_SCALE / 2) / PANGO_SCALE;
+
+            if (!pango_layout_iter_next_line (iter))
+            {
+                break;
+            }
+
+            *height_for_layout += pango_layout_get_spacing (layout);
+        }
+        pango_layout_iter_free (iter);
+    }
+}
+
+static double
+nautilus_canvas_item_get_max_text_width (NautilusCanvasItem *item)
+{
+    EelCanvasItem *canvas_item;
+    NautilusCanvasContainer *container;
+    guint max_text_width;
+
+
+    canvas_item = EEL_CANVAS_ITEM (item);
+    container = NAUTILUS_CANVAS_CONTAINER (canvas_item->canvas);
+
+    switch (nautilus_canvas_container_get_zoom_level (container))
+    {
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL:
+        {
+            max_text_width = MAX_TEXT_WIDTH_SMALL;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_STANDARD:
+        {
+            max_text_width = MAX_TEXT_WIDTH_STANDARD;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGE:
+        {
+            max_text_width = MAX_TEXT_WIDTH_LARGE;
+        }
+        break;
+
+        case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER:
+        {
+            max_text_width = MAX_TEXT_WIDTH_LARGER;
+        }
+        break;
+
+        default:
+        {
+            g_warning ("Zoom level not valid. This may incur in missaligned grid");
+            max_text_width = MAX_TEXT_WIDTH_STANDARD;
+        }
+    }
+
+    return max_text_width * canvas_item->canvas->pixels_per_unit - 2 * TEXT_BACK_PADDING_X;
+}
+
+static void
+prepare_pango_layout_width (NautilusCanvasItem *item,
+                            PangoLayout        *layout)
+{
+    pango_layout_set_width (layout, floor (nautilus_canvas_item_get_max_text_width (item)) * PANGO_SCALE);
+    pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
+}
+
+static void
+prepare_pango_layout_for_measure_entire_text (NautilusCanvasItem *item,
+                                              PangoLayout        *layout)
+{
+    prepare_pango_layout_width (item, layout);
+    pango_layout_set_height (layout, G_MININT);
+}
+
+static void
+prepare_pango_layout_for_draw (NautilusCanvasItem *item,
+                               PangoLayout        *layout)
+{
+    NautilusCanvasItemDetails *details;
+    NautilusCanvasContainer *container;
+    gboolean needs_highlight;
+
+    prepare_pango_layout_width (item, layout);
+
+    container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+    details = item->details;
+
+    needs_highlight = details->is_highlighted_for_selection || details->is_highlighted_for_drop;
+
+    if (needs_highlight ||
+        details->is_highlighted_as_keyboard_focus ||
+        details->entire_text)
+    {
+        /* VOODOO-TODO, cf. compute_text_rectangle() */
+        pango_layout_set_height (layout, G_MININT);
+    }
+    else
+    {
+        /* TODO? we might save some resources, when the re-layout is not neccessary in case
+         * the layout height already fits into max. layout lines. But pango should figure this
+         * out itself (which it doesn't ATM).
+         */
+        pango_layout_set_height (layout,
+                                 nautilus_canvas_container_get_max_layout_lines_for_pango (container));
+    }
+}
+
+static void
+measure_label_text (NautilusCanvasItem *item)
+{
+    NautilusCanvasItemDetails *details;
+    NautilusCanvasContainer *container;
+    gint editable_height, editable_height_for_layout, editable_height_for_entire_text, editable_width, 
editable_dx;
+    gint additional_height, additional_width, additional_dx;
+    PangoLayout *editable_layout;
+    PangoLayout *additional_layout;
+    gboolean have_editable, have_additional;
+
+    /* check to see if the cached values are still valid; if so, there's
+     * no work necessary
+     */
+
+    if (item->details->text_width >= 0 && item->details->text_height >= 0)
+    {
+        return;
+    }
+
+    details = item->details;
+
+    have_editable = details->editable_text != NULL && details->editable_text[0] != '\0';
+    have_additional = details->additional_text != NULL && details->additional_text[0] != '\0';
+
+    /* No font or no text, then do no work. */
+    if (!have_editable && !have_additional)
+    {
+        details->text_height = 0;
+        details->text_height_for_layout = 0;
+        details->text_height_for_entire_text = 0;
+        details->text_width = 0;
+        return;
+    }
+
+#ifdef PERFORMANCE_TEST_MEASURE_DISABLE
+    /* fake out the width */
+    details->text_width = 80;
+    details->text_height = 20;
+    details->text_height_for_layout = 20;
+    details->text_height_for_entire_text = 20;
+    return;
+#endif
+
+    editable_width = 0;
+    editable_height = 0;
+    editable_height_for_layout = 0;
+    editable_height_for_entire_text = 0;
+    editable_dx = 0;
+    additional_width = 0;
+    additional_height = 0;
+    additional_dx = 0;
+
+    container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+    editable_layout = NULL;
+    additional_layout = NULL;
+
+    if (have_editable)
+    {
+        /* first, measure required text height: editable_height_for_entire_text
+         * then, measure text height applicable for layout: editable_height_for_layout
+         * next, measure actually displayed height: editable_height
+         */
+        editable_layout = get_label_layout (&details->editable_text_layout, item, details->editable_text);
+
+        prepare_pango_layout_for_measure_entire_text (item, editable_layout);
+        layout_get_full_size (editable_layout,
+                              NULL,
+                              &editable_height_for_entire_text,
+                              NULL);
+        layout_get_size_for_layout (editable_layout,
+                                    nautilus_canvas_container_get_max_layout_lines (container),
+                                    editable_height_for_entire_text,
+                                    &editable_height_for_layout);
+
+        prepare_pango_layout_for_draw (item, editable_layout);
+        layout_get_full_size (editable_layout,
+                              &editable_width,
+                              &editable_height,
+                              &editable_dx);
+    }
+
+    if (have_additional)
+    {
+        additional_layout = get_label_layout (&details->additional_text_layout, item, 
details->additional_text);
+        prepare_pango_layout_for_draw (item, additional_layout);
+        layout_get_full_size (additional_layout,
+                              &additional_width, &additional_height, &additional_dx);
+    }
+
+    details->editable_text_height = editable_height;
+
+    if (editable_width > additional_width)
+    {
+        details->text_width = editable_width;
+        details->text_dx = editable_dx;
+    }
+    else
+    {
+        details->text_width = additional_width;
+        details->text_dx = additional_dx;
+    }
+
+    if (have_additional)
+    {
+        details->text_height = editable_height + LABEL_LINE_SPACING + additional_height;
+        details->text_height_for_layout = editable_height_for_layout + LABEL_LINE_SPACING + 
additional_height;
+        details->text_height_for_entire_text = editable_height_for_entire_text + LABEL_LINE_SPACING + 
additional_height;
+    }
+    else
+    {
+        details->text_height = editable_height;
+        details->text_height_for_layout = editable_height_for_layout;
+        details->text_height_for_entire_text = editable_height_for_entire_text;
+    }
+
+    /* add some extra space for highlighting even when we don't highlight so things won't move */
+
+    /* extra slop for nicer highlighting */
+    details->text_height += TEXT_BACK_PADDING_Y * 2;
+    details->text_height_for_layout += TEXT_BACK_PADDING_Y * 2;
+    details->text_height_for_entire_text += TEXT_BACK_PADDING_Y * 2;
+    details->editable_text_height += TEXT_BACK_PADDING_Y * 2;
+
+    /* extra to make it look nicer */
+    details->text_width += TEXT_BACK_PADDING_X * 2;
+
+    if (editable_layout)
+    {
+        g_object_unref (editable_layout);
+    }
+
+    if (additional_layout)
+    {
+        g_object_unref (additional_layout);
+    }
+}
+
+static void
+draw_label_text (NautilusCanvasItem *item,
+                 cairo_t            *cr,
+                 EelIRect            icon_rect)
+{
+    NautilusCanvasItemDetails *details;
+    NautilusCanvasContainer *container;
+    PangoLayout *editable_layout;
+    PangoLayout *additional_layout;
+    GtkStyleContext *context;
+    GtkStateFlags state, base_state;
+    gboolean have_editable, have_additional;
+    gboolean needs_highlight;
+    EelIRect text_rect;
+    int x;
+    int max_text_width;
+    gdouble frame_w, frame_h, frame_x, frame_y;
+    gboolean draw_frame = TRUE;
+
+#ifdef PERFORMANCE_TEST_DRAW_DISABLE
+    return;
+#endif
+
+    details = item->details;
+
+    measure_label_text (item);
+    if (details->text_height == 0 ||
+        details->text_width == 0)
+    {
+        return;
+    }
+
+    container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+    context = gtk_widget_get_style_context (GTK_WIDGET (container));
+
+    text_rect = compute_text_rectangle (item, icon_rect, TRUE, BOUNDS_USAGE_FOR_DISPLAY);
+
+    needs_highlight = details->is_highlighted_for_selection || details->is_highlighted_for_drop;
+
+    editable_layout = NULL;
+    additional_layout = NULL;
+
+    have_editable = details->editable_text != NULL && details->editable_text[0] != '\0';
+    have_additional = details->additional_text != NULL && details->additional_text[0] != '\0';
+    g_assert (have_editable || have_additional);
+
+    max_text_width = floor (nautilus_canvas_item_get_max_text_width (item));
+
+    base_state = gtk_widget_get_state_flags (GTK_WIDGET (container));
+    base_state &= ~(GTK_STATE_FLAG_SELECTED |
+                    GTK_STATE_FLAG_PRELIGHT);
+    state = base_state;
+
+    /* if the canvas is highlighted, do some set-up */
+    if (needs_highlight)
+    {
+        state |= GTK_STATE_FLAG_SELECTED;
+
+        frame_x = text_rect.x0;
+        frame_y = text_rect.y0;
+        frame_w = text_rect.x1 - text_rect.x0;
+        frame_h = text_rect.y1 - text_rect.y0;
+    }
+    else
+    {
+        draw_frame = FALSE;
+    }
+
+    if (draw_frame)
+    {
+        gtk_style_context_save (context);
+        gtk_style_context_set_state (context, state);
+
+        gtk_render_frame (context, cr,
+                          frame_x, frame_y,
+                          frame_w, frame_h);
+        gtk_render_background (context, cr,
+                               frame_x, frame_y,
+                               frame_w, frame_h);
+
+        gtk_style_context_restore (context);
+    }
+
+    x = text_rect.x0 + ((text_rect.x1 - text_rect.x0) - max_text_width) / 2;
+
+    if (have_editable)
+    {
+        state = base_state;
+
+        if (needs_highlight)
+        {
+            state |= GTK_STATE_FLAG_SELECTED;
+        }
+
+        editable_layout = get_label_layout (&item->details->editable_text_layout, item, 
item->details->editable_text);
+        prepare_pango_layout_for_draw (item, editable_layout);
+
+        gtk_style_context_save (context);
+        gtk_style_context_set_state (context, state);
+
+        gtk_render_layout (context, cr,
+                           x, text_rect.y0 + TEXT_BACK_PADDING_Y,
+                           editable_layout);
+
+        gtk_style_context_restore (context);
+    }
+
+    if (have_additional)
+    {
+        state = base_state;
+
+        if (needs_highlight)
+        {
+            state |= GTK_STATE_FLAG_SELECTED;
+        }
+
+        additional_layout = get_label_layout (&item->details->additional_text_layout, item, 
item->details->additional_text);
+        prepare_pango_layout_for_draw (item, additional_layout);
+
+        gtk_style_context_save (context);
+        gtk_style_context_set_state (context, state);
+        gtk_style_context_add_class (context, "dim-label");
+
+        gtk_render_layout (context, cr,
+                           x, text_rect.y0 + details->editable_text_height + LABEL_LINE_SPACING + 
TEXT_BACK_PADDING_Y,
+                           additional_layout);
+
+        gtk_style_context_restore (context);
+    }
+
+    if (item->details->is_highlighted_as_keyboard_focus)
+    {
+        if (needs_highlight)
+        {
+            state = GTK_STATE_FLAG_SELECTED;
+        }
+
+        gtk_style_context_save (context);
+        gtk_style_context_set_state (context, state);
+
+        gtk_render_focus (context,
+                          cr,
+                          text_rect.x0,
+                          text_rect.y0,
+                          text_rect.x1 - text_rect.x0,
+                          text_rect.y1 - text_rect.y0);
+
+        gtk_style_context_restore (context);
+    }
+
+    if (editable_layout != NULL)
+    {
+        g_object_unref (editable_layout);
+    }
+
+    if (additional_layout != NULL)
+    {
+        g_object_unref (additional_layout);
+    }
+}
+
+void
+nautilus_canvas_item_set_is_visible (NautilusCanvasItem *item,
+                                     gboolean            visible)
+{
+    if (item->details->is_visible == visible)
+    {
+        return;
+    }
+
+    item->details->is_visible = visible;
+
+    if (!visible)
+    {
+        nautilus_canvas_item_invalidate_label (item);
+    }
+}
+
+void
+nautilus_canvas_item_invalidate_label (NautilusCanvasItem *item)
+{
+    nautilus_canvas_item_invalidate_label_size (item);
+
+    if (item->details->editable_text_layout)
+    {
+        g_object_unref (item->details->editable_text_layout);
+        item->details->editable_text_layout = NULL;
+    }
+
+    if (item->details->additional_text_layout)
+    {
+        g_object_unref (item->details->additional_text_layout);
+        item->details->additional_text_layout = NULL;
+    }
+}
+
+/* shared code to highlight or dim the passed-in pixbuf */
+static cairo_surface_t *
+real_map_surface (NautilusCanvasItem *canvas_item)
+{
+    EelCanvas *canvas;
+    g_autoptr (GdkPixbuf) temp_pixbuf = NULL;
+    gint scale_factor;
+    GdkWindow *window;
+
+    canvas = EEL_CANVAS_ITEM (canvas_item)->canvas;
+    temp_pixbuf = g_object_ref (canvas_item->details->pixbuf);
+    scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (canvas));
+    window = gtk_widget_get_window (GTK_WIDGET (canvas));
+
+    if (canvas_item->details->is_prelit ||
+        canvas_item->details->is_highlighted_for_clipboard)
+    {
+        g_autoptr (GdkPixbuf) old_pixbuf = NULL;
+
+        old_pixbuf = temp_pixbuf;
+        temp_pixbuf = eel_create_spotlight_pixbuf (temp_pixbuf);
+    }
+
+    if (canvas_item->details->is_highlighted_for_selection
+        || canvas_item->details->is_highlighted_for_drop)
+    {
+        GtkWidget *widget;
+        GtkStyleContext *style;
+        gboolean has_focus;
+        GtkStateFlags state;
+        gint width;
+        gint height;
+        gboolean has_alpha;
+        cairo_format_t format;
+        cairo_surface_t *surface;
+        cairo_t *cr;
+        g_autoptr (GdkPixbuf) pixbuf = NULL;
+        g_autoptr (GdkPixbuf) old_pixbuf = NULL;
+
+        widget = GTK_WIDGET (canvas);
+        style = gtk_widget_get_style_context (widget);
+        has_focus = gtk_widget_has_focus (widget);
+        state = has_focus ? GTK_STATE_FLAG_SELECTED : GTK_STATE_FLAG_ACTIVE;
+        width = gdk_pixbuf_get_width (temp_pixbuf);
+        height = gdk_pixbuf_get_height (temp_pixbuf);
+        has_alpha = gdk_pixbuf_get_has_alpha (temp_pixbuf);
+        format = has_alpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24;
+        surface = cairo_image_surface_create (format, width, height);
+        cr = cairo_create (surface);
+
+        gtk_style_context_save (style);
+        gtk_style_context_set_state (style, state);
+
+        gtk_render_background (style, cr,
+                               0, 0,
+                               width, height);
+
+        gtk_style_context_restore (style);
+
+        cairo_surface_flush (surface);
+
+        pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height);
+        old_pixbuf = temp_pixbuf;
+
+        temp_pixbuf = eel_create_colorized_pixbuf (temp_pixbuf, g_steal_pointer (&pixbuf));
+
+        cairo_destroy (cr);
+        cairo_surface_destroy (surface);
+    }
+
+    return gdk_cairo_surface_create_from_pixbuf (temp_pixbuf, scale_factor, window);
+}
+
+static cairo_surface_t *
+map_surface (NautilusCanvasItem *canvas_item)
+{
+    if (!(canvas_item->details->rendered_surface != NULL
+          && canvas_item->details->rendered_is_prelit == canvas_item->details->is_prelit
+          && canvas_item->details->rendered_is_highlighted_for_selection == 
canvas_item->details->is_highlighted_for_selection
+          && canvas_item->details->rendered_is_highlighted_for_drop == 
canvas_item->details->is_highlighted_for_drop
+          && canvas_item->details->rendered_is_highlighted_for_clipboard == 
canvas_item->details->is_highlighted_for_clipboard
+          && (canvas_item->details->is_highlighted_for_selection && 
canvas_item->details->rendered_is_focused == gtk_widget_has_focus (GTK_WIDGET (EEL_CANVAS_ITEM 
(canvas_item)->canvas)))))
+    {
+        if (canvas_item->details->rendered_surface != NULL)
+        {
+            cairo_surface_destroy (canvas_item->details->rendered_surface);
+        }
+        canvas_item->details->rendered_surface = real_map_surface (canvas_item);
+        canvas_item->details->rendered_is_prelit = canvas_item->details->is_prelit;
+        canvas_item->details->rendered_is_highlighted_for_selection = 
canvas_item->details->is_highlighted_for_selection;
+        canvas_item->details->rendered_is_highlighted_for_drop = 
canvas_item->details->is_highlighted_for_drop;
+        canvas_item->details->rendered_is_highlighted_for_clipboard = 
canvas_item->details->is_highlighted_for_clipboard;
+        canvas_item->details->rendered_is_focused = gtk_widget_has_focus (GTK_WIDGET (EEL_CANVAS_ITEM 
(canvas_item)->canvas));
+    }
+
+    cairo_surface_reference (canvas_item->details->rendered_surface);
+
+    return canvas_item->details->rendered_surface;
+}
+
+cairo_surface_t *
+nautilus_canvas_item_get_drag_surface (NautilusCanvasItem *item)
+{
+    cairo_surface_t *surface;
+    EelCanvas *canvas;
+    int width, height;
+    int pix_width, pix_height;
+    int item_offset_x, item_offset_y;
+    EelIRect icon_rect;
+    double item_x, item_y;
+    cairo_t *cr;
+    GtkStyleContext *context;
+    cairo_surface_t *drag_surface;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_ITEM (item), NULL);
+
+    canvas = EEL_CANVAS_ITEM (item)->canvas;
+    context = gtk_widget_get_style_context (GTK_WIDGET (canvas));
+
+    gtk_style_context_save (context);
+    gtk_style_context_add_class (context, "nautilus-canvas-item");
+
+    /* Assume we're updated so canvas item data is right */
+
+    /* Calculate the offset from the top-left corner of the
+     *  new image to the item position (where the pixmap is placed) */
+    eel_canvas_world_to_window (canvas,
+                                item->details->x, item->details->y,
+                                &item_x, &item_y);
+
+    item_offset_x = item_x - EEL_CANVAS_ITEM (item)->x1;
+    item_offset_y = item_y - EEL_CANVAS_ITEM (item)->y1;
+
+    /* Calculate the width of the item */
+    width = EEL_CANVAS_ITEM (item)->x2 - EEL_CANVAS_ITEM (item)->x1;
+    height = EEL_CANVAS_ITEM (item)->y2 - EEL_CANVAS_ITEM (item)->y1;
+
+    surface = gdk_window_create_similar_surface (gtk_widget_get_window (GTK_WIDGET (canvas)),
+                                                 CAIRO_CONTENT_COLOR_ALPHA,
+                                                 width, height);
+    cr = cairo_create (surface);
+
+    drag_surface = map_surface (item);
+    gtk_render_icon_surface (context, cr, drag_surface,
+                             item_offset_x, item_offset_y);
+    cairo_surface_destroy (drag_surface);
+
+    get_scaled_icon_size (item, &pix_width, &pix_height);
+
+    icon_rect.x0 = item_offset_x;
+    icon_rect.y0 = item_offset_y;
+    icon_rect.x1 = item_offset_x + pix_width;
+    icon_rect.y1 = item_offset_y + pix_height;
+
+    draw_label_text (item, cr, icon_rect);
+    cairo_destroy (cr);
+
+    gtk_style_context_restore (context);
+
+    return surface;
+}
+
+/* Draw the canvas item for non-anti-aliased mode. */
+static void
+nautilus_canvas_item_draw (EelCanvasItem  *item,
+                           cairo_t        *cr,
+                           cairo_region_t *region)
+{
+    NautilusCanvasContainer *container;
+    NautilusCanvasItem *canvas_item;
+    NautilusCanvasItemDetails *details;
+    EelIRect icon_rect;
+    cairo_surface_t *temp_surface;
+    GtkStyleContext *context;
+
+    container = NAUTILUS_CANVAS_CONTAINER (item->canvas);
+    canvas_item = NAUTILUS_CANVAS_ITEM (item);
+    details = canvas_item->details;
+
+    /* Draw the pixbuf. */
+    if (details->pixbuf == NULL)
+    {
+        return;
+    }
+
+    context = gtk_widget_get_style_context (GTK_WIDGET (container));
+    gtk_style_context_save (context);
+    gtk_style_context_add_class (context, "nautilus-canvas-item");
+
+    icon_rect = canvas_item->details->icon_rect;
+    temp_surface = map_surface (canvas_item);
+
+    gtk_render_icon_surface (context, cr,
+                             temp_surface,
+                             icon_rect.x0, icon_rect.y0);
+    cairo_surface_destroy (temp_surface);
+
+    /* Draw the label text. */
+    draw_label_text (canvas_item, cr, icon_rect);
+
+    gtk_style_context_restore (context);
+}
+
+#define ZERO_WIDTH_SPACE "\xE2\x80\x8B"
+
+static PangoLayout *
+create_label_layout (NautilusCanvasItem *item,
+                     const char         *text)
+{
+    PangoLayout *layout;
+    PangoContext *context;
+    PangoFontDescription *desc;
+    NautilusCanvasContainer *container;
+    EelCanvasItem *canvas_item;
+    GString *str;
+    char *zeroified_text;
+    const char *p;
+
+    canvas_item = EEL_CANVAS_ITEM (item);
+
+    container = NAUTILUS_CANVAS_CONTAINER (canvas_item->canvas);
+    context = gtk_widget_get_pango_context (GTK_WIDGET (canvas_item->canvas));
+    layout = pango_layout_new (context);
+
+    zeroified_text = NULL;
+
+    if (text != NULL)
+    {
+        str = g_string_new (NULL);
+
+        for (p = text; *p != '\0'; p++)
+        {
+            str = g_string_append_c (str, *p);
+
+            if (*p == '_' || *p == '-' || (*p == '.' && !g_ascii_isdigit (*(p + 1))))
+            {
+                /* Ensure that we allow to break after '_' or '.' characters,
+                 * if they are not followed by a number */
+                str = g_string_append (str, ZERO_WIDTH_SPACE);
+            }
+        }
+
+        zeroified_text = g_string_free (str, FALSE);
+    }
+
+    pango_layout_set_text (layout, zeroified_text, -1);
+    pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
+
+    pango_layout_set_spacing (layout, LABEL_LINE_SPACING);
+    pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
+
+#if PANGO_VERSION_CHECK (1, 44, 4)
+    {
+        PangoAttrList *attr_list = pango_attr_list_new ();
+
+        pango_attr_list_insert (attr_list, pango_attr_insert_hyphens_new (FALSE));
+        pango_layout_set_attributes (layout, attr_list);
+        pango_attr_list_unref (attr_list);
+    }
+#endif
+
+    /* Create a font description */
+    if (container->details->font)
+    {
+        desc = pango_font_description_from_string (container->details->font);
+    }
+    else
+    {
+        desc = pango_font_description_copy (pango_context_get_font_description (context));
+    }
+    pango_layout_set_font_description (layout, desc);
+    pango_font_description_free (desc);
+    g_free (zeroified_text);
+
+    return layout;
+}
+
+static PangoLayout *
+get_label_layout (PangoLayout        **layout_cache,
+                  NautilusCanvasItem  *item,
+                  const char          *text)
+{
+    PangoLayout *layout;
+
+    if (*layout_cache != NULL)
+    {
+        return g_object_ref (*layout_cache);
+    }
+
+    layout = create_label_layout (item, text);
+
+    if (item->details->is_visible)
+    {
+        *layout_cache = g_object_ref (layout);
+    }
+
+    return layout;
+}
+
+/* handle events */
+
+static int
+nautilus_canvas_item_event (EelCanvasItem *item,
+                            GdkEvent      *event)
+{
+    NautilusCanvasItem *canvas_item;
+    GdkCursor *cursor;
+    GdkWindow *cursor_window;
+
+    canvas_item = NAUTILUS_CANVAS_ITEM (item);
+    cursor_window = ((GdkEventAny *) event)->window;
+
+    switch (event->type)
+    {
+        case GDK_ENTER_NOTIFY:
+        {
+            if (!canvas_item->details->is_prelit)
+            {
+                canvas_item->details->is_prelit = TRUE;
+                nautilus_canvas_item_invalidate_label_size (canvas_item);
+                eel_canvas_item_request_update (item);
+                eel_canvas_item_send_behind (item,
+                                             NAUTILUS_CANVAS_CONTAINER 
(item->canvas)->details->rubberband_info.selection_rectangle);
+
+                /* show a hand cursor */
+                if (in_single_click_mode ())
+                {
+                    cursor = gdk_cursor_new_for_display (gdk_display_get_default (),
+                                                         GDK_HAND2);
+                    gdk_window_set_cursor (cursor_window, cursor);
+                    g_object_unref (cursor);
+
+                    canvas_item->details->cursor_window = g_object_ref (cursor_window);
+                }
+            }
+            return TRUE;
+        }
+
+        case GDK_LEAVE_NOTIFY:
+        {
+            if (canvas_item->details->is_prelit
+                || canvas_item->details->is_highlighted_for_drop)
+            {
+                /* When leaving, turn of the prelight state and the
+                 * higlighted for drop. The latter gets turned on
+                 * by the drag&drop motion callback.
+                 */
+                canvas_item->details->is_prelit = FALSE;
+                canvas_item->details->is_highlighted_for_drop = FALSE;
+                nautilus_canvas_item_invalidate_label_size (canvas_item);
+                eel_canvas_item_request_update (item);
+
+                /* show default cursor */
+                gdk_window_set_cursor (cursor_window, NULL);
+                g_clear_object (&canvas_item->details->cursor_window);
+            }
+            return TRUE;
+        }
+
+        default:
+        {
+            /* Don't eat up other events; canvas container might use them. */
+            return FALSE;
+        }
+    }
+}
+
+static gboolean
+hit_test (NautilusCanvasItem *canvas_item,
+          EelIRect            icon_rect)
+{
+    NautilusCanvasItemDetails *details;
+
+    details = canvas_item->details;
+
+    /* Quick check to see if the rect hits the canvas or text at all. */
+    if (!eel_irect_hits_irect (canvas_item->details->icon_rect, icon_rect)
+        && (!eel_irect_hits_irect (details->text_rect, icon_rect)))
+    {
+        return FALSE;
+    }
+
+    /* Check for hit in the canvas. */
+    if (eel_irect_hits_irect (canvas_item->details->icon_rect, icon_rect))
+    {
+        return TRUE;
+    }
+
+    /* Check for hit in the text. */
+    if (eel_irect_hits_irect (details->text_rect, icon_rect))
+    {
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/* Point handler for the canvas canvas item. */
+static double
+nautilus_canvas_item_point (EelCanvasItem  *item,
+                            double          x,
+                            double          y,
+                            int             cx,
+                            int             cy,
+                            EelCanvasItem **actual_item)
+{
+    EelIRect icon_rect;
+
+    *actual_item = item;
+    icon_rect.x0 = cx;
+    icon_rect.y0 = cy;
+    icon_rect.x1 = cx + 1;
+    icon_rect.y1 = cy + 1;
+    if (hit_test (NAUTILUS_CANVAS_ITEM (item), icon_rect))
+    {
+        return 0.0;
+    }
+    else
+    {
+        /* This value means not hit.
+         * It's kind of arbitrary. Can we do better?
+         */
+        return item->canvas->pixels_per_unit * 2 + 10;
+    }
+}
+
+static void
+nautilus_canvas_item_translate (EelCanvasItem *item,
+                                double         dx,
+                                double         dy)
+{
+    NautilusCanvasItem *canvas_item;
+    NautilusCanvasItemDetails *details;
+
+    canvas_item = NAUTILUS_CANVAS_ITEM (item);
+    details = canvas_item->details;
+
+    details->x += dx;
+    details->y += dy;
+}
+
+void
+nautilus_canvas_item_get_bounds_for_layout (NautilusCanvasItem *canvas_item,
+                                            double             *x1,
+                                            double             *y1,
+                                            double             *x2,
+                                            double             *y2)
+{
+    NautilusCanvasItemDetails *details;
+    EelIRect *total_rect;
+
+    details = canvas_item->details;
+
+    nautilus_canvas_item_ensure_bounds_up_to_date (canvas_item);
+    g_assert (details->bounds_cached);
+
+    total_rect = &details->bounds_cache_for_layout;
+
+    /* Return the result. */
+    if (x1 != NULL)
+    {
+        *x1 = (int) details->x + total_rect->x0;
+    }
+    if (y1 != NULL)
+    {
+        *y1 = (int) details->y + total_rect->y0;
+    }
+    if (x2 != NULL)
+    {
+        *x2 = (int) details->x + total_rect->x1 + 1;
+    }
+    if (y2 != NULL)
+    {
+        *y2 = (int) details->y + total_rect->y1 + 1;
+    }
+}
+
+void
+nautilus_canvas_item_get_bounds_for_entire_item (NautilusCanvasItem *canvas_item,
+                                                 double             *x1,
+                                                 double             *y1,
+                                                 double             *x2,
+                                                 double             *y2)
+{
+    NautilusCanvasItemDetails *details;
+    EelIRect *total_rect;
+
+    details = canvas_item->details;
+
+    nautilus_canvas_item_ensure_bounds_up_to_date (canvas_item);
+    g_assert (details->bounds_cached);
+
+    total_rect = &details->bounds_cache_for_entire_item;
+
+    /* Return the result. */
+    if (x1 != NULL)
+    {
+        *x1 = (int) details->x + total_rect->x0;
+    }
+    if (y1 != NULL)
+    {
+        *y1 = (int) details->y + total_rect->y0;
+    }
+    if (x2 != NULL)
+    {
+        *x2 = (int) details->x + total_rect->x1 + 1;
+    }
+    if (y2 != NULL)
+    {
+        *y2 = (int) details->y + total_rect->y1 + 1;
+    }
+}
+
+/* Bounds handler for the canvas canvas item. */
+static void
+nautilus_canvas_item_bounds (EelCanvasItem *item,
+                             double        *x1,
+                             double        *y1,
+                             double        *x2,
+                             double        *y2)
+{
+    NautilusCanvasItem *canvas_item;
+    NautilusCanvasItemDetails *details;
+    EelIRect *total_rect;
+
+    canvas_item = NAUTILUS_CANVAS_ITEM (item);
+    details = canvas_item->details;
+
+    g_assert (x1 != NULL);
+    g_assert (y1 != NULL);
+    g_assert (x2 != NULL);
+    g_assert (y2 != NULL);
+
+    nautilus_canvas_item_ensure_bounds_up_to_date (canvas_item);
+    g_assert (details->bounds_cached);
+
+    total_rect = &details->bounds_cache;
+
+    /* Return the result. */
+    *x1 = (int) details->x + total_rect->x0;
+    *y1 = (int) details->y + total_rect->y0;
+    *x2 = (int) details->x + total_rect->x1 + 1;
+    *y2 = (int) details->y + total_rect->y1 + 1;
+}
+
+static void
+nautilus_canvas_item_ensure_bounds_up_to_date (NautilusCanvasItem *canvas_item)
+{
+    NautilusCanvasItemDetails *details;
+    EelIRect icon_rect;
+    EelIRect text_rect, text_rect_for_layout, text_rect_for_entire_text;
+    EelIRect total_rect, total_rect_for_layout, total_rect_for_entire_text;
+    EelCanvasItem *item;
+    double pixels_per_unit;
+    gint width, height;
+
+    details = canvas_item->details;
+    item = EEL_CANVAS_ITEM (canvas_item);
+
+    if (!details->bounds_cached)
+    {
+        measure_label_text (canvas_item);
+
+        pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+
+        /* Compute scaled canvas rectangle. */
+        icon_rect.x0 = 0;
+        icon_rect.y0 = 0;
+
+        get_scaled_icon_size (canvas_item, &width, &height);
+
+        icon_rect.x1 = width / pixels_per_unit;
+        icon_rect.y1 = height / pixels_per_unit;
+
+        /* Compute text rectangle. */
+        text_rect = compute_text_rectangle (canvas_item, icon_rect, FALSE, BOUNDS_USAGE_FOR_DISPLAY);
+        text_rect_for_layout = compute_text_rectangle (canvas_item, icon_rect, FALSE, 
BOUNDS_USAGE_FOR_LAYOUT);
+        text_rect_for_entire_text = compute_text_rectangle (canvas_item, icon_rect, FALSE, 
BOUNDS_USAGE_FOR_ENTIRE_ITEM);
+
+        /* Compute total rectangle */
+        eel_irect_union (&total_rect, &icon_rect, &text_rect);
+        eel_irect_union (&total_rect_for_layout, &icon_rect, &text_rect_for_layout);
+        eel_irect_union (&total_rect_for_entire_text, &icon_rect, &text_rect_for_entire_text);
+
+        details->bounds_cache = total_rect;
+        details->bounds_cache_for_layout = total_rect_for_layout;
+        details->bounds_cache_for_entire_item = total_rect_for_entire_text;
+        details->bounds_cached = TRUE;
+    }
+}
+
+/* Get the rectangle of the canvas only, in world coordinates. */
+EelDRect
+nautilus_canvas_item_get_icon_rectangle (const NautilusCanvasItem *item)
+{
+    EelDRect rectangle;
+    double pixels_per_unit;
+    gint width, height;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_ITEM (item), eel_drect_empty);
+
+    rectangle.x0 = item->details->x;
+    rectangle.y0 = item->details->y;
+
+    pixels_per_unit = EEL_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+    get_scaled_icon_size (NAUTILUS_CANVAS_ITEM (item), &width, &height);
+    rectangle.x1 = rectangle.x0 + width / pixels_per_unit;
+    rectangle.y1 = rectangle.y0 + height / pixels_per_unit;
+
+    eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
+                         &rectangle.x0,
+                         &rectangle.y0);
+    eel_canvas_item_i2w (EEL_CANVAS_ITEM (item),
+                         &rectangle.x1,
+                         &rectangle.y1);
+
+    return rectangle;
+}
+
+/* Get the rectangle of the icon only, in canvas coordinates. */
+static void
+get_icon_rectangle (NautilusCanvasItem *item,
+                    EelIRect           *rect)
+{
+    gint width, height;
+
+    g_assert (NAUTILUS_IS_CANVAS_ITEM (item));
+    g_assert (rect != NULL);
+
+
+    eel_canvas_w2c (EEL_CANVAS_ITEM (item)->canvas,
+                    item->details->x,
+                    item->details->y,
+                    &rect->x0,
+                    &rect->y0);
+
+    get_scaled_icon_size (item, &width, &height);
+
+    rect->x1 = rect->x0 + width;
+    rect->y1 = rect->y0 + height;
+}
+
+/* nautilus_canvas_item_hit_test_rectangle
+ *
+ * Check and see if there is an intersection between the item and the
+ * canvas rect.
+ */
+gboolean
+nautilus_canvas_item_hit_test_rectangle (NautilusCanvasItem *item,
+                                         EelIRect            icon_rect)
+{
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_ITEM (item), FALSE);
+
+    return hit_test (item, icon_rect);
+}
+
+void
+nautilus_canvas_item_set_entire_text (NautilusCanvasItem *item,
+                                      gboolean            entire_text)
+{
+    if (item->details->entire_text != entire_text)
+    {
+        item->details->entire_text = entire_text;
+
+        nautilus_canvas_item_invalidate_label_size (item);
+        eel_canvas_item_request_update (EEL_CANVAS_ITEM (item));
+    }
+}
+
+/* Class initialization function for the canvas canvas item. */
+static void
+nautilus_canvas_item_class_init (NautilusCanvasItemClass *class)
+{
+    GObjectClass *object_class;
+    EelCanvasItemClass *item_class;
+
+    object_class = G_OBJECT_CLASS (class);
+    item_class = EEL_CANVAS_ITEM_CLASS (class);
+
+    object_class->finalize = nautilus_canvas_item_finalize;
+    object_class->set_property = nautilus_canvas_item_set_property;
+    object_class->get_property = nautilus_canvas_item_get_property;
+
+    g_object_class_install_property (
+        object_class,
+        PROP_EDITABLE_TEXT,
+        g_param_spec_string ("editable_text",
+                             "editable text",
+                             "the editable label",
+                             "", G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        object_class,
+        PROP_ADDITIONAL_TEXT,
+        g_param_spec_string ("additional_text",
+                             "additional text",
+                             "some more text",
+                             "", G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        object_class,
+        PROP_HIGHLIGHTED_FOR_SELECTION,
+        g_param_spec_boolean ("highlighted_for_selection",
+                              "highlighted for selection",
+                              "whether we are highlighted for a selection",
+                              FALSE, G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        object_class,
+        PROP_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
+        g_param_spec_boolean ("highlighted_as_keyboard_focus",
+                              "highlighted as keyboard focus",
+                              "whether we are highlighted to render keyboard focus",
+                              FALSE, G_PARAM_READWRITE));
+
+
+    g_object_class_install_property (
+        object_class,
+        PROP_HIGHLIGHTED_FOR_DROP,
+        g_param_spec_boolean ("highlighted_for_drop",
+                              "highlighted for drop",
+                              "whether we are highlighted for a D&D drop",
+                              FALSE, G_PARAM_READWRITE));
+
+    g_object_class_install_property (
+        object_class,
+        PROP_HIGHLIGHTED_FOR_CLIPBOARD,
+        g_param_spec_boolean ("highlighted_for_clipboard",
+                              "highlighted for clipboard",
+                              "whether we are highlighted for a clipboard paste (after we have been cut)",
+                              FALSE, G_PARAM_READWRITE));
+
+    item_class->update = nautilus_canvas_item_update;
+    item_class->draw = nautilus_canvas_item_draw;
+    item_class->point = nautilus_canvas_item_point;
+    item_class->translate = nautilus_canvas_item_translate;
+    item_class->bounds = nautilus_canvas_item_bounds;
+    item_class->event = nautilus_canvas_item_event;
+
+    atk_registry_set_factory_type (atk_get_default_registry (),
+                                   NAUTILUS_TYPE_CANVAS_ITEM,
+                                   nautilus_canvas_item_accessible_factory_get_type ());
+
+    g_type_class_add_private (class, sizeof (NautilusCanvasItemDetails));
+}
+
+/* ============================= a11y interfaces =========================== */
+
+static const char *nautilus_canvas_item_accessible_action_names[] =
+{
+    "open",
+    "menu",
+    NULL
+};
+
+static const char *nautilus_canvas_item_accessible_action_descriptions[] =
+{
+    "Open item",
+    "Popup context menu",
+    NULL
+};
+
+enum
+{
+    ACTION_OPEN,
+    ACTION_MENU,
+    LAST_ACTION
+};
+
+typedef struct
+{
+    char *action_descriptions[LAST_ACTION];
+    char *image_description;
+    char *description;
+} NautilusCanvasItemAccessiblePrivate;
+
+typedef struct
+{
+    NautilusCanvasItem *item;
+    gint action_number;
+} NautilusCanvasItemAccessibleActionContext;
+
+typedef struct
+{
+    EelCanvasItemAccessible parent;
+    NautilusCanvasItemAccessiblePrivate *priv;
+} NautilusCanvasItemAccessible;
+
+typedef struct
+{
+    EelCanvasItemAccessibleClass parent_class;
+} NautilusCanvasItemAccessibleClass;
+
+#define GET_ACCESSIBLE_PRIV(o) ((NautilusCanvasItemAccessible *) o)->priv;
+
+/* accessible AtkAction interface */
+static gboolean
+nautilus_canvas_item_accessible_idle_do_action (gpointer data)
+{
+    NautilusCanvasItem *item;
+    NautilusCanvasItemAccessibleActionContext *ctx;
+    NautilusCanvasIcon *icon;
+    NautilusCanvasContainer *container;
+    GList *selection;
+    GList file_list;
+    GdkEventButton button_event = { 0 };
+    gint action_number;
+
+    container = NAUTILUS_CANVAS_CONTAINER (data);
+    container->details->a11y_item_action_idle_handler = 0;
+    while (!g_queue_is_empty (container->details->a11y_item_action_queue))
+    {
+        ctx = g_queue_pop_head (container->details->a11y_item_action_queue);
+        action_number = ctx->action_number;
+        item = ctx->item;
+        g_free (ctx);
+        icon = item->user_data;
+
+        switch (action_number)
+        {
+            case ACTION_OPEN:
+            {
+                file_list.data = icon->data;
+                file_list.next = NULL;
+                file_list.prev = NULL;
+                g_signal_emit_by_name (container, "activate", &file_list);
+            }
+            break;
+
+            case ACTION_MENU:
+            {
+                selection = nautilus_canvas_container_get_selection (container);
+                if (selection == NULL ||
+                    g_list_length (selection) != 1 ||
+                    selection->data != icon->data)
+                {
+                    g_list_free (selection);
+                    return FALSE;
+                }
+                g_list_free (selection);
+                g_signal_emit_by_name (container, "context-click-selection", &button_event);
+            }
+            break;
+
+            default:
+            {
+                g_assert_not_reached ();
+            }
+            break;
+        }
+    }
+    return FALSE;
+}
+
+static gboolean
+nautilus_canvas_item_accessible_do_action (AtkAction *accessible,
+                                           int        i)
+{
+    NautilusCanvasItem *item;
+    NautilusCanvasItemAccessibleActionContext *ctx;
+    NautilusCanvasContainer *container;
+
+    g_assert (i < LAST_ACTION);
+
+    item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
+    if (!item)
+    {
+        return FALSE;
+    }
+
+    container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+    switch (i)
+    {
+        case ACTION_OPEN:
+        case ACTION_MENU:
+        {
+            if (container->details->a11y_item_action_queue == NULL)
+            {
+                container->details->a11y_item_action_queue = g_queue_new ();
+            }
+            ctx = g_new (NautilusCanvasItemAccessibleActionContext, 1);
+            ctx->action_number = i;
+            ctx->item = item;
+            g_queue_push_head (container->details->a11y_item_action_queue, ctx);
+            if (container->details->a11y_item_action_idle_handler == 0)
+            {
+                container->details->a11y_item_action_idle_handler = g_idle_add 
(nautilus_canvas_item_accessible_idle_do_action, container);
+            }
+        }
+        break;
+
+        default:
+        {
+            g_warning ("Invalid action passed to NautilusCanvasItemAccessible::do_action");
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static int
+nautilus_canvas_item_accessible_get_n_actions (AtkAction *accessible)
+{
+    return LAST_ACTION;
+}
+
+static const char *
+nautilus_canvas_item_accessible_action_get_description (AtkAction *accessible,
+                                                        int        i)
+{
+    NautilusCanvasItemAccessiblePrivate *priv;
+
+    g_assert (i < LAST_ACTION);
+
+    priv = GET_ACCESSIBLE_PRIV (accessible);
+
+    if (priv->action_descriptions[i])
+    {
+        return priv->action_descriptions[i];
+    }
+    else
+    {
+        return nautilus_canvas_item_accessible_action_descriptions[i];
+    }
+}
+
+static const char *
+nautilus_canvas_item_accessible_action_get_name (AtkAction *accessible,
+                                                 int        i)
+{
+    g_assert (i < LAST_ACTION);
+
+    return nautilus_canvas_item_accessible_action_names[i];
+}
+
+static const char *
+nautilus_canvas_item_accessible_action_get_keybinding (AtkAction *accessible,
+                                                       int        i)
+{
+    g_assert (i < LAST_ACTION);
+
+    return NULL;
+}
+
+static gboolean
+nautilus_canvas_item_accessible_action_set_description (AtkAction  *accessible,
+                                                        int         i,
+                                                        const char *description)
+{
+    NautilusCanvasItemAccessiblePrivate *priv;
+
+    g_assert (i < LAST_ACTION);
+
+    priv = GET_ACCESSIBLE_PRIV (accessible);
+
+    if (priv->action_descriptions[i])
+    {
+        g_free (priv->action_descriptions[i]);
+    }
+    priv->action_descriptions[i] = g_strdup (description);
+
+    return TRUE;
+}
+
+static void
+nautilus_canvas_item_accessible_action_interface_init (AtkActionIface *iface)
+{
+    iface->do_action = nautilus_canvas_item_accessible_do_action;
+    iface->get_n_actions = nautilus_canvas_item_accessible_get_n_actions;
+    iface->get_description = nautilus_canvas_item_accessible_action_get_description;
+    iface->get_keybinding = nautilus_canvas_item_accessible_action_get_keybinding;
+    iface->get_name = nautilus_canvas_item_accessible_action_get_name;
+    iface->set_description = nautilus_canvas_item_accessible_action_set_description;
+}
+
+static const gchar *
+nautilus_canvas_item_accessible_get_name (AtkObject *accessible)
+{
+    NautilusCanvasItem *item;
+
+    if (accessible->name)
+    {
+        return accessible->name;
+    }
+
+    item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
+    if (!item)
+    {
+        return NULL;
+    }
+    return item->details->editable_text;
+}
+
+static const gchar *
+nautilus_canvas_item_accessible_get_description (AtkObject *accessible)
+{
+    NautilusCanvasItem *item;
+
+    item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
+    if (!item)
+    {
+        return NULL;
+    }
+
+    return item->details->additional_text;
+}
+
+static AtkObject *
+nautilus_canvas_item_accessible_get_parent (AtkObject *accessible)
+{
+    NautilusCanvasItem *item;
+
+    item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
+    if (!item)
+    {
+        return NULL;
+    }
+
+    return gtk_widget_get_accessible (GTK_WIDGET (EEL_CANVAS_ITEM (item)->canvas));
+}
+
+static int
+nautilus_canvas_item_accessible_get_index_in_parent (AtkObject *accessible)
+{
+    NautilusCanvasItem *item;
+    NautilusCanvasContainer *container;
+    GList *l;
+    NautilusCanvasIcon *icon;
+    int i;
+
+    item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
+    if (!item)
+    {
+        return -1;
+    }
+
+    container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+
+    l = container->details->icons;
+    i = 0;
+    while (l)
+    {
+        icon = l->data;
+
+        if (icon->item == item)
+        {
+            return i;
+        }
+
+        i++;
+        l = l->next;
+    }
+
+    return -1;
+}
+
+static const gchar *
+nautilus_canvas_item_accessible_get_image_description (AtkImage *image)
+{
+    NautilusCanvasItemAccessiblePrivate *priv;
+    NautilusCanvasItem *item;
+    NautilusCanvasIcon *icon;
+    NautilusCanvasContainer *container;
+    char *description;
+
+    priv = GET_ACCESSIBLE_PRIV (image);
+
+    if (priv->image_description)
+    {
+        return priv->image_description;
+    }
+    else
+    {
+        item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (image)));
+        if (item == NULL)
+        {
+            return NULL;
+        }
+        icon = item->user_data;
+        container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+        description = nautilus_canvas_container_get_icon_description (container, icon->data);
+        g_free (priv->description);
+        priv->description = description;
+        return priv->description;
+    }
+}
+
+static void
+nautilus_canvas_item_accessible_get_image_size (AtkImage *image,
+                                                gint     *width,
+                                                gint     *height)
+{
+    NautilusCanvasItem *item;
+
+    item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (image)));
+    get_scaled_icon_size (item, width, height);
+}
+
+static void
+nautilus_canvas_item_accessible_get_image_position (AtkImage     *image,
+                                                    gint         *x,
+                                                    gint         *y,
+                                                    AtkCoordType  coord_type)
+{
+    NautilusCanvasItem *item;
+    gint x_offset, y_offset, itmp;
+
+    item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (image)));
+    if (!item)
+    {
+        return;
+    }
+    if (!item->details->icon_rect.x0 && !item->details->icon_rect.x1)
+    {
+        return;
+    }
+    else
+    {
+        x_offset = 0;
+        y_offset = 0;
+        if (item->details->text_width)
+        {
+            itmp = item->details->icon_rect.x0 -
+                   item->details->text_rect.x0;
+            if (itmp > x_offset)
+            {
+                x_offset = itmp;
+            }
+            itmp = item->details->icon_rect.y0 -
+                   item->details->text_rect.y0;
+            if (itmp > y_offset)
+            {
+                y_offset = itmp;
+            }
+        }
+    }
+    atk_component_get_extents (ATK_COMPONENT (image), x, y, NULL, NULL, coord_type);
+    *x += x_offset;
+    *y += y_offset;
+}
+
+static gboolean
+nautilus_canvas_item_accessible_set_image_description (AtkImage    *image,
+                                                       const gchar *description)
+{
+    NautilusCanvasItemAccessiblePrivate *priv;
+
+    priv = GET_ACCESSIBLE_PRIV (image);
+
+    g_free (priv->image_description);
+    priv->image_description = g_strdup (description);
+
+    return TRUE;
+}
+
+static void
+nautilus_canvas_item_accessible_image_interface_init (AtkImageIface *iface)
+{
+    iface->get_image_description = nautilus_canvas_item_accessible_get_image_description;
+    iface->set_image_description = nautilus_canvas_item_accessible_set_image_description;
+    iface->get_image_size = nautilus_canvas_item_accessible_get_image_size;
+    iface->get_image_position = nautilus_canvas_item_accessible_get_image_position;
+}
+
+/* accessible text interface */
+static gint
+nautilus_canvas_item_accessible_get_offset_at_point (AtkText      *text,
+                                                     gint          x,
+                                                     gint          y,
+                                                     AtkCoordType  coords)
+{
+    gint real_x, real_y, real_width, real_height;
+    NautilusCanvasItem *item;
+    gint editable_height;
+    gint offset = 0;
+    gint index;
+    PangoLayout *layout, *editable_layout, *additional_layout;
+    PangoRectangle rect0;
+    char *canvas_text;
+    gboolean have_editable;
+    gboolean have_additional;
+    gint text_offset, height;
+
+    atk_component_get_extents (ATK_COMPONENT (text), &real_x, &real_y,
+                               &real_width, &real_height, coords);
+
+    x -= real_x;
+    y -= real_y;
+
+    item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text)));
+
+    if (item->details->pixbuf)
+    {
+        get_scaled_icon_size (item, NULL, &height);
+        y -= height;
+    }
+    have_editable = item->details->editable_text != NULL &&
+                    item->details->editable_text[0] != '\0';
+    have_additional = item->details->additional_text != NULL && item->details->additional_text[0] != '\0';
+
+    editable_layout = NULL;
+    additional_layout = NULL;
+    if (have_editable)
+    {
+        editable_layout = get_label_layout (&item->details->editable_text_layout, item, 
item->details->editable_text);
+        prepare_pango_layout_for_draw (item, editable_layout);
+        pango_layout_get_pixel_size (editable_layout, NULL, &editable_height);
+        if (y >= editable_height &&
+            have_additional)
+        {
+            prepare_pango_layout_for_draw (item, editable_layout);
+            additional_layout = get_label_layout (&item->details->additional_text_layout, item, 
item->details->additional_text);
+            layout = additional_layout;
+            canvas_text = item->details->additional_text;
+            y -= editable_height + LABEL_LINE_SPACING;
+        }
+        else
+        {
+            layout = editable_layout;
+            canvas_text = item->details->editable_text;
+        }
+    }
+    else if (have_additional)
+    {
+        additional_layout = get_label_layout (&item->details->additional_text_layout, item, 
item->details->additional_text);
+        prepare_pango_layout_for_draw (item, additional_layout);
+        layout = additional_layout;
+        canvas_text = item->details->additional_text;
+    }
+    else
+    {
+        return 0;
+    }
+
+    text_offset = 0;
+    if (have_editable)
+    {
+        pango_layout_index_to_pos (editable_layout, 0, &rect0);
+        text_offset = PANGO_PIXELS (rect0.x);
+    }
+    if (have_additional)
+    {
+        gint itmp;
+
+        pango_layout_index_to_pos (additional_layout, 0, &rect0);
+        itmp = PANGO_PIXELS (rect0.x);
+        if (itmp < text_offset)
+        {
+            text_offset = itmp;
+        }
+    }
+    pango_layout_index_to_pos (layout, 0, &rect0);
+    x += text_offset;
+    if (!pango_layout_xy_to_index (layout,
+                                   x * PANGO_SCALE,
+                                   y * PANGO_SCALE,
+                                   &index, NULL))
+    {
+        if (x < 0 || y < 0)
+        {
+            index = 0;
+        }
+        else
+        {
+            index = -1;
+        }
+    }
+    if (index == -1)
+    {
+        offset = g_utf8_strlen (canvas_text, -1);
+    }
+    else
+    {
+        offset = g_utf8_pointer_to_offset (canvas_text, canvas_text + index);
+    }
+    if (layout == additional_layout)
+    {
+        offset += g_utf8_strlen (item->details->editable_text, -1);
+    }
+
+    if (editable_layout != NULL)
+    {
+        g_object_unref (editable_layout);
+    }
+
+    if (additional_layout != NULL)
+    {
+        g_object_unref (additional_layout);
+    }
+
+    return offset;
+}
+
+static void
+nautilus_canvas_item_accessible_get_character_extents (AtkText      *text,
+                                                       gint          offset,
+                                                       gint         *x,
+                                                       gint         *y,
+                                                       gint         *width,
+                                                       gint         *height,
+                                                       AtkCoordType  coords)
+{
+    gint pos_x, pos_y;
+    gint len, byte_offset;
+    gint editable_height;
+    gchar *canvas_text;
+    NautilusCanvasItem *item;
+    PangoLayout *layout, *editable_layout, *additional_layout;
+    PangoRectangle rect;
+    PangoRectangle rect0;
+    gboolean have_editable;
+    gint text_offset, pix_height;
+
+    atk_component_get_extents (ATK_COMPONENT (text), &pos_x, &pos_y, NULL, NULL, coords);
+    item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text)));
+
+    if (item->details->pixbuf)
+    {
+        get_scaled_icon_size (item, NULL, &pix_height);
+        pos_y += pix_height;
+    }
+
+    have_editable = item->details->editable_text != NULL &&
+                    item->details->editable_text[0] != '\0';
+    if (have_editable)
+    {
+        len = g_utf8_strlen (item->details->editable_text, -1);
+    }
+    else
+    {
+        len = 0;
+    }
+
+    editable_layout = get_label_layout (&item->details->editable_text_layout, item, 
item->details->editable_text);
+    additional_layout = get_label_layout (&item->details->additional_text_layout, item, 
item->details->additional_text);
+
+    if (offset < len)
+    {
+        canvas_text = item->details->editable_text;
+        layout = editable_layout;
+    }
+    else
+    {
+        offset -= len;
+        canvas_text = item->details->additional_text;
+        layout = additional_layout;
+        pos_y += LABEL_LINE_SPACING;
+        if (have_editable)
+        {
+            pango_layout_get_pixel_size (editable_layout, NULL, &editable_height);
+            pos_y += editable_height;
+        }
+    }
+    byte_offset = g_utf8_offset_to_pointer (canvas_text, offset) - canvas_text;
+    pango_layout_index_to_pos (layout, byte_offset, &rect);
+    text_offset = 0;
+    if (have_editable)
+    {
+        pango_layout_index_to_pos (editable_layout, 0, &rect0);
+        text_offset = PANGO_PIXELS (rect0.x);
+    }
+    if (item->details->additional_text != NULL &&
+        item->details->additional_text[0] != '\0')
+    {
+        gint itmp;
+
+        pango_layout_index_to_pos (additional_layout, 0, &rect0);
+        itmp = PANGO_PIXELS (rect0.x);
+        if (itmp < text_offset)
+        {
+            text_offset = itmp;
+        }
+    }
+
+    g_object_unref (editable_layout);
+    g_object_unref (additional_layout);
+
+    *x = pos_x + PANGO_PIXELS (rect.x) - text_offset;
+    *y = pos_y + PANGO_PIXELS (rect.y);
+    *width = PANGO_PIXELS (rect.width);
+    *height = PANGO_PIXELS (rect.height);
+}
+
+static char *
+nautilus_canvas_item_accessible_text_get_text (AtkText *text,
+                                               gint     start_pos,
+                                               gint     end_pos)
+{
+    GObject *object;
+    NautilusCanvasItem *item;
+
+    object = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+    item = NAUTILUS_CANVAS_ITEM (object);
+
+    return g_utf8_substring (item->details->text->str, start_pos, end_pos);
+}
+
+static gunichar
+nautilus_canvas_item_accessible_text_get_character_at_offset (AtkText *text,
+                                                              gint     offset)
+{
+    GObject *object;
+    NautilusCanvasItem *item;
+    gchar *pointer;
+
+    object = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+    item = NAUTILUS_CANVAS_ITEM (object);
+    pointer = g_utf8_offset_to_pointer (item->details->text->str, offset);
+
+    return g_utf8_get_char (pointer);
+}
+
+static gint
+nautilus_canvas_item_accessible_text_get_character_count (AtkText *text)
+{
+    GObject *object;
+    NautilusCanvasItem *item;
+
+    object = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+    item = NAUTILUS_CANVAS_ITEM (object);
+
+    return g_utf8_strlen (item->details->text->str, -1);
+}
+
+static void
+nautilus_canvas_item_accessible_text_interface_init (AtkTextIface *iface)
+{
+    iface->get_text = nautilus_canvas_item_accessible_text_get_text;
+    iface->get_character_at_offset = nautilus_canvas_item_accessible_text_get_character_at_offset;
+    iface->get_character_count = nautilus_canvas_item_accessible_text_get_character_count;
+    iface->get_character_extents = nautilus_canvas_item_accessible_get_character_extents;
+    iface->get_offset_at_point = nautilus_canvas_item_accessible_get_offset_at_point;
+}
+
+static GType nautilus_canvas_item_accessible_get_type (void);
+
+G_DEFINE_TYPE_WITH_CODE (NautilusCanvasItemAccessible,
+                         nautilus_canvas_item_accessible,
+                         eel_canvas_item_accessible_get_type (),
+                         G_IMPLEMENT_INTERFACE (ATK_TYPE_IMAGE,
+                                                nautilus_canvas_item_accessible_image_interface_init)
+                         G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT,
+                                                nautilus_canvas_item_accessible_text_interface_init)
+                         G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION,
+                                                nautilus_canvas_item_accessible_action_interface_init));
+
+static AtkStateSet *
+nautilus_canvas_item_accessible_ref_state_set (AtkObject *accessible)
+{
+    AtkStateSet *state_set;
+    NautilusCanvasItem *item;
+    NautilusCanvasContainer *container;
+    GList *selection;
+    gboolean one_item_selected;
+
+    state_set = ATK_OBJECT_CLASS (nautilus_canvas_item_accessible_parent_class)->ref_state_set (accessible);
+
+    item = NAUTILUS_CANVAS_ITEM (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (accessible)));
+    if (!item)
+    {
+        atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
+        return state_set;
+    }
+    container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (item)->canvas);
+    if (item->details->is_highlighted_as_keyboard_focus)
+    {
+        atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
+    }
+    else if (!container->details->keyboard_focus)
+    {
+        selection = nautilus_canvas_container_get_selection (container);
+        one_item_selected = (g_list_length (selection) == 1) &&
+                            item->details->is_highlighted_for_selection;
+
+        if (one_item_selected)
+        {
+            atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
+        }
+
+        g_list_free (selection);
+    }
+
+    return state_set;
+}
+
+static void
+nautilus_canvas_item_accessible_finalize (GObject *object)
+{
+    NautilusCanvasItemAccessiblePrivate *priv;
+    int i;
+
+    priv = GET_ACCESSIBLE_PRIV (object);
+
+    for (i = 0; i < LAST_ACTION; i++)
+    {
+        g_free (priv->action_descriptions[i]);
+    }
+    g_free (priv->image_description);
+    g_free (priv->description);
+
+    G_OBJECT_CLASS (nautilus_canvas_item_accessible_parent_class)->finalize (object);
+}
+
+static void
+nautilus_canvas_item_accessible_initialize (AtkObject *accessible,
+                                            gpointer   widget)
+{
+    ATK_OBJECT_CLASS (nautilus_canvas_item_accessible_parent_class)->initialize (accessible, widget);
+
+    atk_object_set_role (accessible, ATK_ROLE_CANVAS);
+}
+
+static void
+nautilus_canvas_item_accessible_class_init (NautilusCanvasItemAccessibleClass *klass)
+{
+    AtkObjectClass *aclass = ATK_OBJECT_CLASS (klass);
+    GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+    oclass->finalize = nautilus_canvas_item_accessible_finalize;
+
+    aclass->initialize = nautilus_canvas_item_accessible_initialize;
+
+    aclass->get_name = nautilus_canvas_item_accessible_get_name;
+    aclass->get_description = nautilus_canvas_item_accessible_get_description;
+    aclass->get_parent = nautilus_canvas_item_accessible_get_parent;
+    aclass->get_index_in_parent = nautilus_canvas_item_accessible_get_index_in_parent;
+    aclass->ref_state_set = nautilus_canvas_item_accessible_ref_state_set;
+
+    g_type_class_add_private (klass, sizeof (NautilusCanvasItemAccessiblePrivate));
+}
+
+static void
+nautilus_canvas_item_accessible_init (NautilusCanvasItemAccessible *self)
+{
+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nautilus_canvas_item_accessible_get_type (),
+                                              NautilusCanvasItemAccessiblePrivate);
+}
+
+/* dummy typedef */
+typedef AtkObjectFactory NautilusCanvasItemAccessibleFactory;
+typedef AtkObjectFactoryClass NautilusCanvasItemAccessibleFactoryClass;
+
+G_DEFINE_TYPE (NautilusCanvasItemAccessibleFactory, nautilus_canvas_item_accessible_factory,
+               ATK_TYPE_OBJECT_FACTORY);
+
+static AtkObject *
+nautilus_canvas_item_accessible_factory_create_accessible (GObject *for_object)
+{
+    AtkObject *accessible;
+    NautilusCanvasItem *item;
+
+    item = NAUTILUS_CANVAS_ITEM (for_object);
+    g_assert (item != NULL);
+
+    item->details->text = g_string_new (NULL);
+    if (item->details->editable_text)
+    {
+        g_string_append (item->details->text, item->details->editable_text);
+    }
+    if (item->details->additional_text)
+    {
+        g_string_append (item->details->text, item->details->additional_text);
+    }
+
+    accessible = g_object_new (nautilus_canvas_item_accessible_get_type (), NULL);
+    atk_object_initialize (accessible, for_object);
+
+    return accessible;
+}
+
+static GType
+nautilus_canvas_item_accessible_factory_get_accessible_type (void)
+{
+    return nautilus_canvas_item_accessible_get_type ();
+}
+
+static void
+nautilus_canvas_item_accessible_factory_init (NautilusCanvasItemAccessibleFactory *self)
+{
+}
+
+static void
+nautilus_canvas_item_accessible_factory_class_init (NautilusCanvasItemAccessibleFactoryClass *klass)
+{
+    klass->create_accessible = nautilus_canvas_item_accessible_factory_create_accessible;
+    klass->get_accessible_type = nautilus_canvas_item_accessible_factory_get_accessible_type;
+}
diff --git a/src/nautilus-canvas-item.h b/src/nautilus-canvas-item.h
new file mode 100644
index 000000000..436fb6b46
--- /dev/null
+++ b/src/nautilus-canvas-item.h
@@ -0,0 +1,90 @@
+
+/* Nautilus - Canvas item class for canvas container.
+ *
+ * Copyright (C) 2000 Eazel, Inc.
+ *
+ * Author: Andy Hertzfeld <andy eazel com>
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <eel/eel-canvas.h>
+#include <eel/eel-art-extensions.h>
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_CANVAS_ITEM nautilus_canvas_item_get_type()
+#define NAUTILUS_CANVAS_ITEM(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_CANVAS_ITEM, NautilusCanvasItem))
+#define NAUTILUS_CANVAS_ITEM_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_CANVAS_ITEM, NautilusCanvasItemClass))
+#define NAUTILUS_IS_CANVAS_ITEM(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_CANVAS_ITEM))
+#define NAUTILUS_IS_CANVAS_ITEM_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_CANVAS_ITEM))
+#define NAUTILUS_CANVAS_ITEM_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), NAUTILUS_TYPE_CANVAS_ITEM, NautilusCanvasItemClass))
+
+typedef struct NautilusCanvasItem NautilusCanvasItem;
+typedef struct NautilusCanvasItemClass NautilusCanvasItemClass;
+typedef struct NautilusCanvasItemDetails NautilusCanvasItemDetails;
+
+struct NautilusCanvasItem {
+       EelCanvasItem item;
+       NautilusCanvasItemDetails *details;
+       gpointer user_data;
+};
+
+struct NautilusCanvasItemClass {
+       EelCanvasItemClass parent_class;
+};
+
+/* not namespaced due to their length */
+typedef enum {
+       BOUNDS_USAGE_FOR_LAYOUT,
+       BOUNDS_USAGE_FOR_ENTIRE_ITEM,
+       BOUNDS_USAGE_FOR_DISPLAY
+} NautilusCanvasItemBoundsUsage;
+
+/* GObject */
+GType       nautilus_canvas_item_get_type                 (void);
+
+/* attributes */
+void        nautilus_canvas_item_set_image                (NautilusCanvasItem       *item,
+                                                          GdkPixbuf                *image);
+cairo_surface_t* nautilus_canvas_item_get_drag_surface    (NautilusCanvasItem       *item);
+void        nautilus_canvas_item_set_emblems              (NautilusCanvasItem       *item,
+                                                          GList                    *emblem_pixbufs);
+
+/* geometry and hit testing */
+gboolean    nautilus_canvas_item_hit_test_rectangle       (NautilusCanvasItem       *item,
+                                                          EelIRect                  canvas_rect);
+void        nautilus_canvas_item_invalidate_label         (NautilusCanvasItem       *item);
+void        nautilus_canvas_item_invalidate_label_size    (NautilusCanvasItem       *item);
+EelDRect    nautilus_canvas_item_get_icon_rectangle     (const NautilusCanvasItem *item);
+void        nautilus_canvas_item_get_bounds_for_layout    (NautilusCanvasItem       *item,
+                                                          double *x1, double *y1, double *x2, double *y2);
+void        nautilus_canvas_item_get_bounds_for_entire_item (NautilusCanvasItem       *item,
+                                                            double *x1, double *y1, double *x2, double *y2);
+void        nautilus_canvas_item_update_bounds            (NautilusCanvasItem       *item,
+                                                          double i2w_dx, double i2w_dy);
+void        nautilus_canvas_item_set_is_visible           (NautilusCanvasItem       *item,
+                                                          gboolean                  visible);
+/* whether the entire label text must be visible at all times */
+void        nautilus_canvas_item_set_entire_text          (NautilusCanvasItem       *canvas_item,
+                                                          gboolean                  entire_text);
+
+G_END_DECLS
diff --git a/src/nautilus-canvas-private.h b/src/nautilus-canvas-private.h
new file mode 100644
index 000000000..e60e86299
--- /dev/null
+++ b/src/nautilus-canvas-private.h
@@ -0,0 +1,223 @@
+/* gnome-canvas-container-private.h
+
+   Copyright (C) 1999, 2000 Free Software Foundation
+   Copyright (C) 2000 Eazel, Inc.
+
+   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,
+   see <http://www.gnu.org/licenses/>.
+
+   Author: Ettore Perazzoli <ettore gnu org>
+*/
+
+#pragma once
+
+#include <eel/eel-glib-extensions.h>
+#include "nautilus-canvas-item.h"
+#include "nautilus-canvas-container.h"
+#include "nautilus-canvas-dnd.h"
+
+/* An Icon. */
+
+typedef struct {
+       /* Object represented by this icon. */
+       NautilusCanvasIconData *data;
+
+       /* Canvas item for the icon. */
+       NautilusCanvasItem *item;
+
+       /* X/Y coordinates. */
+       double x, y;
+
+       /*
+        * In RTL mode x is RTL x position, we use saved_ltr_x for
+        * keeping track of x value before it gets converted into
+        * RTL value, this is used for saving the icon position 
+        * to the nautilus metafile. 
+        */
+        double saved_ltr_x;
+
+       /* Position in the view */
+       int position;
+
+       /* Whether this item is selected. */
+       eel_boolean_bit is_selected : 1;
+
+       /* Whether this item was selected before rubberbanding. */
+       eel_boolean_bit was_selected_before_rubberband : 1;
+
+       /* Whether this item is visible in the view. */
+       eel_boolean_bit is_visible : 1;
+} NautilusCanvasIcon;
+
+
+/* Private NautilusCanvasContainer members. */
+
+typedef struct {
+       gboolean active;
+
+       double start_x, start_y;
+
+       EelCanvasItem *selection_rectangle;
+       GdkDevice *device;
+
+       guint timer_id;
+
+       guint prev_x, prev_y;
+       int last_adj_x;
+       int last_adj_y;
+} NautilusCanvasRubberbandInfo;
+
+typedef enum {
+       DRAG_STATE_INITIAL,
+       DRAG_STATE_MOVE_OR_COPY,
+       DRAG_STATE_STRETCH
+} DragState;
+
+typedef struct {
+       /* Pointer position in canvas coordinates. */
+       int pointer_x, pointer_y;
+
+       /* Icon top, left, and size in canvas coordinates. */
+       int icon_x, icon_y;
+       guint icon_size;
+} StretchState;
+
+typedef enum {
+       AXIS_NONE,
+       AXIS_HORIZONTAL,
+       AXIS_VERTICAL
+} Axis;
+
+enum {
+       LABEL_COLOR,
+       LABEL_COLOR_HIGHLIGHT,
+       LABEL_COLOR_ACTIVE,
+       LABEL_COLOR_PRELIGHT,
+       LABEL_INFO_COLOR,
+       LABEL_INFO_COLOR_HIGHLIGHT,
+       LABEL_INFO_COLOR_ACTIVE,
+       LAST_LABEL_COLOR
+};
+
+struct NautilusCanvasContainerDetails {
+       /* List of icons. */
+       GList *icons;
+       GList *new_icons;
+       GList *selection;
+       GHashTable *icon_set;
+
+       /* Currently focused icon for accessibility. */
+       NautilusCanvasIcon *focus;
+       gboolean keyboard_focus;
+
+       /* Starting icon for keyboard rubberbanding. */
+       NautilusCanvasIcon *keyboard_rubberband_start;
+
+       /* Last highlighted drop target. */
+       NautilusCanvasIcon *drop_target;
+
+       /* Rubberbanding status. */
+       NautilusCanvasRubberbandInfo rubberband_info;
+
+       /* Timeout used to make a selected icon fully visible after a short
+        * period of time. (The timeout is needed to make sure
+        * double-clicking still works.)
+        */
+       guint keyboard_icon_reveal_timer_id;
+       NautilusCanvasIcon *keyboard_icon_to_reveal;
+
+       /* Used to coalesce selection changed signals in some cases */
+       guint selection_changed_id;
+       
+       /* If a request is made to reveal an unpositioned icon we remember
+        * it and reveal it once it gets positioned (in relayout).
+        */
+       NautilusCanvasIcon *pending_icon_to_reveal;
+
+       /* Remembered information about the start of the current event. */
+       guint32 button_down_time;
+       
+       /* Drag state. Valid only if drag_button is non-zero. */
+       guint drag_button;
+       NautilusCanvasIcon *drag_icon;
+       int drag_x, drag_y;
+       DragState drag_state;
+       gboolean drag_started;
+
+       gboolean icon_selected_on_button_down;
+       gboolean double_clicked;
+       NautilusCanvasIcon *double_click_icon[2]; /* Both clicks in a double click need to be on the same 
icon */
+       guint double_click_button[2];
+
+       NautilusCanvasIcon *range_selection_base_icon;
+       
+       /* Idle ID. */
+       guint idle_id;
+
+       /* Align idle id */
+       guint align_idle_id;
+
+       /* DnD info. */
+       NautilusCanvasDndInfo *dnd_info;
+       NautilusDragInfo *dnd_source_info;
+
+       /* zoom level */
+       int zoom_level;
+
+       /* specific fonts used to draw labels */
+       char *font;
+       
+       /* State used so arrow keys don't wander if icons aren't lined up.
+        */
+       int arrow_key_start_x;
+       int arrow_key_start_y;
+       GtkDirectionType arrow_key_direction;
+
+       /* Mode settings. */
+       gboolean single_click_mode;
+
+        /* Set to TRUE after first allocation has been done */
+       gboolean has_been_allocated;
+
+       int size_allocation_count;
+       guint size_allocation_count_id;
+
+       /* a11y items used by canvas items */
+       guint a11y_item_action_idle_handler;
+       GQueue* a11y_item_action_queue;
+
+       eel_boolean_bit in_layout_now : 1;
+       eel_boolean_bit is_loading : 1;
+       eel_boolean_bit is_populating_container : 1;
+       eel_boolean_bit needs_resort : 1;
+       eel_boolean_bit selection_needs_resort : 1;
+};
+
+/* Private functions shared by mutiple files. */
+NautilusCanvasIcon *nautilus_canvas_container_get_icon_by_uri             (NautilusCanvasContainer 
*container,
+                                                                        const char            *uri);
+void          nautilus_canvas_container_select_list_unselect_others (NautilusCanvasContainer *container,
+                                                                    GList                 *icons);
+char *        nautilus_canvas_container_get_icon_uri                (NautilusCanvasContainer *container,
+                                                                      NautilusCanvasIcon          *canvas);
+char *        nautilus_canvas_container_get_icon_activation_uri     (NautilusCanvasContainer *container,
+                                                                    NautilusCanvasIcon          *canvas);
+char *        nautilus_canvas_container_get_icon_drop_target_uri    (NautilusCanvasContainer *container,
+                                                                      NautilusCanvasIcon          *canvas);
+void          nautilus_canvas_container_update_icon                 (NautilusCanvasContainer *container,
+                                                                      NautilusCanvasIcon          *canvas);
+gboolean      nautilus_canvas_container_scroll                      (NautilusCanvasContainer *container,
+                                                                    int                    delta_x,
+                                                                    int                    delta_y);
+void          nautilus_canvas_container_update_scroll_region        (NautilusCanvasContainer *container);
diff --git a/src/nautilus-canvas-view-container.c b/src/nautilus-canvas-view-container.c
new file mode 100644
index 000000000..a8b1f8ad6
--- /dev/null
+++ b/src/nautilus-canvas-view-container.c
@@ -0,0 +1,377 @@
+/* fm-icon-container.h - the container widget for file manager icons
+ *
+ *  Copyright (C) 2002 Sun Microsystems, Inc.
+ *
+ *  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,
+ *  see <http://www.gnu.org/licenses/>.
+ *
+ *  Author: Michael Meeks <michael ximian com>
+ */
+
+#include "nautilus-canvas-view-container.h"
+
+#include <eel/eel-glib-extensions.h>
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+#include "nautilus-canvas-view.h"
+#include "nautilus-enums.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-thumbnails.h"
+
+struct _NautilusCanvasViewContainer
+{
+    NautilusCanvasContainer parent;
+
+    NautilusCanvasView *view;
+};
+
+G_DEFINE_TYPE (NautilusCanvasViewContainer, nautilus_canvas_view_container, NAUTILUS_TYPE_CANVAS_CONTAINER);
+
+static GQuark attribute_none_q;
+
+static NautilusCanvasView *
+get_canvas_view (NautilusCanvasContainer *container)
+{
+    /* Type unsafe comparison for performance */
+    return ((NautilusCanvasViewContainer *) container)->view;
+}
+
+static NautilusIconInfo *
+nautilus_canvas_view_container_get_icon_images (NautilusCanvasContainer *container,
+                                                NautilusCanvasIconData  *data,
+                                                int                      size,
+                                                gboolean                 for_drag_accept)
+{
+    NautilusCanvasView *canvas_view;
+    NautilusFile *file;
+    NautilusFileIconFlags flags;
+    NautilusIconInfo *icon_info;
+    gint scale;
+
+    file = (NautilusFile *) data;
+
+    g_assert (NAUTILUS_IS_FILE (file));
+    canvas_view = get_canvas_view (container);
+    g_return_val_if_fail (canvas_view != NULL, NULL);
+
+    flags = NAUTILUS_FILE_ICON_FLAGS_USE_EMBLEMS |
+            NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS;
+
+    if (for_drag_accept)
+    {
+        flags |= NAUTILUS_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT;
+    }
+
+    scale = gtk_widget_get_scale_factor (GTK_WIDGET (canvas_view));
+    icon_info = nautilus_file_get_icon (file, size, scale, flags);
+
+    return icon_info;
+}
+
+static char *
+nautilus_canvas_view_container_get_icon_description (NautilusCanvasContainer *container,
+                                                     NautilusCanvasIconData  *data)
+{
+    NautilusFile *file;
+    char *mime_type;
+    const char *description;
+
+    file = NAUTILUS_FILE (data);
+    g_assert (NAUTILUS_IS_FILE (file));
+
+    mime_type = nautilus_file_get_mime_type (file);
+    description = g_content_type_get_description (mime_type);
+    g_free (mime_type);
+    return g_strdup (description);
+}
+
+static void
+nautilus_canvas_view_container_prioritize_thumbnailing (NautilusCanvasContainer *container,
+                                                        NautilusCanvasIconData  *data)
+{
+    NautilusFile *file;
+    char *uri;
+
+    file = (NautilusFile *) data;
+
+    g_assert (NAUTILUS_IS_FILE (file));
+
+    if (nautilus_file_is_thumbnailing (file))
+    {
+        uri = nautilus_file_get_uri (file);
+        nautilus_thumbnail_prioritize (uri);
+        g_free (uri);
+    }
+}
+
+static GQuark *
+get_quark_from_strv (gchar **value)
+{
+    GQuark *quark;
+    int i;
+
+    quark = g_new0 (GQuark, g_strv_length (value) + 1);
+    for (i = 0; value[i] != NULL; ++i)
+    {
+        quark[i] = g_quark_from_string (value[i]);
+    }
+
+    return quark;
+}
+
+/*
+ * Get the preference for which caption text should appear
+ * beneath icons.
+ */
+static GQuark *
+nautilus_canvas_view_container_get_icon_text_attributes_from_preferences (void)
+{
+    GQuark *attributes;
+    gchar **value;
+
+    value = g_settings_get_strv (nautilus_icon_view_preferences,
+                                 NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS);
+    attributes = get_quark_from_strv (value);
+    g_strfreev (value);
+
+    /* We don't need to sanity check the attributes list even though it came
+     * from preferences.
+     *
+     * There are 2 ways that the values in the list could be bad.
+     *
+     * 1) The user picks "bad" values.  "bad" values are those that result in
+     *    there being duplicate attributes in the list.
+     *
+     * 2) Value stored in GConf are tampered with.  Its possible physically do
+     *    this by pulling the rug underneath GConf and manually editing its
+     *    config files.  Its also possible to use a third party GConf key
+     *    editor and store garbage for the keys in question.
+     *
+     * Thankfully, the Nautilus preferences machinery deals with both of
+     * these cases.
+     *
+     * In the first case, the preferences dialog widgetry prevents
+     * duplicate attributes by making "bad" choices insensitive.
+     *
+     * In the second case, the preferences getter (and also the auto storage) for
+     * string_array values are always valid members of the enumeration associated
+     * with the preference.
+     *
+     * So, no more error checking on attributes is needed here and we can return
+     * a the auto stored value.
+     */
+    return attributes;
+}
+
+static int
+quarkv_length (GQuark *attributes)
+{
+    int i;
+    i = 0;
+    while (attributes[i] != 0)
+    {
+        i++;
+    }
+    return i;
+}
+
+/**
+ * nautilus_canvas_view_get_icon_text_attribute_names:
+ *
+ * Get a list representing which text attributes should be displayed
+ * beneath an icon. The result is dependent on zoom level and possibly
+ * user configuration. Don't free the result.
+ * @view: NautilusCanvasView to query.
+ *
+ **/
+static GQuark *
+nautilus_canvas_view_container_get_icon_text_attribute_names (NautilusCanvasContainer *container,
+                                                              int                     *len)
+{
+    GQuark *attributes;
+    int piece_count;
+
+    const int pieces_by_level[] =
+    {
+        1,              /* NAUTILUS_ZOOM_LEVEL_SMALL */
+        2,              /* NAUTILUS_ZOOM_LEVEL_STANDARD */
+        3,              /* NAUTILUS_ZOOM_LEVEL_LARGE */
+        3,              /* NAUTILUS_ZOOM_LEVEL_LARGER */
+    };
+
+    piece_count = pieces_by_level[nautilus_canvas_container_get_zoom_level (container)];
+
+    attributes = nautilus_canvas_view_container_get_icon_text_attributes_from_preferences ();
+
+    *len = MIN (piece_count, quarkv_length (attributes));
+
+    return attributes;
+}
+
+/* This callback returns the text, both the editable part, and the
+ * part below that is not editable.
+ */
+static void
+nautilus_canvas_view_container_get_icon_text (NautilusCanvasContainer  *container,
+                                              NautilusCanvasIconData   *data,
+                                              char                    **editable_text,
+                                              char                    **additional_text,
+                                              gboolean                  include_invisible)
+{
+    GQuark *attributes;
+    char *text_array[4];
+    int i, j, num_attributes;
+    NautilusCanvasView *canvas_view;
+    NautilusFile *file;
+    gboolean use_additional;
+
+    file = NAUTILUS_FILE (data);
+
+    g_assert (NAUTILUS_IS_FILE (file));
+    g_assert (editable_text != NULL);
+    canvas_view = get_canvas_view (container);
+    g_return_if_fail (canvas_view != NULL);
+
+    use_additional = (additional_text != NULL);
+
+    /* Strip the suffix for nautilus object xml files. */
+    *editable_text = nautilus_file_get_display_name (file);
+
+    if (!use_additional)
+    {
+        return;
+    }
+
+    /* Find out what attributes go below each icon. */
+    attributes = nautilus_canvas_view_container_get_icon_text_attribute_names (container,
+                                                                               &num_attributes);
+
+    /* Get the attributes. */
+    j = 0;
+    for (i = 0; i < num_attributes; ++i)
+    {
+        char *text;
+        if (attributes[i] == attribute_none_q)
+        {
+            continue;
+        }
+        text = nautilus_file_get_string_attribute_q (file, attributes[i]);
+        if (text == NULL)
+        {
+            continue;
+        }
+        text_array[j++] = text;
+    }
+    text_array[j] = NULL;
+
+    /* Return them. */
+    if (j == 0)
+    {
+        *additional_text = NULL;
+    }
+    else if (j == 1)
+    {
+        /* Only one item, avoid the strdup + free */
+        *additional_text = text_array[0];
+    }
+    else
+    {
+        *additional_text = g_strjoinv ("\n", text_array);
+
+        for (i = 0; i < j; i++)
+        {
+            g_free (text_array[i]);
+        }
+    }
+
+    g_free (attributes);
+}
+
+static int
+nautilus_canvas_view_container_compare_icons (NautilusCanvasContainer *container,
+                                              NautilusCanvasIconData  *icon_a,
+                                              NautilusCanvasIconData  *icon_b)
+{
+    NautilusCanvasView *canvas_view;
+
+    canvas_view = get_canvas_view (container);
+    g_return_val_if_fail (canvas_view != NULL, 0);
+
+    /* Type unsafe comparisons for performance */
+    return nautilus_canvas_view_compare_files (canvas_view,
+                                               (NautilusFile *) icon_a,
+                                               (NautilusFile *) icon_b);
+}
+
+static int
+nautilus_canvas_view_container_compare_icons_by_name (NautilusCanvasContainer *container,
+                                                      NautilusCanvasIconData  *icon_a,
+                                                      NautilusCanvasIconData  *icon_b)
+{
+    return nautilus_file_compare_for_sort
+               (NAUTILUS_FILE (icon_a),
+               NAUTILUS_FILE (icon_b),
+               NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
+               FALSE, FALSE);
+}
+
+static void
+nautilus_canvas_view_container_class_init (NautilusCanvasViewContainerClass *klass)
+{
+    NautilusCanvasContainerClass *ic_class;
+
+    ic_class = &klass->parent_class;
+
+    attribute_none_q = g_quark_from_static_string ("none");
+
+    ic_class->get_icon_text = nautilus_canvas_view_container_get_icon_text;
+    ic_class->get_icon_images = nautilus_canvas_view_container_get_icon_images;
+    ic_class->get_icon_description = nautilus_canvas_view_container_get_icon_description;
+    ic_class->prioritize_thumbnailing = nautilus_canvas_view_container_prioritize_thumbnailing;
+
+    ic_class->compare_icons = nautilus_canvas_view_container_compare_icons;
+    ic_class->compare_icons_by_name = nautilus_canvas_view_container_compare_icons_by_name;
+}
+
+static void
+nautilus_canvas_view_container_init (NautilusCanvasViewContainer *canvas_container)
+{
+    gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (canvas_container)),
+                                 GTK_STYLE_CLASS_VIEW);
+}
+
+NautilusCanvasContainer *
+nautilus_canvas_view_container_construct (NautilusCanvasViewContainer *canvas_container,
+                                          NautilusCanvasView          *view)
+{
+    AtkObject *atk_obj;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), NULL);
+
+    canvas_container->view = view;
+    atk_obj = gtk_widget_get_accessible (GTK_WIDGET (canvas_container));
+    atk_object_set_name (atk_obj, _("Icon View"));
+
+    return NAUTILUS_CANVAS_CONTAINER (canvas_container);
+}
+
+NautilusCanvasContainer *
+nautilus_canvas_view_container_new (NautilusCanvasView *view)
+{
+    return nautilus_canvas_view_container_construct
+               (g_object_new (NAUTILUS_TYPE_CANVAS_VIEW_CONTAINER, NULL),
+               view);
+}
diff --git a/src/nautilus-canvas-view-container.h b/src/nautilus-canvas-view-container.h
new file mode 100644
index 000000000..b0e103181
--- /dev/null
+++ b/src/nautilus-canvas-view-container.h
@@ -0,0 +1,35 @@
+
+/* fm-icon-container.h - the container widget for file manager icons
+
+   Copyright (C) 2002 Sun Microsystems, Inc.
+
+   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,
+   see <http://www.gnu.org/licenses/>.
+
+   Author: Michael Meeks <michael ximian com>
+*/
+
+#pragma once
+
+#include "nautilus-canvas-container.h"
+
+#define NAUTILUS_TYPE_CANVAS_VIEW_CONTAINER nautilus_canvas_view_container_get_type()
+
+G_DECLARE_FINAL_TYPE (NautilusCanvasViewContainer, nautilus_canvas_view_container,
+                      NAUTILUS, CANVAS_VIEW_CONTAINER,
+                      NautilusCanvasContainer)
+
+NautilusCanvasContainer *nautilus_canvas_view_container_construct (NautilusCanvasViewContainer 
*canvas_container,
+                                                                   NautilusCanvasView      *view);
+NautilusCanvasContainer *nautilus_canvas_view_container_new       (NautilusCanvasView      *view);
diff --git a/src/nautilus-canvas-view.c b/src/nautilus-canvas-view.c
new file mode 100644
index 000000000..3679e8053
--- /dev/null
+++ b/src/nautilus-canvas-view.c
@@ -0,0 +1,1646 @@
+/* fm-canvas-view.c - implementation of canvas view of directory.
+ *
+ *  Copyright (C) 2000, 2001 Eazel, Inc.
+ *
+ *  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,
+ *  see <http://www.gnu.org/licenses/>.
+ *
+ *  Authors: John Sullivan <sullivan eazel com>
+ */
+
+#include <config.h>
+
+#include "nautilus-canvas-view.h"
+
+#include "nautilus-canvas-view-container.h"
+#include "nautilus-error-reporting.h"
+#include "nautilus-files-view-dnd.h"
+#include "nautilus-toolbar.h"
+#include "nautilus-view.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include "nautilus-directory.h"
+#include "nautilus-dnd.h"
+#include "nautilus-file-utilities.h"
+#include "nautilus-ui-utilities.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-canvas-container.h"
+#include "nautilus-canvas-dnd.h"
+#include "nautilus-metadata.h"
+#include "nautilus-clipboard.h"
+#include "nautilus-gtk4-helpers.h"
+
+#define DEBUG_FLAG NAUTILUS_DEBUG_CANVAS_VIEW
+#include "nautilus-debug.h"
+
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+typedef gboolean (*SortCriterionMatchFunc) (NautilusFile *file);
+
+typedef struct
+{
+    const NautilusFileSortType sort_type;
+    const char *metadata_text;
+    const char *action_target_name;
+    const gboolean reverse_order;
+    SortCriterionMatchFunc match_func;
+} SortCriterion;
+
+typedef enum
+{
+    MENU_ITEM_TYPE_STANDARD,
+    MENU_ITEM_TYPE_CHECK,
+    MENU_ITEM_TYPE_RADIO,
+    MENU_ITEM_TYPE_TREE
+} MenuItemType;
+
+struct _NautilusCanvasView
+{
+    NautilusFilesView parent_instance;
+
+    GList *icons_not_positioned;
+
+    guint react_to_canvas_change_idle_id;
+
+    const SortCriterion *sort;
+
+    GtkWidget *canvas_container;
+
+    /* FIXME: Needed for async operations. Suposedly we would use cancellable and gtask,
+     * sadly gtkclipboard doesn't support that.
+     * We follow this pattern for checking validity of the object in the views.
+     * Ideally we would connect to a weak reference and do a cancellable.
+     */
+    gboolean destroyed;
+};
+
+/* Note that the first item in this list is the default sort,
+ * and that the items show up in the menu in the order they
+ * appear in this list.
+ */
+static const SortCriterion sort_criteria[] =
+{
+    {
+        NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
+        "name",
+        "name",
+        FALSE
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
+        "name",
+        "name-desc",
+        TRUE
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_SIZE,
+        "size",
+        "size",
+        TRUE
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_TYPE,
+        "type",
+        "type",
+        FALSE
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_MTIME,
+        "modification date",
+        "modification-date",
+        FALSE
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_MTIME,
+        "modification date",
+        "modification-date-desc",
+        TRUE
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_ATIME,
+        "access date",
+        "access-date",
+        FALSE
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_ATIME,
+        "access date",
+        "access-date-desc",
+        TRUE
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_BTIME,
+        "creation date",
+        "creation-date",
+        FALSE
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_BTIME,
+        "creation date",
+        "creation-date-desc",
+        TRUE
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_TRASHED_TIME,
+        "trashed",
+        "trash-time",
+        TRUE,
+        nautilus_file_is_in_trash
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE,
+        NULL,
+        "search-relevance",
+        TRUE,
+        nautilus_file_is_in_search
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_RECENCY,
+        NULL,
+        "recency",
+        TRUE,
+        nautilus_file_is_in_recent
+    }
+};
+
+G_DEFINE_TYPE (NautilusCanvasView, nautilus_canvas_view, NAUTILUS_TYPE_FILES_VIEW);
+
+static void                 nautilus_canvas_view_set_directory_sort_by (NautilusCanvasView  *canvas_view,
+                                                                        NautilusFile        *file,
+                                                                        const SortCriterion *sort);
+static void                 nautilus_canvas_view_update_click_mode (NautilusCanvasView *canvas_view);
+static void                 nautilus_canvas_view_reveal_selection (NautilusFilesView *view);
+static const SortCriterion *get_sort_criterion_by_metadata_text (const char *metadata_text,
+                                                                 gboolean    reversed);
+static const SortCriterion *get_sort_criterion_by_sort_type (NautilusFileSortType sort_type,
+                                                             gboolean             reversed);
+static const SortCriterion *get_default_sort_order (NautilusFile *file);
+static void                 nautilus_canvas_view_clear (NautilusFilesView *view);
+static void on_clipboard_owner_changed (GtkClipboard *clipboard,
+                                        GdkEvent     *event,
+                                        gpointer      user_data);
+
+static void
+nautilus_canvas_view_destroy (GtkWidget *object)
+{
+    NautilusCanvasView *canvas_view;
+    GtkClipboard *clipboard;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (object);
+
+    nautilus_canvas_view_clear (NAUTILUS_FILES_VIEW (object));
+
+    if (canvas_view->react_to_canvas_change_idle_id != 0)
+    {
+        g_source_remove (canvas_view->react_to_canvas_change_idle_id);
+        canvas_view->react_to_canvas_change_idle_id = 0;
+    }
+
+    clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+    g_signal_handlers_disconnect_by_func (clipboard,
+                                          on_clipboard_owner_changed,
+                                          canvas_view);
+
+    if (canvas_view->icons_not_positioned)
+    {
+        nautilus_file_list_free (canvas_view->icons_not_positioned);
+        canvas_view->icons_not_positioned = NULL;
+    }
+
+    GTK_WIDGET_CLASS (nautilus_canvas_view_parent_class)->destroy (object);
+}
+
+static NautilusCanvasContainer *
+get_canvas_container (NautilusCanvasView *canvas_view)
+{
+    return NAUTILUS_CANVAS_CONTAINER (canvas_view->canvas_container);
+}
+
+NautilusCanvasContainer *
+nautilus_canvas_view_get_canvas_container (NautilusCanvasView *canvas_view)
+{
+    return get_canvas_container (canvas_view);
+}
+
+static void
+update_sort_criterion (NautilusCanvasView  *canvas_view,
+                       const SortCriterion *sort,
+                       gboolean             set_metadata)
+{
+    NautilusFile *file;
+    const SortCriterion *overrided_sort_criterion;
+
+    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (canvas_view));
+
+    /* Make sure we use the default one and not one that the user used previously
+     * of the change to not allow sorting on search and recent, or the
+     * case that the user or some app modified directly the metadata */
+    if (nautilus_file_is_in_search (file) || nautilus_file_is_in_recent (file))
+    {
+        overrided_sort_criterion = get_default_sort_order (file);
+    }
+    else if (sort != NULL && canvas_view->sort != sort)
+    {
+        overrided_sort_criterion = sort;
+        if (set_metadata)
+        {
+            /* Store the new sort setting. */
+            nautilus_canvas_view_set_directory_sort_by (canvas_view,
+                                                        file,
+                                                        sort);
+        }
+    }
+    else
+    {
+        return;
+    }
+
+    canvas_view->sort = overrided_sort_criterion;
+}
+
+static void
+list_covers (NautilusCanvasIconData *data,
+             gpointer                callback_data)
+{
+    GSList **file_list;
+
+    file_list = callback_data;
+
+    *file_list = g_slist_prepend (*file_list, data);
+}
+
+static void
+unref_cover (NautilusCanvasIconData *data,
+             gpointer                callback_data)
+{
+    nautilus_file_unref (NAUTILUS_FILE (data));
+}
+
+static void
+nautilus_canvas_view_clear (NautilusFilesView *view)
+{
+    NautilusCanvasContainer *canvas_container;
+    GSList *file_list;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view));
+
+    canvas_container = get_canvas_container (NAUTILUS_CANVAS_VIEW (view));
+    if (!canvas_container)
+    {
+        return;
+    }
+
+    /* Clear away the existing icons. */
+    file_list = NULL;
+    nautilus_canvas_container_for_each (canvas_container, list_covers, &file_list);
+    nautilus_canvas_container_clear (canvas_container);
+    g_slist_foreach (file_list, (GFunc) unref_cover, NULL);
+    g_slist_free (file_list);
+}
+
+static void
+nautilus_canvas_view_remove_file (NautilusFilesView *view,
+                                  NautilusFile      *file,
+                                  NautilusDirectory *directory)
+{
+    NautilusCanvasView *canvas_view;
+
+    /* This used to assert that 'directory == nautilus_files_view_get_model (view)', but that
+     * resulted in a lot of crash reports (bug #352592). I don't see how that trace happens.
+     * It seems that somehow we get a files_changed event sent to the view from a directory
+     * that isn't the model, but the code disables the monitor and signal callback handlers when
+     * changing directories. Maybe we can get some more information when this happens.
+     * Further discussion in bug #368178.
+     */
+    if (directory != nautilus_files_view_get_model (view))
+    {
+        char *file_uri, *dir_uri, *model_uri;
+        file_uri = nautilus_file_get_uri (file);
+        dir_uri = nautilus_directory_get_uri (directory);
+        model_uri = nautilus_directory_get_uri (nautilus_files_view_get_model (view));
+        g_warning ("nautilus_canvas_view_remove_file() - directory not canvas view model, shouldn't 
happen.\n"
+                   "file: %p:%s, dir: %p:%s, model: %p:%s, view loading: %d\n"
+                   "If you see this, please add this info to 
http://bugzilla.gnome.org/show_bug.cgi?id=368178";,
+                   file, file_uri, directory, dir_uri, nautilus_files_view_get_model (view), model_uri, 
nautilus_files_view_get_loading (view));
+        g_free (file_uri);
+        g_free (dir_uri);
+        g_free (model_uri);
+    }
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (view);
+
+    if (nautilus_canvas_container_remove (get_canvas_container (canvas_view),
+                                          NAUTILUS_CANVAS_ICON_DATA (file)))
+    {
+        nautilus_file_unref (file);
+    }
+}
+
+static void
+nautilus_canvas_view_add_files (NautilusFilesView *view,
+                                GList             *files)
+{
+    NautilusCanvasView *canvas_view;
+    NautilusCanvasContainer *canvas_container;
+    GList *l;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (view);
+    canvas_container = get_canvas_container (canvas_view);
+
+    for (l = files; l != NULL; l = l->next)
+    {
+        if (nautilus_canvas_container_add (canvas_container,
+                                           NAUTILUS_CANVAS_ICON_DATA (l->data)))
+        {
+            nautilus_file_ref (NAUTILUS_FILE (l->data));
+        }
+    }
+}
+
+static void
+nautilus_canvas_view_file_changed (NautilusFilesView *view,
+                                   NautilusFile      *file,
+                                   NautilusDirectory *directory)
+{
+    NautilusCanvasView *canvas_view;
+
+    g_assert (directory == nautilus_files_view_get_model (view));
+
+    g_return_if_fail (view != NULL);
+    canvas_view = NAUTILUS_CANVAS_VIEW (view);
+
+    nautilus_canvas_container_request_update
+        (get_canvas_container (canvas_view),
+        NAUTILUS_CANVAS_ICON_DATA (file));
+}
+
+static const SortCriterion *
+nautilus_canvas_view_get_directory_sort_by (NautilusCanvasView *canvas_view,
+                                            NautilusFile       *file)
+{
+    const SortCriterion *default_sort;
+    g_autofree char *sort_by = NULL;
+    gboolean reversed;
+
+    default_sort = get_default_sort_order (file);
+    g_return_val_if_fail (default_sort != NULL, NULL);
+
+    sort_by = nautilus_file_get_metadata (file,
+                                          NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
+                                          default_sort->metadata_text);
+
+    reversed = nautilus_file_get_boolean_metadata (file,
+                                                   NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
+                                                   default_sort->reverse_order);
+
+    return get_sort_criterion_by_metadata_text (sort_by, reversed);
+}
+
+static const SortCriterion *
+get_default_sort_order (NautilusFile *file)
+{
+    NautilusFileSortType sort_type;
+    gboolean reversed;
+
+    sort_type = nautilus_file_get_default_sort_type (file, &reversed);
+
+    return get_sort_criterion_by_sort_type (sort_type, reversed);
+}
+
+static void
+nautilus_canvas_view_set_directory_sort_by (NautilusCanvasView  *canvas_view,
+                                            NautilusFile        *file,
+                                            const SortCriterion *sort)
+{
+    const SortCriterion *default_sort_criterion;
+
+    default_sort_criterion = get_default_sort_order (file);
+    g_return_if_fail (default_sort_criterion != NULL);
+
+    nautilus_file_set_metadata
+        (file, NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
+        default_sort_criterion->metadata_text,
+        sort->metadata_text);
+    nautilus_file_set_boolean_metadata (file,
+                                        NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
+                                        default_sort_criterion->reverse_order,
+                                        sort->reverse_order);
+}
+
+static const SortCriterion *
+get_sort_criterion_by_metadata_text (const char *metadata_text,
+                                     gboolean    reversed)
+{
+    guint i;
+
+    /* Figure out what the new sort setting should be. */
+    for (i = 0; i < G_N_ELEMENTS (sort_criteria); i++)
+    {
+        if (g_strcmp0 (sort_criteria[i].metadata_text, metadata_text) == 0
+            && reversed == sort_criteria[i].reverse_order)
+        {
+            return &sort_criteria[i];
+        }
+    }
+    return &sort_criteria[0];
+}
+
+static const SortCriterion *
+get_sort_criterion_by_action_target_name (const char *action_target_name)
+{
+    guint i;
+    /* Figure out what the new sort setting should be. */
+    for (i = 0; i < G_N_ELEMENTS (sort_criteria); i++)
+    {
+        if (g_strcmp0 (sort_criteria[i].action_target_name, action_target_name) == 0)
+        {
+            return &sort_criteria[i];
+        }
+    }
+    return NULL;
+}
+
+static const SortCriterion *
+get_sort_criterion_by_sort_type (NautilusFileSortType sort_type,
+                                 gboolean             reversed)
+{
+    guint i;
+
+    /* Figure out what the new sort setting should be. */
+    for (i = 0; i < G_N_ELEMENTS (sort_criteria); i++)
+    {
+        if (sort_type == sort_criteria[i].sort_type
+            && reversed == sort_criteria[i].reverse_order)
+        {
+            return &sort_criteria[i];
+        }
+    }
+
+    return &sort_criteria[0];
+}
+
+static NautilusCanvasZoomLevel
+get_default_zoom_level (NautilusCanvasView *canvas_view)
+{
+    NautilusCanvasZoomLevel default_zoom_level;
+
+    default_zoom_level = g_settings_get_enum (nautilus_icon_view_preferences,
+                                              NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL);
+
+    return CLAMP (default_zoom_level, NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL, NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER);
+}
+
+static void
+nautilus_canvas_view_begin_loading (NautilusFilesView *view)
+{
+    NautilusCanvasView *canvas_view;
+    NautilusFile *file;
+    char *uri;
+    const SortCriterion *sort;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view));
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (view);
+    file = nautilus_files_view_get_directory_as_file (view);
+    uri = nautilus_file_get_uri (file);
+
+    g_free (uri);
+
+    /* Set the sort mode.
+     * It's OK not to resort the icons because the
+     * container doesn't have any icons at this point.
+     */
+    sort = nautilus_canvas_view_get_directory_sort_by (canvas_view, file);
+    update_sort_criterion (canvas_view, sort, FALSE);
+
+    /* We could have changed to the trash directory or to searching, and then
+     * we need to update the menus */
+    nautilus_files_view_update_context_menus (view);
+    nautilus_files_view_update_toolbar_menus (view);
+}
+
+static void
+on_clipboard_contents_received (GtkClipboard     *clipboard,
+                                GtkSelectionData *selection_data,
+                                gpointer          user_data)
+{
+    NautilusCanvasView *canvas_view;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (user_data);
+
+    if (canvas_view->destroyed)
+    {
+        /* We've been destroyed since call */
+        g_object_unref (canvas_view);
+        return;
+    }
+
+    if (nautilus_clipboard_is_cut_from_selection_data (selection_data))
+    {
+        GList *uris;
+        GList *files;
+
+        uris = nautilus_clipboard_get_uri_list_from_selection_data (selection_data);
+        files = nautilus_file_list_from_uri_list (uris);
+        nautilus_canvas_container_set_highlighted_for_clipboard (get_canvas_container (canvas_view),
+                                                                 files);
+
+        nautilus_file_list_free (files);
+        g_list_free_full (uris, g_free);
+    }
+    else
+    {
+        nautilus_canvas_container_set_highlighted_for_clipboard (get_canvas_container (canvas_view),
+                                                                 NULL);
+    }
+
+    g_object_unref (canvas_view);
+}
+
+static void
+update_clipboard_status (NautilusCanvasView *view)
+{
+    g_object_ref (view);     /* Need to keep the object alive until we get the reply */
+    gtk_clipboard_request_contents (nautilus_clipboard_get (GTK_WIDGET (view)),
+                                    nautilus_clipboard_get_atom (),
+                                    on_clipboard_contents_received,
+                                    view);
+}
+
+static void
+on_clipboard_owner_changed (GtkClipboard *clipboard,
+                            GdkEvent     *event,
+                            gpointer      user_data)
+{
+    update_clipboard_status (NAUTILUS_CANVAS_VIEW (user_data));
+}
+
+static void
+nautilus_canvas_view_end_loading (NautilusFilesView *view,
+                                  gboolean           all_files_seen)
+{
+    NautilusCanvasView *canvas_view;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (view);
+    update_clipboard_status (canvas_view);
+}
+
+static NautilusCanvasZoomLevel
+nautilus_canvas_view_get_zoom_level (NautilusFilesView *view)
+{
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), NAUTILUS_CANVAS_ZOOM_LEVEL_LARGE);
+
+    return nautilus_canvas_container_get_zoom_level (get_canvas_container (NAUTILUS_CANVAS_VIEW (view)));
+}
+
+static void
+nautilus_canvas_view_zoom_to_level (NautilusFilesView *view,
+                                    gint               new_level)
+{
+    NautilusCanvasView *canvas_view;
+    NautilusCanvasContainer *canvas_container;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view));
+    g_return_if_fail (new_level >= NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL &&
+                      new_level <= NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER);
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (view);
+    canvas_container = get_canvas_container (canvas_view);
+    if (nautilus_canvas_container_get_zoom_level (canvas_container) == new_level)
+    {
+        return;
+    }
+
+    nautilus_canvas_container_set_zoom_level (canvas_container, new_level);
+    g_action_group_change_action_state (nautilus_files_view_get_action_group (view),
+                                        "zoom-to-level", g_variant_new_int32 (new_level));
+
+    nautilus_files_view_update_toolbar_menus (view);
+}
+
+static void
+nautilus_canvas_view_bump_zoom_level (NautilusFilesView *view,
+                                      int                zoom_increment)
+{
+    NautilusCanvasZoomLevel new_level;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view));
+    if (!nautilus_files_view_supports_zooming (view))
+    {
+        return;
+    }
+
+    new_level = nautilus_canvas_view_get_zoom_level (view) + zoom_increment;
+
+    if (new_level >= NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL &&
+        new_level <= NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER)
+    {
+        nautilus_canvas_view_zoom_to_level (view, new_level);
+    }
+}
+
+static void
+nautilus_canvas_view_restore_standard_zoom_level (NautilusFilesView *view)
+{
+    nautilus_canvas_view_zoom_to_level (view, NAUTILUS_CANVAS_ZOOM_LEVEL_LARGE);
+}
+
+static gboolean
+nautilus_canvas_view_can_zoom_in (NautilusFilesView *view)
+{
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), FALSE);
+
+    return nautilus_canvas_view_get_zoom_level (view)
+           < NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER;
+}
+
+static gboolean
+nautilus_canvas_view_can_zoom_out (NautilusFilesView *view)
+{
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), FALSE);
+
+    return nautilus_canvas_view_get_zoom_level (view)
+           > NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL;
+}
+
+static gfloat
+nautilus_canvas_view_get_zoom_level_percentage (NautilusFilesView *view)
+{
+    guint icon_size;
+    NautilusCanvasZoomLevel zoom_level;
+
+    zoom_level = nautilus_canvas_view_get_zoom_level (view);
+    icon_size = nautilus_canvas_container_get_icon_size_for_zoom_level (zoom_level);
+
+    return (gfloat) icon_size / NAUTILUS_CANVAS_ICON_SIZE_LARGE;
+}
+
+static gboolean
+nautilus_canvas_view_is_zoom_level_default (NautilusFilesView *view)
+{
+    guint icon_size;
+    NautilusCanvasZoomLevel zoom_level;
+
+    zoom_level = nautilus_canvas_view_get_zoom_level (view);
+    icon_size = nautilus_canvas_container_get_icon_size_for_zoom_level (zoom_level);
+
+    return icon_size == NAUTILUS_CANVAS_ICON_SIZE_LARGE;
+}
+
+static gboolean
+nautilus_canvas_view_is_empty (NautilusFilesView *view)
+{
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (view));
+
+    return nautilus_canvas_container_is_empty
+               (get_canvas_container (NAUTILUS_CANVAS_VIEW (view)));
+}
+
+static GList *
+nautilus_canvas_view_get_selection (NautilusFilesView *view)
+{
+    GList *list;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), NULL);
+
+    list = nautilus_canvas_container_get_selection
+               (get_canvas_container (NAUTILUS_CANVAS_VIEW (view)));
+    nautilus_file_list_ref (list);
+    return list;
+}
+
+static void
+action_sort_order_changed (GSimpleAction *action,
+                           GVariant      *value,
+                           gpointer       user_data)
+{
+    const gchar *target_name;
+    const SortCriterion *sort_criterion;
+
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (user_data));
+
+    target_name = g_variant_get_string (value, NULL);
+    sort_criterion = get_sort_criterion_by_action_target_name (target_name);
+
+    g_assert (sort_criterion != NULL);
+
+    update_sort_criterion (user_data, sort_criterion, TRUE);
+
+    nautilus_canvas_container_sort (get_canvas_container (user_data));
+    nautilus_canvas_view_reveal_selection (NAUTILUS_FILES_VIEW (user_data));
+
+    g_simple_action_set_state (action, value);
+}
+
+static void
+action_zoom_to_level (GSimpleAction *action,
+                      GVariant      *state,
+                      gpointer       user_data)
+{
+    NautilusFilesView *view;
+    NautilusCanvasZoomLevel zoom_level;
+
+    g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
+
+    view = NAUTILUS_FILES_VIEW (user_data);
+    zoom_level = g_variant_get_int32 (state);
+    nautilus_canvas_view_zoom_to_level (view, zoom_level);
+
+    g_simple_action_set_state (G_SIMPLE_ACTION (action), state);
+    if (g_settings_get_enum (nautilus_icon_view_preferences,
+                             NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL) != zoom_level)
+    {
+        g_settings_set_enum (nautilus_icon_view_preferences,
+                             NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL,
+                             zoom_level);
+    }
+}
+
+const GActionEntry canvas_view_entries[] =
+{
+    { "sort", NULL, "s", "'name'", action_sort_order_changed },
+    { "zoom-to-level", NULL, NULL, "1", action_zoom_to_level }
+};
+
+static void
+update_sort_action_state_hint (NautilusCanvasView *canvas_view)
+{
+    NautilusFile *file;
+    GVariantBuilder builder;
+    GActionGroup *action_group;
+    GAction *action;
+    GVariant *state_hint;
+    gint idx;
+
+    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (canvas_view));
+    g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+
+    for (idx = 0; idx < G_N_ELEMENTS (sort_criteria); idx++)
+    {
+        if (sort_criteria[idx].match_func == NULL ||
+            (file != NULL && sort_criteria[idx].match_func (file)))
+        {
+            g_variant_builder_add (&builder, "s", sort_criteria[idx].action_target_name);
+        }
+    }
+
+    state_hint = g_variant_builder_end (&builder);
+
+    action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (canvas_view));
+    action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "sort");
+    g_simple_action_set_state_hint (G_SIMPLE_ACTION (action), state_hint);
+
+    g_variant_unref (state_hint);
+}
+
+static gboolean
+showing_recent_directory (NautilusFilesView *view)
+{
+    NautilusFile *file;
+
+    file = nautilus_files_view_get_directory_as_file (view);
+    if (file != NULL)
+    {
+        return nautilus_file_is_in_recent (file);
+    }
+    return FALSE;
+}
+
+static gboolean
+showing_search_directory (NautilusFilesView *view)
+{
+    NautilusFile *file;
+
+    file = nautilus_files_view_get_directory_as_file (view);
+    if (file != NULL)
+    {
+        return nautilus_file_is_in_search (file);
+    }
+    return FALSE;
+}
+
+static void
+nautilus_canvas_view_update_actions_state (NautilusFilesView *view)
+{
+    GActionGroup *view_action_group;
+    GVariant *sort_state;
+    GAction *action;
+    NautilusCanvasView *canvas_view;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (view);
+
+    NAUTILUS_FILES_VIEW_CLASS (nautilus_canvas_view_parent_class)->update_actions_state (view);
+
+    view_action_group = nautilus_files_view_get_action_group (view);
+
+    /* When we change the sort action state, even using the same value, it triggers
+     * the sort action changed handler, which reveals the selection, since we expect
+     * the selection to be visible when the user changes the sort order. But we may
+     * need to update the actions state for others reason than an actual sort change,
+     * so we need to prevent to trigger the sort action changed handler for those cases.
+     * To achieve this, check if the action state value actually changed before setting
+     * it
+     */
+    sort_state = g_action_group_get_action_state (view_action_group, "sort");
+
+    if (g_strcmp0 (g_variant_get_string (sort_state, NULL),
+                   canvas_view->sort->action_target_name) != 0)
+    {
+        g_action_group_change_action_state (view_action_group,
+                                            "sort",
+                                            g_variant_new_string (canvas_view->sort->action_target_name));
+    }
+
+    action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "sort");
+    g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                                 !showing_recent_directory (view) &&
+                                 !showing_search_directory (view));
+
+    update_sort_action_state_hint (canvas_view);
+
+    g_variant_unref (sort_state);
+}
+
+static void
+nautilus_canvas_view_select_all (NautilusFilesView *view)
+{
+    NautilusCanvasContainer *canvas_container;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view));
+
+    canvas_container = get_canvas_container (NAUTILUS_CANVAS_VIEW (view));
+    nautilus_canvas_container_select_all (canvas_container);
+}
+
+static void
+nautilus_canvas_view_select_first (NautilusFilesView *view)
+{
+    NautilusCanvasContainer *canvas_container;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view));
+
+    canvas_container = get_canvas_container (NAUTILUS_CANVAS_VIEW (view));
+    nautilus_canvas_container_select_first (canvas_container);
+}
+
+static void
+nautilus_canvas_view_reveal_selection (NautilusFilesView *view)
+{
+    g_autolist (NautilusFile) selection = NULL;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view));
+
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
+
+    /* Make sure at least one of the selected items is scrolled into view */
+    if (selection != NULL)
+    {
+        /* Update the icon ordering to reveal the rigth selection */
+        nautilus_canvas_container_layout_now (get_canvas_container (NAUTILUS_CANVAS_VIEW (view)));
+        nautilus_canvas_container_reveal
+            (get_canvas_container (NAUTILUS_CANVAS_VIEW (view)),
+            selection->data);
+    }
+}
+
+static GdkRectangle *
+get_rectangle_for_data (NautilusFilesView      *view,
+                        NautilusCanvasIconData *data)
+{
+    NautilusCanvasContainer *container;
+    GdkRectangle *rectangle;
+
+    container = get_canvas_container (NAUTILUS_CANVAS_VIEW (view));
+    rectangle = nautilus_canvas_container_get_icon_bounding_box (container, data);
+    if (rectangle != NULL)
+    {
+        GtkWidget *context_widget;
+        GtkAdjustment *vadjustment;
+        GtkAdjustment *hadjustment;
+
+        context_widget = nautilus_files_view_get_content_widget (view);
+        vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (context_widget));
+        hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (context_widget));
+
+        rectangle->x -= gtk_adjustment_get_value (hadjustment);
+        rectangle->y -= gtk_adjustment_get_value (vadjustment);
+    }
+    return rectangle;
+}
+
+static GdkRectangle *
+nautilus_canvas_view_compute_rename_popover_pointing_to (NautilusFilesView *view)
+{
+    g_autolist (NautilusFile) selection = NULL;
+    NautilusCanvasIconData *data;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), NULL);
+
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
+    g_return_val_if_fail (selection != NULL, NULL);
+
+    /* We only allow renaming one item at once */
+    data = NAUTILUS_CANVAS_ICON_DATA (selection->data);
+
+    return get_rectangle_for_data (view, data);
+}
+
+static GdkRectangle *
+nautilus_canvas_view_reveal_for_selection_context_menu (NautilusFilesView *view)
+{
+    g_autolist (NautilusFile) selection = NULL;
+    NautilusCanvasContainer *container;
+    NautilusCanvasIconData *data;
+
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), NULL);
+
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
+    g_return_val_if_fail (selection != NULL, NULL);
+
+    container = get_canvas_container (NAUTILUS_CANVAS_VIEW (view));
+
+    /* Update the icon ordering to reveal the rigth selection */
+    nautilus_canvas_container_layout_now (container);
+
+    /* Get the data of the focused item, if selected. Otherwise, get the
+     * data of the last selected item.*/
+    data = nautilus_canvas_container_get_focused_icon (container);
+    if (data == NULL || g_list_find (selection, NAUTILUS_FILE (data)) == NULL)
+    {
+        selection = g_list_last (selection);
+        data = NAUTILUS_CANVAS_ICON_DATA (selection->data);
+    }
+
+    nautilus_canvas_container_reveal (container, data);
+
+    return get_rectangle_for_data (view, data);
+}
+
+static void
+nautilus_canvas_view_set_selection (NautilusFilesView *view,
+                                    GList             *selection)
+{
+    g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view));
+
+    nautilus_canvas_container_set_selection
+        (get_canvas_container (NAUTILUS_CANVAS_VIEW (view)), selection);
+}
+
+static void
+nautilus_canvas_view_invert_selection (NautilusFilesView *view)
+{
+    g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view));
+
+    nautilus_canvas_container_invert_selection
+        (get_canvas_container (NAUTILUS_CANVAS_VIEW (view)));
+}
+
+static void
+nautilus_canvas_view_widget_to_file_operation_position (NautilusFilesView *view,
+                                                        GdkPoint          *position)
+{
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (view));
+
+    nautilus_canvas_container_widget_to_file_operation_position
+        (get_canvas_container (NAUTILUS_CANVAS_VIEW (view)), position);
+}
+
+static void
+canvas_container_activate_callback (NautilusCanvasContainer *container,
+                                    GList                   *file_list,
+                                    NautilusCanvasView      *canvas_view)
+{
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view));
+    g_assert (container == get_canvas_container (canvas_view));
+
+    nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (canvas_view),
+                                        file_list,
+                                        0, TRUE);
+}
+
+/* this is called in one of these cases:
+ * - we activate with enter holding shift
+ * - we activate with space holding shift
+ * - we double click an canvas holding shift
+ * - we middle click an canvas
+ *
+ * If we don't open in new windows by default, the behavior should be
+ * - middle click, shift + activate -> open in new tab
+ * - shift + double click -> open in new window
+ *
+ * If we open in new windows by default, the behaviour should be
+ * - middle click, or shift + activate, or shift + double-click -> close parent
+ */
+static void
+canvas_container_activate_alternate_callback (NautilusCanvasContainer *container,
+                                              GList                   *file_list,
+                                              NautilusCanvasView      *canvas_view)
+{
+    GdkEvent *event;
+    GdkEventButton *button_event;
+    GdkEventKey *key_event;
+    gboolean open_in_tab, open_in_window;
+    NautilusOpenFlags flags;
+
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view));
+    g_assert (container == get_canvas_container (canvas_view));
+
+    flags = 0;
+    event = gtk_get_current_event ();
+    open_in_tab = FALSE;
+    open_in_window = FALSE;
+
+    if (event->type == GDK_BUTTON_PRESS ||
+        event->type == GDK_BUTTON_RELEASE ||
+        event->type == GDK_2BUTTON_PRESS ||
+        event->type == GDK_3BUTTON_PRESS)
+    {
+        button_event = (GdkEventButton *) event;
+        open_in_window = ((button_event->state & GDK_SHIFT_MASK) != 0);
+        open_in_tab = !open_in_window;
+    }
+    else if (event->type == GDK_KEY_PRESS ||
+             event->type == GDK_KEY_RELEASE)
+    {
+        key_event = (GdkEventKey *) event;
+        open_in_tab = ((key_event->state & GDK_SHIFT_MASK) != 0);
+    }
+
+    if (open_in_tab)
+    {
+        flags |= NAUTILUS_OPEN_FLAG_NEW_TAB;
+        flags |= NAUTILUS_OPEN_FLAG_DONT_MAKE_ACTIVE;
+    }
+
+    if (open_in_window)
+    {
+        flags |= NAUTILUS_OPEN_FLAG_NEW_WINDOW;
+    }
+
+    DEBUG ("Activate alternate, open in tab %d, new window %d\n",
+           open_in_tab, open_in_window);
+
+    nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (canvas_view),
+                                        file_list,
+                                        flags,
+                                        TRUE);
+}
+
+static void
+band_select_started_callback (NautilusCanvasContainer *container,
+                              NautilusCanvasView      *canvas_view)
+{
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view));
+    g_assert (container == get_canvas_container (canvas_view));
+
+    nautilus_files_view_start_batching_selection_changes (NAUTILUS_FILES_VIEW (canvas_view));
+}
+
+static void
+band_select_ended_callback (NautilusCanvasContainer *container,
+                            NautilusCanvasView      *canvas_view)
+{
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view));
+    g_assert (container == get_canvas_container (canvas_view));
+
+    nautilus_files_view_stop_batching_selection_changes (NAUTILUS_FILES_VIEW (canvas_view));
+}
+
+int
+nautilus_canvas_view_compare_files (NautilusCanvasView *canvas_view,
+                                    NautilusFile       *a,
+                                    NautilusFile       *b)
+{
+    return nautilus_file_compare_for_sort
+               (a, b, canvas_view->sort->sort_type,
+               /* Use type-unsafe cast for performance */
+               nautilus_files_view_should_sort_directories_first ((NautilusFilesView *) canvas_view),
+               canvas_view->sort->reverse_order);
+}
+
+static int
+compare_files (NautilusFilesView *canvas_view,
+               NautilusFile      *a,
+               NautilusFile      *b)
+{
+    return nautilus_canvas_view_compare_files ((NautilusCanvasView *) canvas_view, a, b);
+}
+
+static void
+selection_changed_callback (NautilusCanvasContainer *container,
+                            NautilusCanvasView      *canvas_view)
+{
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view));
+    g_assert (container == get_canvas_container (canvas_view));
+
+    nautilus_files_view_notify_selection_changed (NAUTILUS_FILES_VIEW (canvas_view));
+}
+
+static void
+canvas_container_context_click_selection_callback (NautilusCanvasContainer *container,
+                                                   const GdkEvent          *event,
+                                                   NautilusCanvasView      *canvas_view)
+{
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view));
+
+    nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (canvas_view),
+                                                       event);
+}
+
+static void
+canvas_container_context_click_background_callback (NautilusCanvasContainer *container,
+                                                    const GdkEvent          *event,
+                                                    NautilusCanvasView      *canvas_view)
+{
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view));
+
+    nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (canvas_view),
+                                                        event);
+}
+
+static char *
+get_icon_uri_callback (NautilusCanvasContainer *container,
+                       NautilusFile            *file,
+                       NautilusCanvasView      *canvas_view)
+{
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
+    g_assert (NAUTILUS_IS_FILE (file));
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view));
+
+    return nautilus_file_get_uri (file);
+}
+
+static char *
+get_icon_activation_uri_callback (NautilusCanvasContainer *container,
+                                  NautilusFile            *file,
+                                  NautilusCanvasView      *canvas_view)
+{
+    g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
+    g_assert (NAUTILUS_IS_FILE (file));
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view));
+
+    return nautilus_file_get_activation_uri (file);
+}
+
+static char *
+get_icon_drop_target_uri_callback (NautilusCanvasContainer *container,
+                                   NautilusFile            *file,
+                                   NautilusCanvasView      *canvas_view)
+{
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), NULL);
+    g_return_val_if_fail (NAUTILUS_IS_FILE (file), NULL);
+    g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (canvas_view), NULL);
+
+    return nautilus_file_get_uri (file);
+}
+
+/* Preferences changed callbacks */
+static void
+nautilus_canvas_view_click_policy_changed (NautilusFilesView *directory_view)
+{
+    g_assert (NAUTILUS_IS_CANVAS_VIEW (directory_view));
+
+    nautilus_canvas_view_update_click_mode (NAUTILUS_CANVAS_VIEW (directory_view));
+}
+
+static void
+image_display_policy_changed_callback (gpointer callback_data)
+{
+    NautilusCanvasView *canvas_view;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (callback_data);
+
+    nautilus_canvas_container_request_update_all (get_canvas_container (canvas_view));
+}
+
+static void
+text_attribute_names_changed_callback (gpointer callback_data)
+{
+    NautilusCanvasView *canvas_view;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (callback_data);
+
+    nautilus_canvas_container_request_update_all (get_canvas_container (canvas_view));
+}
+
+static void
+default_sort_order_changed_callback (gpointer callback_data)
+{
+    NautilusCanvasView *canvas_view;
+    NautilusFile *file;
+    const SortCriterion *sort;
+    NautilusCanvasContainer *canvas_container;
+
+    g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (callback_data));
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (callback_data);
+
+    file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (canvas_view));
+    sort = nautilus_canvas_view_get_directory_sort_by (canvas_view, file);
+    update_sort_criterion (canvas_view, sort, FALSE);
+
+    canvas_container = get_canvas_container (canvas_view);
+    g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (canvas_container));
+
+    nautilus_canvas_container_request_update_all (canvas_container);
+}
+
+static void
+nautilus_canvas_view_sort_directories_first_changed (NautilusFilesView *directory_view)
+{
+    NautilusCanvasView *canvas_view;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (directory_view);
+
+    nautilus_canvas_container_sort (get_canvas_container (canvas_view));
+}
+
+static void
+nautilus_canvas_view_preview_selection_event (NautilusFilesView *directory_view,
+                                              GtkDirectionType   direction)
+{
+    NautilusCanvasView *canvas_view;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (directory_view);
+
+    nautilus_canvas_container_preview_selection_event (get_canvas_container (canvas_view),
+                                                       direction);
+}
+
+static char *
+canvas_view_get_container_uri (NautilusCanvasContainer *container,
+                               NautilusFilesView       *view)
+{
+    return nautilus_files_view_get_uri (view);
+}
+
+static void
+canvas_view_move_copy_items (NautilusCanvasContainer *container,
+                             const GList             *item_uris,
+                             const char              *target_dir,
+                             int                      copy_action,
+                             NautilusFilesView       *view)
+{
+    nautilus_clipboard_clear_if_colliding_uris (GTK_WIDGET (view),
+                                                item_uris);
+    nautilus_files_view_move_copy_items (view, item_uris, target_dir,
+                                         copy_action);
+}
+
+static void
+nautilus_canvas_view_update_click_mode (NautilusCanvasView *canvas_view)
+{
+    NautilusCanvasContainer *canvas_container;
+    int click_mode;
+
+    canvas_container = get_canvas_container (canvas_view);
+    g_assert (canvas_container != NULL);
+
+    click_mode = g_settings_get_enum (nautilus_preferences, NAUTILUS_PREFERENCES_CLICK_POLICY);
+
+    nautilus_canvas_container_set_single_click_mode (canvas_container,
+                                                     click_mode == NAUTILUS_CLICK_POLICY_SINGLE);
+}
+
+static void
+canvas_container_longpress_gesture_pressed_callback (GtkGestureLongPress *gesture,
+                                                     gdouble              x,
+                                                     gdouble              y,
+                                                     gpointer             user_data)
+{
+    GdkEventSequence *event_sequence;
+    const GdkEvent *event;
+    NautilusCanvasView *view = NAUTILUS_CANVAS_VIEW (user_data);
+
+    event_sequence = gtk_gesture_get_last_updated_sequence (GTK_GESTURE (gesture));
+    event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), event_sequence);
+
+    if (nautilus_view_get_selection (NAUTILUS_VIEW (view)))
+    {
+        nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (view),
+                                                           event);
+    }
+    else
+    {
+        nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (view),
+                                                            event);
+    }
+}
+
+static void
+initialize_canvas_container (NautilusCanvasView      *canvas_view,
+                             NautilusCanvasContainer *canvas_container)
+{
+    GtkWidget *content_widget;
+    GtkGesture *longpress_gesture;
+
+    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (canvas_view));
+    canvas_view->canvas_container = GTK_WIDGET (canvas_container);
+    g_object_add_weak_pointer (G_OBJECT (canvas_container),
+                               (gpointer *) &canvas_view->canvas_container);
+
+    longpress_gesture = gtk_gesture_long_press_new (GTK_WIDGET (content_widget));
+    gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (longpress_gesture),
+                                                GTK_PHASE_CAPTURE);
+    gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (longpress_gesture),
+                                       TRUE);
+    g_signal_connect (longpress_gesture, "pressed",
+                      (GCallback) canvas_container_longpress_gesture_pressed_callback,
+                      canvas_view);
+
+    gtk_widget_set_can_focus (GTK_WIDGET (canvas_container), TRUE);
+
+    g_signal_connect_object (canvas_container, "activate",
+                             G_CALLBACK (canvas_container_activate_callback), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "activate-alternate",
+                             G_CALLBACK (canvas_container_activate_alternate_callback), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "band-select-started",
+                             G_CALLBACK (band_select_started_callback), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "band-select-ended",
+                             G_CALLBACK (band_select_ended_callback), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "context-click-selection",
+                             G_CALLBACK (canvas_container_context_click_selection_callback), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "context-click-background",
+                             G_CALLBACK (canvas_container_context_click_background_callback), canvas_view, 
0);
+    g_signal_connect_object (canvas_container, "selection-changed",
+                             G_CALLBACK (selection_changed_callback), canvas_view, 0);
+    /* FIXME: many of these should move into fm-canvas-container as virtual methods */
+    g_signal_connect_object (canvas_container, "get-icon-uri",
+                             G_CALLBACK (get_icon_uri_callback), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "get-icon-activation-uri",
+                             G_CALLBACK (get_icon_activation_uri_callback), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "get-icon-drop-target-uri",
+                             G_CALLBACK (get_icon_drop_target_uri_callback), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "move-copy-items",
+                             G_CALLBACK (canvas_view_move_copy_items), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "get-container-uri",
+                             G_CALLBACK (canvas_view_get_container_uri), canvas_view, 0);
+
+    gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (content_widget),
+                                   GTK_WIDGET (canvas_container));
+
+    nautilus_canvas_view_update_click_mode (canvas_view);
+    nautilus_canvas_container_set_zoom_level (canvas_container,
+                                              get_default_zoom_level (canvas_view));
+
+    gtk_widget_show (GTK_WIDGET (canvas_container));
+}
+
+static void
+canvas_view_handle_uri_list (NautilusCanvasContainer *container,
+                             const char              *item_uris,
+                             const char              *target_uri,
+                             GdkDragAction            action,
+                             NautilusCanvasView      *view)
+{
+    nautilus_files_view_handle_uri_list_drop (NAUTILUS_FILES_VIEW (view),
+                                              item_uris, target_uri, action);
+}
+
+/* Handles an URL received from Mozilla */
+static void
+canvas_view_handle_netscape_url (NautilusCanvasContainer *container,
+                                 const char              *encoded_url,
+                                 const char              *target_uri,
+                                 GdkDragAction            action,
+                                 NautilusCanvasView      *view)
+{
+    nautilus_files_view_handle_netscape_url_drop (NAUTILUS_FILES_VIEW (view),
+                                                  encoded_url, target_uri, action);
+}
+
+static void
+canvas_view_handle_text (NautilusCanvasContainer *container,
+                         const char              *text,
+                         const char              *target_uri,
+                         GdkDragAction            action,
+                         NautilusCanvasView      *view)
+{
+    nautilus_files_view_handle_text_drop (NAUTILUS_FILES_VIEW (view),
+                                          text, target_uri, action);
+}
+
+static void
+canvas_view_handle_raw (NautilusCanvasContainer *container,
+                        const char              *raw_data,
+                        int                      length,
+                        const char              *target_uri,
+                        const char              *direct_save_uri,
+                        GdkDragAction            action,
+                        NautilusCanvasView      *view)
+{
+    nautilus_files_view_handle_raw_drop (NAUTILUS_FILES_VIEW (view),
+                                         raw_data, length, target_uri, direct_save_uri, action);
+}
+
+static void
+canvas_view_handle_hover (NautilusCanvasContainer *container,
+                          const char              *target_uri,
+                          NautilusCanvasView      *view)
+{
+    nautilus_files_view_handle_hover (NAUTILUS_FILES_VIEW (view), target_uri);
+}
+
+static char *
+canvas_view_get_first_visible_file (NautilusFilesView *view)
+{
+    NautilusFile *file;
+    NautilusCanvasView *canvas_view;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (view);
+
+    file = NAUTILUS_FILE (nautilus_canvas_container_get_first_visible_icon (get_canvas_container 
(canvas_view)));
+
+    if (file)
+    {
+        return nautilus_file_get_uri (file);
+    }
+
+    return NULL;
+}
+
+static void
+canvas_view_scroll_to_file (NautilusFilesView *view,
+                            const char        *uri)
+{
+    NautilusFile *file;
+    NautilusCanvasView *canvas_view;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (view);
+
+    if (uri != NULL)
+    {
+        /* Only if existing, since we don't want to add the file to
+         *  the directory if it has been removed since then */
+        file = nautilus_file_get_existing_by_uri (uri);
+        if (file != NULL)
+        {
+            nautilus_canvas_container_scroll_to_canvas (get_canvas_container (canvas_view),
+                                                        NAUTILUS_CANVAS_ICON_DATA (file));
+            nautilus_file_unref (file);
+        }
+    }
+}
+
+static guint
+nautilus_canvas_view_get_id (NautilusFilesView *view)
+{
+    return NAUTILUS_VIEW_GRID_ID;
+}
+
+static void
+nautilus_canvas_view_dispose (GObject *object)
+{
+    NautilusCanvasView *canvas_view;
+
+    canvas_view = NAUTILUS_CANVAS_VIEW (object);
+    canvas_view->destroyed = TRUE;
+
+    g_signal_handlers_disconnect_by_func (nautilus_preferences,
+                                          default_sort_order_changed_callback,
+                                          canvas_view);
+    g_signal_handlers_disconnect_by_func (nautilus_preferences,
+                                          image_display_policy_changed_callback,
+                                          canvas_view);
+
+    g_signal_handlers_disconnect_by_func (nautilus_icon_view_preferences,
+                                          text_attribute_names_changed_callback,
+                                          canvas_view);
+
+
+    G_OBJECT_CLASS (nautilus_canvas_view_parent_class)->dispose (object);
+}
+
+static void
+nautilus_canvas_view_class_init (NautilusCanvasViewClass *klass)
+{
+    NautilusFilesViewClass *nautilus_files_view_class;
+    GObjectClass *oclass;
+
+    nautilus_files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass);
+    oclass = G_OBJECT_CLASS (klass);
+
+    oclass->dispose = nautilus_canvas_view_dispose;
+
+    GTK_WIDGET_CLASS (klass)->destroy = nautilus_canvas_view_destroy;
+
+    nautilus_files_view_class->add_files = nautilus_canvas_view_add_files;
+    nautilus_files_view_class->begin_loading = nautilus_canvas_view_begin_loading;
+    nautilus_files_view_class->bump_zoom_level = nautilus_canvas_view_bump_zoom_level;
+    nautilus_files_view_class->can_zoom_in = nautilus_canvas_view_can_zoom_in;
+    nautilus_files_view_class->can_zoom_out = nautilus_canvas_view_can_zoom_out;
+    nautilus_files_view_class->get_zoom_level_percentage = nautilus_canvas_view_get_zoom_level_percentage;
+    nautilus_files_view_class->is_zoom_level_default = nautilus_canvas_view_is_zoom_level_default;
+    nautilus_files_view_class->clear = nautilus_canvas_view_clear;
+    nautilus_files_view_class->end_loading = nautilus_canvas_view_end_loading;
+    nautilus_files_view_class->file_changed = nautilus_canvas_view_file_changed;
+    nautilus_files_view_class->compute_rename_popover_pointing_to = 
nautilus_canvas_view_compute_rename_popover_pointing_to;
+    nautilus_files_view_class->get_selection = nautilus_canvas_view_get_selection;
+    nautilus_files_view_class->get_selection_for_file_transfer = nautilus_canvas_view_get_selection;
+    nautilus_files_view_class->is_empty = nautilus_canvas_view_is_empty;
+    nautilus_files_view_class->remove_file = nautilus_canvas_view_remove_file;
+    nautilus_files_view_class->restore_standard_zoom_level = 
nautilus_canvas_view_restore_standard_zoom_level;
+    nautilus_files_view_class->reveal_selection = nautilus_canvas_view_reveal_selection;
+    nautilus_files_view_class->select_all = nautilus_canvas_view_select_all;
+    nautilus_files_view_class->select_first = nautilus_canvas_view_select_first;
+    nautilus_files_view_class->set_selection = nautilus_canvas_view_set_selection;
+    nautilus_files_view_class->invert_selection = nautilus_canvas_view_invert_selection;
+    nautilus_files_view_class->compare_files = compare_files;
+    nautilus_files_view_class->click_policy_changed = nautilus_canvas_view_click_policy_changed;
+    nautilus_files_view_class->update_actions_state = nautilus_canvas_view_update_actions_state;
+    nautilus_files_view_class->sort_directories_first_changed = 
nautilus_canvas_view_sort_directories_first_changed;
+    nautilus_files_view_class->widget_to_file_operation_position = 
nautilus_canvas_view_widget_to_file_operation_position;
+    nautilus_files_view_class->get_view_id = nautilus_canvas_view_get_id;
+    nautilus_files_view_class->get_first_visible_file = canvas_view_get_first_visible_file;
+    nautilus_files_view_class->scroll_to_file = canvas_view_scroll_to_file;
+    nautilus_files_view_class->reveal_for_selection_context_menu = 
nautilus_canvas_view_reveal_for_selection_context_menu;
+    nautilus_files_view_class->preview_selection_event = nautilus_canvas_view_preview_selection_event;
+}
+
+static void
+nautilus_canvas_view_init (NautilusCanvasView *canvas_view)
+{
+    NautilusCanvasContainer *canvas_container;
+    GActionGroup *view_action_group;
+    GtkClipboard *clipboard;
+
+    canvas_view->sort = &sort_criteria[0];
+    canvas_view->destroyed = FALSE;
+
+    canvas_container = nautilus_canvas_view_container_new (canvas_view);
+    initialize_canvas_container (canvas_view, canvas_container);
+
+    g_signal_connect_swapped (nautilus_preferences,
+                              "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER,
+                              G_CALLBACK (default_sort_order_changed_callback),
+                              canvas_view);
+    g_signal_connect_swapped (nautilus_preferences,
+                              "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER,
+                              G_CALLBACK (default_sort_order_changed_callback),
+                              canvas_view);
+    g_signal_connect_swapped (nautilus_preferences,
+                              "changed::" NAUTILUS_PREFERENCES_SHOW_FILE_THUMBNAILS,
+                              G_CALLBACK (image_display_policy_changed_callback),
+                              canvas_view);
+
+    g_signal_connect_swapped (nautilus_icon_view_preferences,
+                              "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS,
+                              G_CALLBACK (text_attribute_names_changed_callback),
+                              canvas_view);
+
+    g_signal_connect_object (canvas_container, "handle-uri-list",
+                             G_CALLBACK (canvas_view_handle_uri_list), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "handle-netscape-url",
+                             G_CALLBACK (canvas_view_handle_netscape_url), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "handle-text",
+                             G_CALLBACK (canvas_view_handle_text), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "handle-raw",
+                             G_CALLBACK (canvas_view_handle_raw), canvas_view, 0);
+    g_signal_connect_object (canvas_container, "handle-hover",
+                             G_CALLBACK (canvas_view_handle_hover), canvas_view, 0);
+
+    /* React to clipboard changes */
+    clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+    g_signal_connect (clipboard, "owner-change",
+                      G_CALLBACK (on_clipboard_owner_changed), canvas_view);
+
+    view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (canvas_view));
+    g_action_map_add_action_entries (G_ACTION_MAP (view_action_group),
+                                     canvas_view_entries,
+                                     G_N_ELEMENTS (canvas_view_entries),
+                                     canvas_view);
+    /* Keep the action synced with the actual value, so the toolbar can poll it */
+    g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW 
(canvas_view)),
+                                        "zoom-to-level", g_variant_new_int32 (get_default_zoom_level 
(canvas_view)));
+}
+
+NautilusFilesView *
+nautilus_canvas_view_new (NautilusWindowSlot *slot)
+{
+    return g_object_new (NAUTILUS_TYPE_CANVAS_VIEW,
+                         "window-slot", slot,
+                         NULL);
+}
diff --git a/src/nautilus-canvas-view.h b/src/nautilus-canvas-view.h
new file mode 100644
index 000000000..4e8f1ac8b
--- /dev/null
+++ b/src/nautilus-canvas-view.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* nautilus-canvas-view.h - interface for canvas view of directory.
+ *
+ * Copyright (C) 2000 Eazel, Inc.
+ *
+ * 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,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: John Sullivan <sullivan eazel com>
+ *
+ */
+
+#pragma once
+
+#include "nautilus-files-view.h"
+
+#include "nautilus-types.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_CANVAS_VIEW nautilus_canvas_view_get_type()
+
+G_DECLARE_FINAL_TYPE (NautilusCanvasView, nautilus_canvas_view, NAUTILUS, CANVAS_VIEW, NautilusFilesView)
+
+int     nautilus_canvas_view_compare_files (NautilusCanvasView   *canvas_view,
+                                         NautilusFile *a,
+                                         NautilusFile *b);
+
+NautilusFilesView * nautilus_canvas_view_new (NautilusWindowSlot *slot);
+
+NautilusCanvasContainer * nautilus_canvas_view_get_canvas_container (NautilusCanvasView *view);
+
+G_END_DECLS
diff --git a/src/nautilus-debug.c b/src/nautilus-debug.c
index bbf76567c..d189a6f9a 100644
--- a/src/nautilus-debug.c
+++ b/src/nautilus-debug.c
@@ -41,6 +41,7 @@ static GDebugKey keys[] =
     { "DBus", NAUTILUS_DEBUG_DBUS },
     { "DirectoryView", NAUTILUS_DEBUG_DIRECTORY_VIEW },
     { "File", NAUTILUS_DEBUG_FILE },
+    { "CanvasContainer", NAUTILUS_DEBUG_CANVAS_CONTAINER },
     { "IconView", NAUTILUS_DEBUG_GRID_VIEW },
     { "ListView", NAUTILUS_DEBUG_LIST_VIEW },
     { "Mime", NAUTILUS_DEBUG_MIME },
diff --git a/src/nautilus-debug.h b/src/nautilus-debug.h
index 9322a8d62..c277f37a5 100644
--- a/src/nautilus-debug.h
+++ b/src/nautilus-debug.h
@@ -35,18 +35,19 @@ typedef enum {
   NAUTILUS_DEBUG_DBUS = 1 << 4,
   NAUTILUS_DEBUG_DIRECTORY_VIEW = 1 << 5,
   NAUTILUS_DEBUG_FILE = 1 << 6,
-  NAUTILUS_DEBUG_GRID_VIEW = 1 << 7,
-  NAUTILUS_DEBUG_LIST_VIEW = 1 << 8,
-  NAUTILUS_DEBUG_MIME = 1 << 9,
-  NAUTILUS_DEBUG_PLACES = 1 << 10,
-  NAUTILUS_DEBUG_PREVIEWER = 1 << 11,
-  NAUTILUS_DEBUG_SMCLIENT = 1 << 12,
-  NAUTILUS_DEBUG_WINDOW = 1 << 13,
-  NAUTILUS_DEBUG_UNDO = 1 << 14,
-  NAUTILUS_DEBUG_SEARCH = 1 << 15,
-  NAUTILUS_DEBUG_SEARCH_HIT = 1 << 16,
-  NAUTILUS_DEBUG_THUMBNAILS = 1 << 17,
-  NAUTILUS_DEBUG_TAG_MANAGER = 1 << 18,
+  NAUTILUS_DEBUG_CANVAS_CONTAINER = 1 << 7,
+  NAUTILUS_DEBUG_GRID_VIEW = 1 << 8,
+  NAUTILUS_DEBUG_LIST_VIEW = 1 << 9,
+  NAUTILUS_DEBUG_MIME = 1 << 10,
+  NAUTILUS_DEBUG_PLACES = 1 << 11,
+  NAUTILUS_DEBUG_PREVIEWER = 1 << 12,
+  NAUTILUS_DEBUG_SMCLIENT = 1 << 13,
+  NAUTILUS_DEBUG_WINDOW = 1 << 14,
+  NAUTILUS_DEBUG_UNDO = 1 << 15,
+  NAUTILUS_DEBUG_SEARCH = 1 << 16,
+  NAUTILUS_DEBUG_SEARCH_HIT = 1 << 17,
+  NAUTILUS_DEBUG_THUMBNAILS = 1 << 18,
+  NAUTILUS_DEBUG_TAG_MANAGER = 1 << 19,
 } DebugFlags;
 
 void nautilus_debug_set_flags (DebugFlags flags);
diff --git a/src/nautilus-dnd.c b/src/nautilus-dnd.c
index 7b7a1ba2b..067bc27ec 100644
--- a/src/nautilus-dnd.c
+++ b/src/nautilus-dnd.c
@@ -32,6 +32,7 @@
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 #include "nautilus-file-utilities.h"
+#include "nautilus-canvas-dnd.h"
 #include <src/nautilus-list-view-dnd.h>
 #include <stdio.h>
 #include <string.h>
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c
index 5fff557eb..93270c9bf 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -51,6 +51,7 @@
 #include "nautilus-application.h"
 #include "nautilus-batch-rename-dialog.h"
 #include "nautilus-batch-rename-utilities.h"
+#include "nautilus-canvas-view.h"
 #include "nautilus-clipboard.h"
 #include "nautilus-compress-dialog-controller.h"
 #include "nautilus-directory.h"
diff --git a/src/nautilus-selection-canvas-item.c b/src/nautilus-selection-canvas-item.c
new file mode 100644
index 000000000..b54cb68ed
--- /dev/null
+++ b/src/nautilus-selection-canvas-item.c
@@ -0,0 +1,554 @@
+/* Nautilus - Canvas item for floating selection.
+ *
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Cosimo Cecchi <cosimoc redhat com>
+ */
+
+#include <config.h>
+
+#include "nautilus-selection-canvas-item.h"
+
+#include <math.h>
+
+enum
+{
+    PROP_X1 = 1,
+    PROP_Y1,
+    PROP_X2,
+    PROP_Y2,
+    NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL };
+
+typedef struct
+{
+    /*< public >*/
+    int x0, y0, x1, y1;
+}  Rect;
+
+struct _NautilusSelectionCanvasItemDetails
+{
+    Rect last_update_rect;
+    Rect last_outline_update_rect;
+    int last_outline_update_width;
+
+    double x1, y1, x2, y2;              /* Corners of item */
+};
+
+G_DEFINE_TYPE (NautilusSelectionCanvasItem, nautilus_selection_canvas_item, EEL_TYPE_CANVAS_ITEM);
+
+static void
+nautilus_selection_canvas_item_draw (EelCanvasItem  *item,
+                                     cairo_t        *cr,
+                                     cairo_region_t *region)
+{
+    NautilusSelectionCanvasItem *self;
+    double x1, y1, x2, y2;
+    int cx1, cy1, cx2, cy2;
+    double i2w_dx, i2w_dy;
+    GtkStyleContext *context;
+
+    self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
+
+    /* Get canvas pixel coordinates */
+    i2w_dx = 0.0;
+    i2w_dy = 0.0;
+    eel_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
+
+    x1 = self->priv->x1 + i2w_dx;
+    y1 = self->priv->y1 + i2w_dy;
+    x2 = self->priv->x2 + i2w_dx;
+    y2 = self->priv->y2 + i2w_dy;
+
+    eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
+    eel_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2);
+
+    if (cx2 <= cx1 || cy2 <= cy1)
+    {
+        return;
+    }
+
+    context = gtk_widget_get_style_context (GTK_WIDGET (item->canvas));
+
+    gtk_style_context_save (context);
+
+    gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND);
+
+    cairo_save (cr);
+
+    gtk_render_background (context, cr,
+                           cx1, cy1,
+                           cx2 - cx1,
+                           cy2 - cy1);
+    gtk_render_frame (context, cr,
+                      cx1, cy1,
+                      cx2 - cx1,
+                      cy2 - cy1);
+
+    cairo_restore (cr);
+
+    gtk_style_context_restore (context);
+}
+
+static double
+nautilus_selection_canvas_item_point (EelCanvasItem  *item,
+                                      double          x,
+                                      double          y,
+                                      int             cx,
+                                      int             cy,
+                                      EelCanvasItem **actual_item)
+{
+    NautilusSelectionCanvasItem *self;
+    double x1, y1, x2, y2;
+    double hwidth;
+    double dx, dy;
+
+    self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
+    *actual_item = item;
+
+    /* Find the bounds for the rectangle plus its outline width */
+
+    x1 = self->priv->x1;
+    y1 = self->priv->y1;
+    x2 = self->priv->x2;
+    y2 = self->priv->y2;
+
+    hwidth = (1.0 / item->canvas->pixels_per_unit) / 2.0;
+
+    x1 -= hwidth;
+    y1 -= hwidth;
+    x2 += hwidth;
+    y2 += hwidth;
+
+    /* Is point inside rectangle (which can be hollow if it has no fill set)? */
+
+    if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2))
+    {
+        return 0.0;
+    }
+
+    /* Point is outside rectangle */
+
+    if (x < x1)
+    {
+        dx = x1 - x;
+    }
+    else if (x > x2)
+    {
+        dx = x - x2;
+    }
+    else
+    {
+        dx = 0.0;
+    }
+
+    if (y < y1)
+    {
+        dy = y1 - y;
+    }
+    else if (y > y2)
+    {
+        dy = y - y2;
+    }
+    else
+    {
+        dy = 0.0;
+    }
+
+    return sqrt (dx * dx + dy * dy);
+}
+
+static void
+request_redraw_borders (EelCanvas *canvas,
+                        Rect      *update_rect,
+                        int        width)
+{
+    /* Top */
+    eel_canvas_request_redraw (canvas,
+                               update_rect->x0, update_rect->y0,
+                               update_rect->x1, update_rect->y0 + width);
+    /* Bottom */
+    eel_canvas_request_redraw (canvas,
+                               update_rect->x0, update_rect->y1 - width,
+                               update_rect->x1, update_rect->y1);
+    /* Left */
+    eel_canvas_request_redraw (canvas,
+                               update_rect->x0, update_rect->y0,
+                               update_rect->x0 + width, update_rect->y1);
+    /* Right */
+    eel_canvas_request_redraw (canvas,
+                               update_rect->x1 - width, update_rect->y0,
+                               update_rect->x1, update_rect->y1);
+}
+
+static Rect make_rect (int x0,
+                       int y0,
+                       int x1,
+                       int y1);
+
+static int
+rect_empty (const Rect *src)
+{
+    return (src->x1 <= src->x0 || src->y1 <= src->y0);
+}
+
+static gboolean
+rects_intersect (Rect r1,
+                 Rect r2)
+{
+    if (r1.x0 >= r2.x1)
+    {
+        return FALSE;
+    }
+    if (r2.x0 >= r1.x1)
+    {
+        return FALSE;
+    }
+    if (r1.y0 >= r2.y1)
+    {
+        return FALSE;
+    }
+    if (r2.y0 >= r1.y1)
+    {
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static void
+diff_rects_guts (Rect ra,
+                 Rect rb,
+                 int *count,
+                 Rect result[4])
+{
+    if (ra.x0 < rb.x0)
+    {
+        result[(*count)++] = make_rect (ra.x0, ra.y0, rb.x0, ra.y1);
+    }
+    if (ra.y0 < rb.y0)
+    {
+        result[(*count)++] = make_rect (ra.x0, ra.y0, ra.x1, rb.y0);
+    }
+    if (ra.x1 < rb.x1)
+    {
+        result[(*count)++] = make_rect (ra.x1, rb.y0, rb.x1, rb.y1);
+    }
+    if (ra.y1 < rb.y1)
+    {
+        result[(*count)++] = make_rect (rb.x0, ra.y1, rb.x1, rb.y1);
+    }
+}
+
+static void
+diff_rects (Rect r1,
+            Rect r2,
+            int *count,
+            Rect result[4])
+{
+    g_assert (count != NULL);
+    g_assert (result != NULL);
+
+    *count = 0;
+
+    if (rects_intersect (r1, r2))
+    {
+        diff_rects_guts (r1, r2, count, result);
+        diff_rects_guts (r2, r1, count, result);
+    }
+    else
+    {
+        if (!rect_empty (&r1))
+        {
+            result[(*count)++] = r1;
+        }
+        if (!rect_empty (&r2))
+        {
+            result[(*count)++] = r2;
+        }
+    }
+}
+
+static Rect
+make_rect (int x0,
+           int y0,
+           int x1,
+           int y1)
+{
+    Rect r;
+
+    r.x0 = x0;
+    r.y0 = y0;
+    r.x1 = x1;
+    r.y1 = y1;
+    return r;
+}
+
+static void
+nautilus_selection_canvas_item_update (EelCanvasItem *item,
+                                       double         i2w_dx,
+                                       double         i2w_dy,
+                                       gint           flags)
+{
+    NautilusSelectionCanvasItem *self;
+    NautilusSelectionCanvasItemDetails *priv;
+    double x1, y1, x2, y2;
+    int cx1, cy1, cx2, cy2;
+    int repaint_rects_count, i;
+    GtkStyleContext *context;
+    GtkBorder border;
+    Rect update_rect, repaint_rects[4];
+
+    if (EEL_CANVAS_ITEM_CLASS (nautilus_selection_canvas_item_parent_class)->update)
+    {
+        (*EEL_CANVAS_ITEM_CLASS (nautilus_selection_canvas_item_parent_class)->update)(item, i2w_dx, i2w_dy, 
flags);
+    }
+
+    self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
+    priv = self->priv;
+
+    x1 = priv->x1 + i2w_dx;
+    y1 = priv->y1 + i2w_dy;
+    x2 = priv->x2 + i2w_dx;
+    y2 = priv->y2 + i2w_dy;
+
+    eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
+    eel_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2);
+
+    update_rect = make_rect (cx1, cy1, cx2 + 1, cy2 + 1);
+    diff_rects (update_rect, priv->last_update_rect,
+                &repaint_rects_count, repaint_rects);
+    for (i = 0; i < repaint_rects_count; i++)
+    {
+        eel_canvas_request_redraw (item->canvas,
+                                   repaint_rects[i].x0, repaint_rects[i].y0,
+                                   repaint_rects[i].x1, repaint_rects[i].y1);
+    }
+
+    priv->last_update_rect = update_rect;
+
+    context = gtk_widget_get_style_context (GTK_WIDGET (item->canvas));
+
+    gtk_style_context_save (context);
+    gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND);
+    gtk_style_context_get_border (context, GTK_STATE_FLAG_NORMAL, &border);
+    gtk_style_context_restore (context);
+
+    cx1 -= border.left;
+    cy1 -= border.top;
+    cx2 += border.right;
+    cy2 += border.bottom;
+
+    update_rect = make_rect (cx1, cy1, cx2, cy2);
+    request_redraw_borders (item->canvas, &update_rect,
+                            border.left + border.top + border.right + border.bottom);
+    request_redraw_borders (item->canvas, &priv->last_outline_update_rect,
+                            priv->last_outline_update_width);
+    priv->last_outline_update_rect = update_rect;
+    priv->last_outline_update_width = border.left + border.top + border.right + border.bottom;
+
+    item->x1 = cx1;
+    item->y1 = cy1;
+    item->x2 = cx2;
+    item->y2 = cy2;
+}
+
+static void
+nautilus_selection_canvas_item_translate (EelCanvasItem *item,
+                                          double         dx,
+                                          double         dy)
+{
+    NautilusSelectionCanvasItem *self;
+
+    self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
+
+    self->priv->x1 += dx;
+    self->priv->y1 += dy;
+    self->priv->x2 += dx;
+    self->priv->y2 += dy;
+}
+
+static void
+nautilus_selection_canvas_item_bounds (EelCanvasItem *item,
+                                       double        *x1,
+                                       double        *y1,
+                                       double        *x2,
+                                       double        *y2)
+{
+    NautilusSelectionCanvasItem *self;
+    GtkStyleContext *context;
+    GtkBorder border;
+
+    self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
+    context = gtk_widget_get_style_context (GTK_WIDGET (item->canvas));
+
+    gtk_style_context_save (context);
+    gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND);
+    gtk_style_context_get_border (context, GTK_STATE_FLAG_NORMAL, &border);
+    gtk_style_context_restore (context);
+
+    *x1 = self->priv->x1 - (border.left / item->canvas->pixels_per_unit) / 2.0;
+    *y1 = self->priv->y1 - (border.top / item->canvas->pixels_per_unit) / 2.0;
+    *x2 = self->priv->x2 + (border.right / item->canvas->pixels_per_unit) / 2.0;
+    *y2 = self->priv->y2 + (border.bottom / item->canvas->pixels_per_unit) / 2.0;
+}
+
+static void
+nautilus_selection_canvas_item_set_property (GObject      *object,
+                                             guint         param_id,
+                                             const GValue *value,
+                                             GParamSpec   *pspec)
+{
+    EelCanvasItem *item;
+    NautilusSelectionCanvasItem *self;
+
+    self = NAUTILUS_SELECTION_CANVAS_ITEM (object);
+    item = EEL_CANVAS_ITEM (object);
+
+    switch (param_id)
+    {
+        case PROP_X1:
+        {
+            self->priv->x1 = g_value_get_double (value);
+
+            eel_canvas_item_request_update (item);
+        }
+        break;
+
+        case PROP_Y1:
+        {
+            self->priv->y1 = g_value_get_double (value);
+
+            eel_canvas_item_request_update (item);
+        }
+        break;
+
+        case PROP_X2:
+        {
+            self->priv->x2 = g_value_get_double (value);
+
+            eel_canvas_item_request_update (item);
+        }
+        break;
+
+        case PROP_Y2:
+        {
+            self->priv->y2 = g_value_get_double (value);
+
+            eel_canvas_item_request_update (item);
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+        }
+        break;
+    }
+}
+
+static void
+nautilus_selection_canvas_item_get_property (GObject    *object,
+                                             guint       param_id,
+                                             GValue     *value,
+                                             GParamSpec *pspec)
+{
+    NautilusSelectionCanvasItem *self;
+
+    self = NAUTILUS_SELECTION_CANVAS_ITEM (object);
+
+    switch (param_id)
+    {
+        case PROP_X1:
+        {
+            g_value_set_double (value, self->priv->x1);
+        }
+        break;
+
+        case PROP_Y1:
+        {
+            g_value_set_double (value, self->priv->y1);
+        }
+        break;
+
+        case PROP_X2:
+        {
+            g_value_set_double (value, self->priv->x2);
+        }
+        break;
+
+        case PROP_Y2:
+        {
+            g_value_set_double (value, self->priv->y2);
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+        }
+        break;
+    }
+}
+
+static void
+nautilus_selection_canvas_item_class_init (NautilusSelectionCanvasItemClass *klass)
+{
+    EelCanvasItemClass *item_class;
+    GObjectClass *gobject_class;
+
+    gobject_class = G_OBJECT_CLASS (klass);
+    item_class = EEL_CANVAS_ITEM_CLASS (klass);
+
+    gobject_class->set_property = nautilus_selection_canvas_item_set_property;
+    gobject_class->get_property = nautilus_selection_canvas_item_get_property;
+
+    item_class->draw = nautilus_selection_canvas_item_draw;
+    item_class->point = nautilus_selection_canvas_item_point;
+    item_class->update = nautilus_selection_canvas_item_update;
+    item_class->bounds = nautilus_selection_canvas_item_bounds;
+    item_class->translate = nautilus_selection_canvas_item_translate;
+
+    properties[PROP_X1] =
+        g_param_spec_double ("x1", NULL, NULL,
+                             -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+                             G_PARAM_READWRITE);
+    properties[PROP_Y1] =
+        g_param_spec_double ("y1", NULL, NULL,
+                             -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+                             G_PARAM_READWRITE);
+    properties[PROP_X2] =
+        g_param_spec_double ("x2", NULL, NULL,
+                             -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+                             G_PARAM_READWRITE);
+    properties[PROP_Y2] =
+        g_param_spec_double ("y2", NULL, NULL,
+                             -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+                             G_PARAM_READWRITE);
+
+    g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
+    g_type_class_add_private (klass, sizeof (NautilusSelectionCanvasItemDetails));
+}
+
+static void
+nautilus_selection_canvas_item_init (NautilusSelectionCanvasItem *self)
+{
+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NAUTILUS_TYPE_SELECTION_CANVAS_ITEM,
+                                              NautilusSelectionCanvasItemDetails);
+}
diff --git a/src/nautilus-selection-canvas-item.h b/src/nautilus-selection-canvas-item.h
new file mode 100644
index 000000000..6c13fe2a8
--- /dev/null
+++ b/src/nautilus-selection-canvas-item.h
@@ -0,0 +1,62 @@
+
+/* Nautilus - Canvas item for floating selection.
+ *
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Cosimo Cecchi <cosimoc redhat com>
+ */
+
+#pragma once
+
+#include <eel/eel-canvas.h>
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_SELECTION_CANVAS_ITEM nautilus_selection_canvas_item_get_type()
+#define NAUTILUS_SELECTION_CANVAS_ITEM(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_SELECTION_CANVAS_ITEM, NautilusSelectionCanvasItem))
+#define NAUTILUS_SELECTION_CANVAS_ITEM_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_SELECTION_CANVAS_ITEM, NautilusSelectionCanvasItemClass))
+#define NAUTILUS_IS_SELECTION_CANVAS_ITEM(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_SELECTION_CANVAS_ITEM))
+#define NAUTILUS_IS_SELECTION_CANVAS_ITEM_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_SELECTION_CANVAS_ITEM))
+#define NAUTILUS_SELECTION_CANVAS_ITEM_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), NAUTILUS_TYPE_SELECTION_CANVAS_ITEM, NautilusSelectionCanvasItemClass))
+
+typedef struct _NautilusSelectionCanvasItem NautilusSelectionCanvasItem;
+typedef struct _NautilusSelectionCanvasItemClass NautilusSelectionCanvasItemClass;
+typedef struct _NautilusSelectionCanvasItemDetails NautilusSelectionCanvasItemDetails;
+
+struct _NautilusSelectionCanvasItem {
+       EelCanvasItem item;
+       NautilusSelectionCanvasItemDetails *priv;
+       gpointer user_data;
+};
+
+struct _NautilusSelectionCanvasItemClass {
+       EelCanvasItemClass parent_class;
+};
+
+/* GObject */
+GType       nautilus_selection_canvas_item_get_type                 (void);
+
+void nautilus_selection_canvas_item_fade_out (NautilusSelectionCanvasItem *self,
+                                             guint transition_time);
+
+G_END_DECLS
\ No newline at end of file
diff --git a/src/resources/css/Adwaita.css b/src/resources/css/Adwaita.css
index 0e2f302f0..968794388 100644
--- a/src/resources/css/Adwaita.css
+++ b/src/resources/css/Adwaita.css
@@ -8,6 +8,18 @@
     opacity: 0.50;
 }
 
+.nautilus-canvas-item {
+    border-radius: 5px;
+}
+
+.nautilus-canvas-item.dim-label {
+    color: mix (@theme_fg_color, @theme_bg_color, 0.50);
+}
+
+.nautilus-canvas-item.dim-label:selected {
+    color: mix (@theme_selected_fg_color, @theme_selected_bg_color, 0.20);
+}
+
 /* Toolbar */
 
 /* Here we use the .button background-image colors from Adwaita, but ligthen them,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]