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