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