]> Pileus Git - ~andy/gtk/blob - gtk/gtkclist.c
Make prototypes for drag_data_get and drag_data_received handlers match
[~andy/gtk] / gtk / gtkclist.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball, Josh MacDonald, 
3  * Copyright (C) 1997-1998 Jay Painter <jpaint@serv.net><jpaint@gimp.org>  
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
26  */
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include "config.h"
31 #include "gtkmain.h"
32 #include "gtkclist.h"
33 #include "gtkbindings.h"
34 #include "gtkdnd.h"
35 #include <gdk/gdkx.h>
36 #include <gdk/gdkkeysyms.h>
37
38 /* length of button_actions array */
39 #define MAX_BUTTON 5
40
41 /* the number rows memchunk expands at a time */
42 #define CLIST_OPTIMUM_SIZE 512
43
44 /* the width of the column resize windows */
45 #define DRAG_WIDTH  6
46
47 /* minimum allowed width of a column */
48 #define COLUMN_MIN_WIDTH 5
49
50 /* this defigns the base grid spacing */
51 #define CELL_SPACING 1
52
53 /* added the horizontal space at the beginning and end of a row*/
54 #define COLUMN_INSET 3
55
56 /* used for auto-scrolling */
57 #define SCROLL_TIME  100
58
59 /* gives the top pixel of the given row in context of
60  * the clist's voffset */
61 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
62                                     (((row) + 1) * CELL_SPACING) + \
63                                     (clist)->voffset)
64
65 /* returns the row index from a y pixel location in the 
66  * context of the clist's voffset */
67 #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
68                                     ((clist)->row_height + CELL_SPACING))
69
70 /* gives the left pixel of the given column in context of
71  * the clist's hoffset */
72 #define COLUMN_LEFT_XPIXEL(clist, colnum)  ((clist)->column[(colnum)].area.x + \
73                                             (clist)->hoffset)
74
75 /* returns the column index from a x pixel location in the 
76  * context of the clist's hoffset */
77 static inline gint
78 COLUMN_FROM_XPIXEL (GtkCList * clist,
79                     gint x)
80 {
81   gint i, cx;
82
83   for (i = 0; i < clist->columns; i++)
84     if (clist->column[i].visible)
85       {
86         cx = clist->column[i].area.x + clist->hoffset;
87
88         if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
89             x <= (cx + clist->column[i].area.width + COLUMN_INSET))
90           return i;
91       }
92
93   /* no match */
94   return -1;
95 }
96
97 /* returns the top pixel of the given row in the context of
98  * the list height */
99 #define ROW_TOP(clist, row)        (((clist)->row_height + CELL_SPACING) * (row))
100
101 /* returns the left pixel of the given column in the context of
102  * the list width */
103 #define COLUMN_LEFT(clist, colnum) ((clist)->column[(colnum)].area.x)
104
105 /* returns the total height of the list */
106 #define LIST_HEIGHT(clist)         (((clist)->row_height * ((clist)->rows)) + \
107                                     (CELL_SPACING * ((clist)->rows + 1)))
108
109
110 /* returns the total width of the list */
111 static inline gint
112 LIST_WIDTH (GtkCList * clist) 
113 {
114   gint last_column;
115
116   for (last_column = clist->columns - 1;
117        last_column >= 0 && !clist->column[last_column].visible; last_column--);
118
119   if (last_column >= 0)
120     return (clist->column[last_column].area.x +
121             clist->column[last_column].area.width +
122             COLUMN_INSET + CELL_SPACING);
123   return 0;
124 }
125
126 #define GTK_CLIST_CLASS_FW(_widget_) GTK_CLIST_CLASS (((GtkObject*) (_widget_))->klass)
127
128 /* redraw the list if it's not frozen */
129 #define CLIST_UNFROZEN(clist)     (((GtkCList*) (clist))->freeze_count == 0)
130 #define CLIST_REFRESH(clist)    G_STMT_START { \
131   if (CLIST_UNFROZEN (clist)) \
132     GTK_CLIST_CLASS_FW (clist)->refresh ((GtkCList*) (clist)); \
133 } G_STMT_END
134
135
136 /* Signals */
137 enum
138 {
139   SELECT_ROW,
140   UNSELECT_ROW,
141   ROW_MOVE,
142   CLICK_COLUMN,
143   RESIZE_COLUMN,
144   TOGGLE_FOCUS_ROW,
145   SELECT_ALL,
146   UNSELECT_ALL,
147   UNDO_SELECTION,
148   START_SELECTION,
149   END_SELECTION,
150   TOGGLE_ADD_MODE,
151   EXTEND_SELECTION,
152   SCROLL_VERTICAL,
153   SCROLL_HORIZONTAL,
154   ABORT_COLUMN_RESIZE,
155   LAST_SIGNAL
156 };
157
158 enum
159 {
160   SYNC_REMOVE,
161   SYNC_INSERT
162 };
163
164 enum {
165   ARG_0,
166   ARG_N_COLUMNS,
167   ARG_SHADOW_TYPE,
168   ARG_SELECTION_MODE,
169   ARG_ROW_HEIGHT,
170   ARG_TITLES_ACTIVE,
171   ARG_REORDERABLE,
172   ARG_USE_DRAG_ICONS
173 };
174
175 /* GtkCList Methods */
176 static void gtk_clist_class_init (GtkCListClass *klass);
177 static void gtk_clist_init       (GtkCList      *clist);
178
179 /* GtkObject Methods */
180 static void gtk_clist_destroy  (GtkObject *object);
181 static void gtk_clist_finalize (GtkObject *object);
182 static void gtk_clist_set_arg  (GtkObject *object,
183                                 GtkArg    *arg,
184                                 guint      arg_id);
185 static void gtk_clist_get_arg  (GtkObject *object,
186                                 GtkArg    *arg,
187                                 guint      arg_id);
188
189 /* GtkWidget Methods */
190 static void gtk_clist_set_scroll_adjustments (GtkCList      *clist,
191                                               GtkAdjustment *hadjustment,
192                                               GtkAdjustment *vadjustment);
193 static void gtk_clist_realize         (GtkWidget        *widget);
194 static void gtk_clist_unrealize       (GtkWidget        *widget);
195 static void gtk_clist_map             (GtkWidget        *widget);
196 static void gtk_clist_unmap           (GtkWidget        *widget);
197 static void gtk_clist_draw            (GtkWidget        *widget,
198                                        GdkRectangle     *area);
199 static gint gtk_clist_expose          (GtkWidget        *widget,
200                                        GdkEventExpose   *event);
201 static gint gtk_clist_key_press       (GtkWidget        *widget,
202                                        GdkEventKey      *event);
203 static gint gtk_clist_button_press    (GtkWidget        *widget,
204                                        GdkEventButton   *event);
205 static gint gtk_clist_button_release  (GtkWidget        *widget,
206                                        GdkEventButton   *event);
207 static gint gtk_clist_motion          (GtkWidget        *widget, 
208                                        GdkEventMotion   *event);
209 static void gtk_clist_size_request    (GtkWidget        *widget,
210                                        GtkRequisition   *requisition);
211 static void gtk_clist_size_allocate   (GtkWidget        *widget,
212                                        GtkAllocation    *allocation);
213 static void gtk_clist_draw_focus      (GtkWidget        *widget);
214 static gint gtk_clist_focus_in        (GtkWidget        *widget,
215                                        GdkEventFocus    *event);
216 static gint gtk_clist_focus_out       (GtkWidget        *widget,
217                                        GdkEventFocus    *event);
218 static gint gtk_clist_focus           (GtkContainer     *container,
219                                        GtkDirectionType  direction);
220 static void gtk_clist_style_set       (GtkWidget        *widget,
221                                        GtkStyle         *previous_style);
222 static void gtk_clist_drag_begin      (GtkWidget        *widget,
223                                        GdkDragContext   *context);
224 static gint gtk_clist_drag_motion     (GtkWidget        *widget,
225                                        GdkDragContext   *context,
226                                        gint              x,
227                                        gint              y,
228                                        guint             time);
229 static void gtk_clist_drag_leave      (GtkWidget        *widget,
230                                        GdkDragContext   *context,
231                                        guint             time);
232 static void gtk_clist_drag_end        (GtkWidget        *widget,
233                                        GdkDragContext   *context);
234 static gboolean gtk_clist_drag_drop   (GtkWidget      *widget,
235                                        GdkDragContext *context,
236                                        gint            x,
237                                        gint            y,
238                                        guint           time);
239 static void gtk_clist_drag_data_get   (GtkWidget        *widget,
240                                        GdkDragContext   *context,
241                                        GtkSelectionData *selection_data,
242                                        guint             info,
243                                        guint             time);
244 static void gtk_clist_drag_data_received (GtkWidget        *widget,
245                                           GdkDragContext   *context,
246                                           gint              x,
247                                           gint              y,
248                                           GtkSelectionData *selection_data,
249                                           guint             info,
250                                           guint             time);
251
252 /* GtkContainer Methods */
253 static void gtk_clist_set_focus_child (GtkContainer  *container,
254                                        GtkWidget     *child);
255 static void gtk_clist_forall          (GtkContainer  *container,
256                                        gboolean       include_internals,
257                                        GtkCallback    callback,
258                                        gpointer       callback_data);
259
260 /* Selection */
261 static void toggle_row                (GtkCList      *clist,
262                                        gint           row,
263                                        gint           column,
264                                        GdkEvent      *event);
265 static void real_select_row           (GtkCList      *clist,
266                                        gint           row,
267                                        gint           column,
268                                        GdkEvent      *event);
269 static void real_unselect_row         (GtkCList      *clist,
270                                        gint           row,
271                                        gint           column,
272                                        GdkEvent      *event);
273 static void update_extended_selection (GtkCList      *clist,
274                                        gint           row);
275 static GList *selection_find          (GtkCList      *clist,
276                                        gint           row_number,
277                                        GList         *row_list_element);
278 static void real_select_all           (GtkCList      *clist);
279 static void real_unselect_all         (GtkCList      *clist);
280 static void move_vertical             (GtkCList      *clist,
281                                        gint           row,
282                                        gfloat         align);
283 static void move_horizontal           (GtkCList      *clist,
284                                        gint           diff);
285 static void real_undo_selection       (GtkCList      *clist);
286 static void fake_unselect_all         (GtkCList      *clist,
287                                        gint           row);
288 static void fake_toggle_row           (GtkCList      *clist,
289                                        gint           row);
290 static void resync_selection          (GtkCList      *clist,
291                                        GdkEvent      *event);
292 static void sync_selection            (GtkCList      *clist,
293                                        gint           row,
294                                        gint           mode);
295 static void set_anchor                (GtkCList      *clist,
296                                        gboolean       add_mode,
297                                        gint           anchor,
298                                        gint           undo_anchor);
299 static void start_selection           (GtkCList      *clist);
300 static void end_selection             (GtkCList      *clist);
301 static void toggle_add_mode           (GtkCList      *clist);
302 static void toggle_focus_row          (GtkCList      *clist);
303 static void extend_selection          (GtkCList      *clist,
304                                        GtkScrollType  scroll_type,
305                                        gfloat         position,
306                                        gboolean       auto_start_selection);
307 static gint get_selection_info        (GtkCList       *clist,
308                                        gint            x,
309                                        gint            y,
310                                        gint           *row,
311                                        gint           *column);
312
313 /* Scrolling */
314 static void move_focus_row     (GtkCList      *clist,
315                                 GtkScrollType  scroll_type,
316                                 gfloat         position);
317 static void scroll_horizontal  (GtkCList      *clist,
318                                 GtkScrollType  scroll_type,
319                                 gfloat         position);
320 static void scroll_vertical    (GtkCList      *clist,
321                                 GtkScrollType  scroll_type,
322                                 gfloat         position);
323 static void move_horizontal    (GtkCList      *clist,
324                                 gint           diff);
325 static void move_vertical      (GtkCList      *clist,
326                                 gint           row,
327                                 gfloat         align);
328 static gint horizontal_timeout (GtkCList      *clist);
329 static gint vertical_timeout   (GtkCList      *clist);
330 static void remove_grab        (GtkCList      *clist);
331
332
333 /* Resize Columns */
334 static void draw_xor_line             (GtkCList       *clist);
335 static gint new_column_width          (GtkCList       *clist,
336                                        gint            column,
337                                        gint           *x);
338 static void column_auto_resize        (GtkCList       *clist,
339                                        GtkCListRow    *clist_row,
340                                        gint            column,
341                                        gint            old_width);
342 static void real_resize_column        (GtkCList       *clist,
343                                        gint            column,
344                                        gint            width);
345 static void abort_column_resize       (GtkCList       *clist);
346 static void cell_size_request         (GtkCList       *clist,
347                                        GtkCListRow    *clist_row,
348                                        gint            column,
349                                        GtkRequisition *requisition);
350
351 /* Buttons */
352 static void column_button_create      (GtkCList       *clist,
353                                        gint            column);
354 static void column_button_clicked     (GtkWidget      *widget,
355                                        gpointer        data);
356
357 /* Adjustments */
358 static void adjust_adjustments        (GtkCList       *clist,
359                                        gboolean        block_resize);
360 static void check_exposures           (GtkCList       *clist);
361 static void vadjustment_changed       (GtkAdjustment  *adjustment,
362                                        gpointer        data);
363 static void vadjustment_value_changed (GtkAdjustment  *adjustment,
364                                        gpointer        data);
365 static void hadjustment_changed       (GtkAdjustment  *adjustment,
366                                        gpointer        data);
367 static void hadjustment_value_changed (GtkAdjustment  *adjustment,
368                                        gpointer        data);
369
370 /* Drawing */
371 static void get_cell_style   (GtkCList      *clist,
372                               GtkCListRow   *clist_row,
373                               gint           state,
374                               gint           column,
375                               GtkStyle     **style,
376                               GdkGC        **fg_gc,
377                               GdkGC        **bg_gc);
378 static gint draw_cell_pixmap (GdkWindow     *window,
379                               GdkRectangle  *clip_rectangle,
380                               GdkGC         *fg_gc,
381                               GdkPixmap     *pixmap,
382                               GdkBitmap     *mask,
383                               gint           x,
384                               gint           y,
385                               gint           width,
386                               gint           height);
387 static void draw_row         (GtkCList      *clist,
388                               GdkRectangle  *area,
389                               gint           row,
390                               GtkCListRow   *clist_row);
391 static void draw_rows        (GtkCList      *clist,
392                               GdkRectangle  *area);
393 static void clist_refresh    (GtkCList      *clist);
394 static void draw_drag_highlight (GtkCList        *clist,
395                                  GtkCListRow     *dest_row,
396                                  gint             dest_row_number,
397                                  GtkCListDragPos  drag_pos);
398      
399 /* Size Allocation / Requisition */
400 static void size_allocate_title_buttons (GtkCList *clist);
401 static void size_allocate_columns       (GtkCList *clist,
402                                          gboolean  block_resize);
403 static gint list_requisition_width      (GtkCList *clist);
404
405 /* Memory Allocation/Distruction Routines */
406 static GtkCListColumn *columns_new (GtkCList      *clist);
407 static void column_title_new       (GtkCList      *clist,
408                                     gint           column,
409                                     const gchar   *title);
410 static void columns_delete         (GtkCList      *clist);
411 static GtkCListRow *row_new        (GtkCList      *clist);
412 static void row_delete             (GtkCList      *clist,
413                                     GtkCListRow   *clist_row);
414 static void set_cell_contents      (GtkCList      *clist,
415                                     GtkCListRow   *clist_row,
416                                     gint           column,
417                                     GtkCellType    type,
418                                     const gchar   *text,
419                                     guint8         spacing,
420                                     GdkPixmap     *pixmap,
421                                     GdkBitmap     *mask);
422 static gint real_insert_row        (GtkCList      *clist,
423                                     gint           row,
424                                     gchar         *text[]);
425 static void real_remove_row        (GtkCList      *clist,
426                                     gint           row);
427 static void real_clear             (GtkCList      *clist);
428
429 /* Sorting */
430 static gint default_compare        (GtkCList      *clist,
431                                     gconstpointer  row1,
432                                     gconstpointer  row2);
433 static void real_sort_list         (GtkCList      *clist);
434 static GList *gtk_clist_merge      (GtkCList      *clist,
435                                     GList         *a,
436                                     GList         *b);
437 static GList *gtk_clist_mergesort  (GtkCList      *clist,
438                                     GList         *list,
439                                     gint           num);
440 /* Misc */
441 static gboolean title_focus (GtkCList *clist,
442                              gint      dir);
443 static void real_row_move   (GtkCList *clist,
444                              gint      source_row,
445                              gint      dest_row);
446
447
448
449 static GtkContainerClass *parent_class = NULL;
450 static guint clist_signals[LAST_SIGNAL] = {0};
451
452 static GtkTargetEntry clist_target_table = { "gtk-clist-drag-reorder", 0, 0};
453
454 GtkType
455 gtk_clist_get_type (void)
456 {
457   static GtkType clist_type = 0;
458
459   if (!clist_type)
460     {
461       static const GtkTypeInfo clist_info =
462       {
463         "GtkCList",
464         sizeof (GtkCList),
465         sizeof (GtkCListClass),
466         (GtkClassInitFunc) gtk_clist_class_init,
467         (GtkObjectInitFunc) gtk_clist_init,
468         /* reserved_1 */ NULL,
469         /* reserved_2 */ NULL,
470         (GtkClassInitFunc) NULL,
471       };
472
473       clist_type = gtk_type_unique (GTK_TYPE_CONTAINER, &clist_info);
474     }
475
476   return clist_type;
477 }
478
479 static void
480 gtk_clist_class_init (GtkCListClass *klass)
481 {
482   GtkObjectClass *object_class;
483   GtkWidgetClass *widget_class;
484   GtkContainerClass *container_class;
485   GtkBindingSet *binding_set;
486
487   object_class = (GtkObjectClass *) klass;
488   widget_class = (GtkWidgetClass *) klass;
489   container_class = (GtkContainerClass *) klass;
490
491   parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
492
493   gtk_object_add_arg_type ("GtkCList::n_columns",
494                            GTK_TYPE_UINT,
495                            GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT_ONLY,
496                            ARG_N_COLUMNS);
497   gtk_object_add_arg_type ("GtkCList::shadow_type",
498                            GTK_TYPE_SHADOW_TYPE,
499                            GTK_ARG_READWRITE,
500                            ARG_SHADOW_TYPE);
501   gtk_object_add_arg_type ("GtkCList::selection_mode",
502                            GTK_TYPE_SELECTION_MODE,
503                            GTK_ARG_READWRITE,
504                            ARG_SELECTION_MODE);
505   gtk_object_add_arg_type ("GtkCList::row_height",
506                            GTK_TYPE_UINT,
507                            GTK_ARG_READWRITE,
508                            ARG_ROW_HEIGHT);
509   gtk_object_add_arg_type ("GtkCList::reorderable",
510                            GTK_TYPE_BOOL,
511                            GTK_ARG_READWRITE,
512                            ARG_REORDERABLE);
513   gtk_object_add_arg_type ("GtkCList::titles_active",
514                            GTK_TYPE_BOOL,
515                            GTK_ARG_READWRITE,
516                            ARG_TITLES_ACTIVE);
517   gtk_object_add_arg_type ("GtkCList::use_drag_icons",
518                            GTK_TYPE_BOOL,
519                            GTK_ARG_READWRITE,
520                            ARG_USE_DRAG_ICONS);
521   
522   object_class->set_arg = gtk_clist_set_arg;
523   object_class->get_arg = gtk_clist_get_arg;
524   object_class->destroy = gtk_clist_destroy;
525   object_class->finalize = gtk_clist_finalize;
526
527
528   widget_class->set_scroll_adjustments_signal =
529     gtk_signal_new ("set_scroll_adjustments",
530                     GTK_RUN_LAST,
531                     object_class->type,
532                     GTK_SIGNAL_OFFSET (GtkCListClass, set_scroll_adjustments),
533                     gtk_marshal_NONE__POINTER_POINTER,
534                     GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
535
536   clist_signals[SELECT_ROW] =
537     gtk_signal_new ("select_row",
538                     GTK_RUN_FIRST,
539                     object_class->type,
540                     GTK_SIGNAL_OFFSET (GtkCListClass, select_row),
541                     gtk_marshal_NONE__INT_INT_POINTER,
542                     GTK_TYPE_NONE, 3,
543                     GTK_TYPE_INT,
544                     GTK_TYPE_INT,
545                     GTK_TYPE_GDK_EVENT);
546   clist_signals[UNSELECT_ROW] =
547     gtk_signal_new ("unselect_row",
548                     GTK_RUN_FIRST,
549                     object_class->type,
550                     GTK_SIGNAL_OFFSET (GtkCListClass, unselect_row),
551                     gtk_marshal_NONE__INT_INT_POINTER,
552                     GTK_TYPE_NONE, 3, GTK_TYPE_INT,
553                     GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
554   clist_signals[ROW_MOVE] =
555     gtk_signal_new ("row_move",
556                     GTK_RUN_LAST,
557                     object_class->type,
558                     GTK_SIGNAL_OFFSET (GtkCListClass, row_move),
559                     gtk_marshal_NONE__INT_INT,
560                     GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT);
561   clist_signals[CLICK_COLUMN] =
562     gtk_signal_new ("click_column",
563                     GTK_RUN_FIRST,
564                     object_class->type,
565                     GTK_SIGNAL_OFFSET (GtkCListClass, click_column),
566                     gtk_marshal_NONE__INT,
567                     GTK_TYPE_NONE, 1, GTK_TYPE_INT);
568   clist_signals[RESIZE_COLUMN] =
569     gtk_signal_new ("resize_column",
570                     GTK_RUN_LAST,
571                     object_class->type,
572                     GTK_SIGNAL_OFFSET (GtkCListClass, resize_column),
573                     gtk_marshal_NONE__INT_INT,
574                     GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT);
575
576   clist_signals[TOGGLE_FOCUS_ROW] =
577     gtk_signal_new ("toggle_focus_row",
578                     GTK_RUN_LAST | GTK_RUN_ACTION,
579                     object_class->type,
580                     GTK_SIGNAL_OFFSET (GtkCListClass, toggle_focus_row),
581                     gtk_marshal_NONE__NONE,
582                     GTK_TYPE_NONE, 0);
583   clist_signals[SELECT_ALL] =
584     gtk_signal_new ("select_all",
585                     GTK_RUN_LAST | GTK_RUN_ACTION,
586                     object_class->type,
587                     GTK_SIGNAL_OFFSET (GtkCListClass, select_all),
588                     gtk_marshal_NONE__NONE,
589                     GTK_TYPE_NONE, 0);
590   clist_signals[UNSELECT_ALL] =
591     gtk_signal_new ("unselect_all",
592                     GTK_RUN_LAST | GTK_RUN_ACTION,
593                     object_class->type,
594                     GTK_SIGNAL_OFFSET (GtkCListClass, unselect_all),
595                     gtk_marshal_NONE__NONE,
596                     GTK_TYPE_NONE, 0);
597   clist_signals[UNDO_SELECTION] =
598     gtk_signal_new ("undo_selection",
599                     GTK_RUN_LAST | GTK_RUN_ACTION,
600                     object_class->type,
601                     GTK_SIGNAL_OFFSET (GtkCListClass, undo_selection),
602                     gtk_marshal_NONE__NONE,
603                     GTK_TYPE_NONE, 0);
604   clist_signals[START_SELECTION] =
605     gtk_signal_new ("start_selection",
606                     GTK_RUN_LAST | GTK_RUN_ACTION,
607                     object_class->type,
608                     GTK_SIGNAL_OFFSET (GtkCListClass, start_selection),
609                     gtk_marshal_NONE__NONE,
610                     GTK_TYPE_NONE, 0);
611   clist_signals[END_SELECTION] =
612     gtk_signal_new ("end_selection",
613                     GTK_RUN_LAST | GTK_RUN_ACTION,
614                     object_class->type,
615                     GTK_SIGNAL_OFFSET (GtkCListClass, end_selection),
616                     gtk_marshal_NONE__NONE,
617                     GTK_TYPE_NONE, 0);
618   clist_signals[TOGGLE_ADD_MODE] =
619     gtk_signal_new ("toggle_add_mode",
620                     GTK_RUN_LAST | GTK_RUN_ACTION,
621                     object_class->type,
622                     GTK_SIGNAL_OFFSET (GtkCListClass, toggle_add_mode),
623                     gtk_marshal_NONE__NONE,
624                     GTK_TYPE_NONE, 0);
625   clist_signals[EXTEND_SELECTION] =
626     gtk_signal_new ("extend_selection",
627                     GTK_RUN_LAST | GTK_RUN_ACTION,
628                     object_class->type,
629                     GTK_SIGNAL_OFFSET (GtkCListClass, extend_selection),
630                     gtk_marshal_NONE__ENUM_FLOAT_BOOL,
631                     GTK_TYPE_NONE, 3,
632                     GTK_TYPE_SCROLL_TYPE, GTK_TYPE_FLOAT, GTK_TYPE_BOOL);
633   clist_signals[SCROLL_VERTICAL] =
634     gtk_signal_new ("scroll_vertical",
635                     GTK_RUN_LAST | GTK_RUN_ACTION,
636                     object_class->type,
637                     GTK_SIGNAL_OFFSET (GtkCListClass, scroll_vertical),
638                     gtk_marshal_NONE__ENUM_FLOAT,
639                     GTK_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, GTK_TYPE_FLOAT);
640   clist_signals[SCROLL_HORIZONTAL] =
641     gtk_signal_new ("scroll_horizontal",
642                     GTK_RUN_LAST | GTK_RUN_ACTION,
643                     object_class->type,
644                     GTK_SIGNAL_OFFSET (GtkCListClass, scroll_horizontal),
645                     gtk_marshal_NONE__ENUM_FLOAT,
646                     GTK_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, GTK_TYPE_FLOAT);
647   clist_signals[ABORT_COLUMN_RESIZE] =
648     gtk_signal_new ("abort_column_resize",
649                     GTK_RUN_LAST | GTK_RUN_ACTION,
650                     object_class->type,
651                     GTK_SIGNAL_OFFSET (GtkCListClass, abort_column_resize),
652                     gtk_marshal_NONE__NONE,
653                     GTK_TYPE_NONE, 0);
654   gtk_object_class_add_signals (object_class, clist_signals, LAST_SIGNAL);
655
656   widget_class->realize = gtk_clist_realize;
657   widget_class->unrealize = gtk_clist_unrealize;
658   widget_class->map = gtk_clist_map;
659   widget_class->unmap = gtk_clist_unmap;
660   widget_class->draw = gtk_clist_draw;
661   widget_class->button_press_event = gtk_clist_button_press;
662   widget_class->button_release_event = gtk_clist_button_release;
663   widget_class->motion_notify_event = gtk_clist_motion;
664   widget_class->expose_event = gtk_clist_expose;
665   widget_class->size_request = gtk_clist_size_request;
666   widget_class->size_allocate = gtk_clist_size_allocate;
667   widget_class->key_press_event = gtk_clist_key_press;
668   widget_class->focus_in_event = gtk_clist_focus_in;
669   widget_class->focus_out_event = gtk_clist_focus_out;
670   widget_class->draw_focus = gtk_clist_draw_focus;
671   widget_class->style_set = gtk_clist_style_set;
672   widget_class->drag_begin = gtk_clist_drag_begin;
673   widget_class->drag_end = gtk_clist_drag_end;
674   widget_class->drag_motion = gtk_clist_drag_motion;
675   widget_class->drag_leave = gtk_clist_drag_leave;
676   widget_class->drag_drop = gtk_clist_drag_drop;
677   widget_class->drag_data_get = gtk_clist_drag_data_get;
678   widget_class->drag_data_received = gtk_clist_drag_data_received;
679
680   /* container_class->add = NULL; use the default GtkContainerClass warning */
681   /* container_class->remove=NULL; use the default GtkContainerClass warning */
682
683   container_class->forall = gtk_clist_forall;
684   container_class->focus = gtk_clist_focus;
685   container_class->set_focus_child = gtk_clist_set_focus_child;
686
687   klass->set_scroll_adjustments = gtk_clist_set_scroll_adjustments;
688   klass->refresh = clist_refresh;
689   klass->select_row = real_select_row;
690   klass->unselect_row = real_unselect_row;
691   klass->row_move = real_row_move;
692   klass->undo_selection = real_undo_selection;
693   klass->resync_selection = resync_selection;
694   klass->selection_find = selection_find;
695   klass->click_column = NULL;
696   klass->resize_column = real_resize_column;
697   klass->draw_row = draw_row;
698   klass->draw_drag_highlight = draw_drag_highlight;
699   klass->insert_row = real_insert_row;
700   klass->remove_row = real_remove_row;
701   klass->clear = real_clear;
702   klass->sort_list = real_sort_list;
703   klass->select_all = real_select_all;
704   klass->unselect_all = real_unselect_all;
705   klass->fake_unselect_all = fake_unselect_all;
706   klass->scroll_horizontal = scroll_horizontal;
707   klass->scroll_vertical = scroll_vertical;
708   klass->extend_selection = extend_selection;
709   klass->toggle_focus_row = toggle_focus_row;
710   klass->toggle_add_mode = toggle_add_mode;
711   klass->start_selection = start_selection;
712   klass->end_selection = end_selection;
713   klass->abort_column_resize = abort_column_resize;
714   klass->set_cell_contents = set_cell_contents;
715   klass->cell_size_request = cell_size_request;
716
717   binding_set = gtk_binding_set_by_class (klass);
718   gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
719                                 "scroll_vertical", 2,
720                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
721                                 GTK_TYPE_FLOAT, 0.0);
722   gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
723                                 "scroll_vertical", 2,
724                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
725                                 GTK_TYPE_FLOAT, 0.0);
726   gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
727                                 "scroll_vertical", 2,
728                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
729                                 GTK_TYPE_FLOAT, 0.0);
730   gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
731                                 "scroll_vertical", 2,
732                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
733                                 GTK_TYPE_FLOAT, 0.0);
734   gtk_binding_entry_add_signal (binding_set, GDK_Home, GDK_CONTROL_MASK,
735                                 "scroll_vertical", 2,
736                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
737                                 GTK_TYPE_FLOAT, 0.0);
738   gtk_binding_entry_add_signal (binding_set, GDK_End, GDK_CONTROL_MASK,
739                                 "scroll_vertical", 2,
740                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
741                                 GTK_TYPE_FLOAT, 1.0);
742
743   gtk_binding_entry_add_signal (binding_set, GDK_Up, GDK_SHIFT_MASK,
744                                 "extend_selection", 3,
745                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
746                                 GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
747   gtk_binding_entry_add_signal (binding_set, GDK_Down, GDK_SHIFT_MASK,
748                                 "extend_selection", 3,
749                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
750                                 GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
751   gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, GDK_SHIFT_MASK,
752                                 "extend_selection", 3,
753                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
754                                 GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
755   gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, GDK_SHIFT_MASK,
756                                 "extend_selection", 3,
757                                 GTK_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
758                                 GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
759   gtk_binding_entry_add_signal (binding_set, GDK_Home,
760                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
761                                 "extend_selection", 3,
762                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
763                                 GTK_TYPE_FLOAT, 0.0, GTK_TYPE_BOOL, TRUE);
764   gtk_binding_entry_add_signal (binding_set, GDK_End,
765                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
766                                 "extend_selection", 3,
767                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
768                                 GTK_TYPE_FLOAT, 1.0, GTK_TYPE_BOOL, TRUE);
769
770   gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
771                                 "scroll_horizontal", 2,
772                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
773                                 GTK_TYPE_FLOAT, 0.0);
774   gtk_binding_entry_add_signal (binding_set, GDK_Right, 0,
775                                 "scroll_horizontal", 2,
776                                 GTK_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
777                                 GTK_TYPE_FLOAT, 0.0);
778   gtk_binding_entry_add_signal (binding_set, GDK_Home, 0,
779                                 "scroll_horizontal", 2,
780                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
781                                 GTK_TYPE_FLOAT, 0.0);
782   gtk_binding_entry_add_signal (binding_set, GDK_End, 0,
783                                 "scroll_horizontal", 2,
784                                 GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
785                                 GTK_TYPE_FLOAT, 1.0);
786
787   gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
788                                 "undo_selection", 0);
789   gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
790                                 "abort_column_resize", 0);
791   gtk_binding_entry_add_signal (binding_set, GDK_space, 0,
792                                 "toggle_focus_row", 0);
793   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK,
794                                 "toggle_add_mode", 0);
795   gtk_binding_entry_add_signal (binding_set, '/', GDK_CONTROL_MASK,
796                                 "select_all", 0);
797   gtk_binding_entry_add_signal (binding_set, '\\', GDK_CONTROL_MASK,
798                                 "unselect_all", 0);
799   gtk_binding_entry_add_signal (binding_set, GDK_Shift_L,
800                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK,
801                                 "end_selection", 0);
802   gtk_binding_entry_add_signal (binding_set, GDK_Shift_R,
803                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK,
804                                 "end_selection", 0);
805   gtk_binding_entry_add_signal (binding_set, GDK_Shift_L,
806                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK |
807                                 GDK_CONTROL_MASK,
808                                 "end_selection", 0);
809   gtk_binding_entry_add_signal (binding_set, GDK_Shift_R,
810                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK |
811                                 GDK_CONTROL_MASK,
812                                 "end_selection", 0);
813 }
814
815 static void
816 gtk_clist_set_arg (GtkObject      *object,
817                    GtkArg         *arg,
818                    guint           arg_id)
819 {
820   GtkCList *clist;
821
822   clist = GTK_CLIST (object);
823
824   switch (arg_id)
825     {
826     case ARG_N_COLUMNS: /* construct-only arg, only set when !GTK_CONSTRUCTED */
827       gtk_clist_construct (clist, MAX (1, GTK_VALUE_UINT (*arg)), NULL);
828       break;
829     case ARG_SHADOW_TYPE:
830       gtk_clist_set_shadow_type (clist, GTK_VALUE_ENUM (*arg));
831       break;
832     case ARG_SELECTION_MODE:
833       gtk_clist_set_selection_mode (clist, GTK_VALUE_ENUM (*arg));
834       break;
835     case ARG_ROW_HEIGHT:
836       gtk_clist_set_row_height (clist, GTK_VALUE_UINT (*arg));
837       break;
838     case ARG_REORDERABLE:
839       gtk_clist_set_reorderable (clist, GTK_VALUE_BOOL (*arg));
840       break;
841     case ARG_TITLES_ACTIVE:
842       if (GTK_VALUE_BOOL (*arg))
843         gtk_clist_column_titles_active (clist);
844       else
845         gtk_clist_column_titles_passive (clist);
846       break;
847     case ARG_USE_DRAG_ICONS:
848       gtk_clist_set_use_drag_icons (clist, GTK_VALUE_BOOL (*arg));
849       break;
850     default:
851       break;
852     }
853 }
854
855 static void
856 gtk_clist_get_arg (GtkObject      *object,
857                    GtkArg         *arg,
858                    guint           arg_id)
859 {
860   GtkCList *clist;
861
862   clist = GTK_CLIST (object);
863
864   switch (arg_id)
865     {
866       guint i;
867
868     case ARG_N_COLUMNS:
869       GTK_VALUE_UINT (*arg) = clist->columns;
870       break;
871     case ARG_SHADOW_TYPE:
872       GTK_VALUE_ENUM (*arg) = clist->shadow_type;
873       break;
874     case ARG_SELECTION_MODE:
875       GTK_VALUE_ENUM (*arg) = clist->selection_mode;
876       break;
877     case ARG_ROW_HEIGHT:
878       GTK_VALUE_UINT (*arg) = GTK_CLIST_ROW_HEIGHT_SET(clist) ? clist->row_height : 0;
879       break;
880     case ARG_REORDERABLE:
881       GTK_VALUE_BOOL (*arg) = GTK_CLIST_REORDERABLE (clist);
882       break;
883     case ARG_TITLES_ACTIVE:
884       GTK_VALUE_BOOL (*arg) = TRUE;
885       for (i = 0; i < clist->columns; i++)
886         if (clist->column[i].button &&
887             !GTK_WIDGET_SENSITIVE (clist->column[i].button))
888           {
889             GTK_VALUE_BOOL (*arg) = FALSE;
890             break;
891           }
892       break;
893     case ARG_USE_DRAG_ICONS:
894       GTK_VALUE_BOOL (*arg) = GTK_CLIST_USE_DRAG_ICONS (clist);
895       break;
896     default:
897       arg->type = GTK_TYPE_INVALID;
898       break;
899     }
900 }
901
902 static void
903 gtk_clist_init (GtkCList *clist)
904 {
905   clist->flags = 0;
906
907   GTK_WIDGET_UNSET_FLAGS (clist, GTK_NO_WINDOW);
908   GTK_WIDGET_SET_FLAGS (clist, GTK_CAN_FOCUS);
909   GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
910   GTK_CLIST_SET_FLAG (clist, CLIST_DRAW_DRAG_LINE);
911   GTK_CLIST_SET_FLAG (clist, CLIST_USE_DRAG_ICONS);
912
913   clist->row_mem_chunk = NULL;
914   clist->cell_mem_chunk = NULL;
915
916   clist->freeze_count = 0;
917
918   clist->rows = 0;
919   clist->row_center_offset = 0;
920   clist->row_height = 0;
921   clist->row_list = NULL;
922   clist->row_list_end = NULL;
923
924   clist->columns = 0;
925
926   clist->title_window = NULL;
927   clist->column_title_area.x = 0;
928   clist->column_title_area.y = 0;
929   clist->column_title_area.width = 1;
930   clist->column_title_area.height = 1;
931
932   clist->clist_window = NULL;
933   clist->clist_window_width = 1;
934   clist->clist_window_height = 1;
935
936   clist->hoffset = 0;
937   clist->voffset = 0;
938
939   clist->shadow_type = GTK_SHADOW_IN;
940   clist->vadjustment = NULL;
941   clist->hadjustment = NULL;
942
943   clist->button_actions[0] = GTK_BUTTON_SELECTS | GTK_BUTTON_DRAGS;
944   clist->button_actions[1] = GTK_BUTTON_IGNORED;
945   clist->button_actions[2] = GTK_BUTTON_IGNORED;
946   clist->button_actions[3] = GTK_BUTTON_IGNORED;
947   clist->button_actions[4] = GTK_BUTTON_IGNORED;
948
949   clist->cursor_drag = NULL;
950   clist->xor_gc = NULL;
951   clist->fg_gc = NULL;
952   clist->bg_gc = NULL;
953   clist->x_drag = 0;
954
955   clist->selection_mode = GTK_SELECTION_SINGLE;
956   clist->selection = NULL;
957   clist->selection_end = NULL;
958   clist->undo_selection = NULL;
959   clist->undo_unselection = NULL;
960
961   clist->focus_row = -1;
962   clist->undo_anchor = -1;
963
964   clist->anchor = -1;
965   clist->anchor_state = GTK_STATE_SELECTED;
966   clist->drag_pos = -1;
967   clist->htimer = 0;
968   clist->vtimer = 0;
969
970   clist->click_cell.row = -1;
971   clist->click_cell.column = -1;
972
973   clist->compare = default_compare;
974   clist->sort_type = GTK_SORT_ASCENDING;
975   clist->sort_column = 0;
976 }
977
978 /* Constructors */
979 void
980 gtk_clist_construct (GtkCList *clist,
981                      gint      columns,
982                      gchar    *titles[])
983 {
984   g_return_if_fail (clist != NULL);
985   g_return_if_fail (GTK_IS_CLIST (clist));
986   g_return_if_fail (columns > 0);
987   g_return_if_fail (GTK_OBJECT_CONSTRUCTED (clist) == FALSE);
988
989   /* mark the object as constructed */
990   gtk_object_constructed (GTK_OBJECT (clist));
991
992   /* initalize memory chunks, if this has not been done by any
993    * possibly derived widget
994    */
995   if (!clist->row_mem_chunk)
996     clist->row_mem_chunk = g_mem_chunk_new ("clist row mem chunk",
997                                             sizeof (GtkCListRow),
998                                             sizeof (GtkCListRow) *
999                                             CLIST_OPTIMUM_SIZE, 
1000                                             G_ALLOC_AND_FREE);
1001
1002   if (!clist->cell_mem_chunk)
1003     clist->cell_mem_chunk = g_mem_chunk_new ("clist cell mem chunk",
1004                                              sizeof (GtkCell) * columns,
1005                                              sizeof (GtkCell) * columns *
1006                                              CLIST_OPTIMUM_SIZE, 
1007                                              G_ALLOC_AND_FREE);
1008
1009   /* set number of columns, allocate memory */
1010   clist->columns = columns;
1011   clist->column = columns_new (clist);
1012
1013   /* there needs to be at least one column button 
1014    * because there is alot of code that will break if it
1015    * isn't there*/
1016   column_button_create (clist, 0);
1017
1018   if (titles)
1019     {
1020       guint i;
1021       
1022       GTK_CLIST_SET_FLAG (clist, CLIST_SHOW_TITLES);
1023       for (i = 0; i < columns; i++)
1024         gtk_clist_set_column_title (clist, i, titles[i]);
1025     }
1026   else
1027     {
1028       GTK_CLIST_UNSET_FLAG (clist, CLIST_SHOW_TITLES);
1029     }
1030 }
1031
1032 /* GTKCLIST PUBLIC INTERFACE
1033  *   gtk_clist_new
1034  *   gtk_clist_new_with_titles
1035  *   gtk_clist_set_hadjustment
1036  *   gtk_clist_set_vadjustment
1037  *   gtk_clist_get_hadjustment
1038  *   gtk_clist_get_vadjustment
1039  *   gtk_clist_set_shadow_type
1040  *   gtk_clist_set_selection_mode
1041  *   gtk_clist_freeze
1042  *   gtk_clist_thaw
1043  */
1044 GtkWidget*
1045 gtk_clist_new (gint columns)
1046 {
1047   return gtk_clist_new_with_titles (columns, NULL);
1048 }
1049  
1050 GtkWidget*
1051 gtk_clist_new_with_titles (gint   columns,
1052                            gchar *titles[])
1053 {
1054   GtkWidget *widget;
1055
1056   widget = gtk_type_new (GTK_TYPE_CLIST);
1057   gtk_clist_construct (GTK_CLIST (widget), columns, titles);
1058
1059   return widget;
1060 }
1061
1062 void
1063 gtk_clist_set_hadjustment (GtkCList      *clist,
1064                            GtkAdjustment *adjustment)
1065 {
1066   GtkAdjustment *old_adjustment;
1067
1068   g_return_if_fail (clist != NULL);
1069   g_return_if_fail (GTK_IS_CLIST (clist));
1070   if (adjustment)
1071     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1072   
1073   if (clist->hadjustment == adjustment)
1074     return;
1075   
1076   old_adjustment = clist->hadjustment;
1077
1078   if (clist->hadjustment)
1079     {
1080       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->hadjustment), clist);
1081       gtk_object_unref (GTK_OBJECT (clist->hadjustment));
1082     }
1083
1084   clist->hadjustment = adjustment;
1085
1086   if (clist->hadjustment)
1087     {
1088       gtk_object_ref (GTK_OBJECT (clist->hadjustment));
1089       gtk_object_sink (GTK_OBJECT (clist->hadjustment));
1090
1091       gtk_signal_connect (GTK_OBJECT (clist->hadjustment), "changed",
1092                           (GtkSignalFunc) hadjustment_changed,
1093                           (gpointer) clist);
1094       gtk_signal_connect (GTK_OBJECT (clist->hadjustment), "value_changed",
1095                           (GtkSignalFunc) hadjustment_value_changed,
1096                           (gpointer) clist);
1097     }
1098
1099   if (!clist->hadjustment || !old_adjustment)
1100     gtk_widget_queue_resize (GTK_WIDGET (clist));
1101 }
1102
1103 GtkAdjustment *
1104 gtk_clist_get_hadjustment (GtkCList *clist)
1105 {
1106   g_return_val_if_fail (clist != NULL, NULL);
1107   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1108
1109   return clist->hadjustment;
1110 }
1111
1112 void
1113 gtk_clist_set_vadjustment (GtkCList      *clist,
1114                            GtkAdjustment *adjustment)
1115 {
1116   GtkAdjustment *old_adjustment;
1117
1118   g_return_if_fail (clist != NULL);
1119   g_return_if_fail (GTK_IS_CLIST (clist));
1120   if (adjustment)
1121     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1122
1123   if (clist->vadjustment == adjustment)
1124     return;
1125   
1126   old_adjustment = clist->vadjustment;
1127
1128   if (clist->vadjustment)
1129     {
1130       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->vadjustment), clist);
1131       gtk_object_unref (GTK_OBJECT (clist->vadjustment));
1132     }
1133
1134   clist->vadjustment = adjustment;
1135
1136   if (clist->vadjustment)
1137     {
1138       gtk_object_ref (GTK_OBJECT (clist->vadjustment));
1139       gtk_object_sink (GTK_OBJECT (clist->vadjustment));
1140
1141       gtk_signal_connect (GTK_OBJECT (clist->vadjustment), "changed",
1142                           (GtkSignalFunc) vadjustment_changed,
1143                           (gpointer) clist);
1144       gtk_signal_connect (GTK_OBJECT (clist->vadjustment), "value_changed",
1145                           (GtkSignalFunc) vadjustment_value_changed,
1146                           (gpointer) clist);
1147     }
1148
1149   if (!clist->vadjustment || !old_adjustment)
1150     gtk_widget_queue_resize (GTK_WIDGET (clist));
1151 }
1152
1153 GtkAdjustment *
1154 gtk_clist_get_vadjustment (GtkCList *clist)
1155 {
1156   g_return_val_if_fail (clist != NULL, NULL);
1157   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1158
1159   return clist->vadjustment;
1160 }
1161
1162 static void
1163 gtk_clist_set_scroll_adjustments (GtkCList      *clist,
1164                                   GtkAdjustment *hadjustment,
1165                                   GtkAdjustment *vadjustment)
1166 {
1167   if (clist->hadjustment != hadjustment)
1168     gtk_clist_set_hadjustment (clist, hadjustment);
1169   if (clist->vadjustment != vadjustment)
1170     gtk_clist_set_vadjustment (clist, vadjustment);
1171 }
1172
1173 void
1174 gtk_clist_set_shadow_type (GtkCList      *clist,
1175                            GtkShadowType  type)
1176 {
1177   g_return_if_fail (clist != NULL);
1178   g_return_if_fail (GTK_IS_CLIST (clist));
1179
1180   clist->shadow_type = type;
1181
1182   if (GTK_WIDGET_VISIBLE (clist))
1183     gtk_widget_queue_resize (GTK_WIDGET (clist));
1184 }
1185
1186 void
1187 gtk_clist_set_selection_mode (GtkCList         *clist,
1188                               GtkSelectionMode  mode)
1189 {
1190   g_return_if_fail (clist != NULL);
1191   g_return_if_fail (GTK_IS_CLIST (clist));
1192
1193   if (mode == clist->selection_mode)
1194     return;
1195
1196   clist->selection_mode = mode;
1197   clist->anchor = -1;
1198   clist->anchor_state = GTK_STATE_SELECTED;
1199   clist->drag_pos = -1;
1200   clist->undo_anchor = clist->focus_row;
1201
1202   g_list_free (clist->undo_selection);
1203   g_list_free (clist->undo_unselection);
1204   clist->undo_selection = NULL;
1205   clist->undo_unselection = NULL;
1206
1207   switch (mode)
1208     {
1209     case GTK_SELECTION_MULTIPLE:
1210     case GTK_SELECTION_EXTENDED:
1211       return;
1212     case GTK_SELECTION_BROWSE:
1213     case GTK_SELECTION_SINGLE:
1214       gtk_clist_unselect_all (clist);
1215       break;
1216     }
1217 }
1218
1219 void
1220 gtk_clist_freeze (GtkCList *clist)
1221 {
1222   g_return_if_fail (clist != NULL);
1223   g_return_if_fail (GTK_IS_CLIST (clist));
1224
1225   clist->freeze_count++;
1226 }
1227
1228 void
1229 gtk_clist_thaw (GtkCList *clist)
1230 {
1231   g_return_if_fail (clist != NULL);
1232   g_return_if_fail (GTK_IS_CLIST (clist));
1233
1234   if (clist->freeze_count)
1235     {
1236       clist->freeze_count--;
1237       CLIST_REFRESH (clist);
1238     }
1239 }
1240
1241 /* PUBLIC COLUMN FUNCTIONS
1242  *   gtk_clist_column_titles_show
1243  *   gtk_clist_column_titles_hide
1244  *   gtk_clist_column_title_active
1245  *   gtk_clist_column_title_passive
1246  *   gtk_clist_column_titles_active
1247  *   gtk_clist_column_titles_passive
1248  *   gtk_clist_set_column_title
1249  *   gtk_clist_get_column_title
1250  *   gtk_clist_set_column_widget
1251  *   gtk_clist_set_column_justification
1252  *   gtk_clist_set_column_visibility
1253  *   gtk_clist_set_column_resizeable
1254  *   gtk_clist_set_column_auto_resize
1255  *   gtk_clist_optimal_column_width
1256  *   gtk_clist_set_column_width
1257  *   gtk_clist_set_column_min_width
1258  *   gtk_clist_set_column_max_width
1259  */
1260 void
1261 gtk_clist_column_titles_show (GtkCList *clist)
1262 {
1263   g_return_if_fail (clist != NULL);
1264   g_return_if_fail (GTK_IS_CLIST (clist));
1265
1266   if (!GTK_CLIST_SHOW_TITLES(clist))
1267     {
1268       GTK_CLIST_SET_FLAG (clist, CLIST_SHOW_TITLES);
1269       if (clist->title_window)
1270         gdk_window_show (clist->title_window);
1271       gtk_widget_queue_resize (GTK_WIDGET (clist));
1272     }
1273 }
1274
1275 void 
1276 gtk_clist_column_titles_hide (GtkCList *clist)
1277 {
1278   g_return_if_fail (clist != NULL);
1279   g_return_if_fail (GTK_IS_CLIST (clist));
1280
1281   if (GTK_CLIST_SHOW_TITLES(clist))
1282     {
1283       GTK_CLIST_UNSET_FLAG (clist, CLIST_SHOW_TITLES);
1284       if (clist->title_window)
1285         gdk_window_hide (clist->title_window);
1286       gtk_widget_queue_resize (GTK_WIDGET (clist));
1287     }
1288 }
1289
1290 void
1291 gtk_clist_column_title_active (GtkCList *clist,
1292                                gint      column)
1293 {
1294   g_return_if_fail (clist != NULL);
1295   g_return_if_fail (GTK_IS_CLIST (clist));
1296
1297   if (column < 0 || column >= clist->columns)
1298     return;
1299   if (!clist->column[column].button)
1300     return;
1301
1302   if (!GTK_WIDGET_SENSITIVE (clist->column[column].button) ||
1303       !GTK_WIDGET_CAN_FOCUS (clist->column[column].button))
1304     {
1305       GTK_WIDGET_SET_FLAGS (clist->column[column].button,
1306                             GTK_SENSITIVE | GTK_CAN_FOCUS);
1307       if (GTK_WIDGET_VISIBLE (clist))
1308         gtk_widget_queue_draw (clist->column[column].button);
1309     }
1310 }
1311
1312 void
1313 gtk_clist_column_title_passive (GtkCList *clist,
1314                                 gint      column)
1315 {
1316   g_return_if_fail (clist != NULL);
1317   g_return_if_fail (GTK_IS_CLIST (clist));
1318
1319   if (column < 0 || column >= clist->columns)
1320     return;
1321   if (!clist->column[column].button)
1322     return;
1323
1324   if (GTK_WIDGET_SENSITIVE (clist->column[column].button) ||
1325       GTK_WIDGET_CAN_FOCUS (clist->column[column].button))
1326     {
1327       GTK_WIDGET_UNSET_FLAGS (clist->column[column].button,
1328                               GTK_SENSITIVE | GTK_CAN_FOCUS);
1329       if (GTK_WIDGET_VISIBLE (clist))
1330         gtk_widget_queue_draw (clist->column[column].button);
1331     }
1332 }
1333
1334 void
1335 gtk_clist_column_titles_active (GtkCList *clist)
1336 {
1337   gint i;
1338
1339   g_return_if_fail (clist != NULL);
1340   g_return_if_fail (GTK_IS_CLIST (clist));
1341
1342   if (!GTK_CLIST_SHOW_TITLES(clist))
1343     return;
1344
1345   for (i = 0; i < clist->columns; i++)
1346     gtk_clist_column_title_active (clist, i);
1347 }
1348
1349 void
1350 gtk_clist_column_titles_passive (GtkCList *clist)
1351 {
1352   gint i;
1353
1354   g_return_if_fail (clist != NULL);
1355   g_return_if_fail (GTK_IS_CLIST (clist));
1356
1357   if (!GTK_CLIST_SHOW_TITLES(clist))
1358     return;
1359
1360   for (i = 0; i < clist->columns; i++)
1361     gtk_clist_column_title_passive (clist, i);
1362 }
1363
1364 void
1365 gtk_clist_set_column_title (GtkCList    *clist,
1366                             gint         column,
1367                             const gchar *title)
1368 {
1369   gint new_button = 0;
1370   GtkWidget *old_widget;
1371   GtkWidget *alignment = NULL;
1372   GtkWidget *label;
1373
1374   g_return_if_fail (clist != NULL);
1375   g_return_if_fail (GTK_IS_CLIST (clist));
1376
1377   if (column < 0 || column >= clist->columns)
1378     return;
1379
1380   /* if the column button doesn't currently exist,
1381    * it has to be created first */
1382   if (!clist->column[column].button)
1383     {
1384       column_button_create (clist, column);
1385       new_button = 1;
1386     }
1387
1388   column_title_new (clist, column, title);
1389
1390   /* remove and destroy the old widget */
1391   old_widget = GTK_BIN (clist->column[column].button)->child;
1392   if (old_widget)
1393     gtk_container_remove (GTK_CONTAINER (clist->column[column].button), old_widget);
1394
1395   /* create new alignment based no column justification */
1396   switch (clist->column[column].justification)
1397     {
1398     case GTK_JUSTIFY_LEFT:
1399       alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
1400       break;
1401
1402     case GTK_JUSTIFY_RIGHT:
1403       alignment = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
1404       break;
1405
1406     case GTK_JUSTIFY_CENTER:
1407       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1408       break;
1409
1410     case GTK_JUSTIFY_FILL:
1411       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1412       break;
1413     }
1414
1415   gtk_widget_push_composite_child ();
1416   label = gtk_label_new (clist->column[column].title);
1417   gtk_widget_pop_composite_child ();
1418   gtk_container_add (GTK_CONTAINER (alignment), label);
1419   gtk_container_add (GTK_CONTAINER (clist->column[column].button), alignment);
1420   gtk_widget_show (label);
1421   gtk_widget_show (alignment);
1422
1423   /* if this button didn't previously exist, then the
1424    * column button positions have to be re-computed */
1425   if (GTK_WIDGET_VISIBLE (clist) && new_button)
1426     size_allocate_title_buttons (clist);
1427 }
1428
1429 gchar *
1430 gtk_clist_get_column_title (GtkCList *clist,
1431                             gint      column)
1432 {
1433   g_return_val_if_fail (clist != NULL, NULL);
1434   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1435
1436   if (column < 0 || column >= clist->columns)
1437     return NULL;
1438
1439   return clist->column[column].title;
1440 }
1441
1442 void
1443 gtk_clist_set_column_widget (GtkCList  *clist,
1444                              gint       column,
1445                              GtkWidget *widget)
1446 {
1447   gint new_button = 0;
1448   GtkWidget *old_widget;
1449
1450   g_return_if_fail (clist != NULL);
1451   g_return_if_fail (GTK_IS_CLIST (clist));
1452
1453   if (column < 0 || column >= clist->columns)
1454     return;
1455
1456   /* if the column button doesn't currently exist,
1457    * it has to be created first */
1458   if (!clist->column[column].button)
1459     {
1460       column_button_create (clist, column);
1461       new_button = 1;
1462     }
1463
1464   column_title_new (clist, column, NULL);
1465
1466   /* remove and destroy the old widget */
1467   old_widget = GTK_BIN (clist->column[column].button)->child;
1468   if (old_widget)
1469     gtk_container_remove (GTK_CONTAINER (clist->column[column].button),
1470                           old_widget);
1471
1472   /* add and show the widget */
1473   if (widget)
1474     {
1475       gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget);
1476       gtk_widget_show (widget);
1477     }
1478
1479   /* if this button didn't previously exist, then the
1480    * column button positions have to be re-computed */
1481   if (GTK_WIDGET_VISIBLE (clist) && new_button)
1482     size_allocate_title_buttons (clist);
1483 }
1484
1485 GtkWidget *
1486 gtk_clist_get_column_widget (GtkCList *clist,
1487                              gint      column)
1488 {
1489   g_return_val_if_fail (clist != NULL, NULL);
1490   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1491
1492   if (column < 0 || column >= clist->columns)
1493     return NULL;
1494
1495   if (clist->column[column].button)
1496     return GTK_BUTTON (clist->column[column].button)->child;
1497
1498   return NULL;
1499 }
1500
1501 void
1502 gtk_clist_set_column_justification (GtkCList         *clist,
1503                                     gint              column,
1504                                     GtkJustification  justification)
1505 {
1506   GtkWidget *alignment;
1507
1508   g_return_if_fail (clist != NULL);
1509   g_return_if_fail (GTK_IS_CLIST (clist));
1510
1511   if (column < 0 || column >= clist->columns)
1512     return;
1513
1514   clist->column[column].justification = justification;
1515
1516   /* change the alinment of the button title if it's not a
1517    * custom widget */
1518   if (clist->column[column].title)
1519     {
1520       alignment = GTK_BIN (clist->column[column].button)->child;
1521
1522       switch (clist->column[column].justification)
1523         {
1524         case GTK_JUSTIFY_LEFT:
1525           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.0, 0.5, 0.0, 0.0);
1526           break;
1527
1528         case GTK_JUSTIFY_RIGHT:
1529           gtk_alignment_set (GTK_ALIGNMENT (alignment), 1.0, 0.5, 0.0, 0.0);
1530           break;
1531
1532         case GTK_JUSTIFY_CENTER:
1533           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1534           break;
1535
1536         case GTK_JUSTIFY_FILL:
1537           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1538           break;
1539
1540         default:
1541           break;
1542         }
1543     }
1544
1545   if (CLIST_UNFROZEN (clist))
1546     draw_rows (clist, NULL);
1547 }
1548
1549 void
1550 gtk_clist_set_column_visibility (GtkCList *clist,
1551                                  gint      column,
1552                                  gboolean  visible)
1553 {
1554   g_return_if_fail (clist != NULL);
1555   g_return_if_fail (GTK_IS_CLIST (clist));
1556
1557   if (column < 0 || column >= clist->columns)
1558     return;
1559   if (clist->column[column].visible == visible)
1560     return;
1561
1562   /* don't hide last visible column */
1563   if (!visible)
1564     {
1565       gint i;
1566       gint vis_columns = 0;
1567
1568       for (i = 0, vis_columns = 0; i < clist->columns && vis_columns < 2; i++)
1569         if (clist->column[i].visible)
1570           vis_columns++;
1571
1572       if (vis_columns < 2)
1573         return;
1574     }
1575
1576   clist->column[column].visible = visible;
1577
1578   if (clist->column[column].button)
1579     {
1580       if (visible)
1581         gtk_widget_show (clist->column[column].button);
1582       else
1583         gtk_widget_hide (clist->column[column].button);
1584     }
1585   
1586   gtk_widget_queue_resize (GTK_WIDGET(clist));
1587 }
1588
1589 void
1590 gtk_clist_set_column_resizeable (GtkCList *clist,
1591                                  gint      column,
1592                                  gint      resizeable)
1593 {
1594   g_return_if_fail (clist != NULL);
1595   g_return_if_fail (GTK_IS_CLIST (clist));
1596
1597   if (column < 0 || column >= clist->columns)
1598     return;
1599   if (clist->column[column].resizeable == resizeable)
1600     return;
1601
1602   clist->column[column].resizeable = resizeable;
1603   if (resizeable)
1604     clist->column[column].auto_resize = FALSE;
1605
1606   if (GTK_WIDGET_VISIBLE (clist))
1607     size_allocate_title_buttons (clist);
1608 }
1609
1610 void
1611 gtk_clist_set_column_auto_resize (GtkCList *clist,
1612                                   gint      column,
1613                                   gboolean  auto_resize)
1614 {
1615   g_return_if_fail (clist != NULL);
1616   g_return_if_fail (GTK_IS_CLIST (clist));
1617
1618   if (column < 0 || column >= clist->columns)
1619     return;
1620   if (clist->column[column].auto_resize == auto_resize)
1621     return;
1622
1623   clist->column[column].auto_resize = auto_resize;
1624   if (auto_resize)
1625     {
1626       clist->column[column].resizeable = FALSE;
1627       if (!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
1628         {
1629           gint width;
1630
1631           width = gtk_clist_optimal_column_width (clist, column);
1632           gtk_clist_set_column_width (clist, column, width);
1633         }
1634     }
1635
1636   if (GTK_WIDGET_VISIBLE (clist))
1637     size_allocate_title_buttons (clist);
1638 }
1639
1640 gint
1641 gtk_clist_columns_autosize (GtkCList *clist)
1642 {
1643   gint i;
1644   gint width;
1645
1646   g_return_val_if_fail (clist != NULL, 0);
1647   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
1648
1649   gtk_clist_freeze (clist);
1650   width = 0;
1651   for (i = 0; i < clist->columns; i++)
1652     {
1653       gtk_clist_set_column_width (clist, i,
1654                                   gtk_clist_optimal_column_width (clist, i));
1655
1656       width += clist->column[i].width;
1657     }
1658
1659   gtk_clist_thaw (clist);
1660   return width;
1661 }
1662
1663 gint
1664 gtk_clist_optimal_column_width (GtkCList *clist,
1665                                 gint      column)
1666 {
1667   GtkRequisition requisition;
1668   GList *list;
1669   gint width;
1670
1671   g_return_val_if_fail (clist != NULL, 0);
1672   g_return_val_if_fail (GTK_CLIST (clist), 0);
1673
1674   if (column < 0 || column > clist->columns)
1675     return 0;
1676
1677   if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[column].button)
1678     width = (clist->column[column].button->requisition.width)
1679 #if 0
1680              (CELL_SPACING + (2 * COLUMN_INSET)))
1681 #endif
1682                 ;
1683   else
1684     width = 0;
1685
1686   for (list = clist->row_list; list; list = list->next)
1687     {
1688       GTK_CLIST_CLASS_FW (clist)->cell_size_request
1689         (clist, GTK_CLIST_ROW (list), column, &requisition);
1690       width = MAX (width, requisition.width);
1691     }
1692
1693   return width;
1694 }
1695
1696 void
1697 gtk_clist_set_column_width (GtkCList *clist,
1698                             gint      column,
1699                             gint      width)
1700 {
1701   g_return_if_fail (clist != NULL);
1702   g_return_if_fail (GTK_IS_CLIST (clist));
1703
1704   if (column < 0 || column >= clist->columns)
1705     return;
1706
1707   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[RESIZE_COLUMN],
1708                    column, width);
1709 }
1710
1711 void
1712 gtk_clist_set_column_min_width (GtkCList *clist,
1713                                 gint      column,
1714                                 gint      min_width)
1715 {
1716   g_return_if_fail (clist != NULL);
1717   g_return_if_fail (GTK_IS_CLIST (clist));
1718
1719   if (column < 0 || column >= clist->columns)
1720     return;
1721   if (clist->column[column].min_width == min_width)
1722     return;
1723
1724   if (clist->column[column].max_width >= 0  &&
1725       clist->column[column].max_width < min_width)
1726     clist->column[column].min_width = clist->column[column].max_width;
1727   else
1728     clist->column[column].min_width = min_width;
1729
1730   if (clist->column[column].area.width < clist->column[column].min_width)
1731     gtk_clist_set_column_width (clist, column,clist->column[column].min_width);
1732 }
1733
1734 void
1735 gtk_clist_set_column_max_width (GtkCList *clist,
1736                                 gint      column,
1737                                 gint      max_width)
1738 {
1739   g_return_if_fail (clist != NULL);
1740   g_return_if_fail (GTK_IS_CLIST (clist));
1741
1742   if (column < 0 || column >= clist->columns)
1743     return;
1744   if (clist->column[column].max_width == max_width)
1745     return;
1746
1747   if (clist->column[column].min_width >= 0 && max_width >= 0 &&
1748       clist->column[column].min_width > max_width)
1749     clist->column[column].max_width = clist->column[column].min_width;
1750   else
1751     clist->column[column].max_width = max_width;
1752   
1753   if (clist->column[column].area.width > clist->column[column].max_width)
1754     gtk_clist_set_column_width (clist, column,clist->column[column].max_width);
1755 }
1756
1757 /* PRIVATE COLUMN FUNCTIONS
1758  *   column_auto_resize
1759  *   real_resize_column
1760  *   abort_column_resize
1761  *   size_allocate_title_buttons
1762  *   size_allocate_columns
1763  *   list_requisition_width
1764  *   new_column_width
1765  *   column_button_create
1766  *   column_button_clicked
1767  */
1768 static void
1769 column_auto_resize (GtkCList    *clist,
1770                     GtkCListRow *clist_row,
1771                     gint         column,
1772                     gint         old_width)
1773 {
1774   /* resize column if needed for auto_resize */
1775   GtkRequisition requisition;
1776
1777   if (!clist->column[column].auto_resize ||
1778       GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
1779     return;
1780
1781   if (clist_row)
1782     GTK_CLIST_CLASS_FW (clist)->cell_size_request (clist, clist_row,
1783                                                    column, &requisition);
1784   else
1785     requisition.width = 0;
1786
1787   if (requisition.width > clist->column[column].width)
1788     gtk_clist_set_column_width (clist, column, requisition.width);
1789   else if (requisition.width < old_width &&
1790            old_width == clist->column[column].width)
1791     {
1792       GList *list;
1793       gint new_width = 0;
1794
1795       /* run a "gtk_clist_optimal_column_width" but break, if
1796        * the column doesn't shrink */
1797       if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[column].button)
1798         new_width = (clist->column[column].button->requisition.width -
1799                      (CELL_SPACING + (2 * COLUMN_INSET)));
1800       else
1801         new_width = 0;
1802
1803       for (list = clist->row_list; list; list = list->next)
1804         {
1805           GTK_CLIST_CLASS_FW (clist)->cell_size_request
1806             (clist, GTK_CLIST_ROW (list), column, &requisition);
1807           new_width = MAX (new_width, requisition.width);
1808           if (new_width == clist->column[column].width)
1809             break;
1810         }
1811       if (new_width < clist->column[column].width)
1812         gtk_clist_set_column_width
1813           (clist, column, MAX (new_width, clist->column[column].min_width));
1814     }
1815 }
1816
1817 static void
1818 real_resize_column (GtkCList *clist,
1819                     gint      column,
1820                     gint      width)
1821 {
1822   g_return_if_fail (clist != NULL);
1823   g_return_if_fail (GTK_IS_CLIST (clist));
1824
1825   if (column < 0 || column >= clist->columns)
1826     return;
1827   
1828   if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
1829     width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
1830   if (clist->column[column].max_width >= 0 &&
1831       width > clist->column[column].max_width)
1832     width = clist->column[column].max_width;
1833
1834   clist->column[column].width = width;
1835   clist->column[column].width_set = TRUE;
1836
1837   /* FIXME: this is quite expensive to do if the widget hasn't
1838    *        been size_allocated yet, and pointless. Should
1839    *        a flag be kept
1840    */
1841   size_allocate_columns (clist, TRUE);
1842   size_allocate_title_buttons (clist);
1843
1844   CLIST_REFRESH (clist);
1845 }
1846
1847 static void
1848 abort_column_resize (GtkCList *clist)
1849 {
1850   g_return_if_fail (clist != NULL);
1851   g_return_if_fail (GTK_IS_CLIST (clist));
1852
1853   if (!GTK_CLIST_IN_DRAG(clist))
1854     return;
1855
1856   GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
1857   gtk_grab_remove (GTK_WIDGET (clist));
1858   gdk_pointer_ungrab (GDK_CURRENT_TIME);
1859   clist->drag_pos = -1;
1860
1861   if (clist->x_drag >= 0 && clist->x_drag <= clist->clist_window_width - 1)
1862     draw_xor_line (clist);
1863
1864   if (GTK_CLIST_ADD_MODE(clist))
1865     {
1866       gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_ON_OFF_DASH, 0,0);
1867       gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
1868     }
1869 }
1870
1871 static void
1872 size_allocate_title_buttons (GtkCList *clist)
1873 {
1874   GtkAllocation button_allocation;
1875   gint last_column;
1876   gint last_button = 0;
1877   gint i;
1878
1879   if (!GTK_WIDGET_REALIZED (clist))
1880     return;
1881
1882   button_allocation.x = clist->hoffset;
1883   button_allocation.y = 0;
1884   button_allocation.width = 0;
1885   button_allocation.height = clist->column_title_area.height;
1886
1887   /* find last visible column */
1888   for (last_column = clist->columns - 1; last_column >= 0; last_column--)
1889     if (clist->column[last_column].visible)
1890       break;
1891
1892   for (i = 0; i < last_column; i++)
1893     {
1894       if (!clist->column[i].visible)
1895         {
1896           last_button = i + 1;
1897           gdk_window_hide (clist->column[i].window);
1898           continue;
1899         }
1900
1901       button_allocation.width += (clist->column[i].area.width +
1902                                   CELL_SPACING + 2 * COLUMN_INSET);
1903
1904       if (!clist->column[i + 1].button)
1905         {
1906           gdk_window_hide (clist->column[i].window);
1907           continue;
1908         }
1909
1910       gtk_widget_size_allocate (clist->column[last_button].button,
1911                                 &button_allocation);
1912       button_allocation.x += button_allocation.width;
1913       button_allocation.width = 0;
1914
1915       if (clist->column[last_button].resizeable)
1916         {
1917           gdk_window_show (clist->column[last_button].window);
1918           gdk_window_move_resize (clist->column[last_button].window,
1919                                   button_allocation.x - (DRAG_WIDTH / 2), 
1920                                   0, DRAG_WIDTH,
1921                                   clist->column_title_area.height);
1922         }
1923       else
1924         gdk_window_hide (clist->column[last_button].window);
1925
1926       last_button = i + 1;
1927     }
1928
1929   button_allocation.width += (clist->column[last_column].area.width +
1930                               2 * (CELL_SPACING + COLUMN_INSET));
1931   gtk_widget_size_allocate (clist->column[last_button].button,
1932                             &button_allocation);
1933
1934   if (clist->column[last_button].resizeable)
1935     {
1936       button_allocation.x += button_allocation.width;
1937
1938       gdk_window_show (clist->column[last_button].window);
1939       gdk_window_move_resize (clist->column[last_button].window,
1940                               button_allocation.x - (DRAG_WIDTH / 2), 
1941                               0, DRAG_WIDTH, clist->column_title_area.height);
1942     }
1943   else
1944     gdk_window_hide (clist->column[last_button].window);
1945 }
1946
1947 static void
1948 size_allocate_columns (GtkCList *clist,
1949                        gboolean  block_resize)
1950 {
1951   gint xoffset = CELL_SPACING + COLUMN_INSET;
1952   gint last_column;
1953   gint i;
1954
1955   /* find last visible column and calculate correct column width */
1956   for (last_column = clist->columns - 1;
1957        last_column >= 0 && !clist->column[last_column].visible; last_column--);
1958
1959   if (last_column < 0)
1960     return;
1961
1962   for (i = 0; i <= last_column; i++)
1963     {
1964       if (!clist->column[i].visible)
1965         continue;
1966       clist->column[i].area.x = xoffset;
1967       if (clist->column[i].width_set)
1968         {
1969           if (!block_resize && GTK_CLIST_SHOW_TITLES(clist) &&
1970               clist->column[i].auto_resize && clist->column[i].button)
1971             {
1972               gint width;
1973
1974               width = (clist->column[i].button->requisition.width -
1975                        (CELL_SPACING + (2 * COLUMN_INSET)));
1976
1977               if (width > clist->column[i].width)
1978                 gtk_clist_set_column_width (clist, i, width);
1979             }
1980
1981           clist->column[i].area.width = clist->column[i].width;
1982           xoffset += clist->column[i].width + CELL_SPACING + (2* COLUMN_INSET);
1983         }
1984       else if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button)
1985         {
1986           clist->column[i].area.width =
1987             clist->column[i].button->requisition.width -
1988             (CELL_SPACING + (2 * COLUMN_INSET));
1989           xoffset += clist->column[i].button->requisition.width;
1990         }
1991     }
1992
1993   clist->column[last_column].area.width = clist->column[last_column].area.width
1994     + MAX (0, clist->clist_window_width + COLUMN_INSET - xoffset);
1995 }
1996
1997 static gint
1998 list_requisition_width (GtkCList *clist) 
1999 {
2000   gint width = CELL_SPACING;
2001   gint i;
2002
2003   for (i = clist->columns - 1; i >= 0; i--)
2004     {
2005       if (!clist->column[i].visible)
2006         continue;
2007
2008       if (clist->column[i].width_set)
2009         width += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
2010       else if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button)
2011         width += clist->column[i].button->requisition.width;
2012     }
2013
2014   return width;
2015 }
2016
2017 /* this function returns the new width of the column being resized given
2018  * the column and x position of the cursor; the x cursor position is passed
2019  * in as a pointer and automagicly corrected if it's beyond min/max limits */
2020 static gint
2021 new_column_width (GtkCList *clist,
2022                   gint      column,
2023                   gint     *x)
2024 {
2025   gint xthickness = GTK_WIDGET (clist)->style->klass->xthickness;
2026   gint width;
2027   gint cx;
2028   gint dx;
2029   gint last_column;
2030
2031   /* first translate the x position from widget->window
2032    * to clist->clist_window */
2033   cx = *x - xthickness;
2034
2035   for (last_column = clist->columns - 1;
2036        last_column >= 0 && !clist->column[last_column].visible; last_column--);
2037
2038   /* calculate new column width making sure it doesn't end up
2039    * less than the minimum width */
2040   dx = (COLUMN_LEFT_XPIXEL (clist, column) + COLUMN_INSET +
2041         (column < last_column) * CELL_SPACING);
2042   width = cx - dx;
2043
2044   if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
2045     {
2046       width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
2047       cx = dx + width;
2048       *x = cx + xthickness;
2049     }
2050   else if (clist->column[column].max_width >= COLUMN_MIN_WIDTH &&
2051            width > clist->column[column].max_width)
2052     {
2053       width = clist->column[column].max_width;
2054       cx = dx + clist->column[column].max_width;
2055       *x = cx + xthickness;
2056     }      
2057
2058   if (cx < 0 || cx > clist->clist_window_width)
2059     *x = -1;
2060
2061   return width;
2062 }
2063
2064 static void
2065 column_button_create (GtkCList *clist,
2066                       gint      column)
2067 {
2068   GtkWidget *button;
2069
2070   gtk_widget_push_composite_child ();
2071   button = clist->column[column].button = gtk_button_new ();
2072   gtk_widget_pop_composite_child ();
2073
2074   if (GTK_WIDGET_REALIZED (clist) && clist->title_window)
2075     gtk_widget_set_parent_window (clist->column[column].button,
2076                                   clist->title_window);
2077   gtk_widget_set_parent (button, GTK_WIDGET (clist));
2078
2079   gtk_signal_connect (GTK_OBJECT (button), "clicked",
2080                       (GtkSignalFunc) column_button_clicked,
2081                       (gpointer) clist);
2082
2083   gtk_widget_show (button);
2084 }
2085
2086 static void
2087 column_button_clicked (GtkWidget *widget,
2088                        gpointer   data)
2089 {
2090   gint i;
2091   GtkCList *clist;
2092
2093   g_return_if_fail (widget != NULL);
2094   g_return_if_fail (GTK_IS_CLIST (data));
2095
2096   clist = GTK_CLIST (data);
2097
2098   /* find the column who's button was pressed */
2099   for (i = 0; i < clist->columns; i++)
2100     if (clist->column[i].button == widget)
2101       break;
2102
2103   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[CLICK_COLUMN], i);
2104 }
2105
2106 void
2107 gtk_clist_set_row_height (GtkCList *clist,
2108                           guint     height)
2109 {
2110   GtkWidget *widget;
2111
2112   g_return_if_fail (clist != NULL);
2113   g_return_if_fail (GTK_IS_CLIST (clist));
2114
2115   widget = GTK_WIDGET (clist);
2116
2117   if (height > 0)
2118     {
2119       clist->row_height = height;
2120       GTK_CLIST_SET_FLAG (clist, CLIST_ROW_HEIGHT_SET);
2121     }
2122   else
2123     {
2124       GTK_CLIST_UNSET_FLAG (clist, CLIST_ROW_HEIGHT_SET);
2125       clist->row_height = 0;
2126     }
2127
2128   if (GTK_WIDGET_REALIZED (clist))
2129     {
2130       if (!GTK_CLIST_ROW_HEIGHT_SET(clist))
2131         {
2132           clist->row_height = (widget->style->font->ascent +
2133                                widget->style->font->descent + 1);
2134           clist->row_center_offset = widget->style->font->ascent + 1.5;
2135         }
2136       else
2137         clist->row_center_offset = 1.5 + (clist->row_height +
2138                                           widget->style->font->ascent -
2139                                           widget->style->font->descent - 1) / 2;
2140     }
2141       
2142   CLIST_REFRESH (clist);
2143 }
2144
2145 void
2146 gtk_clist_moveto (GtkCList *clist,
2147                   gint      row,
2148                   gint      column,
2149                   gfloat    row_align,
2150                   gfloat    col_align)
2151 {
2152   g_return_if_fail (clist != NULL);
2153   g_return_if_fail (GTK_IS_CLIST (clist));
2154
2155   if (row < -1 || row >= clist->rows)
2156     return;
2157   if (column < -1 || column >= clist->columns)
2158     return;
2159
2160   row_align = CLAMP (row_align, 0, 1);
2161   col_align = CLAMP (col_align, 0, 1);
2162
2163   /* adjust horizontal scrollbar */
2164   if (clist->hadjustment && column >= 0)
2165     {
2166       gint x;
2167
2168       x = (COLUMN_LEFT (clist, column) - CELL_SPACING - COLUMN_INSET -
2169            (col_align * (clist->clist_window_width - 2 * COLUMN_INSET -
2170                          CELL_SPACING - clist->column[column].area.width)));
2171       if (x < 0)
2172         gtk_adjustment_set_value (clist->hadjustment, 0.0);
2173       else if (x > LIST_WIDTH (clist) - clist->clist_window_width)
2174         gtk_adjustment_set_value 
2175           (clist->hadjustment, LIST_WIDTH (clist) - clist->clist_window_width);
2176       else
2177         gtk_adjustment_set_value (clist->hadjustment, x);
2178     }
2179
2180   /* adjust vertical scrollbar */
2181   if (clist->vadjustment && row >= 0)
2182     move_vertical (clist, row, row_align);
2183 }
2184
2185 /* PUBLIC CELL FUNCTIONS
2186  *   gtk_clist_get_cell_type
2187  *   gtk_clist_set_text
2188  *   gtk_clist_get_text
2189  *   gtk_clist_set_pixmap
2190  *   gtk_clist_get_pixmap
2191  *   gtk_clist_set_pixtext
2192  *   gtk_clist_get_pixtext
2193  *   gtk_clist_set_shift
2194  */
2195 GtkCellType 
2196 gtk_clist_get_cell_type (GtkCList *clist,
2197                          gint      row,
2198                          gint      column)
2199 {
2200   GtkCListRow *clist_row;
2201
2202   g_return_val_if_fail (clist != NULL, -1);
2203   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2204
2205   if (row < 0 || row >= clist->rows)
2206     return -1;
2207   if (column < 0 || column >= clist->columns)
2208     return -1;
2209
2210   clist_row = (g_list_nth (clist->row_list, row))->data;
2211
2212   return clist_row->cell[column].type;
2213 }
2214
2215 void
2216 gtk_clist_set_text (GtkCList    *clist,
2217                     gint         row,
2218                     gint         column,
2219                     const gchar *text)
2220 {
2221   GtkCListRow *clist_row;
2222
2223   g_return_if_fail (clist != NULL);
2224   g_return_if_fail (GTK_IS_CLIST (clist));
2225
2226   if (row < 0 || row >= clist->rows)
2227     return;
2228   if (column < 0 || column >= clist->columns)
2229     return;
2230
2231   clist_row = (g_list_nth (clist->row_list, row))->data;
2232
2233   /* if text is null, then the cell is empty */
2234   GTK_CLIST_CLASS_FW (clist)->set_cell_contents
2235     (clist, clist_row, column, GTK_CELL_TEXT, text, 0, NULL, NULL);
2236
2237   /* redraw the list if it's not frozen */
2238   if (CLIST_UNFROZEN (clist))
2239     {
2240       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2241         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
2242     }
2243 }
2244
2245 gint
2246 gtk_clist_get_text (GtkCList  *clist,
2247                     gint       row,
2248                     gint       column,
2249                     gchar    **text)
2250 {
2251   GtkCListRow *clist_row;
2252
2253   g_return_val_if_fail (clist != NULL, 0);
2254   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
2255
2256   if (row < 0 || row >= clist->rows)
2257     return 0;
2258   if (column < 0 || column >= clist->columns)
2259     return 0;
2260
2261   clist_row = (g_list_nth (clist->row_list, row))->data;
2262
2263   if (clist_row->cell[column].type != GTK_CELL_TEXT)
2264     return 0;
2265
2266   if (text)
2267     *text = GTK_CELL_TEXT (clist_row->cell[column])->text;
2268
2269   return 1;
2270 }
2271
2272 void
2273 gtk_clist_set_pixmap (GtkCList  *clist,
2274                       gint       row,
2275                       gint       column,
2276                       GdkPixmap *pixmap,
2277                       GdkBitmap *mask)
2278 {
2279   GtkCListRow *clist_row;
2280
2281   g_return_if_fail (clist != NULL);
2282   g_return_if_fail (GTK_IS_CLIST (clist));
2283
2284   if (row < 0 || row >= clist->rows)
2285     return;
2286   if (column < 0 || column >= clist->columns)
2287     return;
2288
2289   clist_row = (g_list_nth (clist->row_list, row))->data;
2290   
2291   gdk_pixmap_ref (pixmap);
2292   
2293   if (mask) gdk_pixmap_ref (mask);
2294   
2295   GTK_CLIST_CLASS_FW (clist)->set_cell_contents
2296     (clist, clist_row, column, GTK_CELL_PIXMAP, NULL, 0, pixmap, mask);
2297
2298   /* redraw the list if it's not frozen */
2299   if (CLIST_UNFROZEN (clist))
2300     {
2301       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2302         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
2303     }
2304 }
2305
2306 gint
2307 gtk_clist_get_pixmap (GtkCList   *clist,
2308                       gint        row,
2309                       gint        column,
2310                       GdkPixmap **pixmap,
2311                       GdkBitmap **mask)
2312 {
2313   GtkCListRow *clist_row;
2314
2315   g_return_val_if_fail (clist != NULL, 0);
2316   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
2317
2318   if (row < 0 || row >= clist->rows)
2319     return 0;
2320   if (column < 0 || column >= clist->columns)
2321     return 0;
2322
2323   clist_row = (g_list_nth (clist->row_list, row))->data;
2324
2325   if (clist_row->cell[column].type != GTK_CELL_PIXMAP)
2326     return 0;
2327
2328   if (pixmap)
2329   {
2330     *pixmap = GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap;
2331     /* mask can be NULL */
2332     *mask = GTK_CELL_PIXMAP (clist_row->cell[column])->mask;
2333   }
2334
2335   return 1;
2336 }
2337
2338 void
2339 gtk_clist_set_pixtext (GtkCList    *clist,
2340                        gint         row,
2341                        gint         column,
2342                        const gchar *text,
2343                        guint8       spacing,
2344                        GdkPixmap   *pixmap,
2345                        GdkBitmap   *mask)
2346 {
2347   GtkCListRow *clist_row;
2348
2349   g_return_if_fail (clist != NULL);
2350   g_return_if_fail (GTK_IS_CLIST (clist));
2351
2352   if (row < 0 || row >= clist->rows)
2353     return;
2354   if (column < 0 || column >= clist->columns)
2355     return;
2356
2357   clist_row = (g_list_nth (clist->row_list, row))->data;
2358   
2359   gdk_pixmap_ref (pixmap);
2360   if (mask) gdk_pixmap_ref (mask);
2361   GTK_CLIST_CLASS_FW (clist)->set_cell_contents
2362     (clist, clist_row, column, GTK_CELL_PIXTEXT, text, spacing, pixmap, mask);
2363
2364   /* redraw the list if it's not frozen */
2365   if (CLIST_UNFROZEN (clist))
2366     {
2367       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2368         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
2369     }
2370 }
2371
2372 gint
2373 gtk_clist_get_pixtext (GtkCList   *clist,
2374                        gint        row,
2375                        gint        column,
2376                        gchar     **text,
2377                        guint8     *spacing,
2378                        GdkPixmap **pixmap,
2379                        GdkBitmap **mask)
2380 {
2381   GtkCListRow *clist_row;
2382
2383   g_return_val_if_fail (clist != NULL, 0);
2384   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
2385
2386   if (row < 0 || row >= clist->rows)
2387     return 0;
2388   if (column < 0 || column >= clist->columns)
2389     return 0;
2390
2391   clist_row = (g_list_nth (clist->row_list, row))->data;
2392
2393   if (clist_row->cell[column].type != GTK_CELL_PIXTEXT)
2394     return 0;
2395
2396   if (text)
2397     *text = GTK_CELL_PIXTEXT (clist_row->cell[column])->text;
2398   if (spacing)
2399     *spacing = GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing;
2400   if (pixmap)
2401     *pixmap = GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap;
2402
2403   /* mask can be NULL */
2404   *mask = GTK_CELL_PIXTEXT (clist_row->cell[column])->mask;
2405
2406   return 1;
2407 }
2408
2409 void
2410 gtk_clist_set_shift (GtkCList *clist,
2411                      gint      row,
2412                      gint      column,
2413                      gint      vertical,
2414                      gint      horizontal)
2415 {
2416   GtkRequisition requisition;
2417   GtkCListRow *clist_row;
2418
2419   g_return_if_fail (clist != NULL);
2420   g_return_if_fail (GTK_IS_CLIST (clist));
2421
2422   if (row < 0 || row >= clist->rows)
2423     return;
2424   if (column < 0 || column >= clist->columns)
2425     return;
2426
2427   clist_row = (g_list_nth (clist->row_list, row))->data;
2428
2429   if (clist->column[column].auto_resize &&
2430       !GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
2431     GTK_CLIST_CLASS_FW (clist)->cell_size_request (clist, clist_row,
2432                                                    column, &requisition);
2433
2434   clist_row->cell[column].vertical = vertical;
2435   clist_row->cell[column].horizontal = horizontal;
2436
2437   column_auto_resize (clist, clist_row, column, requisition.width);
2438
2439   if (CLIST_UNFROZEN (clist) && gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2440     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
2441 }
2442
2443 /* PRIVATE CELL FUNCTIONS
2444  *   set_cell_contents
2445  *   cell_size_request
2446  */
2447 static void
2448 set_cell_contents (GtkCList    *clist,
2449                    GtkCListRow *clist_row,
2450                    gint         column,
2451                    GtkCellType  type,
2452                    const gchar *text,
2453                    guint8       spacing,
2454                    GdkPixmap   *pixmap,
2455                    GdkBitmap   *mask)
2456 {
2457   GtkRequisition requisition;
2458
2459   g_return_if_fail (clist != NULL);
2460   g_return_if_fail (GTK_IS_CLIST (clist));
2461   g_return_if_fail (clist_row != NULL);
2462
2463   if (clist->column[column].auto_resize &&
2464       !GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
2465     GTK_CLIST_CLASS_FW (clist)->cell_size_request (clist, clist_row,
2466                                                    column, &requisition);
2467
2468   switch (clist_row->cell[column].type)
2469     {
2470     case GTK_CELL_EMPTY:
2471       break;
2472     case GTK_CELL_TEXT:
2473       g_free (GTK_CELL_TEXT (clist_row->cell[column])->text);
2474       break;
2475     case GTK_CELL_PIXMAP:
2476       gdk_pixmap_unref (GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap);
2477       if (GTK_CELL_PIXMAP (clist_row->cell[column])->mask)
2478         gdk_bitmap_unref (GTK_CELL_PIXMAP (clist_row->cell[column])->mask);
2479       break;
2480     case GTK_CELL_PIXTEXT:
2481       g_free (GTK_CELL_PIXTEXT (clist_row->cell[column])->text);
2482       gdk_pixmap_unref (GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap);
2483       if (GTK_CELL_PIXTEXT (clist_row->cell[column])->mask)
2484         gdk_bitmap_unref (GTK_CELL_PIXTEXT (clist_row->cell[column])->mask);
2485       break;
2486     case GTK_CELL_WIDGET:
2487       /* unimplimented */
2488       break;
2489     default:
2490       break;
2491     }
2492
2493   clist_row->cell[column].type = GTK_CELL_EMPTY;
2494
2495   switch (type)
2496     {
2497     case GTK_CELL_TEXT:
2498       if (text)
2499         {
2500           clist_row->cell[column].type = GTK_CELL_TEXT;
2501           GTK_CELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
2502         }
2503       break;
2504     case GTK_CELL_PIXMAP:
2505       if (pixmap)
2506         {
2507           clist_row->cell[column].type = GTK_CELL_PIXMAP;
2508           GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap = pixmap;
2509           /* We set the mask even if it is NULL */
2510           GTK_CELL_PIXMAP (clist_row->cell[column])->mask = mask;
2511         }
2512       break;
2513     case GTK_CELL_PIXTEXT:
2514       if (text && pixmap)
2515         {
2516           clist_row->cell[column].type = GTK_CELL_PIXTEXT;
2517           GTK_CELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
2518           GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
2519           GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap = pixmap;
2520           GTK_CELL_PIXTEXT (clist_row->cell[column])->mask = mask;
2521         }
2522       break;
2523     default:
2524       break;
2525     }
2526
2527   if (clist->column[column].auto_resize &&
2528       !GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
2529     column_auto_resize (clist, clist_row, column, requisition.width);
2530 }
2531
2532 static void
2533 cell_size_request (GtkCList       *clist,
2534                    GtkCListRow    *clist_row,
2535                    gint            column,
2536                    GtkRequisition *requisition)
2537 {
2538   GtkStyle *style;
2539   gint width;
2540   gint height;
2541
2542   g_return_if_fail (clist != NULL);
2543   g_return_if_fail (GTK_IS_CLIST (clist));
2544   g_return_if_fail (requisition != NULL);
2545
2546   get_cell_style (clist, clist_row, GTK_STATE_NORMAL, column, &style,
2547                   NULL, NULL);
2548
2549   switch (clist_row->cell[column].type)
2550     {
2551     case GTK_CELL_TEXT:
2552       requisition->width =
2553         gdk_string_width (style->font,
2554                           GTK_CELL_TEXT (clist_row->cell[column])->text);
2555       requisition->height = style->font->ascent + style->font->descent;
2556       break;
2557     case GTK_CELL_PIXTEXT:
2558       gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap,
2559                            &width, &height);
2560       requisition->width = width +
2561         GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing +
2562         gdk_string_width (style->font,
2563                           GTK_CELL_TEXT (clist_row->cell[column])->text);
2564
2565       requisition->height = MAX (style->font->ascent + style->font->descent,
2566                                  height);
2567       break;
2568     case GTK_CELL_PIXMAP:
2569       gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap,
2570                            &width, &height);
2571       requisition->width = width;
2572       requisition->height = height;
2573       break;
2574     default:
2575       requisition->width  = 0;
2576       requisition->height = 0;
2577       break;
2578     }
2579
2580   requisition->width  += clist_row->cell[column].horizontal;
2581   requisition->height += clist_row->cell[column].vertical;
2582 }
2583
2584 /* PUBLIC INSERT/REMOVE ROW FUNCTIONS
2585  *   gtk_clist_prepend
2586  *   gtk_clist_append
2587  *   gtk_clist_insert
2588  *   gtk_clist_remove
2589  *   gtk_clist_clear
2590  */
2591 gint
2592 gtk_clist_prepend (GtkCList    *clist,
2593                    gchar       *text[])
2594 {
2595   g_return_val_if_fail (clist != NULL, -1);
2596   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2597   g_return_val_if_fail (text != NULL, -1);
2598
2599   return GTK_CLIST_CLASS_FW (clist)->insert_row (clist, 0, text);
2600 }
2601
2602 gint
2603 gtk_clist_append (GtkCList    *clist,
2604                   gchar       *text[])
2605 {
2606   g_return_val_if_fail (clist != NULL, -1);
2607   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2608   g_return_val_if_fail (text != NULL, -1);
2609
2610   return GTK_CLIST_CLASS_FW (clist)->insert_row (clist, clist->rows, text);
2611 }
2612
2613 gint
2614 gtk_clist_insert (GtkCList    *clist,
2615                   gint         row,
2616                   gchar       *text[])
2617 {
2618   g_return_val_if_fail (clist != NULL, -1);
2619   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2620   g_return_val_if_fail (text != NULL, -1);
2621
2622   if (row < 0 || row > clist->rows)
2623     row = clist->rows;
2624
2625   return GTK_CLIST_CLASS_FW (clist)->insert_row (clist, row, text);
2626 }
2627
2628 void
2629 gtk_clist_remove (GtkCList *clist,
2630                   gint      row)
2631 {
2632   GTK_CLIST_CLASS_FW (clist)->remove_row (clist, row);
2633 }
2634
2635 void
2636 gtk_clist_clear (GtkCList *clist)
2637 {
2638   g_return_if_fail (clist != NULL);
2639   g_return_if_fail (GTK_IS_CLIST (clist));
2640   
2641   GTK_CLIST_CLASS_FW (clist)->clear (clist);
2642 }
2643
2644 /* PRIVATE INSERT/REMOVE ROW FUNCTIONS
2645  *   real_insert_row
2646  *   real_remove_row
2647  *   real_clear
2648  *   real_row_move
2649  */
2650 static gint
2651 real_insert_row (GtkCList *clist,
2652                  gint      row,
2653                  gchar    *text[])
2654 {
2655   gint i;
2656   GtkCListRow *clist_row;
2657
2658   g_return_val_if_fail (clist != NULL, -1);
2659   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2660   g_return_val_if_fail (text != NULL, -1);
2661
2662   /* return if out of bounds */
2663   if (row < 0 || row > clist->rows)
2664     return -1;
2665
2666   /* create the row */
2667   clist_row = row_new (clist);
2668
2669   /* set the text in the row's columns */
2670   for (i = 0; i < clist->columns; i++)
2671     if (text[i])
2672       GTK_CLIST_CLASS_FW (clist)->set_cell_contents
2673         (clist, clist_row, i, GTK_CELL_TEXT, text[i], 0, NULL ,NULL);
2674
2675   if (!clist->rows)
2676     {
2677       clist->row_list = g_list_append (clist->row_list, clist_row);
2678       clist->row_list_end = clist->row_list;
2679     }
2680   else
2681     {
2682       if (GTK_CLIST_AUTO_SORT(clist))   /* override insertion pos */
2683         {
2684           GList *work;
2685           
2686           row = 0;
2687           work = clist->row_list;
2688           
2689           if (clist->sort_type == GTK_SORT_ASCENDING)
2690             {
2691               while (row < clist->rows &&
2692                      clist->compare (clist, clist_row,
2693                                      GTK_CLIST_ROW (work)) > 0)
2694                 {
2695                   row++;
2696                   work = work->next;
2697                 }
2698             }
2699           else
2700             {
2701               while (row < clist->rows &&
2702                      clist->compare (clist, clist_row,
2703                                      GTK_CLIST_ROW (work)) < 0)
2704                 {
2705                   row++;
2706                   work = work->next;
2707                 }
2708             }
2709         }
2710       
2711       /* reset the row end pointer if we're inserting at the end of the list */
2712       if (row == clist->rows)
2713         clist->row_list_end = (g_list_append (clist->row_list_end,
2714                                               clist_row))->next;
2715       else
2716         clist->row_list = g_list_insert (clist->row_list, clist_row, row);
2717
2718     }
2719   clist->rows++;
2720
2721   if (row < ROW_FROM_YPIXEL (clist, 0))
2722     clist->voffset -= (clist->row_height + CELL_SPACING);
2723
2724   /* syncronize the selection list */
2725   sync_selection (clist, row, SYNC_INSERT);
2726
2727   if (clist->rows == 1)
2728     {
2729       clist->focus_row = 0;
2730       if (clist->selection_mode == GTK_SELECTION_BROWSE)
2731         gtk_clist_select_row (clist, 0, -1);
2732     }
2733
2734   /* redraw the list if it isn't frozen */
2735   if (CLIST_UNFROZEN (clist))
2736     {
2737       adjust_adjustments (clist, FALSE);
2738
2739       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2740         draw_rows (clist, NULL);
2741     }
2742
2743   return row;
2744 }
2745
2746 static void
2747 real_remove_row (GtkCList *clist,
2748                  gint      row)
2749 {
2750   gint was_visible, was_selected;
2751   GList *list;
2752   GtkCListRow *clist_row;
2753
2754   g_return_if_fail (clist != NULL);
2755   g_return_if_fail (GTK_IS_CLIST (clist));
2756
2757   /* return if out of bounds */
2758   if (row < 0 || row > (clist->rows - 1))
2759     return;
2760
2761   was_visible = (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE);
2762   was_selected = 0;
2763
2764   /* get the row we're going to delete */
2765   list = g_list_nth (clist->row_list, row);
2766   g_assert (list != NULL);
2767   clist_row = list->data;
2768
2769   /* if we're removing a selected row, we have to make sure
2770    * it's properly unselected, and then sync up the clist->selected
2771    * list to reflect the deincrimented indexies of rows after the
2772    * removal */
2773   if (clist_row->state == GTK_STATE_SELECTED)
2774     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
2775                      row, -1, NULL);
2776
2777   /* reset the row end pointer if we're removing at the
2778    * end of the list */
2779   clist->rows--;
2780   if (clist->row_list == list)
2781     clist->row_list = g_list_next (list);
2782   if (clist->row_list_end == list)
2783     clist->row_list_end = g_list_previous (list);
2784   g_list_remove (list, clist_row);
2785
2786   /*if (clist->focus_row >=0 &&
2787       (row <= clist->focus_row || clist->focus_row >= clist->rows))
2788       clist->focus_row--;*/
2789
2790   if (row < ROW_FROM_YPIXEL (clist, 0))
2791     clist->voffset += clist->row_height + CELL_SPACING;
2792
2793   sync_selection (clist, row, SYNC_REMOVE);
2794
2795   if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection &&
2796       clist->focus_row >= 0)
2797     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
2798                      clist->focus_row, -1, NULL);
2799
2800   /* toast the row */
2801   row_delete (clist, clist_row);
2802
2803   /* redraw the row if it isn't frozen */
2804   if (CLIST_UNFROZEN (clist))
2805     {
2806       adjust_adjustments (clist, FALSE);
2807
2808       if (was_visible)
2809         draw_rows (clist, NULL);
2810     }
2811 }
2812
2813 static void
2814 real_clear (GtkCList *clist)
2815 {
2816   GList *list;
2817   GList *free_list;
2818   gint i;
2819
2820   g_return_if_fail (clist != NULL);
2821   g_return_if_fail (GTK_IS_CLIST (clist));
2822
2823   /* free up the selection list */
2824   g_list_free (clist->selection);
2825   g_list_free (clist->undo_selection);
2826   g_list_free (clist->undo_unselection);
2827
2828   clist->selection = NULL;
2829   clist->selection_end = NULL;
2830   clist->undo_selection = NULL;
2831   clist->undo_unselection = NULL;
2832   clist->voffset = 0;
2833   clist->focus_row = -1;
2834   clist->anchor = -1;
2835   clist->undo_anchor = -1;
2836   clist->anchor_state = GTK_STATE_SELECTED;
2837   clist->drag_pos = -1;
2838
2839   /* remove all the rows */
2840   GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
2841   free_list = clist->row_list;
2842   clist->row_list = NULL;
2843   clist->row_list_end = NULL;
2844   clist->rows = 0;
2845   for (list = free_list; list; list = list->next)
2846     row_delete (clist, GTK_CLIST_ROW (list));
2847   g_list_free (free_list);
2848   GTK_CLIST_UNSET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
2849   for (i = 0; i < clist->columns; i++)
2850     if (clist->column[i].auto_resize)
2851       {
2852         if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button)
2853           gtk_clist_set_column_width
2854             (clist, i, (clist->column[i].button->requisition.width -
2855                         (CELL_SPACING + (2 * COLUMN_INSET))));
2856         else
2857           gtk_clist_set_column_width (clist, i, 0);
2858       }
2859   /* zero-out the scrollbars */
2860   if (clist->vadjustment)
2861     {
2862       gtk_adjustment_set_value (clist->vadjustment, 0.0);
2863       CLIST_REFRESH (clist);
2864     }
2865   else
2866     gtk_widget_queue_resize (GTK_WIDGET (clist));
2867 }
2868
2869 static void
2870 real_row_move (GtkCList *clist,
2871                gint      source_row,
2872                gint      dest_row)
2873 {
2874   GtkCListRow *clist_row;
2875   GList *list;
2876   gint first, last;
2877   gint d;
2878
2879   g_return_if_fail (clist != NULL);
2880   g_return_if_fail (GTK_IS_CLIST (clist));
2881
2882   if (GTK_CLIST_AUTO_SORT(clist))
2883     return;
2884
2885   if (source_row < 0 || source_row >= clist->rows ||
2886       dest_row   < 0 || dest_row   >= clist->rows ||
2887       source_row == dest_row)
2888     return;
2889
2890   gtk_clist_freeze (clist);
2891
2892   /* unlink source row */
2893   clist_row = g_list_nth_data (clist->row_list, source_row);
2894   if (source_row == clist->rows - 1)
2895     clist->row_list_end = clist->row_list_end->prev;
2896   clist->row_list = g_list_remove (clist->row_list, clist_row);
2897   clist->rows--;
2898
2899   /* relink source row */
2900   clist->row_list = g_list_insert (clist->row_list, clist_row, dest_row);
2901   if (dest_row == clist->rows)
2902     clist->row_list_end = clist->row_list_end->next;
2903   clist->rows++;
2904
2905   /* sync selection */
2906   if (source_row > dest_row)
2907     {
2908       first = dest_row;
2909       last  = source_row;
2910       d = 1;
2911     }
2912   else
2913     {
2914       first = source_row;
2915       last  = dest_row;
2916       d = -1;
2917     }
2918
2919   for (list = clist->selection; list; list = list->next)
2920     {
2921       if (list->data == GINT_TO_POINTER (source_row))
2922         list->data = GINT_TO_POINTER (dest_row);
2923       else if (first <= GPOINTER_TO_INT (list->data) &&
2924                last >= GPOINTER_TO_INT (list->data))
2925         list->data = GINT_TO_POINTER (GPOINTER_TO_INT (list->data) + d);
2926     }
2927   
2928   if (clist->focus_row == source_row)
2929     clist->focus_row = dest_row;
2930   else if (clist->focus_row > first)
2931     clist->focus_row += d;
2932
2933   gtk_clist_thaw (clist);
2934 }
2935
2936 /* PUBLIC ROW FUNCTIONS
2937  *   gtk_clist_set_row_data
2938  *   gtk_clist_set_row_data_full
2939  *   gtk_clist_get_row_data
2940  *   gtk_clist_find_row_from_data
2941  *   gtk_clist_swap_rows
2942  *   gtk_clist_row_move
2943  *   gtk_clist_row_is_visible
2944  *   gtk_clist_set_foreground
2945  *   gtk_clist_set_background
2946  */
2947 void
2948 gtk_clist_set_row_data (GtkCList *clist,
2949                         gint      row,
2950                         gpointer  data)
2951 {
2952   gtk_clist_set_row_data_full (clist, row, data, NULL);
2953 }
2954
2955 void
2956 gtk_clist_set_row_data_full (GtkCList         *clist,
2957                              gint              row,
2958                              gpointer          data,
2959                              GtkDestroyNotify  destroy)
2960 {
2961   GtkCListRow *clist_row;
2962
2963   g_return_if_fail (clist != NULL);
2964   g_return_if_fail (GTK_IS_CLIST (clist));
2965
2966   if (row < 0 || row > (clist->rows - 1))
2967     return;
2968
2969   clist_row = (g_list_nth (clist->row_list, row))->data;
2970   clist_row->data = data;
2971   clist_row->destroy = destroy;
2972 }
2973
2974 gpointer
2975 gtk_clist_get_row_data (GtkCList *clist,
2976                         gint      row)
2977 {
2978   GtkCListRow *clist_row;
2979
2980   g_return_val_if_fail (clist != NULL, NULL);
2981   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
2982
2983   if (row < 0 || row > (clist->rows - 1))
2984     return NULL;
2985
2986   clist_row = (g_list_nth (clist->row_list, row))->data;
2987   return clist_row->data;
2988 }
2989
2990 gint
2991 gtk_clist_find_row_from_data (GtkCList *clist,
2992                               gpointer  data)
2993 {
2994   GList *list;
2995   gint n;
2996
2997   g_return_val_if_fail (clist != NULL, -1);
2998   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2999
3000   for (n = 0, list = clist->row_list; list; n++, list = list->next)
3001     if (GTK_CLIST_ROW (list)->data == data)
3002       return n;
3003
3004   return -1;
3005 }
3006
3007 void 
3008 gtk_clist_swap_rows (GtkCList *clist,
3009                      gint      row1, 
3010                      gint      row2)
3011 {
3012   gint first, last;
3013
3014   g_return_if_fail (clist != NULL);
3015   g_return_if_fail (GTK_IS_CLIST (clist));
3016   g_return_if_fail (row1 != row2);
3017
3018   if (GTK_CLIST_AUTO_SORT(clist))
3019     return;
3020
3021   gtk_clist_freeze (clist);
3022
3023   first = MIN (row1, row2);
3024   last  = MAX (row1, row2);
3025
3026   gtk_clist_row_move (clist, last, first);
3027   gtk_clist_row_move (clist, first + 1, last);
3028   
3029   gtk_clist_thaw (clist);
3030 }
3031
3032 void
3033 gtk_clist_row_move (GtkCList *clist,
3034                     gint      source_row,
3035                     gint      dest_row)
3036 {
3037   g_return_if_fail (clist != NULL);
3038   g_return_if_fail (GTK_IS_CLIST (clist));
3039
3040   if (GTK_CLIST_AUTO_SORT(clist))
3041     return;
3042
3043   if (source_row < 0 || source_row >= clist->rows ||
3044       dest_row   < 0 || dest_row   >= clist->rows ||
3045       source_row == dest_row)
3046     return;
3047
3048   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[ROW_MOVE],
3049                    source_row, dest_row);
3050 }
3051
3052 GtkVisibility
3053 gtk_clist_row_is_visible (GtkCList *clist,
3054                           gint      row)
3055 {
3056   gint top;
3057
3058   g_return_val_if_fail (clist != NULL, 0);
3059   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
3060
3061   if (row < 0 || row >= clist->rows)
3062     return GTK_VISIBILITY_NONE;
3063
3064   if (clist->row_height == 0)
3065     return GTK_VISIBILITY_NONE;
3066
3067   if (row < ROW_FROM_YPIXEL (clist, 0))
3068     return GTK_VISIBILITY_NONE;
3069
3070   if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height))
3071     return GTK_VISIBILITY_NONE;
3072
3073   top = ROW_TOP_YPIXEL (clist, row);
3074
3075   if ((top < 0)
3076       || ((top + clist->row_height) >= clist->clist_window_height))
3077     return GTK_VISIBILITY_PARTIAL;
3078
3079   return GTK_VISIBILITY_FULL;
3080 }
3081
3082 void
3083 gtk_clist_set_foreground (GtkCList *clist,
3084                           gint      row,
3085                           GdkColor *color)
3086 {
3087   GtkCListRow *clist_row;
3088
3089   g_return_if_fail (clist != NULL);
3090   g_return_if_fail (GTK_IS_CLIST (clist));
3091
3092   if (row < 0 || row >= clist->rows)
3093     return;
3094
3095   clist_row = (g_list_nth (clist->row_list, row))->data;
3096
3097   if (color)
3098     {
3099       clist_row->foreground = *color;
3100       clist_row->fg_set = TRUE;
3101       if (GTK_WIDGET_REALIZED (clist))
3102         gdk_color_alloc (gtk_widget_get_colormap (GTK_WIDGET (clist)),
3103                          &clist_row->foreground);
3104     }
3105   else
3106     clist_row->fg_set = FALSE;
3107
3108   if (CLIST_UNFROZEN (clist) && gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3109     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
3110 }
3111
3112 void
3113 gtk_clist_set_background (GtkCList *clist,
3114                           gint      row,
3115                           GdkColor *color)
3116 {
3117   GtkCListRow *clist_row;
3118
3119   g_return_if_fail (clist != NULL);
3120   g_return_if_fail (GTK_IS_CLIST (clist));
3121
3122   if (row < 0 || row >= clist->rows)
3123     return;
3124
3125   clist_row = (g_list_nth (clist->row_list, row))->data;
3126
3127   if (color)
3128     {
3129       clist_row->background = *color;
3130       clist_row->bg_set = TRUE;
3131       if (GTK_WIDGET_REALIZED (clist))
3132         gdk_color_alloc (gtk_widget_get_colormap (GTK_WIDGET (clist)),
3133                          &clist_row->background);
3134     }
3135   else
3136     clist_row->bg_set = FALSE;
3137
3138   if (CLIST_UNFROZEN (clist)
3139       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3140     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
3141 }
3142
3143 /* PUBLIC ROW/CELL STYLE FUNCTIONS
3144  *   gtk_clist_set_cell_style
3145  *   gtk_clist_get_cell_style
3146  *   gtk_clist_set_row_style
3147  *   gtk_clist_get_row_style
3148  */
3149 void
3150 gtk_clist_set_cell_style (GtkCList *clist,
3151                           gint      row,
3152                           gint      column,
3153                           GtkStyle *style)
3154 {
3155   GtkRequisition requisition;
3156   GtkCListRow *clist_row;
3157
3158   g_return_if_fail (clist != NULL);
3159   g_return_if_fail (GTK_IS_CLIST (clist));
3160
3161   if (row < 0 || row >= clist->rows)
3162     return;
3163   if (column < 0 || column >= clist->columns)
3164     return;
3165
3166   clist_row = (g_list_nth (clist->row_list, row))->data;
3167
3168   if (clist_row->cell[column].style == style)
3169     return;
3170
3171   if (clist->column[column].auto_resize &&
3172       !GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
3173     GTK_CLIST_CLASS_FW (clist)->cell_size_request (clist, clist_row,
3174                                                    column, &requisition);
3175
3176   if (clist_row->cell[column].style)
3177     {
3178       if (GTK_WIDGET_REALIZED (clist))
3179         gtk_style_detach (clist_row->cell[column].style);
3180       gtk_style_unref (clist_row->cell[column].style);
3181     }
3182
3183   clist_row->cell[column].style = style;
3184
3185   if (clist_row->cell[column].style)
3186     {
3187       gtk_style_ref (clist_row->cell[column].style);
3188       
3189       if (GTK_WIDGET_REALIZED (clist))
3190         clist_row->cell[column].style =
3191           gtk_style_attach (clist_row->cell[column].style,
3192                             clist->clist_window);
3193     }
3194
3195   column_auto_resize (clist, clist_row, column, requisition.width);
3196
3197   /* redraw the list if it's not frozen */
3198   if (CLIST_UNFROZEN (clist))
3199     {
3200       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3201         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
3202     }
3203 }
3204
3205 GtkStyle *
3206 gtk_clist_get_cell_style (GtkCList *clist,
3207                           gint      row,
3208                           gint      column)
3209 {
3210   GtkCListRow *clist_row;
3211
3212   g_return_val_if_fail (clist != NULL, NULL);
3213   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
3214
3215   if (row < 0 || row >= clist->rows || column < 0 || column >= clist->columns)
3216     return NULL;
3217
3218   clist_row = (g_list_nth (clist->row_list, row))->data;
3219
3220   return clist_row->cell[column].style;
3221 }
3222
3223 void
3224 gtk_clist_set_row_style (GtkCList *clist,
3225                          gint      row,
3226                          GtkStyle *style)
3227 {
3228   GtkRequisition requisition;
3229   GtkCListRow *clist_row;
3230   gint *old_width;
3231   gint i;
3232
3233   g_return_if_fail (clist != NULL);
3234   g_return_if_fail (GTK_IS_CLIST (clist));
3235
3236   if (row < 0 || row >= clist->rows)
3237     return;
3238
3239   clist_row = (g_list_nth (clist->row_list, row))->data;
3240
3241   if (clist_row->style == style)
3242     return;
3243
3244   old_width = g_new (gint, clist->columns);
3245
3246   if (!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
3247     {
3248       for (i = 0; i < clist->columns; i++)
3249         if (clist->column[i].auto_resize)
3250           {
3251             GTK_CLIST_CLASS_FW (clist)->cell_size_request (clist, clist_row,
3252                                                            i, &requisition);
3253             old_width[i] = requisition.width;
3254           }
3255     }
3256
3257   if (clist_row->style)
3258     {
3259       if (GTK_WIDGET_REALIZED (clist))
3260         gtk_style_detach (clist_row->style);
3261       gtk_style_unref (clist_row->style);
3262     }
3263
3264   clist_row->style = style;
3265
3266   if (clist_row->style)
3267     {
3268       gtk_style_ref (clist_row->style);
3269       
3270       if (GTK_WIDGET_REALIZED (clist))
3271         clist_row->style = gtk_style_attach (clist_row->style,
3272                                              clist->clist_window);
3273     }
3274
3275   if (GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
3276     for (i = 0; i < clist->columns; i++)
3277       column_auto_resize (clist, clist_row, i, old_width[i]);
3278
3279   g_free (old_width);
3280
3281   /* redraw the list if it's not frozen */
3282   if (CLIST_UNFROZEN (clist))
3283     {
3284       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3285         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
3286     }
3287 }
3288
3289 GtkStyle *
3290 gtk_clist_get_row_style (GtkCList *clist,
3291                          gint      row)
3292 {
3293   GtkCListRow *clist_row;
3294
3295   g_return_val_if_fail (clist != NULL, NULL);
3296   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
3297
3298   if (row < 0 || row >= clist->rows)
3299     return NULL;
3300
3301   clist_row = (g_list_nth (clist->row_list, row))->data;
3302
3303   return clist_row->style;
3304 }
3305
3306 /* PUBLIC SELECTION FUNCTIONS
3307  *   gtk_clist_set_selectable
3308  *   gtk_clist_get_selectable
3309  *   gtk_clist_select_row
3310  *   gtk_clist_unselect_row
3311  *   gtk_clist_select_all
3312  *   gtk_clist_unselect_all
3313  *   gtk_clist_undo_selection
3314  */
3315 void
3316 gtk_clist_set_selectable (GtkCList *clist,
3317                           gint      row,
3318                           gboolean  selectable)
3319 {
3320   GtkCListRow *clist_row;
3321
3322   g_return_if_fail (clist != NULL);
3323   g_return_if_fail (GTK_IS_CLIST (clist));
3324
3325   if (row < 0 || row >= clist->rows)
3326     return;
3327
3328   clist_row = (g_list_nth (clist->row_list, row))->data;
3329
3330   if (selectable == clist_row->selectable)
3331     return;
3332
3333   clist_row->selectable = selectable;
3334
3335   if (!selectable && clist_row->state == GTK_STATE_SELECTED)
3336     {
3337       if (clist->anchor >= 0 &&
3338           clist->selection_mode == GTK_SELECTION_EXTENDED)
3339         {
3340           clist->drag_button = 0;
3341           remove_grab (clist);
3342           GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
3343         }
3344       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3345                        row, -1, NULL);
3346     }      
3347 }
3348
3349 gboolean
3350 gtk_clist_get_selectable (GtkCList *clist,
3351                           gint      row)
3352 {
3353   g_return_val_if_fail (clist != NULL, FALSE);
3354   g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
3355
3356   if (row < 0 || row >= clist->rows)
3357     return FALSE;
3358
3359   return GTK_CLIST_ROW (g_list_nth (clist->row_list, row))->selectable;
3360 }
3361
3362 void
3363 gtk_clist_select_row (GtkCList *clist,
3364                       gint      row,
3365                       gint      column)
3366 {
3367   g_return_if_fail (clist != NULL);
3368   g_return_if_fail (GTK_IS_CLIST (clist));
3369
3370   if (row < 0 || row >= clist->rows)
3371     return;
3372   if (column < -1 || column >= clist->columns)
3373     return;
3374
3375   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3376                    row, column, NULL);
3377 }
3378
3379 void
3380 gtk_clist_unselect_row (GtkCList *clist,
3381                         gint      row,
3382                         gint      column)
3383 {
3384   g_return_if_fail (clist != NULL);
3385   g_return_if_fail (GTK_IS_CLIST (clist));
3386
3387   if (row < 0 || row >= clist->rows)
3388     return;
3389   if (column < -1 || column >= clist->columns)
3390     return;
3391
3392   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3393                    row, column, NULL);
3394 }
3395
3396 void
3397 gtk_clist_select_all (GtkCList *clist)
3398 {
3399   g_return_if_fail (clist != NULL);
3400   g_return_if_fail (GTK_IS_CLIST (clist));
3401
3402   GTK_CLIST_CLASS_FW (clist)->select_all (clist);
3403 }
3404
3405 void
3406 gtk_clist_unselect_all (GtkCList *clist)
3407 {
3408   g_return_if_fail (clist != NULL);
3409   g_return_if_fail (GTK_IS_CLIST (clist));
3410
3411   GTK_CLIST_CLASS_FW (clist)->unselect_all (clist);
3412 }
3413
3414 void
3415 gtk_clist_undo_selection (GtkCList *clist)
3416 {
3417   g_return_if_fail (clist != NULL);
3418   g_return_if_fail (GTK_IS_CLIST (clist));
3419
3420   if (clist->selection_mode == GTK_SELECTION_EXTENDED &&
3421       (clist->undo_selection || clist->undo_unselection))
3422     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNDO_SELECTION]);
3423 }
3424
3425 /* PRIVATE SELECTION FUNCTIONS
3426  *   selection_find
3427  *   toggle_row
3428  *   fake_toggle_row
3429  *   toggle_focus_row
3430  *   toggle_add_mode
3431  *   real_select_row
3432  *   real_unselect_row
3433  *   real_select_all
3434  *   real_unselect_all
3435  *   fake_unselect_all
3436  *   real_undo_selection
3437  *   set_anchor
3438  *   resync_selection
3439  *   update_extended_selection
3440  *   start_selection
3441  *   end_selection
3442  *   extend_selection
3443  *   sync_selection
3444  */
3445 static GList *
3446 selection_find (GtkCList *clist,
3447                 gint      row_number,
3448                 GList    *row_list_element)
3449 {
3450   return g_list_find (clist->selection, GINT_TO_POINTER (row_number));
3451 }
3452
3453 static void
3454 toggle_row (GtkCList *clist,
3455             gint      row,
3456             gint      column,
3457             GdkEvent *event)
3458 {
3459   GtkCListRow *clist_row;
3460
3461   switch (clist->selection_mode)
3462     {
3463     case GTK_SELECTION_EXTENDED:
3464     case GTK_SELECTION_MULTIPLE:
3465     case GTK_SELECTION_SINGLE:
3466       clist_row = g_list_nth (clist->row_list, row)->data;
3467
3468       if (!clist_row)
3469         return;
3470
3471       if (clist_row->state == GTK_STATE_SELECTED)
3472         {
3473           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3474                            row, column, event);
3475           return;
3476         }
3477     case GTK_SELECTION_BROWSE:
3478       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3479                        row, column, event);
3480       break;
3481     }
3482 }
3483
3484 static void
3485 fake_toggle_row (GtkCList *clist,
3486                  gint      row)
3487 {
3488   GList *work;
3489
3490   work = g_list_nth (clist->row_list, row);
3491
3492   if (!work || !GTK_CLIST_ROW (work)->selectable)
3493     return;
3494   
3495   if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL)
3496     clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
3497   else
3498     clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
3499   
3500   if (CLIST_UNFROZEN (clist) &&
3501       gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3502     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row,
3503                                           GTK_CLIST_ROW (work));
3504 }
3505
3506 static void
3507 toggle_focus_row (GtkCList *clist)
3508 {
3509   g_return_if_fail (clist != 0);
3510   g_return_if_fail (GTK_IS_CLIST (clist));
3511
3512   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
3513       clist->focus_row < 0 || clist->focus_row >= clist->rows)
3514     return;
3515
3516   switch (clist->selection_mode)
3517     {
3518     case  GTK_SELECTION_SINGLE:
3519     case  GTK_SELECTION_MULTIPLE:
3520       toggle_row (clist, clist->focus_row, 0, NULL);
3521       break;
3522     case GTK_SELECTION_EXTENDED:
3523       g_list_free (clist->undo_selection);
3524       g_list_free (clist->undo_unselection);
3525       clist->undo_selection = NULL;
3526       clist->undo_unselection = NULL;
3527
3528       clist->anchor = clist->focus_row;
3529       clist->drag_pos = clist->focus_row;
3530       clist->undo_anchor = clist->focus_row;
3531       
3532       if (GTK_CLIST_ADD_MODE(clist))
3533         fake_toggle_row (clist, clist->focus_row);
3534       else
3535         GTK_CLIST_CLASS_FW (clist)->fake_unselect_all (clist,clist->focus_row);
3536
3537       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
3538       break;
3539     default:
3540       break;
3541     }
3542 }
3543
3544 static void
3545 toggle_add_mode (GtkCList *clist)
3546 {
3547   g_return_if_fail (clist != 0);
3548   g_return_if_fail (GTK_IS_CLIST (clist));
3549   
3550   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
3551       clist->selection_mode != GTK_SELECTION_EXTENDED)
3552     return;
3553
3554   gtk_clist_draw_focus (GTK_WIDGET (clist));
3555   if (!GTK_CLIST_ADD_MODE(clist))
3556     {
3557       GTK_CLIST_SET_FLAG (clist, CLIST_ADD_MODE);
3558       gdk_gc_set_line_attributes (clist->xor_gc, 1,
3559                                   GDK_LINE_ON_OFF_DASH, 0, 0);
3560       gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
3561     }
3562   else
3563     {
3564       GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
3565       gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
3566       clist->anchor_state = GTK_STATE_SELECTED;
3567     }
3568   gtk_clist_draw_focus (GTK_WIDGET (clist));
3569 }
3570
3571 static void
3572 real_select_row (GtkCList *clist,
3573                  gint      row,
3574                  gint      column,
3575                  GdkEvent *event)
3576 {
3577   GtkCListRow *clist_row;
3578   GList *list;
3579   gint sel_row;
3580   gboolean row_selected;
3581
3582   g_return_if_fail (clist != NULL);
3583   g_return_if_fail (GTK_IS_CLIST (clist));
3584
3585   if (row < 0 || row > (clist->rows - 1))
3586     return;
3587
3588   switch (clist->selection_mode)
3589     {
3590     case GTK_SELECTION_SINGLE:
3591     case GTK_SELECTION_BROWSE:
3592
3593       row_selected = FALSE;
3594       list = clist->selection;
3595
3596       while (list)
3597         {
3598           sel_row = GPOINTER_TO_INT (list->data);
3599           list = list->next;
3600
3601           if (row == sel_row)
3602             row_selected = TRUE;
3603           else
3604             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3605                              sel_row, column, event);
3606         }
3607
3608       if (row_selected)
3609         return;
3610       
3611     default:
3612       break;
3613     }
3614
3615   clist_row = (g_list_nth (clist->row_list, row))->data;
3616
3617   if (clist_row->state != GTK_STATE_NORMAL || !clist_row->selectable)
3618     return;
3619
3620   clist_row->state = GTK_STATE_SELECTED;
3621   if (!clist->selection)
3622     {
3623       clist->selection = g_list_append (clist->selection,
3624                                         GINT_TO_POINTER (row));
3625       clist->selection_end = clist->selection;
3626     }
3627   else
3628     clist->selection_end = 
3629       g_list_append (clist->selection_end, GINT_TO_POINTER (row))->next;
3630   
3631   if (CLIST_UNFROZEN (clist)
3632       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3633     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
3634 }
3635
3636 static void
3637 real_unselect_row (GtkCList *clist,
3638                    gint      row,
3639                    gint      column,
3640                    GdkEvent *event)
3641 {
3642   GtkCListRow *clist_row;
3643
3644   g_return_if_fail (clist != NULL);
3645   g_return_if_fail (GTK_IS_CLIST (clist));
3646
3647   if (row < 0 || row > (clist->rows - 1))
3648     return;
3649
3650   clist_row = (g_list_nth (clist->row_list, row))->data;
3651
3652   if (clist_row->state == GTK_STATE_SELECTED)
3653     {
3654       clist_row->state = GTK_STATE_NORMAL;
3655
3656       if (clist->selection_end && 
3657           clist->selection_end->data == GINT_TO_POINTER (row))
3658         clist->selection_end = clist->selection_end->prev;
3659
3660       clist->selection = g_list_remove (clist->selection,
3661                                         GINT_TO_POINTER (row));
3662       
3663       if (CLIST_UNFROZEN (clist)
3664           && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3665         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
3666     }
3667 }
3668
3669 static void
3670 real_select_all (GtkCList *clist)
3671 {
3672   GList *list;
3673   gint i;
3674  
3675   g_return_if_fail (clist != NULL);
3676   g_return_if_fail (GTK_IS_CLIST (clist));
3677
3678   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
3679     return;
3680
3681   switch (clist->selection_mode)
3682     {
3683     case GTK_SELECTION_SINGLE:
3684     case GTK_SELECTION_BROWSE:
3685       return;
3686
3687     case GTK_SELECTION_EXTENDED:
3688       g_list_free (clist->undo_selection);
3689       g_list_free (clist->undo_unselection);
3690       clist->undo_selection = NULL;
3691       clist->undo_unselection = NULL;
3692           
3693       if (clist->rows &&
3694           ((GtkCListRow *) (clist->row_list->data))->state !=
3695           GTK_STATE_SELECTED)
3696         fake_toggle_row (clist, 0);
3697
3698       clist->anchor_state =  GTK_STATE_SELECTED;
3699       clist->anchor = 0;
3700       clist->drag_pos = 0;
3701       clist->undo_anchor = clist->focus_row;
3702       update_extended_selection (clist, clist->rows);
3703       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
3704       return;
3705
3706     case GTK_SELECTION_MULTIPLE:
3707       for (i = 0, list = clist->row_list; list; i++, list = list->next)
3708         {
3709           if (((GtkCListRow *)(list->data))->state == GTK_STATE_NORMAL)
3710             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3711                              i, -1, NULL);
3712         }
3713       return;
3714     }
3715 }
3716
3717 static void
3718 real_unselect_all (GtkCList *clist)
3719 {
3720   GList *list;
3721   gint i;
3722  
3723   g_return_if_fail (clist != NULL);
3724   g_return_if_fail (GTK_IS_CLIST (clist));
3725
3726   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
3727     return;
3728
3729   switch (clist->selection_mode)
3730     {
3731     case GTK_SELECTION_BROWSE:
3732       if (clist->focus_row >= 0)
3733         {
3734           gtk_signal_emit (GTK_OBJECT (clist),
3735                            clist_signals[SELECT_ROW],
3736                            clist->focus_row, -1, NULL);
3737           return;
3738         }
3739       break;
3740     case GTK_SELECTION_EXTENDED:
3741       g_list_free (clist->undo_selection);
3742       g_list_free (clist->undo_unselection);
3743       clist->undo_selection = NULL;
3744       clist->undo_unselection = NULL;
3745
3746       clist->anchor = -1;
3747       clist->drag_pos = -1;
3748       clist->undo_anchor = clist->focus_row;
3749       break;
3750     default:
3751       break;
3752     }
3753
3754   list = clist->selection;
3755   while (list)
3756     {
3757       i = GPOINTER_TO_INT (list->data);
3758       list = list->next;
3759       gtk_signal_emit (GTK_OBJECT (clist),
3760                        clist_signals[UNSELECT_ROW], i, -1, NULL);
3761     }
3762 }
3763
3764 static void
3765 fake_unselect_all (GtkCList *clist,
3766                    gint      row)
3767 {
3768   GList *list;
3769   GList *work;
3770   gint i;
3771
3772   if (row >= 0 && (work = g_list_nth (clist->row_list, row)))
3773     {
3774       if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL &&
3775           GTK_CLIST_ROW (work)->selectable)
3776         {
3777           GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
3778           
3779           if (CLIST_UNFROZEN (clist) &&
3780               gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3781             GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row,
3782                                                   GTK_CLIST_ROW (work));
3783         }  
3784     }
3785
3786   clist->undo_selection = clist->selection;
3787   clist->selection = NULL;
3788   clist->selection_end = NULL;
3789   
3790   for (list = clist->undo_selection; list; list = list->next)
3791     {
3792       if ((i = GPOINTER_TO_INT (list->data)) == row ||
3793           !(work = g_list_nth (clist->row_list, i)))
3794         continue;
3795
3796       GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
3797       if (CLIST_UNFROZEN (clist) &&
3798           gtk_clist_row_is_visible (clist, i) != GTK_VISIBILITY_NONE)
3799         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, i,
3800                                               GTK_CLIST_ROW (work));
3801     }
3802 }
3803
3804 static void
3805 real_undo_selection (GtkCList *clist)
3806 {
3807   GList *work;
3808
3809   g_return_if_fail (clist != NULL);
3810   g_return_if_fail (GTK_IS_CLIST (clist));
3811
3812   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
3813       clist->selection_mode != GTK_SELECTION_EXTENDED)
3814     return;
3815
3816   if (clist->anchor >= 0)
3817     GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
3818
3819   if (!(clist->undo_selection || clist->undo_unselection))
3820     {
3821       gtk_clist_unselect_all (clist);
3822       return;
3823     }
3824
3825   for (work = clist->undo_selection; work; work = work->next)
3826     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3827                      GPOINTER_TO_INT (work->data), -1, NULL);
3828
3829   for (work = clist->undo_unselection; work; work = work->next)
3830     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3831                      GPOINTER_TO_INT (work->data), -1, NULL);
3832
3833   if (GTK_WIDGET_HAS_FOCUS(clist) && clist->focus_row != clist->undo_anchor)
3834     {
3835       gtk_clist_draw_focus (GTK_WIDGET (clist));
3836       clist->focus_row = clist->undo_anchor;
3837       gtk_clist_draw_focus (GTK_WIDGET (clist));
3838     }
3839   else
3840     clist->focus_row = clist->undo_anchor;
3841   
3842   clist->undo_anchor = -1;
3843  
3844   g_list_free (clist->undo_selection);
3845   g_list_free (clist->undo_unselection);
3846   clist->undo_selection = NULL;
3847   clist->undo_unselection = NULL;
3848
3849   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
3850       clist->clist_window_height)
3851     gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
3852   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
3853     gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
3854 }
3855
3856 static void
3857 set_anchor (GtkCList *clist,
3858             gboolean  add_mode,
3859             gint      anchor,
3860             gint      undo_anchor)
3861 {
3862   g_return_if_fail (clist != NULL);
3863   g_return_if_fail (GTK_IS_CLIST (clist));
3864   
3865   if (clist->selection_mode != GTK_SELECTION_EXTENDED || clist->anchor >= 0)
3866     return;
3867
3868   g_list_free (clist->undo_selection);
3869   g_list_free (clist->undo_unselection);
3870   clist->undo_selection = NULL;
3871   clist->undo_unselection = NULL;
3872
3873   if (add_mode)
3874     fake_toggle_row (clist, anchor);
3875   else
3876     {
3877       GTK_CLIST_CLASS_FW (clist)->fake_unselect_all (clist, anchor);
3878       clist->anchor_state = GTK_STATE_SELECTED;
3879     }
3880
3881   clist->anchor = anchor;
3882   clist->drag_pos = anchor;
3883   clist->undo_anchor = undo_anchor;
3884 }
3885
3886 static void
3887 resync_selection (GtkCList *clist,
3888                   GdkEvent *event)
3889 {
3890   gint i;
3891   gint e;
3892   gint row;
3893   GList *list;
3894   GtkCListRow *clist_row;
3895
3896   if (clist->anchor < 0)
3897     return;
3898
3899   gtk_clist_freeze (clist);
3900
3901   i = MIN (clist->anchor, clist->drag_pos);
3902   e = MAX (clist->anchor, clist->drag_pos);
3903
3904   if (clist->undo_selection)
3905     {
3906       list = clist->selection;
3907       clist->selection = clist->undo_selection;
3908       clist->selection_end = g_list_last (clist->selection);
3909       clist->undo_selection = list;
3910       list = clist->selection;
3911       while (list)
3912         {
3913           row = GPOINTER_TO_INT (list->data);
3914           list = list->next;
3915           if (row < i || row > e)
3916             {
3917               clist_row = g_list_nth (clist->row_list, row)->data;
3918               if (clist_row->selectable)
3919                 {
3920                   clist_row->state = GTK_STATE_SELECTED;
3921                   gtk_signal_emit (GTK_OBJECT (clist),
3922                                    clist_signals[UNSELECT_ROW],
3923                                    row, -1, event);
3924                   clist->undo_selection = g_list_prepend
3925                     (clist->undo_selection, GINT_TO_POINTER (row));
3926                 }
3927             }
3928         }
3929     }    
3930
3931   if (clist->anchor < clist->drag_pos)
3932     {
3933       for (list = g_list_nth (clist->row_list, i); i <= e;
3934            i++, list = list->next)
3935         if (GTK_CLIST_ROW (list)->selectable)
3936           {
3937             if (g_list_find (clist->selection, GINT_TO_POINTER(i)))
3938               {
3939                 if (GTK_CLIST_ROW (list)->state == GTK_STATE_NORMAL)
3940                   {
3941                     GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
3942                     gtk_signal_emit (GTK_OBJECT (clist),
3943                                      clist_signals[UNSELECT_ROW],
3944                                      i, -1, event);
3945                     clist->undo_selection =
3946                       g_list_prepend (clist->undo_selection,
3947                                       GINT_TO_POINTER (i));
3948                   }
3949               }
3950             else if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
3951               {
3952                 GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
3953                 clist->undo_unselection =
3954                   g_list_prepend (clist->undo_unselection,
3955                                   GINT_TO_POINTER (i));
3956               }
3957           }
3958     }
3959   else
3960     {
3961       for (list = g_list_nth (clist->row_list, e); i <= e;
3962            e--, list = list->prev)
3963         if (GTK_CLIST_ROW (list)->selectable)
3964           {
3965             if (g_list_find (clist->selection, GINT_TO_POINTER(e)))
3966               {
3967                 if (GTK_CLIST_ROW (list)->state == GTK_STATE_NORMAL)
3968                   {
3969                     GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
3970                     gtk_signal_emit (GTK_OBJECT (clist),
3971                                      clist_signals[UNSELECT_ROW],
3972                                      e, -1, event);
3973                     clist->undo_selection =
3974                       g_list_prepend (clist->undo_selection,
3975                                       GINT_TO_POINTER (e));
3976                   }
3977               }
3978             else if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
3979               {
3980                 GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
3981                 clist->undo_unselection =
3982                   g_list_prepend (clist->undo_unselection,
3983                                   GINT_TO_POINTER (e));
3984               }
3985           }
3986     }
3987
3988   for (list = g_list_reverse (clist->undo_unselection); list;
3989        list = list->next)
3990     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3991                      GPOINTER_TO_INT (list->data), -1, event);
3992
3993   clist->anchor = -1;
3994   clist->drag_pos = -1;
3995
3996   gtk_clist_thaw (clist);
3997 }
3998
3999 static void
4000 update_extended_selection (GtkCList *clist,
4001                            gint      row)
4002 {
4003   gint i;
4004   GList *list;
4005   GdkRectangle area;
4006   gint s1 = -1;
4007   gint s2 = -1;
4008   gint e1 = -1;
4009   gint e2 = -1;
4010   gint y1 = clist->clist_window_height;
4011   gint y2 = clist->clist_window_height;
4012   gint h1 = 0;
4013   gint h2 = 0;
4014   gint top;
4015
4016   if (clist->selection_mode != GTK_SELECTION_EXTENDED || clist->anchor == -1)
4017     return;
4018
4019   if (row < 0)
4020     row = 0;
4021   if (row >= clist->rows)
4022     row = clist->rows - 1;
4023
4024   /* extending downwards */
4025   if (row > clist->drag_pos && clist->anchor <= clist->drag_pos)
4026     {
4027       s2 = clist->drag_pos + 1;
4028       e2 = row;
4029     }
4030   /* extending upwards */
4031   else if (row < clist->drag_pos && clist->anchor >= clist->drag_pos)
4032     {
4033       s2 = row;
4034       e2 = clist->drag_pos - 1;
4035     }
4036   else if (row < clist->drag_pos && clist->anchor < clist->drag_pos)
4037     {
4038       e1 = clist->drag_pos;
4039       /* row and drag_pos on different sides of anchor :
4040          take back the selection between anchor and drag_pos,
4041          select between anchor and row */
4042       if (row < clist->anchor)
4043         {
4044           s1 = clist->anchor + 1;
4045           s2 = row;
4046           e2 = clist->anchor - 1;
4047         }
4048       /* take back the selection between anchor and drag_pos */
4049       else
4050         s1 = row + 1;
4051     }
4052   else if (row > clist->drag_pos && clist->anchor > clist->drag_pos)
4053     {
4054       s1 = clist->drag_pos;
4055       /* row and drag_pos on different sides of anchor :
4056          take back the selection between anchor and drag_pos,
4057          select between anchor and row */
4058       if (row > clist->anchor)
4059         {
4060           e1 = clist->anchor - 1;
4061           s2 = clist->anchor + 1;
4062           e2 = row;
4063         }
4064       /* take back the selection between anchor and drag_pos */
4065       else
4066         e1 = row - 1;
4067     }
4068
4069   clist->drag_pos = row;
4070
4071   area.x = 0;
4072   area.width = clist->clist_window_width;
4073
4074   /* restore the elements between s1 and e1 */
4075   if (s1 >= 0)
4076     {
4077       for (i = s1, list = g_list_nth (clist->row_list, i); i <= e1;
4078            i++, list = list->next)
4079         if (GTK_CLIST_ROW (list)->selectable)
4080           {
4081             if (GTK_CLIST_CLASS_FW (clist)->selection_find (clist, i, list))
4082               GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
4083             else
4084               GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
4085           }
4086
4087       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4088
4089       if (top + clist->row_height <= 0)
4090         {
4091           area.y = 0;
4092           area.height = ROW_TOP_YPIXEL (clist, e1) + clist->row_height;
4093           draw_rows (clist, &area);
4094           gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4095         }
4096       else if (top >= clist->clist_window_height)
4097         {
4098           area.y = ROW_TOP_YPIXEL (clist, s1) - 1;
4099           area.height = clist->clist_window_height - area.y;
4100           draw_rows (clist, &area);
4101           gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4102         }
4103       else if (top < 0)
4104         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4105       else if (top + clist->row_height > clist->clist_window_height)
4106         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4107
4108       y1 = ROW_TOP_YPIXEL (clist, s1) - 1;
4109       h1 = (e1 - s1 + 1) * (clist->row_height + CELL_SPACING);
4110     }
4111
4112   /* extend the selection between s2 and e2 */
4113   if (s2 >= 0)
4114     {
4115       for (i = s2, list = g_list_nth (clist->row_list, i); i <= e2;
4116            i++, list = list->next)
4117         if (GTK_CLIST_ROW (list)->selectable &&
4118             GTK_CLIST_ROW (list)->state != clist->anchor_state)
4119           GTK_CLIST_ROW (list)->state = clist->anchor_state;
4120
4121       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4122
4123       if (top + clist->row_height <= 0)
4124         {
4125           area.y = 0;
4126           area.height = ROW_TOP_YPIXEL (clist, e2) + clist->row_height;
4127           draw_rows (clist, &area);
4128           gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4129         }
4130       else if (top >= clist->clist_window_height)
4131         {
4132           area.y = ROW_TOP_YPIXEL (clist, s2) - 1;
4133           area.height = clist->clist_window_height - area.y;
4134           draw_rows (clist, &area);
4135           gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4136         }
4137       else if (top < 0)
4138         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4139       else if (top + clist->row_height > clist->clist_window_height)
4140         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4141
4142       y2 = ROW_TOP_YPIXEL (clist, s2) - 1;
4143       h2 = (e2 - s2 + 1) * (clist->row_height + CELL_SPACING);
4144     }
4145
4146   area.y = MAX (0, MIN (y1, y2));
4147   if (area.y > clist->clist_window_height)
4148     area.y = 0;
4149   area.height = MIN (clist->clist_window_height, h1 + h2);
4150   if (s1 >= 0 && s2 >= 0)
4151     area.height += (clist->row_height + CELL_SPACING);
4152   draw_rows (clist, &area);
4153 }
4154
4155 static void
4156 start_selection (GtkCList *clist)
4157 {
4158   g_return_if_fail (clist != NULL);
4159   g_return_if_fail (GTK_IS_CLIST (clist));
4160
4161   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
4162     return;
4163
4164   set_anchor (clist, GTK_CLIST_ADD_MODE(clist), clist->focus_row,
4165               clist->focus_row);
4166 }
4167
4168 static void
4169 end_selection (GtkCList *clist)
4170 {
4171   g_return_if_fail (clist != NULL);
4172   g_return_if_fail (GTK_IS_CLIST (clist));
4173
4174   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_FOCUS(clist)) ||
4175       clist->anchor == -1)
4176     return;
4177   
4178   GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
4179 }
4180
4181 static void
4182 extend_selection (GtkCList      *clist,
4183                   GtkScrollType  scroll_type,
4184                   gfloat         position,
4185                   gboolean       auto_start_selection)
4186 {
4187   g_return_if_fail (clist != NULL);
4188   g_return_if_fail (GTK_IS_CLIST (clist));
4189
4190   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
4191       clist->selection_mode != GTK_SELECTION_EXTENDED)
4192     return;
4193
4194   if (auto_start_selection)
4195     set_anchor (clist, GTK_CLIST_ADD_MODE(clist), clist->focus_row,
4196                 clist->focus_row);
4197   else if (clist->anchor == -1)
4198     return;
4199
4200   move_focus_row (clist, scroll_type, position);
4201
4202   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
4203       clist->clist_window_height)
4204     gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4205   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
4206     gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4207
4208   update_extended_selection (clist, clist->focus_row);
4209 }
4210
4211 static void
4212 sync_selection (GtkCList *clist,
4213                 gint      row,
4214                 gint      mode)
4215 {
4216   GList *list;
4217   gint d;
4218
4219   if (mode == SYNC_INSERT)
4220     d = 1;
4221   else
4222     d = -1;
4223       
4224   if (clist->focus_row >= row)
4225     {
4226       if (d > 0 || clist->focus_row > row)
4227         clist->focus_row += d;
4228       if (clist->focus_row == -1 && clist->rows >= 1)
4229         clist->focus_row = 0;
4230       else if (clist->focus_row >= clist->rows)
4231         clist->focus_row = clist->rows - 1;
4232     }
4233
4234   if (clist->selection_mode == GTK_SELECTION_BROWSE && clist->anchor != -1)
4235     GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
4236
4237   g_list_free (clist->undo_selection);
4238   g_list_free (clist->undo_unselection);
4239   clist->undo_selection = NULL;
4240   clist->undo_unselection = NULL;
4241
4242   clist->anchor = -1;
4243   clist->drag_pos = -1;
4244   clist->undo_anchor = clist->focus_row;
4245
4246   list = clist->selection;
4247
4248   while (list)
4249     {
4250       if (GPOINTER_TO_INT (list->data) >= row)
4251         list->data = ((gchar*) list->data) + d;
4252       list = list->next;
4253     }
4254 }
4255
4256 /* GTKOBJECT
4257  *   gtk_clist_destroy
4258  *   gtk_clist_finalize
4259  */
4260 static void
4261 gtk_clist_destroy (GtkObject *object)
4262 {
4263   gint i;
4264   GtkCList *clist;
4265
4266   g_return_if_fail (object != NULL);
4267   g_return_if_fail (GTK_IS_CLIST (object));
4268
4269   clist = GTK_CLIST (object);
4270
4271   /* freeze the list */
4272   clist->freeze_count++;
4273
4274   /* get rid of all the rows */
4275   gtk_clist_clear (clist);
4276
4277   /* Since we don't have a _remove method, unparent the children
4278    * instead of destroying them so the focus will be unset properly.
4279    * (For other containers, the _remove method takes care of the
4280    * unparent) The destroy will happen when the refcount drops
4281    * to zero.
4282    */
4283
4284   /* unref adjustments */
4285   if (clist->hadjustment)
4286     {
4287       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->hadjustment), clist);
4288       gtk_object_unref (GTK_OBJECT (clist->hadjustment));
4289       clist->hadjustment = NULL;
4290     }
4291   if (clist->vadjustment)
4292     {
4293       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->vadjustment), clist);
4294       gtk_object_unref (GTK_OBJECT (clist->vadjustment));
4295       clist->vadjustment = NULL;
4296     }
4297
4298   remove_grab (clist);
4299
4300   /* destroy the column buttons */
4301   for (i = 0; i < clist->columns; i++)
4302     if (clist->column[i].button)
4303       {
4304         gtk_widget_unparent (clist->column[i].button);
4305         clist->column[i].button = NULL;
4306       }
4307
4308   if (GTK_OBJECT_CLASS (parent_class)->destroy)
4309     (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
4310 }
4311
4312 static void
4313 gtk_clist_finalize (GtkObject *object)
4314 {
4315   GtkCList *clist;
4316
4317   g_return_if_fail (object != NULL);
4318   g_return_if_fail (GTK_IS_CLIST (object));
4319
4320   clist = GTK_CLIST (object);
4321
4322   columns_delete (clist);
4323
4324   g_mem_chunk_destroy (clist->cell_mem_chunk);
4325   g_mem_chunk_destroy (clist->row_mem_chunk);
4326
4327   if (GTK_OBJECT_CLASS (parent_class)->finalize)
4328     (*GTK_OBJECT_CLASS (parent_class)->finalize) (object);
4329 }
4330
4331 /* GTKWIDGET
4332  *   gtk_clist_realize
4333  *   gtk_clist_unrealize
4334  *   gtk_clist_map
4335  *   gtk_clist_unmap
4336  *   gtk_clist_draw
4337  *   gtk_clist_expose
4338  *   gtk_clist_style_set
4339  *   gtk_clist_key_press
4340  *   gtk_clist_button_press
4341  *   gtk_clist_button_release
4342  *   gtk_clist_motion
4343  *   gtk_clist_size_request
4344  *   gtk_clist_size_allocate
4345  */
4346 static void
4347 gtk_clist_realize (GtkWidget *widget)
4348 {
4349   GtkCList *clist;
4350   GdkWindowAttr attributes;
4351   GdkGCValues values;
4352   GtkCListRow *clist_row;
4353   GList *list;
4354   gint attributes_mask;
4355   gint border_width;
4356   gint i;
4357   gint j;
4358
4359   g_return_if_fail (widget != NULL);
4360   g_return_if_fail (GTK_IS_CLIST (widget));
4361
4362   clist = GTK_CLIST (widget);
4363
4364   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
4365
4366   border_width = GTK_CONTAINER (widget)->border_width;
4367   
4368   attributes.window_type = GDK_WINDOW_CHILD;
4369   attributes.x = widget->allocation.x + border_width;
4370   attributes.y = widget->allocation.y + border_width;
4371   attributes.width = widget->allocation.width - border_width * 2;
4372   attributes.height = widget->allocation.height - border_width * 2;
4373   attributes.wclass = GDK_INPUT_OUTPUT;
4374   attributes.visual = gtk_widget_get_visual (widget);
4375   attributes.colormap = gtk_widget_get_colormap (widget);
4376   attributes.event_mask = gtk_widget_get_events (widget);
4377   attributes.event_mask |= (GDK_EXPOSURE_MASK |
4378                             GDK_BUTTON_PRESS_MASK |
4379                             GDK_BUTTON_RELEASE_MASK |
4380                             GDK_KEY_PRESS_MASK |
4381                             GDK_KEY_RELEASE_MASK);
4382   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
4383
4384   /* main window */
4385   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
4386                                    &attributes, attributes_mask);
4387   gdk_window_set_user_data (widget->window, clist);
4388
4389   widget->style = gtk_style_attach (widget->style, widget->window);
4390
4391   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
4392
4393   /* column-title window */
4394
4395   attributes.x = clist->column_title_area.x;
4396   attributes.y = clist->column_title_area.y;
4397   attributes.width = clist->column_title_area.width;
4398   attributes.height = clist->column_title_area.height;
4399   
4400   clist->title_window = gdk_window_new (widget->window, &attributes,
4401                                         attributes_mask);
4402   gdk_window_set_user_data (clist->title_window, clist);
4403
4404   gtk_style_set_background (widget->style, clist->title_window,
4405                             GTK_STATE_NORMAL);
4406   gdk_window_show (clist->title_window);
4407
4408   /* set things up so column buttons are drawn in title window */
4409   for (i = 0; i < clist->columns; i++)
4410     if (clist->column[i].button)
4411       gtk_widget_set_parent_window (clist->column[i].button,
4412                                     clist->title_window);
4413
4414   /* clist-window */
4415   attributes.x = (clist->internal_allocation.x +
4416                   widget->style->klass->xthickness);
4417   attributes.y = (clist->internal_allocation.y +
4418                   widget->style->klass->ythickness +
4419                   clist->column_title_area.height);
4420   attributes.width = clist->clist_window_width;
4421   attributes.height = clist->clist_window_height;
4422   
4423   clist->clist_window = gdk_window_new (widget->window, &attributes,
4424                                         attributes_mask);
4425   gdk_window_set_user_data (clist->clist_window, clist);
4426
4427   gdk_window_set_background (clist->clist_window,
4428                              &widget->style->base[GTK_STATE_NORMAL]);
4429   gdk_window_show (clist->clist_window);
4430   gdk_window_get_size (clist->clist_window, &clist->clist_window_width,
4431                        &clist->clist_window_height);
4432
4433   /* create resize windows */
4434   attributes.wclass = GDK_INPUT_ONLY;
4435   attributes.event_mask = (GDK_BUTTON_PRESS_MASK |
4436                            GDK_BUTTON_RELEASE_MASK |
4437                            GDK_POINTER_MOTION_MASK |
4438                            GDK_POINTER_MOTION_HINT_MASK |
4439                            GDK_KEY_PRESS_MASK);
4440   attributes_mask = GDK_WA_CURSOR;
4441   attributes.cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
4442   clist->cursor_drag = attributes.cursor;
4443
4444   attributes.x =  LIST_WIDTH (clist) + 1;
4445   attributes.y = 0;
4446   attributes.width = 0;
4447   attributes.height = 0;
4448
4449   for (i = 0; i < clist->columns; i++)
4450     {
4451       clist->column[i].window = gdk_window_new (clist->title_window,
4452                                                 &attributes, attributes_mask);
4453       gdk_window_set_user_data (clist->column[i].window, clist);
4454     }
4455
4456   /* This is slightly less efficient than creating them with the
4457    * right size to begin with, but easier
4458    */
4459   size_allocate_title_buttons (clist);
4460
4461   /* GCs */
4462   clist->fg_gc = gdk_gc_new (widget->window);
4463   clist->bg_gc = gdk_gc_new (widget->window);
4464   
4465   /* We'll use this gc to do scrolling as well */
4466   gdk_gc_set_exposures (clist->fg_gc, TRUE);
4467
4468   values.foreground = widget->style->white;
4469   values.function = GDK_XOR;
4470   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
4471   clist->xor_gc = gdk_gc_new_with_values (widget->window,
4472                                           &values,
4473                                           GDK_GC_FOREGROUND |
4474                                           GDK_GC_FUNCTION |
4475                                           GDK_GC_SUBWINDOW);
4476
4477   /* attach optional row/cell styles, allocate foreground/background colors */
4478   list = clist->row_list;
4479   for (i = 0; i < clist->rows; i++)
4480     {
4481       clist_row = list->data;
4482       list = list->next;
4483
4484       if (clist_row->style)
4485         clist_row->style = gtk_style_attach (clist_row->style,
4486                                              clist->clist_window);
4487
4488       if (clist_row->fg_set || clist_row->bg_set)
4489         {
4490           GdkColormap *colormap;
4491
4492           colormap = gtk_widget_get_colormap (widget);
4493           if (clist_row->fg_set)
4494             gdk_color_alloc (colormap, &clist_row->foreground);
4495           if (clist_row->bg_set)
4496             gdk_color_alloc (colormap, &clist_row->background);
4497         }
4498       
4499       for (j = 0; j < clist->columns; j++)
4500         if  (clist_row->cell[j].style)
4501           clist_row->cell[j].style =
4502             gtk_style_attach (clist_row->cell[j].style, clist->clist_window);
4503     }
4504 }
4505
4506 static void
4507 gtk_clist_unrealize (GtkWidget *widget)
4508 {
4509   gint i;
4510   GtkCList *clist;
4511
4512   g_return_if_fail (widget != NULL);
4513   g_return_if_fail (GTK_IS_CLIST (widget));
4514
4515   clist = GTK_CLIST (widget);
4516
4517   /* freeze the list */
4518   clist->freeze_count++;
4519
4520   /* detach optional row/cell styles */
4521
4522   if (GTK_WIDGET_REALIZED (widget))
4523     {
4524       GtkCListRow *clist_row;
4525       GList *list;
4526       gint j;
4527
4528       list = clist->row_list;
4529       for (i = 0; i < clist->rows; i++)
4530         {
4531           clist_row = list->data;
4532           list = list->next;
4533
4534           if (clist_row->style)
4535             gtk_style_detach (clist_row->style);
4536           for (j = 0; j < clist->columns; j++)
4537             if  (clist_row->cell[j].style)
4538               gtk_style_detach (clist_row->cell[j].style);
4539         }
4540     }
4541
4542   gdk_cursor_destroy (clist->cursor_drag);
4543   gdk_gc_destroy (clist->xor_gc);
4544   gdk_gc_destroy (clist->fg_gc);
4545   gdk_gc_destroy (clist->bg_gc);
4546
4547   for (i = 0; i < clist->columns; i++)
4548     if (clist->column[i].window)
4549       {
4550         gdk_window_set_user_data (clist->column[i].window, NULL);
4551         gdk_window_destroy (clist->column[i].window);
4552         clist->column[i].window = NULL;
4553       }
4554
4555   gdk_window_set_user_data (clist->clist_window, NULL);
4556   gdk_window_destroy (clist->clist_window);
4557   clist->clist_window = NULL;
4558
4559   gdk_window_set_user_data (clist->title_window, NULL);
4560   gdk_window_destroy (clist->title_window);
4561   clist->title_window = NULL;
4562
4563   clist->cursor_drag = NULL;
4564   clist->xor_gc = NULL;
4565   clist->fg_gc = NULL;
4566   clist->bg_gc = NULL;
4567
4568   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
4569     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
4570 }
4571
4572 static void
4573 gtk_clist_map (GtkWidget *widget)
4574 {
4575   gint i;
4576   GtkCList *clist;
4577
4578   g_return_if_fail (widget != NULL);
4579   g_return_if_fail (GTK_IS_CLIST (widget));
4580
4581   clist = GTK_CLIST (widget);
4582
4583   if (!GTK_WIDGET_MAPPED (widget))
4584     {
4585       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
4586
4587       /* map column buttons */
4588       for (i = 0; i < clist->columns; i++)
4589         if (clist->column[i].button &&
4590             GTK_WIDGET_VISIBLE (clist->column[i].button) &&
4591             !GTK_WIDGET_MAPPED (clist->column[i].button))
4592           gtk_widget_map (clist->column[i].button);
4593       
4594       for (i = 0; i < clist->columns; i++)
4595         if (clist->column[i].window && clist->column[i].button)
4596           {
4597             gdk_window_raise (clist->column[i].window);
4598             gdk_window_show (clist->column[i].window);
4599           }
4600
4601       gdk_window_show (clist->title_window);
4602       gdk_window_show (clist->clist_window);
4603       gdk_window_show (widget->window);
4604
4605       /* unfreeze the list */
4606       clist->freeze_count = 0;
4607     }
4608 }
4609
4610 static void
4611 gtk_clist_unmap (GtkWidget *widget)
4612 {
4613   gint i;
4614   GtkCList *clist;
4615
4616   g_return_if_fail (widget != NULL);
4617   g_return_if_fail (GTK_IS_CLIST (widget));
4618
4619   clist = GTK_CLIST (widget);
4620
4621   if (GTK_WIDGET_MAPPED (widget))
4622     {
4623       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
4624
4625       if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
4626         {
4627           remove_grab (clist);
4628
4629           if (clist->anchor != -1 &&
4630               clist->selection_mode == GTK_SELECTION_EXTENDED)
4631             GTK_CLIST_CLASS_FW (widget)->resync_selection (clist, NULL);
4632
4633           clist->click_cell.row = -1;
4634           clist->click_cell.column = -1;
4635           clist->drag_button = 0;
4636
4637           if (GTK_CLIST_IN_DRAG(clist))
4638             {
4639               gpointer drag_data;
4640
4641               GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
4642               drag_data = gtk_object_get_data (GTK_OBJECT (clist),
4643                                                "gtk-site-data");
4644               if (drag_data)
4645                 gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist),
4646                                                     drag_data);
4647             }
4648         }
4649
4650       for (i = 0; i < clist->columns; i++)
4651         if (clist->column[i].window)
4652           gdk_window_hide (clist->column[i].window);
4653
4654       gdk_window_hide (clist->clist_window);
4655       gdk_window_hide (clist->title_window);
4656       gdk_window_hide (widget->window);
4657
4658       /* unmap column buttons */
4659       for (i = 0; i < clist->columns; i++)
4660         if (clist->column[i].button &&
4661             GTK_WIDGET_MAPPED (clist->column[i].button))
4662           gtk_widget_unmap (clist->column[i].button);
4663
4664       /* freeze the list */
4665       clist->freeze_count++;
4666     }
4667 }
4668
4669 static void
4670 gtk_clist_draw (GtkWidget    *widget,
4671                 GdkRectangle *area)
4672 {
4673   GtkCList *clist;
4674   gint border_width;
4675   GdkRectangle child_area;
4676   int i;
4677
4678   g_return_if_fail (widget != NULL);
4679   g_return_if_fail (GTK_IS_CLIST (widget));
4680   g_return_if_fail (area != NULL);
4681
4682   if (GTK_WIDGET_DRAWABLE (widget))
4683     {
4684       clist = GTK_CLIST (widget);
4685       border_width = GTK_CONTAINER (widget)->border_width;
4686
4687       gdk_window_clear_area (widget->window,
4688                              area->x - border_width, 
4689                              area->y - border_width,
4690                              area->width, area->height);
4691
4692       /* draw list shadow/border */
4693       gtk_draw_shadow (widget->style, widget->window,
4694                        GTK_STATE_NORMAL, clist->shadow_type,
4695                        0, 0, 
4696                        clist->clist_window_width +
4697                        (2 * widget->style->klass->xthickness),
4698                        clist->clist_window_height +
4699                        (2 * widget->style->klass->ythickness) +
4700                        clist->column_title_area.height);
4701
4702       gdk_window_clear_area (clist->clist_window, 0, 0, -1, -1);
4703       draw_rows (clist, NULL);
4704
4705       for (i = 0; i < clist->columns; i++)
4706         {
4707           if (!clist->column[i].visible)
4708             continue;
4709           if (clist->column[i].button &&
4710               gtk_widget_intersect(clist->column[i].button, area, &child_area))
4711             gtk_widget_draw (clist->column[i].button, &child_area);
4712         }
4713     }
4714 }
4715
4716 static gint
4717 gtk_clist_expose (GtkWidget      *widget,
4718                   GdkEventExpose *event)
4719 {
4720   GtkCList *clist;
4721
4722   g_return_val_if_fail (widget != NULL, FALSE);
4723   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4724   g_return_val_if_fail (event != NULL, FALSE);
4725
4726   if (GTK_WIDGET_DRAWABLE (widget))
4727     {
4728       clist = GTK_CLIST (widget);
4729
4730       /* draw border */
4731       if (event->window == widget->window)
4732         gtk_draw_shadow (widget->style, widget->window,
4733                          GTK_STATE_NORMAL, clist->shadow_type,
4734                          0, 0,
4735                          clist->clist_window_width +
4736                          (2 * widget->style->klass->xthickness),
4737                          clist->clist_window_height +
4738                          (2 * widget->style->klass->ythickness) +
4739                          clist->column_title_area.height);
4740
4741       /* exposure events on the list */
4742       if (event->window == clist->clist_window)
4743         draw_rows (clist, &event->area);
4744     }
4745
4746   return FALSE;
4747 }
4748
4749 static void
4750 gtk_clist_style_set (GtkWidget *widget,
4751                      GtkStyle  *previous_style)
4752 {
4753   GtkCList *clist;
4754
4755   g_return_if_fail (widget != NULL);
4756   g_return_if_fail (GTK_IS_CLIST (widget));
4757
4758   if (GTK_WIDGET_CLASS (parent_class)->style_set)
4759     (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
4760
4761   clist = GTK_CLIST (widget);
4762
4763   if (GTK_WIDGET_REALIZED (widget))
4764     {
4765       gtk_style_set_background (widget->style, widget->window, widget->state);
4766       gtk_style_set_background (widget->style, clist->title_window, GTK_STATE_SELECTED);
4767       gdk_window_set_background (clist->clist_window, &widget->style->base[GTK_STATE_NORMAL]);
4768     }
4769
4770   /* Fill in data after widget has correct style */
4771
4772   /* text properties */
4773   if (!GTK_CLIST_ROW_HEIGHT_SET(clist))
4774     {
4775       clist->row_height = (widget->style->font->ascent +
4776                            widget->style->font->descent + 1);
4777       clist->row_center_offset = widget->style->font->ascent + 1.5;
4778     }
4779   else
4780     clist->row_center_offset = 1.5 + (clist->row_height +
4781                                       widget->style->font->ascent -
4782                                       widget->style->font->descent - 1) / 2;
4783
4784   /* Column widths */
4785   if (!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
4786     {
4787       gint width;
4788       gint i;
4789
4790       for (i = 0; i < clist->columns; i++)
4791         if (clist->column[i].auto_resize)
4792           {
4793             width = gtk_clist_optimal_column_width (clist, i);
4794             if (width != clist->column[i].width)
4795               gtk_clist_set_column_width (clist, i, width);
4796           }
4797     }
4798 }
4799
4800 static gint
4801 gtk_clist_key_press (GtkWidget   *widget,
4802                      GdkEventKey *event)
4803 {
4804   g_return_val_if_fail (widget != NULL, FALSE);
4805   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4806   g_return_val_if_fail (event != NULL, FALSE);
4807
4808   if (GTK_WIDGET_CLASS (parent_class)->key_press_event &&
4809       GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
4810     return TRUE;
4811
4812   switch (event->keyval)
4813     {
4814     case GDK_Tab:
4815     case GDK_ISO_Left_Tab:
4816       if (event->state & GDK_SHIFT_MASK)
4817         return gtk_container_focus (GTK_CONTAINER (widget),
4818                                     GTK_DIR_TAB_BACKWARD);
4819       else
4820         return gtk_container_focus (GTK_CONTAINER (widget),
4821                                     GTK_DIR_TAB_FORWARD);
4822     default:
4823       break;
4824     }
4825   return FALSE;
4826 }
4827
4828 static gint
4829 gtk_clist_button_press (GtkWidget      *widget,
4830                         GdkEventButton *event)
4831 {
4832   gint i;
4833   GtkCList *clist;
4834   gint x;
4835   gint y;
4836   gint row;
4837   gint column;
4838   gint button_actions;
4839
4840   g_return_val_if_fail (widget != NULL, FALSE);
4841   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4842   g_return_val_if_fail (event != NULL, FALSE);
4843
4844   clist = GTK_CLIST (widget);
4845
4846   button_actions = clist->button_actions[event->button - 1];
4847
4848   if (button_actions == GTK_BUTTON_IGNORED)
4849     return FALSE;
4850
4851   /* selections on the list */
4852   if (event->window == clist->clist_window)
4853     {
4854       x = event->x;
4855       y = event->y;
4856
4857       if (get_selection_info (clist, x, y, &row, &column))
4858         {
4859           gint old_row = clist->focus_row;
4860
4861           if (clist->focus_row == -1)
4862             old_row = row;
4863
4864           if (event->type == GDK_BUTTON_PRESS)
4865             {
4866               GdkEventMask mask = ((1 << (4 + event->button)) |
4867                                    GDK_POINTER_MOTION_HINT_MASK |
4868                                    GDK_BUTTON_RELEASE_MASK);
4869
4870               if (gdk_pointer_grab (clist->clist_window, FALSE, mask,
4871                                     NULL, NULL, event->time))
4872                 return FALSE;
4873               gtk_grab_add (widget);
4874
4875               clist->click_cell.row = row;
4876               clist->click_cell.column = column;
4877               clist->drag_button = event->button;
4878             }
4879           else
4880             {
4881               clist->click_cell.row = -1;
4882               clist->click_cell.column = -1;
4883
4884               clist->drag_button = 0;
4885               remove_grab (clist);
4886             }
4887
4888           if (button_actions & GTK_BUTTON_SELECTS)
4889             {
4890               if (GTK_CLIST_ADD_MODE(clist))
4891                 {
4892                   GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
4893                   if (GTK_WIDGET_HAS_FOCUS(widget))
4894                     {
4895                       gtk_clist_draw_focus (widget);
4896                       gdk_gc_set_line_attributes (clist->xor_gc, 1,
4897                                                   GDK_LINE_SOLID, 0, 0);
4898                       clist->focus_row = row;
4899                       gtk_clist_draw_focus (widget);
4900                     }
4901                   else
4902                     {
4903                       gdk_gc_set_line_attributes (clist->xor_gc, 1,
4904                                                   GDK_LINE_SOLID, 0, 0);
4905                       clist->focus_row = row;
4906                     }
4907                 }
4908               else if (row != clist->focus_row)
4909                 {
4910                   if (GTK_WIDGET_HAS_FOCUS(widget))
4911                     {
4912                       gtk_clist_draw_focus (widget);
4913                       clist->focus_row = row;
4914                       gtk_clist_draw_focus (widget);
4915                     }
4916                   else
4917                     clist->focus_row = row;
4918                 }
4919             }
4920
4921           if (!GTK_WIDGET_HAS_FOCUS(widget))
4922             gtk_widget_grab_focus (widget);
4923
4924           if (button_actions & GTK_BUTTON_SELECTS)
4925             {
4926               switch (clist->selection_mode)
4927                 {
4928                 case GTK_SELECTION_SINGLE:
4929                 case GTK_SELECTION_MULTIPLE:
4930                   if (event->type != GDK_BUTTON_PRESS)
4931                     gtk_signal_emit (GTK_OBJECT (clist),
4932                                      clist_signals[SELECT_ROW],
4933                                      row, column, event);
4934                   else
4935                     clist->anchor = row;
4936                   break;
4937                 case GTK_SELECTION_BROWSE:
4938                   gtk_signal_emit (GTK_OBJECT (clist),
4939                                    clist_signals[SELECT_ROW],
4940                                    row, column, event);
4941                   break;
4942                 case GTK_SELECTION_EXTENDED:
4943                   if (event->type != GDK_BUTTON_PRESS)
4944                     {
4945                       if (clist->anchor != -1)
4946                         {
4947                           update_extended_selection (clist, clist->focus_row);
4948                           GTK_CLIST_CLASS_FW (clist)->resync_selection
4949                             (clist, (GdkEvent *) event);
4950                         }
4951                       gtk_signal_emit (GTK_OBJECT (clist),
4952                                        clist_signals[SELECT_ROW],
4953                                        row, column, event);
4954                       break;
4955                     }
4956               
4957                   if (event->state & GDK_CONTROL_MASK)
4958                     {
4959                       if (event->state & GDK_SHIFT_MASK)
4960                         {
4961                           if (clist->anchor < 0)
4962                             {
4963                               g_list_free (clist->undo_selection);
4964                               g_list_free (clist->undo_unselection);
4965                               clist->undo_selection = NULL;
4966                               clist->undo_unselection = NULL;
4967                               clist->anchor = old_row;
4968                               clist->drag_pos = old_row;
4969                               clist->undo_anchor = old_row;
4970                             }
4971                           update_extended_selection (clist, clist->focus_row);
4972                         }
4973                       else
4974                         {
4975                           if (clist->anchor == -1)
4976                             set_anchor (clist, TRUE, row, old_row);
4977                           else
4978                             update_extended_selection (clist,
4979                                                        clist->focus_row);
4980                         }
4981                       break;
4982                     }
4983
4984                   if (event->state & GDK_SHIFT_MASK)
4985                     {
4986                       set_anchor (clist, FALSE, old_row, old_row);
4987                       update_extended_selection (clist, clist->focus_row);
4988                       break;
4989                     }
4990
4991                   if (clist->anchor == -1)
4992                     set_anchor (clist, FALSE, row, old_row);
4993                   else
4994                     update_extended_selection (clist, clist->focus_row);
4995                   break;
4996                 default:
4997                   break;
4998                 }
4999             }
5000         }
5001       return FALSE;
5002     }
5003
5004   /* press on resize windows */
5005   for (i = 0; i < clist->columns; i++)
5006     if (clist->column[i].resizeable && clist->column[i].window &&
5007         event->window == clist->column[i].window)
5008       {
5009         gpointer drag_data;
5010
5011         if (gdk_pointer_grab (clist->column[i].window, FALSE,
5012                               GDK_POINTER_MOTION_HINT_MASK |
5013                               GDK_BUTTON1_MOTION_MASK |
5014                               GDK_BUTTON_RELEASE_MASK,
5015                               NULL, NULL, event->time))
5016           return FALSE;
5017
5018         gtk_grab_add (widget);
5019         GTK_CLIST_SET_FLAG (clist, CLIST_IN_DRAG);
5020
5021         /* block attached dnd signal handler */
5022         drag_data = gtk_object_get_data (GTK_OBJECT (clist), "gtk-site-data");
5023         if (drag_data)
5024           gtk_signal_handler_block_by_data (GTK_OBJECT (clist), drag_data);
5025
5026         if (!GTK_WIDGET_HAS_FOCUS(widget))
5027           gtk_widget_grab_focus (widget);
5028
5029         clist->drag_pos = i;
5030         clist->x_drag = (COLUMN_LEFT_XPIXEL(clist, i) + COLUMN_INSET +
5031                          clist->column[i].area.width + CELL_SPACING);
5032
5033         if (GTK_CLIST_ADD_MODE(clist))
5034           gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
5035         draw_xor_line (clist);
5036       }
5037   return FALSE;
5038 }
5039
5040 static gint
5041 gtk_clist_button_release (GtkWidget      *widget,
5042                           GdkEventButton *event)
5043 {
5044   GtkCList *clist;
5045   gint button_actions;
5046
5047   g_return_val_if_fail (widget != NULL, FALSE);
5048   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
5049   g_return_val_if_fail (event != NULL, FALSE);
5050
5051   clist = GTK_CLIST (widget);
5052
5053   button_actions = clist->button_actions[event->button - 1];
5054   if (button_actions == GTK_BUTTON_IGNORED)
5055     return FALSE;
5056
5057   /* release on resize windows */
5058   if (GTK_CLIST_IN_DRAG(clist))
5059     {
5060       gpointer drag_data;
5061       gint width;
5062       gint x;
5063       gint i;
5064
5065       i = clist->drag_pos;
5066       clist->drag_pos = -1;
5067
5068       /* unblock attached dnd signal handler */
5069       drag_data = gtk_object_get_data (GTK_OBJECT (clist), "gtk-site-data");
5070       if (drag_data)
5071         gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist), drag_data);
5072
5073       GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
5074       gtk_widget_get_pointer (widget, &x, NULL);
5075       gtk_grab_remove (widget);
5076       gdk_pointer_ungrab (event->time);
5077
5078       if (clist->x_drag >= 0)
5079         draw_xor_line (clist);
5080
5081       if (GTK_CLIST_ADD_MODE(clist))
5082         {
5083           gdk_gc_set_line_attributes (clist->xor_gc, 1,
5084                                       GDK_LINE_ON_OFF_DASH, 0, 0);
5085           gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
5086         }
5087
5088       width = new_column_width (clist, i, &x);
5089       gtk_clist_set_column_width (clist, i, width);
5090       return FALSE;
5091     }
5092
5093   if (clist->drag_button == event->button)
5094     {
5095       gint row;
5096       gint column;
5097
5098       clist->drag_button = 0;
5099       clist->click_cell.row = -1;
5100       clist->click_cell.column = -1;
5101
5102       remove_grab (clist);
5103
5104       if (button_actions & GTK_BUTTON_SELECTS)
5105         {
5106           switch (clist->selection_mode)
5107             {
5108             case GTK_SELECTION_EXTENDED:
5109               if (!(event->state & GDK_SHIFT_MASK) ||
5110                   event->x < 0 || event->x >= clist->clist_window_width ||
5111                   event->y < 0 || event->y >= clist->clist_window_height)
5112                 GTK_CLIST_CLASS_FW (clist)->resync_selection
5113                   (clist, (GdkEvent *) event);
5114               break;
5115             case GTK_SELECTION_SINGLE:
5116             case GTK_SELECTION_MULTIPLE:
5117               if (get_selection_info (clist, event->x, event->y,
5118                                       &row, &column))
5119                 {
5120                   if (row >= 0 && row < clist->rows && clist->anchor == row)
5121                     toggle_row (clist, row, column, (GdkEvent *) event);
5122                 }
5123               clist->anchor = -1;
5124               break;
5125             default:
5126               break;
5127             }
5128         }
5129     }
5130   return FALSE;
5131 }
5132
5133 static gint
5134 gtk_clist_motion (GtkWidget      *widget,
5135                   GdkEventMotion *event)
5136 {
5137   GtkCList *clist;
5138   gint x;
5139   gint y;
5140   gint row;
5141   gint new_width;
5142   gint button_actions = 0;
5143
5144   g_return_val_if_fail (widget != NULL, FALSE);
5145   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
5146
5147   clist = GTK_CLIST (widget);
5148   if (!(gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)))
5149     return FALSE;
5150
5151   if (clist->drag_button > 0)
5152     button_actions = clist->button_actions[clist->drag_button - 1];
5153
5154   if (GTK_CLIST_IN_DRAG(clist))
5155     {
5156       if (event->is_hint || event->window != widget->window)
5157         gtk_widget_get_pointer (widget, &x, NULL);
5158       else
5159         x = event->x;
5160       
5161       new_width = new_column_width (clist, clist->drag_pos, &x);
5162       if (x != clist->x_drag)
5163         {
5164           /* x_drag < 0 indicates that the xor line is already invisible */
5165           if (clist->x_drag >= 0)
5166             draw_xor_line (clist);
5167
5168           clist->x_drag = x;
5169
5170           if (clist->x_drag >= 0)
5171             draw_xor_line (clist);
5172         }
5173
5174       if (new_width <= MAX (COLUMN_MIN_WIDTH + 1,
5175                             clist->column[clist->drag_pos].min_width + 1))
5176         {
5177           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) < 0 && x < 0)
5178             gtk_clist_moveto (clist, -1, clist->drag_pos, 0, 0);
5179           return FALSE;
5180         }
5181       if (clist->column[clist->drag_pos].max_width >= COLUMN_MIN_WIDTH &&
5182           new_width >= clist->column[clist->drag_pos].max_width)
5183         {
5184           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) + new_width >
5185               clist->clist_window_width && x < 0)
5186             move_horizontal (clist,
5187                              COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) +
5188                              new_width - clist->clist_window_width +
5189                              COLUMN_INSET + CELL_SPACING);
5190           return FALSE;
5191         }
5192     }
5193
5194   if (event->is_hint || event->window != clist->clist_window)
5195     gdk_window_get_pointer (clist->clist_window, &x, &y, NULL);
5196
5197   if (GTK_CLIST_REORDERABLE(clist) && button_actions & GTK_BUTTON_DRAGS)
5198     {
5199       /* delayed drag start */
5200       if (event->window == clist->clist_window &&
5201           clist->click_cell.row >= 0 && clist->click_cell.column >= 0 &&
5202           (y < 0 || y >= clist->clist_window_height ||
5203            x < 0 || x >= clist->clist_window_width  ||
5204            y < ROW_TOP_YPIXEL (clist, clist->click_cell.row) ||
5205            y >= (ROW_TOP_YPIXEL (clist, clist->click_cell.row) +
5206                  clist->row_height) ||
5207            x < COLUMN_LEFT_XPIXEL (clist, clist->click_cell.column) ||
5208            x >= (COLUMN_LEFT_XPIXEL(clist, clist->click_cell.column) + 
5209                  clist->column[clist->click_cell.column].area.width)))
5210         {
5211           GtkTargetList  *target_list;
5212
5213           target_list = gtk_target_list_new (&clist_target_table, 1);
5214           gtk_drag_begin (widget, target_list, GDK_ACTION_MOVE,
5215                           clist->drag_button, (GdkEvent *)event);
5216
5217         }
5218       return TRUE;
5219     }
5220
5221   /* horizontal autoscrolling */
5222   if (clist->hadjustment && LIST_WIDTH (clist) > clist->clist_window_width &&
5223       (x < 0 || x >= clist->clist_window_width))
5224     {
5225       if (clist->htimer)
5226         return FALSE;
5227
5228       clist->htimer = gtk_timeout_add
5229         (SCROLL_TIME, (GtkFunction) horizontal_timeout, clist);
5230
5231       if (!((x < 0 && clist->hadjustment->value == 0) ||
5232             (x >= clist->clist_window_width &&
5233              clist->hadjustment->value ==
5234              LIST_WIDTH (clist) - clist->clist_window_width)))
5235         {
5236           if (x < 0)
5237             move_horizontal (clist, -1 + (x/2));
5238           else
5239             move_horizontal (clist, 1 + (x - clist->clist_window_width) / 2);
5240         }
5241     }
5242
5243   if (GTK_CLIST_IN_DRAG(clist))
5244     return FALSE;
5245
5246   /* vertical autoscrolling */
5247   row = ROW_FROM_YPIXEL (clist, y);
5248
5249   /* don't scroll on last pixel row if it's a cell spacing */
5250   if (y == clist->clist_window_height - 1 &&
5251       y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height)
5252     return FALSE;
5253
5254   if (LIST_HEIGHT (clist) > clist->clist_window_height &&
5255       (y < 0 || y >= clist->clist_window_height))
5256     {
5257       if (clist->vtimer)
5258         return FALSE;
5259
5260       clist->vtimer = gtk_timeout_add (SCROLL_TIME,
5261                                        (GtkFunction) vertical_timeout, clist);
5262
5263       if (clist->drag_button &&
5264           ((y < 0 && clist->focus_row == 0) ||
5265            (y >= clist->clist_window_height &&
5266             clist->focus_row == clist->rows - 1)))
5267         return FALSE;
5268     }
5269
5270   row = CLAMP (row, 0, clist->rows - 1);
5271
5272   if (button_actions & GTK_BUTTON_SELECTS &
5273       !gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"))
5274     {
5275       if (row == clist->focus_row)
5276         return FALSE;
5277
5278       gtk_clist_draw_focus (widget);
5279       clist->focus_row = row;
5280       gtk_clist_draw_focus (widget);
5281
5282       switch (clist->selection_mode)
5283         {
5284         case GTK_SELECTION_BROWSE:
5285           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
5286                            clist->focus_row, -1, event);
5287           break;
5288         case GTK_SELECTION_EXTENDED:
5289           update_extended_selection (clist, clist->focus_row);
5290           break;
5291         default:
5292           break;
5293         }
5294     }
5295   
5296   if (ROW_TOP_YPIXEL(clist, row) < 0)
5297     move_vertical (clist, row, 0);
5298   else if (ROW_TOP_YPIXEL(clist, row) + clist->row_height >
5299            clist->clist_window_height)
5300     move_vertical (clist, row, 1);
5301
5302   return FALSE;
5303 }
5304
5305 static void
5306 gtk_clist_size_request (GtkWidget      *widget,
5307                         GtkRequisition *requisition)
5308 {
5309   GtkCList *clist;
5310   gint i;
5311
5312   g_return_if_fail (widget != NULL);
5313   g_return_if_fail (GTK_IS_CLIST (widget));
5314   g_return_if_fail (requisition != NULL);
5315
5316   clist = GTK_CLIST (widget);
5317
5318   requisition->width = 0;
5319   requisition->height = 0;
5320
5321   /* compute the size of the column title (title) area */
5322   clist->column_title_area.height = 0;
5323   if (GTK_CLIST_SHOW_TITLES(clist))
5324     for (i = 0; i < clist->columns; i++)
5325       if (clist->column[i].button)
5326         {
5327           GtkRequisition child_requisition;
5328           
5329           gtk_widget_size_request (clist->column[i].button,
5330                                    &child_requisition);
5331           clist->column_title_area.height =
5332             MAX (clist->column_title_area.height,
5333                  child_requisition.height);
5334         }
5335
5336   requisition->width += (widget->style->klass->xthickness +
5337                          GTK_CONTAINER (widget)->border_width) * 2;
5338   requisition->height += (clist->column_title_area.height +
5339                           (widget->style->klass->ythickness +
5340                            GTK_CONTAINER (widget)->border_width) * 2);
5341
5342   /* if (!clist->hadjustment) */
5343   requisition->width += list_requisition_width (clist);
5344   /* if (!clist->vadjustment) */
5345   requisition->height += LIST_HEIGHT (clist);
5346 }
5347
5348 static void
5349 gtk_clist_size_allocate (GtkWidget     *widget,
5350                          GtkAllocation *allocation)
5351 {
5352   GtkCList *clist;
5353   GtkAllocation clist_allocation;
5354   gint border_width;
5355
5356   g_return_if_fail (widget != NULL);
5357   g_return_if_fail (GTK_IS_CLIST (widget));
5358   g_return_if_fail (allocation != NULL);
5359
5360   clist = GTK_CLIST (widget);
5361   widget->allocation = *allocation;
5362   border_width = GTK_CONTAINER (widget)->border_width;
5363
5364   if (GTK_WIDGET_REALIZED (widget))
5365     {
5366       gdk_window_move_resize (widget->window,
5367                               allocation->x + border_width,
5368                               allocation->y + border_width,
5369                               allocation->width - border_width * 2,
5370                               allocation->height - border_width * 2);
5371     }
5372
5373   /* use internal allocation structure for all the math
5374    * because it's easier than always subtracting the container
5375    * border width */
5376   clist->internal_allocation.x = 0;
5377   clist->internal_allocation.y = 0;
5378   clist->internal_allocation.width = MAX (1, (gint)allocation->width -
5379                                           border_width * 2);
5380   clist->internal_allocation.height = MAX (1, (gint)allocation->height -
5381                                            border_width * 2);
5382         
5383   /* allocate clist window assuming no scrollbars */
5384   clist_allocation.x = (clist->internal_allocation.x +
5385                         widget->style->klass->xthickness);
5386   clist_allocation.y = (clist->internal_allocation.y +
5387                         widget->style->klass->ythickness +
5388                         clist->column_title_area.height);
5389   clist_allocation.width = MAX (1, (gint)clist->internal_allocation.width - 
5390                                 (2 * (gint)widget->style->klass->xthickness));
5391   clist_allocation.height = MAX (1, (gint)clist->internal_allocation.height -
5392                                  (2 * (gint)widget->style->klass->ythickness) -
5393                                  (gint)clist->column_title_area.height);
5394   
5395   clist->clist_window_width = clist_allocation.width;
5396   clist->clist_window_height = clist_allocation.height;
5397   
5398   if (GTK_WIDGET_REALIZED (widget))
5399     {
5400       gdk_window_move_resize (clist->clist_window,
5401                               clist_allocation.x,
5402                               clist_allocation.y,
5403                               clist_allocation.width,
5404                               clist_allocation.height);
5405     }
5406   
5407   /* position the window which holds the column title buttons */
5408   clist->column_title_area.x = widget->style->klass->xthickness;
5409   clist->column_title_area.y = widget->style->klass->ythickness;
5410   clist->column_title_area.width = clist_allocation.width;
5411   
5412   if (GTK_WIDGET_REALIZED (widget))
5413     {
5414       gdk_window_move_resize (clist->title_window,
5415                               clist->column_title_area.x,
5416                               clist->column_title_area.y,
5417                               clist->column_title_area.width,
5418                               clist->column_title_area.height);
5419     }
5420   
5421   /* column button allocation */
5422   size_allocate_columns (clist, FALSE);
5423   size_allocate_title_buttons (clist);
5424
5425   adjust_adjustments (clist, TRUE);
5426 }
5427
5428 /* GTKCONTAINER
5429  *   gtk_clist_forall
5430  */
5431 static void
5432 gtk_clist_forall (GtkContainer *container,
5433                   gboolean      include_internals,
5434                   GtkCallback   callback,
5435                   gpointer      callback_data)
5436 {
5437   GtkCList *clist;
5438   guint i;
5439
5440   g_return_if_fail (container != NULL);
5441   g_return_if_fail (GTK_IS_CLIST (container));
5442   g_return_if_fail (callback != NULL);
5443
5444   if (!include_internals)
5445     return;
5446
5447   clist = GTK_CLIST (container);
5448       
5449   /* callback for the column buttons */
5450   for (i = 0; i < clist->columns; i++)
5451     if (clist->column[i].button)
5452       (*callback) (clist->column[i].button, callback_data);
5453 }
5454
5455 /* PRIVATE DRAWING FUNCTIONS
5456  *   get_cell_style
5457  *   draw_cell_pixmap
5458  *   draw_row
5459  *   draw_rows
5460  *   draw_xor_line
5461  *   clist_refresh
5462  */
5463 static void
5464 get_cell_style (GtkCList     *clist,
5465                 GtkCListRow  *clist_row,
5466                 gint          state,
5467                 gint          column,
5468                 GtkStyle    **style,
5469                 GdkGC       **fg_gc,
5470                 GdkGC       **bg_gc)
5471 {
5472   gint fg_state;
5473
5474   if ((state == GTK_STATE_NORMAL) &&
5475       (GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
5476     fg_state = GTK_STATE_INSENSITIVE;
5477   else
5478     fg_state = state;
5479
5480   if (clist_row->cell[column].style)
5481     {
5482       if (style)
5483         *style = clist_row->cell[column].style;
5484       if (fg_gc)
5485         *fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
5486       if (bg_gc) {
5487         if (state == GTK_STATE_SELECTED)
5488           *bg_gc = clist_row->cell[column].style->bg_gc[state];
5489         else
5490           *bg_gc = clist_row->cell[column].style->base_gc[state];
5491       }
5492     }
5493   else if (clist_row->style)
5494     {
5495       if (style)
5496         *style = clist_row->style;
5497       if (fg_gc)
5498         *fg_gc = clist_row->style->fg_gc[fg_state];
5499       if (bg_gc) {
5500         if (state == GTK_STATE_SELECTED)
5501           *bg_gc = clist_row->style->bg_gc[state];
5502         else
5503           *bg_gc = clist_row->style->base_gc[state];
5504       }
5505     }
5506   else
5507     {
5508       if (style)
5509         *style = GTK_WIDGET (clist)->style;
5510       if (fg_gc)
5511         *fg_gc = GTK_WIDGET (clist)->style->fg_gc[fg_state];
5512       if (bg_gc) {
5513         if (state == GTK_STATE_SELECTED)
5514           *bg_gc = GTK_WIDGET (clist)->style->bg_gc[state];
5515         else
5516           *bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
5517       }
5518
5519       if (state != GTK_STATE_SELECTED)
5520         {
5521           if (fg_gc && clist_row->fg_set)
5522             *fg_gc = clist->fg_gc;
5523           if (bg_gc && clist_row->bg_set)
5524             *bg_gc = clist->bg_gc;
5525         }
5526     }
5527 }
5528
5529 static gint
5530 draw_cell_pixmap (GdkWindow    *window,
5531                   GdkRectangle *clip_rectangle,
5532                   GdkGC        *fg_gc,
5533                   GdkPixmap    *pixmap,
5534                   GdkBitmap    *mask,
5535                   gint          x,
5536                   gint          y,
5537                   gint          width,
5538                   gint          height)
5539 {
5540   gint xsrc = 0;
5541   gint ysrc = 0;
5542
5543   if (mask)
5544     {
5545       gdk_gc_set_clip_mask (fg_gc, mask);
5546       gdk_gc_set_clip_origin (fg_gc, x, y);
5547     }
5548
5549   if (x < clip_rectangle->x)
5550     {
5551       xsrc = clip_rectangle->x - x;
5552       width -= xsrc;
5553       x = clip_rectangle->x;
5554     }
5555   if (x + width > clip_rectangle->x + clip_rectangle->width)
5556     width = clip_rectangle->x + clip_rectangle->width - x;
5557
5558   if (y < clip_rectangle->y)
5559     {
5560       ysrc = clip_rectangle->y - y;
5561       height -= ysrc;
5562       y = clip_rectangle->y;
5563     }
5564   if (y + height > clip_rectangle->y + clip_rectangle->height)
5565     height = clip_rectangle->y + clip_rectangle->height - y;
5566
5567   gdk_draw_pixmap (window, fg_gc, pixmap, xsrc, ysrc, x, y, width, height);
5568   gdk_gc_set_clip_origin (fg_gc, 0, 0);
5569   if (mask)
5570     gdk_gc_set_clip_mask (fg_gc, NULL);
5571
5572   return x + MAX (width, 0);
5573 }
5574
5575 static void
5576 draw_row (GtkCList     *clist,
5577           GdkRectangle *area,
5578           gint          row,
5579           GtkCListRow  *clist_row)
5580 {
5581   GtkWidget *widget;
5582   GdkRectangle *rect;
5583   GdkRectangle row_rectangle;
5584   GdkRectangle cell_rectangle;
5585   GdkRectangle clip_rectangle;
5586   GdkRectangle intersect_rectangle;
5587   gint last_column;
5588   gint state;
5589   gint i;
5590
5591   g_return_if_fail (clist != NULL);
5592
5593   /* bail now if we arn't drawable yet */
5594   if (!GTK_WIDGET_DRAWABLE (clist) || row < 0 || row >= clist->rows)
5595     return;
5596
5597   widget = GTK_WIDGET (clist);
5598
5599   /* if the function is passed the pointer to the row instead of null,
5600    * it avoids this expensive lookup */
5601   if (!clist_row)
5602     clist_row = (g_list_nth (clist->row_list, row))->data;
5603
5604   /* rectangle of the entire row */
5605   row_rectangle.x = 0;
5606   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
5607   row_rectangle.width = clist->clist_window_width;
5608   row_rectangle.height = clist->row_height;
5609
5610   /* rectangle of the cell spacing above the row */
5611   cell_rectangle.x = 0;
5612   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
5613   cell_rectangle.width = row_rectangle.width;
5614   cell_rectangle.height = CELL_SPACING;
5615
5616   /* rectangle used to clip drawing operations, it's y and height
5617    * positions only need to be set once, so we set them once here. 
5618    * the x and width are set withing the drawing loop below once per
5619    * column */
5620   clip_rectangle.y = row_rectangle.y;
5621   clip_rectangle.height = row_rectangle.height;
5622
5623   if (clist_row->state == GTK_STATE_NORMAL)
5624     {
5625       if (clist_row->fg_set)
5626         gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
5627       if (clist_row->bg_set)
5628         gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
5629     }
5630
5631   state = clist_row->state;
5632
5633   /* draw the cell borders and background */
5634   if (area)
5635     {
5636       rect = &intersect_rectangle;
5637       if (gdk_rectangle_intersect (area, &cell_rectangle,
5638                                    &intersect_rectangle))
5639         gdk_draw_rectangle (clist->clist_window,
5640                             widget->style->base_gc[GTK_STATE_ACTIVE],
5641                             TRUE,
5642                             intersect_rectangle.x,
5643                             intersect_rectangle.y,
5644                             intersect_rectangle.width,
5645                             intersect_rectangle.height);
5646
5647       /* the last row has to clear it's bottom cell spacing too */
5648       if (clist_row == clist->row_list_end->data)
5649         {
5650           cell_rectangle.y += clist->row_height + CELL_SPACING;
5651
5652           if (gdk_rectangle_intersect (area, &cell_rectangle,
5653                                        &intersect_rectangle))
5654             gdk_draw_rectangle (clist->clist_window,
5655                                 widget->style->base_gc[GTK_STATE_ACTIVE],
5656                                 TRUE,
5657                                 intersect_rectangle.x,
5658                                 intersect_rectangle.y,
5659                                 intersect_rectangle.width,
5660                                 intersect_rectangle.height);
5661         }
5662
5663       if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
5664         return;
5665
5666     }
5667   else
5668     {
5669       rect = &clip_rectangle;
5670       gdk_draw_rectangle (clist->clist_window,
5671                           widget->style->base_gc[GTK_STATE_ACTIVE],
5672                           TRUE,
5673                           cell_rectangle.x,
5674                           cell_rectangle.y,
5675                           cell_rectangle.width,
5676                           cell_rectangle.height);
5677
5678       /* the last row has to clear it's bottom cell spacing too */
5679       if (clist_row == clist->row_list_end->data)
5680         {
5681           cell_rectangle.y += clist->row_height + CELL_SPACING;
5682
5683           gdk_draw_rectangle (clist->clist_window,
5684                               widget->style->base_gc[GTK_STATE_ACTIVE],
5685                               TRUE,
5686                               cell_rectangle.x,
5687                               cell_rectangle.y,
5688                               cell_rectangle.width,
5689                               cell_rectangle.height);     
5690         }         
5691     }
5692   
5693   for (last_column = clist->columns - 1;
5694        last_column >= 0 && !clist->column[last_column].visible; last_column--)
5695     ;
5696
5697   /* iterate and draw all the columns (row cells) and draw their contents */
5698   for (i = 0; i < clist->columns; i++)
5699     {
5700       GtkStyle *style;
5701       GdkGC *fg_gc;
5702       GdkGC *bg_gc;
5703
5704       gint width;
5705       gint height;
5706       gint pixmap_width;
5707       gint offset = 0;
5708       gint row_center_offset;
5709
5710       if (!clist->column[i].visible)
5711         continue;
5712
5713       get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
5714
5715       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
5716       clip_rectangle.width = clist->column[i].area.width;
5717
5718       /* calculate clipping region clipping region */
5719       clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
5720       clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
5721                                (i == last_column) * CELL_SPACING);
5722       
5723       if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
5724                                             &intersect_rectangle))
5725         continue;
5726
5727       gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
5728                           rect->x, rect->y, rect->width, rect->height);
5729
5730       clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
5731       clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
5732                                (i == last_column) * CELL_SPACING);
5733
5734       /* calculate real width for column justification */
5735       pixmap_width = 0;
5736       offset = 0;
5737       switch (clist_row->cell[i].type)
5738         {
5739         case GTK_CELL_TEXT:
5740           width = gdk_string_width (style->font,
5741                                     GTK_CELL_TEXT (clist_row->cell[i])->text);
5742           break;
5743         case GTK_CELL_PIXMAP:
5744           gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5745                                &pixmap_width, &height);
5746           width = pixmap_width;
5747           break;
5748         case GTK_CELL_PIXTEXT:
5749           gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5750                                &pixmap_width, &height);
5751           width = (pixmap_width +
5752                    GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing +
5753                    gdk_string_width (style->font,
5754                                      GTK_CELL_PIXTEXT
5755                                      (clist_row->cell[i])->text));
5756           break;
5757         default:
5758           continue;
5759           break;
5760         }
5761
5762       switch (clist->column[i].justification)
5763         {
5764         case GTK_JUSTIFY_LEFT:
5765           offset = clip_rectangle.x + clist_row->cell[i].horizontal;
5766           break;
5767         case GTK_JUSTIFY_RIGHT:
5768           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5769                     clip_rectangle.width - width);
5770           break;
5771         case GTK_JUSTIFY_CENTER:
5772         case GTK_JUSTIFY_FILL:
5773           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5774                     (clip_rectangle.width / 2) - (width / 2));
5775           break;
5776         };
5777
5778       /* Draw Text and/or Pixmap */
5779       switch (clist_row->cell[i].type)
5780         {
5781         case GTK_CELL_PIXMAP:
5782           draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5783                             GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5784                             GTK_CELL_PIXMAP (clist_row->cell[i])->mask,
5785                             offset,
5786                             clip_rectangle.y + clist_row->cell[i].vertical +
5787                             (clip_rectangle.height - height) / 2,
5788                             pixmap_width, height);
5789           break;
5790         case GTK_CELL_PIXTEXT:
5791           offset =
5792             draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5793                               GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5794                               GTK_CELL_PIXTEXT (clist_row->cell[i])->mask,
5795                               offset,
5796                               clip_rectangle.y + clist_row->cell[i].vertical+
5797                               (clip_rectangle.height - height) / 2,
5798                               pixmap_width, height);
5799           offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
5800         case GTK_CELL_TEXT:
5801           if (style != GTK_WIDGET (clist)->style)
5802             row_center_offset = (((clist->row_height - style->font->ascent -
5803                                   style->font->descent - 1) / 2) + 1.5 +
5804                                  style->font->ascent);
5805           else
5806             row_center_offset = clist->row_center_offset;
5807
5808           gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
5809           gdk_draw_string (clist->clist_window, style->font, fg_gc,
5810                            offset,
5811                            row_rectangle.y + row_center_offset + 
5812                            clist_row->cell[i].vertical,
5813                            (clist_row->cell[i].type == GTK_CELL_PIXTEXT) ?
5814                            GTK_CELL_PIXTEXT (clist_row->cell[i])->text :
5815                            GTK_CELL_TEXT (clist_row->cell[i])->text);
5816           gdk_gc_set_clip_rectangle (fg_gc, NULL);
5817           break;
5818         default:
5819           break;
5820         }
5821     }
5822
5823   /* draw focus rectangle */
5824   if (clist->focus_row == row &&
5825       GTK_WIDGET_CAN_FOCUS (widget) && GTK_WIDGET_HAS_FOCUS(widget))
5826     {
5827       if (!area)
5828         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5829                             row_rectangle.x, row_rectangle.y,
5830                             row_rectangle.width - 1, row_rectangle.height - 1);
5831       else if (gdk_rectangle_intersect (area, &row_rectangle,
5832                                         &intersect_rectangle))
5833         {
5834           gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
5835           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5836                               row_rectangle.x, row_rectangle.y,
5837                               row_rectangle.width - 1,
5838                               row_rectangle.height - 1);
5839           gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
5840         }
5841     }
5842 }
5843
5844 static void
5845 draw_rows (GtkCList     *clist,
5846            GdkRectangle *area)
5847 {
5848   GList *list;
5849   GtkCListRow *clist_row;
5850   gint i;
5851   gint first_row;
5852   gint last_row;
5853
5854   g_return_if_fail (clist != NULL);
5855   g_return_if_fail (GTK_IS_CLIST (clist));
5856
5857   if (clist->row_height == 0 ||
5858       !GTK_WIDGET_DRAWABLE (clist))
5859     return;
5860
5861   if (area)
5862     {
5863       first_row = ROW_FROM_YPIXEL (clist, area->y);
5864       last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
5865     }
5866   else
5867     {
5868       first_row = ROW_FROM_YPIXEL (clist, 0);
5869       last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
5870     }
5871
5872   /* this is a small special case which exposes the bottom cell line
5873    * on the last row -- it might go away if I change the wall the cell
5874    * spacings are drawn
5875    */
5876   if (clist->rows == first_row)
5877     first_row--;
5878
5879   list = g_list_nth (clist->row_list, first_row);
5880   i = first_row;
5881   while (list)
5882     {
5883       clist_row = list->data;
5884       list = list->next;
5885
5886       if (i > last_row)
5887         return;
5888
5889       GTK_CLIST_CLASS_FW (clist)->draw_row (clist, area, i, clist_row);
5890       i++;
5891     }
5892
5893   if (!area)
5894     gdk_window_clear_area (clist->clist_window,
5895                            0, ROW_TOP_YPIXEL (clist, i), -1, -1);
5896 }
5897
5898 static void                          
5899 draw_xor_line (GtkCList *clist)
5900 {
5901   GtkWidget *widget;
5902
5903   g_return_if_fail (clist != NULL);
5904
5905   widget = GTK_WIDGET (clist);
5906
5907   gdk_draw_line (widget->window, clist->xor_gc,
5908                  clist->x_drag,
5909                  widget->style->klass->ythickness,
5910                  clist->x_drag,
5911                  clist->column_title_area.height +
5912                  clist->clist_window_height + 1);
5913 }
5914
5915 static void
5916 clist_refresh (GtkCList *clist)
5917 {
5918   g_return_if_fail (clist != NULL);
5919   g_return_if_fail (GTK_IS_CLIST (clist));
5920   
5921   if (CLIST_UNFROZEN (clist))
5922     { 
5923       adjust_adjustments (clist, FALSE);
5924       draw_rows (clist, NULL);
5925     }
5926 }
5927
5928 /* get cell from coordinates
5929  *   get_selection_info
5930  *   gtk_clist_get_selection_info
5931  */
5932 static gint
5933 get_selection_info (GtkCList *clist,
5934                     gint      x,
5935                     gint      y,
5936                     gint     *row,
5937                     gint     *column)
5938 {
5939   gint trow, tcol;
5940
5941   g_return_val_if_fail (clist != NULL, 0);
5942   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
5943
5944   /* bounds checking, return false if the user clicked 
5945    * on a blank area */
5946   trow = ROW_FROM_YPIXEL (clist, y);
5947   if (trow >= clist->rows)
5948     return 0;
5949
5950   if (row)
5951     *row = trow;
5952
5953   tcol = COLUMN_FROM_XPIXEL (clist, x);
5954   if (tcol >= clist->columns)
5955     return 0;
5956
5957   if (column)
5958     *column = tcol;
5959
5960   return 1;
5961 }
5962
5963 gint
5964 gtk_clist_get_selection_info (GtkCList *clist, 
5965                               gint      x, 
5966                               gint      y, 
5967                               gint     *row, 
5968                               gint     *column)
5969 {
5970   g_return_val_if_fail (clist != NULL, 0);
5971   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
5972   return get_selection_info (clist, x, y, row, column);
5973 }
5974
5975 /* PRIVATE ADJUSTMENT FUNCTIONS
5976  *   adjust_adjustments
5977  *   vadjustment_changed
5978  *   hadjustment_changed
5979  *   vadjustment_value_changed
5980  *   hadjustment_value_changed 
5981  *   check_exposures
5982  */
5983 static void
5984 adjust_adjustments (GtkCList *clist,
5985                     gboolean  block_resize)
5986 {
5987   if (clist->vadjustment)
5988     {
5989       clist->vadjustment->page_size = clist->clist_window_height;
5990       clist->vadjustment->page_increment = clist->clist_window_height / 2;
5991       clist->vadjustment->step_increment = clist->row_height;
5992       clist->vadjustment->lower = 0;
5993       clist->vadjustment->upper = LIST_HEIGHT (clist);
5994
5995       if (clist->clist_window_height - clist->voffset > LIST_HEIGHT (clist))
5996         {
5997           clist->vadjustment->value = MAX (0, (LIST_HEIGHT (clist) -
5998                                                clist->clist_window_height));
5999           gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment),
6000                                    "value_changed");
6001         }
6002       gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment), "changed");
6003     }
6004
6005   if (clist->hadjustment)
6006     {
6007       clist->hadjustment->page_size = clist->clist_window_width;
6008       clist->hadjustment->page_increment = clist->clist_window_width / 2;
6009       clist->hadjustment->step_increment = 10;
6010       clist->hadjustment->lower = 0;
6011       clist->hadjustment->upper = LIST_WIDTH (clist);
6012
6013       if (clist->clist_window_width - clist->hoffset > LIST_WIDTH (clist))
6014         {
6015           clist->hadjustment->value = MAX (0, (LIST_WIDTH (clist) -
6016                                                clist->clist_window_width));
6017           gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment),
6018                                    "value_changed");
6019         }
6020       gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment), "changed");
6021     }
6022
6023   if (!block_resize && (!clist->vadjustment || !clist->hadjustment))
6024     {
6025       GtkWidget *widget;
6026       GtkRequisition requisition;
6027
6028       widget = GTK_WIDGET (clist);
6029       gtk_widget_size_request (widget, &requisition);
6030
6031       if ((!clist->hadjustment &&
6032            requisition.width != widget->allocation.width) ||
6033           (!clist->vadjustment &&
6034            requisition.height != widget->allocation.height))
6035         gtk_widget_queue_resize (widget);
6036     }
6037 }
6038
6039 static void
6040 vadjustment_changed (GtkAdjustment *adjustment,
6041                      gpointer       data)
6042 {
6043   GtkCList *clist;
6044
6045   g_return_if_fail (adjustment != NULL);
6046   g_return_if_fail (data != NULL);
6047
6048   clist = GTK_CLIST (data);
6049 }
6050
6051 static void
6052 hadjustment_changed (GtkAdjustment *adjustment,
6053                      gpointer       data)
6054 {
6055   GtkCList *clist;
6056
6057   g_return_if_fail (adjustment != NULL);
6058   g_return_if_fail (data != NULL);
6059
6060   clist = GTK_CLIST (data);
6061 }
6062
6063 static void
6064 vadjustment_value_changed (GtkAdjustment *adjustment,
6065                            gpointer       data)
6066 {
6067   GtkCList *clist;
6068   GdkRectangle area;
6069   gint diff, value;
6070
6071   g_return_if_fail (adjustment != NULL);
6072   g_return_if_fail (data != NULL);
6073   g_return_if_fail (GTK_IS_CLIST (data));
6074
6075   clist = GTK_CLIST (data);
6076
6077   if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->vadjustment)
6078     return;
6079
6080   value = adjustment->value;
6081
6082   if (value > -clist->voffset)
6083     {
6084       /* scroll down */
6085       diff = value + clist->voffset;
6086
6087       /* we have to re-draw the whole screen here... */
6088       if (diff >= clist->clist_window_height)
6089         {
6090           clist->voffset = -value;
6091           draw_rows (clist, NULL);
6092           return;
6093         }
6094
6095       if ((diff != 0) && (diff != clist->clist_window_height))
6096         gdk_window_copy_area (clist->clist_window, clist->fg_gc,
6097                               0, 0, clist->clist_window, 0, diff,
6098                               clist->clist_window_width,
6099                               clist->clist_window_height - diff);
6100
6101       area.x = 0;
6102       area.y = clist->clist_window_height - diff;
6103       area.width = clist->clist_window_width;
6104       area.height = diff;
6105     }
6106   else
6107     {
6108       /* scroll up */
6109       diff = -clist->voffset - value;
6110
6111       /* we have to re-draw the whole screen here... */
6112       if (diff >= clist->clist_window_height)
6113         {
6114           clist->voffset = -value;
6115           draw_rows (clist, NULL);
6116           return;
6117         }
6118
6119       if ((diff != 0) && (diff != clist->clist_window_height))
6120         gdk_window_copy_area (clist->clist_window, clist->fg_gc,
6121                               0, diff, clist->clist_window, 0, 0,
6122                               clist->clist_window_width,
6123                               clist->clist_window_height - diff);
6124
6125       area.x = 0;
6126       area.y = 0;
6127       area.width = clist->clist_window_width;
6128       area.height = diff;
6129     }
6130
6131   clist->voffset = -value;
6132   if ((diff != 0) && (diff != clist->clist_window_height))
6133     check_exposures (clist);
6134
6135   draw_rows (clist, &area);
6136 }
6137
6138 static void
6139 hadjustment_value_changed (GtkAdjustment *adjustment,
6140                            gpointer       data)
6141 {
6142   GtkCList *clist;
6143   GdkRectangle area;
6144   gint i;
6145   gint y = 0;
6146   gint diff = 0;
6147   gint value;
6148
6149   g_return_if_fail (adjustment != NULL);
6150   g_return_if_fail (data != NULL);
6151   g_return_if_fail (GTK_IS_CLIST (data));
6152
6153   clist = GTK_CLIST (data);
6154
6155   if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->hadjustment)
6156     return;
6157
6158   value = adjustment->value;
6159
6160   /* move the column buttons and resize windows */
6161   for (i = 0; i < clist->columns; i++)
6162     {
6163       if (clist->column[i].button)
6164         {
6165           clist->column[i].button->allocation.x -= value + clist->hoffset;
6166           
6167           if (clist->column[i].button->window)
6168             {
6169               gdk_window_move (clist->column[i].button->window,
6170                                clist->column[i].button->allocation.x,
6171                                clist->column[i].button->allocation.y);
6172               
6173               if (clist->column[i].window)
6174                 gdk_window_move (clist->column[i].window,
6175                                  clist->column[i].button->allocation.x +
6176                                  clist->column[i].button->allocation.width - 
6177                                  (DRAG_WIDTH / 2), 0); 
6178             }
6179         }
6180     }
6181
6182   if (value > -clist->hoffset)
6183     {
6184       /* scroll right */
6185       diff = value + clist->hoffset;
6186       
6187       clist->hoffset = -value;
6188       
6189       /* we have to re-draw the whole screen here... */
6190       if (diff >= clist->clist_window_width)
6191         {
6192           draw_rows (clist, NULL);
6193           return;
6194         }
6195
6196       if (GTK_WIDGET_CAN_FOCUS(clist) && GTK_WIDGET_HAS_FOCUS(clist) &&
6197           !GTK_CLIST_CHILD_HAS_FOCUS(clist) && GTK_CLIST_ADD_MODE(clist))
6198         {
6199           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6200               
6201           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
6202                               clist->clist_window_width - 1,
6203                               clist->row_height - 1);
6204         }
6205       gdk_window_copy_area (clist->clist_window,
6206                             clist->fg_gc,
6207                             0, 0,
6208                             clist->clist_window,
6209                             diff,
6210                             0,
6211                             clist->clist_window_width - diff,
6212                             clist->clist_window_height);
6213
6214       area.x = clist->clist_window_width - diff;
6215     }
6216   else
6217     {
6218       /* scroll left */
6219       if (!(diff = -clist->hoffset - value))
6220         return;
6221
6222       clist->hoffset = -value;
6223       
6224       /* we have to re-draw the whole screen here... */
6225       if (diff >= clist->clist_window_width)
6226         {
6227           draw_rows (clist, NULL);
6228           return;
6229         }
6230       
6231       if (GTK_WIDGET_CAN_FOCUS(clist) && GTK_WIDGET_HAS_FOCUS(clist) &&
6232           !GTK_CLIST_CHILD_HAS_FOCUS(clist) && GTK_CLIST_ADD_MODE(clist))
6233         {
6234           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6235           
6236           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
6237                               clist->clist_window_width - 1,
6238                               clist->row_height - 1);
6239         }
6240
6241       gdk_window_copy_area (clist->clist_window,
6242                             clist->fg_gc,
6243                             diff, 0,
6244                             clist->clist_window,
6245                             0,
6246                             0,
6247                             clist->clist_window_width - diff,
6248                             clist->clist_window_height);
6249           
6250       area.x = 0;
6251     }
6252
6253   area.y = 0;
6254   area.width = diff;
6255   area.height = clist->clist_window_height;
6256
6257   check_exposures (clist);
6258
6259   if (GTK_WIDGET_CAN_FOCUS(clist) && GTK_WIDGET_HAS_FOCUS(clist) &&
6260       !GTK_CLIST_CHILD_HAS_FOCUS(clist))
6261     {
6262       if (GTK_CLIST_ADD_MODE(clist))
6263         {
6264           gint focus_row;
6265           
6266           focus_row = clist->focus_row;
6267           clist->focus_row = -1;
6268           draw_rows (clist, &area);
6269           clist->focus_row = focus_row;
6270           
6271           gdk_draw_rectangle (clist->clist_window, clist->xor_gc,
6272                               FALSE, 0, y, clist->clist_window_width - 1,
6273                               clist->row_height - 1);
6274           return;
6275         }
6276       else
6277         {
6278           gint x0;
6279           gint x1;
6280           
6281           if (area.x == 0)
6282             {
6283               x0 = clist->clist_window_width - 1;
6284               x1 = diff;
6285             }
6286           else
6287             {
6288               x0 = 0;
6289               x1 = area.x - 1;
6290             }
6291           
6292           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6293           gdk_draw_line (clist->clist_window, clist->xor_gc,
6294                          x0, y + 1, x0, y + clist->row_height - 2);
6295           gdk_draw_line (clist->clist_window, clist->xor_gc,
6296                          x1, y + 1, x1, y + clist->row_height - 2);
6297           
6298         }
6299     }
6300   draw_rows (clist, &area);
6301 }
6302
6303 static void
6304 check_exposures (GtkCList *clist)
6305 {
6306   GdkEvent *event;
6307
6308   if (!GTK_WIDGET_REALIZED (clist))
6309     return;
6310
6311   /* Make sure graphics expose events are processed before scrolling
6312    * again */
6313   while ((event = gdk_event_get_graphics_expose (clist->clist_window)) != NULL)
6314     {
6315       gtk_widget_event (GTK_WIDGET (clist), event);
6316       if (event->expose.count == 0)
6317         {
6318           gdk_event_free (event);
6319           break;
6320         }
6321       gdk_event_free (event);
6322     }
6323 }
6324
6325 /* PRIVATE 
6326  * Memory Allocation/Distruction Routines for GtkCList stuctures
6327  *
6328  * functions:
6329  *   columns_new
6330  *   column_title_new
6331  *   columns_delete
6332  *   row_new
6333  *   row_delete
6334  */
6335 static GtkCListColumn *
6336 columns_new (GtkCList *clist)
6337 {
6338   GtkCListColumn *column;
6339   gint i;
6340
6341   column = g_new (GtkCListColumn, clist->columns);
6342
6343   for (i = 0; i < clist->columns; i++)
6344     {
6345       column[i].area.x = 0;
6346       column[i].area.y = 0;
6347       column[i].area.width = 0;
6348       column[i].area.height = 0;
6349       column[i].title = NULL;
6350       column[i].button = NULL;
6351       column[i].window = NULL;
6352       column[i].width = 0;
6353       column[i].min_width = -1;
6354       column[i].max_width = -1;
6355       column[i].visible = TRUE;
6356       column[i].width_set = FALSE;
6357       column[i].resizeable = TRUE;
6358       column[i].auto_resize = FALSE;
6359       column[i].justification = GTK_JUSTIFY_LEFT;
6360     }
6361
6362   return column;
6363 }
6364
6365 static void
6366 column_title_new (GtkCList    *clist,
6367                   gint         column,
6368                   const gchar *title)
6369 {
6370   if (clist->column[column].title)
6371     g_free (clist->column[column].title);
6372
6373   clist->column[column].title = g_strdup (title);
6374 }
6375
6376 static void
6377 columns_delete (GtkCList *clist)
6378 {
6379   gint i;
6380
6381   for (i = 0; i < clist->columns; i++)
6382     if (clist->column[i].title)
6383       g_free (clist->column[i].title);
6384       
6385   g_free (clist->column);
6386 }
6387
6388 static GtkCListRow *
6389 row_new (GtkCList *clist)
6390 {
6391   int i;
6392   GtkCListRow *clist_row;
6393
6394   clist_row = g_chunk_new (GtkCListRow, clist->row_mem_chunk);
6395   clist_row->cell = g_chunk_new (GtkCell, clist->cell_mem_chunk);
6396
6397   for (i = 0; i < clist->columns; i++)
6398     {
6399       clist_row->cell[i].type = GTK_CELL_EMPTY;
6400       clist_row->cell[i].vertical = 0;
6401       clist_row->cell[i].horizontal = 0;
6402       clist_row->cell[i].style = NULL;
6403     }
6404
6405   clist_row->fg_set = FALSE;
6406   clist_row->bg_set = FALSE;
6407   clist_row->style = NULL;
6408   clist_row->selectable = TRUE;
6409   clist_row->state = GTK_STATE_NORMAL;
6410   clist_row->data = NULL;
6411   clist_row->destroy = NULL;
6412
6413   return clist_row;
6414 }
6415
6416 static void
6417 row_delete (GtkCList    *clist,
6418             GtkCListRow *clist_row)
6419 {
6420   gint i;
6421
6422   for (i = 0; i < clist->columns; i++)
6423     {
6424       GTK_CLIST_CLASS_FW (clist)->set_cell_contents
6425         (clist, clist_row, i, GTK_CELL_EMPTY, NULL, 0, NULL, NULL);
6426       if (clist_row->cell[i].style)
6427         {
6428           if (GTK_WIDGET_REALIZED (clist))
6429             gtk_style_detach (clist_row->cell[i].style);
6430           gtk_style_unref (clist_row->cell[i].style);
6431         }
6432     }
6433
6434   if (clist_row->style)
6435     {
6436       if (GTK_WIDGET_REALIZED (clist))
6437         gtk_style_detach (clist_row->style);
6438       gtk_style_unref (clist_row->style);
6439     }
6440
6441   if (clist_row->destroy)
6442     clist_row->destroy (clist_row->data);
6443
6444   g_mem_chunk_free (clist->cell_mem_chunk, clist_row->cell);
6445   g_mem_chunk_free (clist->row_mem_chunk, clist_row);
6446 }
6447
6448 /* FOCUS FUNCTIONS
6449  *   gtk_clist_focus
6450  *   gtk_clist_draw_focus
6451  *   gtk_clist_focus_in
6452  *   gtk_clist_focus_out
6453  *   gtk_clist_set_focus_child
6454  *   title_focus
6455  */
6456 static gint
6457 gtk_clist_focus (GtkContainer     *container,
6458                  GtkDirectionType  direction)
6459 {
6460   GtkCList *clist;
6461   GtkWidget *focus_child;
6462   gint old_row;
6463
6464   g_return_val_if_fail (container != NULL, FALSE);
6465   g_return_val_if_fail (GTK_IS_CLIST (container), FALSE);
6466
6467   if (!GTK_WIDGET_IS_SENSITIVE (container))
6468     return FALSE;
6469   
6470   clist = GTK_CLIST (container);
6471   focus_child = container->focus_child;
6472   old_row = clist->focus_row;
6473
6474   switch (direction)
6475     {
6476     case GTK_DIR_LEFT:
6477     case GTK_DIR_RIGHT:
6478       if (GTK_CLIST_CHILD_HAS_FOCUS(clist))
6479         {
6480           if (title_focus (clist, direction))
6481             return TRUE;
6482           gtk_container_set_focus_child (container, NULL);
6483           return FALSE;
6484          }
6485       gtk_widget_grab_focus (GTK_WIDGET (container));
6486       return TRUE;
6487     case GTK_DIR_DOWN:
6488     case GTK_DIR_TAB_FORWARD:
6489       if (GTK_CLIST_CHILD_HAS_FOCUS(clist))
6490         {
6491           gboolean tf = FALSE;
6492
6493           if (((focus_child && direction == GTK_DIR_DOWN) ||
6494                !(tf = title_focus (clist, GTK_DIR_TAB_FORWARD)))
6495               && clist->rows)
6496             {
6497               if (clist->focus_row < 0)
6498                 {
6499                   clist->focus_row = 0;
6500
6501                   if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6502                        clist->selection_mode == GTK_SELECTION_EXTENDED) &&
6503                       !clist->selection)
6504                     gtk_signal_emit (GTK_OBJECT (clist),
6505                                      clist_signals[SELECT_ROW],
6506                                      clist->focus_row, -1, NULL);
6507                 }
6508               gtk_widget_grab_focus (GTK_WIDGET (container));
6509               return TRUE;
6510             }
6511
6512           if (tf)
6513             return TRUE;
6514         }
6515       
6516       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
6517       break;
6518     case GTK_DIR_UP:
6519     case GTK_DIR_TAB_BACKWARD:
6520       if (!focus_child &&
6521           GTK_CLIST_CHILD_HAS_FOCUS(clist) && clist->rows)
6522         {
6523           if (clist->focus_row < 0)
6524             {
6525               clist->focus_row = 0;
6526               if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6527                    clist->selection_mode == GTK_SELECTION_EXTENDED) &&
6528                   !clist->selection)
6529                 gtk_signal_emit (GTK_OBJECT (clist),
6530                                  clist_signals[SELECT_ROW],
6531                                  clist->focus_row, -1, NULL);
6532             }
6533           gtk_widget_grab_focus (GTK_WIDGET (container));
6534           return TRUE;
6535         }
6536
6537       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
6538
6539       if (title_focus (clist, direction))
6540         return TRUE;
6541
6542       break;
6543     default:
6544       break;
6545     }
6546
6547   gtk_container_set_focus_child (container, NULL);
6548   return FALSE;
6549 }
6550
6551 static void
6552 gtk_clist_draw_focus (GtkWidget *widget)
6553 {
6554   GtkCList *clist;
6555
6556   g_return_if_fail (widget != NULL);
6557   g_return_if_fail (GTK_IS_CLIST (widget));
6558
6559   if (!GTK_WIDGET_DRAWABLE (widget) || !GTK_WIDGET_CAN_FOCUS (widget))
6560     return;
6561
6562   clist = GTK_CLIST (widget);
6563   if (clist->focus_row >= 0)
6564     gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
6565                         0, ROW_TOP_YPIXEL(clist, clist->focus_row),
6566                         clist->clist_window_width - 1,
6567                         clist->row_height - 1);
6568 }
6569
6570 static gint
6571 gtk_clist_focus_in (GtkWidget     *widget,
6572                     GdkEventFocus *event)
6573 {
6574   GtkCList *clist;
6575
6576   g_return_val_if_fail (widget != NULL, FALSE);
6577   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
6578   g_return_val_if_fail (event != NULL, FALSE);
6579
6580   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
6581   GTK_CLIST_UNSET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
6582
6583   clist = GTK_CLIST (widget);
6584
6585   if (clist->selection_mode == GTK_SELECTION_BROWSE &&
6586       clist->selection == NULL && clist->focus_row > -1)
6587     {
6588       GList *list;
6589
6590       list = g_list_nth (clist->row_list, clist->focus_row);
6591       if (list && GTK_CLIST_ROW (list)->selectable)
6592         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6593                          clist->focus_row, -1, event);
6594       else
6595         gtk_widget_draw_focus (widget);
6596     }
6597   else
6598     gtk_widget_draw_focus (widget);
6599
6600   return FALSE;
6601 }
6602
6603 static gint
6604 gtk_clist_focus_out (GtkWidget     *widget,
6605                      GdkEventFocus *event)
6606 {
6607   GtkCList *clist;
6608
6609   g_return_val_if_fail (widget != NULL, FALSE);
6610   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
6611   g_return_val_if_fail (event != NULL, FALSE);
6612
6613   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
6614   GTK_CLIST_SET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
6615
6616   gtk_widget_draw_focus (widget);
6617   
6618   clist = GTK_CLIST (widget);
6619
6620   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_EXTENDED)
6621     GTK_CLIST_CLASS_FW (widget)->resync_selection (clist, (GdkEvent *) event);
6622
6623   return FALSE;
6624 }
6625
6626 static void
6627 gtk_clist_set_focus_child (GtkContainer *container,
6628                            GtkWidget    *child)
6629 {
6630   g_return_if_fail (container != NULL);
6631   g_return_if_fail (GTK_IS_CLIST (container));
6632
6633   if (child)
6634     {
6635       g_return_if_fail (GTK_IS_WIDGET (child));
6636       GTK_CLIST_SET_FLAG (container, CLIST_CHILD_HAS_FOCUS);
6637     }
6638
6639   parent_class->set_focus_child (container, child);
6640 }
6641
6642 static gboolean
6643 title_focus (GtkCList *clist,
6644              gint      dir)
6645 {
6646   GtkWidget *focus_child;
6647   gboolean return_val = FALSE;
6648   gint last_column;
6649   gint d = 1;
6650   gint i = 0;
6651   gint j;
6652
6653   if (!GTK_CLIST_SHOW_TITLES(clist))
6654     return FALSE;
6655
6656   focus_child = GTK_CONTAINER (clist)->focus_child;
6657
6658   for (last_column = clist->columns - 1;
6659        last_column >= 0 && !clist->column[last_column].visible; last_column--)
6660     ;
6661   
6662   switch (dir)
6663     {
6664     case GTK_DIR_TAB_BACKWARD:
6665     case GTK_DIR_UP:
6666       if (!focus_child || !GTK_CLIST_CHILD_HAS_FOCUS(clist))
6667         {
6668           if (dir == GTK_DIR_UP)
6669             i = COLUMN_FROM_XPIXEL (clist, 0);
6670           else
6671             i = last_column;
6672           focus_child = clist->column[i].button;
6673           dir = GTK_DIR_TAB_FORWARD;
6674         }
6675       else
6676         d = -1;
6677       break;
6678     case GTK_DIR_LEFT:
6679       d = -1;
6680       if (!focus_child)
6681         {
6682           i = last_column;
6683           focus_child = clist->column[i].button;
6684         }
6685       break;
6686     case GTK_DIR_RIGHT:
6687       if (!focus_child)
6688         {
6689           i = 0;
6690           focus_child = clist->column[i].button;
6691         }
6692       break;
6693     }
6694
6695   if (focus_child)
6696     while (i < clist->columns)
6697       {
6698         if (clist->column[i].button == focus_child)
6699           {
6700             if (clist->column[i].button && 
6701                 GTK_WIDGET_VISIBLE (clist->column[i].button) &&
6702                 GTK_IS_CONTAINER (clist->column[i].button) &&
6703                 !GTK_WIDGET_HAS_FOCUS(clist->column[i].button))
6704               if (gtk_container_focus 
6705                   (GTK_CONTAINER (clist->column[i].button), dir))
6706                 {
6707                   return_val = TRUE;
6708                   i -= d;
6709                 }
6710             if (!return_val && dir == GTK_DIR_UP)
6711               return FALSE;
6712             i += d;
6713             break;
6714           }
6715         i++;
6716       }
6717
6718   j = i;
6719
6720   if (!return_val)
6721     while (j >= 0 && j < clist->columns)
6722       {
6723         if (clist->column[j].button &&
6724             GTK_WIDGET_VISIBLE (clist->column[j].button))
6725           {
6726             if (GTK_IS_CONTAINER (clist->column[j].button) &&
6727                 gtk_container_focus 
6728                 (GTK_CONTAINER (clist->column[j].button), dir))
6729               {
6730                 return_val = TRUE;
6731                 break;
6732               }
6733             else if (GTK_WIDGET_CAN_FOCUS (clist->column[j].button))
6734               {
6735                 gtk_widget_grab_focus (clist->column[j].button);
6736                 return_val = TRUE;
6737                 break;
6738               }
6739           }
6740         j += d;
6741       }
6742   
6743   if (return_val)
6744     {
6745       if (COLUMN_LEFT_XPIXEL (clist, j) < CELL_SPACING + COLUMN_INSET)
6746         gtk_clist_moveto (clist, -1, j, 0, 0);
6747       else if (COLUMN_LEFT_XPIXEL(clist, j) + clist->column[j].area.width >
6748                clist->clist_window_width)
6749         {
6750           if (j == last_column)
6751             gtk_clist_moveto (clist, -1, j, 0, 0);
6752           else
6753             gtk_clist_moveto (clist, -1, j, 0, 1);
6754         }
6755     }
6756   return return_val;
6757 }
6758
6759 /* PRIVATE SCROLLING FUNCTIONS
6760  *   move_focus_row
6761  *   scroll_horizontal
6762  *   scroll_vertical
6763  *   move_horizontal
6764  *   move_vertical
6765  *   horizontal_timeout
6766  *   vertical_timeout
6767  *   remove_grab
6768  */
6769 static void
6770 move_focus_row (GtkCList      *clist,
6771                 GtkScrollType  scroll_type,
6772                 gfloat         position)
6773 {
6774   GtkWidget *widget;
6775
6776   g_return_if_fail (clist != 0);
6777   g_return_if_fail (GTK_IS_CLIST (clist));
6778
6779   widget = GTK_WIDGET (clist);
6780
6781   switch (scroll_type)
6782     {
6783     case GTK_SCROLL_STEP_BACKWARD:
6784       if (clist->focus_row <= 0)
6785         return;
6786       gtk_clist_draw_focus (widget);
6787       clist->focus_row--;
6788       gtk_clist_draw_focus (widget);
6789       break;
6790     case GTK_SCROLL_STEP_FORWARD:
6791       if (clist->focus_row >= clist->rows - 1)
6792         return;
6793       gtk_clist_draw_focus (widget);
6794       clist->focus_row++;
6795       gtk_clist_draw_focus (widget);
6796       break;
6797     case GTK_SCROLL_PAGE_BACKWARD:
6798       if (clist->focus_row <= 0)
6799         return;
6800       gtk_clist_draw_focus (widget);
6801       clist->focus_row = MAX (0, clist->focus_row -
6802                               (2 * clist->clist_window_height -
6803                                clist->row_height - CELL_SPACING) / 
6804                               (2 * (clist->row_height + CELL_SPACING)));
6805       gtk_clist_draw_focus (widget);
6806       break;
6807     case GTK_SCROLL_PAGE_FORWARD:
6808       if (clist->focus_row >= clist->rows - 1)
6809         return;
6810       gtk_clist_draw_focus (widget);
6811       clist->focus_row = MIN (clist->rows - 1, clist->focus_row + 
6812                               (2 * clist->clist_window_height -
6813                                clist->row_height - CELL_SPACING) / 
6814                               (2 * (clist->row_height + CELL_SPACING)));
6815       gtk_clist_draw_focus (widget);
6816       break;
6817     case GTK_SCROLL_JUMP:
6818       if (position >= 0 && position <= 1)
6819         {
6820           gtk_clist_draw_focus (widget);
6821           clist->focus_row = position * (clist->rows - 1);
6822           gtk_clist_draw_focus (widget);
6823         }
6824       break;
6825     default:
6826       break;
6827     }
6828 }
6829
6830 static void
6831 scroll_horizontal (GtkCList      *clist,
6832                    GtkScrollType  scroll_type,
6833                    gfloat         position)
6834 {
6835   gint column = 0;
6836   gint last_column;
6837
6838   g_return_if_fail (clist != 0);
6839   g_return_if_fail (GTK_IS_CLIST (clist));
6840
6841   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
6842     return;
6843
6844   for (last_column = clist->columns - 1;
6845        last_column >= 0 && !clist->column[last_column].visible; last_column--)
6846     ;
6847
6848   switch (scroll_type)
6849     {
6850     case GTK_SCROLL_STEP_BACKWARD:
6851       column = COLUMN_FROM_XPIXEL (clist, 0);
6852       if (COLUMN_LEFT_XPIXEL (clist, column) - CELL_SPACING - COLUMN_INSET >= 0
6853           && column > 0)
6854         column--;
6855       break;
6856     case GTK_SCROLL_STEP_FORWARD:
6857       column = COLUMN_FROM_XPIXEL (clist, clist->clist_window_width);
6858       if (column < 0)
6859         return;
6860       if (COLUMN_LEFT_XPIXEL (clist, column) +
6861           clist->column[column].area.width +
6862           CELL_SPACING + COLUMN_INSET - 1 <= clist->clist_window_width &&
6863           column < last_column)
6864         column++;
6865       break;
6866     case GTK_SCROLL_PAGE_BACKWARD:
6867     case GTK_SCROLL_PAGE_FORWARD:
6868       return;
6869     case GTK_SCROLL_JUMP:
6870       if (position >= 0 && position <= 1)
6871         {
6872           gint vis_columns = 0;
6873           gint i;
6874
6875           for (i = 0; i <= last_column; i++)
6876             if (clist->column[i].visible)
6877               vis_columns++;
6878
6879           column = position * vis_columns;
6880
6881           for (i = 0; i <= last_column && column > 0; i++)
6882             if (clist->column[i].visible)
6883               column--;
6884
6885           column = i;
6886         }
6887       else
6888         return;
6889       break;
6890     default:
6891       break;
6892     }
6893
6894   if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET)
6895     gtk_clist_moveto (clist, -1, column, 0, 0);
6896   else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1
6897            + clist->column[column].area.width > clist->clist_window_width)
6898     {
6899       if (column == last_column)
6900         gtk_clist_moveto (clist, -1, column, 0, 0);
6901       else
6902         gtk_clist_moveto (clist, -1, column, 0, 1);
6903     }
6904 }
6905
6906 static void
6907 scroll_vertical (GtkCList      *clist,
6908                  GtkScrollType  scroll_type,
6909                  gfloat         position)
6910 {
6911   gint old_focus_row;
6912
6913   g_return_if_fail (clist != NULL);
6914   g_return_if_fail (GTK_IS_CLIST (clist));
6915
6916   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
6917     return;
6918
6919   switch (clist->selection_mode)
6920     {
6921     case GTK_SELECTION_EXTENDED:
6922       if (clist->anchor >= 0)
6923         return;
6924     case GTK_SELECTION_BROWSE:
6925
6926       old_focus_row = clist->focus_row;
6927       move_focus_row (clist, scroll_type, position);
6928
6929       if (old_focus_row != clist->focus_row)
6930         {
6931           if (clist->selection_mode == GTK_SELECTION_BROWSE)
6932             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
6933                              old_focus_row, -1, NULL);
6934           else if (!GTK_CLIST_ADD_MODE(clist))
6935             {
6936               gtk_clist_unselect_all (clist);
6937               clist->undo_anchor = old_focus_row;
6938             }
6939         }
6940
6941       switch (gtk_clist_row_is_visible (clist, clist->focus_row))
6942         {
6943         case GTK_VISIBILITY_NONE:
6944           if (old_focus_row != clist->focus_row &&
6945               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
6946                 GTK_CLIST_ADD_MODE(clist)))
6947             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6948                              clist->focus_row, -1, NULL);
6949           switch (scroll_type)
6950             {
6951             case GTK_SCROLL_STEP_BACKWARD:
6952             case GTK_SCROLL_PAGE_BACKWARD:
6953               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6954               break;
6955             case GTK_SCROLL_STEP_FORWARD:
6956             case GTK_SCROLL_PAGE_FORWARD:
6957               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6958               break;
6959             case GTK_SCROLL_JUMP:
6960               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
6961               break;
6962             default:
6963               break;
6964             }
6965           break;
6966         case GTK_VISIBILITY_PARTIAL:
6967           switch (scroll_type)
6968             {
6969             case GTK_SCROLL_STEP_BACKWARD:
6970             case GTK_SCROLL_PAGE_BACKWARD:
6971               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6972               break;
6973             case GTK_SCROLL_STEP_FORWARD:
6974             case GTK_SCROLL_PAGE_FORWARD:
6975               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6976               break;
6977             case GTK_SCROLL_JUMP:
6978               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
6979               break;
6980             default:
6981               break;
6982             }
6983         default:
6984           if (old_focus_row != clist->focus_row &&
6985               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
6986                 GTK_CLIST_ADD_MODE(clist)))
6987             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6988                              clist->focus_row, -1, NULL);
6989           break;
6990         }
6991       break;
6992     default:
6993       move_focus_row (clist, scroll_type, position);
6994
6995       if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
6996           clist->clist_window_height)
6997         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6998       else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
6999         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
7000       break;
7001     }
7002 }
7003
7004 static void
7005 move_horizontal (GtkCList *clist,
7006                  gint      diff)
7007 {
7008   gfloat value;
7009
7010   if (!clist->hadjustment)
7011     return;
7012
7013   value = CLAMP (clist->hadjustment->value + diff, 0.0,
7014                  clist->hadjustment->upper - clist->hadjustment->page_size);
7015   gtk_adjustment_set_value(clist->hadjustment, value);
7016 }
7017
7018 static void
7019 move_vertical (GtkCList *clist,
7020                gint      row,
7021                gfloat    align)
7022 {
7023   gfloat value;
7024
7025   if (!clist->vadjustment)
7026     return;
7027
7028   value = (ROW_TOP_YPIXEL (clist, row) - clist->voffset -
7029            align * (clist->clist_window_height - clist->row_height) +
7030            (2 * align - 1) * CELL_SPACING);
7031
7032   if (value + clist->vadjustment->page_size > clist->vadjustment->upper)
7033     value = clist->vadjustment->upper - clist->vadjustment->page_size;
7034
7035   gtk_adjustment_set_value(clist->vadjustment, value);
7036 }
7037
7038 static gint
7039 horizontal_timeout (GtkCList *clist)
7040 {
7041   gint x, y;
7042   GdkEventMotion event;
7043   GdkModifierType mask;
7044
7045   GDK_THREADS_ENTER ();
7046
7047   clist->htimer = 0;
7048   gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
7049
7050   event.is_hint = 0;
7051   event.x = x;
7052   event.y = y;
7053   event.state = mask;
7054
7055   gtk_clist_motion (GTK_WIDGET (clist), &event);
7056
7057   GDK_THREADS_LEAVE ();
7058   
7059   return FALSE;
7060 }
7061
7062 static gint
7063 vertical_timeout (GtkCList *clist)
7064 {
7065   gint x, y;
7066   GdkEventMotion event;
7067   GdkModifierType mask;
7068
7069   GDK_THREADS_ENTER ();
7070
7071   clist->vtimer = 0;
7072   gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
7073
7074   event.is_hint = 0;
7075   event.x = x;
7076   event.y = y;
7077   event.state = mask;
7078
7079   gtk_clist_motion (GTK_WIDGET (clist), &event);
7080
7081   GDK_THREADS_LEAVE ();
7082
7083   return FALSE;
7084 }
7085
7086 static void
7087 remove_grab (GtkCList *clist)
7088 {
7089   if (GTK_WIDGET_HAS_GRAB (clist))
7090     {
7091       gtk_grab_remove (GTK_WIDGET (clist));
7092       if (gdk_pointer_is_grabbed ())
7093         gdk_pointer_ungrab (GDK_CURRENT_TIME);
7094     }
7095
7096   if (clist->htimer)
7097     {
7098       gtk_timeout_remove (clist->htimer);
7099       clist->htimer = 0;
7100     }
7101
7102   if (clist->vtimer)
7103     {
7104       gtk_timeout_remove (clist->vtimer);
7105       clist->vtimer = 0;
7106     }
7107 }
7108
7109 /* PUBLIC SORTING FUNCTIONS
7110  * gtk_clist_sort
7111  * gtk_clist_set_compare_func
7112  * gtk_clist_set_auto_sort
7113  * gtk_clist_set_sort_type
7114  * gtk_clist_set_sort_column
7115  */
7116 void
7117 gtk_clist_sort (GtkCList *clist)
7118 {
7119   g_return_if_fail (clist != NULL);
7120   g_return_if_fail (GTK_IS_CLIST (clist));
7121
7122   GTK_CLIST_CLASS_FW (clist)->sort_list (clist);
7123 }
7124
7125 void
7126 gtk_clist_set_compare_func (GtkCList            *clist,
7127                             GtkCListCompareFunc  cmp_func)
7128 {
7129   g_return_if_fail (clist != NULL);
7130   g_return_if_fail (GTK_IS_CLIST (clist));
7131
7132   clist->compare = (cmp_func) ? cmp_func : default_compare;
7133 }
7134
7135 void       
7136 gtk_clist_set_auto_sort (GtkCList *clist,
7137                          gboolean  auto_sort)
7138 {
7139   g_return_if_fail (clist != NULL);
7140   g_return_if_fail (GTK_IS_CLIST (clist));
7141   
7142   if (GTK_CLIST_AUTO_SORT(clist) && !auto_sort)
7143     GTK_CLIST_UNSET_FLAG (clist, CLIST_AUTO_SORT);
7144   else if (!GTK_CLIST_AUTO_SORT(clist) && auto_sort)
7145     {
7146       GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_SORT);
7147       gtk_clist_sort (clist);
7148     }
7149 }
7150
7151 void       
7152 gtk_clist_set_sort_type (GtkCList    *clist,
7153                          GtkSortType  sort_type)
7154 {
7155   g_return_if_fail (clist != NULL);
7156   g_return_if_fail (GTK_IS_CLIST (clist));
7157   
7158   clist->sort_type = sort_type;
7159 }
7160
7161 void
7162 gtk_clist_set_sort_column (GtkCList *clist,
7163                            gint      column)
7164 {
7165   g_return_if_fail (clist != NULL);
7166   g_return_if_fail (GTK_IS_CLIST (clist));
7167
7168   if (column < 0 || column >= clist->columns)
7169     return;
7170
7171   clist->sort_column = column;
7172 }
7173
7174 /* PRIVATE SORTING FUNCTIONS
7175  *   default_compare
7176  *   real_sort_list
7177  *   gtk_clist_merge
7178  *   gtk_clist_mergesort
7179  */
7180 static gint
7181 default_compare (GtkCList      *clist,
7182                  gconstpointer  ptr1,
7183                  gconstpointer  ptr2)
7184 {
7185   char *text1 = NULL;
7186   char *text2 = NULL;
7187
7188   GtkCListRow *row1 = (GtkCListRow *) ptr1;
7189   GtkCListRow *row2 = (GtkCListRow *) ptr2;
7190
7191   switch (row1->cell[clist->sort_column].type)
7192     {
7193     case GTK_CELL_TEXT:
7194       text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
7195       break;
7196     case GTK_CELL_PIXTEXT:
7197       text1 = GTK_CELL_PIXTEXT (row1->cell[clist->sort_column])->text;
7198       break;
7199     default:
7200       break;
7201     }
7202  
7203   switch (row2->cell[clist->sort_column].type)
7204     {
7205     case GTK_CELL_TEXT:
7206       text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
7207       break;
7208     case GTK_CELL_PIXTEXT:
7209       text2 = GTK_CELL_PIXTEXT (row2->cell[clist->sort_column])->text;
7210       break;
7211     default:
7212       break;
7213     }
7214
7215   if (!text2)
7216     return (text1 != NULL);
7217
7218   if (!text1)
7219     return -1;
7220
7221   return strcmp (text1, text2);
7222 }
7223
7224 static void
7225 real_sort_list (GtkCList *clist)
7226 {
7227   GList *list;
7228   GList *work;
7229   gint i;
7230
7231   g_return_if_fail (clist != NULL);
7232   g_return_if_fail (GTK_IS_CLIST (clist));
7233
7234   if (clist->rows <= 1)
7235     return;
7236
7237   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
7238     return;
7239
7240   gtk_clist_freeze (clist);
7241
7242   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_EXTENDED)
7243     {
7244       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
7245       g_list_free (clist->undo_selection);
7246       g_list_free (clist->undo_unselection);
7247       clist->undo_selection = NULL;
7248       clist->undo_unselection = NULL;
7249     }
7250    
7251   clist->row_list = gtk_clist_mergesort (clist, clist->row_list, clist->rows);
7252
7253   work = clist->selection;
7254
7255   for (i = 0, list = clist->row_list; i < clist->rows; i++, list = list->next)
7256     {
7257       if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
7258         {
7259           work->data = GINT_TO_POINTER (i);
7260           work = work->next;
7261         }
7262       
7263       if (i == clist->rows - 1)
7264         clist->row_list_end = list;
7265     }
7266
7267   gtk_clist_thaw (clist);
7268 }
7269
7270 static GList *
7271 gtk_clist_merge (GtkCList *clist,
7272                  GList    *a,         /* first list to merge */
7273                  GList    *b)         /* second list to merge */
7274 {
7275   GList z = { 0 };                    /* auxiliary node */
7276   GList *c;
7277   gint cmp;
7278
7279   c = &z;
7280
7281   while (a || b)
7282     {
7283       if (a && !b)
7284         {
7285           c->next = a;
7286           a->prev = c;
7287           c = a;
7288           a = a->next;
7289           break;
7290         }
7291       else if (!a && b)
7292         {
7293           c->next = b;
7294           b->prev = c;
7295           c = b;
7296           b = b->next;
7297           break;
7298         }
7299       else /* a && b */
7300         {
7301           cmp = clist->compare (clist, GTK_CLIST_ROW (a), GTK_CLIST_ROW (b));
7302           if ((cmp >= 0 && clist->sort_type == GTK_SORT_DESCENDING) ||
7303               (cmp <= 0 && clist->sort_type == GTK_SORT_ASCENDING) ||
7304               (a && !b))
7305             {
7306               c->next = a;
7307               a->prev = c;
7308               c = a;
7309               a = a->next;
7310             }
7311           else
7312             {
7313               c->next = b;
7314               b->prev = c;
7315               c = b;
7316               b = b->next;
7317             }
7318         }
7319     }
7320
7321   return z.next;
7322 }
7323
7324 static GList *
7325 gtk_clist_mergesort (GtkCList *clist,
7326                      GList    *list,         /* the list to sort */
7327                      gint      num)          /* the list's length */
7328 {
7329   GList *half;
7330   gint i;
7331
7332   if (num == 1)
7333     {
7334       return list;
7335     }
7336   else
7337     {
7338       /* move "half" to the middle */
7339       half = list;
7340       for (i = 0; i < num / 2; i++)
7341         half = half->next;
7342
7343       /* cut the list in two */
7344       half->prev->next = NULL;
7345       half->prev = NULL;
7346
7347       /* recursively sort both lists */
7348       return gtk_clist_merge (clist,
7349                        gtk_clist_mergesort (clist, list, num / 2),
7350                        gtk_clist_mergesort (clist, half, num - num / 2));
7351     }
7352 }
7353
7354 /************************/
7355
7356 static void
7357 drag_source_info_destroy (gpointer data)
7358 {
7359   GtkCListCellInfo *info = data;
7360
7361   g_free (info);
7362 }
7363
7364 static void
7365 drag_dest_info_destroy (gpointer data)
7366 {
7367   GtkCListDestInfo *info = data;
7368
7369   g_free (info);
7370 }
7371
7372 static void
7373 gtk_clist_drag_begin (GtkWidget      *widget,
7374                       GdkDragContext *context)
7375 {
7376   GtkCList *clist;
7377   GtkCListCellInfo *info;
7378
7379   g_return_if_fail (widget != NULL);
7380   g_return_if_fail (GTK_IS_CLIST (widget));
7381   g_return_if_fail (context != NULL);
7382
7383   clist = GTK_CLIST (widget);
7384
7385   clist->drag_button = 0;
7386   remove_grab (clist);
7387
7388   switch (clist->selection_mode)
7389     {
7390     case GTK_SELECTION_EXTENDED:
7391       update_extended_selection (clist, clist->focus_row);
7392       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
7393       break;
7394     case GTK_SELECTION_SINGLE:
7395     case GTK_SELECTION_MULTIPLE:
7396       clist->anchor = -1;
7397     case GTK_SELECTION_BROWSE:
7398       break;
7399     }
7400
7401   info = g_dataset_get_data (context, "gtk-clist-drag-source");
7402
7403   if (!info)
7404     {
7405       info = g_new (GtkCListCellInfo, 1);
7406
7407       if (clist->click_cell.row < 0)
7408         clist->click_cell.row = 0;
7409       else if (clist->click_cell.row >= clist->rows)
7410         clist->click_cell.row = clist->rows - 1;
7411       info->row = clist->click_cell.row;
7412       info->column = clist->click_cell.column;
7413
7414       g_dataset_set_data_full (context, "gtk-clist-drag-source", info,
7415                                drag_source_info_destroy);
7416     }
7417
7418   if (GTK_CLIST_USE_DRAG_ICONS (clist))
7419     gtk_drag_set_icon_default (context);
7420 }
7421
7422 static void
7423 gtk_clist_drag_end (GtkWidget      *widget,
7424                     GdkDragContext *context)
7425 {
7426   GtkCList *clist;
7427
7428   g_return_if_fail (widget != NULL);
7429   g_return_if_fail (GTK_IS_CLIST (widget));
7430   g_return_if_fail (context != NULL);
7431
7432   clist = GTK_CLIST (widget);
7433
7434   clist->click_cell.row = -1;
7435   clist->click_cell.column = -1;
7436 }
7437
7438 static void
7439 gtk_clist_drag_leave (GtkWidget      *widget,
7440                       GdkDragContext *context,
7441                       guint           time)
7442 {
7443   GtkCList *clist;
7444   GtkCListDestInfo *dest_info;
7445
7446   g_return_if_fail (widget != NULL);
7447   g_return_if_fail (GTK_IS_CLIST (widget));
7448   g_return_if_fail (context != NULL);
7449
7450   clist = GTK_CLIST (widget);
7451
7452   dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7453   
7454   if (dest_info)
7455     {
7456       if (dest_info->cell.row >= 0 &&
7457           GTK_CLIST_REORDERABLE(clist) &&
7458           gtk_drag_get_source_widget (context) == widget)
7459         {
7460           GList *list;
7461           GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
7462
7463           list = context->targets;
7464           while (list)
7465             {
7466               if (atom == GPOINTER_TO_INT (list->data))
7467                 {
7468                   GTK_CLIST_CLASS_FW (clist)->draw_drag_highlight
7469                     (clist,
7470                      g_list_nth (clist->row_list, dest_info->cell.row)->data,
7471                      dest_info->cell.row, dest_info->insert_pos);
7472                   break;
7473                 }
7474               list = list->next;
7475             }
7476         }
7477       g_dataset_remove_data (context, "gtk-clist-drag-dest");
7478     }
7479 }
7480
7481 static gint
7482 gtk_clist_drag_motion (GtkWidget      *widget,
7483                        GdkDragContext *context,
7484                        gint            x,
7485                        gint            y,
7486                        guint           time)
7487 {
7488   GtkCList *clist;
7489   gint row, column;
7490   GtkCListDestInfo *dest_info;
7491   gint h = 0;
7492   gint insert_pos = GTK_CLIST_DRAG_NONE;
7493   gint y_delta;
7494
7495   g_return_val_if_fail (widget != NULL, FALSE);
7496   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
7497
7498   clist = GTK_CLIST (widget);
7499
7500   y -= (GTK_CONTAINER (widget)->border_width +
7501         widget->style->klass->ythickness + clist->column_title_area.height);
7502   row = ROW_FROM_YPIXEL (clist, y);
7503
7504   if (row >= clist->rows)
7505     {
7506       row = clist->rows - 1;
7507       y = ROW_TOP_YPIXEL (clist, row) + clist->row_height;
7508     }
7509   if (row < -1)
7510     row = -1;
7511
7512   x -= GTK_CONTAINER (widget)->border_width + widget->style->klass->xthickness;
7513   column = COLUMN_FROM_XPIXEL (clist, x);
7514
7515   if (row >= 0)
7516     {
7517       y_delta = y - ROW_TOP_YPIXEL (clist, row);
7518       
7519       if (GTK_CLIST_DRAW_DRAG_RECT(clist))
7520         {
7521           insert_pos = GTK_CLIST_DRAG_INTO;
7522           h = clist->row_height / 4;
7523         }
7524       else if (GTK_CLIST_DRAW_DRAG_LINE(clist))
7525         {
7526           insert_pos = GTK_CLIST_DRAG_BEFORE;
7527           h = clist->row_height / 2;
7528         }
7529
7530       if (GTK_CLIST_DRAW_DRAG_LINE(clist))
7531         {
7532           if (y_delta < h)
7533             insert_pos = GTK_CLIST_DRAG_BEFORE;
7534           else if (clist->row_height - y_delta < h)
7535             insert_pos = GTK_CLIST_DRAG_AFTER;
7536         }
7537     }
7538
7539   dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7540
7541   if (!dest_info)
7542     {
7543       dest_info = g_new (GtkCListDestInfo, 1);
7544
7545       dest_info->insert_pos  = GTK_CLIST_DRAG_NONE;
7546       dest_info->cell.row    = -1;
7547       dest_info->cell.column = -1;
7548
7549       g_dataset_set_data_full (context, "gtk-clist-drag-dest", dest_info,
7550                                drag_dest_info_destroy);
7551     }
7552
7553   if (GTK_CLIST_REORDERABLE (clist))
7554     {
7555       GList *list;
7556       GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
7557
7558       list = context->targets;
7559       while (list)
7560         {
7561           if (atom == GPOINTER_TO_INT (list->data))
7562             break;
7563           list = list->next;
7564         }
7565
7566       if (list)
7567         {
7568           if (gtk_drag_get_source_widget (context) != widget ||
7569               insert_pos == GTK_CLIST_DRAG_NONE ||
7570               row == clist->click_cell.row ||
7571               (row == clist->click_cell.row - 1 &&
7572                insert_pos == GTK_CLIST_DRAG_AFTER) ||
7573               (row == clist->click_cell.row + 1 &&
7574                insert_pos == GTK_CLIST_DRAG_BEFORE))
7575             {
7576               if (dest_info->cell.row < 0)
7577                 {
7578                   gdk_drag_status (context, GDK_ACTION_DEFAULT, time);
7579                   return FALSE;
7580                 }
7581               return TRUE;
7582             }
7583                 
7584           if (row != dest_info->cell.row ||
7585               (row == dest_info->cell.row &&
7586                dest_info->insert_pos != insert_pos))
7587             {
7588               if (dest_info->cell.row >= 0)
7589                 GTK_CLIST_CLASS_FW (clist)->draw_drag_highlight
7590                   (clist, g_list_nth (clist->row_list,
7591                                       dest_info->cell.row)->data,
7592                    dest_info->cell.row, dest_info->insert_pos);
7593
7594               dest_info->insert_pos  = insert_pos;
7595               dest_info->cell.row    = row;
7596               dest_info->cell.column = column;
7597               
7598               GTK_CLIST_CLASS_FW (clist)->draw_drag_highlight
7599                 (clist, g_list_nth (clist->row_list,
7600                                     dest_info->cell.row)->data,
7601                  dest_info->cell.row, dest_info->insert_pos);
7602
7603               gdk_drag_status (context, context->suggested_action, time);
7604             }
7605           return TRUE;
7606         }
7607     }
7608
7609   dest_info->insert_pos  = insert_pos;
7610   dest_info->cell.row    = row;
7611   dest_info->cell.column = column;
7612   return TRUE;
7613 }
7614
7615 static gboolean
7616 gtk_clist_drag_drop (GtkWidget      *widget,
7617                      GdkDragContext *context,
7618                      gint            x,
7619                      gint            y,
7620                      guint           time)
7621 {
7622   g_return_val_if_fail (widget != NULL, FALSE);
7623   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
7624   g_return_val_if_fail (context != NULL, FALSE);
7625
7626   if (GTK_CLIST_REORDERABLE (widget) &&
7627       gtk_drag_get_source_widget (context) == widget)
7628     {
7629       GList *list;
7630       GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
7631
7632       list = context->targets;
7633       while (list)
7634         {
7635           if (atom == GPOINTER_TO_INT (list->data))
7636             return TRUE;
7637           list = list->next;
7638         }
7639     }
7640   return FALSE;
7641 }
7642
7643 static void
7644 gtk_clist_drag_data_received (GtkWidget        *widget,
7645                               GdkDragContext   *context,
7646                               gint              x,
7647                               gint              y,
7648                               GtkSelectionData *selection_data,
7649                               guint             info,
7650                               guint             time)
7651 {
7652   GtkCList *clist;
7653
7654   g_return_if_fail (widget != NULL);
7655   g_return_if_fail (GTK_IS_CLIST (widget));
7656   g_return_if_fail (context != NULL);
7657   g_return_if_fail (selection_data != NULL);
7658
7659   clist = GTK_CLIST (widget);
7660
7661   if (GTK_CLIST_REORDERABLE (clist) &&
7662       gtk_drag_get_source_widget (context) == widget &&
7663       selection_data->target ==
7664       gdk_atom_intern ("gtk-clist-drag-reorder", FALSE) &&
7665       selection_data->format == GTK_TYPE_POINTER &&
7666       selection_data->length == sizeof (GtkCListCellInfo))
7667     {
7668       GtkCListCellInfo *source_info;
7669       GtkCListDestInfo *dest_info;
7670
7671       source_info = (GtkCListCellInfo *)(selection_data->data);
7672       dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7673
7674       if (dest_info && source_info)
7675         {
7676           if (dest_info->insert_pos == GTK_CLIST_DRAG_AFTER)
7677             dest_info->cell.row++;
7678           if (source_info->row < dest_info->cell.row)
7679             dest_info->cell.row--;
7680
7681           if (dest_info->cell.row != source_info->row)
7682             gtk_clist_row_move (GTK_CLIST (widget),
7683                                 source_info->row,
7684                                 dest_info->cell.row);
7685           g_dataset_remove_data (context, "gtk-clist-drag-dest");
7686         }
7687     }
7688 }
7689
7690 static void  
7691 gtk_clist_drag_data_get (GtkWidget        *widget,
7692                          GdkDragContext   *context,
7693                          GtkSelectionData *selection_data,
7694                          guint             info,
7695                          guint             time)
7696 {
7697   g_return_if_fail (widget != NULL);
7698   g_return_if_fail (GTK_IS_CLIST (widget));
7699   g_return_if_fail (context != NULL);
7700   g_return_if_fail (selection_data != NULL);
7701
7702   if (selection_data->target ==
7703       gdk_atom_intern ("gtk-clist-drag-reorder", FALSE))
7704     {
7705       GtkCListCellInfo *info;
7706
7707       info = g_dataset_get_data (context, "gtk-clist-drag-source");
7708
7709       if (info)
7710         {
7711           GtkCListCellInfo *ret_info;
7712
7713           ret_info = g_new (GtkCListCellInfo, 1);
7714           ret_info->row = info->row;
7715           ret_info->column = info->column;
7716
7717           gtk_selection_data_set (selection_data,
7718                                   selection_data->target,
7719                                   GTK_TYPE_POINTER,
7720                                   (guchar *) ret_info,
7721                                   sizeof (GtkCListCellInfo));
7722         }
7723       else
7724         gtk_selection_data_set (selection_data, selection_data->target,
7725                                 GTK_TYPE_POINTER, NULL, 0);
7726     }
7727 }
7728
7729 static void
7730 draw_drag_highlight (GtkCList        *clist,
7731                      GtkCListRow     *dest_row,
7732                      gint             dest_row_number,
7733                      GtkCListDragPos  drag_pos)
7734 {
7735   gint y;
7736
7737   y = ROW_TOP_YPIXEL (clist, dest_row_number) - 1;
7738
7739   switch (drag_pos)
7740     {
7741     case GTK_CLIST_DRAG_NONE:
7742       break;
7743     case GTK_CLIST_DRAG_AFTER:
7744       y += clist->row_height + 1;
7745     case GTK_CLIST_DRAG_BEFORE:
7746       gdk_draw_line (clist->clist_window, clist->xor_gc,
7747                      0, y, clist->clist_window_width, y);
7748       break;
7749     case GTK_CLIST_DRAG_INTO:
7750       gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
7751                           clist->clist_window_width - 1, clist->row_height);
7752       break;
7753     }
7754 }
7755
7756 void
7757 gtk_clist_set_reorderable (GtkCList *clist, 
7758                            gboolean  reorderable)
7759 {
7760   GtkWidget *widget;
7761
7762   g_return_if_fail (clist != NULL);
7763   g_return_if_fail (GTK_IS_CLIST (clist));
7764
7765   if ((GTK_CLIST_REORDERABLE(clist) != 0) == reorderable)
7766     return;
7767
7768   widget = GTK_WIDGET (clist);
7769
7770   if (reorderable)
7771     {
7772       GTK_CLIST_SET_FLAG (clist, CLIST_REORDERABLE);
7773       gtk_drag_dest_set (widget,
7774                          GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
7775                          &clist_target_table, 1, GDK_ACTION_MOVE);
7776     }
7777   else
7778     {
7779       GTK_CLIST_UNSET_FLAG (clist, CLIST_REORDERABLE);
7780       gtk_drag_dest_unset (GTK_WIDGET (clist));
7781     }
7782 }
7783
7784 void
7785 gtk_clist_set_use_drag_icons (GtkCList *clist,
7786                               gboolean  use_icons)
7787 {
7788   g_return_if_fail (clist != NULL);
7789   g_return_if_fail (GTK_IS_CLIST (clist));
7790
7791   if (use_icons != 0)
7792     GTK_CLIST_SET_FLAG (clist, CLIST_USE_DRAG_ICONS);
7793   else
7794     GTK_CLIST_UNSET_FLAG (clist, CLIST_USE_DRAG_ICONS);
7795 }
7796
7797 void
7798 gtk_clist_set_button_actions (GtkCList *clist,
7799                               guint     button,
7800                               guint8    button_actions)
7801 {
7802   g_return_if_fail (clist != NULL);
7803   g_return_if_fail (GTK_IS_CLIST (clist));
7804   
7805   if (button <= MAX_BUTTON)
7806     {
7807       if (gdk_pointer_is_grabbed () || GTK_WIDGET_HAS_GRAB (clist))
7808         {
7809           remove_grab (clist);
7810           clist->drag_button = 0;
7811         }
7812
7813       if (clist->anchor >= 0 &&
7814           clist->selection_mode == GTK_SELECTION_EXTENDED)
7815         GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
7816
7817       clist->button_actions[button] = button_actions;
7818     }
7819 }