]> Pileus Git - ~andy/gtk/blob - gtk/gtklist.c
new class methods: (sort_list) (insert_row) (remove_row)
[~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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library 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 #include "gtklist.h"
20 #include "gtklistitem.h"
21 #include "gtkmain.h"
22 #include "gtksignal.h"
23 #include "gtklabel.h"
24
25 enum {
26   SELECTION_CHANGED,
27   SELECT_CHILD,
28   UNSELECT_CHILD,
29   LAST_SIGNAL
30 };
31
32 #define SCROLL_TIME  100
33
34 static void gtk_list_class_init      (GtkListClass   *klass);
35 static void gtk_list_init            (GtkList        *list);
36 static void gtk_list_shutdown        (GtkObject      *object);
37 static void gtk_list_destroy         (GtkObject      *object);
38 static void gtk_list_map             (GtkWidget      *widget);
39 static void gtk_list_unmap           (GtkWidget      *widget);
40 static void gtk_list_realize         (GtkWidget      *widget);
41 static void gtk_list_draw            (GtkWidget      *widget,
42                                       GdkRectangle   *area);
43 static gint gtk_list_expose          (GtkWidget      *widget,
44                                       GdkEventExpose *event);
45 static gint gtk_list_motion_notify   (GtkWidget      *widget,
46                                       GdkEventMotion *event);
47 static gint gtk_list_button_press    (GtkWidget      *widget,
48                                       GdkEventButton *event);
49 static gint gtk_list_button_release  (GtkWidget      *widget,
50                                       GdkEventButton *event);
51 static void gtk_list_size_request    (GtkWidget      *widget,
52                                       GtkRequisition *requisition);
53 static void gtk_list_size_allocate   (GtkWidget      *widget,
54                                       GtkAllocation  *allocation);
55 static void gtk_list_add             (GtkContainer   *container,
56                                       GtkWidget      *widget);
57 static void gtk_list_remove          (GtkContainer   *container,
58                                       GtkWidget      *widget);
59 static void gtk_list_foreach         (GtkContainer   *container,
60                                       GtkCallback     callback,
61                                       gpointer        callback_data);
62
63 static void gtk_real_list_select_child   (GtkList       *list,
64                                           GtkWidget     *child);
65 static void gtk_real_list_unselect_child (GtkList       *list,
66                                           GtkWidget     *child);
67
68
69 static void gtk_list_set_anchor          (GtkList       *list,
70                                           gboolean       add_mode,
71                                           gint           anchor,
72                                           GtkWidget     *undo_focus_child);
73 static void gtk_list_fake_unselect_all   (GtkList       *list,
74                                           GtkWidget     *item);
75 static void gtk_list_fake_toggle_row     (GtkList       *list,
76                                           GtkWidget     *item);
77 static void gtk_list_move_focus_child    (GtkList       *list,
78                                           GtkScrollType  scroll_type,
79                                           gfloat         position);
80 static void gtk_list_update_extended_selection (GtkList *list,
81                                                 gint     row);
82 static void gtk_list_focus_lost          (GtkWidget     *item,
83                                           GdkEventKey   *event,
84                                           GtkList       *list);
85 static void gtk_list_set_focus_child     (GtkContainer  *container,
86                                           GtkWidget     *widget);
87 static gint gtk_list_focus               (GtkContainer     *container,
88                                           GtkDirectionType  direction);
89
90
91 static GtkType gtk_list_child_type  (GtkContainer   *container);
92
93
94 static GtkContainerClass *parent_class = NULL;
95 static guint list_signals[LAST_SIGNAL] = { 0 };
96
97 static const gchar *vadjustment_key = "gtk-vadjustment";
98 static guint        vadjustment_key_id = 0;
99 static const gchar *hadjustment_key = "gtk-hadjustment";
100 static guint        hadjustment_key_id = 0;
101
102 GtkType
103 gtk_list_get_type (void)
104 {
105   static guint list_type = 0;
106
107   if (!list_type)
108     {
109       GtkTypeInfo list_info =
110       {
111         "GtkList",
112         sizeof (GtkList),
113         sizeof (GtkListClass),
114         (GtkClassInitFunc) gtk_list_class_init,
115         (GtkObjectInitFunc) gtk_list_init,
116         /* reserved_1 */ NULL,
117         /* reserved_2 */ NULL,
118         (GtkClassInitFunc) NULL,
119       };
120
121       list_type = gtk_type_unique (gtk_container_get_type (), &list_info);
122     }
123
124   return list_type;
125 }
126
127 static void
128 gtk_list_class_init (GtkListClass *class)
129 {
130   GtkObjectClass *object_class;
131   GtkWidgetClass *widget_class;
132   GtkContainerClass *container_class;
133
134   object_class = (GtkObjectClass*) class;
135   widget_class = (GtkWidgetClass*) class;
136   container_class = (GtkContainerClass*) class;
137
138   parent_class = gtk_type_class (gtk_container_get_type ());
139
140   vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
141   hadjustment_key_id = g_quark_from_static_string (hadjustment_key);
142
143   list_signals[SELECTION_CHANGED] =
144     gtk_signal_new ("selection_changed",
145                     GTK_RUN_FIRST,
146                     object_class->type,
147                     GTK_SIGNAL_OFFSET (GtkListClass, selection_changed),
148                     gtk_marshal_NONE__NONE,
149                     GTK_TYPE_NONE, 0);
150   list_signals[SELECT_CHILD] =
151     gtk_signal_new ("select_child",
152                     GTK_RUN_FIRST,
153                     object_class->type,
154                     GTK_SIGNAL_OFFSET (GtkListClass, select_child),
155                     gtk_marshal_NONE__POINTER,
156                     GTK_TYPE_NONE, 1,
157                     GTK_TYPE_WIDGET);
158   list_signals[UNSELECT_CHILD] =
159     gtk_signal_new ("unselect_child",
160                     GTK_RUN_FIRST,
161                     object_class->type,
162                     GTK_SIGNAL_OFFSET (GtkListClass, unselect_child),
163                     gtk_marshal_NONE__POINTER,
164                     GTK_TYPE_NONE, 1,
165                     GTK_TYPE_WIDGET);
166
167   gtk_object_class_add_signals (object_class, list_signals, LAST_SIGNAL);
168
169   object_class->shutdown = gtk_list_shutdown;
170   object_class->destroy = gtk_list_destroy;
171
172   widget_class->map = gtk_list_map;
173   widget_class->unmap = gtk_list_unmap;
174   widget_class->realize = gtk_list_realize;
175   widget_class->draw = gtk_list_draw;
176   widget_class->expose_event = gtk_list_expose;
177   widget_class->button_press_event = gtk_list_button_press;
178   widget_class->button_release_event = gtk_list_button_release;
179   widget_class->motion_notify_event = gtk_list_motion_notify;
180   widget_class->size_request = gtk_list_size_request;
181   widget_class->size_allocate = gtk_list_size_allocate;
182
183   container_class->add = gtk_list_add;
184   container_class->remove = gtk_list_remove;
185   container_class->foreach = gtk_list_foreach;
186   container_class->child_type = gtk_list_child_type;
187   container_class->set_focus_child = gtk_list_set_focus_child;
188   container_class->focus = gtk_list_focus;
189
190   class->selection_changed = NULL;
191   class->select_child = gtk_real_list_select_child;
192   class->unselect_child = gtk_real_list_unselect_child;
193 }
194
195 static GtkType
196 gtk_list_child_type (GtkContainer     *container)
197 {
198   return GTK_TYPE_LIST_ITEM;
199 }
200
201 static void
202 gtk_list_init (GtkList *list)
203 {
204   list->children = NULL;
205   list->selection = NULL;
206
207   list->undo_selection = NULL;
208   list->undo_unselection = NULL;
209
210   list->last_focus_child = NULL;
211   list->undo_focus_child = NULL;
212
213   list->htimer = 0;
214   list->vtimer = 0;
215
216   list->anchor = -1;
217   list->drag_pos = -1;
218   list->anchor_state = GTK_STATE_SELECTED;
219
220   list->selection_mode = GTK_SELECTION_SINGLE;
221   list->drag_selection = FALSE;
222   list->add_mode = FALSE;
223 }
224
225 GtkWidget*
226 gtk_list_new (void)
227 {
228   return GTK_WIDGET (gtk_type_new (gtk_list_get_type ()));
229 }
230
231 static void
232 gtk_list_shutdown (GtkObject *object)
233 {
234   gtk_list_clear_items (GTK_LIST (object), 0, -1);
235
236   GTK_OBJECT_CLASS (parent_class)->shutdown (object);
237 }
238
239 static void
240 gtk_list_destroy (GtkObject *object)
241 {
242   GList *node;
243
244   GtkList *list = GTK_LIST (object);
245
246   for (node = list->children; node; node = node->next)
247     {
248       GtkWidget *child;
249
250       child = (GtkWidget *)node->data;
251       gtk_widget_ref (child);
252       gtk_widget_unparent (child);
253       gtk_widget_destroy (child);
254       gtk_widget_unref (child);
255     }
256   g_list_free (list->children);
257   list->children = NULL;
258
259   for (node = list->selection; node; node = node->next)
260     {
261       GtkWidget *child;
262
263       child = (GtkWidget *)node->data;
264       gtk_widget_unref (child);
265     }
266   g_list_free (list->selection);
267   list->selection = NULL;
268
269   if (GTK_OBJECT_CLASS (parent_class)->destroy)
270     (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
271 }
272
273 void
274 gtk_list_end_drag_selection (GtkList *list)
275 {
276   g_return_if_fail (list != NULL);
277   g_return_if_fail (GTK_IS_LIST (list));
278
279   list->drag_selection = FALSE;
280   if (GTK_WIDGET_HAS_GRAB (list))
281     {
282       gtk_grab_remove (GTK_WIDGET (list));
283       gdk_pointer_ungrab (GDK_CURRENT_TIME);
284     }
285   if (list->htimer)
286     {
287       gtk_timeout_remove (list->htimer);
288       list->htimer = 0;
289     }
290   if (list->vtimer)
291     {
292       gtk_timeout_remove (list->vtimer);
293       list->vtimer = 0;
294     }
295 }
296
297 void
298 gtk_list_insert_items (GtkList *list,
299                        GList   *items,
300                        gint     position)
301 {
302   GtkWidget *widget;
303   GList *tmp_list;
304   GList *last;
305   gint nchildren;
306
307   g_return_if_fail (list != NULL);
308   g_return_if_fail (GTK_IS_LIST (list));
309
310   if (!items)
311     return;
312
313   gtk_list_end_drag_selection (list);
314   if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
315     gtk_list_end_selection (list);
316
317   tmp_list = items;
318   while (tmp_list)
319     {
320       widget = tmp_list->data;
321       tmp_list = tmp_list->next;
322
323       gtk_widget_set_parent (widget, GTK_WIDGET (list));
324       gtk_signal_connect (GTK_OBJECT (widget), "focus_out_event",
325                           GTK_SIGNAL_FUNC (gtk_list_focus_lost), list);
326
327       if (GTK_WIDGET_VISIBLE (widget->parent))
328         {
329           if (GTK_WIDGET_REALIZED (widget->parent) &&
330               !GTK_WIDGET_REALIZED (widget))
331             gtk_widget_realize (widget);
332
333           if (GTK_WIDGET_MAPPED (widget->parent) &&
334               !GTK_WIDGET_MAPPED (widget))
335             gtk_widget_map (widget);
336         }
337     }
338
339   nchildren = g_list_length (list->children);
340   if ((position < 0) || (position > nchildren))
341     position = nchildren;
342
343   if (position == nchildren)
344     {
345       if (list->children)
346         {
347           tmp_list = g_list_last (list->children);
348           tmp_list->next = items;
349           items->prev = tmp_list;
350         }
351       else
352         {
353           list->children = items;
354         }
355     }
356   else
357     {
358       tmp_list = g_list_nth (list->children, position);
359       last = g_list_last (items);
360
361       if (tmp_list->prev)
362         tmp_list->prev->next = items;
363       last->next = tmp_list;
364       items->prev = tmp_list->prev;
365       tmp_list->prev = last;
366
367       if (tmp_list == list->children)
368         list->children = items;
369     }
370
371   if (list->children && !list->selection &&
372       (list->selection_mode == GTK_SELECTION_BROWSE))
373     {
374       widget = list->children->data;
375       gtk_list_select_child (list, widget);
376     }
377
378   if (GTK_WIDGET_VISIBLE (list))
379     gtk_widget_queue_resize (GTK_WIDGET (list));
380 }
381
382 void
383 gtk_list_append_items (GtkList *list,
384                        GList   *items)
385 {
386   g_return_if_fail (list != NULL);
387   g_return_if_fail (GTK_IS_LIST (list));
388
389   gtk_list_insert_items (list, items, -1);
390 }
391
392 void
393 gtk_list_prepend_items (GtkList *list,
394                         GList   *items)
395 {
396   g_return_if_fail (list != NULL);
397   g_return_if_fail (GTK_IS_LIST (list));
398
399   gtk_list_insert_items (list, items, 0);
400 }
401
402 static void
403 gtk_list_remove_items_internal (GtkList  *list,
404                                 GList    *items,
405                                 gboolean no_unref)
406 {
407   GtkWidget *widget;
408   GtkWidget *new_focus_child;
409   GtkWidget *old_focus_child;
410   GtkContainer *container;
411   GList *selected_widgets;
412   GList *tmp_list;
413   GList *work;
414   gboolean grab_focus = FALSE;
415   
416   g_return_if_fail (list != NULL);
417   g_return_if_fail (GTK_IS_LIST (list));
418
419   if (!items)
420     return;
421   
422   container = GTK_CONTAINER (list);
423
424   gtk_list_end_drag_selection (list);
425   if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
426     gtk_list_end_selection (list);
427
428   
429   tmp_list = items;
430   selected_widgets = NULL;
431   widget = NULL;
432   old_focus_child = new_focus_child = container->focus_child;
433
434   while (tmp_list)
435     {
436       widget = tmp_list->data;
437
438       tmp_list = tmp_list->next;
439       
440       if (no_unref)
441         gtk_widget_ref (widget);
442       
443       gtk_signal_disconnect_by_func
444         (GTK_OBJECT (widget), GTK_SIGNAL_FUNC (gtk_list_focus_lost), list);
445
446
447       if (widget == new_focus_child) 
448         {
449           work = g_list_find (list->children, widget);
450
451           if (work)
452             {
453               if (work->next)
454                 new_focus_child = work->next->data;
455               else if (list->children != work && work->prev)
456                 new_focus_child = work->prev->data;
457               else
458                 new_focus_child = NULL;
459               
460               if (GTK_WIDGET_HAS_FOCUS (widget))
461                 grab_focus = TRUE;
462             }
463         }
464
465       list->children = g_list_remove (list->children, widget);
466
467       if (widget->state == GTK_STATE_SELECTED)
468         selected_widgets = g_list_prepend (selected_widgets, widget);
469       else
470         gtk_widget_unparent (widget);
471     }
472   
473   if (selected_widgets)
474     {
475       tmp_list = selected_widgets;
476       while (tmp_list)
477         {
478           widget = tmp_list->data;
479           tmp_list = tmp_list->next;
480           
481           gtk_list_unselect_child (list, widget);
482
483           gtk_widget_unparent (widget);
484         }
485       
486       gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
487     }
488   
489   g_list_free (selected_widgets);
490   
491   if (new_focus_child && new_focus_child != old_focus_child)
492     {
493       if (grab_focus)
494         gtk_widget_grab_focus (new_focus_child);
495       else
496         gtk_container_set_focus_child (container, new_focus_child);
497     }
498   
499   if (GTK_WIDGET_VISIBLE (list))
500     gtk_widget_queue_resize (GTK_WIDGET (list));
501 }
502
503 void
504 gtk_list_remove_items (GtkList  *list,
505                        GList    *items)
506 {
507   gtk_list_remove_items_internal (list, items, FALSE);
508 }
509
510 void
511 gtk_list_remove_items_no_unref (GtkList  *list,
512                                 GList    *items)
513 {
514   gtk_list_remove_items_internal (list, items, TRUE);
515 }
516
517 void
518 gtk_list_clear_items (GtkList *list,
519                       gint     start,
520                       gint     end)
521 {
522   GtkWidget *widget;
523   GList *start_list;
524   GList *end_list;
525   GList *tmp_list;
526   guint nchildren;
527
528   g_return_if_fail (list != NULL);
529   g_return_if_fail (GTK_IS_LIST (list));
530
531   nchildren = g_list_length (list->children);
532
533   if (nchildren > 0)
534     {
535       gboolean selection_changed;
536
537       if ((end < 0) || (end > nchildren))
538         end = nchildren;
539
540       if (start >= end)
541         return;
542
543       start_list = g_list_nth (list->children, start);
544       end_list = g_list_nth (list->children, end);
545
546       gtk_list_end_drag_selection (list);
547       if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
548         gtk_list_end_selection (list);
549
550       if (start_list->prev)
551         start_list->prev->next = end_list;
552       if (end_list && end_list->prev)
553         end_list->prev->next = NULL;
554       if (end_list)
555         end_list->prev = start_list->prev;
556       if (start_list == list->children)
557         list->children = end_list;
558
559       selection_changed = FALSE;
560       widget = NULL;
561       tmp_list = start_list;
562
563       while (tmp_list)
564         {
565           widget = tmp_list->data;
566           tmp_list = tmp_list->next;
567
568           if (widget->state == GTK_STATE_SELECTED)
569             {
570               gtk_list_unselect_child (list, widget);
571               selection_changed = TRUE;
572             }
573
574           gtk_widget_unparent (widget);
575         }
576
577       g_list_free (start_list);
578
579       if (list->children && !list->selection &&
580           (list->selection_mode == GTK_SELECTION_BROWSE))
581         {
582           widget = list->children->data;
583           gtk_list_select_child (list, widget);
584         }
585       else if (selection_changed)
586         gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
587
588       gtk_widget_queue_resize (GTK_WIDGET (list));
589     }
590 }
591
592 void
593 gtk_list_select_item (GtkList *list,
594                       gint     item)
595 {
596   GList *tmp_list;
597
598   g_return_if_fail (list != NULL);
599   g_return_if_fail (GTK_IS_LIST (list));
600
601   tmp_list = g_list_nth (list->children, item);
602   if (tmp_list)
603     gtk_list_select_child (list, GTK_WIDGET (tmp_list->data));
604 }
605
606 void
607 gtk_list_unselect_item (GtkList *list,
608                         gint     item)
609 {
610   GList *tmp_list;
611
612   g_return_if_fail (list != NULL);
613   g_return_if_fail (GTK_IS_LIST (list));
614
615   tmp_list = g_list_nth (list->children, item);
616   if (tmp_list)
617     gtk_list_unselect_child (list, GTK_WIDGET (tmp_list->data));
618 }
619
620 void
621 gtk_list_select_child (GtkList   *list,
622                        GtkWidget *child)
623 {
624   gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECT_CHILD], child);
625 }
626
627 void
628 gtk_list_unselect_child (GtkList   *list,
629                          GtkWidget *child)
630 {
631   gtk_signal_emit (GTK_OBJECT (list), list_signals[UNSELECT_CHILD], child);
632 }
633
634 gint
635 gtk_list_child_position (GtkList   *list,
636                          GtkWidget *child)
637 {
638   GList *children;
639   gint pos;
640
641   g_return_val_if_fail (list != NULL, -1);
642   g_return_val_if_fail (GTK_IS_LIST (list), -1);
643   g_return_val_if_fail (child != NULL, -1);
644
645   pos = 0;
646   children = list->children;
647
648   while (children)
649     {
650       if (child == GTK_WIDGET (children->data))
651         return pos;
652
653       pos += 1;
654       children = children->next;
655     }
656
657   return -1;
658 }
659
660 void
661 gtk_list_set_selection_mode (GtkList          *list,
662                              GtkSelectionMode  mode)
663 {
664   g_return_if_fail (list != NULL);
665   g_return_if_fail (GTK_IS_LIST (list));
666
667   if (list->selection_mode == mode)
668     return;
669
670   list->selection_mode = mode;
671
672   switch (mode)
673     {
674     case GTK_SELECTION_SINGLE:
675     case GTK_SELECTION_BROWSE:
676       gtk_list_unselect_all (list);
677       break;
678
679     default:
680       break;
681     }
682 }
683
684
685 static void
686 gtk_list_map (GtkWidget *widget)
687 {
688   GtkList *list;
689   GtkWidget *child;
690   GList *children;
691
692   g_return_if_fail (widget != NULL);
693   g_return_if_fail (GTK_IS_LIST (widget));
694
695   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
696   list = GTK_LIST (widget);
697
698   gdk_window_show (widget->window);
699
700   children = list->children;
701   while (children)
702     {
703       child = children->data;
704       children = children->next;
705
706       if (GTK_WIDGET_VISIBLE (child) &&
707           !GTK_WIDGET_MAPPED (child))
708         gtk_widget_map (child);
709     }
710 }
711
712 static void
713 gtk_list_unmap (GtkWidget *widget)
714 {
715   g_return_if_fail (widget != NULL);
716   g_return_if_fail (GTK_IS_LIST (widget));
717
718   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
719   gdk_window_hide (widget->window);
720 }
721
722 static void
723 gtk_list_realize (GtkWidget *widget)
724 {
725   GdkWindowAttr attributes;
726   gint attributes_mask;
727
728   g_return_if_fail (widget != NULL);
729   g_return_if_fail (GTK_IS_LIST (widget));
730
731   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
732
733   attributes.window_type = GDK_WINDOW_CHILD;
734   attributes.x = widget->allocation.x;
735   attributes.y = widget->allocation.y;
736   attributes.width = widget->allocation.width;
737   attributes.height = widget->allocation.height;
738   attributes.wclass = GDK_INPUT_OUTPUT;
739   attributes.visual = gtk_widget_get_visual (widget);
740   attributes.colormap = gtk_widget_get_colormap (widget);
741   attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
742
743   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
744
745   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
746   gdk_window_set_user_data (widget->window, widget);
747
748   widget->style = gtk_style_attach (widget->style, widget->window);
749   gdk_window_set_background (widget->window, 
750                              &widget->style->base[GTK_STATE_NORMAL]);
751 }
752
753 static void
754 gtk_list_draw (GtkWidget    *widget,
755                GdkRectangle *area)
756 {
757   GtkList *list;
758   GtkWidget *child;
759   GdkRectangle child_area;
760   GList *children;
761
762   g_return_if_fail (widget != NULL);
763   g_return_if_fail (GTK_IS_LIST (widget));
764   g_return_if_fail (area != NULL);
765
766   if (GTK_WIDGET_DRAWABLE (widget))
767     {
768       list = GTK_LIST (widget);
769
770       children = list->children;
771       while (children)
772         {
773           child = children->data;
774           children = children->next;
775
776           if (gtk_widget_intersect (child, area, &child_area))
777             gtk_widget_draw (child, &child_area);
778         }
779     }
780 }
781
782 static gint
783 gtk_list_expose (GtkWidget      *widget,
784                  GdkEventExpose *event)
785 {
786   GtkList *list;
787   GtkWidget *child;
788   GdkEventExpose child_event;
789   GList *children;
790
791   g_return_val_if_fail (widget != NULL, FALSE);
792   g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
793   g_return_val_if_fail (event != NULL, FALSE);
794
795   if (GTK_WIDGET_DRAWABLE (widget))
796     {
797       list = GTK_LIST (widget);
798
799       child_event = *event;
800
801       children = list->children;
802       while (children)
803         {
804           child = children->data;
805           children = children->next;
806
807           if (GTK_WIDGET_NO_WINDOW (child) &&
808               gtk_widget_intersect (child, &event->area, &child_event.area))
809             gtk_widget_event (child, (GdkEvent*) &child_event);
810         }
811     }
812
813   return FALSE;
814 }
815
816 static void
817 move_horizontal (GtkList       *list,
818                  GtkAdjustment *adj,
819                  gint           diff)
820 {
821   gfloat upper;
822
823   adj->value += diff;
824
825   upper = adj->upper - adj->page_size;
826   adj->value = MIN (adj->value, upper);
827   adj->value = MAX (adj->value, 0.0);
828
829   gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
830 }
831
832 static gint
833 horizontal_timeout (GtkWidget *list)
834 {
835   gint x, y;
836   GdkEventMotion event;
837   GdkModifierType mask;
838
839   g_return_val_if_fail (GTK_IS_LIST (list), FALSE);
840
841   GTK_LIST (list)->htimer = 0;
842   gdk_window_get_pointer (list->window, &x, &y, &mask);
843
844   event.is_hint = 0;
845   event.x = x;
846   event.y = y;
847   event.state = mask;
848
849   gtk_list_motion_notify (list, &event);
850
851   return FALSE;
852 }
853
854 static gint
855 vertical_timeout (GtkWidget *list)
856 {
857   gint x;
858   gint y;
859   GdkEventMotion event;
860   GdkModifierType mask;
861
862   g_return_val_if_fail (GTK_IS_LIST (list), FALSE);
863
864   GTK_LIST (list)->vtimer = 0;
865   gdk_window_get_pointer (list->window, &x, &y, &mask);
866
867   event.is_hint = 0;
868   event.x = x;
869   event.y = y;
870   event.state = mask;
871
872   gtk_list_motion_notify (list, &event);
873
874   return FALSE;
875 }
876
877 static gint
878 gtk_list_motion_notify (GtkWidget      *widget,
879                         GdkEventMotion *event)
880 {
881   GtkList *list;
882   GtkWidget *item = NULL;
883   GtkAdjustment *adj;
884   GtkContainer *container;
885   GList *work;
886   gint x;
887   gint y;
888   gint row = -1;
889   gint focus_row = 0;
890   gint length = 0;
891
892   g_return_val_if_fail (widget != NULL, FALSE);
893   g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
894   g_return_val_if_fail (event != NULL, FALSE);
895
896   list = GTK_LIST (widget);
897
898   if (!list->drag_selection || !list->children)
899     return FALSE;
900
901   container = GTK_CONTAINER (widget);
902
903   if (event->is_hint || event->window != widget->window)
904     gdk_window_get_pointer (widget->window, &x, &y, NULL);
905
906   adj = gtk_object_get_data_by_id (GTK_OBJECT (list), hadjustment_key_id);
907
908   /* horizontal autoscrolling */
909   if (adj && widget->allocation.width > adj->page_size &&
910       (x < adj->value || x >= adj->value + adj->page_size))
911     {
912       if (list->htimer == 0)
913         {
914           list->htimer = gtk_timeout_add
915             (SCROLL_TIME, (GtkFunction) horizontal_timeout, widget);
916           
917           if (!((x < adj->value && adj->value <= 0) ||
918                 (x > adj->value + adj->page_size &&
919                  adj->value >= adj->upper - adj->page_size)))
920             {
921               if (x < adj->value)
922                 move_horizontal (list, adj, - 1 + (x - adj->value) / 2 );
923               else
924                 move_horizontal (list, adj,
925                                  1 + (x - adj->value - adj->page_size) / 2);
926             }
927         }
928       else
929         return FALSE;
930     }
931
932   
933   /* vertical autoscrolling */
934   for (work = list->children; work; length++, work = work->next)
935     {
936       if (row < 0)
937         {
938           item = GTK_WIDGET (work->data);
939           if (item->allocation.y > y || 
940               (item->allocation.y <= y &&
941                item->allocation.y + item->allocation.height > y))
942             row = length;
943         }
944
945       if (work->data == container->focus_child)
946         focus_row = length;
947     }
948   
949   if (row < 0)
950     row = length - 1;
951
952   if (list->vtimer != 0)
953     return FALSE;
954
955   if (!((y < 0 && focus_row == 0) ||
956         (y > widget->allocation.height && focus_row >= length - 1)))
957     list->vtimer = gtk_timeout_add (SCROLL_TIME,
958                                     (GtkFunction) vertical_timeout, list);
959           
960   if (row != focus_row)
961     gtk_widget_grab_focus (item);
962           
963   switch (list->selection_mode)
964     {
965     case GTK_SELECTION_BROWSE:
966       gtk_list_select_child (list, item);
967       break;
968       
969     case GTK_SELECTION_EXTENDED:
970       gtk_list_update_extended_selection (list, row);
971       break;
972
973     default:
974       break;
975     }
976
977   return FALSE;
978 }
979
980 static gint
981 gtk_list_button_press (GtkWidget      *widget,
982                        GdkEventButton *event)
983 {
984   GtkList *list;
985   GtkWidget *item;
986
987   g_return_val_if_fail (widget != NULL, FALSE);
988   g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
989   g_return_val_if_fail (event != NULL, FALSE);
990
991   if (event->button != 1)
992     return FALSE;
993
994   list = GTK_LIST (widget);
995   item = gtk_get_event_widget ((GdkEvent*) event);
996
997   while (item && !GTK_IS_LIST_ITEM (item))
998     item = item->parent;
999
1000   if (item && (item->parent == widget))
1001     {
1002       gint last_focus_row;
1003       gint focus_row;
1004
1005       if (event->type == GDK_BUTTON_PRESS)
1006         {
1007           list->drag_selection = TRUE;
1008           gdk_pointer_grab (widget->window, TRUE,
1009                             GDK_POINTER_MOTION_HINT_MASK |
1010                             GDK_BUTTON1_MOTION_MASK |
1011                             GDK_BUTTON_RELEASE_MASK,
1012                             NULL, NULL, event->time);
1013           gtk_grab_add (widget);
1014         }
1015       else if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
1016         gtk_list_end_drag_selection (list);
1017           
1018       if (!GTK_WIDGET_HAS_FOCUS(item))
1019         gtk_widget_grab_focus (item);
1020
1021       if (list->add_mode)
1022         {
1023           list->add_mode = FALSE;
1024           gtk_widget_queue_draw (item);
1025         }
1026       
1027       switch (list->selection_mode)
1028         {
1029         case GTK_SELECTION_SINGLE:
1030         case GTK_SELECTION_MULTIPLE:
1031           if (event->type != GDK_BUTTON_PRESS)
1032             gtk_list_select_child (list, item);
1033           else
1034             list->undo_focus_child = item;
1035           break;
1036           
1037         case GTK_SELECTION_BROWSE:
1038           break;
1039
1040         case GTK_SELECTION_EXTENDED:
1041           focus_row = g_list_index (list->children, item);
1042
1043           if (list->last_focus_child)
1044             last_focus_row = g_list_index (list->children,
1045                                            list->last_focus_child);
1046           else
1047             {
1048               last_focus_row = focus_row;
1049               list->last_focus_child = item;
1050             }
1051
1052           if (event->type != GDK_BUTTON_PRESS)
1053             {
1054               if (list->anchor >= 0)
1055                 {
1056                   gtk_list_update_extended_selection (list, focus_row);
1057                   gtk_list_end_selection (list);
1058                 }
1059               gtk_list_select_child (list, item);
1060               break;
1061             }
1062               
1063           if (event->state & GDK_CONTROL_MASK)
1064             {
1065               if (event->state & GDK_SHIFT_MASK)
1066                 {
1067                   if (list->anchor < 0)
1068                     {
1069                       g_list_free (list->undo_selection);
1070                       g_list_free (list->undo_unselection);
1071                       list->undo_selection = NULL;
1072                       list->undo_unselection = NULL;
1073
1074                       list->anchor = last_focus_row;
1075                       list->drag_pos = last_focus_row;
1076                       list->undo_focus_child = list->last_focus_child;
1077                     }
1078                   gtk_list_update_extended_selection (list, focus_row);
1079                 }
1080               else
1081                 {
1082                   if (list->anchor < 0)
1083                     gtk_list_set_anchor (list, TRUE,
1084                                          focus_row, list->last_focus_child);
1085                   else
1086                     gtk_list_update_extended_selection (list, focus_row);
1087                 }
1088               break;
1089             }
1090
1091           if (event->state & GDK_SHIFT_MASK)
1092             {
1093               gtk_list_set_anchor (list, FALSE,
1094                                    last_focus_row, list->last_focus_child);
1095               gtk_list_update_extended_selection (list, focus_row);
1096               break;
1097             }
1098
1099           if (list->anchor < 0)
1100             gtk_list_set_anchor (list, FALSE, focus_row,
1101                                  list->last_focus_child);
1102           else
1103             gtk_list_update_extended_selection (list, focus_row);
1104           break;
1105           
1106         default:
1107           break;
1108         }
1109     }
1110
1111   return FALSE;
1112 }
1113
1114 static gint
1115 gtk_list_button_release (GtkWidget      *widget,
1116                          GdkEventButton *event)
1117 {
1118   GtkList *list;
1119   GtkWidget *item;
1120
1121   g_return_val_if_fail (widget != NULL, FALSE);
1122   g_return_val_if_fail (GTK_IS_LIST (widget), FALSE);
1123   g_return_val_if_fail (event != NULL, FALSE);
1124
1125   list = GTK_LIST (widget);
1126
1127   /* we don't handle button 2 and 3 */
1128   if (event->button != 1)
1129     return FALSE;
1130
1131   if (list->drag_selection)
1132     {
1133       gtk_list_end_drag_selection (list);
1134
1135       switch (list->selection_mode)
1136         {
1137         case GTK_SELECTION_EXTENDED:
1138           if (!(event->state & GDK_SHIFT_MASK))
1139             gtk_list_end_selection (list);
1140           break;
1141
1142         case GTK_SELECTION_SINGLE:
1143         case GTK_SELECTION_MULTIPLE:
1144
1145           item = gtk_get_event_widget ((GdkEvent*) event);
1146   
1147           while (item && !GTK_IS_LIST_ITEM (item))
1148             item = item->parent;
1149           
1150           if (item && item->parent == widget)
1151             {
1152               if (list->undo_focus_child == item)
1153                 gtk_list_toggle_row (list, item);
1154             }
1155           list->undo_focus_child = NULL;
1156           break;
1157
1158         default:
1159           break;
1160         }
1161     }
1162   
1163   return FALSE;
1164 }
1165
1166 static void
1167 gtk_list_size_request (GtkWidget      *widget,
1168                        GtkRequisition *requisition)
1169 {
1170   GtkList *list;
1171   GtkWidget *child;
1172   GList *children;
1173
1174   g_return_if_fail (widget != NULL);
1175   g_return_if_fail (GTK_IS_LIST (widget));
1176   g_return_if_fail (requisition != NULL);
1177
1178   list = GTK_LIST (widget);
1179   requisition->width = 0;
1180   requisition->height = 0;
1181
1182   children = list->children;
1183   while (children)
1184     {
1185       child = children->data;
1186       children = children->next;
1187
1188       if (GTK_WIDGET_VISIBLE (child))
1189         {
1190           gtk_widget_size_request (child, &child->requisition);
1191
1192           requisition->width = MAX (requisition->width, child->requisition.width);
1193           requisition->height += child->requisition.height;
1194         }
1195     }
1196
1197   requisition->width += GTK_CONTAINER (list)->border_width * 2;
1198   requisition->height += GTK_CONTAINER (list)->border_width * 2;
1199
1200   requisition->width = MAX (requisition->width, 1);
1201   requisition->height = MAX (requisition->height, 1);
1202 }
1203
1204 static void
1205 gtk_list_size_allocate (GtkWidget     *widget,
1206                         GtkAllocation *allocation)
1207 {
1208   GtkList *list;
1209   GtkWidget *child;
1210   GtkAllocation child_allocation;
1211   GList *children;
1212
1213   g_return_if_fail (widget != NULL);
1214   g_return_if_fail (GTK_IS_LIST (widget));
1215   g_return_if_fail (allocation != NULL);
1216
1217   list = GTK_LIST (widget);
1218
1219   widget->allocation = *allocation;
1220   if (GTK_WIDGET_REALIZED (widget))
1221     gdk_window_move_resize (widget->window,
1222                             allocation->x, allocation->y,
1223                             allocation->width, allocation->height);
1224
1225   if (list->children)
1226     {
1227       child_allocation.x = GTK_CONTAINER (list)->border_width;
1228       child_allocation.y = GTK_CONTAINER (list)->border_width;
1229       child_allocation.width = MAX (1, allocation->width - child_allocation.x * 2);
1230
1231       children = list->children;
1232
1233       while (children)
1234         {
1235           child = children->data;
1236           children = children->next;
1237
1238           if (GTK_WIDGET_VISIBLE (child))
1239             {
1240               child_allocation.height = child->requisition.height;
1241
1242               gtk_widget_size_allocate (child, &child_allocation);
1243
1244               child_allocation.y += child_allocation.height;
1245             }
1246         }
1247     }
1248 }
1249
1250 static void
1251 gtk_list_add (GtkContainer *container,
1252               GtkWidget    *widget)
1253 {
1254   GList *item_list;
1255
1256   g_return_if_fail (container != NULL);
1257   g_return_if_fail (GTK_IS_LIST (container));
1258   g_return_if_fail (widget != NULL);
1259   g_return_if_fail (GTK_IS_LIST_ITEM (widget));
1260
1261   item_list = g_list_alloc ();
1262   item_list->data = widget;
1263   
1264   gtk_list_append_items (GTK_LIST (container), item_list);
1265 }
1266
1267 static void
1268 gtk_list_remove (GtkContainer *container,
1269                  GtkWidget    *widget)
1270 {
1271   GList *item_list;
1272   
1273   g_return_if_fail (container != NULL);
1274   g_return_if_fail (GTK_IS_LIST (container));
1275   g_return_if_fail (widget != NULL);
1276   g_return_if_fail (container == GTK_CONTAINER (widget->parent));
1277   
1278   
1279   item_list = g_list_alloc ();
1280   item_list->data = widget;
1281   
1282   gtk_list_remove_items (GTK_LIST (container), item_list);
1283   
1284   g_list_free (item_list);
1285 }
1286
1287 static void
1288 gtk_list_foreach (GtkContainer *container,
1289                   GtkCallback   callback,
1290                   gpointer      callback_data)
1291 {
1292   GtkList *list;
1293   GtkWidget *child;
1294   GList *children;
1295
1296   g_return_if_fail (container != NULL);
1297   g_return_if_fail (GTK_IS_LIST (container));
1298   g_return_if_fail (callback != NULL);
1299
1300   list = GTK_LIST (container);
1301   children = list->children;
1302
1303   while (children)
1304     {
1305       child = children->data;
1306       children = children->next;
1307
1308       (* callback) (child, callback_data);
1309     }
1310 }
1311
1312 static void
1313 gtk_real_list_select_child (GtkList   *list,
1314                             GtkWidget *child)
1315 {
1316   GList *selection;
1317   GList *tmp_list;
1318   GtkWidget *tmp_item;
1319
1320   g_return_if_fail (list != NULL);
1321   g_return_if_fail (GTK_IS_LIST (list));
1322   g_return_if_fail (child != NULL);
1323   g_return_if_fail (GTK_IS_LIST_ITEM (child));
1324
1325   switch (list->selection_mode)
1326     {
1327     case GTK_SELECTION_SINGLE:
1328     case GTK_SELECTION_BROWSE:
1329       selection = list->selection;
1330
1331       while (selection)
1332         {
1333           tmp_item = selection->data;
1334
1335           if (tmp_item != child)
1336             {
1337               tmp_list = selection;
1338               selection = selection->next;
1339
1340               list->selection = g_list_remove_link (list->selection, tmp_list);
1341               g_list_free (tmp_list);
1342
1343               gtk_list_item_deselect (GTK_LIST_ITEM (tmp_item));
1344               gtk_widget_unref (GTK_WIDGET (tmp_item));
1345             }
1346           else
1347             selection = selection->next;
1348         }
1349
1350       if (child->state == GTK_STATE_NORMAL)
1351         {
1352           list->selection = g_list_prepend (list->selection, child);
1353           gtk_widget_ref (child);
1354           gtk_list_item_select (GTK_LIST_ITEM (child));
1355         }
1356       gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
1357       break;
1358
1359     case GTK_SELECTION_EXTENDED:
1360       if (list->anchor >= 0)
1361         return;
1362
1363     case GTK_SELECTION_MULTIPLE:
1364       if (child->state == GTK_STATE_NORMAL)
1365         {
1366           list->selection = g_list_prepend (list->selection, child);
1367           gtk_widget_ref (child);
1368           gtk_list_item_select (GTK_LIST_ITEM (child));
1369           gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
1370         }
1371       break;
1372     }
1373 }
1374
1375 static void
1376 gtk_real_list_unselect_child (GtkList   *list,
1377                               GtkWidget *child)
1378 {
1379   g_return_if_fail (list != NULL);
1380   g_return_if_fail (GTK_IS_LIST (list));
1381   g_return_if_fail (child != NULL);
1382   g_return_if_fail (GTK_IS_LIST_ITEM (child));
1383
1384   if (child->state == GTK_STATE_SELECTED)
1385     {
1386       list->selection = g_list_remove (list->selection, child);
1387       gtk_list_item_deselect (GTK_LIST_ITEM (child));
1388       gtk_widget_unref (child);
1389       gtk_signal_emit (GTK_OBJECT (list), list_signals[SELECTION_CHANGED]);
1390     }
1391 }
1392
1393
1394 /***************************************************************************/
1395
1396 static void
1397 gtk_list_set_anchor (GtkList   *list,
1398                      gboolean   add_mode,
1399                      gint       anchor,
1400                      GtkWidget *undo_focus_child)
1401 {
1402   GList *work;
1403
1404   g_return_if_fail (list != NULL);
1405   g_return_if_fail (GTK_IS_LIST (list));
1406   
1407   if (list->selection_mode != GTK_SELECTION_EXTENDED || list->anchor >= 0)
1408     return;
1409
1410   g_list_free (list->undo_selection);
1411   g_list_free (list->undo_unselection);
1412   list->undo_selection = NULL;
1413   list->undo_unselection = NULL;
1414
1415   if ((work = g_list_nth (list->children, anchor)))
1416     {
1417       if (add_mode)
1418         gtk_list_fake_toggle_row (list, GTK_WIDGET (work->data));
1419       else
1420         {
1421           gtk_list_fake_unselect_all (list, GTK_WIDGET (work->data));
1422           list->anchor_state = GTK_STATE_SELECTED;
1423         }
1424     }
1425
1426   list->anchor = anchor;
1427   list->drag_pos = anchor;
1428   list->undo_focus_child = undo_focus_child;
1429 }
1430
1431 static void
1432 gtk_list_fake_unselect_all (GtkList   *list,
1433                             GtkWidget *item)
1434 {
1435   GList *work;
1436
1437   if (item && item->state == GTK_STATE_NORMAL)
1438     gtk_widget_set_state (item, GTK_STATE_SELECTED);
1439
1440   list->undo_selection = list->selection;
1441   list->selection = NULL;
1442   
1443   for (work = list->undo_selection; work; work = work->next)
1444     if (work->data != item)
1445       gtk_widget_set_state (GTK_WIDGET (work->data), GTK_STATE_NORMAL);
1446 }
1447
1448 static void
1449 gtk_list_fake_toggle_row (GtkList   *list,
1450                           GtkWidget *item)
1451 {
1452   if (!item)
1453     return;
1454   
1455   if (item->state == GTK_STATE_NORMAL)
1456     {
1457       list->anchor_state = GTK_STATE_SELECTED;
1458       gtk_widget_set_state (item, GTK_STATE_SELECTED);
1459     }
1460   else
1461     {
1462       list->anchor_state = GTK_STATE_NORMAL;
1463       gtk_widget_set_state (item, GTK_STATE_NORMAL);
1464     }
1465 }
1466
1467 void
1468 gtk_list_scroll_horizontal (GtkList       *list,
1469                             GtkScrollType  scroll_type,
1470                             gfloat         position)
1471 {
1472   GtkAdjustment *adj;
1473
1474   g_return_if_fail (list != 0);
1475   g_return_if_fail (GTK_IS_LIST (list));
1476
1477   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
1478     return;
1479
1480   if (!(adj =
1481         gtk_object_get_data_by_id (GTK_OBJECT (list), hadjustment_key_id)))
1482     return;
1483
1484   switch (scroll_type)
1485     {
1486     case GTK_SCROLL_STEP_BACKWARD:
1487       adj->value = CLAMP (adj->value - adj->step_increment, adj->lower,
1488                           adj->upper - adj->page_size);
1489       break;
1490     case GTK_SCROLL_STEP_FORWARD:
1491       adj->value = CLAMP (adj->value + adj->step_increment, adj->lower,
1492                           adj->upper - adj->page_size);
1493       break;
1494     case GTK_SCROLL_PAGE_BACKWARD:
1495       adj->value = CLAMP (adj->value - adj->page_increment, adj->lower,
1496                           adj->upper - adj->page_size);
1497       break;
1498     case GTK_SCROLL_PAGE_FORWARD:
1499       adj->value = CLAMP (adj->value + adj->page_increment, adj->lower,
1500                           adj->upper - adj->page_size);
1501       break;
1502     case GTK_SCROLL_JUMP:
1503       adj->value = CLAMP (adj->lower + (adj->upper - adj->lower) * position,
1504                           adj->lower, adj->upper - adj->page_size);
1505       break;
1506     default:
1507       break;
1508     }
1509   gtk_adjustment_value_changed (adj);
1510 }
1511
1512 void
1513 gtk_list_scroll_vertical (GtkList       *list,
1514                           GtkScrollType  scroll_type,
1515                           gfloat         position)
1516 {
1517   g_return_if_fail (list != NULL);
1518   g_return_if_fail (GTK_IS_LIST (list));
1519
1520   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
1521     return;
1522
1523   if (list->selection_mode == GTK_SELECTION_EXTENDED)
1524     {
1525       GtkContainer *container;
1526
1527       if (list->anchor >= 0)
1528         return;
1529
1530       container = GTK_CONTAINER (list);
1531       list->undo_focus_child = container->focus_child;
1532       gtk_list_move_focus_child (list, scroll_type, position);
1533       if (container->focus_child != list->undo_focus_child && !list->add_mode)
1534         {
1535           gtk_list_unselect_all (list);
1536           gtk_list_select_child (list, container->focus_child);
1537         }
1538     }
1539   else
1540     gtk_list_move_focus_child (list, scroll_type, position);
1541 }
1542
1543 static void
1544 gtk_list_move_focus_child (GtkList       *list,
1545                            GtkScrollType  scroll_type,
1546                            gfloat         position)
1547 {
1548   GtkContainer *container;
1549   GList *work;
1550   GtkWidget *item;
1551   GtkAdjustment *adj;
1552   gint new_value;
1553
1554   g_return_if_fail (list != 0);
1555   g_return_if_fail (GTK_IS_LIST (list));
1556
1557   container = GTK_CONTAINER (list);
1558
1559   if (container->focus_child)
1560     work = g_list_find (list->children, container->focus_child);
1561   else
1562     work = list->children;
1563
1564   if (!work)
1565     return;
1566
1567   switch (scroll_type)
1568     {
1569     case GTK_SCROLL_STEP_BACKWARD:
1570       work = work->prev;
1571       if (work)
1572         gtk_widget_grab_focus (GTK_WIDGET (work->data));
1573       break;
1574
1575     case GTK_SCROLL_STEP_FORWARD:
1576       work = work->next;
1577       if (work)
1578         gtk_widget_grab_focus (GTK_WIDGET (work->data));
1579       break;
1580
1581     case GTK_SCROLL_PAGE_BACKWARD:
1582       if (!work->prev)
1583         return;
1584
1585       item = work->data;
1586       adj = gtk_object_get_data_by_id (GTK_OBJECT (list), vadjustment_key_id);
1587
1588       if (adj)
1589         {
1590           gboolean correct = FALSE;
1591
1592           new_value = adj->value;
1593
1594           if (item->allocation.y <= adj->value)
1595             {
1596               new_value = MAX (item->allocation.y + item->allocation.height
1597                                - adj->page_size, adj->lower);
1598               correct = TRUE;
1599             }
1600
1601           if (item->allocation.y > new_value)
1602             for (; work; work = work->prev)
1603               {
1604                 item = GTK_WIDGET (work->data);
1605                 if (item->allocation.y <= new_value &&
1606                     item->allocation.y + item->allocation.height > new_value)
1607                   break;
1608               }
1609           else
1610             for (; work; work = work->next)
1611               {
1612                 item = GTK_WIDGET (work->data);
1613                 if (item->allocation.y <= new_value &&
1614                     item->allocation.y + item->allocation.height > new_value)
1615                   break;
1616               }
1617
1618           if (correct && work && work->next && item->allocation.y < new_value)
1619             item = work->next->data;
1620         }
1621       else
1622         item = list->children->data;
1623           
1624       gtk_widget_grab_focus (item);
1625       break;
1626           
1627     case GTK_SCROLL_PAGE_FORWARD:
1628       if (!work->next)
1629         return;
1630
1631       item = work->data;
1632       adj = gtk_object_get_data_by_id (GTK_OBJECT (list), vadjustment_key_id);
1633
1634       if (adj)
1635         {
1636           gboolean correct = FALSE;
1637
1638           new_value = adj->value;
1639
1640           if (item->allocation.y + item->allocation.height >=
1641               adj->value + adj->page_size)
1642             {
1643               new_value = item->allocation.y;
1644               correct = TRUE;
1645             }
1646
1647           new_value = MIN (new_value + adj->page_size, adj->upper);
1648
1649           if (item->allocation.y > new_value)
1650             for (; work; work = work->prev)
1651               {
1652                 item = GTK_WIDGET (work->data);
1653                 if (item->allocation.y <= new_value &&
1654                     item->allocation.y + item->allocation.height > new_value)
1655                   break;
1656               }
1657           else
1658             for (; work; work = work->next)
1659               {
1660                 item = GTK_WIDGET (work->data);
1661                 if (item->allocation.y <= new_value &&
1662                     item->allocation.y + item->allocation.height > new_value)
1663                   break;
1664               }
1665
1666           if (correct && work && work->prev &&
1667               item->allocation.y + item->allocation.height - 1 > new_value)
1668             item = work->prev->data;
1669         }
1670       else
1671         item = g_list_last (work)->data;
1672           
1673       gtk_widget_grab_focus (item);
1674       break;
1675
1676     case GTK_SCROLL_JUMP:
1677       new_value = GTK_WIDGET(list)->allocation.height * CLAMP (position, 0, 1);
1678
1679       for (item = NULL, work = list->children; work; work =work->next)
1680         {
1681           item = GTK_WIDGET (work->data);
1682           if (item->allocation.y <= new_value &&
1683               item->allocation.y + item->allocation.height > new_value)
1684             break;
1685         }
1686
1687       gtk_widget_grab_focus (item);
1688       break;
1689
1690     default:
1691       break;
1692     }
1693 }
1694
1695 void
1696 gtk_list_select_all (GtkList *list)
1697 {
1698   GtkContainer *container;
1699   GList *work;
1700  
1701   g_return_if_fail (list != NULL);
1702   g_return_if_fail (GTK_IS_LIST (list));
1703
1704   if (!list->children)
1705     return;
1706   
1707   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
1708     gtk_list_end_drag_selection (list);
1709
1710   if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
1711     gtk_list_end_selection (list);
1712
1713   container = GTK_CONTAINER (list);
1714
1715   switch (list->selection_mode)
1716     {
1717     case GTK_SELECTION_BROWSE:
1718       if (container->focus_child)
1719         {
1720           gtk_list_select_child (list, container->focus_child);
1721           return;
1722         }
1723       break;
1724
1725     case GTK_SELECTION_EXTENDED:
1726       g_list_free (list->undo_selection);
1727       g_list_free (list->undo_unselection);
1728       list->undo_selection = NULL;
1729       list->undo_unselection = NULL;
1730
1731       if (list->children &&
1732           GTK_WIDGET_STATE (list->children->data) != GTK_STATE_SELECTED)
1733         gtk_list_fake_toggle_row (list, GTK_WIDGET (list->children->data));
1734
1735       list->anchor_state =  GTK_STATE_SELECTED;
1736       list->anchor = 0;
1737       list->drag_pos = 0;
1738       list->undo_focus_child = container->focus_child;
1739       gtk_list_update_extended_selection (list, g_list_length(list->children));
1740       gtk_list_end_selection (list);
1741       return;
1742
1743     case GTK_SELECTION_MULTIPLE:
1744       for (work = list->children; work; work = work->next)
1745         {
1746           if (GTK_WIDGET_STATE (work->data) == GTK_STATE_NORMAL)
1747             gtk_list_select_child (list, GTK_WIDGET (work->data));
1748         }
1749       return;
1750
1751     default:
1752       break;
1753     }
1754 }
1755
1756 void
1757 gtk_list_unselect_all (GtkList *list)
1758 {
1759   GtkContainer *container;
1760   GtkWidget *item;
1761   GList *work;
1762  
1763   g_return_if_fail (list != NULL);
1764   g_return_if_fail (GTK_IS_LIST (list));
1765
1766   if (!list->children)
1767     return;
1768   
1769   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
1770     gtk_list_end_drag_selection (list);
1771
1772   if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
1773     gtk_list_end_selection (list);
1774
1775   container = GTK_CONTAINER (list);
1776
1777   switch (list->selection_mode)
1778     {
1779     case GTK_SELECTION_BROWSE:
1780       if (container->focus_child)
1781         {
1782           gtk_list_select_child (list, container->focus_child);
1783           return;
1784         }
1785       break;
1786
1787     case GTK_SELECTION_EXTENDED:
1788       g_list_free (list->undo_selection);
1789       g_list_free (list->undo_unselection);
1790       list->undo_selection = NULL;
1791       list->undo_unselection = NULL;
1792
1793       list->anchor = -1;
1794       list->drag_pos = -1;
1795       list->undo_focus_child = container->focus_child;
1796       break;
1797
1798     default:
1799       break;
1800     }
1801
1802   work = list->selection;
1803
1804   while (work)
1805     {
1806       item = work->data;
1807       work = work->next;
1808       gtk_list_unselect_child (list, item);
1809     }
1810 }
1811
1812 void
1813 gtk_list_extend_selection (GtkList       *list,
1814                            GtkScrollType  scroll_type,
1815                            gfloat         position,
1816                            gboolean       auto_start_selection)
1817 {
1818   GtkContainer *container;
1819
1820   g_return_if_fail (list != NULL);
1821   g_return_if_fail (GTK_IS_LIST (list));
1822
1823   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
1824       list->selection_mode != GTK_SELECTION_EXTENDED)
1825     return;
1826
1827   container = GTK_CONTAINER (list);
1828
1829   if (auto_start_selection)
1830     {
1831       gint focus_row;
1832       focus_row = g_list_index (list->children, container->focus_child);
1833       gtk_list_set_anchor (list, list->add_mode, focus_row,
1834                            container->focus_child);
1835     }
1836   else if (list->anchor < 0)
1837     return;
1838
1839   gtk_list_move_focus_child (list, scroll_type, position);
1840   gtk_list_update_extended_selection 
1841     (list, g_list_index (list->children, container->focus_child));
1842 }
1843
1844 static void
1845 gtk_list_update_extended_selection (GtkList *list,
1846                                     gint     row)
1847 {
1848   gint i;
1849   GList *work;
1850   gint s1 = -1;
1851   gint s2 = -1;
1852   gint e1 = -1;
1853   gint e2 = -1;
1854   gint length;
1855
1856   if (row < 0)
1857     row = 0;
1858
1859   length = g_list_length (list->children);
1860   if (row >= length)
1861     row = length - 1;
1862
1863   if (list->selection_mode != GTK_SELECTION_EXTENDED || !list->anchor < 0)
1864     return;
1865
1866   /* extending downwards */
1867   if (row > list->drag_pos && list->anchor <= list->drag_pos)
1868     {
1869       s2 = list->drag_pos + 1;
1870       e2 = row;
1871     }
1872   /* extending upwards */
1873   else if (row < list->drag_pos && list->anchor >= list->drag_pos)
1874     {
1875       s2 = row;
1876       e2 = list->drag_pos - 1;
1877     }
1878   else if (row < list->drag_pos && list->anchor < list->drag_pos)
1879     {
1880       e1 = list->drag_pos;
1881       /* row and drag_pos on different sides of anchor :
1882          take back the selection between anchor and drag_pos,
1883          select between anchor and row */
1884       if (row < list->anchor)
1885         {
1886           s1 = list->anchor + 1;
1887           s2 = row;
1888           e2 = list->anchor - 1;
1889         }
1890       /* take back the selection between anchor and drag_pos */
1891       else
1892         s1 = row + 1;
1893     }
1894   else if (row > list->drag_pos && list->anchor > list->drag_pos)
1895     {
1896       s1 = list->drag_pos;
1897       /* row and drag_pos on different sides of anchor :
1898          take back the selection between anchor and drag_pos,
1899          select between anchor and row */
1900       if (row > list->anchor)
1901         {
1902           e1 = list->anchor - 1;
1903           s2 = list->anchor + 1;
1904           e2 = row;
1905         }
1906       /* take back the selection between anchor and drag_pos */
1907       else
1908         e1 = row - 1;
1909     }
1910
1911   list->drag_pos = row;
1912
1913   /* restore the elements between s1 and e1 */
1914   if (s1 >= 0)
1915     {
1916       for (i = s1, work = g_list_nth (list->children, i); i <= e1;
1917            i++, work = work->next)
1918         {
1919           if (g_list_find (list->selection, work->data))
1920             gtk_widget_set_state (GTK_WIDGET (work->data), GTK_STATE_SELECTED);
1921           else
1922             gtk_widget_set_state (GTK_WIDGET (work->data), GTK_STATE_NORMAL);
1923         }
1924     }
1925
1926   /* extend the selection between s2 and e2 */
1927   if (s2 >= 0)
1928     {
1929       for (i = s2, work = g_list_nth (list->children, i); i <= e2;
1930            i++, work = work->next)
1931         if (GTK_WIDGET (work->data)->state != list->anchor_state)
1932           gtk_widget_set_state (GTK_WIDGET (work->data), list->anchor_state);
1933     }
1934 }
1935
1936 void
1937 gtk_list_end_selection (GtkList *list)
1938 {
1939   gint i;
1940   gint e;
1941   GList *work;
1942   GtkWidget *item;
1943   gint item_index;
1944
1945   g_return_if_fail (list != NULL);
1946   g_return_if_fail (GTK_IS_LIST (list));
1947
1948   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
1949       list->anchor < 0)
1950     return;
1951
1952   i = MIN (list->anchor, list->drag_pos);
1953   e = MAX (list->anchor, list->drag_pos);
1954   
1955   list->anchor = -1;
1956   list->drag_pos = -1;
1957
1958   if (list->undo_selection)
1959     {
1960       work = list->selection;
1961       list->selection = list->undo_selection;
1962       list->undo_selection = work;
1963       work = list->selection;
1964       while (work)
1965         {
1966           item = work->data;
1967           work = work->next;
1968           item_index = g_list_index (list->children, item);
1969           if (item_index < i || item_index > e)
1970             {
1971               gtk_widget_set_state (item, GTK_STATE_SELECTED);
1972               gtk_list_unselect_child (list, item);
1973               list->undo_selection = g_list_prepend (list->undo_selection,
1974                                                      item);
1975             }
1976         }
1977     }    
1978
1979   for (work = g_list_nth (list->children, i); i <= e; i++, work = work->next)
1980     {
1981       item = work->data;
1982       if (g_list_find (list->selection, item))
1983           {
1984             if (item->state == GTK_STATE_NORMAL)
1985               {
1986                 gtk_widget_set_state (item, GTK_STATE_SELECTED);
1987                 gtk_list_unselect_child (list, item);
1988                 list->undo_selection = g_list_prepend (list->undo_selection,
1989                                                        item);
1990               }
1991           }
1992       else if (item->state == GTK_STATE_SELECTED)
1993         {
1994           gtk_widget_set_state (item, GTK_STATE_NORMAL);
1995           list->undo_unselection = g_list_prepend (list->undo_unselection,
1996                                                    item);
1997         }
1998     }
1999
2000   for (work = list->undo_unselection; work; work = work->next)
2001     gtk_list_select_child (list, GTK_WIDGET (work->data));
2002
2003 }
2004
2005 void
2006 gtk_list_start_selection (GtkList *list)
2007 {
2008   GtkContainer *container;
2009   gint focus_row;
2010
2011   g_return_if_fail (list != NULL);
2012   g_return_if_fail (GTK_IS_LIST (list));
2013
2014   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list))
2015     return;
2016
2017   container = GTK_CONTAINER (list);
2018
2019   if ((focus_row = g_list_index (list->selection, container->focus_child))
2020       >= 0)
2021     gtk_list_set_anchor (list, list->add_mode,
2022                          focus_row, container->focus_child);
2023 }
2024
2025 void
2026 gtk_list_toggle_row (GtkList   *list,
2027                      GtkWidget *item)
2028 {
2029   g_return_if_fail (list != NULL);
2030   g_return_if_fail (GTK_IS_LIST (list));
2031   g_return_if_fail (item != NULL);
2032   g_return_if_fail (GTK_IS_LIST_ITEM (item));
2033
2034   switch (list->selection_mode)
2035     {
2036     case GTK_SELECTION_EXTENDED:
2037     case GTK_SELECTION_MULTIPLE:
2038     case GTK_SELECTION_SINGLE:
2039
2040       if (item->state == GTK_STATE_SELECTED)
2041         {
2042           gtk_list_unselect_child (list, item);
2043           return;
2044         }
2045
2046     case GTK_SELECTION_BROWSE:
2047       gtk_list_select_child (list, item);
2048       break;
2049     }
2050 }
2051
2052 void
2053 gtk_list_toggle_focus_row (GtkList *list)
2054 {
2055   GtkContainer *container;
2056   gint focus_row;
2057
2058   g_return_if_fail (list != 0);
2059   g_return_if_fail (GTK_IS_LIST (list));
2060
2061   container = GTK_CONTAINER (list);
2062
2063   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
2064       !container->focus_child)
2065     return;
2066
2067   switch (list->selection_mode)
2068     {
2069     case  GTK_SELECTION_SINGLE:
2070     case  GTK_SELECTION_MULTIPLE:
2071       
2072       gtk_list_toggle_row (list, container->focus_child);
2073       break;
2074       
2075     case GTK_SELECTION_EXTENDED:
2076
2077       if ((focus_row = g_list_index (list->children, container->focus_child))
2078           < 0)
2079         return;
2080
2081       g_list_free (list->undo_selection);
2082       g_list_free (list->undo_unselection);
2083       list->undo_selection = NULL;
2084       list->undo_unselection = NULL;
2085
2086       list->anchor = focus_row;
2087       list->drag_pos = focus_row;
2088       list->undo_focus_child = container->focus_child;
2089
2090       if (list->add_mode)
2091         gtk_list_fake_toggle_row (list, container->focus_child);
2092       else
2093         gtk_list_fake_unselect_all (list, container->focus_child);
2094       
2095       gtk_list_end_selection (list);
2096       break;
2097       
2098     default:
2099       break;
2100     }
2101 }
2102
2103 void
2104 gtk_list_toggle_add_mode (GtkList *list)
2105 {
2106   GtkContainer *container;
2107
2108   g_return_if_fail (list != 0);
2109   g_return_if_fail (GTK_IS_LIST (list));
2110   
2111   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)) ||
2112       list->selection_mode != GTK_SELECTION_EXTENDED)
2113     return;
2114   
2115   container = GTK_CONTAINER (list);
2116
2117   if (list->add_mode)
2118     {
2119       list->add_mode = FALSE;
2120       list->anchor_state = GTK_STATE_SELECTED;
2121     }
2122   else
2123     list->add_mode = TRUE;
2124   
2125   if (container->focus_child)
2126     gtk_widget_queue_draw (container->focus_child);
2127 }
2128
2129 void
2130 gtk_list_undo_selection (GtkList *list)
2131 {
2132   GList *work;
2133
2134   g_return_if_fail (list != NULL);
2135   g_return_if_fail (GTK_IS_LIST (list));
2136
2137   if (list->selection_mode != GTK_SELECTION_EXTENDED ||
2138       (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (list)))
2139     return;
2140   
2141   if (list->anchor >= 0)
2142     gtk_list_end_selection (list);
2143
2144   if (!(list->undo_selection || list->undo_unselection))
2145     {
2146       gtk_list_unselect_all (list);
2147       return;
2148     }
2149
2150   for (work = list->undo_selection; work; work = work->next)
2151     gtk_list_select_child (list, GTK_WIDGET (work->data));
2152
2153   for (work = list->undo_unselection; work; work = work->next)
2154     gtk_list_unselect_child (list, GTK_WIDGET (work->data));
2155
2156   if (list->undo_focus_child)
2157     {
2158       GtkContainer *container;
2159
2160       container = GTK_CONTAINER (list);
2161
2162       if (container->focus_child &&
2163           GTK_WIDGET_HAS_FOCUS (container->focus_child))
2164         gtk_widget_grab_focus (list->undo_focus_child);
2165       else
2166         gtk_container_set_focus_child (container, list->undo_focus_child);
2167     }
2168
2169   list->undo_focus_child = NULL;
2170  
2171   g_list_free (list->undo_selection);
2172   g_list_free (list->undo_unselection);
2173   list->undo_selection = NULL;
2174   list->undo_unselection = NULL;
2175 }
2176
2177 static void
2178 gtk_list_focus_lost (GtkWidget   *item,
2179                      GdkEventKey *event,
2180                      GtkList     *list)
2181 {
2182   g_return_if_fail (list != NULL);
2183   g_return_if_fail (GTK_IS_LIST (list));
2184   g_return_if_fail (item != NULL);
2185   g_return_if_fail (GTK_IS_LIST_ITEM (item));
2186
2187   if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0 &&
2188       item == GTK_CONTAINER (list)->focus_child)
2189     gtk_list_end_selection (list);
2190 }
2191
2192 static void
2193 gtk_list_set_focus_child (GtkContainer *container,
2194                           GtkWidget    *child)
2195 {
2196   GtkList *list;
2197
2198   g_return_if_fail (container != NULL);
2199   g_return_if_fail (GTK_IS_LIST (container));
2200
2201   if (child)
2202     g_return_if_fail (GTK_IS_WIDGET (child));
2203
2204   list = GTK_LIST (container);
2205   list->last_focus_child = container->focus_child;
2206
2207   if (child != container->focus_child)
2208     {
2209       if (container->focus_child)
2210         gtk_widget_unref (container->focus_child);
2211       container->focus_child = child;
2212       if (container->focus_child)
2213         gtk_widget_ref (container->focus_child);
2214     }
2215
2216   /* check for v adjustment */
2217   if (container->focus_child)
2218     {
2219       GtkAdjustment *adjustment;
2220
2221       adjustment = gtk_object_get_data_by_id (GTK_OBJECT (container),
2222                                               vadjustment_key_id);
2223       if (adjustment)
2224         gtk_adjustment_clamp_page (adjustment,
2225                                    container->focus_child->allocation.y,
2226                                    (container->focus_child->allocation.y +
2227                                     container->focus_child->allocation.height));
2228     }
2229
2230   switch (list->selection_mode)
2231     {
2232     case GTK_SELECTION_BROWSE:
2233       if (child)
2234         gtk_list_select_child (list, child);
2235       break;
2236     default:
2237       break;
2238     }
2239 }
2240
2241
2242 static gint
2243 gtk_list_focus (GtkContainer     *container,
2244                 GtkDirectionType  direction)
2245 {
2246   gint return_val = FALSE;
2247
2248   g_return_val_if_fail (container != NULL, FALSE);
2249   g_return_val_if_fail (GTK_IS_LIST (container), FALSE);
2250
2251   if (!GTK_WIDGET_SENSITIVE (container))
2252     return_val = FALSE;
2253   else if (container->focus_child == NULL ||
2254       !GTK_WIDGET_HAS_FOCUS (container->focus_child))
2255     {
2256       if (*GTK_CONTAINER_CLASS (parent_class)->focus)
2257         return_val = GTK_CONTAINER_CLASS (parent_class)->focus
2258           (container, direction);
2259     }
2260   
2261   if (!return_val)
2262     {
2263       GtkList *list;
2264
2265       list =  GTK_LIST (container);
2266       if (list->selection_mode == GTK_SELECTION_EXTENDED && list->anchor >= 0)
2267         gtk_list_end_selection (list);
2268     }
2269
2270   return return_val;
2271 }