]> Pileus Git - ~andy/gtk/blob - gtk/gtklist.c
Remove explicit pointer grabs, since they are no longer necessary.
[~andy/gtk] / gtk / gtklist.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <string.h> /* memset */
28
29 #include "gtklist.h"
30 #include "gtklistitem.h"
31 #include "gtkmain.h"
32 #include "gtksignal.h"
33 #include "gtklabel.h"
34
35 enum {
36   SELECTION_CHANGED,
37   SELECT_CHILD,
38   UNSELECT_CHILD,
39   LAST_SIGNAL
40 };
41
42 enum {
43   ARG_0,
44   ARG_SELECTION_MODE
45 };
46
47 #define SCROLL_TIME  100
48
49 /*** GtkList Methods ***/
50 static void gtk_list_class_init      (GtkListClass   *klass);
51 static void gtk_list_init            (GtkList        *list);
52 static void gtk_list_set_arg         (GtkObject      *object,
53                                       GtkArg         *arg,
54                                       guint           arg_id);
55 static void gtk_list_get_arg         (GtkObject      *object,
56                                       GtkArg         *arg,
57                                       guint           arg_id);
58 /*** GtkObject Methods ***/
59 static void gtk_list_shutdown        (GObject        *object);
60
61 /*** GtkWidget Methods ***/
62 static void gtk_list_size_request    (GtkWidget      *widget,
63                                       GtkRequisition *requisition);
64 static void gtk_list_size_allocate   (GtkWidget      *widget,
65                                       GtkAllocation  *allocation);
66 static void gtk_list_realize         (GtkWidget      *widget);
67 static void gtk_list_map             (GtkWidget      *widget);
68 static void gtk_list_unmap           (GtkWidget      *widget);
69 static void gtk_list_style_set       (GtkWidget      *widget,
70                                       GtkStyle       *previous_style);
71 static gint gtk_list_motion_notify   (GtkWidget      *widget,
72                                       GdkEventMotion *event);
73 static gint gtk_list_button_press    (GtkWidget      *widget,
74                                       GdkEventButton *event);
75 static gint gtk_list_button_release  (GtkWidget      *widget,
76                                       GdkEventButton *event);
77
78 /*** GtkContainer Methods ***/
79 static void gtk_list_add             (GtkContainer     *container,
80                                       GtkWidget        *widget);
81 static void gtk_list_remove          (GtkContainer     *container,
82                                       GtkWidget        *widget);
83 static void gtk_list_forall          (GtkContainer     *container,
84                                       gboolean          include_internals,
85                                       GtkCallback       callback,
86                                       gpointer          callback_data);
87 static GtkType gtk_list_child_type   (GtkContainer     *container);
88 static void gtk_list_set_focus_child (GtkContainer     *container,
89                                       GtkWidget        *widget);
90 static gint gtk_list_focus           (GtkContainer     *container,
91                                       GtkDirectionType  direction);
92
93 /*** GtkList Private Functions ***/
94 static void gtk_list_move_focus_child      (GtkList       *list,
95                                             GtkScrollType  scroll_type,
96                                             gfloat         position);
97 static gint gtk_list_horizontal_timeout    (GtkWidget     *list);
98 static gint gtk_list_vertical_timeout      (GtkWidget     *list);
99 static void gtk_list_remove_items_internal (GtkList       *list,
100                                             GList         *items,
101                                             gboolean       no_unref);
102
103 /*** GtkList Selection Methods ***/
104 static void gtk_real_list_select_child          (GtkList   *list,
105                                                  GtkWidget *child);
106 static void gtk_real_list_unselect_child        (GtkList   *list,
107                                                  GtkWidget *child);
108
109 /*** GtkList Selection Functions ***/
110 static void gtk_list_set_anchor                 (GtkList   *list,
111                                                  gboolean   add_mode,
112                                                  gint       anchor,
113                                                  GtkWidget *undo_focus_child);
114 static void gtk_list_fake_unselect_all          (GtkList   *list,
115                                                  GtkWidget *item);
116 static void gtk_list_fake_toggle_row            (GtkList   *list,
117                                                  GtkWidget *item);
118 static void gtk_list_update_extended_selection  (GtkList   *list,
119                                                  gint       row);
120 static void gtk_list_reset_extended_selection   (GtkList   *list);
121
122 /*** GtkListItem Signal Functions ***/
123 static void gtk_list_signal_drag_begin         (GtkWidget      *widget,
124                                                 GdkDragContext *context,
125                                                 GtkList        *list);
126 static void gtk_list_signal_toggle_focus_row   (GtkListItem   *list_item,
127                                                 GtkList       *list);
128 static void gtk_list_signal_select_all         (GtkListItem   *list_item,
129                                                 GtkList       *list);
130 static void gtk_list_signal_unselect_all       (GtkListItem   *list_item,
131                                                 GtkList       *list);
132 static void gtk_list_signal_undo_selection     (GtkListItem   *list_item,
133                                                 GtkList       *list);
134 static void gtk_list_signal_start_selection    (GtkListItem   *list_item,
135                                                 GtkList       *list);
136 static void gtk_list_signal_end_selection      (GtkListItem   *list_item,
137                                                 GtkList       *list);
138 static void gtk_list_signal_extend_selection   (GtkListItem   *list_item,
139                                                 GtkScrollType  scroll_type,
140                                                 gfloat         position,
141                                                 gboolean       auto_start_selection,
142                                                 GtkList       *list);
143 static void gtk_list_signal_scroll_horizontal  (GtkListItem   *list_item,
144                                                 GtkScrollType  scroll_type,
145                                                 gfloat         position,
146                                                 GtkList       *list);
147 static void gtk_list_signal_scroll_vertical    (GtkListItem   *list_item,
148                                                 GtkScrollType  scroll_type,
149                                                 gfloat         position,
150                                                 GtkList       *list);
151 static void gtk_list_signal_toggle_add_mode    (GtkListItem   *list_item,
152                                                 GtkList       *list);
153 static void gtk_list_signal_item_select        (GtkListItem   *list_item,
154                                                 GtkList       *list);
155 static void gtk_list_signal_item_deselect      (GtkListItem   *list_item,
156                                                 GtkList       *list);
157 static void gtk_list_signal_item_toggle        (GtkListItem   *list_item,
158                                                 GtkList       *list);
159
160
161 static void gtk_list_drag_begin (GtkWidget      *widget,
162                                  GdkDragContext *context);
163
164
165 static GtkContainerClass *parent_class = NULL;
166 static guint list_signals[LAST_SIGNAL] = { 0 };
167
168 static const gchar *vadjustment_key = "gtk-vadjustment";
169 static guint        vadjustment_key_id = 0;
170 static const gchar *hadjustment_key = "gtk-hadjustment";
171 static guint        hadjustment_key_id = 0;
172
173 GtkType
174 gtk_list_get_type (void)
175 {
176   static GtkType list_type = 0;
177
178   if (!list_type)
179     {
180       static const GtkTypeInfo list_info =
181       {
182         "GtkList",
183         sizeof (GtkList),
184         sizeof (GtkListClass),
185         (GtkClassInitFunc) gtk_list_class_init,
186         (GtkObjectInitFunc) gtk_list_init,
187         /* reserved_1 */ NULL,
188         /* reserved_2 */ NULL,
189         (GtkClassInitFunc) NULL,
190       };
191
192       list_type = gtk_type_unique (GTK_TYPE_CONTAINER, &list_info);
193     }
194
195   return list_type;
196 }
197
198 static void
199 gtk_list_class_init (GtkListClass *class)
200 {
201   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
202   GtkObjectClass *object_class;
203   GtkWidgetClass *widget_class;
204   GtkContainerClass *container_class;
205
206   object_class = (GtkObjectClass*) class;
207   widget_class = (GtkWidgetClass*) class;
208   container_class = (GtkContainerClass*) class;
209
210   parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
211
212   vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
213   hadjustment_key_id = g_quark_from_static_string (hadjustment_key);
214
215   gobject_class->shutdown = gtk_list_shutdown;
216
217
218   object_class->set_arg = gtk_list_set_arg;
219   object_class->get_arg = gtk_list_get_arg;
220
221   widget_class->map = gtk_list_map;
222   widget_class->unmap = gtk_list_unmap;
223   widget_class->style_set = gtk_list_style_set;
224   widget_class->realize = gtk_list_realize;
225   widget_class->button_press_event = gtk_list_button_press;
226   widget_class->button_release_event = gtk_list_button_release;
227   widget_class->motion_notify_event = gtk_list_motion_notify;
228   widget_class->size_request = gtk_list_size_request;
229   widget_class->size_allocate = gtk_list_size_allocate;
230   widget_class->drag_begin = gtk_list_drag_begin;
231
232   container_class->add = gtk_list_add;
233   container_class->remove = gtk_list_remove;
234   container_class->forall = gtk_list_forall;
235   container_class->child_type = gtk_list_child_type;
236   container_class->set_focus_child = gtk_list_set_focus_child;
237   container_class->focus = gtk_list_focus;
238
239   class->selection_changed = NULL;
240   class->select_child = gtk_real_list_select_child;
241   class->unselect_child = gtk_real_list_unselect_child;
242
243   list_signals[SELECTION_CHANGED] =
244     gtk_signal_new ("selection_changed",
245                     GTK_RUN_FIRST,
246                     GTK_CLASS_TYPE (object_class),
247                     GTK_SIGNAL_OFFSET (GtkListClass, selection_changed),
248                     gtk_marshal_VOID__VOID,
249                     GTK_TYPE_NONE, 0);
250   list_signals[SELECT_CHILD] =
251     gtk_signal_new ("select_child",
252                     GTK_RUN_FIRST,
253                     GTK_CLASS_TYPE (object_class),
254                     GTK_SIGNAL_OFFSET (GtkListClass, select_child),
255                     gtk_marshal_VOID__OBJECT,
256                     GTK_TYPE_NONE, 1,
257                     GTK_TYPE_WIDGET);
258   list_signals[UNSELECT_CHILD] =
259     gtk_signal_new ("unselect_child",
260                     GTK_RUN_FIRST,
261                     GTK_CLASS_TYPE (object_class),
262                     GTK_SIGNAL_OFFSET (GtkListClass, unselect_child),
263                     gtk_marshal_VOID__OBJECT,
264                     GTK_TYPE_NONE, 1,
265                     GTK_TYPE_WIDGET);
266   
267   gtk_object_add_arg_type ("GtkList::selection_mode",
268                            GTK_TYPE_SELECTION_MODE, GTK_ARG_READWRITE,
269                            ARG_SELECTION_MODE);
270 }
271
272 static void
273 gtk_list_init (GtkList *list)
274 {
275   list->children = NULL;
276   list->selection = NULL;
277
278   list->undo_selection = NULL;
279   list->undo_unselection = NULL;
280
281   list->last_focus_child = NULL;
282   list->undo_focus_child = NULL;
283
284   list->htimer = 0;
285   list->vtimer = 0;
286
287   list->anchor = -1;
288   list->drag_pos = -1;
289   list->anchor_state = GTK_STATE_SELECTED;
290
291   list->selection_mode = GTK_SELECTION_SINGLE;
292   list->drag_selection = FALSE;
293   list->add_mode = FALSE;
294 }
295
296 static void
297 gtk_list_set_arg (GtkObject      *object,
298                   GtkArg         *arg,
299                   guint           arg_id)
300 {
301   GtkList *list = GTK_LIST (object);
302   
303   switch (arg_id)
304     {
305     case ARG_SELECTION_MODE:
306       gtk_list_set_selection_mode (list, GTK_VALUE_ENUM (*arg));
307       break;
308     }
309 }
310
311 static void
312 gtk_list_get_arg (GtkObject      *object,
313                   GtkArg         *arg,
314                   guint           arg_id)
315 {
316   GtkList *list = GTK_LIST (object);
317   
318   switch (arg_id)
319     {
320     case ARG_SELECTION_MODE: 
321       GTK_VALUE_ENUM (*arg) = list->selection_mode; 
322       break;
323     default:
324       arg->type = GTK_TYPE_INVALID;
325       break;
326     }
327 }
328
329 GtkWidget*
330 gtk_list_new (void)
331 {
332   return GTK_WIDGET (gtk_type_new (GTK_TYPE_LIST));
333 }
334
335
336 /* Private GtkObject Methods :
337  * 
338  * gtk_list_shutdown
339  */
340 static void
341 gtk_list_shutdown (GObject *object)
342 {
343   gtk_list_clear_items (GTK_LIST (object), 0, -1);
344
345   G_OBJECT_CLASS (parent_class)->shutdown (object);
346 }
347
348
349 /* Private GtkWidget Methods :
350  * 
351  * gtk_list_size_request
352  * gtk_list_size_allocate
353  * gtk_list_realize
354  * gtk_list_map
355  * gtk_list_unmap
356  * gtk_list_motion_notify
357  * gtk_list_button_press
358  * gtk_list_button_release
359  */
360 static void
361 gtk_list_size_request (GtkWidget      *widget,
362                        GtkRequisition *requisition)
363 {
364   GtkList *list;
365   GtkWidget *child;
366   GList *children;
367
368   g_return_if_fail (widget != NULL);
369   g_return_if_fail (GTK_IS_LIST (widget));
370   g_return_if_fail (requisition != NULL);
371
372   list = GTK_LIST (widget);
373   requisition->width = 0;
374   requisition->height = 0;
375
376   children = list->children;
377   while (children)
378     {
379       child = children->data;
380       children = children->next;
381
382       if (GTK_WIDGET_VISIBLE (child))
383         {
384           GtkRequisition child_requisition;
385           
386           gtk_widget_size_request (child, &child_requisition);
387
388           requisition->width = MAX (requisition->width,
389                                     child_requisition.width);
390           requisition->height += child_requisition.height;
391         }
392     }
393
394   requisition->width += GTK_CONTAINER (list)->border_width * 2;
395   requisition->height += GTK_CONTAINER (list)->border_width * 2;
396
397   requisition->width = MAX (requisition->width, 1);
398   requisition->height = MAX (requisition->height, 1);
399 }
400
401 static void
402 gtk_list_size_allocate (GtkWidget     *widget,
403                         GtkAllocation *allocation)
404 {
405   GtkList *list;
406   GtkWidget *child;
407   GtkAllocation child_allocation;
408   GList *children;
409
410   g_return_if_fail (widget != NULL);
411   g_return_if_fail (GTK_IS_LIST (widget));
412   g_return_if_fail (allocation != NULL);
413
414   list = GTK_LIST (widget);
415
416   widget->allocation = *allocation;
417   if (GTK_WIDGET_REALIZED (widget))
418     gdk_window_move_resize (widget->window,
419                             allocation->x, allocation->y,
420                             allocation->width, allocation->height);
421
422   if (list->children)
423     {
424       child_allocation.x = GTK_CONTAINER (list)->border_width;
425       child_allocation.y = GTK_CONTAINER (list)->border_width;
426       child_allocation.width = MAX (1, (gint)allocation->width -
427                                     child_allocation.x * 2);
428
429       children = list->children;
430
431       while (children)
432         {
433           child = children->data;
434           children = children->next;
435
436           if (GTK_WIDGET_VISIBLE (child))
437             {
438               GtkRequisition child_requisition;
439               gtk_widget_get_child_requisition (child, &child_requisition);
440               
441               child_allocation.height = child_requisition.height;
442
443               gtk_widget_size_allocate (child, &child_allocation);
444
445               child_allocation.y += child_allocation.height;
446             }
447         }
448     }
449 }
450
451 static void
452 gtk_list_realize (GtkWidget *widget)
453 {
454   GdkWindowAttr attributes;
455   gint attributes_mask;
456
457   g_return_if_fail (widget != NULL);
458   g_return_if_fail (GTK_IS_LIST (widget));
459
460   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
461
462   attributes.window_type = GDK_WINDOW_CHILD;
463   attributes.x = widget->allocation.x;
464   attributes.y = widget->allocation.y;
465   attributes.width = widget->allocation.width;
466   attributes.height = widget->allocation.height;
467   attributes.wclass = GDK_INPUT_OUTPUT;
468   attributes.visual = gtk_widget_get_visual (widget);
469   attributes.colormap = gtk_widget_get_colormap (widget);
470   attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
471
472   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
473
474   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
475                                    &attributes, attributes_mask);
476   gdk_window_set_user_data (widget->window, widget);
477
478   widget->style = gtk_style_attach (widget->style, widget->window);
479   gdk_window_set_background (widget->window, 
480                              &widget->style->base[GTK_STATE_NORMAL]);
481 }
482
483 static void
484 gtk_list_map (GtkWidget *widget)
485 {
486   GtkList *list;
487   GtkWidget *child;
488   GList *children;
489
490   g_return_if_fail (widget != NULL);
491   g_return_if_fail (GTK_IS_LIST (widget));
492
493   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
494   list = GTK_LIST (widget);
495
496   children = list->children;
497   while (children)
498     {
499       child = children->data;
500       children = children->next;
501
502       if (GTK_WIDGET_VISIBLE (child) &&
503           !GTK_WIDGET_MAPPED (child))
504         gtk_widget_map (child);
505     }
506
507   gdk_window_show (widget->window);
508 }
509
510 static void
511 gtk_list_unmap (GtkWidget *widget)
512 {
513   GtkList *list;
514
515   g_return_if_fail (widget != NULL);
516   g_return_if_fail (GTK_IS_LIST (widget));
517
518   if (!GTK_WIDGET_MAPPED (widget))
519     return;
520
521   list = GTK_LIST (widget);
522
523   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
524
525   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
526     {
527       gtk_list_end_drag_selection (list);
528
529       if (list->anchor != -1 && list->selection_mode == GTK_SELECTION_EXTENDED)
530         gtk_list_end_selection (list);
531     }
532
533   gdk_window_hide (widget->window);
534 }
535
536 static gint
537 gtk_list_motion_notify (GtkWidget      *widget,
538                         GdkEventMotion *event)
539 {
540   GtkList *list;
541   GtkWidget *item = NULL;
542   GtkAdjustment *adj;
543   GtkContainer *container;
544   GList *work;
545   gint x;
546   gint y;
547   gint row = -1;
548   gint focus_row = 0;
549   gint length = 0;
550
551   g_return_val_if_fail (widget != NULL, FALSE);
552   g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
553   g_return_val_if_fail (event != NULL, FALSE);
554
555   list = GTK_LIST (widget);
556
557   if (!list->drag_selection || !list->children)
558     return FALSE;
559
560   container = GTK_CONTAINER (widget);
561
562   if (event->is_hint || event->window != widget->window)
563     gdk_window_get_pointer (widget->window, &x, &y, NULL);
564   else
565     {
566       x = event->x;
567       y = event->y;
568     }
569
570   adj = gtk_object_get_data_by_id (GTK_OBJECT (list), hadjustment_key_id);
571
572   /* horizontal autoscrolling */
573   if (adj && widget->allocation.width > adj->page_size &&
574       (x < adj->value || x >= adj->value + adj->page_size))
575     {
576       if (list->htimer == 0)
577         {
578           list->htimer = gtk_timeout_add
579             (SCROLL_TIME, (GtkFunction) gtk_list_horizontal_timeout, widget);
580           
581           if (!((x < adj->value && adj->value <= 0) ||
582                 (x > adj->value + adj->page_size &&
583                  adj->value >= adj->upper - adj->page_size)))
584             {
585               gdouble value;
586
587               if (x < adj->value)
588                 value = adj->value + (x - adj->value) / 2 - 1;
589               else
590                 value = adj->value + 1 + (x - adj->value - adj->page_size) / 2;
591
592               gtk_adjustment_set_value (adj,
593                                         CLAMP (value, 0.0,
594                                                adj->upper - adj->page_size));
595             }
596         }
597       else
598         return FALSE;
599     }
600
601   
602   /* vertical autoscrolling */
603   for (work = list->children; work; length++, work = work->next)
604     {
605       if (row < 0)
606         {
607           item = GTK_WIDGET (work->data);
608           if (item->allocation.y > y || 
609               (item->allocation.y <= y &&
610                item->allocation.y + item->allocation.height > y))
611             row = length;
612         }
613
614       if (work->data == container->focus_child)
615         focus_row = length;
616     }
617   
618   if (row < 0)
619     row = length - 1;
620
621   if (list->vtimer != 0)
622     return FALSE;
623
624   if (!((y < 0 && focus_row == 0) ||
625         (y > widget->allocation.height && focus_row >= length - 1)))
626     list->vtimer = gtk_timeout_add (SCROLL_TIME,
627                                     (GtkFunction) gtk_list_vertical_timeout,
628                                     list);
629
630   if (row != focus_row)
631     gtk_widget_grab_focus (item);
632
633   switch (list->selection_mode)
634     {
635     case GTK_SELECTION_BROWSE:
636       gtk_list_select_child (list, item);
637       break;
638     case GTK_SELECTION_EXTENDED:
639       gtk_list_update_extended_selection (list, row);
640       break;
641     default:
642       break;
643     }
644
645   return FALSE;
646 }
647
648 static gint
649 gtk_list_button_press (GtkWidget      *widget,
650                        GdkEventButton *event)
651 {
652   GtkList *list;
653   GtkWidget *item;
654
655   g_return_val_if_fail (widget != NULL, FALSE);
656   g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
657   g_return_val_if_fail (event != NULL, FALSE);
658
659   if (event->button != 1)
660     return FALSE;
661
662   list = GTK_LIST (widget);
663   item = gtk_get_event_widget ((GdkEvent*) event);
664
665   while (item && !GTK_IS_LIST_ITEM (item))
666     item = item->parent;
667
668   if (item && (item->parent == widget))
669     {
670       gint last_focus_row;
671       gint focus_row;
672
673       if (event->type == GDK_BUTTON_PRESS)
674         {
675           gtk_grab_add (widget);
676           list->drag_selection = TRUE;
677         }
678       else if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
679         gtk_list_end_drag_selection (list);
680           
681       if (!GTK_WIDGET_HAS_FOCUS(item))
682         gtk_widget_grab_focus (item);
683
684       if (list->add_mode)
685         {
686           list->add_mode = FALSE;
687           gtk_widget_queue_draw (item);
688         }
689       
690       switch (list->selection_mode)
691         {
692         case GTK_SELECTION_SINGLE:
693         case GTK_SELECTION_MULTIPLE:
694           if (event->type != GDK_BUTTON_PRESS)
695             gtk_list_select_child (list, item);
696           else
697             list->undo_focus_child = item;
698           break;
699           
700         case GTK_SELECTION_BROWSE:
701           break;
702
703         case GTK_SELECTION_EXTENDED:
704           focus_row = g_list_index (list->children, item);
705
706           if (list->last_focus_child)
707             last_focus_row = g_list_index (list->children,
708                                            list->last_focus_child);
709           else
710             {
711               last_focus_row = focus_row;
712               list->last_focus_child = item;
713             }
714
715           if (event->type != GDK_BUTTON_PRESS)
716             {
717               if (list->anchor >= 0)
718                 {
719                   gtk_list_update_extended_selection (list, focus_row);
720                   gtk_list_end_selection (list);
721                 }
722               gtk_list_select_child (list, item);
723               break;
724             }
725               
726           if (event->state & GDK_CONTROL_MASK)
727             {
728               if (event->state & GDK_SHIFT_MASK)
729                 {
730                   if (list->anchor < 0)
731                     {
732                       g_list_free (list->undo_selection);
733                       g_list_free (list->undo_unselection);
734                       list->undo_selection = NULL;
735                       list->undo_unselection = NULL;
736
737                       list->anchor = last_focus_row;
738                       list->drag_pos = last_focus_row;
739                       list->undo_focus_child = list->last_focus_child;
740                     }
741                   gtk_list_update_extended_selection (list, focus_row);
742                 }
743               else
744                 {
745                   if (list->anchor < 0)
746                     gtk_list_set_anchor (list, TRUE,
747                                          focus_row, list->last_focus_child);
748                   else
749                     gtk_list_update_extended_selection (list, focus_row);
750                 }
751               break;
752             }
753
754           if (event->state & GDK_SHIFT_MASK)
755             {
756               gtk_list_set_anchor (list, FALSE,
757                                    last_focus_row, list->last_focus_child);
758               gtk_list_update_extended_selection (list, focus_row);
759               break;
760             }
761
762           if (list->anchor < 0)
763             gtk_list_set_anchor (list, FALSE, focus_row,
764                                  list->last_focus_child);
765           else
766             gtk_list_update_extended_selection (list, focus_row);
767           break;
768           
769         default:
770           break;
771         }
772
773       return TRUE;
774     }
775
776   return FALSE;
777 }
778
779 static gint
780 gtk_list_button_release (GtkWidget      *widget,
781                          GdkEventButton *event)
782 {
783   GtkList *list;
784   GtkWidget *item;
785
786   g_return_val_if_fail (widget != NULL, FALSE);
787   g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
788   g_return_val_if_fail (event != NULL, FALSE);
789
790   list = GTK_LIST (widget);
791
792   /* we don't handle button 2 and 3 */
793   if (event->button != 1)
794     return FALSE;
795
796   if (list->drag_selection)
797     {
798       gtk_list_end_drag_selection (list);
799
800       switch (list->selection_mode)
801         {
802         case GTK_SELECTION_EXTENDED:
803           if (!(event->state & GDK_SHIFT_MASK))
804             gtk_list_end_selection (list);
805           break;
806
807         case GTK_SELECTION_SINGLE:
808         case GTK_SELECTION_MULTIPLE:
809
810           item = gtk_get_event_widget ((GdkEvent*) event);
811   
812           while (item && !GTK_IS_LIST_ITEM (item))
813             item = item->parent;
814           
815           if (item && item->parent == widget)
816             {
817               if (list->undo_focus_child == item)
818                 gtk_list_toggle_row (list, item);
819             }
820           list->undo_focus_child = NULL;
821           break;
822
823         default:
824           break;
825         }
826
827       return TRUE;
828     }
829   
830   return FALSE;
831 }
832
833 static void 
834 gtk_list_style_set      (GtkWidget      *widget,
835                          GtkStyle       *previous_style)
836 {
837   g_return_if_fail (widget != NULL);
838
839   if (previous_style && GTK_WIDGET_REALIZED (widget))
840     gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
841 }
842
843 /* GtkContainer Methods :
844  * gtk_list_add
845  * gtk_list_remove
846  * gtk_list_forall
847  * gtk_list_child_type
848  * gtk_list_set_focus_child
849  * gtk_list_focus
850  */
851 static void
852 gtk_list_add (GtkContainer *container,
853               GtkWidget    *widget)
854 {
855   GList *item_list;
856
857   g_return_if_fail (container != NULL);
858   g_return_if_fail (GTK_IS_LIST (container));
859   g_return_if_fail (widget != NULL);
860   g_return_if_fail (GTK_IS_LIST_ITEM (widget));
861
862   item_list = g_list_alloc ();
863   item_list->data = widget;
864   
865   gtk_list_append_items (GTK_LIST (container), item_list);
866 }
867
868 static void
869 gtk_list_remove (GtkContainer *container,
870                  GtkWidget    *widget)
871 {
872   GList *item_list;
873   
874   g_return_if_fail (container != NULL);
875   g_return_if_fail (GTK_IS_LIST (container));
876   g_return_if_fail (widget != NULL);
877   g_return_if_fail (container == GTK_CONTAINER (widget->parent));
878   
879   item_list = g_list_alloc ();
880   item_list->data = widget;
881   
882   gtk_list_remove_items (GTK_LIST (container), item_list);
883   
884   g_list_free (item_list);
885 }
886
887 static void
888 gtk_list_forall (GtkContainer  *container,
889                  gboolean       include_internals,
890                  GtkCallback    callback,
891                  gpointer       callback_data)
892 {
893   GtkList *list;
894   GtkWidget *child;
895   GList *children;
896
897   g_return_if_fail (container != NULL);
898   g_return_if_fail (GTK_IS_LIST (container));
899   g_return_if_fail (callback != NULL);
900
901   list = GTK_LIST (container);
902   children = list->children;
903
904   while (children)
905     {
906       child = children->data;
907       children = children->next;
908
909       (* callback) (child, callback_data);
910     }
911 }
912
913 static GtkType
914 gtk_list_child_type (GtkContainer *container)
915 {
916   return GTK_TYPE_LIST_ITEM;
917 }
918
919 static void
920 gtk_list_set_focus_child (GtkContainer *container,
921                           GtkWidget    *child)
922 {
923   GtkList *list;
924
925   g_return_if_fail (container != NULL);
926   g_return_if_fail (GTK_IS_LIST (container));
927  
928   if (child)
929     g_return_if_fail (GTK_IS_WIDGET (child));
930
931   list = GTK_LIST (container);
932
933   if (child != container->focus_child)
934     {
935       if (container->focus_child)
936         {
937           list->last_focus_child = container->focus_child;
938           gtk_widget_unref (container->focus_child);
939         }
940       container->focus_child = child;
941       if (container->focus_child)
942         gtk_widget_ref (container->focus_child);
943     }
944
945   /* check for v adjustment */
946   if (container->focus_child)
947     {
948       GtkAdjustment *adjustment;
949
950       adjustment = gtk_object_get_data_by_id (GTK_OBJECT (container),
951                                               vadjustment_key_id);
952       if (adjustment)
953         gtk_adjustment_clamp_page (adjustment,
954                                    container->focus_child->allocation.y,
955                                    (container->focus_child->allocation.y +
956                                     container->focus_child->allocation.height));
957       switch (list->selection_mode)
958         {
959         case GTK_SELECTION_BROWSE:
960           gtk_list_select_child (list, child);
961           break;
962         case GTK_SELECTION_EXTENDED:
963           if (!list->last_focus_child && !list->add_mode)
964             {
965               list->undo_focus_child = list->last_focus_child;
966               gtk_list_unselect_all (list);
967               gtk_list_select_child (list, child);
968             }
969           break;
970         default:
971           break;
972         }
973     }
974 }
975
976 static gint
977 gtk_list_focus (GtkContainer     *container,
978                 GtkDirectionType  direction)
979 {
980   gint return_val = FALSE;
981
982   g_return_val_if_fail (container != NULL, FALSE);
983   g_return_val_if_fail (GTK_IS_LIST (container), FALSE);
984
985   if (container->focus_child == NULL ||
986       !GTK_WIDGET_HAS_FOCUS (container->focus_child))
987     {
988       if (GTK_LIST (container)->last_focus_child)
989         gtk_container_set_focus_child
990           (container, GTK_LIST (container)->last_focus_child);
991
992       if (GTK_CONTAINER_CLASS (parent_class)->focus)
993         return_val = GTK_CONTAINER_CLASS (parent_class)->focus (container,
994                                                                 direction);
995     }
996
997   if (!return_val)
998     {
999       GtkList *list;
1000
1001       list =  GTK_LIST (container);
1002       if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
1003         gtk_list_end_selection (list);
1004
1005       if (container->focus_child)
1006         list->last_focus_child = container->focus_child;
1007     }
1008
1009   return return_val;
1010 }
1011
1012
1013 /* Public GtkList Methods :
1014  *
1015  * gtk_list_insert_items
1016  * gtk_list_append_items
1017  * gtk_list_prepend_items
1018  * gtk_list_remove_items
1019  * gtk_list_remove_items_no_unref
1020  * gtk_list_clear_items
1021  *
1022  * gtk_list_child_position
1023  */
1024 void
1025 gtk_list_insert_items (GtkList *list,
1026                        GList   *items,
1027                        gint     position)
1028 {
1029   GtkWidget *widget;
1030   GList *tmp_list;
1031   GList *last;
1032   gint nchildren;
1033
1034   g_return_if_fail (list != NULL);
1035   g_return_if_fail (GTK_IS_LIST (list));
1036
1037   if (!items)
1038     return;
1039
1040   gtk_list_end_drag_selection (list);
1041   if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
1042     gtk_list_end_selection (list);
1043
1044   tmp_list = items;
1045   while (tmp_list)
1046     {
1047       widget = tmp_list->data;
1048       tmp_list = tmp_list->next;
1049
1050       gtk_widget_set_parent (widget, GTK_WIDGET (list));
1051       gtk_signal_connect (GTK_OBJECT (widget), "drag_begin",
1052                           GTK_SIGNAL_FUNC (gtk_list_signal_drag_begin),
1053                           list);
1054       gtk_signal_connect (GTK_OBJECT (widget), "toggle_focus_row",
1055                           GTK_SIGNAL_FUNC (gtk_list_signal_toggle_focus_row),
1056                           list);
1057       gtk_signal_connect (GTK_OBJECT (widget), "select_all",
1058                           GTK_SIGNAL_FUNC (gtk_list_signal_select_all),
1059                           list);
1060       gtk_signal_connect (GTK_OBJECT (widget), "unselect_all",
1061                           GTK_SIGNAL_FUNC (gtk_list_signal_unselect_all),
1062                           list);
1063       gtk_signal_connect (GTK_OBJECT (widget), "undo_selection",
1064                           GTK_SIGNAL_FUNC (gtk_list_signal_undo_selection),
1065                           list);
1066       gtk_signal_connect (GTK_OBJECT (widget), "start_selection",
1067                           GTK_SIGNAL_FUNC (gtk_list_signal_start_selection),
1068                           list);
1069       gtk_signal_connect (GTK_OBJECT (widget), "end_selection",
1070                           GTK_SIGNAL_FUNC (gtk_list_signal_end_selection),
1071                           list);
1072       gtk_signal_connect (GTK_OBJECT (widget), "extend_selection",
1073                           GTK_SIGNAL_FUNC (gtk_list_signal_extend_selection),
1074                           list);
1075       gtk_signal_connect (GTK_OBJECT (widget), "scroll_horizontal",
1076                           GTK_SIGNAL_FUNC (gtk_list_signal_scroll_horizontal),
1077                           list);
1078       gtk_signal_connect (GTK_OBJECT (widget), "scroll_vertical",
1079                           GTK_SIGNAL_FUNC (gtk_list_signal_scroll_vertical),
1080                           list);
1081       gtk_signal_connect (GTK_OBJECT (widget), "toggle_add_mode",
1082                           GTK_SIGNAL_FUNC (gtk_list_signal_toggle_add_mode),
1083                           list);
1084       gtk_signal_connect (GTK_OBJECT (widget), "select",
1085                           GTK_SIGNAL_FUNC (gtk_list_signal_item_select),
1086                           list);
1087       gtk_signal_connect (GTK_OBJECT (widget), "deselect",
1088                           GTK_SIGNAL_FUNC (gtk_list_signal_item_deselect),
1089                           list);
1090       gtk_signal_connect (GTK_OBJECT (widget), "toggle",
1091                           GTK_SIGNAL_FUNC (gtk_list_signal_item_toggle),
1092                           list);
1093
1094       if (GTK_WIDGET_REALIZED (widget->parent))
1095         gtk_widget_realize (widget);
1096
1097       if (GTK_WIDGET_VISIBLE (widget->parent) && GTK_WIDGET_VISIBLE (widget))
1098         {
1099           if (GTK_WIDGET_MAPPED (widget->parent))
1100             gtk_widget_map (widget);
1101
1102           gtk_widget_queue_resize (widget);
1103         }
1104     }
1105
1106
1107   nchildren = g_list_length (list->children);
1108   if ((position < 0) || (position > nchildren))
1109     position = nchildren;
1110
1111   if (position == nchildren)
1112     {
1113       if (list->children)
1114         {
1115           tmp_list = g_list_last (list->children);
1116           tmp_list->next = items;
1117           items->prev = tmp_list;
1118         }
1119       else
1120         {
1121           list->children = items;
1122         }
1123     }
1124   else
1125     {
1126       tmp_list = g_list_nth (list->children, position);
1127       last = g_list_last (items);
1128
1129       if (tmp_list->prev)
1130         tmp_list->prev->next = items;
1131       last->next = tmp_list;
1132       items->prev = tmp_list->prev;
1133       tmp_list->prev = last;
1134
1135       if (tmp_list == list->children)
1136         list->children = items;
1137     }
1138   
1139   if (list->children && !list->selection &&
1140       (list->selection_mode == GTK_SELECTION_BROWSE))
1141     {
1142       widget = list->children->data;
1143       gtk_list_select_child (list, widget);
1144     }
1145 }
1146
1147 void
1148 gtk_list_append_items (GtkList *list,
1149                        GList   *items)
1150 {
1151   g_return_if_fail (list != NULL);
1152   g_return_if_fail (GTK_IS_LIST (list));
1153
1154   gtk_list_insert_items (list, items, -1);
1155 }
1156
1157 void
1158 gtk_list_prepend_items (GtkList *list,
1159                         GList   *items)
1160 {
1161   g_return_if_fail (list != NULL);
1162   g_return_if_fail (GTK_IS_LIST (list));
1163
1164   gtk_list_insert_items (list, items, 0);
1165 }
1166
1167 void
1168 gtk_list_remove_items (GtkList  *list,
1169                        GList    *items)
1170 {
1171   gtk_list_remove_items_internal (list, items, FALSE);
1172 }
1173
1174 void
1175 gtk_list_remove_items_no_unref (GtkList  *list,
1176                                 GList    *items)
1177 {
1178   gtk_list_remove_items_internal (list, items, TRUE);
1179 }
1180
1181 void
1182 gtk_list_clear_items (GtkList *list,
1183                       gint     start,
1184                       gint     end)
1185 {
1186   GtkContainer *container;
1187   GtkWidget *widget;
1188   GtkWidget *new_focus_child = NULL;
1189   GList *start_list;
1190   GList *end_list;
1191   GList *tmp_list;
1192   guint nchildren;
1193   gboolean grab_focus = FALSE;
1194
1195   g_return_if_fail (list != NULL);
1196   g_return_if_fail (GTK_IS_LIST (list));
1197
1198   nchildren = g_list_length (list->children);
1199
1200   if (nchildren == 0)
1201     return;
1202
1203   if ((end < 0) || (end > nchildren))
1204     end = nchildren;
1205
1206   if (start >= end)
1207     return;
1208
1209   container = GTK_CONTAINER (list);
1210
1211   gtk_list_end_drag_selection (list);
1212   if (list->selection_mode == GTK_SELECTION_EXTENDED)
1213     {
1214       if (list->anchor >= 0)
1215         gtk_list_end_selection (list);
1216
1217       gtk_list_reset_extended_selection (list);
1218     }
1219
1220   start_list = g_list_nth (list->children, start);
1221   end_list = g_list_nth (list->children, end);
1222
1223   if (start_list->prev)
1224     start_list->prev->next = end_list;
1225   if (end_list && end_list->prev)
1226     end_list->prev->next = NULL;
1227   if (end_list)
1228     end_list->prev = start_list->prev;
1229   if (start_list == list->children)
1230     list->children = end_list;
1231
1232   if (container->focus_child)
1233     {
1234       if (g_list_find (start_list, container->focus_child))
1235         {
1236           if (start_list->prev)
1237             new_focus_child = start_list->prev->data;
1238           else if (list->children)
1239             new_focus_child = list->children->prev->data;
1240
1241           if (GTK_WIDGET_HAS_FOCUS (container->focus_child))
1242             grab_focus = TRUE;
1243         }
1244     }
1245
1246   tmp_list = start_list;
1247   while (tmp_list)
1248     {
1249       widget = tmp_list->data;
1250       tmp_list = tmp_list->next;
1251
1252       if (widget->state == GTK_STATE_SELECTED)
1253         gtk_list_unselect_child (list, widget);
1254
1255       if (widget == list->undo_focus_child)
1256         list->undo_focus_child = NULL;
1257       if (widget == list->last_focus_child)
1258         list->last_focus_child = NULL;
1259
1260       gtk_signal_disconnect_by_data (GTK_OBJECT (widget), (gpointer) list);
1261       gtk_widget_unparent (widget);
1262     }
1263
1264   g_list_free (start_list);
1265
1266   if (new_focus_child)
1267     {
1268       if (grab_focus)
1269         gtk_widget_grab_focus (new_focus_child);
1270       else if (container->focus_child)
1271         gtk_container_set_focus_child (container, new_focus_child);
1272
1273       if ((list->selection_mode == GTK_SELECTION_BROWSE ||
1274            list->selection_mode == GTK_SELECTION_EXTENDED) && !list->selection)
1275         {
1276           list->last_focus_child = new_focus_child; 
1277           gtk_list_select_child (list, new_focus_child);
1278         }
1279     }
1280
1281   if (GTK_WIDGET_VISIBLE (list))
1282     gtk_widget_queue_resize (GTK_WIDGET (list));
1283 }
1284
1285 gint
1286 gtk_list_child_position (GtkList   *list,
1287                          GtkWidget *child)
1288 {
1289   GList *children;
1290   gint pos;
1291
1292   g_return_val_if_fail (list != NULL, -1);
1293   g_return_val_if_fail (GTK_IS_LIST (list), -1);
1294   g_return_val_if_fail (child != NULL, -1);
1295
1296   pos = 0;
1297   children = list->children;
1298
1299   while (children)
1300     {
1301       if (child == GTK_WIDGET (children->data))
1302         return pos;
1303
1304       pos += 1;
1305       children = children->next;
1306     }
1307
1308   return -1;
1309 }
1310
1311
1312 /* Private GtkList Insert/Remove Item Functions:
1313  *
1314  * gtk_list_remove_items_internal
1315  */
1316 static void
1317 gtk_list_remove_items_internal (GtkList  *list,
1318                                 GList    *items,
1319                                 gboolean  no_unref)
1320 {
1321   GtkWidget *widget;
1322   GtkWidget *new_focus_child;
1323   GtkWidget *old_focus_child;
1324   GtkContainer *container;
1325   GList *tmp_list;
1326   GList *work;
1327   gboolean grab_focus = FALSE;
1328   
1329   g_return_if_fail (list != NULL);
1330   g_return_if_fail (GTK_IS_LIST (list));
1331
1332   if (!items)
1333     return;
1334   
1335   container = GTK_CONTAINER (list);
1336
1337   gtk_list_end_drag_selection (list);
1338   if (list->selection_mode == GTK_SELECTION_EXTENDED)
1339     {
1340       if (list->anchor >= 0)
1341         gtk_list_end_selection (list);
1342
1343       gtk_list_reset_extended_selection (list);
1344     }
1345
1346   tmp_list = items;
1347   while (tmp_list)
1348     {
1349       widget = tmp_list->data;
1350       tmp_list = tmp_list->next;
1351       
1352       if (widget->state == GTK_STATE_SELECTED)
1353         gtk_list_unselect_child (list, widget);
1354     }
1355
1356   if (container->focus_child)
1357     {
1358       old_focus_child = new_focus_child = container->focus_child;
1359       if (GTK_WIDGET_HAS_FOCUS (container->focus_child))
1360         grab_focus = TRUE;
1361     }
1362   else
1363     old_focus_child = new_focus_child = list->last_focus_child;
1364
1365   tmp_list = items;
1366   while (tmp_list)
1367     {
1368       widget = tmp_list->data;
1369       tmp_list = tmp_list->next;
1370
1371       if (no_unref)
1372         gtk_widget_ref (widget);
1373
1374       if (widget == new_focus_child) 
1375         {
1376           work = g_list_find (list->children, widget);
1377
1378           if (work)
1379             {
1380               if (work->next)
1381                 new_focus_child = work->next->data;
1382               else if (list->children != work && work->prev)
1383                 new_focus_child = work->prev->data;
1384               else
1385                 new_focus_child = NULL;
1386             }
1387         }
1388
1389       if (widget == list->undo_focus_child)
1390         list->undo_focus_child = NULL;
1391       if (widget == list->last_focus_child)
1392         list->last_focus_child = NULL;
1393
1394       gtk_signal_disconnect_by_data (GTK_OBJECT (widget), (gpointer) list);
1395       list->children = g_list_remove (list->children, widget);
1396       gtk_widget_unparent (widget);
1397     }
1398   
1399   if (new_focus_child && new_focus_child != old_focus_child)
1400     {
1401       if (grab_focus)
1402         gtk_widget_grab_focus (new_focus_child);
1403       else if (container->focus_child)
1404         gtk_container_set_focus_child (container, new_focus_child);
1405
1406       if (list->selection_mode == GTK_SELECTION_BROWSE && !list->selection)
1407         {
1408           list->last_focus_child = new_focus_child; 
1409           gtk_list_select_child (list, new_focus_child);
1410         }
1411     }
1412
1413   if (GTK_WIDGET_VISIBLE (list))
1414     gtk_widget_queue_resize (GTK_WIDGET (list));
1415 }
1416
1417
1418 /* Public GtkList Selection Methods :
1419  *
1420  * gtk_list_set_selection_mode
1421  * gtk_list_select_item
1422  * gtk_list_unselect_item
1423  * gtk_list_select_child
1424  * gtk_list_unselect_child
1425  * gtk_list_select_all
1426  * gtk_list_unselect_all
1427  * gtk_list_extend_selection
1428  * gtk_list_end_drag_selection
1429  * gtk_list_start_selection
1430  * gtk_list_end_selection
1431  * gtk_list_toggle_row
1432  * gtk_list_toggle_focus_row
1433  * gtk_list_toggle_add_mode
1434  * gtk_list_undo_selection
1435  */
1436 void
1437 gtk_list_set_selection_mode (GtkList          *list,
1438                              GtkSelectionMode  mode)
1439 {
1440   g_return_if_fail (list != NULL);
1441   g_return_if_fail (GTK_IS_LIST (list));
1442
1443   if (list->selection_mode == mode)
1444     return;
1445
1446   list->selection_mode = mode;
1447
1448   switch (mode)
1449     {
1450     case GTK_SELECTION_SINGLE:
1451     case GTK_SELECTION_BROWSE:
1452       gtk_list_unselect_all (list);
1453       break;
1454     default:
1455       break;
1456     }
1457 }
1458
1459 void
1460 gtk_list_select_item (GtkList *list,
1461                       gint     item)
1462 {
1463   GList *tmp_list;
1464
1465   g_return_if_fail (list != NULL);
1466   g_return_if_fail (GTK_IS_LIST (list));
1467
1468   tmp_list = g_list_nth (list->children, item);
1469   if (tmp_list)
1470     gtk_list_select_child (list, GTK_WIDGET (tmp_list->data));
1471 }
1472
1473 void
1474 gtk_list_unselect_item (GtkList *list,
1475                         gint     item)
1476 {
1477   GList *tmp_list;
1478
1479   g_return_if_fail (list != NULL);
1480   g_return_if_fail (GTK_IS_LIST (list));
1481
1482   tmp_list = g_list_nth (list->children, item);
1483   if (tmp_list)
1484     gtk_list_unselect_child (list, GTK_WIDGET (tmp_list->data));
1485 }
1486
1487 void
1488 gtk_list_select_child (GtkList   *list,
1489                        GtkWidget *child)
1490 {
1491   gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECT_CHILD], child);
1492 }
1493
1494 void
1495 gtk_list_unselect_child (GtkList   *list,
1496                          GtkWidget *child)
1497 {
1498   gtk_signal_emit (GTK_OBJECT (list), list_signals[UNSELECT_CHILD], child);
1499 }
1500
1501 void
1502 gtk_list_select_all (GtkList *list)
1503 {
1504   GtkContainer *container;
1505   GList *work;
1506  
1507   g_return_if_fail (list != NULL);
1508   g_return_if_fail (GTK_IS_LIST (list));
1509
1510   if (!list->children)
1511     return;
1512   
1513   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
1514     gtk_list_end_drag_selection (list);
1515
1516   if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
1517     gtk_list_end_selection (list);
1518
1519   container = GTK_CONTAINER (list);
1520
1521   switch (list->selection_mode)
1522     {
1523     case GTK_SELECTION_BROWSE:
1524       if (container->focus_child)
1525         {
1526           gtk_list_select_child (list, container->focus_child);
1527           return;
1528         }
1529       break;
1530     case GTK_SELECTION_EXTENDED:
1531       g_list_free (list->undo_selection);
1532       g_list_free (list->undo_unselection);
1533       list->undo_selection = NULL;
1534       list->undo_unselection = NULL;
1535
1536       if (list->children &&
1537           GTK_WIDGET_STATE (list->children->data) != GTK_STATE_SELECTED)
1538         gtk_list_fake_toggle_row (list, GTK_WIDGET (list->children->data));
1539
1540       list->anchor_state =  GTK_STATE_SELECTED;
1541       list->anchor = 0;
1542       list->drag_pos = 0;
1543       list->undo_focus_child = container->focus_child;
1544       gtk_list_update_extended_selection (list, g_list_length(list->children));
1545       gtk_list_end_selection (list);
1546       return;
1547     case GTK_SELECTION_MULTIPLE:
1548       for (work = list->children; work; work = work->next)
1549         {
1550           if (GTK_WIDGET_STATE (work->data) == GTK_STATE_NORMAL)
1551             gtk_list_select_child (list, GTK_WIDGET (work->data));
1552         }
1553       return;
1554     default:
1555       break;
1556     }
1557 }
1558
1559 void
1560 gtk_list_unselect_all (GtkList *list)
1561 {
1562   GtkContainer *container;
1563   GtkWidget *item;
1564   GList *work;
1565  
1566   g_return_if_fail (list != NULL);
1567   g_return_if_fail (GTK_IS_LIST (list));
1568
1569   if (!list->children)
1570     return;
1571
1572   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
1573     gtk_list_end_drag_selection (list);
1574
1575   if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
1576     gtk_list_end_selection (list);
1577
1578   container = GTK_CONTAINER (list);
1579
1580   switch (list->selection_mode)
1581     {
1582     case GTK_SELECTION_BROWSE:
1583       if (container->focus_child)
1584         {
1585           gtk_list_select_child (list, container->focus_child);
1586           return;
1587         }
1588       break;
1589     case GTK_SELECTION_EXTENDED:
1590       gtk_list_reset_extended_selection (list);
1591       break;
1592     default:
1593       break;
1594     }
1595
1596   work = list->selection;
1597
1598   while (work)
1599     {
1600       item = work->data;
1601       work = work->next;
1602       gtk_list_unselect_child (list, item);
1603     }
1604 }
1605
1606 void
1607 gtk_list_extend_selection (GtkList       *list,
1608                            GtkScrollType  scroll_type,
1609                            gfloat         position,
1610                            gboolean       auto_start_selection)
1611 {
1612   GtkContainer *container;
1613
1614   g_return_if_fail (list != NULL);
1615   g_return_if_fail (GTK_IS_LIST (list));
1616
1617   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
1618       list->selection_mode != GTK_SELECTION_EXTENDED)
1619     return;
1620
1621   container = GTK_CONTAINER (list);
1622
1623   if (auto_start_selection)
1624     {
1625       gint focus_row;
1626
1627       focus_row = g_list_index (list->children, container->focus_child);
1628       gtk_list_set_anchor (list, list->add_mode, focus_row,
1629                            container->focus_child);
1630     }
1631   else if (list->anchor < 0)
1632     return;
1633
1634   gtk_list_move_focus_child (list, scroll_type, position);
1635   gtk_list_update_extended_selection 
1636     (list, g_list_index (list->children, container->focus_child));
1637 }
1638
1639 void
1640 gtk_list_end_drag_selection (GtkList *list)
1641 {
1642   g_return_if_fail (list != NULL);
1643   g_return_if_fail (GTK_IS_LIST (list));
1644
1645   list->drag_selection = FALSE;
1646   if (GTK_WIDGET_HAS_GRAB (list))
1647     gtk_grab_remove (GTK_WIDGET (list));
1648
1649   if (list->htimer)
1650     {
1651       gtk_timeout_remove (list->htimer);
1652       list->htimer = 0;
1653     }
1654   if (list->vtimer)
1655     {
1656       gtk_timeout_remove (list->vtimer);
1657       list->vtimer = 0;
1658     }
1659 }
1660
1661 void
1662 gtk_list_start_selection (GtkList *list)
1663 {
1664   GtkContainer *container;
1665   gint focus_row;
1666
1667   g_return_if_fail (list != NULL);
1668   g_return_if_fail (GTK_IS_LIST (list));
1669
1670   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
1671     return;
1672
1673   container = GTK_CONTAINER (list);
1674
1675   if ((focus_row = g_list_index (list->selection, container->focus_child))
1676       >= 0)
1677     gtk_list_set_anchor (list, list->add_mode,
1678                          focus_row, container->focus_child);
1679 }
1680
1681 void
1682 gtk_list_end_selection (GtkList *list)
1683 {
1684   gint i;
1685   gint e;
1686   gboolean top_down;
1687   GList *work;
1688   GtkWidget *item;
1689   gint item_index;
1690
1691   g_return_if_fail (list != NULL);
1692   g_return_if_fail (GTK_IS_LIST (list));
1693
1694   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
1695       list->anchor < 0)
1696     return;
1697
1698   i = MIN (list->anchor, list->drag_pos);
1699   e = MAX (list->anchor, list->drag_pos);
1700
1701   top_down = (list->anchor < list->drag_pos);
1702
1703   list->anchor = -1;
1704   list->drag_pos = -1;
1705   
1706   if (list->undo_selection)
1707     {
1708       work = list->selection;
1709       list->selection = list->undo_selection;
1710       list->undo_selection = work;
1711       work = list->selection;
1712       while (work)
1713         {
1714           item = work->data;
1715           work = work->next;
1716           item_index = g_list_index (list->children, item);
1717           if (item_index < i || item_index > e)
1718             {
1719               gtk_widget_set_state (item, GTK_STATE_SELECTED);
1720               gtk_list_unselect_child (list, item);
1721               list->undo_selection = g_list_prepend (list->undo_selection,
1722                                                      item);
1723             }
1724         }
1725     }    
1726
1727   if (top_down)
1728     {
1729       for (work = g_list_nth (list->children, i); i <= e;
1730            i++, work = work->next)
1731         {
1732           item = work->data;
1733           if (g_list_find (list->selection, item))
1734             {
1735               if (item->state == GTK_STATE_NORMAL)
1736                 {
1737                   gtk_widget_set_state (item, GTK_STATE_SELECTED);
1738                   gtk_list_unselect_child (list, item);
1739                   list->undo_selection = g_list_prepend (list->undo_selection,
1740                                                          item);
1741                 }
1742             }
1743           else if (item->state == GTK_STATE_SELECTED)
1744             {
1745               gtk_widget_set_state (item, GTK_STATE_NORMAL);
1746               list->undo_unselection = g_list_prepend (list->undo_unselection,
1747                                                        item);
1748             }
1749         }
1750     }
1751   else
1752     {
1753       for (work = g_list_nth (list->children, e); i <= e;
1754            e--, work = work->prev)
1755         {
1756           item = work->data;
1757           if (g_list_find (list->selection, item))
1758             {
1759               if (item->state == GTK_STATE_NORMAL)
1760                 {
1761                   gtk_widget_set_state (item, GTK_STATE_SELECTED);
1762                   gtk_list_unselect_child (list, item);
1763                   list->undo_selection = g_list_prepend (list->undo_selection,
1764                                                          item);
1765                 }
1766             }
1767           else if (item->state == GTK_STATE_SELECTED)
1768             {
1769               gtk_widget_set_state (item, GTK_STATE_NORMAL);
1770               list->undo_unselection = g_list_prepend (list->undo_unselection,
1771                                                        item);
1772             }
1773         }
1774     }
1775
1776   for (work = g_list_reverse (list->undo_unselection); work; work = work->next)
1777     gtk_list_select_child (list, GTK_WIDGET (work->data));
1778
1779
1780 }
1781
1782 void
1783 gtk_list_toggle_row (GtkList   *list,
1784                      GtkWidget *item)
1785 {
1786   g_return_if_fail (list != NULL);
1787   g_return_if_fail (GTK_IS_LIST (list));
1788   g_return_if_fail (item != NULL);
1789   g_return_if_fail (GTK_IS_LIST_ITEM (item));
1790
1791   switch (list->selection_mode)
1792     {
1793     case GTK_SELECTION_EXTENDED:
1794     case GTK_SELECTION_MULTIPLE:
1795     case GTK_SELECTION_SINGLE:
1796       if (item->state == GTK_STATE_SELECTED)
1797         {
1798           gtk_list_unselect_child (list, item);
1799           return;
1800         }
1801     case GTK_SELECTION_BROWSE:
1802       gtk_list_select_child (list, item);
1803       break;
1804     }
1805 }
1806
1807 void
1808 gtk_list_toggle_focus_row (GtkList *list)
1809 {
1810   GtkContainer *container;
1811   gint focus_row;
1812
1813   g_return_if_fail (list != 0);
1814   g_return_if_fail (GTK_IS_LIST (list));
1815
1816   container = GTK_CONTAINER (list);
1817
1818   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
1819       !container->focus_child)
1820     return;
1821
1822   switch (list->selection_mode)
1823     {
1824     case  GTK_SELECTION_SINGLE:
1825     case  GTK_SELECTION_MULTIPLE:
1826       gtk_list_toggle_row (list, container->focus_child);
1827       break;
1828     case GTK_SELECTION_EXTENDED:
1829       if ((focus_row = g_list_index (list->children, container->focus_child))
1830           < 0)
1831         return;
1832
1833       g_list_free (list->undo_selection);
1834       g_list_free (list->undo_unselection);
1835       list->undo_selection = NULL;
1836       list->undo_unselection = NULL;
1837
1838       list->anchor = focus_row;
1839       list->drag_pos = focus_row;
1840       list->undo_focus_child = container->focus_child;
1841
1842       if (list->add_mode)
1843         gtk_list_fake_toggle_row (list, container->focus_child);
1844       else
1845         gtk_list_fake_unselect_all (list, container->focus_child);
1846       
1847       gtk_list_end_selection (list);
1848       break;
1849     default:
1850       break;
1851     }
1852 }
1853
1854 void
1855 gtk_list_toggle_add_mode (GtkList *list)
1856 {
1857   GtkContainer *container;
1858
1859   g_return_if_fail (list != 0);
1860   g_return_if_fail (GTK_IS_LIST (list));
1861   
1862   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
1863       list->selection_mode != GTK_SELECTION_EXTENDED)
1864     return;
1865   
1866   container = GTK_CONTAINER (list);
1867
1868   if (list->add_mode)
1869     {
1870       list->add_mode = FALSE;
1871       list->anchor_state = GTK_STATE_SELECTED;
1872     }
1873   else
1874     list->add_mode = TRUE;
1875   
1876   if (container->focus_child)
1877     gtk_widget_queue_draw (container->focus_child);
1878 }
1879
1880 void
1881 gtk_list_undo_selection (GtkList *list)
1882 {
1883   GList *work;
1884
1885   g_return_if_fail (list != NULL);
1886   g_return_if_fail (GTK_IS_LIST (list));
1887
1888   if (list->selection_mode != GTK_SELECTION_EXTENDED ||
1889       (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)))
1890     return;
1891   
1892   if (list->anchor >= 0)
1893     gtk_list_end_selection (list);
1894
1895   if (!(list->undo_selection || list->undo_unselection))
1896     {
1897       gtk_list_unselect_all (list);
1898       return;
1899     }
1900
1901   for (work = list->undo_selection; work; work = work->next)
1902     gtk_list_select_child (list, GTK_WIDGET (work->data));
1903
1904   for (work = list->undo_unselection; work; work = work->next)
1905     gtk_list_unselect_child (list, GTK_WIDGET (work->data));
1906
1907   if (list->undo_focus_child)
1908     {
1909       GtkContainer *container;
1910
1911       container = GTK_CONTAINER (list);
1912
1913       if (container->focus_child &&
1914           GTK_WIDGET_HAS_FOCUS (container->focus_child))
1915         gtk_widget_grab_focus (list->undo_focus_child);
1916       else
1917         gtk_container_set_focus_child (container, list->undo_focus_child);
1918     }
1919
1920   list->undo_focus_child = NULL;
1921  
1922   g_list_free (list->undo_selection);
1923   g_list_free (list->undo_unselection);
1924   list->undo_selection = NULL;
1925   list->undo_unselection = NULL;
1926 }
1927
1928
1929 /* Private GtkList Selection Methods :
1930  *
1931  * gtk_real_list_select_child
1932  * gtk_real_list_unselect_child
1933  */
1934 static void
1935 gtk_real_list_select_child (GtkList   *list,
1936                             GtkWidget *child)
1937 {
1938   g_return_if_fail (list != NULL);
1939   g_return_if_fail (GTK_IS_LIST (list));
1940   g_return_if_fail (child != NULL);
1941   g_return_if_fail (GTK_IS_LIST_ITEM (child));
1942
1943   switch (child->state)
1944     {
1945     case GTK_STATE_SELECTED:
1946     case GTK_STATE_INSENSITIVE:
1947       break;
1948     default:
1949       gtk_list_item_select (GTK_LIST_ITEM (child));
1950       break;
1951     }
1952 }
1953
1954 static void
1955 gtk_real_list_unselect_child (GtkList   *list,
1956                               GtkWidget *child)
1957 {
1958   g_return_if_fail (list != NULL);
1959   g_return_if_fail (GTK_IS_LIST (list));
1960   g_return_if_fail (child != NULL);
1961   g_return_if_fail (GTK_IS_LIST_ITEM (child));
1962
1963   if (child->state == GTK_STATE_SELECTED)
1964     gtk_list_item_deselect (GTK_LIST_ITEM (child));
1965 }
1966
1967
1968 /* Private GtkList Selection Functions :
1969  *
1970  * gtk_list_set_anchor
1971  * gtk_list_fake_unselect_all
1972  * gtk_list_fake_toggle_row
1973  * gtk_list_update_extended_selection
1974  * gtk_list_reset_extended_selection
1975  */
1976 static void
1977 gtk_list_set_anchor (GtkList   *list,
1978                      gboolean   add_mode,
1979                      gint       anchor,
1980                      GtkWidget *undo_focus_child)
1981 {
1982   GList *work;
1983
1984   g_return_if_fail (list != NULL);
1985   g_return_if_fail (GTK_IS_LIST (list));
1986   
1987   if (list->selection_mode != GTK_SELECTION_EXTENDED || list->anchor >= 0)
1988     return;
1989
1990   g_list_free (list->undo_selection);
1991   g_list_free (list->undo_unselection);
1992   list->undo_selection = NULL;
1993   list->undo_unselection = NULL;
1994
1995   if ((work = g_list_nth (list->children, anchor)))
1996     {
1997       if (add_mode)
1998         gtk_list_fake_toggle_row (list, GTK_WIDGET (work->data));
1999       else
2000         {
2001           gtk_list_fake_unselect_all (list, GTK_WIDGET (work->data));
2002           list->anchor_state = GTK_STATE_SELECTED;
2003         }
2004     }
2005
2006   list->anchor = anchor;
2007   list->drag_pos = anchor;
2008   list->undo_focus_child = undo_focus_child;
2009 }
2010
2011 static void
2012 gtk_list_fake_unselect_all (GtkList   *list,
2013                             GtkWidget *item)
2014 {
2015   GList *work;
2016
2017   if (item && item->state == GTK_STATE_NORMAL)
2018     gtk_widget_set_state (item, GTK_STATE_SELECTED);
2019
2020   list->undo_selection = list->selection;
2021   list->selection = NULL;
2022   
2023   for (work = list->undo_selection; work; work = work->next)
2024     if (work->data != item)
2025       gtk_widget_set_state (GTK_WIDGET (work->data), GTK_STATE_NORMAL);
2026 }
2027
2028 static void
2029 gtk_list_fake_toggle_row (GtkList   *list,
2030                           GtkWidget *item)
2031 {
2032   if (!item)
2033     return;
2034   
2035   if (item->state == GTK_STATE_NORMAL)
2036     {
2037       list->anchor_state = GTK_STATE_SELECTED;
2038       gtk_widget_set_state (item, GTK_STATE_SELECTED);
2039     }
2040   else
2041     {
2042       list->anchor_state = GTK_STATE_NORMAL;
2043       gtk_widget_set_state (item, GTK_STATE_NORMAL);
2044     }
2045 }
2046
2047 static void
2048 gtk_list_update_extended_selection (GtkList *list,
2049                                     gint     row)
2050 {
2051   gint i;
2052   GList *work;
2053   gint s1 = -1;
2054   gint s2 = -1;
2055   gint e1 = -1;
2056   gint e2 = -1;
2057   gint length;
2058
2059   if (row < 0)
2060     row = 0;
2061
2062   length = g_list_length (list->children);
2063   if (row >= length)
2064     row = length - 1;
2065
2066   if (list->selection_mode != GTK_SELECTION_EXTENDED || !list->anchor < 0)
2067     return;
2068
2069   /* extending downwards */
2070   if (row > list->drag_pos && list->anchor <= list->drag_pos)
2071     {
2072       s2 = list->drag_pos + 1;
2073       e2 = row;
2074     }
2075   /* extending upwards */
2076   else if (row < list->drag_pos && list->anchor >= list->drag_pos)
2077     {
2078       s2 = row;
2079       e2 = list->drag_pos - 1;
2080     }
2081   else if (row < list->drag_pos && list->anchor < list->drag_pos)
2082     {
2083       e1 = list->drag_pos;
2084       /* row and drag_pos on different sides of anchor :
2085          take back the selection between anchor and drag_pos,
2086          select between anchor and row */
2087       if (row < list->anchor)
2088         {
2089           s1 = list->anchor + 1;
2090           s2 = row;
2091           e2 = list->anchor - 1;
2092         }
2093       /* take back the selection between anchor and drag_pos */
2094       else
2095         s1 = row + 1;
2096     }
2097   else if (row > list->drag_pos && list->anchor > list->drag_pos)
2098     {
2099       s1 = list->drag_pos;
2100       /* row and drag_pos on different sides of anchor :
2101          take back the selection between anchor and drag_pos,
2102          select between anchor and row */
2103       if (row > list->anchor)
2104         {
2105           e1 = list->anchor - 1;
2106           s2 = list->anchor + 1;
2107           e2 = row;
2108         }
2109       /* take back the selection between anchor and drag_pos */
2110       else
2111         e1 = row - 1;
2112     }
2113
2114   list->drag_pos = row;
2115
2116   /* restore the elements between s1 and e1 */
2117   if (s1 >= 0)
2118     {
2119       for (i = s1, work = g_list_nth (list->children, i); i <= e1;
2120            i++, work = work->next)
2121         {
2122           if (g_list_find (list->selection, work->data))
2123             gtk_widget_set_state (GTK_WIDGET (work->data), GTK_STATE_SELECTED);
2124           else
2125             gtk_widget_set_state (GTK_WIDGET (work->data), GTK_STATE_NORMAL);
2126         }
2127     }
2128
2129   /* extend the selection between s2 and e2 */
2130   if (s2 >= 0)
2131     {
2132       for (i = s2, work = g_list_nth (list->children, i); i <= e2;
2133            i++, work = work->next)
2134         if (GTK_WIDGET (work->data)->state != list->anchor_state)
2135           gtk_widget_set_state (GTK_WIDGET (work->data), list->anchor_state);
2136     }
2137 }
2138
2139 static void
2140 gtk_list_reset_extended_selection (GtkList *list)
2141
2142   g_return_if_fail (list != 0);
2143   g_return_if_fail (GTK_IS_LIST (list));
2144
2145   g_list_free (list->undo_selection);
2146   g_list_free (list->undo_unselection);
2147   list->undo_selection = NULL;
2148   list->undo_unselection = NULL;
2149
2150   list->anchor = -1;
2151   list->drag_pos = -1;
2152   list->undo_focus_child = GTK_CONTAINER (list)->focus_child;
2153 }
2154
2155 /* Public GtkList Scroll Methods :
2156  *
2157  * gtk_list_scroll_horizontal
2158  * gtk_list_scroll_vertical
2159  */
2160 void
2161 gtk_list_scroll_horizontal (GtkList       *list,
2162                             GtkScrollType  scroll_type,
2163                             gfloat         position)
2164 {
2165   GtkAdjustment *adj;
2166
2167   g_return_if_fail (list != 0);
2168   g_return_if_fail (GTK_IS_LIST (list));
2169
2170   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
2171     return;
2172
2173   if (!(adj =
2174         gtk_object_get_data_by_id (GTK_OBJECT (list), hadjustment_key_id)))
2175     return;
2176
2177   switch (scroll_type)
2178     {
2179     case GTK_SCROLL_STEP_UP:
2180     case GTK_SCROLL_STEP_BACKWARD:
2181       adj->value = CLAMP (adj->value - adj->step_increment, adj->lower,
2182                           adj->upper - adj->page_size);
2183       break;
2184     case GTK_SCROLL_STEP_DOWN:
2185     case GTK_SCROLL_STEP_FORWARD:
2186       adj->value = CLAMP (adj->value + adj->step_increment, adj->lower,
2187                           adj->upper - adj->page_size);
2188       break;
2189     case GTK_SCROLL_PAGE_UP:
2190     case GTK_SCROLL_PAGE_BACKWARD:
2191       adj->value = CLAMP (adj->value - adj->page_increment, adj->lower,
2192                           adj->upper - adj->page_size);
2193       break;
2194     case GTK_SCROLL_PAGE_DOWN:
2195     case GTK_SCROLL_PAGE_FORWARD:
2196       adj->value = CLAMP (adj->value + adj->page_increment, adj->lower,
2197                           adj->upper - adj->page_size);
2198       break;
2199     case GTK_SCROLL_JUMP:
2200       adj->value = CLAMP (adj->lower + (adj->upper - adj->lower) * position,
2201                           adj->lower, adj->upper - adj->page_size);
2202       break;
2203     default:
2204       break;
2205     }
2206   gtk_adjustment_value_changed (adj);
2207 }
2208
2209 void
2210 gtk_list_scroll_vertical (GtkList       *list,
2211                           GtkScrollType  scroll_type,
2212                           gfloat         position)
2213 {
2214   g_return_if_fail (list != NULL);
2215   g_return_if_fail (GTK_IS_LIST (list));
2216
2217   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
2218     return;
2219
2220   if (list->selection_mode == GTK_SELECTION_EXTENDED)
2221     {
2222       GtkContainer *container;
2223
2224       if (list->anchor >= 0)
2225         return;
2226
2227       container = GTK_CONTAINER (list);
2228       list->undo_focus_child = container->focus_child;
2229       gtk_list_move_focus_child (list, scroll_type, position);
2230       if (container->focus_child != list->undo_focus_child && !list->add_mode)
2231         {
2232           gtk_list_unselect_all (list);
2233           gtk_list_select_child (list, container->focus_child);
2234         }
2235     }
2236   else
2237     gtk_list_move_focus_child (list, scroll_type, position);
2238 }
2239
2240
2241 /* Private GtkList Scroll/Focus Functions :
2242  *
2243  * gtk_list_move_focus_child
2244  * gtk_list_horizontal_timeout
2245  * gtk_list_vertical_timeout
2246  */
2247 static void
2248 gtk_list_move_focus_child (GtkList       *list,
2249                            GtkScrollType  scroll_type,
2250                            gfloat         position)
2251 {
2252   GtkContainer *container;
2253   GList *work;
2254   GtkWidget *item;
2255   GtkAdjustment *adj;
2256   gint new_value;
2257
2258   g_return_if_fail (list != 0);
2259   g_return_if_fail (GTK_IS_LIST (list));
2260
2261   container = GTK_CONTAINER (list);
2262
2263   if (container->focus_child)
2264     work = g_list_find (list->children, container->focus_child);
2265   else
2266     work = list->children;
2267
2268   if (!work)
2269     return;
2270
2271   switch (scroll_type)
2272     {
2273     case GTK_SCROLL_STEP_BACKWARD:
2274       work = work->prev;
2275       if (work)
2276         gtk_widget_grab_focus (GTK_WIDGET (work->data));
2277       break;
2278     case GTK_SCROLL_STEP_FORWARD:
2279       work = work->next;
2280       if (work)
2281         gtk_widget_grab_focus (GTK_WIDGET (work->data));
2282       break;
2283     case GTK_SCROLL_PAGE_BACKWARD:
2284       if (!work->prev)
2285         return;
2286
2287       item = work->data;
2288       adj = gtk_object_get_data_by_id (GTK_OBJECT (list), vadjustment_key_id);
2289
2290       if (adj)
2291         {
2292           gboolean correct = FALSE;
2293
2294           new_value = adj->value;
2295
2296           if (item->allocation.y <= adj->value)
2297             {
2298               new_value = MAX (item->allocation.y + item->allocation.height
2299                                - adj->page_size, adj->lower);
2300               correct = TRUE;
2301             }
2302
2303           if (item->allocation.y > new_value)
2304             for (; work; work = work->prev)
2305               {
2306                 item = GTK_WIDGET (work->data);
2307                 if (item->allocation.y <= new_value &&
2308                     item->allocation.y + item->allocation.height > new_value)
2309                   break;
2310               }
2311           else
2312             for (; work; work = work->next)
2313               {
2314                 item = GTK_WIDGET (work->data);
2315                 if (item->allocation.y <= new_value &&
2316                     item->allocation.y + item->allocation.height > new_value)
2317                   break;
2318               }
2319
2320           if (correct && work && work->next && item->allocation.y < new_value)
2321             item = work->next->data;
2322         }
2323       else
2324         item = list->children->data;
2325           
2326       gtk_widget_grab_focus (item);
2327       break;
2328     case GTK_SCROLL_PAGE_FORWARD:
2329       if (!work->next)
2330         return;
2331
2332       item = work->data;
2333       adj = gtk_object_get_data_by_id (GTK_OBJECT (list), vadjustment_key_id);
2334
2335       if (adj)
2336         {
2337           gboolean correct = FALSE;
2338
2339           new_value = adj->value;
2340
2341           if (item->allocation.y + item->allocation.height >=
2342               adj->value + adj->page_size)
2343             {
2344               new_value = item->allocation.y;
2345               correct = TRUE;
2346             }
2347
2348           new_value = MIN (new_value + adj->page_size, adj->upper);
2349
2350           if (item->allocation.y > new_value)
2351             for (; work; work = work->prev)
2352               {
2353                 item = GTK_WIDGET (work->data);
2354                 if (item->allocation.y <= new_value &&
2355                     item->allocation.y + item->allocation.height > new_value)
2356                   break;
2357               }
2358           else
2359             for (; work; work = work->next)
2360               {
2361                 item = GTK_WIDGET (work->data);
2362                 if (item->allocation.y <= new_value &&
2363                     item->allocation.y + item->allocation.height > new_value)
2364                   break;
2365               }
2366
2367           if (correct && work && work->prev &&
2368               item->allocation.y + item->allocation.height - 1 > new_value)
2369             item = work->prev->data;
2370         }
2371       else
2372         item = g_list_last (work)->data;
2373           
2374       gtk_widget_grab_focus (item);
2375       break;
2376     case GTK_SCROLL_JUMP:
2377       new_value = GTK_WIDGET(list)->allocation.height * CLAMP (position, 0, 1);
2378
2379       for (item = NULL, work = list->children; work; work =work->next)
2380         {
2381           item = GTK_WIDGET (work->data);
2382           if (item->allocation.y <= new_value &&
2383               item->allocation.y + item->allocation.height > new_value)
2384             break;
2385         }
2386
2387       gtk_widget_grab_focus (item);
2388       break;
2389     default:
2390       break;
2391     }
2392 }
2393
2394 static gint
2395 gtk_list_horizontal_timeout (GtkWidget *list)
2396 {
2397   GdkEventMotion event;
2398
2399   memset (&event, 0, sizeof (event));
2400
2401   GDK_THREADS_ENTER ();
2402
2403   GTK_LIST (list)->htimer = 0;
2404
2405   event.type = GDK_MOTION_NOTIFY;
2406   event.send_event = TRUE;
2407
2408   gtk_list_motion_notify (list, &event);
2409
2410   GDK_THREADS_LEAVE ();
2411
2412   return FALSE;
2413 }
2414
2415 static gint
2416 gtk_list_vertical_timeout (GtkWidget *list)
2417 {
2418   GdkEventMotion event;
2419
2420   memset (&event, 0, sizeof (event));
2421
2422   GDK_THREADS_ENTER ();
2423
2424   GTK_LIST (list)->vtimer = 0;
2425
2426   event.type = GDK_MOTION_NOTIFY;
2427   event.send_event = TRUE;
2428
2429   gtk_list_motion_notify (list, &event);
2430
2431   GDK_THREADS_LEAVE ();
2432
2433   return FALSE;
2434 }
2435
2436
2437 /* Private GtkListItem Signal Functions :
2438  *
2439  * gtk_list_signal_toggle_focus_row
2440  * gtk_list_signal_select_all
2441  * gtk_list_signal_unselect_all
2442  * gtk_list_signal_undo_selection
2443  * gtk_list_signal_start_selection
2444  * gtk_list_signal_end_selection
2445  * gtk_list_signal_extend_selection
2446  * gtk_list_signal_scroll_horizontal
2447  * gtk_list_signal_scroll_vertical
2448  * gtk_list_signal_toggle_add_mode
2449  * gtk_list_signal_item_select
2450  * gtk_list_signal_item_deselect
2451  * gtk_list_signal_item_toggle
2452  */
2453 static void
2454 gtk_list_signal_toggle_focus_row (GtkListItem *list_item,
2455                                   GtkList     *list)
2456 {
2457   g_return_if_fail (list_item != 0);
2458   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2459   g_return_if_fail (list != NULL);
2460   g_return_if_fail (GTK_IS_LIST (list));
2461
2462   gtk_list_toggle_focus_row (list);
2463 }
2464
2465 static void
2466 gtk_list_signal_select_all (GtkListItem *list_item,
2467                             GtkList     *list)
2468 {
2469   g_return_if_fail (list_item != 0);
2470   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2471   g_return_if_fail (list != NULL);
2472   g_return_if_fail (GTK_IS_LIST (list));
2473
2474   gtk_list_select_all (list);
2475 }
2476
2477 static void
2478 gtk_list_signal_unselect_all (GtkListItem *list_item,
2479                               GtkList     *list)
2480 {
2481   g_return_if_fail (list_item != 0);
2482   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2483   g_return_if_fail (list != NULL);
2484   g_return_if_fail (GTK_IS_LIST (list));
2485
2486   gtk_list_unselect_all (list);
2487 }
2488
2489 static void
2490 gtk_list_signal_undo_selection (GtkListItem *list_item,
2491                                 GtkList     *list)
2492 {
2493   g_return_if_fail (list_item != 0);
2494   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2495   g_return_if_fail (list != NULL);
2496   g_return_if_fail (GTK_IS_LIST (list));
2497
2498   gtk_list_undo_selection (list);
2499 }
2500
2501 static void
2502 gtk_list_signal_start_selection (GtkListItem *list_item,
2503                                  GtkList     *list)
2504 {
2505   g_return_if_fail (list_item != 0);
2506   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2507   g_return_if_fail (list != NULL);
2508   g_return_if_fail (GTK_IS_LIST (list));
2509
2510   gtk_list_start_selection (list);
2511 }
2512
2513 static void
2514 gtk_list_signal_end_selection (GtkListItem *list_item,
2515                                GtkList     *list)
2516 {
2517   g_return_if_fail (list_item != 0);
2518   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2519   g_return_if_fail (list != NULL);
2520   g_return_if_fail (GTK_IS_LIST (list));
2521
2522   gtk_list_end_selection (list);
2523 }
2524
2525 static void
2526 gtk_list_signal_extend_selection (GtkListItem   *list_item,
2527                                   GtkScrollType  scroll_type,
2528                                   gfloat         position,
2529                                   gboolean       auto_start_selection,
2530                                   GtkList       *list)
2531 {
2532   g_return_if_fail (list_item != 0);
2533   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2534   g_return_if_fail (list != NULL);
2535   g_return_if_fail (GTK_IS_LIST (list));
2536
2537   gtk_list_extend_selection (list, scroll_type, position,
2538                              auto_start_selection);
2539 }
2540
2541 static void
2542 gtk_list_signal_scroll_horizontal (GtkListItem   *list_item,
2543                                    GtkScrollType  scroll_type,
2544                                    gfloat         position,
2545                                    GtkList       *list)
2546 {
2547   g_return_if_fail (list_item != 0);
2548   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2549   g_return_if_fail (list != NULL);
2550   g_return_if_fail (GTK_IS_LIST (list));
2551
2552   gtk_list_scroll_horizontal (list, scroll_type, position);
2553 }
2554
2555 static void
2556 gtk_list_signal_scroll_vertical (GtkListItem   *list_item,
2557                                  GtkScrollType  scroll_type,
2558                                  gfloat         position,
2559                                  GtkList       *list)
2560 {
2561   g_return_if_fail (list_item != 0);
2562   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2563   g_return_if_fail (list != NULL);
2564   g_return_if_fail (GTK_IS_LIST (list));
2565
2566   gtk_list_scroll_vertical (list, scroll_type, position);
2567 }
2568
2569 static void
2570 gtk_list_signal_toggle_add_mode (GtkListItem *list_item,
2571                                  GtkList     *list)
2572 {
2573   g_return_if_fail (list_item != 0);
2574   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2575   g_return_if_fail (list != NULL);
2576   g_return_if_fail (GTK_IS_LIST (list));
2577
2578   gtk_list_toggle_add_mode (list);
2579 }
2580
2581 static void
2582 gtk_list_signal_item_select (GtkListItem *list_item,
2583                              GtkList     *list)
2584 {
2585   GList *selection;
2586   GList *tmp_list;
2587   GList *sel_list;
2588
2589   g_return_if_fail (list_item != 0);
2590   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2591   g_return_if_fail (list != NULL);
2592   g_return_if_fail (GTK_IS_LIST (list));
2593
2594   if (GTK_WIDGET (list_item)->state != GTK_STATE_SELECTED)
2595     return;
2596
2597   switch (list->selection_mode)
2598     {
2599     case GTK_SELECTION_SINGLE:
2600     case GTK_SELECTION_BROWSE:
2601       sel_list = NULL;
2602       selection = list->selection;
2603
2604       while (selection)
2605         {
2606           tmp_list = selection;
2607           selection = selection->next;
2608
2609           if (tmp_list->data == list_item)
2610             sel_list = tmp_list;
2611           else
2612             gtk_list_item_deselect (GTK_LIST_ITEM (tmp_list->data));
2613         }
2614
2615       if (!sel_list)
2616         {
2617           list->selection = g_list_prepend (list->selection, list_item);
2618           gtk_widget_ref (GTK_WIDGET (list_item));
2619         }
2620       gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
2621       break;
2622     case GTK_SELECTION_EXTENDED:
2623       if (list->anchor >= 0)
2624         return;
2625     case GTK_SELECTION_MULTIPLE:
2626       if (!g_list_find (list->selection, list_item))
2627         {
2628           list->selection = g_list_prepend (list->selection, list_item);
2629           gtk_widget_ref (GTK_WIDGET (list_item));
2630           gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
2631         }
2632       break;
2633     }
2634 }
2635
2636 static void
2637 gtk_list_signal_item_deselect (GtkListItem *list_item,
2638                                GtkList     *list)
2639 {
2640   GList *node;
2641
2642   g_return_if_fail (list_item != 0);
2643   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2644   g_return_if_fail (list != NULL);
2645   g_return_if_fail (GTK_IS_LIST (list));
2646
2647   if (GTK_WIDGET (list_item)->state != GTK_STATE_NORMAL)
2648     return;
2649
2650   node = g_list_find (list->selection, list_item);
2651
2652   if (node)
2653     {
2654       list->selection = g_list_remove_link (list->selection, node);
2655       g_list_free_1 (node);
2656       gtk_widget_unref (GTK_WIDGET (list_item));
2657       gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
2658     }
2659 }
2660
2661 static void
2662 gtk_list_signal_item_toggle (GtkListItem *list_item,
2663                              GtkList     *list)
2664 {
2665   g_return_if_fail (list_item != 0);
2666   g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
2667   g_return_if_fail (list != NULL);
2668   g_return_if_fail (GTK_IS_LIST (list));
2669
2670   if ((list->selection_mode == GTK_SELECTION_BROWSE ||
2671        list->selection_mode == GTK_SELECTION_EXTENDED) &&
2672       GTK_WIDGET (list_item)->state == GTK_STATE_NORMAL)
2673     {
2674       gtk_widget_set_state (GTK_WIDGET (list_item), GTK_STATE_SELECTED);
2675       return;
2676     }
2677   
2678   switch (GTK_WIDGET (list_item)->state)
2679     {
2680     case GTK_STATE_SELECTED:
2681       gtk_list_signal_item_select (list_item, list);
2682       break;
2683     case GTK_STATE_NORMAL:
2684       gtk_list_signal_item_deselect (list_item, list);
2685       break;
2686     default:
2687       break;
2688     }
2689 }
2690
2691 static void
2692 gtk_list_signal_drag_begin (GtkWidget      *widget,
2693                             GdkDragContext *context,
2694                             GtkList         *list)
2695 {
2696   g_return_if_fail (widget != NULL);
2697   g_return_if_fail (GTK_IS_LIST_ITEM (widget));
2698   g_return_if_fail (list != NULL);
2699   g_return_if_fail (GTK_IS_LIST (list));
2700
2701   gtk_list_drag_begin (GTK_WIDGET (list), context);
2702 }
2703
2704 static void
2705 gtk_list_drag_begin (GtkWidget      *widget,
2706                      GdkDragContext *context)
2707 {
2708   GtkList *list;
2709
2710   g_return_if_fail (widget != NULL);
2711   g_return_if_fail (GTK_IS_LIST (widget));
2712   g_return_if_fail (context != NULL);
2713
2714   list = GTK_LIST (widget);
2715
2716   if (list->drag_selection)
2717     {
2718       gtk_list_end_drag_selection (list);
2719
2720       switch (list->selection_mode)
2721         {
2722         case GTK_SELECTION_EXTENDED:
2723           gtk_list_end_selection (list);
2724           break;
2725         case GTK_SELECTION_SINGLE:
2726         case GTK_SELECTION_MULTIPLE:
2727           list->undo_focus_child = NULL;
2728           break;
2729         default:
2730           break;
2731         }
2732     }
2733 }