]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtklist.c
Deprecate widget flag: GTK_WIDGET_VISIBLE
[~andy/gtk] / gtk / gtklist.c
index 6ce42f7308cffbbcc6054fc88ff992f18362a536..9b9f4f08a3d66809969b0991cfda5f388234dfcf 100644 (file)
@@ -2,25 +2,43 @@
  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) 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.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+#include "config.h"
+#include <string.h> /* memset */
+
+#undef GTK_DISABLE_DEPRECATED
+#define __GTK_LIST_C__
+
 #include "gtklist.h"
 #include "gtklistitem.h"
 #include "gtkmain.h"
 #include "gtksignal.h"
 #include "gtklabel.h"
+#include "gtkmarshalers.h"
+#include "gtkintl.h"
+
+#include "gtkalias.h"
 
 enum {
   SELECTION_CHANGED,
@@ -29,75 +47,135 @@ enum {
   LAST_SIGNAL
 };
 
+enum {
+  ARG_0,
+  ARG_SELECTION_MODE
+};
+
 #define SCROLL_TIME  100
 
+/*** GtkList Methods ***/
 static void gtk_list_class_init             (GtkListClass   *klass);
 static void gtk_list_init           (GtkList        *list);
-static void gtk_list_shutdown       (GtkObject      *object);
-static void gtk_list_destroy        (GtkObject      *object);
-static void gtk_list_map            (GtkWidget      *widget);
-static void gtk_list_unmap          (GtkWidget      *widget);
+static void gtk_list_set_arg         (GtkObject      *object,
+                                     GtkArg         *arg,
+                                     guint           arg_id);
+static void gtk_list_get_arg         (GtkObject      *object,
+                                     GtkArg         *arg,
+                                     guint           arg_id);
+/*** GtkObject Methods ***/
+static void gtk_list_dispose        (GObject        *object);
+
+/*** GtkWidget Methods ***/
+static void gtk_list_size_request    (GtkWidget             *widget,
+                                     GtkRequisition *requisition);
+static void gtk_list_size_allocate   (GtkWidget             *widget,
+                                     GtkAllocation  *allocation);
 static void gtk_list_realize        (GtkWidget      *widget);
-static void gtk_list_draw           (GtkWidget      *widget,
-                                     GdkRectangle   *area);
-static gint gtk_list_expose         (GtkWidget      *widget,
-                                     GdkEventExpose *event);
+static void gtk_list_unmap          (GtkWidget      *widget);
+static void gtk_list_style_set      (GtkWidget      *widget,
+                                     GtkStyle       *previous_style);
 static gint gtk_list_motion_notify   (GtkWidget      *widget,
                                      GdkEventMotion *event);
 static gint gtk_list_button_press    (GtkWidget      *widget,
                                      GdkEventButton *event);
 static gint gtk_list_button_release  (GtkWidget             *widget,
                                      GdkEventButton *event);
-static void gtk_list_size_request    (GtkWidget             *widget,
-                                     GtkRequisition *requisition);
-static void gtk_list_size_allocate   (GtkWidget             *widget,
-                                     GtkAllocation  *allocation);
-static void gtk_list_add            (GtkContainer   *container,
-                                     GtkWidget      *widget);
-static void gtk_list_remove         (GtkContainer   *container,
-                                     GtkWidget      *widget);
-static void gtk_list_forall         (GtkContainer   *container,
-                                     gboolean        include_internals,
-                                     GtkCallback     callback,
-                                     gpointer        callback_data);
-
-static void gtk_real_list_select_child  (GtkList       *list,
-                                         GtkWidget     *child);
-static void gtk_real_list_unselect_child (GtkList      *list,
-                                         GtkWidget     *child);
-
-
-static void gtk_list_set_anchor          (GtkList       *list,
-                                         gboolean       add_mode,
-                                         gint           anchor,
-                                         GtkWidget     *undo_focus_child);
-static void gtk_list_fake_unselect_all   (GtkList       *list,
-                                         GtkWidget     *item);
-static void gtk_list_fake_toggle_row     (GtkList       *list,
-                                         GtkWidget     *item);
-static void gtk_list_move_focus_child    (GtkList       *list,
-                                         GtkScrollType  scroll_type,
-                                         gfloat         position);
-static void gtk_list_update_extended_selection (GtkList *list,
-                                               gint     row);
-static void gtk_list_focus_lost          (GtkWidget     *item,
-                                         GdkEventKey   *event,
-                                         GtkList       *list);
-static void gtk_list_set_focus_child     (GtkContainer  *container,
-                                         GtkWidget     *widget);
-static gint gtk_list_focus               (GtkContainer     *container,
-                                         GtkDirectionType  direction);
-
-
-static GtkType gtk_list_child_type  (GtkContainer   *container);
+
+static gboolean gtk_list_focus       (GtkWidget        *widget,
+                                      GtkDirectionType  direction);
+
+/*** GtkContainer Methods ***/
+static void gtk_list_add            (GtkContainer     *container,
+                                     GtkWidget        *widget);
+static void gtk_list_remove         (GtkContainer     *container,
+                                     GtkWidget        *widget);
+static void gtk_list_forall         (GtkContainer     *container,
+                                     gboolean          include_internals,
+                                     GtkCallback       callback,
+                                     gpointer          callback_data);
+static GtkType gtk_list_child_type   (GtkContainer     *container);
+static void gtk_list_set_focus_child (GtkContainer     *container,
+                                     GtkWidget        *widget);
+
+/*** GtkList Private Functions ***/
+static void gtk_list_move_focus_child      (GtkList       *list,
+                                           GtkScrollType  scroll_type,
+                                           gfloat         position);
+static gint gtk_list_horizontal_timeout    (GtkWidget     *list);
+static gint gtk_list_vertical_timeout      (GtkWidget     *list);
+static void gtk_list_remove_items_internal (GtkList       *list,
+                                           GList         *items,
+                                           gboolean       no_unref);
+
+/*** GtkList Selection Methods ***/
+static void gtk_real_list_select_child         (GtkList   *list,
+                                                GtkWidget *child);
+static void gtk_real_list_unselect_child        (GtkList   *list,
+                                                GtkWidget *child);
+
+/*** GtkList Selection Functions ***/
+static void gtk_list_set_anchor                 (GtkList   *list,
+                                                gboolean   add_mode,
+                                                gint       anchor,
+                                                GtkWidget *undo_focus_child);
+static void gtk_list_fake_unselect_all          (GtkList   *list,
+                                                GtkWidget *item);
+static void gtk_list_fake_toggle_row            (GtkList   *list,
+                                                GtkWidget *item);
+static void gtk_list_update_extended_selection  (GtkList   *list,
+                                                gint       row);
+static void gtk_list_reset_extended_selection   (GtkList   *list);
+
+/*** GtkListItem Signal Functions ***/
+static void gtk_list_signal_drag_begin         (GtkWidget      *widget,
+                                               GdkDragContext *context,
+                                               GtkList        *list);
+static void gtk_list_signal_toggle_focus_row   (GtkListItem   *list_item,
+                                               GtkList       *list);
+static void gtk_list_signal_select_all         (GtkListItem   *list_item,
+                                               GtkList       *list);
+static void gtk_list_signal_unselect_all       (GtkListItem   *list_item,
+                                               GtkList       *list);
+static void gtk_list_signal_undo_selection     (GtkListItem   *list_item,
+                                               GtkList       *list);
+static void gtk_list_signal_start_selection    (GtkListItem   *list_item,
+                                               GtkList       *list);
+static void gtk_list_signal_end_selection      (GtkListItem   *list_item,
+                                               GtkList       *list);
+static void gtk_list_signal_extend_selection   (GtkListItem   *list_item,
+                                               GtkScrollType  scroll_type,
+                                               gfloat         position,
+                                               gboolean       auto_start_selection,
+                                               GtkList       *list);
+static void gtk_list_signal_scroll_horizontal  (GtkListItem   *list_item,
+                                               GtkScrollType  scroll_type,
+                                               gfloat         position,
+                                               GtkList       *list);
+static void gtk_list_signal_scroll_vertical    (GtkListItem   *list_item,
+                                               GtkScrollType  scroll_type,
+                                               gfloat         position,
+                                               GtkList       *list);
+static void gtk_list_signal_toggle_add_mode    (GtkListItem   *list_item,
+                                               GtkList       *list);
+static void gtk_list_signal_item_select        (GtkListItem   *list_item,
+                                               GtkList       *list);
+static void gtk_list_signal_item_deselect      (GtkListItem   *list_item,
+                                               GtkList       *list);
+static void gtk_list_signal_item_toggle        (GtkListItem   *list_item,
+                                               GtkList       *list);
+
+
+static void gtk_list_drag_begin (GtkWidget      *widget,
+                                GdkDragContext *context);
 
 
 static GtkContainerClass *parent_class = NULL;
 static guint list_signals[LAST_SIGNAL] = { 0 };
 
-static const gchar *vadjustment_key = "gtk-vadjustment";
+static const gchar vadjustment_key[] = "gtk-vadjustment";
 static guint        vadjustment_key_id = 0;
-static const gchar *hadjustment_key = "gtk-hadjustment";
+static const gchar hadjustment_key[] = "gtk-hadjustment";
 static guint        hadjustment_key_id = 0;
 
 GtkType
@@ -107,7 +185,7 @@ gtk_list_get_type (void)
 
   if (!list_type)
     {
-      GtkTypeInfo list_info =
+      static const GtkTypeInfo list_info =
       {
        "GtkList",
        sizeof (GtkList),
@@ -119,6 +197,7 @@ gtk_list_get_type (void)
         (GtkClassInitFunc) NULL,
       };
 
+      I_("GtkList");
       list_type = gtk_type_unique (GTK_TYPE_CONTAINER, &list_info);
     }
 
@@ -128,6 +207,7 @@ gtk_list_get_type (void)
 static void
 gtk_list_class_init (GtkListClass *class)
 {
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
   GtkObjectClass *object_class;
   GtkWidgetClass *widget_class;
   GtkContainerClass *container_class;
@@ -141,62 +221,61 @@ gtk_list_class_init (GtkListClass *class)
   vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
   hadjustment_key_id = g_quark_from_static_string (hadjustment_key);
 
-  list_signals[SELECTION_CHANGED] =
-    gtk_signal_new ("selection_changed",
-                   GTK_RUN_FIRST,
-                   object_class->type,
-                   GTK_SIGNAL_OFFSET (GtkListClass, selection_changed),
-                   gtk_marshal_NONE__NONE,
-                   GTK_TYPE_NONE, 0);
-  list_signals[SELECT_CHILD] =
-    gtk_signal_new ("select_child",
-                   GTK_RUN_FIRST,
-                   object_class->type,
-                   GTK_SIGNAL_OFFSET (GtkListClass, select_child),
-                   gtk_marshal_NONE__POINTER,
-                   GTK_TYPE_NONE, 1,
-                   GTK_TYPE_WIDGET);
-  list_signals[UNSELECT_CHILD] =
-    gtk_signal_new ("unselect_child",
-                   GTK_RUN_FIRST,
-                   object_class->type,
-                   GTK_SIGNAL_OFFSET (GtkListClass, unselect_child),
-                   gtk_marshal_NONE__POINTER,
-                   GTK_TYPE_NONE, 1,
-                   GTK_TYPE_WIDGET);
+  gobject_class->dispose = gtk_list_dispose;
 
-  gtk_object_class_add_signals (object_class, list_signals, LAST_SIGNAL);
 
-  object_class->shutdown = gtk_list_shutdown;
-  object_class->destroy = gtk_list_destroy;
+  object_class->set_arg = gtk_list_set_arg;
+  object_class->get_arg = gtk_list_get_arg;
 
-  widget_class->map = gtk_list_map;
   widget_class->unmap = gtk_list_unmap;
+  widget_class->style_set = gtk_list_style_set;
   widget_class->realize = gtk_list_realize;
-  widget_class->draw = gtk_list_draw;
-  widget_class->expose_event = gtk_list_expose;
   widget_class->button_press_event = gtk_list_button_press;
   widget_class->button_release_event = gtk_list_button_release;
   widget_class->motion_notify_event = gtk_list_motion_notify;
   widget_class->size_request = gtk_list_size_request;
   widget_class->size_allocate = gtk_list_size_allocate;
-
+  widget_class->drag_begin = gtk_list_drag_begin;
+  widget_class->focus = gtk_list_focus;
+  
   container_class->add = gtk_list_add;
   container_class->remove = gtk_list_remove;
   container_class->forall = gtk_list_forall;
   container_class->child_type = gtk_list_child_type;
   container_class->set_focus_child = gtk_list_set_focus_child;
-  container_class->focus = gtk_list_focus;
 
   class->selection_changed = NULL;
   class->select_child = gtk_real_list_select_child;
   class->unselect_child = gtk_real_list_unselect_child;
-}
 
-static GtkType
-gtk_list_child_type (GtkContainer     *container)
-{
-  return GTK_TYPE_LIST_ITEM;
+  list_signals[SELECTION_CHANGED] =
+    gtk_signal_new (I_("selection-changed"),
+                   GTK_RUN_FIRST,
+                   GTK_CLASS_TYPE (object_class),
+                   GTK_SIGNAL_OFFSET (GtkListClass, selection_changed),
+                   _gtk_marshal_VOID__VOID,
+                   GTK_TYPE_NONE, 0);
+  list_signals[SELECT_CHILD] =
+    gtk_signal_new (I_("select-child"),
+                   GTK_RUN_FIRST,
+                   GTK_CLASS_TYPE (object_class),
+                   GTK_SIGNAL_OFFSET (GtkListClass, select_child),
+                   _gtk_marshal_VOID__OBJECT,
+                   GTK_TYPE_NONE, 1,
+                   GTK_TYPE_WIDGET);
+  list_signals[UNSELECT_CHILD] =
+    gtk_signal_new (I_("unselect-child"),
+                   GTK_RUN_FIRST,
+                   GTK_CLASS_TYPE (object_class),
+                   GTK_SIGNAL_OFFSET (GtkListClass, unselect_child),
+                   _gtk_marshal_VOID__OBJECT,
+                   GTK_TYPE_NONE, 1,
+                   GTK_TYPE_WIDGET);
+  
+  gtk_object_add_arg_type ("GtkList::selection-mode",
+                          GTK_TYPE_SELECTION_MODE, 
+                          GTK_ARG_READWRITE | G_PARAM_STATIC_NAME,
+                          ARG_SELECTION_MODE);
 }
 
 static void
@@ -223,663 +302,212 @@ gtk_list_init (GtkList *list)
   list->add_mode = FALSE;
 }
 
+static void
+gtk_list_set_arg (GtkObject      *object,
+                 GtkArg         *arg,
+                 guint           arg_id)
+{
+  GtkList *list = GTK_LIST (object);
+  
+  switch (arg_id)
+    {
+    case ARG_SELECTION_MODE:
+      gtk_list_set_selection_mode (list, GTK_VALUE_ENUM (*arg));
+      break;
+    }
+}
+
+static void
+gtk_list_get_arg (GtkObject      *object,
+                 GtkArg         *arg,
+                 guint           arg_id)
+{
+  GtkList *list = GTK_LIST (object);
+  
+  switch (arg_id)
+    {
+    case ARG_SELECTION_MODE: 
+      GTK_VALUE_ENUM (*arg) = list->selection_mode; 
+      break;
+    default:
+      arg->type = GTK_TYPE_INVALID;
+      break;
+    }
+}
+
 GtkWidget*
 gtk_list_new (void)
 {
   return GTK_WIDGET (gtk_type_new (GTK_TYPE_LIST));
 }
 
+
+/* Private GtkObject Methods :
+ * 
+ * gtk_list_dispose
+ */
 static void
-gtk_list_shutdown (GtkObject *object)
+gtk_list_dispose (GObject *object)
 {
   gtk_list_clear_items (GTK_LIST (object), 0, -1);
 
-  GTK_OBJECT_CLASS (parent_class)->shutdown (object);
+  G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
+
+/* Private GtkWidget Methods :
+ * 
+ * gtk_list_size_request
+ * gtk_list_size_allocate
+ * gtk_list_realize
+ * gtk_list_unmap
+ * gtk_list_motion_notify
+ * gtk_list_button_press
+ * gtk_list_button_release
+ */
 static void
-gtk_list_destroy (GtkObject *object)
+gtk_list_size_request (GtkWidget      *widget,
+                      GtkRequisition *requisition)
 {
-  GList *node;
+  GtkList *list = GTK_LIST (widget);
+  GtkWidget *child;
+  GList *children;
 
-  GtkList *list = GTK_LIST (object);
+  requisition->width = 0;
+  requisition->height = 0;
 
-  for (node = list->children; node; node = node->next)
+  children = list->children;
+  while (children)
     {
-      GtkWidget *child;
-
-      child = (GtkWidget *)node->data;
-      gtk_widget_ref (child);
-      gtk_widget_unparent (child);
-      gtk_widget_destroy (child);
-      gtk_widget_unref (child);
-    }
-  g_list_free (list->children);
-  list->children = NULL;
+      child = children->data;
+      children = children->next;
 
-  for (node = list->selection; node; node = node->next)
-    {
-      GtkWidget *child;
+      if (gtk_widget_get_visible (child))
+       {
+         GtkRequisition child_requisition;
+         
+         gtk_widget_size_request (child, &child_requisition);
 
-      child = (GtkWidget *)node->data;
-      gtk_widget_unref (child);
+         requisition->width = MAX (requisition->width,
+                                   child_requisition.width);
+         requisition->height += child_requisition.height;
+       }
     }
-  g_list_free (list->selection);
-  list->selection = NULL;
-
-  if (GTK_OBJECT_CLASS (parent_class)->destroy)
-    (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
-}
 
-void
-gtk_list_end_drag_selection (GtkList *list)
-{
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
+  requisition->width += GTK_CONTAINER (list)->border_width * 2;
+  requisition->height += GTK_CONTAINER (list)->border_width * 2;
 
-  list->drag_selection = FALSE;
-  if (GTK_WIDGET_HAS_GRAB (list))
-    {
-      gtk_grab_remove (GTK_WIDGET (list));
-      gdk_pointer_ungrab (GDK_CURRENT_TIME);
-    }
-  if (list->htimer)
-    {
-      gtk_timeout_remove (list->htimer);
-      list->htimer = 0;
-    }
-  if (list->vtimer)
-    {
-      gtk_timeout_remove (list->vtimer);
-      list->vtimer = 0;
-    }
+  requisition->width = MAX (requisition->width, 1);
+  requisition->height = MAX (requisition->height, 1);
 }
 
-void
-gtk_list_insert_items (GtkList *list,
-                      GList   *items,
-                      gint     position)
+static void
+gtk_list_size_allocate (GtkWidget     *widget,
+                       GtkAllocation *allocation)
 {
-  GtkWidget *widget;
-  GList *tmp_list;
-  GList *last;
-  gint nchildren;
-
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
-
-  if (!items)
-    return;
+  GtkList *list = GTK_LIST (widget);
+  GtkWidget *child;
+  GtkAllocation child_allocation;
+  GList *children;
 
-  gtk_list_end_drag_selection (list);
-  if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
-    gtk_list_end_selection (list);
+  widget->allocation = *allocation;
+  if (GTK_WIDGET_REALIZED (widget))
+    gdk_window_move_resize (widget->window,
+                           allocation->x, allocation->y,
+                           allocation->width, allocation->height);
 
-  tmp_list = items;
-  while (tmp_list)
+  if (list->children)
     {
-      widget = tmp_list->data;
-      tmp_list = tmp_list->next;
+      child_allocation.x = GTK_CONTAINER (list)->border_width;
+      child_allocation.y = GTK_CONTAINER (list)->border_width;
+      child_allocation.width = MAX (1, (gint)allocation->width -
+                                   child_allocation.x * 2);
 
-      gtk_widget_set_parent (widget, GTK_WIDGET (list));
-      gtk_signal_connect (GTK_OBJECT (widget), "focus_out_event",
-                         GTK_SIGNAL_FUNC (gtk_list_focus_lost), list);
+      children = list->children;
 
-      if (GTK_WIDGET_VISIBLE (widget->parent))
+      while (children)
        {
-         if (GTK_WIDGET_REALIZED (widget->parent) &&
-             !GTK_WIDGET_REALIZED (widget))
-           gtk_widget_realize (widget);
+         child = children->data;
+         children = children->next;
 
-         if (GTK_WIDGET_MAPPED (widget->parent) &&
-             !GTK_WIDGET_MAPPED (widget))
-           gtk_widget_map (widget);
-       }
-    }
+         if (gtk_widget_get_visible (child))
+           {
+             GtkRequisition child_requisition;
+             gtk_widget_get_child_requisition (child, &child_requisition);
+             
+             child_allocation.height = child_requisition.height;
 
-  nchildren = g_list_length (list->children);
-  if ((position < 0) || (position > nchildren))
-    position = nchildren;
+             gtk_widget_size_allocate (child, &child_allocation);
 
-  if (position == nchildren)
-    {
-      if (list->children)
-       {
-         tmp_list = g_list_last (list->children);
-         tmp_list->next = items;
-         items->prev = tmp_list;
-       }
-      else
-       {
-         list->children = items;
+             child_allocation.y += child_allocation.height;
+           }
        }
     }
-  else
-    {
-      tmp_list = g_list_nth (list->children, position);
-      last = g_list_last (items);
+}
 
-      if (tmp_list->prev)
-       tmp_list->prev->next = items;
-      last->next = tmp_list;
-      items->prev = tmp_list->prev;
-      tmp_list->prev = last;
+static void
+gtk_list_realize (GtkWidget *widget)
+{
+  GdkWindowAttr attributes;
+  gint attributes_mask;
 
-      if (tmp_list == list->children)
-       list->children = items;
-    }
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
 
-  if (list->children && !list->selection &&
-      (list->selection_mode == GTK_SELECTION_BROWSE))
-    {
-      widget = list->children->data;
-      gtk_list_select_child (list, widget);
-    }
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.x = widget->allocation.x;
+  attributes.y = widget->allocation.y;
+  attributes.width = widget->allocation.width;
+  attributes.height = widget->allocation.height;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
 
-  if (GTK_WIDGET_VISIBLE (list))
-    gtk_widget_queue_resize (GTK_WIDGET (list));
-}
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
 
-void
-gtk_list_append_items (GtkList *list,
-                      GList   *items)
-{
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
+  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                  &attributes, attributes_mask);
+  gdk_window_set_user_data (widget->window, widget);
 
-  gtk_list_insert_items (list, items, -1);
+  widget->style = gtk_style_attach (widget->style, widget->window);
+  gdk_window_set_background (widget->window, 
+                            &widget->style->base[GTK_STATE_NORMAL]);
 }
 
-void
-gtk_list_prepend_items (GtkList *list,
-                       GList   *items)
+static gboolean
+list_has_grab (GtkList *list)
 {
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
-
-  gtk_list_insert_items (list, items, 0);
+  return (GTK_WIDGET_HAS_GRAB (list) &&
+         gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (list))));
+         
 }
 
 static void
-gtk_list_remove_items_internal (GtkList         *list,
-                               GList    *items,
-                               gboolean no_unref)
+gtk_list_unmap (GtkWidget *widget)
 {
-  GtkWidget *widget;
-  GtkWidget *new_focus_child;
-  GtkWidget *old_focus_child;
-  GtkContainer *container;
-  GList *selected_widgets;
-  GList *tmp_list;
-  GList *work;
-  gboolean grab_focus = FALSE;
-  
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
+  GtkList *list = GTK_LIST (widget);
 
-  if (!items)
+  if (!GTK_WIDGET_MAPPED (widget))
     return;
-  
-  container = GTK_CONTAINER (list);
-
-  gtk_list_end_drag_selection (list);
-  if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
-    gtk_list_end_selection (list);
 
-  
-  tmp_list = items;
-  selected_widgets = NULL;
-  widget = NULL;
-  old_focus_child = new_focus_child = container->focus_child;
+  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
 
-  while (tmp_list)
+  if (list_has_grab (list))
     {
-      widget = tmp_list->data;
-
-      tmp_list = tmp_list->next;
-      
-      if (no_unref)
-       gtk_widget_ref (widget);
-      
-      gtk_signal_disconnect_by_func
-       (GTK_OBJECT (widget), GTK_SIGNAL_FUNC (gtk_list_focus_lost), list);
-
-
-      if (widget == new_focus_child) 
-       {
-         work = g_list_find (list->children, widget);
-
-         if (work)
-           {
-             if (work->next)
-               new_focus_child = work->next->data;
-             else if (list->children != work && work->prev)
-               new_focus_child = work->prev->data;
-             else
-               new_focus_child = NULL;
-             
-             if (GTK_WIDGET_HAS_FOCUS (widget))
-               grab_focus = TRUE;
-           }
-       }
-
-      list->children = g_list_remove (list->children, widget);
-
-      if (widget->state == GTK_STATE_SELECTED)
-       selected_widgets = g_list_prepend (selected_widgets, widget);
-      else
-       gtk_widget_unparent (widget);
-    }
-  
-  if (selected_widgets)
-    {
-      tmp_list = selected_widgets;
-      while (tmp_list)
-       {
-         widget = tmp_list->data;
-         tmp_list = tmp_list->next;
-         
-         gtk_list_unselect_child (list, widget);
-
-         gtk_widget_unparent (widget);
-       }
-      
-      gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
-    }
-  
-  g_list_free (selected_widgets);
-  
-  if (new_focus_child && new_focus_child != old_focus_child)
-    {
-      if (grab_focus)
-       gtk_widget_grab_focus (new_focus_child);
-      else
-       gtk_container_set_focus_child (container, new_focus_child);
-    }
-  
-  if (GTK_WIDGET_VISIBLE (list))
-    gtk_widget_queue_resize (GTK_WIDGET (list));
-}
-
-void
-gtk_list_remove_items (GtkList *list,
-                      GList    *items)
-{
-  gtk_list_remove_items_internal (list, items, FALSE);
-}
-
-void
-gtk_list_remove_items_no_unref (GtkList         *list,
-                               GList    *items)
-{
-  gtk_list_remove_items_internal (list, items, TRUE);
-}
-
-void
-gtk_list_clear_items (GtkList *list,
-                     gint     start,
-                     gint     end)
-{
-  GtkWidget *widget;
-  GList *start_list;
-  GList *end_list;
-  GList *tmp_list;
-  guint nchildren;
-
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
-
-  nchildren = g_list_length (list->children);
-
-  if (nchildren > 0)
-    {
-      gboolean selection_changed;
-
-      if ((end < 0) || (end > nchildren))
-       end = nchildren;
-
-      if (start >= end)
-       return;
-
-      start_list = g_list_nth (list->children, start);
-      end_list = g_list_nth (list->children, end);
-
-      gtk_list_end_drag_selection (list);
-      if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
-       gtk_list_end_selection (list);
-
-      if (start_list->prev)
-       start_list->prev->next = end_list;
-      if (end_list && end_list->prev)
-       end_list->prev->next = NULL;
-      if (end_list)
-       end_list->prev = start_list->prev;
-      if (start_list == list->children)
-       list->children = end_list;
-
-      selection_changed = FALSE;
-      widget = NULL;
-      tmp_list = start_list;
-
-      while (tmp_list)
-       {
-         widget = tmp_list->data;
-         tmp_list = tmp_list->next;
-
-         if (widget->state == GTK_STATE_SELECTED)
-           {
-             gtk_list_unselect_child (list, widget);
-             selection_changed = TRUE;
-           }
-
-         gtk_widget_unparent (widget);
-       }
-
-      g_list_free (start_list);
-
-      if (list->children && !list->selection &&
-         (list->selection_mode == GTK_SELECTION_BROWSE))
-       {
-         widget = list->children->data;
-         gtk_list_select_child (list, widget);
-       }
-      else if (selection_changed)
-       gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
-
-      gtk_widget_queue_resize (GTK_WIDGET (list));
-    }
-}
-
-void
-gtk_list_select_item (GtkList *list,
-                     gint     item)
-{
-  GList *tmp_list;
-
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
-
-  tmp_list = g_list_nth (list->children, item);
-  if (tmp_list)
-    gtk_list_select_child (list, GTK_WIDGET (tmp_list->data));
-}
-
-void
-gtk_list_unselect_item (GtkList *list,
-                       gint     item)
-{
-  GList *tmp_list;
-
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
-
-  tmp_list = g_list_nth (list->children, item);
-  if (tmp_list)
-    gtk_list_unselect_child (list, GTK_WIDGET (tmp_list->data));
-}
-
-void
-gtk_list_select_child (GtkList  *list,
-                      GtkWidget *child)
-{
-  gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECT_CHILD], child);
-}
-
-void
-gtk_list_unselect_child (GtkList   *list,
-                        GtkWidget *child)
-{
-  gtk_signal_emit (GTK_OBJECT (list), list_signals[UNSELECT_CHILD], child);
-}
-
-gint
-gtk_list_child_position (GtkList   *list,
-                        GtkWidget *child)
-{
-  GList *children;
-  gint pos;
-
-  g_return_val_if_fail (list != NULL, -1);
-  g_return_val_if_fail (GTK_IS_LIST (list), -1);
-  g_return_val_if_fail (child != NULL, -1);
-
-  pos = 0;
-  children = list->children;
-
-  while (children)
-    {
-      if (child == GTK_WIDGET (children->data))
-       return pos;
-
-      pos += 1;
-      children = children->next;
-    }
-
-  return -1;
-}
-
-void
-gtk_list_set_selection_mode (GtkList         *list,
-                            GtkSelectionMode  mode)
-{
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
-
-  if (list->selection_mode == mode)
-    return;
-
-  list->selection_mode = mode;
-
-  switch (mode)
-    {
-    case GTK_SELECTION_SINGLE:
-    case GTK_SELECTION_BROWSE:
-      gtk_list_unselect_all (list);
-      break;
-
-    default:
-      break;
-    }
-}
-
-
-static void
-gtk_list_map (GtkWidget *widget)
-{
-  GtkList *list;
-  GtkWidget *child;
-  GList *children;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_LIST (widget));
-
-  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
-  list = GTK_LIST (widget);
-
-  gdk_window_show (widget->window);
-
-  children = list->children;
-  while (children)
-    {
-      child = children->data;
-      children = children->next;
-
-      if (GTK_WIDGET_VISIBLE (child) &&
-         !GTK_WIDGET_MAPPED (child))
-       gtk_widget_map (child);
-    }
-}
-
-static void
-gtk_list_unmap (GtkWidget *widget)
-{
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_LIST (widget));
-
-  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
-  gdk_window_hide (widget->window);
-}
-
-static void
-gtk_list_realize (GtkWidget *widget)
-{
-  GdkWindowAttr attributes;
-  gint attributes_mask;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_LIST (widget));
-
-  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
-
-  attributes.window_type = GDK_WINDOW_CHILD;
-  attributes.x = widget->allocation.x;
-  attributes.y = widget->allocation.y;
-  attributes.width = widget->allocation.width;
-  attributes.height = widget->allocation.height;
-  attributes.wclass = GDK_INPUT_OUTPUT;
-  attributes.visual = gtk_widget_get_visual (widget);
-  attributes.colormap = gtk_widget_get_colormap (widget);
-  attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
-
-  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
-
-  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
-  gdk_window_set_user_data (widget->window, widget);
-
-  widget->style = gtk_style_attach (widget->style, widget->window);
-  gdk_window_set_background (widget->window, 
-                            &widget->style->base[GTK_STATE_NORMAL]);
-}
-
-static void
-gtk_list_draw (GtkWidget    *widget,
-              GdkRectangle *area)
-{
-  GtkList *list;
-  GtkWidget *child;
-  GdkRectangle child_area;
-  GList *children;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_LIST (widget));
-  g_return_if_fail (area != NULL);
-
-  if (GTK_WIDGET_DRAWABLE (widget))
-    {
-      list = GTK_LIST (widget);
-
-      children = list->children;
-      while (children)
-       {
-         child = children->data;
-         children = children->next;
-
-         if (gtk_widget_intersect (child, area, &child_area))
-           gtk_widget_draw (child, &child_area);
-       }
-    }
-}
-
-static gint
-gtk_list_expose (GtkWidget     *widget,
-                GdkEventExpose *event)
-{
-  GtkList *list;
-  GtkWidget *child;
-  GdkEventExpose child_event;
-  GList *children;
-
-  g_return_val_if_fail (widget != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
-
-  if (GTK_WIDGET_DRAWABLE (widget))
-    {
-      list = GTK_LIST (widget);
-
-      child_event = *event;
-
-      children = list->children;
-      while (children)
-       {
-         child = children->data;
-         children = children->next;
+      gtk_list_end_drag_selection (list);
 
-         if (GTK_WIDGET_NO_WINDOW (child) &&
-             gtk_widget_intersect (child, &event->area, &child_event.area))
-           gtk_widget_event (child, (GdkEvent*) &child_event);
-       }
+      if (list->anchor != -1 && list->selection_mode == GTK_SELECTION_MULTIPLE)
+       gtk_list_end_selection (list);
     }
 
-  return FALSE;
-}
-
-static void
-move_horizontal (GtkList       *list,
-                GtkAdjustment *adj,
-                gint           diff)
-{
-  gfloat upper;
-
-  adj->value += diff;
-
-  upper = adj->upper - adj->page_size;
-  adj->value = MIN (adj->value, upper);
-  adj->value = MAX (adj->value, 0.0);
-
-  gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
-}
-
-static gint
-horizontal_timeout (GtkWidget *list)
-{
-  gint x, y;
-  GdkEventMotion event;
-  GdkModifierType mask;
-
-  g_return_val_if_fail (GTK_IS_LIST (list), FALSE);
-
-  GTK_LIST (list)->htimer = 0;
-  gdk_window_get_pointer (list->window, &x, &y, &mask);
-
-  event.is_hint = 0;
-  event.x = x;
-  event.y = y;
-  event.state = mask;
-
-  gtk_list_motion_notify (list, &event);
-
-  return FALSE;
-}
-
-static gint
-vertical_timeout (GtkWidget *list)
-{
-  gint x;
-  gint y;
-  GdkEventMotion event;
-  GdkModifierType mask;
-
-  g_return_val_if_fail (GTK_IS_LIST (list), FALSE);
-
-  GTK_LIST (list)->vtimer = 0;
-  gdk_window_get_pointer (list->window, &x, &y, &mask);
-
-  event.is_hint = 0;
-  event.x = x;
-  event.y = y;
-  event.state = mask;
-
-  gtk_list_motion_notify (list, &event);
-
-  return FALSE;
+  gdk_window_hide (widget->window);
 }
 
 static gint
 gtk_list_motion_notify (GtkWidget      *widget,
                        GdkEventMotion *event)
 {
-  GtkList *list;
+  GtkList *list = GTK_LIST (widget);
   GtkWidget *item = NULL;
   GtkAdjustment *adj;
   GtkContainer *container;
@@ -890,12 +518,6 @@ gtk_list_motion_notify (GtkWidget      *widget,
   gint focus_row = 0;
   gint length = 0;
 
-  g_return_val_if_fail (widget != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
-
-  list = GTK_LIST (widget);
-
   if (!list->drag_selection || !list->children)
     return FALSE;
 
@@ -903,6 +525,11 @@ gtk_list_motion_notify (GtkWidget      *widget,
 
   if (event->is_hint || event->window != widget->window)
     gdk_window_get_pointer (widget->window, &x, &y, NULL);
+  else
+    {
+      x = event->x;
+      y = event->y;
+    }
 
   adj = gtk_object_get_data_by_id (GTK_OBJECT (list), hadjustment_key_id);
 
@@ -912,18 +539,23 @@ gtk_list_motion_notify (GtkWidget      *widget,
     {
       if (list->htimer == 0)
        {
-         list->htimer = gtk_timeout_add
-           (SCROLL_TIME, (GtkFunction) horizontal_timeout, widget);
+         list->htimer = gdk_threads_add_timeout
+           (SCROLL_TIME, (GSourceFunc) gtk_list_horizontal_timeout, widget);
          
          if (!((x < adj->value && adj->value <= 0) ||
                (x > adj->value + adj->page_size &&
                 adj->value >= adj->upper - adj->page_size)))
            {
+             gdouble value;
+
              if (x < adj->value)
-               move_horizontal (list, adj, - 1 + (x - adj->value) / 2 );
+               value = adj->value + (x - adj->value) / 2 - 1;
              else
-               move_horizontal (list, adj,
-                                1 + (x - adj->value - adj->page_size) / 2);
+               value = adj->value + 1 + (x - adj->value - adj->page_size) / 2;
+
+             gtk_adjustment_set_value (adj,
+                                       CLAMP (value, 0.0,
+                                              adj->upper - adj->page_size));
            }
        }
       else
@@ -955,22 +587,21 @@ gtk_list_motion_notify (GtkWidget      *widget,
 
   if (!((y < 0 && focus_row == 0) ||
        (y > widget->allocation.height && focus_row >= length - 1)))
-    list->vtimer = gtk_timeout_add (SCROLL_TIME,
-                                   (GtkFunction) vertical_timeout, list);
-         
+    list->vtimer = gdk_threads_add_timeout (SCROLL_TIME,
+                                 (GSourceFunc) gtk_list_vertical_timeout,
+                                 list);
+
   if (row != focus_row)
     gtk_widget_grab_focus (item);
-         
+
   switch (list->selection_mode)
     {
     case GTK_SELECTION_BROWSE:
       gtk_list_select_child (list, item);
       break;
-      
-    case GTK_SELECTION_EXTENDED:
+    case GTK_SELECTION_MULTIPLE:
       gtk_list_update_extended_selection (list, row);
       break;
-
     default:
       break;
     }
@@ -982,17 +613,12 @@ static gint
 gtk_list_button_press (GtkWidget      *widget,
                       GdkEventButton *event)
 {
-  GtkList *list;
+  GtkList *list = GTK_LIST (widget);
   GtkWidget *item;
 
-  g_return_val_if_fail (widget != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
-
   if (event->button != 1)
     return FALSE;
 
-  list = GTK_LIST (widget);
   item = gtk_get_event_widget ((GdkEvent*) event);
 
   while (item && !GTK_IS_LIST_ITEM (item))
@@ -1005,18 +631,13 @@ gtk_list_button_press (GtkWidget      *widget,
 
       if (event->type == GDK_BUTTON_PRESS)
        {
-         list->drag_selection = TRUE;
-         gdk_pointer_grab (widget->window, TRUE,
-                           GDK_POINTER_MOTION_HINT_MASK |
-                           GDK_BUTTON1_MOTION_MASK |
-                           GDK_BUTTON_RELEASE_MASK,
-                           NULL, NULL, event->time);
          gtk_grab_add (widget);
+         list->drag_selection = TRUE;
        }
-      else if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
+      else if (list_has_grab (list))
        gtk_list_end_drag_selection (list);
          
-      if (!GTK_WIDGET_HAS_FOCUS(item))
+      if (!gtk_widget_has_focus(item))
        gtk_widget_grab_focus (item);
 
       if (list->add_mode)
@@ -1028,7 +649,6 @@ gtk_list_button_press (GtkWidget      *widget,
       switch (list->selection_mode)
        {
        case GTK_SELECTION_SINGLE:
-       case GTK_SELECTION_MULTIPLE:
          if (event->type != GDK_BUTTON_PRESS)
            gtk_list_select_child (list, item);
          else
@@ -1038,7 +658,7 @@ gtk_list_button_press (GtkWidget      *widget,
        case GTK_SELECTION_BROWSE:
          break;
 
-       case GTK_SELECTION_EXTENDED:
+       case GTK_SELECTION_MULTIPLE:
          focus_row = g_list_index (list->children, item);
 
          if (list->last_focus_child)
@@ -1107,6 +727,8 @@ gtk_list_button_press (GtkWidget      *widget,
        default:
          break;
        }
+
+      return TRUE;
     }
 
   return FALSE;
@@ -1116,731 +738,1208 @@ static gint
 gtk_list_button_release (GtkWidget     *widget,
                         GdkEventButton *event)
 {
-  GtkList *list;
-  GtkWidget *item;
+  GtkList *list = GTK_LIST (widget);
+  GtkWidget *item;
+
+  /* we don't handle button 2 and 3 */
+  if (event->button != 1)
+    return FALSE;
+
+  if (list->drag_selection)
+    {
+      gtk_list_end_drag_selection (list);
+
+      switch (list->selection_mode)
+       {
+       case GTK_SELECTION_MULTIPLE:
+         if (!(event->state & GDK_SHIFT_MASK))
+           gtk_list_end_selection (list);
+         break;
+
+       case GTK_SELECTION_SINGLE:
+
+         item = gtk_get_event_widget ((GdkEvent*) event);
+  
+         while (item && !GTK_IS_LIST_ITEM (item))
+           item = item->parent;
+         
+         if (item && item->parent == widget)
+           {
+             if (list->undo_focus_child == item)
+               gtk_list_toggle_row (list, item);
+           }
+         list->undo_focus_child = NULL;
+         break;
+
+       default:
+         break;
+       }
+
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static void 
+gtk_list_style_set     (GtkWidget      *widget,
+                        GtkStyle       *previous_style)
+{
+  if (previous_style && GTK_WIDGET_REALIZED (widget))
+    gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
+}
+
+/* GtkContainer Methods :
+ * gtk_list_add
+ * gtk_list_remove
+ * gtk_list_forall
+ * gtk_list_child_type
+ * gtk_list_set_focus_child
+ * gtk_list_focus
+ */
+static void
+gtk_list_add (GtkContainer *container,
+             GtkWidget    *widget)
+{
+  GList *item_list;
+
+  g_return_if_fail (GTK_IS_LIST_ITEM (widget));
+
+  item_list = g_list_alloc ();
+  item_list->data = widget;
+  
+  gtk_list_append_items (GTK_LIST (container), item_list);
+}
+
+static void
+gtk_list_remove (GtkContainer *container,
+                GtkWidget    *widget)
+{
+  GList *item_list;
+
+  g_return_if_fail (container == GTK_CONTAINER (widget->parent));
+  
+  item_list = g_list_alloc ();
+  item_list->data = widget;
+  
+  gtk_list_remove_items (GTK_LIST (container), item_list);
+  
+  g_list_free (item_list);
+}
+
+static void
+gtk_list_forall (GtkContainer  *container,
+                gboolean       include_internals,
+                GtkCallback    callback,
+                gpointer       callback_data)
+{
+  GtkList *list = GTK_LIST (container);
+  GtkWidget *child;
+  GList *children;
+
+  children = list->children;
+
+  while (children)
+    {
+      child = children->data;
+      children = children->next;
+
+      (* callback) (child, callback_data);
+    }
+}
+
+static GtkType
+gtk_list_child_type (GtkContainer *container)
+{
+  return GTK_TYPE_LIST_ITEM;
+}
+
+static void
+gtk_list_set_focus_child (GtkContainer *container,
+                         GtkWidget    *child)
+{
+  GtkList *list;
+
+  g_return_if_fail (GTK_IS_LIST (container));
+  if (child)
+    g_return_if_fail (GTK_IS_WIDGET (child));
+
+  list = GTK_LIST (container);
+
+  if (child != container->focus_child)
+    {
+      if (container->focus_child)
+       {
+         list->last_focus_child = container->focus_child;
+         g_object_unref (container->focus_child);
+       }
+      container->focus_child = child;
+      if (container->focus_child)
+        g_object_ref (container->focus_child);
+    }
+
+  /* check for v adjustment */
+  if (container->focus_child)
+    {
+      GtkAdjustment *adjustment;
+
+      adjustment = gtk_object_get_data_by_id (GTK_OBJECT (container),
+                                             vadjustment_key_id);
+      if (adjustment)
+        gtk_adjustment_clamp_page (adjustment,
+                                   container->focus_child->allocation.y,
+                                   (container->focus_child->allocation.y +
+                                    container->focus_child->allocation.height));
+      switch (list->selection_mode)
+       {
+       case GTK_SELECTION_BROWSE:
+         gtk_list_select_child (list, child);
+         break;
+       case GTK_SELECTION_MULTIPLE:
+         if (!list->last_focus_child && !list->add_mode)
+           {
+             list->undo_focus_child = list->last_focus_child;
+             gtk_list_unselect_all (list);
+             gtk_list_select_child (list, child);
+           }
+         break;
+       default:
+         break;
+       }
+    }
+}
+
+static gboolean
+gtk_list_focus (GtkWidget        *widget,
+               GtkDirectionType  direction)
+{
+  gint return_val = FALSE;
+  GtkContainer *container;
+
+  container = GTK_CONTAINER (widget);
+  
+  if (container->focus_child == NULL ||
+      !gtk_widget_has_focus (container->focus_child))
+    {
+      if (GTK_LIST (container)->last_focus_child)
+       gtk_container_set_focus_child
+         (container, GTK_LIST (container)->last_focus_child);
+
+      if (GTK_WIDGET_CLASS (parent_class)->focus)
+       return_val = GTK_WIDGET_CLASS (parent_class)->focus (widget,
+                                                             direction);
+    }
+
+  if (!return_val)
+    {
+      GtkList *list;
+
+      list =  GTK_LIST (container);
+      if (list->selection_mode == GTK_SELECTION_MULTIPLE && list->anchor >= 0)
+       gtk_list_end_selection (list);
+
+      if (container->focus_child)
+       list->last_focus_child = container->focus_child;
+    }
+
+  return return_val;
+}
+
+
+/* Public GtkList Methods :
+ *
+ * gtk_list_insert_items
+ * gtk_list_append_items
+ * gtk_list_prepend_items
+ * gtk_list_remove_items
+ * gtk_list_remove_items_no_unref
+ * gtk_list_clear_items
+ *
+ * gtk_list_child_position
+ */
+void
+gtk_list_insert_items (GtkList *list,
+                      GList   *items,
+                      gint     position)
+{
+  GtkWidget *widget;
+  GList *tmp_list;
+  GList *last;
+  gint nchildren;
 
-  g_return_val_if_fail (widget != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
+  g_return_if_fail (GTK_IS_LIST (list));
 
-  list = GTK_LIST (widget);
+  if (!items)
+    return;
 
-  /* we don't handle button 2 and 3 */
-  if (event->button != 1)
-    return FALSE;
+  gtk_list_end_drag_selection (list);
+  if (list->selection_mode == GTK_SELECTION_MULTIPLE && list->anchor >= 0)
+    gtk_list_end_selection (list);
 
-  if (list->drag_selection)
+  tmp_list = items;
+  while (tmp_list)
     {
-      gtk_list_end_drag_selection (list);
+      widget = tmp_list->data;
+      tmp_list = tmp_list->next;
 
-      switch (list->selection_mode)
-       {
-       case GTK_SELECTION_EXTENDED:
-         if (!(event->state & GDK_SHIFT_MASK))
-           gtk_list_end_selection (list);
-         break;
+      gtk_widget_set_parent (widget, GTK_WIDGET (list));
+      gtk_signal_connect (GTK_OBJECT (widget), "drag-begin",
+                         G_CALLBACK (gtk_list_signal_drag_begin),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "toggle-focus-row",
+                         G_CALLBACK (gtk_list_signal_toggle_focus_row),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "select-all",
+                         G_CALLBACK (gtk_list_signal_select_all),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "unselect-all",
+                         G_CALLBACK (gtk_list_signal_unselect_all),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "undo-selection",
+                         G_CALLBACK (gtk_list_signal_undo_selection),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "start-selection",
+                         G_CALLBACK (gtk_list_signal_start_selection),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "end-selection",
+                         G_CALLBACK (gtk_list_signal_end_selection),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "extend-selection",
+                         G_CALLBACK (gtk_list_signal_extend_selection),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "scroll-horizontal",
+                         G_CALLBACK (gtk_list_signal_scroll_horizontal),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "scroll-vertical",
+                         G_CALLBACK (gtk_list_signal_scroll_vertical),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "toggle-add-mode",
+                         G_CALLBACK (gtk_list_signal_toggle_add_mode),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "select",
+                         G_CALLBACK (gtk_list_signal_item_select),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "deselect",
+                         G_CALLBACK (gtk_list_signal_item_deselect),
+                         list);
+      gtk_signal_connect (GTK_OBJECT (widget), "toggle",
+                         G_CALLBACK (gtk_list_signal_item_toggle),
+                         list);
+    }
 
-       case GTK_SELECTION_SINGLE:
-       case GTK_SELECTION_MULTIPLE:
 
-         item = gtk_get_event_widget ((GdkEvent*) event);
-  
-         while (item && !GTK_IS_LIST_ITEM (item))
-           item = item->parent;
-         
-         if (item && item->parent == widget)
-           {
-             if (list->undo_focus_child == item)
-               gtk_list_toggle_row (list, item);
-           }
-         list->undo_focus_child = NULL;
-         break;
+  nchildren = g_list_length (list->children);
+  if ((position < 0) || (position > nchildren))
+    position = nchildren;
 
-       default:
-         break;
+  if (position == nchildren)
+    {
+      if (list->children)
+       {
+         tmp_list = g_list_last (list->children);
+         tmp_list->next = items;
+         items->prev = tmp_list;
+       }
+      else
+       {
+         list->children = items;
        }
     }
+  else
+    {
+      tmp_list = g_list_nth (list->children, position);
+      last = g_list_last (items);
+
+      if (tmp_list->prev)
+       tmp_list->prev->next = items;
+      last->next = tmp_list;
+      items->prev = tmp_list->prev;
+      tmp_list->prev = last;
+
+      if (tmp_list == list->children)
+       list->children = items;
+    }
   
-  return FALSE;
+  if (list->children && !list->selection &&
+      (list->selection_mode == GTK_SELECTION_BROWSE))
+    {
+      widget = list->children->data;
+      gtk_list_select_child (list, widget);
+    }
 }
 
-static void
-gtk_list_size_request (GtkWidget      *widget,
-                      GtkRequisition *requisition)
+void
+gtk_list_append_items (GtkList *list,
+                      GList   *items)
 {
-  GtkList *list;
-  GtkWidget *child;
-  GList *children;
-
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_LIST (widget));
-  g_return_if_fail (requisition != NULL);
-
-  list = GTK_LIST (widget);
-  requisition->width = 0;
-  requisition->height = 0;
+  g_return_if_fail (GTK_IS_LIST (list));
 
-  children = list->children;
-  while (children)
-    {
-      child = children->data;
-      children = children->next;
+  gtk_list_insert_items (list, items, -1);
+}
 
-      if (GTK_WIDGET_VISIBLE (child))
-       {
-         gtk_widget_size_request (child, &child->requisition);
+void
+gtk_list_prepend_items (GtkList *list,
+                       GList   *items)
+{
+  g_return_if_fail (GTK_IS_LIST (list));
 
-         requisition->width = MAX (requisition->width, child->requisition.width);
-         requisition->height += child->requisition.height;
-       }
-    }
+  gtk_list_insert_items (list, items, 0);
+}
 
-  requisition->width += GTK_CONTAINER (list)->border_width * 2;
-  requisition->height += GTK_CONTAINER (list)->border_width * 2;
+void
+gtk_list_remove_items (GtkList *list,
+                      GList    *items)
+{
+  gtk_list_remove_items_internal (list, items, FALSE);
+}
 
-  requisition->width = MAX (requisition->width, 1);
-  requisition->height = MAX (requisition->height, 1);
+void
+gtk_list_remove_items_no_unref (GtkList         *list,
+                               GList    *items)
+{
+  gtk_list_remove_items_internal (list, items, TRUE);
 }
 
-static void
-gtk_list_size_allocate (GtkWidget     *widget,
-                       GtkAllocation *allocation)
+void
+gtk_list_clear_items (GtkList *list,
+                     gint     start,
+                     gint     end)
 {
-  GtkList *list;
-  GtkWidget *child;
-  GtkAllocation child_allocation;
-  GList *children;
+  GtkContainer *container;
+  GtkWidget *widget;
+  GtkWidget *new_focus_child = NULL;
+  GList *start_list;
+  GList *end_list;
+  GList *tmp_list;
+  guint nchildren;
+  gboolean grab_focus = FALSE;
 
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_LIST (widget));
-  g_return_if_fail (allocation != NULL);
+  g_return_if_fail (GTK_IS_LIST (list));
 
-  list = GTK_LIST (widget);
+  nchildren = g_list_length (list->children);
 
-  widget->allocation = *allocation;
-  if (GTK_WIDGET_REALIZED (widget))
-    gdk_window_move_resize (widget->window,
-                           allocation->x, allocation->y,
-                           allocation->width, allocation->height);
+  if (nchildren == 0)
+    return;
 
-  if (list->children)
+  if ((end < 0) || (end > nchildren))
+    end = nchildren;
+
+  if (start >= end)
+    return;
+
+  container = GTK_CONTAINER (list);
+
+  gtk_list_end_drag_selection (list);
+  if (list->selection_mode == GTK_SELECTION_MULTIPLE)
     {
-      child_allocation.x = GTK_CONTAINER (list)->border_width;
-      child_allocation.y = GTK_CONTAINER (list)->border_width;
-      child_allocation.width = MAX (1, allocation->width - child_allocation.x * 2);
+      if (list->anchor >= 0)
+       gtk_list_end_selection (list);
 
-      children = list->children;
+      gtk_list_reset_extended_selection (list);
+    }
 
-      while (children)
-       {
-         child = children->data;
-         children = children->next;
+  start_list = g_list_nth (list->children, start);
+  end_list = g_list_nth (list->children, end);
 
-         if (GTK_WIDGET_VISIBLE (child))
-           {
-             child_allocation.height = child->requisition.height;
+  if (start_list->prev)
+    start_list->prev->next = end_list;
+  if (end_list && end_list->prev)
+    end_list->prev->next = NULL;
+  if (end_list)
+    end_list->prev = start_list->prev;
+  if (start_list == list->children)
+    list->children = end_list;
 
-             gtk_widget_size_allocate (child, &child_allocation);
+  if (container->focus_child)
+    {
+      if (g_list_find (start_list, container->focus_child))
+       {
+         if (start_list->prev)
+           new_focus_child = start_list->prev->data;
+         else if (list->children)
+           new_focus_child = list->children->data;
 
-             child_allocation.y += child_allocation.height;
-           }
+         if (gtk_widget_has_focus (container->focus_child))
+           grab_focus = TRUE;
        }
     }
-}
 
-static void
-gtk_list_add (GtkContainer *container,
-             GtkWidget    *widget)
-{
-  GList *item_list;
+  tmp_list = start_list;
+  while (tmp_list)
+    {
+      widget = tmp_list->data;
+      tmp_list = tmp_list->next;
 
-  g_return_if_fail (container != NULL);
-  g_return_if_fail (GTK_IS_LIST (container));
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_LIST_ITEM (widget));
+      g_object_ref (widget);
 
-  item_list = g_list_alloc ();
-  item_list->data = widget;
-  
-  gtk_list_append_items (GTK_LIST (container), item_list);
-}
+      if (widget->state == GTK_STATE_SELECTED)
+       gtk_list_unselect_child (list, widget);
 
-static void
-gtk_list_remove (GtkContainer *container,
-                GtkWidget    *widget)
-{
-  GList *item_list;
-  
-  g_return_if_fail (container != NULL);
-  g_return_if_fail (GTK_IS_LIST (container));
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (container == GTK_CONTAINER (widget->parent));
-  
-  
-  item_list = g_list_alloc ();
-  item_list->data = widget;
-  
-  gtk_list_remove_items (GTK_LIST (container), item_list);
-  
-  g_list_free (item_list);
+      gtk_signal_disconnect_by_data (GTK_OBJECT (widget), (gpointer) list);
+      gtk_widget_unparent (widget);
+      
+      if (widget == list->undo_focus_child)
+       list->undo_focus_child = NULL;
+      if (widget == list->last_focus_child)
+       list->last_focus_child = NULL;
+
+      g_object_unref (widget);
+    }
+
+  g_list_free (start_list);
+
+  if (new_focus_child)
+    {
+      if (grab_focus)
+       gtk_widget_grab_focus (new_focus_child);
+      else if (container->focus_child)
+       gtk_container_set_focus_child (container, new_focus_child);
+
+      if ((list->selection_mode == GTK_SELECTION_BROWSE ||
+          list->selection_mode == GTK_SELECTION_MULTIPLE) && !list->selection)
+       {
+         list->last_focus_child = new_focus_child; 
+         gtk_list_select_child (list, new_focus_child);
+       }
+    }
+
+  if (gtk_widget_get_visible (GTK_WIDGET (list)))
+    gtk_widget_queue_resize (GTK_WIDGET (list));
 }
 
-static void
-gtk_list_forall (GtkContainer  *container,
-                gboolean       include_internals,
-                GtkCallback    callback,
-                gpointer       callback_data)
+gint
+gtk_list_child_position (GtkList   *list,
+                        GtkWidget *child)
 {
-  GtkList *list;
-  GtkWidget *child;
   GList *children;
+  gint pos;
 
-  g_return_if_fail (container != NULL);
-  g_return_if_fail (GTK_IS_LIST (container));
-  g_return_if_fail (callback != NULL);
+  g_return_val_if_fail (GTK_IS_LIST (list), -1);
+  g_return_val_if_fail (child != NULL, -1);
 
-  list = GTK_LIST (container);
+  pos = 0;
   children = list->children;
 
   while (children)
     {
-      child = children->data;
+      if (child == GTK_WIDGET (children->data))
+       return pos;
+
+      pos += 1;
       children = children->next;
-
-      (* callback) (child, callback_data);
     }
+
+  return -1;
 }
 
+
+/* Private GtkList Insert/Remove Item Functions:
+ *
+ * gtk_list_remove_items_internal
+ */
 static void
-gtk_real_list_select_child (GtkList   *list,
-                           GtkWidget *child)
+gtk_list_remove_items_internal (GtkList         *list,
+                               GList    *items,
+                               gboolean  no_unref)
 {
-  GList *selection;
+  GtkWidget *widget;
+  GtkWidget *new_focus_child;
+  GtkWidget *old_focus_child;
+  GtkContainer *container;
   GList *tmp_list;
-  GtkWidget *tmp_item;
-
-  g_return_if_fail (list != NULL);
+  GList *work;
+  gboolean grab_focus = FALSE;
+  
   g_return_if_fail (GTK_IS_LIST (list));
-  g_return_if_fail (child != NULL);
-  g_return_if_fail (GTK_IS_LIST_ITEM (child));
 
-  switch (list->selection_mode)
+  if (!items)
+    return;
+  
+  container = GTK_CONTAINER (list);
+
+  gtk_list_end_drag_selection (list);
+  if (list->selection_mode == GTK_SELECTION_MULTIPLE)
     {
-    case GTK_SELECTION_SINGLE:
-    case GTK_SELECTION_BROWSE:
-      selection = list->selection;
+      if (list->anchor >= 0)
+       gtk_list_end_selection (list);
 
-      while (selection)
-       {
-         tmp_item = selection->data;
+      gtk_list_reset_extended_selection (list);
+    }
 
-         if (tmp_item != child)
-           {
-             tmp_list = selection;
-             selection = selection->next;
+  tmp_list = items;
+  while (tmp_list)
+    {
+      widget = tmp_list->data;
+      tmp_list = tmp_list->next;
+      
+      if (widget->state == GTK_STATE_SELECTED)
+       gtk_list_unselect_child (list, widget);
+    }
+
+  if (container->focus_child)
+    {
+      old_focus_child = new_focus_child = container->focus_child;
+      if (gtk_widget_has_focus (container->focus_child))
+       grab_focus = TRUE;
+    }
+  else
+    old_focus_child = new_focus_child = list->last_focus_child;
 
-             list->selection = g_list_remove_link (list->selection, tmp_list);
-             g_list_free (tmp_list);
+  tmp_list = items;
+  while (tmp_list)
+    {
+      widget = tmp_list->data;
+      tmp_list = tmp_list->next;
 
-             gtk_list_item_deselect (GTK_LIST_ITEM (tmp_item));
-             gtk_widget_unref (GTK_WIDGET (tmp_item));
-           }
-         else
-           selection = selection->next;
-       }
+      g_object_ref (widget);
+      if (no_unref)
+       g_object_ref (widget);
 
-      if (child->state == GTK_STATE_NORMAL)
+      if (widget == new_focus_child) 
        {
-         list->selection = g_list_prepend (list->selection, child);
-         gtk_widget_ref (child);
-         gtk_list_item_select (GTK_LIST_ITEM (child));
+         work = g_list_find (list->children, widget);
+
+         if (work)
+           {
+             if (work->next)
+               new_focus_child = work->next->data;
+             else if (list->children != work && work->prev)
+               new_focus_child = work->prev->data;
+             else
+               new_focus_child = NULL;
+           }
        }
-      gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
-      break;
 
-    case GTK_SELECTION_EXTENDED:
-      if (list->anchor >= 0)
-       return;
+      gtk_signal_disconnect_by_data (GTK_OBJECT (widget), (gpointer) list);
+      list->children = g_list_remove (list->children, widget);
+      gtk_widget_unparent (widget);
 
-    case GTK_SELECTION_MULTIPLE:
-      if (child->state == GTK_STATE_NORMAL)
+      if (widget == list->undo_focus_child)
+       list->undo_focus_child = NULL;
+      if (widget == list->last_focus_child)
+       list->last_focus_child = NULL;
+
+      g_object_unref (widget);
+    }
+  
+  if (new_focus_child && new_focus_child != old_focus_child)
+    {
+      if (grab_focus)
+       gtk_widget_grab_focus (new_focus_child);
+      else if (container->focus_child)
+       gtk_container_set_focus_child (container, new_focus_child);
+
+      if (list->selection_mode == GTK_SELECTION_BROWSE && !list->selection)
        {
-         list->selection = g_list_prepend (list->selection, child);
-         gtk_widget_ref (child);
-         gtk_list_item_select (GTK_LIST_ITEM (child));
-         gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
+         list->last_focus_child = new_focus_child; 
+         gtk_list_select_child (list, new_focus_child);
        }
-      break;
     }
+
+  if (gtk_widget_get_visible (GTK_WIDGET (list)))
+    gtk_widget_queue_resize (GTK_WIDGET (list));
 }
 
-static void
-gtk_real_list_unselect_child (GtkList  *list,
-                             GtkWidget *child)
+
+/* Public GtkList Selection Methods :
+ *
+ * gtk_list_set_selection_mode
+ * gtk_list_select_item
+ * gtk_list_unselect_item
+ * gtk_list_select_child
+ * gtk_list_unselect_child
+ * gtk_list_select_all
+ * gtk_list_unselect_all
+ * gtk_list_extend_selection
+ * gtk_list_end_drag_selection
+ * gtk_list_start_selection
+ * gtk_list_end_selection
+ * gtk_list_toggle_row
+ * gtk_list_toggle_focus_row
+ * gtk_list_toggle_add_mode
+ * gtk_list_undo_selection
+ */
+void
+gtk_list_set_selection_mode (GtkList         *list,
+                            GtkSelectionMode  mode)
 {
-  g_return_if_fail (list != NULL);
   g_return_if_fail (GTK_IS_LIST (list));
-  g_return_if_fail (child != NULL);
-  g_return_if_fail (GTK_IS_LIST_ITEM (child));
 
-  if (child->state == GTK_STATE_SELECTED)
+  if (list->selection_mode == mode)
+    return;
+
+  list->selection_mode = mode;
+
+  switch (mode)
     {
-      list->selection = g_list_remove (list->selection, child);
-      gtk_list_item_deselect (GTK_LIST_ITEM (child));
-      gtk_widget_unref (child);
-      gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
+    case GTK_SELECTION_SINGLE:
+    case GTK_SELECTION_BROWSE:
+      gtk_list_unselect_all (list);
+      break;
+    default:
+      break;
     }
 }
 
-
-/***************************************************************************/
-
-static void
-gtk_list_set_anchor (GtkList   *list,
-                    gboolean   add_mode,
-                    gint       anchor,
-                    GtkWidget *undo_focus_child)
+void
+gtk_list_select_item (GtkList *list,
+                     gint     item)
 {
-  GList *work;
+  GList *tmp_list;
 
-  g_return_if_fail (list != NULL);
   g_return_if_fail (GTK_IS_LIST (list));
-  
-  if (list->selection_mode != GTK_SELECTION_EXTENDED || list->anchor >= 0)
-    return;
-
-  g_list_free (list->undo_selection);
-  g_list_free (list->undo_unselection);
-  list->undo_selection = NULL;
-  list->undo_unselection = NULL;
-
-  if ((work = g_list_nth (list->children, anchor)))
-    {
-      if (add_mode)
-       gtk_list_fake_toggle_row (list, GTK_WIDGET (work->data));
-      else
-       {
-         gtk_list_fake_unselect_all (list, GTK_WIDGET (work->data));
-         list->anchor_state = GTK_STATE_SELECTED;
-       }
-    }
 
-  list->anchor = anchor;
-  list->drag_pos = anchor;
-  list->undo_focus_child = undo_focus_child;
+  tmp_list = g_list_nth (list->children, item);
+  if (tmp_list)
+    gtk_list_select_child (list, GTK_WIDGET (tmp_list->data));
 }
 
-static void
-gtk_list_fake_unselect_all (GtkList   *list,
-                           GtkWidget *item)
+void
+gtk_list_unselect_item (GtkList *list,
+                       gint     item)
 {
-  GList *work;
+  GList *tmp_list;
 
-  if (item && item->state == GTK_STATE_NORMAL)
-    gtk_widget_set_state (item, GTK_STATE_SELECTED);
+  g_return_if_fail (GTK_IS_LIST (list));
 
-  list->undo_selection = list->selection;
-  list->selection = NULL;
-  
-  for (work = list->undo_selection; work; work = work->next)
-    if (work->data != item)
-      gtk_widget_set_state (GTK_WIDGET (work->data), GTK_STATE_NORMAL);
+  tmp_list = g_list_nth (list->children, item);
+  if (tmp_list)
+    gtk_list_unselect_child (list, GTK_WIDGET (tmp_list->data));
 }
 
-static void
-gtk_list_fake_toggle_row (GtkList   *list,
-                         GtkWidget *item)
+void
+gtk_list_select_child (GtkList  *list,
+                      GtkWidget *child)
 {
-  if (!item)
-    return;
-  
-  if (item->state == GTK_STATE_NORMAL)
-    {
-      list->anchor_state = GTK_STATE_SELECTED;
-      gtk_widget_set_state (item, GTK_STATE_SELECTED);
-    }
-  else
-    {
-      list->anchor_state = GTK_STATE_NORMAL;
-      gtk_widget_set_state (item, GTK_STATE_NORMAL);
-    }
+  gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECT_CHILD], child);
 }
 
 void
-gtk_list_scroll_horizontal (GtkList       *list,
-                           GtkScrollType  scroll_type,
-                           gfloat         position)
+gtk_list_unselect_child (GtkList   *list,
+                        GtkWidget *child)
 {
-  GtkAdjustment *adj;
+  gtk_signal_emit (GTK_OBJECT (list), list_signals[UNSELECT_CHILD], child);
+}
 
-  g_return_if_fail (list != 0);
+void
+gtk_list_select_all (GtkList *list)
+{
+  GtkContainer *container;
   g_return_if_fail (GTK_IS_LIST (list));
 
-  if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
+  if (!list->children)
     return;
+  
+  if (list_has_grab (list))
+    gtk_list_end_drag_selection (list);
 
-  if (!(adj =
-       gtk_object_get_data_by_id (GTK_OBJECT (list), hadjustment_key_id)))
-    return;
+  if (list->selection_mode == GTK_SELECTION_MULTIPLE && list->anchor >= 0)
+    gtk_list_end_selection (list);
 
-  switch (scroll_type)
+  container = GTK_CONTAINER (list);
+
+  switch (list->selection_mode)
     {
-    case GTK_SCROLL_STEP_BACKWARD:
-      adj->value = CLAMP (adj->value - adj->step_increment, adj->lower,
-                         adj->upper - adj->page_size);
-      break;
-    case GTK_SCROLL_STEP_FORWARD:
-      adj->value = CLAMP (adj->value + adj->step_increment, adj->lower,
-                         adj->upper - adj->page_size);
-      break;
-    case GTK_SCROLL_PAGE_BACKWARD:
-      adj->value = CLAMP (adj->value - adj->page_increment, adj->lower,
-                         adj->upper - adj->page_size);
-      break;
-    case GTK_SCROLL_PAGE_FORWARD:
-      adj->value = CLAMP (adj->value + adj->page_increment, adj->lower,
-                         adj->upper - adj->page_size);
-      break;
-    case GTK_SCROLL_JUMP:
-      adj->value = CLAMP (adj->lower + (adj->upper - adj->lower) * position,
-                         adj->lower, adj->upper - adj->page_size);
+    case GTK_SELECTION_BROWSE:
+      if (container->focus_child)
+       {
+         gtk_list_select_child (list, container->focus_child);
+         return;
+       }
       break;
+    case GTK_SELECTION_MULTIPLE:
+      g_list_free (list->undo_selection);
+      g_list_free (list->undo_unselection);
+      list->undo_selection = NULL;
+      list->undo_unselection = NULL;
+
+      if (list->children &&
+         GTK_WIDGET_STATE (list->children->data) != GTK_STATE_SELECTED)
+       gtk_list_fake_toggle_row (list, GTK_WIDGET (list->children->data));
+
+      list->anchor_state =  GTK_STATE_SELECTED;
+      list->anchor = 0;
+      list->drag_pos = 0;
+      list->undo_focus_child = container->focus_child;
+      gtk_list_update_extended_selection (list, g_list_length(list->children));
+      gtk_list_end_selection (list);
+      return;
     default:
       break;
     }
-  gtk_adjustment_value_changed (adj);
 }
 
 void
-gtk_list_scroll_vertical (GtkList       *list,
-                         GtkScrollType  scroll_type,
-                         gfloat         position)
+gtk_list_unselect_all (GtkList *list)
 {
-  g_return_if_fail (list != NULL);
+  GtkContainer *container;
+  GtkWidget *item;
+  GList *work;
   g_return_if_fail (GTK_IS_LIST (list));
 
-  if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
+  if (!list->children)
     return;
 
-  if (list->selection_mode == GTK_SELECTION_EXTENDED)
-    {
-      GtkContainer *container;
+  if (list_has_grab (list))
+    gtk_list_end_drag_selection (list);
 
-      if (list->anchor >= 0)
-       return;
+  if (list->selection_mode == GTK_SELECTION_MULTIPLE && list->anchor >= 0)
+    gtk_list_end_selection (list);
 
-      container = GTK_CONTAINER (list);
-      list->undo_focus_child = container->focus_child;
-      gtk_list_move_focus_child (list, scroll_type, position);
-      if (container->focus_child != list->undo_focus_child && !list->add_mode)
+  container = GTK_CONTAINER (list);
+
+  switch (list->selection_mode)
+    {
+    case GTK_SELECTION_BROWSE:
+      if (container->focus_child)
        {
-         gtk_list_unselect_all (list);
          gtk_list_select_child (list, container->focus_child);
+         return;
        }
+      break;
+    case GTK_SELECTION_MULTIPLE:
+      gtk_list_reset_extended_selection (list);
+      break;
+    default:
+      break;
+    }
+
+  work = list->selection;
+
+  while (work)
+    {
+      item = work->data;
+      work = work->next;
+      gtk_list_unselect_child (list, item);
     }
-  else
-    gtk_list_move_focus_child (list, scroll_type, position);
 }
 
-static void
-gtk_list_move_focus_child (GtkList       *list,
+void
+gtk_list_extend_selection (GtkList       *list,
                           GtkScrollType  scroll_type,
-                          gfloat         position)
+                          gfloat         position,
+                          gboolean       auto_start_selection)
 {
   GtkContainer *container;
-  GList *work;
-  GtkWidget *item;
-  GtkAdjustment *adj;
-  gint new_value;
 
-  g_return_if_fail (list != 0);
   g_return_if_fail (GTK_IS_LIST (list));
 
+  if (list_has_grab (list) ||
+      list->selection_mode != GTK_SELECTION_MULTIPLE)
+    return;
+
   container = GTK_CONTAINER (list);
 
-  if (container->focus_child)
-    work = g_list_find (list->children, container->focus_child);
-  else
-    work = list->children;
+  if (auto_start_selection)
+    {
+      gint focus_row;
 
-  if (!work)
+      focus_row = g_list_index (list->children, container->focus_child);
+      gtk_list_set_anchor (list, list->add_mode, focus_row,
+                          container->focus_child);
+    }
+  else if (list->anchor < 0)
     return;
 
-  switch (scroll_type)
-    {
-    case GTK_SCROLL_STEP_BACKWARD:
-      work = work->prev;
-      if (work)
-       gtk_widget_grab_focus (GTK_WIDGET (work->data));
-      break;
+  gtk_list_move_focus_child (list, scroll_type, position);
+  gtk_list_update_extended_selection 
+    (list, g_list_index (list->children, container->focus_child));
+}
 
-    case GTK_SCROLL_STEP_FORWARD:
-      work = work->next;
-      if (work)
-       gtk_widget_grab_focus (GTK_WIDGET (work->data));
-      break;
+void
+gtk_list_end_drag_selection (GtkList *list)
+{
+  g_return_if_fail (GTK_IS_LIST (list));
 
-    case GTK_SCROLL_PAGE_BACKWARD:
-      if (!work->prev)
-       return;
+  list->drag_selection = FALSE;
+  if (GTK_WIDGET_HAS_GRAB (list))
+    gtk_grab_remove (GTK_WIDGET (list));
 
-      item = work->data;
-      adj = gtk_object_get_data_by_id (GTK_OBJECT (list), vadjustment_key_id);
+  if (list->htimer)
+    {
+      g_source_remove (list->htimer);
+      list->htimer = 0;
+    }
+  if (list->vtimer)
+    {
+      g_source_remove (list->vtimer);
+      list->vtimer = 0;
+    }
+}
 
-      if (adj)
-       {
-         gboolean correct = FALSE;
+void
+gtk_list_start_selection (GtkList *list)
+{
+  GtkContainer *container;
+  gint focus_row;
 
-         new_value = adj->value;
+  g_return_if_fail (GTK_IS_LIST (list));
 
-         if (item->allocation.y <= adj->value)
-           {
-             new_value = MAX (item->allocation.y + item->allocation.height
-                              - adj->page_size, adj->lower);
-             correct = TRUE;
-           }
+  if (list_has_grab (list))
+    return;
 
-         if (item->allocation.y > new_value)
-           for (; work; work = work->prev)
-             {
-               item = GTK_WIDGET (work->data);
-               if (item->allocation.y <= new_value &&
-                   item->allocation.y + item->allocation.height > new_value)
-                 break;
-             }
-         else
-           for (; work; work = work->next)
-             {
-               item = GTK_WIDGET (work->data);
-               if (item->allocation.y <= new_value &&
-                   item->allocation.y + item->allocation.height > new_value)
-                 break;
-             }
+  container = GTK_CONTAINER (list);
 
-         if (correct && work && work->next && item->allocation.y < new_value)
-           item = work->next->data;
-       }
-      else
-       item = list->children->data;
-         
-      gtk_widget_grab_focus (item);
-      break;
-         
-    case GTK_SCROLL_PAGE_FORWARD:
-      if (!work->next)
-       return;
+  if ((focus_row = g_list_index (list->selection, container->focus_child))
+      >= 0)
+    gtk_list_set_anchor (list, list->add_mode,
+                        focus_row, container->focus_child);
+}
 
-      item = work->data;
-      adj = gtk_object_get_data_by_id (GTK_OBJECT (list), vadjustment_key_id);
+void
+gtk_list_end_selection (GtkList *list)
+{
+  gint i;
+  gint e;
+  gboolean top_down;
+  GList *work;
+  GtkWidget *item;
+  gint item_index;
 
-      if (adj)
-       {
-         gboolean correct = FALSE;
+  g_return_if_fail (GTK_IS_LIST (list));
 
-         new_value = adj->value;
+  if (list_has_grab (list) || list->anchor < 0)
+    return;
 
-         if (item->allocation.y + item->allocation.height >=
-             adj->value + adj->page_size)
-           {
-             new_value = item->allocation.y;
-             correct = TRUE;
-           }
+  i = MIN (list->anchor, list->drag_pos);
+  e = MAX (list->anchor, list->drag_pos);
 
-         new_value = MIN (new_value + adj->page_size, adj->upper);
+  top_down = (list->anchor < list->drag_pos);
 
-         if (item->allocation.y > new_value)
-           for (; work; work = work->prev)
-             {
-               item = GTK_WIDGET (work->data);
-               if (item->allocation.y <= new_value &&
-                   item->allocation.y + item->allocation.height > new_value)
-                 break;
-             }
-         else
-           for (; work; work = work->next)
-             {
-               item = GTK_WIDGET (work->data);
-               if (item->allocation.y <= new_value &&
-                   item->allocation.y + item->allocation.height > new_value)
-                 break;
-             }
+  list->anchor = -1;
+  list->drag_pos = -1;
+  
+  if (list->undo_selection)
+    {
+      work = list->selection;
+      list->selection = list->undo_selection;
+      list->undo_selection = work;
+      work = list->selection;
+      while (work)
+       {
+         item = work->data;
+         work = work->next;
+         item_index = g_list_index (list->children, item);
+         if (item_index < i || item_index > e)
+           {
+             gtk_widget_set_state (item, GTK_STATE_SELECTED);
+             gtk_list_unselect_child (list, item);
+             list->undo_selection = g_list_prepend (list->undo_selection,
+                                                    item);
+           }
+       }
+    }    
 
-         if (correct && work && work->prev &&
-             item->allocation.y + item->allocation.height - 1 > new_value)
-           item = work->prev->data;
+  if (top_down)
+    {
+      for (work = g_list_nth (list->children, i); i <= e;
+          i++, work = work->next)
+       {
+         item = work->data;
+         if (g_list_find (list->selection, item))
+           {
+             if (item->state == GTK_STATE_NORMAL)
+               {
+                 gtk_widget_set_state (item, GTK_STATE_SELECTED);
+                 gtk_list_unselect_child (list, item);
+                 list->undo_selection = g_list_prepend (list->undo_selection,
+                                                        item);
+               }
+           }
+         else if (item->state == GTK_STATE_SELECTED)
+           {
+             gtk_widget_set_state (item, GTK_STATE_NORMAL);
+             list->undo_unselection = g_list_prepend (list->undo_unselection,
+                                                      item);
+           }
        }
-      else
-       item = g_list_last (work)->data;
-         
-      gtk_widget_grab_focus (item);
-      break;
+    }
+  else
+    {
+      for (work = g_list_nth (list->children, e); i <= e;
+          e--, work = work->prev)
+       {
+         item = work->data;
+         if (g_list_find (list->selection, item))
+           {
+             if (item->state == GTK_STATE_NORMAL)
+               {
+                 gtk_widget_set_state (item, GTK_STATE_SELECTED);
+                 gtk_list_unselect_child (list, item);
+                 list->undo_selection = g_list_prepend (list->undo_selection,
+                                                        item);
+               }
+           }
+         else if (item->state == GTK_STATE_SELECTED)
+           {
+             gtk_widget_set_state (item, GTK_STATE_NORMAL);
+             list->undo_unselection = g_list_prepend (list->undo_unselection,
+                                                      item);
+           }
+       }
+    }
+
+  for (work = g_list_reverse (list->undo_unselection); work; work = work->next)
+    gtk_list_select_child (list, GTK_WIDGET (work->data));
 
-    case GTK_SCROLL_JUMP:
-      new_value = GTK_WIDGET(list)->allocation.height * CLAMP (position, 0, 1);
 
-      for (item = NULL, work = list->children; work; work =work->next)
-       {
-         item = GTK_WIDGET (work->data);
-         if (item->allocation.y <= new_value &&
-             item->allocation.y + item->allocation.height > new_value)
-           break;
-       }
+}
 
-      gtk_widget_grab_focus (item);
-      break;
+void
+gtk_list_toggle_row (GtkList   *list,
+                    GtkWidget *item)
+{
+  g_return_if_fail (GTK_IS_LIST (list));
+  g_return_if_fail (GTK_IS_LIST_ITEM (item));
 
-    default:
+  switch (list->selection_mode)
+    {
+    case GTK_SELECTION_MULTIPLE:
+    case GTK_SELECTION_SINGLE:
+      if (item->state == GTK_STATE_SELECTED)
+       {
+         gtk_list_unselect_child (list, item);
+         return;
+       }
+    case GTK_SELECTION_BROWSE:
+      gtk_list_select_child (list, item);
       break;
     }
 }
 
 void
-gtk_list_select_all (GtkList *list)
+gtk_list_toggle_focus_row (GtkList *list)
 {
   GtkContainer *container;
-  GList *work;
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
-
-  if (!list->children)
-    return;
-  
-  if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
-    gtk_list_end_drag_selection (list);
+  gint focus_row;
 
-  if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
-    gtk_list_end_selection (list);
+  g_return_if_fail (list != 0);
+  g_return_if_fail (GTK_IS_LIST (list));
 
   container = GTK_CONTAINER (list);
 
+  if (list_has_grab (list) || !container->focus_child)
+    return;
+
   switch (list->selection_mode)
     {
-    case GTK_SELECTION_BROWSE:
-      if (container->focus_child)
-       {
-         gtk_list_select_child (list, container->focus_child);
-         return;
-       }
+    case  GTK_SELECTION_SINGLE:
+      gtk_list_toggle_row (list, container->focus_child);
       break;
+    case GTK_SELECTION_MULTIPLE:
+      if ((focus_row = g_list_index (list->children, container->focus_child))
+         < 0)
+       return;
 
-    case GTK_SELECTION_EXTENDED:
       g_list_free (list->undo_selection);
       g_list_free (list->undo_unselection);
       list->undo_selection = NULL;
       list->undo_unselection = NULL;
 
-      if (list->children &&
-         GTK_WIDGET_STATE (list->children->data) != GTK_STATE_SELECTED)
-       gtk_list_fake_toggle_row (list, GTK_WIDGET (list->children->data));
-
-      list->anchor_state =  GTK_STATE_SELECTED;
-      list->anchor = 0;
-      list->drag_pos = 0;
+      list->anchor = focus_row;
+      list->drag_pos = focus_row;
       list->undo_focus_child = container->focus_child;
-      gtk_list_update_extended_selection (list, g_list_length(list->children));
-      gtk_list_end_selection (list);
-      return;
-
-    case GTK_SELECTION_MULTIPLE:
-      for (work = list->children; work; work = work->next)
-       {
-         if (GTK_WIDGET_STATE (work->data) == GTK_STATE_NORMAL)
-           gtk_list_select_child (list, GTK_WIDGET (work->data));
-       }
-      return;
 
+      if (list->add_mode)
+       gtk_list_fake_toggle_row (list, container->focus_child);
+      else
+       gtk_list_fake_unselect_all (list, container->focus_child);
+      
+      gtk_list_end_selection (list);
+      break;
     default:
       break;
     }
 }
 
 void
-gtk_list_unselect_all (GtkList *list)
+gtk_list_toggle_add_mode (GtkList *list)
 {
   GtkContainer *container;
-  GtkWidget *item;
+
+  g_return_if_fail (list != 0);
+  g_return_if_fail (GTK_IS_LIST (list));
+  
+  if (list_has_grab (list) ||
+      list->selection_mode != GTK_SELECTION_MULTIPLE)
+    return;
+  
+  container = GTK_CONTAINER (list);
+
+  if (list->add_mode)
+    {
+      list->add_mode = FALSE;
+      list->anchor_state = GTK_STATE_SELECTED;
+    }
+  else
+    list->add_mode = TRUE;
+  
+  if (container->focus_child)
+    gtk_widget_queue_draw (container->focus_child);
+}
+
+void
+gtk_list_undo_selection (GtkList *list)
+{
   GList *work;
-  g_return_if_fail (list != NULL);
+
   g_return_if_fail (GTK_IS_LIST (list));
 
-  if (!list->children)
+  if (list->selection_mode != GTK_SELECTION_MULTIPLE ||
+      list_has_grab (list))
     return;
   
-  if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
-    gtk_list_end_drag_selection (list);
-
-  if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
+  if (list->anchor >= 0)
     gtk_list_end_selection (list);
 
-  container = GTK_CONTAINER (list);
+  if (!(list->undo_selection || list->undo_unselection))
+    {
+      gtk_list_unselect_all (list);
+      return;
+    }
 
-  switch (list->selection_mode)
+  for (work = list->undo_selection; work; work = work->next)
+    gtk_list_select_child (list, GTK_WIDGET (work->data));
+
+  for (work = list->undo_unselection; work; work = work->next)
+    gtk_list_unselect_child (list, GTK_WIDGET (work->data));
+
+  if (list->undo_focus_child)
     {
-    case GTK_SELECTION_BROWSE:
-      if (container->focus_child)
-       {
-         gtk_list_select_child (list, container->focus_child);
-         return;
-       }
-      break;
+      GtkContainer *container;
 
-    case GTK_SELECTION_EXTENDED:
-      g_list_free (list->undo_selection);
-      g_list_free (list->undo_unselection);
-      list->undo_selection = NULL;
-      list->undo_unselection = NULL;
+      container = GTK_CONTAINER (list);
 
-      list->anchor = -1;
-      list->drag_pos = -1;
-      list->undo_focus_child = container->focus_child;
-      break;
+      if (container->focus_child &&
+         gtk_widget_has_focus (container->focus_child))
+       gtk_widget_grab_focus (list->undo_focus_child);
+      else
+       gtk_container_set_focus_child (container, list->undo_focus_child);
+    }
+
+  list->undo_focus_child = NULL;
+  g_list_free (list->undo_selection);
+  g_list_free (list->undo_unselection);
+  list->undo_selection = NULL;
+  list->undo_unselection = NULL;
+}
+
+
+/* Private GtkList Selection Methods :
+ *
+ * gtk_real_list_select_child
+ * gtk_real_list_unselect_child
+ */
+static void
+gtk_real_list_select_child (GtkList   *list,
+                           GtkWidget *child)
+{
+  g_return_if_fail (GTK_IS_LIST (list));
+  g_return_if_fail (GTK_IS_LIST_ITEM (child));
 
+  switch (child->state)
+    {
+    case GTK_STATE_SELECTED:
+    case GTK_STATE_INSENSITIVE:
+      break;
     default:
+      gtk_list_item_select (GTK_LIST_ITEM (child));
       break;
     }
+}
 
-  work = list->selection;
+static void
+gtk_real_list_unselect_child (GtkList  *list,
+                             GtkWidget *child)
+{
+  g_return_if_fail (GTK_IS_LIST (list));
+  g_return_if_fail (GTK_IS_LIST_ITEM (child));
 
-  while (work)
-    {
-      item = work->data;
-      work = work->next;
-      gtk_list_unselect_child (list, item);
-    }
+  if (child->state == GTK_STATE_SELECTED)
+    gtk_list_item_deselect (GTK_LIST_ITEM (child));
 }
 
-void
-gtk_list_extend_selection (GtkList       *list,
-                          GtkScrollType  scroll_type,
-                          gfloat         position,
-                          gboolean       auto_start_selection)
+
+/* Private GtkList Selection Functions :
+ *
+ * gtk_list_set_anchor
+ * gtk_list_fake_unselect_all
+ * gtk_list_fake_toggle_row
+ * gtk_list_update_extended_selection
+ * gtk_list_reset_extended_selection
+ */
+static void
+gtk_list_set_anchor (GtkList   *list,
+                    gboolean   add_mode,
+                    gint       anchor,
+                    GtkWidget *undo_focus_child)
 {
-  GtkContainer *container;
+  GList *work;
 
-  g_return_if_fail (list != NULL);
   g_return_if_fail (GTK_IS_LIST (list));
-
-  if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
-      list->selection_mode != GTK_SELECTION_EXTENDED)
+  
+  if (list->selection_mode != GTK_SELECTION_MULTIPLE || list->anchor >= 0)
     return;
 
-  container = GTK_CONTAINER (list);
+  g_list_free (list->undo_selection);
+  g_list_free (list->undo_unselection);
+  list->undo_selection = NULL;
+  list->undo_unselection = NULL;
 
-  if (auto_start_selection)
+  if ((work = g_list_nth (list->children, anchor)))
     {
-      gint focus_row;
-      focus_row = g_list_index (list->children, container->focus_child);
-      gtk_list_set_anchor (list, list->add_mode, focus_row,
-                          container->focus_child);
+      if (add_mode)
+       gtk_list_fake_toggle_row (list, GTK_WIDGET (work->data));
+      else
+       {
+         gtk_list_fake_unselect_all (list, GTK_WIDGET (work->data));
+         list->anchor_state = GTK_STATE_SELECTED;
+       }
+    }
+
+  list->anchor = anchor;
+  list->drag_pos = anchor;
+  list->undo_focus_child = undo_focus_child;
+}
+
+static void
+gtk_list_fake_unselect_all (GtkList   *list,
+                           GtkWidget *item)
+{
+  GList *work;
+
+  if (item && item->state == GTK_STATE_NORMAL)
+    gtk_widget_set_state (item, GTK_STATE_SELECTED);
+
+  list->undo_selection = list->selection;
+  list->selection = NULL;
+  
+  for (work = list->undo_selection; work; work = work->next)
+    if (work->data != item)
+      gtk_widget_set_state (GTK_WIDGET (work->data), GTK_STATE_NORMAL);
+}
+
+static void
+gtk_list_fake_toggle_row (GtkList   *list,
+                         GtkWidget *item)
+{
+  if (!item)
+    return;
+  
+  if (item->state == GTK_STATE_NORMAL)
+    {
+      list->anchor_state = GTK_STATE_SELECTED;
+      gtk_widget_set_state (item, GTK_STATE_SELECTED);
+    }
+  else
+    {
+      list->anchor_state = GTK_STATE_NORMAL;
+      gtk_widget_set_state (item, GTK_STATE_NORMAL);
     }
-  else if (list->anchor < 0)
-    return;
-
-  gtk_list_move_focus_child (list, scroll_type, position);
-  gtk_list_update_extended_selection 
-    (list, g_list_index (list->children, container->focus_child));
 }
 
 static void
@@ -1862,7 +1961,7 @@ gtk_list_update_extended_selection (GtkList *list,
   if (row >= length)
     row = length - 1;
 
-  if (list->selection_mode != GTK_SELECTION_EXTENDED || !list->anchor < 0)
+  if (list->selection_mode != GTK_SELECTION_MULTIPLE || !list->anchor < 0)
     return;
 
   /* extending downwards */
@@ -1935,339 +2034,548 @@ gtk_list_update_extended_selection (GtkList *list,
     }
 }
 
+static void
+gtk_list_reset_extended_selection (GtkList *list)
+{ 
+  g_return_if_fail (list != 0);
+  g_return_if_fail (GTK_IS_LIST (list));
+
+  g_list_free (list->undo_selection);
+  g_list_free (list->undo_unselection);
+  list->undo_selection = NULL;
+  list->undo_unselection = NULL;
+
+  list->anchor = -1;
+  list->drag_pos = -1;
+  list->undo_focus_child = GTK_CONTAINER (list)->focus_child;
+}
+
+/* Public GtkList Scroll Methods :
+ *
+ * gtk_list_scroll_horizontal
+ * gtk_list_scroll_vertical
+ */
 void
-gtk_list_end_selection (GtkList *list)
+gtk_list_scroll_horizontal (GtkList       *list,
+                           GtkScrollType  scroll_type,
+                           gfloat         position)
 {
-  gint i;
-  gint e;
+  GtkAdjustment *adj;
+
+  g_return_if_fail (list != 0);
+  g_return_if_fail (GTK_IS_LIST (list));
+
+  if (list_has_grab (list))
+    return;
+
+  if (!(adj =
+       gtk_object_get_data_by_id (GTK_OBJECT (list), hadjustment_key_id)))
+    return;
+
+  switch (scroll_type)
+    {
+    case GTK_SCROLL_STEP_UP:
+    case GTK_SCROLL_STEP_BACKWARD:
+      adj->value = CLAMP (adj->value - adj->step_increment, adj->lower,
+                         adj->upper - adj->page_size);
+      break;
+    case GTK_SCROLL_STEP_DOWN:
+    case GTK_SCROLL_STEP_FORWARD:
+      adj->value = CLAMP (adj->value + adj->step_increment, adj->lower,
+                         adj->upper - adj->page_size);
+      break;
+    case GTK_SCROLL_PAGE_UP:
+    case GTK_SCROLL_PAGE_BACKWARD:
+      adj->value = CLAMP (adj->value - adj->page_increment, adj->lower,
+                         adj->upper - adj->page_size);
+      break;
+    case GTK_SCROLL_PAGE_DOWN:
+    case GTK_SCROLL_PAGE_FORWARD:
+      adj->value = CLAMP (adj->value + adj->page_increment, adj->lower,
+                         adj->upper - adj->page_size);
+      break;
+    case GTK_SCROLL_JUMP:
+      adj->value = CLAMP (adj->lower + (adj->upper - adj->lower) * position,
+                         adj->lower, adj->upper - adj->page_size);
+      break;
+    default:
+      break;
+    }
+  gtk_adjustment_value_changed (adj);
+}
+
+void
+gtk_list_scroll_vertical (GtkList       *list,
+                         GtkScrollType  scroll_type,
+                         gfloat         position)
+{
+  g_return_if_fail (GTK_IS_LIST (list));
+
+  if (list_has_grab (list))
+    return;
+
+  if (list->selection_mode == GTK_SELECTION_MULTIPLE)
+    {
+      GtkContainer *container;
+
+      if (list->anchor >= 0)
+       return;
+
+      container = GTK_CONTAINER (list);
+      list->undo_focus_child = container->focus_child;
+      gtk_list_move_focus_child (list, scroll_type, position);
+      if (container->focus_child != list->undo_focus_child && !list->add_mode)
+       {
+         gtk_list_unselect_all (list);
+         gtk_list_select_child (list, container->focus_child);
+       }
+    }
+  else
+    gtk_list_move_focus_child (list, scroll_type, position);
+}
+
+
+/* Private GtkList Scroll/Focus Functions :
+ *
+ * gtk_list_move_focus_child
+ * gtk_list_horizontal_timeout
+ * gtk_list_vertical_timeout
+ */
+static void
+gtk_list_move_focus_child (GtkList       *list,
+                          GtkScrollType  scroll_type,
+                          gfloat         position)
+{
+  GtkContainer *container;
   GList *work;
   GtkWidget *item;
-  gint item_index;
+  GtkAdjustment *adj;
+  gint new_value;
 
-  g_return_if_fail (list != NULL);
+  g_return_if_fail (list != 0);
   g_return_if_fail (GTK_IS_LIST (list));
 
-  if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
-      list->anchor < 0)
-    return;
+  container = GTK_CONTAINER (list);
 
-  i = MIN (list->anchor, list->drag_pos);
-  e = MAX (list->anchor, list->drag_pos);
-  
-  list->anchor = -1;
-  list->drag_pos = -1;
+  if (container->focus_child)
+    work = g_list_find (list->children, container->focus_child);
+  else
+    work = list->children;
 
-  if (list->undo_selection)
+  if (!work)
+    return;
+
+  switch (scroll_type)
     {
-      work = list->selection;
-      list->selection = list->undo_selection;
-      list->undo_selection = work;
-      work = list->selection;
-      while (work)
+    case GTK_SCROLL_STEP_BACKWARD:
+      work = work->prev;
+      if (work)
+       gtk_widget_grab_focus (GTK_WIDGET (work->data));
+      break;
+    case GTK_SCROLL_STEP_FORWARD:
+      work = work->next;
+      if (work)
+       gtk_widget_grab_focus (GTK_WIDGET (work->data));
+      break;
+    case GTK_SCROLL_PAGE_BACKWARD:
+      if (!work->prev)
+       return;
+
+      item = work->data;
+      adj = gtk_object_get_data_by_id (GTK_OBJECT (list), vadjustment_key_id);
+
+      if (adj)
        {
-         item = work->data;
-         work = work->next;
-         item_index = g_list_index (list->children, item);
-         if (item_index < i || item_index > e)
+         gboolean correct = FALSE;
+
+         new_value = adj->value;
+
+         if (item->allocation.y <= adj->value)
            {
-             gtk_widget_set_state (item, GTK_STATE_SELECTED);
-             gtk_list_unselect_child (list, item);
-             list->undo_selection = g_list_prepend (list->undo_selection,
-                                                    item);
+             new_value = MAX (item->allocation.y + item->allocation.height
+                              - adj->page_size, adj->lower);
+             correct = TRUE;
            }
+
+         if (item->allocation.y > new_value)
+           for (; work; work = work->prev)
+             {
+               item = GTK_WIDGET (work->data);
+               if (item->allocation.y <= new_value &&
+                   item->allocation.y + item->allocation.height > new_value)
+                 break;
+             }
+         else
+           for (; work; work = work->next)
+             {
+               item = GTK_WIDGET (work->data);
+               if (item->allocation.y <= new_value &&
+                   item->allocation.y + item->allocation.height > new_value)
+                 break;
+             }
+
+         if (correct && work && work->next && item->allocation.y < new_value)
+           item = work->next->data;
        }
-    }    
+      else
+       item = list->children->data;
+         
+      gtk_widget_grab_focus (item);
+      break;
+    case GTK_SCROLL_PAGE_FORWARD:
+      if (!work->next)
+       return;
 
-  for (work = g_list_nth (list->children, i); i <= e; i++, work = work->next)
-    {
       item = work->data;
-      if (g_list_find (list->selection, item))
-         {
-           if (item->state == GTK_STATE_NORMAL)
+      adj = gtk_object_get_data_by_id (GTK_OBJECT (list), vadjustment_key_id);
+
+      if (adj)
+       {
+         gboolean correct = FALSE;
+
+         new_value = adj->value;
+
+         if (item->allocation.y + item->allocation.height >=
+             adj->value + adj->page_size)
+           {
+             new_value = item->allocation.y;
+             correct = TRUE;
+           }
+
+         new_value = MIN (new_value + adj->page_size, adj->upper);
+
+         if (item->allocation.y > new_value)
+           for (; work; work = work->prev)
              {
-               gtk_widget_set_state (item, GTK_STATE_SELECTED);
-               gtk_list_unselect_child (list, item);
-               list->undo_selection = g_list_prepend (list->undo_selection,
-                                                      item);
+               item = GTK_WIDGET (work->data);
+               if (item->allocation.y <= new_value &&
+                   item->allocation.y + item->allocation.height > new_value)
+                 break;
+             }
+         else
+           for (; work; work = work->next)
+             {
+               item = GTK_WIDGET (work->data);
+               if (item->allocation.y <= new_value &&
+                   item->allocation.y + item->allocation.height > new_value)
+                 break;
              }
-         }
-      else if (item->state == GTK_STATE_SELECTED)
+
+         if (correct && work && work->prev &&
+             item->allocation.y + item->allocation.height - 1 > new_value)
+           item = work->prev->data;
+       }
+      else
+       item = g_list_last (work)->data;
+         
+      gtk_widget_grab_focus (item);
+      break;
+    case GTK_SCROLL_JUMP:
+      new_value = GTK_WIDGET(list)->allocation.height * CLAMP (position, 0, 1);
+
+      for (item = NULL, work = list->children; work; work =work->next)
        {
-         gtk_widget_set_state (item, GTK_STATE_NORMAL);
-         list->undo_unselection = g_list_prepend (list->undo_unselection,
-                                                  item);
+         item = GTK_WIDGET (work->data);
+         if (item->allocation.y <= new_value &&
+             item->allocation.y + item->allocation.height > new_value)
+           break;
        }
+
+      gtk_widget_grab_focus (item);
+      break;
+    default:
+      break;
     }
+}
 
-  for (work = list->undo_unselection; work; work = work->next)
-    gtk_list_select_child (list, GTK_WIDGET (work->data));
+static void
+do_fake_motion (GtkWidget *list)
+{
+  GdkEvent *event = gdk_event_new (GDK_MOTION_NOTIFY);
+
+  event->motion.send_event = TRUE;
 
+  gtk_list_motion_notify (list, (GdkEventMotion *)event);
+  gdk_event_free (event);
 }
 
-void
-gtk_list_start_selection (GtkList *list)
+static gint
+gtk_list_horizontal_timeout (GtkWidget *list)
 {
-  GtkContainer *container;
-  gint focus_row;
+  GTK_LIST (list)->htimer = 0;
+  do_fake_motion (list);
+  
+  return FALSE;
+}
 
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
+static gint
+gtk_list_vertical_timeout (GtkWidget *list)
+{
+  GTK_LIST (list)->vtimer = 0;
+  do_fake_motion (list);
 
-  if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
-    return;
+  return FALSE;
+}
 
-  container = GTK_CONTAINER (list);
 
-  if ((focus_row = g_list_index (list->selection, container->focus_child))
-      >= 0)
-    gtk_list_set_anchor (list, list->add_mode,
-                        focus_row, container->focus_child);
+/* Private GtkListItem Signal Functions :
+ *
+ * gtk_list_signal_toggle_focus_row
+ * gtk_list_signal_select_all
+ * gtk_list_signal_unselect_all
+ * gtk_list_signal_undo_selection
+ * gtk_list_signal_start_selection
+ * gtk_list_signal_end_selection
+ * gtk_list_signal_extend_selection
+ * gtk_list_signal_scroll_horizontal
+ * gtk_list_signal_scroll_vertical
+ * gtk_list_signal_toggle_add_mode
+ * gtk_list_signal_item_select
+ * gtk_list_signal_item_deselect
+ * gtk_list_signal_item_toggle
+ */
+static void
+gtk_list_signal_toggle_focus_row (GtkListItem *list_item,
+                                 GtkList     *list)
+{
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
+  g_return_if_fail (GTK_IS_LIST (list));
+
+  gtk_list_toggle_focus_row (list);
 }
 
-void
-gtk_list_toggle_row (GtkList   *list,
-                    GtkWidget *item)
+static void
+gtk_list_signal_select_all (GtkListItem *list_item,
+                           GtkList     *list)
 {
-  g_return_if_fail (list != NULL);
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
   g_return_if_fail (GTK_IS_LIST (list));
-  g_return_if_fail (item != NULL);
-  g_return_if_fail (GTK_IS_LIST_ITEM (item));
 
-  switch (list->selection_mode)
-    {
-    case GTK_SELECTION_EXTENDED:
-    case GTK_SELECTION_MULTIPLE:
-    case GTK_SELECTION_SINGLE:
+  gtk_list_select_all (list);
+}
 
-      if (item->state == GTK_STATE_SELECTED)
-       {
-         gtk_list_unselect_child (list, item);
-         return;
-       }
+static void
+gtk_list_signal_unselect_all (GtkListItem *list_item,
+                             GtkList     *list)
+{
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
+  g_return_if_fail (GTK_IS_LIST (list));
 
-    case GTK_SELECTION_BROWSE:
-      gtk_list_select_child (list, item);
-      break;
-    }
+  gtk_list_unselect_all (list);
 }
 
-void
-gtk_list_toggle_focus_row (GtkList *list)
+static void
+gtk_list_signal_undo_selection (GtkListItem *list_item,
+                               GtkList     *list)
 {
-  GtkContainer *container;
-  gint focus_row;
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
+  g_return_if_fail (GTK_IS_LIST (list));
 
-  g_return_if_fail (list != 0);
+  gtk_list_undo_selection (list);
+}
+
+static void
+gtk_list_signal_start_selection (GtkListItem *list_item,
+                                GtkList     *list)
+{
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
   g_return_if_fail (GTK_IS_LIST (list));
 
-  container = GTK_CONTAINER (list);
+  gtk_list_start_selection (list);
+}
 
-  if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
-      !container->focus_child)
-    return;
+static void
+gtk_list_signal_end_selection (GtkListItem *list_item,
+                              GtkList     *list)
+{
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
+  g_return_if_fail (GTK_IS_LIST (list));
 
-  switch (list->selection_mode)
-    {
-    case  GTK_SELECTION_SINGLE:
-    case  GTK_SELECTION_MULTIPLE:
-      
-      gtk_list_toggle_row (list, container->focus_child);
-      break;
-      
-    case GTK_SELECTION_EXTENDED:
+  gtk_list_end_selection (list);
+}
 
-      if ((focus_row = g_list_index (list->children, container->focus_child))
-         < 0)
-       return;
+static void
+gtk_list_signal_extend_selection (GtkListItem   *list_item,
+                                 GtkScrollType  scroll_type,
+                                 gfloat         position,
+                                 gboolean       auto_start_selection,
+                                 GtkList       *list)
+{
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
+  g_return_if_fail (GTK_IS_LIST (list));
 
-      g_list_free (list->undo_selection);
-      g_list_free (list->undo_unselection);
-      list->undo_selection = NULL;
-      list->undo_unselection = NULL;
+  gtk_list_extend_selection (list, scroll_type, position,
+                            auto_start_selection);
+}
 
-      list->anchor = focus_row;
-      list->drag_pos = focus_row;
-      list->undo_focus_child = container->focus_child;
+static void
+gtk_list_signal_scroll_horizontal (GtkListItem   *list_item,
+                                  GtkScrollType  scroll_type,
+                                  gfloat         position,
+                                  GtkList       *list)
+{
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
+  g_return_if_fail (GTK_IS_LIST (list));
 
-      if (list->add_mode)
-       gtk_list_fake_toggle_row (list, container->focus_child);
-      else
-       gtk_list_fake_unselect_all (list, container->focus_child);
-      
-      gtk_list_end_selection (list);
-      break;
-      
-    default:
-      break;
-    }
+  gtk_list_scroll_horizontal (list, scroll_type, position);
 }
 
-void
-gtk_list_toggle_add_mode (GtkList *list)
+static void
+gtk_list_signal_scroll_vertical (GtkListItem   *list_item,
+                                GtkScrollType  scroll_type,
+                                gfloat         position,
+                                GtkList       *list)
 {
-  GtkContainer *container;
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
+  g_return_if_fail (GTK_IS_LIST (list));
 
-  g_return_if_fail (list != 0);
+  gtk_list_scroll_vertical (list, scroll_type, position);
+}
+
+static void
+gtk_list_signal_toggle_add_mode (GtkListItem *list_item,
+                                GtkList     *list)
+{
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
   g_return_if_fail (GTK_IS_LIST (list));
-  
-  if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
-      list->selection_mode != GTK_SELECTION_EXTENDED)
-    return;
-  
-  container = GTK_CONTAINER (list);
 
-  if (list->add_mode)
-    {
-      list->add_mode = FALSE;
-      list->anchor_state = GTK_STATE_SELECTED;
-    }
-  else
-    list->add_mode = TRUE;
-  
-  if (container->focus_child)
-    gtk_widget_queue_draw (container->focus_child);
+  gtk_list_toggle_add_mode (list);
 }
 
-void
-gtk_list_undo_selection (GtkList *list)
+static void
+gtk_list_signal_item_select (GtkListItem *list_item,
+                            GtkList     *list)
 {
-  GList *work;
+  GList *selection;
+  GList *tmp_list;
+  GList *sel_list;
 
-  g_return_if_fail (list != NULL);
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
   g_return_if_fail (GTK_IS_LIST (list));
 
-  if (list->selection_mode != GTK_SELECTION_EXTENDED ||
-      (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)))
+  if (GTK_WIDGET (list_item)->state != GTK_STATE_SELECTED)
     return;
-  
-  if (list->anchor >= 0)
-    gtk_list_end_selection (list);
 
-  if (!(list->undo_selection || list->undo_unselection))
+  switch (list->selection_mode)
     {
-      gtk_list_unselect_all (list);
-      return;
-    }
-
-  for (work = list->undo_selection; work; work = work->next)
-    gtk_list_select_child (list, GTK_WIDGET (work->data));
-
-  for (work = list->undo_unselection; work; work = work->next)
-    gtk_list_unselect_child (list, GTK_WIDGET (work->data));
+    case GTK_SELECTION_SINGLE:
+    case GTK_SELECTION_BROWSE:
+      sel_list = NULL;
+      selection = list->selection;
 
-  if (list->undo_focus_child)
-    {
-      GtkContainer *container;
+      while (selection)
+       {
+         tmp_list = selection;
+         selection = selection->next;
 
-      container = GTK_CONTAINER (list);
+         if (tmp_list->data == list_item)
+           sel_list = tmp_list;
+         else
+           gtk_list_item_deselect (GTK_LIST_ITEM (tmp_list->data));
+       }
 
-      if (container->focus_child &&
-         GTK_WIDGET_HAS_FOCUS (container->focus_child))
-       gtk_widget_grab_focus (list->undo_focus_child);
-      else
-       gtk_container_set_focus_child (container, list->undo_focus_child);
+      if (!sel_list)
+       {
+         list->selection = g_list_prepend (list->selection, list_item);
+         g_object_ref (list_item);
+       }
+      gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
+      break;
+    case GTK_SELECTION_MULTIPLE:
+      if (list->anchor >= 0)
+       return;
     }
-
-  list->undo_focus_child = NULL;
-  g_list_free (list->undo_selection);
-  g_list_free (list->undo_unselection);
-  list->undo_selection = NULL;
-  list->undo_unselection = NULL;
-}
-
-static void
-gtk_list_focus_lost (GtkWidget   *item,
-                    GdkEventKey *event,
-                    GtkList     *list)
-{
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (GTK_IS_LIST (list));
-  g_return_if_fail (item != NULL);
-  g_return_if_fail (GTK_IS_LIST_ITEM (item));
-
-  if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0 &&
-      item == GTK_CONTAINER (list)->focus_child)
-    gtk_list_end_selection (list);
 }
 
 static void
-gtk_list_set_focus_child (GtkContainer *container,
-                         GtkWidget    *child)
+gtk_list_signal_item_deselect (GtkListItem *list_item,
+                              GtkList     *list)
 {
-  GtkList *list;
+  GList *node;
 
-  g_return_if_fail (container != NULL);
-  g_return_if_fail (GTK_IS_LIST (container));
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
+  g_return_if_fail (GTK_IS_LIST (list));
 
-  if (child)
-    g_return_if_fail (GTK_IS_WIDGET (child));
+  if (GTK_WIDGET (list_item)->state != GTK_STATE_NORMAL)
+    return;
 
-  list = GTK_LIST (container);
-  list->last_focus_child = container->focus_child;
+  node = g_list_find (list->selection, list_item);
 
-  if (child != container->focus_child)
+  if (node)
     {
-      if (container->focus_child)
-        gtk_widget_unref (container->focus_child);
-      container->focus_child = child;
-      if (container->focus_child)
-        gtk_widget_ref (container->focus_child);
+      list->selection = g_list_remove_link (list->selection, node);
+      g_list_free_1 (node);
+      g_object_unref (list_item);
+      gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
     }
+}
 
-  /* check for v adjustment */
-  if (container->focus_child)
-    {
-      GtkAdjustment *adjustment;
+static void
+gtk_list_signal_item_toggle (GtkListItem *list_item,
+                            GtkList     *list)
+{
+  g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
+  g_return_if_fail (GTK_IS_LIST (list));
 
-      adjustment = gtk_object_get_data_by_id (GTK_OBJECT (container),
-                                             vadjustment_key_id);
-      if (adjustment)
-        gtk_adjustment_clamp_page (adjustment,
-                                   container->focus_child->allocation.y,
-                                   (container->focus_child->allocation.y +
-                                    container->focus_child->allocation.height));
+  if ((list->selection_mode == GTK_SELECTION_BROWSE ||
+       list->selection_mode == GTK_SELECTION_MULTIPLE) &&
+      GTK_WIDGET (list_item)->state == GTK_STATE_NORMAL)
+    {
+      gtk_widget_set_state (GTK_WIDGET (list_item), GTK_STATE_SELECTED);
+      return;
     }
-
-  switch (list->selection_mode)
+  
+  switch (GTK_WIDGET (list_item)->state)
     {
-    case GTK_SELECTION_BROWSE:
-      if (child)
-       gtk_list_select_child (list, child);
+    case GTK_STATE_SELECTED:
+      gtk_list_signal_item_select (list_item, list);
+      break;
+    case GTK_STATE_NORMAL:
+      gtk_list_signal_item_deselect (list_item, list);
       break;
     default:
       break;
     }
 }
 
+static void
+gtk_list_signal_drag_begin (GtkWidget      *widget,
+                           GdkDragContext *context,
+                           GtkList         *list)
+{
+  g_return_if_fail (GTK_IS_LIST_ITEM (widget));
+  g_return_if_fail (GTK_IS_LIST (list));
+
+  gtk_list_drag_begin (GTK_WIDGET (list), context);
+}
 
-static gint
-gtk_list_focus (GtkContainer     *container,
-               GtkDirectionType  direction)
+static void
+gtk_list_drag_begin (GtkWidget      *widget,
+                    GdkDragContext *context)
 {
-  gint return_val = FALSE;
+  GtkList *list;
+
+  g_return_if_fail (GTK_IS_LIST (widget));
+  g_return_if_fail (context != NULL);
 
-  g_return_val_if_fail (container != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_LIST (container), FALSE);
+  list = GTK_LIST (widget);
 
-  if (!GTK_WIDGET_SENSITIVE (container))
-    return_val = FALSE;
-  else if (container->focus_child == NULL ||
-      !GTK_WIDGET_HAS_FOCUS (container->focus_child))
-    {
-      if (*GTK_CONTAINER_CLASS (parent_class)->focus)
-       return_val = GTK_CONTAINER_CLASS (parent_class)->focus
-         (container, direction);
-    }
-  
-  if (!return_val)
+  if (list->drag_selection)
     {
-      GtkList *list;
+      gtk_list_end_drag_selection (list);
 
-      list =  GTK_LIST (container);
-      if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
-       gtk_list_end_selection (list);
+      switch (list->selection_mode)
+       {
+       case GTK_SELECTION_MULTIPLE:
+         gtk_list_end_selection (list);
+         break;
+       case GTK_SELECTION_SINGLE:
+         list->undo_focus_child = NULL;
+         break;
+       default:
+         break;
+       }
     }
-
-  return return_val;
 }
+
+#include "gtkaliasdef.c"