]> Pileus Git - ~andy/gtk/blob - gtk/gtkclist.c
8bcc6b03b17d43e2f7fe7f77efe5afed1fd80f10
[~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
3451       if (!clist_row)
3452         return;
3453
3454       if (clist_row->state == GTK_STATE_SELECTED)
3455         {
3456           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3457                            row, column, event);
3458           return;
3459         }
3460     case GTK_SELECTION_BROWSE:
3461       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3462                        row, column, event);
3463       break;
3464     }
3465 }
3466
3467 static void
3468 fake_toggle_row (GtkCList *clist,
3469                  gint      row)
3470 {
3471   GList *work;
3472
3473   work = g_list_nth (clist->row_list, row);
3474
3475   if (!work || !GTK_CLIST_ROW (work)->selectable)
3476     return;
3477   
3478   if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL)
3479     clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
3480   else
3481     clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
3482   
3483   if (CLIST_UNFROZEN (clist) &&
3484       gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3485     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row,
3486                                           GTK_CLIST_ROW (work));
3487 }
3488
3489 static void
3490 toggle_focus_row (GtkCList *clist)
3491 {
3492   g_return_if_fail (clist != 0);
3493   g_return_if_fail (GTK_IS_CLIST (clist));
3494
3495   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
3496       clist->focus_row < 0 || clist->focus_row >= clist->rows)
3497     return;
3498
3499   switch (clist->selection_mode)
3500     {
3501     case  GTK_SELECTION_SINGLE:
3502     case  GTK_SELECTION_MULTIPLE:
3503       toggle_row (clist, clist->focus_row, 0, NULL);
3504       break;
3505     case GTK_SELECTION_EXTENDED:
3506       g_list_free (clist->undo_selection);
3507       g_list_free (clist->undo_unselection);
3508       clist->undo_selection = NULL;
3509       clist->undo_unselection = NULL;
3510
3511       clist->anchor = clist->focus_row;
3512       clist->drag_pos = clist->focus_row;
3513       clist->undo_anchor = clist->focus_row;
3514       
3515       if (GTK_CLIST_ADD_MODE (clist))
3516         fake_toggle_row (clist, clist->focus_row);
3517       else
3518         GTK_CLIST_CLASS_FW (clist)->fake_unselect_all (clist,clist->focus_row);
3519
3520       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
3521       break;
3522     default:
3523       break;
3524     }
3525 }
3526
3527 static void
3528 toggle_add_mode (GtkCList *clist)
3529 {
3530   g_return_if_fail (clist != 0);
3531   g_return_if_fail (GTK_IS_CLIST (clist));
3532   
3533   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
3534       clist->selection_mode != GTK_SELECTION_EXTENDED)
3535     return;
3536
3537   gtk_clist_draw_focus (GTK_WIDGET (clist));
3538   if (!GTK_CLIST_ADD_MODE (clist))
3539     {
3540       GTK_CLIST_SET_FLAG (clist, CLIST_ADD_MODE);
3541       gdk_gc_set_line_attributes (clist->xor_gc, 1,
3542                                   GDK_LINE_ON_OFF_DASH, 0, 0);
3543       gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
3544     }
3545   else
3546     {
3547       GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
3548       gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
3549       clist->anchor_state = GTK_STATE_SELECTED;
3550     }
3551   gtk_clist_draw_focus (GTK_WIDGET (clist));
3552 }
3553
3554 static void
3555 real_select_row (GtkCList *clist,
3556                  gint      row,
3557                  gint      column,
3558                  GdkEvent *event)
3559 {
3560   GtkCListRow *clist_row;
3561   GList *list;
3562   gint sel_row;
3563   gboolean row_selected;
3564
3565   g_return_if_fail (clist != NULL);
3566   g_return_if_fail (GTK_IS_CLIST (clist));
3567
3568   if (row < 0 || row > (clist->rows - 1))
3569     return;
3570
3571   switch (clist->selection_mode)
3572     {
3573     case GTK_SELECTION_SINGLE:
3574     case GTK_SELECTION_BROWSE:
3575
3576       row_selected = FALSE;
3577       list = clist->selection;
3578
3579       while (list)
3580         {
3581           sel_row = GPOINTER_TO_INT (list->data);
3582           list = list->next;
3583
3584           if (row == sel_row)
3585             row_selected = TRUE;
3586           else
3587             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3588                              sel_row, column, event);
3589         }
3590
3591       if (row_selected)
3592         return;
3593       
3594     default:
3595       break;
3596     }
3597
3598   clist_row = (g_list_nth (clist->row_list, row))->data;
3599
3600   if (clist_row->state != GTK_STATE_NORMAL || !clist_row->selectable)
3601     return;
3602
3603   clist_row->state = GTK_STATE_SELECTED;
3604   if (!clist->selection)
3605     {
3606       clist->selection = g_list_append (clist->selection,
3607                                         GINT_TO_POINTER (row));
3608       clist->selection_end = clist->selection;
3609     }
3610   else
3611     clist->selection_end = 
3612       g_list_append (clist->selection_end, GINT_TO_POINTER (row))->next;
3613   
3614   if (CLIST_UNFROZEN (clist)
3615       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3616     GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
3617 }
3618
3619 static void
3620 real_unselect_row (GtkCList *clist,
3621                    gint      row,
3622                    gint      column,
3623                    GdkEvent *event)
3624 {
3625   GtkCListRow *clist_row;
3626
3627   g_return_if_fail (clist != NULL);
3628   g_return_if_fail (GTK_IS_CLIST (clist));
3629
3630   if (row < 0 || row > (clist->rows - 1))
3631     return;
3632
3633   clist_row = (g_list_nth (clist->row_list, row))->data;
3634
3635   if (clist_row->state == GTK_STATE_SELECTED)
3636     {
3637       clist_row->state = GTK_STATE_NORMAL;
3638
3639       if (clist->selection_end && 
3640           clist->selection_end->data == GINT_TO_POINTER (row))
3641         clist->selection_end = clist->selection_end->prev;
3642
3643       clist->selection = g_list_remove (clist->selection,
3644                                         GINT_TO_POINTER (row));
3645       
3646       if (CLIST_UNFROZEN (clist)
3647           && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3648         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row, clist_row);
3649     }
3650 }
3651
3652 static void
3653 real_select_all (GtkCList *clist)
3654 {
3655   GList *list;
3656   gint i;
3657  
3658   g_return_if_fail (clist != NULL);
3659   g_return_if_fail (GTK_IS_CLIST (clist));
3660
3661   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
3662     return;
3663
3664   switch (clist->selection_mode)
3665     {
3666     case GTK_SELECTION_SINGLE:
3667     case GTK_SELECTION_BROWSE:
3668       return;
3669
3670     case GTK_SELECTION_EXTENDED:
3671       g_list_free (clist->undo_selection);
3672       g_list_free (clist->undo_unselection);
3673       clist->undo_selection = NULL;
3674       clist->undo_unselection = NULL;
3675           
3676       if (clist->rows &&
3677           ((GtkCListRow *) (clist->row_list->data))->state !=
3678           GTK_STATE_SELECTED)
3679         fake_toggle_row (clist, 0);
3680
3681       clist->anchor_state =  GTK_STATE_SELECTED;
3682       clist->anchor = 0;
3683       clist->drag_pos = 0;
3684       clist->undo_anchor = clist->focus_row;
3685       update_extended_selection (clist, clist->rows);
3686       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
3687       return;
3688
3689     case GTK_SELECTION_MULTIPLE:
3690       for (i = 0, list = clist->row_list; list; i++, list = list->next)
3691         {
3692           if (((GtkCListRow *)(list->data))->state == GTK_STATE_NORMAL)
3693             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3694                              i, -1, NULL);
3695         }
3696       return;
3697     }
3698 }
3699
3700 static void
3701 real_unselect_all (GtkCList *clist)
3702 {
3703   GList *list;
3704   gint i;
3705  
3706   g_return_if_fail (clist != NULL);
3707   g_return_if_fail (GTK_IS_CLIST (clist));
3708
3709   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
3710     return;
3711
3712   switch (clist->selection_mode)
3713     {
3714     case GTK_SELECTION_BROWSE:
3715       if (clist->focus_row >= 0)
3716         {
3717           gtk_signal_emit (GTK_OBJECT (clist),
3718                            clist_signals[SELECT_ROW],
3719                            clist->focus_row, -1, NULL);
3720           return;
3721         }
3722       break;
3723     case GTK_SELECTION_EXTENDED:
3724       g_list_free (clist->undo_selection);
3725       g_list_free (clist->undo_unselection);
3726       clist->undo_selection = NULL;
3727       clist->undo_unselection = NULL;
3728
3729       clist->anchor = -1;
3730       clist->drag_pos = -1;
3731       clist->undo_anchor = clist->focus_row;
3732       break;
3733     default:
3734       break;
3735     }
3736
3737   list = clist->selection;
3738   while (list)
3739     {
3740       i = GPOINTER_TO_INT (list->data);
3741       list = list->next;
3742       gtk_signal_emit (GTK_OBJECT (clist),
3743                        clist_signals[UNSELECT_ROW], i, -1, NULL);
3744     }
3745 }
3746
3747 static void
3748 fake_unselect_all (GtkCList *clist,
3749                    gint      row)
3750 {
3751   GList *list;
3752   GList *work;
3753   gint i;
3754
3755   if (row >= 0 && (work = g_list_nth (clist->row_list, row)))
3756     {
3757       if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL &&
3758           GTK_CLIST_ROW (work)->selectable)
3759         {
3760           GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
3761           
3762           if (CLIST_UNFROZEN (clist) &&
3763               gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3764             GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, row,
3765                                                   GTK_CLIST_ROW (work));
3766         }  
3767     }
3768
3769   clist->undo_selection = clist->selection;
3770   clist->selection = NULL;
3771   clist->selection_end = NULL;
3772   
3773   for (list = clist->undo_selection; list; list = list->next)
3774     {
3775       if ((i = GPOINTER_TO_INT (list->data)) == row ||
3776           !(work = g_list_nth (clist->row_list, i)))
3777         continue;
3778
3779       GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
3780       if (CLIST_UNFROZEN (clist) &&
3781           gtk_clist_row_is_visible (clist, i) != GTK_VISIBILITY_NONE)
3782         GTK_CLIST_CLASS_FW (clist)->draw_row (clist, NULL, i,
3783                                               GTK_CLIST_ROW (work));
3784     }
3785 }
3786
3787 static void
3788 real_undo_selection (GtkCList *clist)
3789 {
3790   GList *work;
3791
3792   g_return_if_fail (clist != NULL);
3793   g_return_if_fail (GTK_IS_CLIST (clist));
3794
3795   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
3796       clist->selection_mode != GTK_SELECTION_EXTENDED)
3797     return;
3798
3799   if (clist->anchor >= 0)
3800     GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
3801
3802   if (!(clist->undo_selection || clist->undo_unselection))
3803     {
3804       gtk_clist_unselect_all (clist);
3805       return;
3806     }
3807
3808   for (work = clist->undo_selection; work; work = work->next)
3809     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3810                      GPOINTER_TO_INT (work->data), -1, NULL);
3811
3812   for (work = clist->undo_unselection; work; work = work->next)
3813     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3814                      GPOINTER_TO_INT (work->data), -1, NULL);
3815
3816   if (GTK_WIDGET_HAS_FOCUS (clist) && clist->focus_row != clist->undo_anchor)
3817     {
3818       gtk_clist_draw_focus (GTK_WIDGET (clist));
3819       clist->focus_row = clist->undo_anchor;
3820       gtk_clist_draw_focus (GTK_WIDGET (clist));
3821     }
3822   else
3823     clist->focus_row = clist->undo_anchor;
3824   
3825   clist->undo_anchor = -1;
3826  
3827   g_list_free (clist->undo_selection);
3828   g_list_free (clist->undo_unselection);
3829   clist->undo_selection = NULL;
3830   clist->undo_unselection = NULL;
3831
3832   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
3833       clist->clist_window_height)
3834     gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
3835   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
3836     gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
3837 }
3838
3839 static void
3840 set_anchor (GtkCList *clist,
3841             gboolean  add_mode,
3842             gint      anchor,
3843             gint      undo_anchor)
3844 {
3845   g_return_if_fail (clist != NULL);
3846   g_return_if_fail (GTK_IS_CLIST (clist));
3847   
3848   if (clist->selection_mode != GTK_SELECTION_EXTENDED || clist->anchor >= 0)
3849     return;
3850
3851   g_list_free (clist->undo_selection);
3852   g_list_free (clist->undo_unselection);
3853   clist->undo_selection = NULL;
3854   clist->undo_unselection = NULL;
3855
3856   if (add_mode)
3857     fake_toggle_row (clist, anchor);
3858   else
3859     {
3860       GTK_CLIST_CLASS_FW (clist)->fake_unselect_all (clist, anchor);
3861       clist->anchor_state = GTK_STATE_SELECTED;
3862     }
3863
3864   clist->anchor = anchor;
3865   clist->drag_pos = anchor;
3866   clist->undo_anchor = undo_anchor;
3867 }
3868
3869 static void
3870 resync_selection (GtkCList *clist,
3871                   GdkEvent *event)
3872 {
3873   gint i;
3874   gint e;
3875   gint row;
3876   GList *list;
3877   GtkCListRow *clist_row;
3878
3879   if (clist->anchor < 0)
3880     return;
3881
3882   gtk_clist_freeze (clist);
3883
3884   i = MIN (clist->anchor, clist->drag_pos);
3885   e = MAX (clist->anchor, clist->drag_pos);
3886
3887   if (clist->undo_selection)
3888     {
3889       list = clist->selection;
3890       clist->selection = clist->undo_selection;
3891       clist->selection_end = g_list_last (clist->selection);
3892       clist->undo_selection = list;
3893       list = clist->selection;
3894       while (list)
3895         {
3896           row = GPOINTER_TO_INT (list->data);
3897           list = list->next;
3898           if (row < i || row > e)
3899             {
3900               clist_row = g_list_nth (clist->row_list, row)->data;
3901               if (clist_row->selectable)
3902                 {
3903                   clist_row->state = GTK_STATE_SELECTED;
3904                   gtk_signal_emit (GTK_OBJECT (clist),
3905                                    clist_signals[UNSELECT_ROW],
3906                                    row, -1, event);
3907                   clist->undo_selection = g_list_prepend
3908                     (clist->undo_selection, GINT_TO_POINTER (row));
3909                 }
3910             }
3911         }
3912     }    
3913
3914   for (list = g_list_nth (clist->row_list, i); i <= e; i++, list = list->next)
3915     if (GTK_CLIST_ROW (list)->selectable)
3916       {
3917         if (g_list_find (clist->selection, GINT_TO_POINTER(i)))
3918           {
3919             if (GTK_CLIST_ROW (list)->state == GTK_STATE_NORMAL)
3920               {
3921                 GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
3922                 gtk_signal_emit (GTK_OBJECT (clist),
3923                                  clist_signals[UNSELECT_ROW], i, -1, event);
3924                 clist->undo_selection = g_list_prepend (clist->undo_selection,
3925                                                         GINT_TO_POINTER (i));
3926               }
3927           }
3928         else if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
3929           {
3930             GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
3931             clist->undo_unselection = g_list_prepend (clist->undo_unselection,
3932                                                       GINT_TO_POINTER (i));
3933           }
3934       }
3935
3936   for (list = clist->undo_unselection; list; list = list->next)
3937     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3938                      GPOINTER_TO_INT (list->data), -1, event);
3939
3940   clist->anchor = -1;
3941   clist->drag_pos = -1;
3942
3943   gtk_clist_thaw (clist);
3944 }
3945
3946 static void
3947 update_extended_selection (GtkCList *clist,
3948                            gint      row)
3949 {
3950   gint i;
3951   GList *list;
3952   GdkRectangle area;
3953   gint s1 = -1;
3954   gint s2 = -1;
3955   gint e1 = -1;
3956   gint e2 = -1;
3957   gint y1 = clist->clist_window_height;
3958   gint y2 = clist->clist_window_height;
3959   gint h1 = 0;
3960   gint h2 = 0;
3961   gint top;
3962
3963   if (clist->selection_mode != GTK_SELECTION_EXTENDED || clist->anchor == -1)
3964     return;
3965
3966   if (row < 0)
3967     row = 0;
3968   if (row >= clist->rows)
3969     row = clist->rows - 1;
3970
3971   /* extending downwards */
3972   if (row > clist->drag_pos && clist->anchor <= clist->drag_pos)
3973     {
3974       s2 = clist->drag_pos + 1;
3975       e2 = row;
3976     }
3977   /* extending upwards */
3978   else if (row < clist->drag_pos && clist->anchor >= clist->drag_pos)
3979     {
3980       s2 = row;
3981       e2 = clist->drag_pos - 1;
3982     }
3983   else if (row < clist->drag_pos && clist->anchor < clist->drag_pos)
3984     {
3985       e1 = clist->drag_pos;
3986       /* row and drag_pos on different sides of anchor :
3987          take back the selection between anchor and drag_pos,
3988          select between anchor and row */
3989       if (row < clist->anchor)
3990         {
3991           s1 = clist->anchor + 1;
3992           s2 = row;
3993           e2 = clist->anchor - 1;
3994         }
3995       /* take back the selection between anchor and drag_pos */
3996       else
3997         s1 = row + 1;
3998     }
3999   else if (row > clist->drag_pos && clist->anchor > clist->drag_pos)
4000     {
4001       s1 = clist->drag_pos;
4002       /* row and drag_pos on different sides of anchor :
4003          take back the selection between anchor and drag_pos,
4004          select between anchor and row */
4005       if (row > clist->anchor)
4006         {
4007           e1 = clist->anchor - 1;
4008           s2 = clist->anchor + 1;
4009           e2 = row;
4010         }
4011       /* take back the selection between anchor and drag_pos */
4012       else
4013         e1 = row - 1;
4014     }
4015
4016   clist->drag_pos = row;
4017
4018   area.x = 0;
4019   area.width = clist->clist_window_width;
4020
4021   /* restore the elements between s1 and e1 */
4022   if (s1 >= 0)
4023     {
4024       for (i = s1, list = g_list_nth (clist->row_list, i); i <= e1;
4025            i++, list = list->next)
4026         if (GTK_CLIST_ROW (list)->selectable)
4027           {
4028             if (GTK_CLIST_CLASS_FW (clist)->selection_find (clist, i, list))
4029               GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
4030             else
4031               GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
4032           }
4033
4034       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4035
4036       if (top + clist->row_height <= 0)
4037         {
4038           area.y = 0;
4039           area.height = ROW_TOP_YPIXEL (clist, e1) + clist->row_height;
4040           draw_rows (clist, &area);
4041           gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4042         }
4043       else if (top >= clist->clist_window_height)
4044         {
4045           area.y = ROW_TOP_YPIXEL (clist, s1) - 1;
4046           area.height = clist->clist_window_height - area.y;
4047           draw_rows (clist, &area);
4048           gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4049         }
4050       else if (top < 0)
4051         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4052       else if (top + clist->row_height > clist->clist_window_height)
4053         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4054
4055       y1 = ROW_TOP_YPIXEL (clist, s1) - 1;
4056       h1 = (e1 - s1 + 1) * (clist->row_height + CELL_SPACING);
4057     }
4058
4059   /* extend the selection between s2 and e2 */
4060   if (s2 >= 0)
4061     {
4062       for (i = s2, list = g_list_nth (clist->row_list, i); i <= e2;
4063            i++, list = list->next)
4064         if (GTK_CLIST_ROW (list)->selectable &&
4065             GTK_CLIST_ROW (list)->state != clist->anchor_state)
4066           GTK_CLIST_ROW (list)->state = clist->anchor_state;
4067
4068       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4069
4070       if (top + clist->row_height <= 0)
4071         {
4072           area.y = 0;
4073           area.height = ROW_TOP_YPIXEL (clist, e2) + clist->row_height;
4074           draw_rows (clist, &area);
4075           gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4076         }
4077       else if (top >= clist->clist_window_height)
4078         {
4079           area.y = ROW_TOP_YPIXEL (clist, s2) - 1;
4080           area.height = clist->clist_window_height - area.y;
4081           draw_rows (clist, &area);
4082           gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4083         }
4084       else if (top < 0)
4085         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4086       else if (top + clist->row_height > clist->clist_window_height)
4087         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4088
4089       y2 = ROW_TOP_YPIXEL (clist, s2) - 1;
4090       h2 = (e2 - s2 + 1) * (clist->row_height + CELL_SPACING);
4091     }
4092
4093   area.y = MAX (0, MIN (y1, y2));
4094   if (area.y > clist->clist_window_height)
4095     area.y = 0;
4096   area.height = MIN (clist->clist_window_height, h1 + h2);
4097   if (s1 >= 0 && s2 >= 0)
4098     area.height += (clist->row_height + CELL_SPACING);
4099   draw_rows (clist, &area);
4100 }
4101
4102 static void
4103 start_selection (GtkCList *clist)
4104 {
4105   g_return_if_fail (clist != NULL);
4106   g_return_if_fail (GTK_IS_CLIST (clist));
4107
4108   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
4109     return;
4110
4111   set_anchor (clist, GTK_CLIST_ADD_MODE (clist), clist->focus_row,
4112               clist->focus_row);
4113 }
4114
4115 static void
4116 end_selection (GtkCList *clist)
4117 {
4118   g_return_if_fail (clist != NULL);
4119   g_return_if_fail (GTK_IS_CLIST (clist));
4120
4121   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_FOCUS (clist)) ||
4122       clist->anchor == -1)
4123     return;
4124   
4125   GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
4126 }
4127
4128 static void
4129 extend_selection (GtkCList      *clist,
4130                   GtkScrollType  scroll_type,
4131                   gfloat         position,
4132                   gboolean       auto_start_selection)
4133 {
4134   g_return_if_fail (clist != NULL);
4135   g_return_if_fail (GTK_IS_CLIST (clist));
4136
4137   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
4138       clist->selection_mode != GTK_SELECTION_EXTENDED)
4139     return;
4140
4141   if (auto_start_selection)
4142     set_anchor (clist, GTK_CLIST_ADD_MODE (clist), clist->focus_row,
4143                 clist->focus_row);
4144   else if (clist->anchor == -1)
4145     return;
4146
4147   move_focus_row (clist, scroll_type, position);
4148
4149   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
4150       clist->clist_window_height)
4151     gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4152   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
4153     gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4154
4155   update_extended_selection (clist, clist->focus_row);
4156 }
4157
4158 static void
4159 sync_selection (GtkCList *clist,
4160                 gint      row,
4161                 gint      mode)
4162 {
4163   GList *list;
4164   gint d;
4165
4166   if (mode == SYNC_INSERT)
4167     d = 1;
4168   else
4169     d = -1;
4170       
4171   if (clist->focus_row >= row)
4172     {
4173       if (d > 0 || clist->focus_row > row)
4174         clist->focus_row += d;
4175       if (clist->focus_row == -1 && clist->rows >= 1)
4176         clist->focus_row = 0;
4177       else if (clist->focus_row >= clist->rows)
4178         clist->focus_row = clist->rows - 1;
4179     }
4180
4181   if (clist->selection_mode == GTK_SELECTION_BROWSE && clist->anchor != -1)
4182     GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
4183
4184   g_list_free (clist->undo_selection);
4185   g_list_free (clist->undo_unselection);
4186   clist->undo_selection = NULL;
4187   clist->undo_unselection = NULL;
4188
4189   clist->anchor = -1;
4190   clist->drag_pos = -1;
4191   clist->undo_anchor = clist->focus_row;
4192
4193   list = clist->selection;
4194
4195   while (list)
4196     {
4197       if (GPOINTER_TO_INT (list->data) >= row)
4198         list->data = ((gchar*) list->data) + d;
4199       list = list->next;
4200     }
4201 }
4202
4203 /* GTKOBJECT
4204  *   gtk_clist_destroy
4205  *   gtk_clist_finalize
4206  */
4207 static void
4208 gtk_clist_destroy (GtkObject *object)
4209 {
4210   gint i;
4211   GtkCList *clist;
4212
4213   g_return_if_fail (object != NULL);
4214   g_return_if_fail (GTK_IS_CLIST (object));
4215
4216   clist = GTK_CLIST (object);
4217
4218   /* freeze the list */
4219   clist->freeze_count++;
4220
4221   /* get rid of all the rows */
4222   gtk_clist_clear (clist);
4223
4224   /* Since we don't have a _remove method, unparent the children
4225    * instead of destroying them so the focus will be unset properly.
4226    * (For other containers, the _remove method takes care of the
4227    * unparent) The destroy will happen when the refcount drops
4228    * to zero.
4229    */
4230
4231   /* unref adjustments */
4232   if (clist->hadjustment)
4233     {
4234       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->hadjustment), clist);
4235       gtk_object_unref (GTK_OBJECT (clist->hadjustment));
4236       clist->hadjustment = NULL;
4237     }
4238   if (clist->vadjustment)
4239     {
4240       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->vadjustment), clist);
4241       gtk_object_unref (GTK_OBJECT (clist->vadjustment));
4242       clist->vadjustment = NULL;
4243     }
4244
4245   if (clist->htimer)
4246     {
4247       gtk_timeout_remove (clist->htimer);
4248       clist->htimer = 0;
4249     }
4250   if (clist->vtimer)
4251     {
4252       gtk_timeout_remove (clist->vtimer);
4253       clist->vtimer = 0;
4254     }
4255
4256   /* destroy the column buttons */
4257   for (i = 0; i < clist->columns; i++)
4258     if (clist->column[i].button)
4259       {
4260         gtk_widget_unparent (clist->column[i].button);
4261         clist->column[i].button = NULL;
4262       }
4263
4264   if (GTK_OBJECT_CLASS (parent_class)->destroy)
4265     (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
4266 }
4267
4268 static void
4269 gtk_clist_finalize (GtkObject *object)
4270 {
4271   GtkCList *clist;
4272
4273   g_return_if_fail (object != NULL);
4274   g_return_if_fail (GTK_IS_CLIST (object));
4275
4276   clist = GTK_CLIST (object);
4277
4278   columns_delete (clist);
4279
4280   g_mem_chunk_destroy (clist->cell_mem_chunk);
4281   g_mem_chunk_destroy (clist->row_mem_chunk);
4282
4283   if (GTK_OBJECT_CLASS (parent_class)->finalize)
4284     (*GTK_OBJECT_CLASS (parent_class)->finalize) (object);
4285 }
4286
4287 /* GTKWIDGET
4288  *   gtk_clist_realize
4289  *   gtk_clist_unrealize
4290  *   gtk_clist_map
4291  *   gtk_clist_unmap
4292  *   gtk_clist_draw
4293  *   gtk_clist_expose
4294  *   gtk_clist_style_set
4295  *   gtk_clist_key_press
4296  *   gtk_clist_button_press
4297  *   gtk_clist_button_release
4298  *   gtk_clist_motion
4299  *   gtk_clist_size_request
4300  *   gtk_clist_size_allocate
4301  */
4302 static void
4303 gtk_clist_realize (GtkWidget *widget)
4304 {
4305   GtkCList *clist;
4306   GdkWindowAttr attributes;
4307   GdkGCValues values;
4308   GtkCListRow *clist_row;
4309   GList *list;
4310   gint attributes_mask;
4311   gint border_width;
4312   gint i;
4313   gint j;
4314
4315   g_return_if_fail (widget != NULL);
4316   g_return_if_fail (GTK_IS_CLIST (widget));
4317
4318   clist = GTK_CLIST (widget);
4319
4320   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
4321
4322   border_width = GTK_CONTAINER (widget)->border_width;
4323   
4324   attributes.window_type = GDK_WINDOW_CHILD;
4325   attributes.x = widget->allocation.x + border_width;
4326   attributes.y = widget->allocation.y + border_width;
4327   attributes.width = widget->allocation.width - border_width * 2;
4328   attributes.height = widget->allocation.height - border_width * 2;
4329   attributes.wclass = GDK_INPUT_OUTPUT;
4330   attributes.visual = gtk_widget_get_visual (widget);
4331   attributes.colormap = gtk_widget_get_colormap (widget);
4332   attributes.event_mask = gtk_widget_get_events (widget);
4333   attributes.event_mask |= (GDK_EXPOSURE_MASK |
4334                             GDK_BUTTON_PRESS_MASK |
4335                             GDK_BUTTON_RELEASE_MASK |
4336                             GDK_KEY_PRESS_MASK |
4337                             GDK_KEY_RELEASE_MASK);
4338   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
4339
4340   /* main window */
4341   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
4342                                    &attributes, attributes_mask);
4343   gdk_window_set_user_data (widget->window, clist);
4344
4345   widget->style = gtk_style_attach (widget->style, widget->window);
4346
4347   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
4348
4349   /* column-title window */
4350
4351   attributes.x = clist->column_title_area.x;
4352   attributes.y = clist->column_title_area.y;
4353   attributes.width = clist->column_title_area.width;
4354   attributes.height = clist->column_title_area.height;
4355   
4356   clist->title_window = gdk_window_new (widget->window, &attributes,
4357                                         attributes_mask);
4358   gdk_window_set_user_data (clist->title_window, clist);
4359
4360   gtk_style_set_background (widget->style, clist->title_window,
4361                             GTK_STATE_NORMAL);
4362   gdk_window_show (clist->title_window);
4363
4364   /* set things up so column buttons are drawn in title window */
4365   for (i = 0; i < clist->columns; i++)
4366     if (clist->column[i].button)
4367       gtk_widget_set_parent_window (clist->column[i].button,
4368                                     clist->title_window);
4369
4370   /* clist-window */
4371   attributes.x = (clist->internal_allocation.x +
4372                   widget->style->klass->xthickness);
4373   attributes.y = (clist->internal_allocation.y +
4374                   widget->style->klass->ythickness +
4375                   clist->column_title_area.height);
4376   attributes.width = clist->clist_window_width;
4377   attributes.height = clist->clist_window_height;
4378   
4379   clist->clist_window = gdk_window_new (widget->window, &attributes,
4380                                         attributes_mask);
4381   gdk_window_set_user_data (clist->clist_window, clist);
4382
4383   gdk_window_set_background (clist->clist_window,
4384                              &widget->style->bg[GTK_STATE_PRELIGHT]);
4385   gdk_window_show (clist->clist_window);
4386   gdk_window_get_size (clist->clist_window, &clist->clist_window_width,
4387                        &clist->clist_window_height);
4388
4389   /* create resize windows */
4390   attributes.wclass = GDK_INPUT_ONLY;
4391   attributes.event_mask = (GDK_BUTTON_PRESS_MASK |
4392                            GDK_BUTTON_RELEASE_MASK |
4393                            GDK_POINTER_MOTION_MASK |
4394                            GDK_POINTER_MOTION_HINT_MASK |
4395                            GDK_KEY_PRESS_MASK);
4396   attributes_mask = GDK_WA_CURSOR;
4397   attributes.cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
4398   clist->cursor_drag = attributes.cursor;
4399
4400   attributes.x =  LIST_WIDTH (clist) + 1;
4401   attributes.y = 0;
4402   attributes.width = 0;
4403   attributes.height = 0;
4404
4405   for (i = 0; i < clist->columns; i++)
4406     {
4407       clist->column[i].window = gdk_window_new (clist->title_window,
4408                                                 &attributes, attributes_mask);
4409       gdk_window_set_user_data (clist->column[i].window, clist);
4410     }
4411
4412   /* This is slightly less efficient than creating them with the
4413    * right size to begin with, but easier
4414    */
4415   size_allocate_title_buttons (clist);
4416
4417   /* GCs */
4418   clist->fg_gc = gdk_gc_new (widget->window);
4419   clist->bg_gc = gdk_gc_new (widget->window);
4420   
4421   /* We'll use this gc to do scrolling as well */
4422   gdk_gc_set_exposures (clist->fg_gc, TRUE);
4423
4424   values.foreground = widget->style->white;
4425   values.function = GDK_XOR;
4426   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
4427   clist->xor_gc = gdk_gc_new_with_values (widget->window,
4428                                           &values,
4429                                           GDK_GC_FOREGROUND |
4430                                           GDK_GC_FUNCTION |
4431                                           GDK_GC_SUBWINDOW);
4432
4433   /* attach optional row/cell styles, allocate foreground/background colors */
4434   list = clist->row_list;
4435   for (i = 0; i < clist->rows; i++)
4436     {
4437       clist_row = list->data;
4438       list = list->next;
4439
4440       if (clist_row->style)
4441         clist_row->style = gtk_style_attach (clist_row->style,
4442                                              clist->clist_window);
4443
4444       if (clist_row->fg_set || clist_row->bg_set)
4445         {
4446           GdkColormap *colormap;
4447
4448           colormap = gtk_widget_get_colormap (widget);
4449           if (clist_row->fg_set)
4450             gdk_color_alloc (colormap, &clist_row->foreground);
4451           if (clist_row->bg_set)
4452             gdk_color_alloc (colormap, &clist_row->background);
4453         }
4454       
4455       for (j = 0; j < clist->columns; j++)
4456         if  (clist_row->cell[j].style)
4457           clist_row->cell[j].style =
4458             gtk_style_attach (clist_row->cell[j].style, clist->clist_window);
4459     }
4460 }
4461
4462 static void
4463 gtk_clist_unrealize (GtkWidget *widget)
4464 {
4465   gint i;
4466   GtkCList *clist;
4467
4468   g_return_if_fail (widget != NULL);
4469   g_return_if_fail (GTK_IS_CLIST (widget));
4470
4471   clist = GTK_CLIST (widget);
4472
4473   /* freeze the list */
4474   clist->freeze_count++;
4475
4476   /* detach optional row/cell styles */
4477
4478   if (GTK_WIDGET_REALIZED (widget))
4479     {
4480       GtkCListRow *clist_row;
4481       GList *list;
4482       gint j;
4483
4484       list = clist->row_list;
4485       for (i = 0; i < clist->rows; i++)
4486         {
4487           clist_row = list->data;
4488           list = list->next;
4489
4490           if (clist_row->style)
4491             gtk_style_detach (clist_row->style);
4492           for (j = 0; j < clist->columns; j++)
4493             if  (clist_row->cell[j].style)
4494               gtk_style_detach (clist_row->cell[j].style);
4495         }
4496     }
4497
4498   gdk_cursor_destroy (clist->cursor_drag);
4499   gdk_gc_destroy (clist->xor_gc);
4500   gdk_gc_destroy (clist->fg_gc);
4501   gdk_gc_destroy (clist->bg_gc);
4502
4503   for (i = 0; i < clist->columns; i++)
4504     if (clist->column[i].window)
4505       {
4506         gdk_window_set_user_data (clist->column[i].window, NULL);
4507         gdk_window_destroy (clist->column[i].window);
4508         clist->column[i].window = NULL;
4509       }
4510
4511   gdk_window_set_user_data (clist->clist_window, NULL);
4512   gdk_window_destroy (clist->clist_window);
4513   clist->clist_window = NULL;
4514
4515   gdk_window_set_user_data (clist->title_window, NULL);
4516   gdk_window_destroy (clist->title_window);
4517   clist->title_window = NULL;
4518
4519   clist->cursor_drag = NULL;
4520   clist->xor_gc = NULL;
4521   clist->fg_gc = NULL;
4522   clist->bg_gc = NULL;
4523
4524   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
4525     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
4526 }
4527
4528 static void
4529 gtk_clist_map (GtkWidget *widget)
4530 {
4531   gint i;
4532   GtkCList *clist;
4533
4534   g_return_if_fail (widget != NULL);
4535   g_return_if_fail (GTK_IS_CLIST (widget));
4536
4537   clist = GTK_CLIST (widget);
4538
4539   if (!GTK_WIDGET_MAPPED (widget))
4540     {
4541       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
4542
4543       gdk_window_show (widget->window);
4544       gdk_window_show (clist->title_window);
4545       gdk_window_show (clist->clist_window);
4546
4547       /* map column buttons */
4548       for (i = 0; i < clist->columns; i++)
4549         if (clist->column[i].button &&
4550             GTK_WIDGET_VISIBLE (clist->column[i].button) &&
4551             !GTK_WIDGET_MAPPED (clist->column[i].button))
4552           gtk_widget_map (clist->column[i].button);
4553       
4554       /* map resize windows AFTER column buttons (above) */
4555       for (i = 0; i < clist->columns; i++)
4556         if (clist->column[i].window && clist->column[i].button)
4557           gdk_window_show (clist->column[i].window);
4558        
4559       /* unfreeze the list */
4560       clist->freeze_count = 0;
4561     }
4562 }
4563
4564 static void
4565 gtk_clist_unmap (GtkWidget *widget)
4566 {
4567   gint i;
4568   GtkCList *clist;
4569
4570   g_return_if_fail (widget != NULL);
4571   g_return_if_fail (GTK_IS_CLIST (widget));
4572
4573   clist = GTK_CLIST (widget);
4574
4575   if (GTK_WIDGET_MAPPED (widget))
4576     {
4577       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
4578
4579       for (i = 0; i < clist->columns; i++)
4580         if (clist->column[i].window)
4581           gdk_window_hide (clist->column[i].window);
4582
4583       gdk_window_hide (clist->clist_window);
4584       gdk_window_hide (clist->title_window);
4585       gdk_window_hide (widget->window);
4586
4587       /* unmap column buttons */
4588       for (i = 0; i < clist->columns; i++)
4589         if (clist->column[i].button &&
4590             GTK_WIDGET_MAPPED (clist->column[i].button))
4591           gtk_widget_unmap (clist->column[i].button);
4592
4593       /* freeze the list */
4594       clist->freeze_count++;
4595     }
4596 }
4597
4598 static void
4599 gtk_clist_draw (GtkWidget    *widget,
4600                 GdkRectangle *area)
4601 {
4602   GtkCList *clist;
4603   gint border_width;
4604   GdkRectangle child_area;
4605   int i;
4606
4607   g_return_if_fail (widget != NULL);
4608   g_return_if_fail (GTK_IS_CLIST (widget));
4609   g_return_if_fail (area != NULL);
4610
4611   if (GTK_WIDGET_DRAWABLE (widget))
4612     {
4613       clist = GTK_CLIST (widget);
4614       border_width = GTK_CONTAINER (widget)->border_width;
4615
4616       gdk_window_clear_area (widget->window,
4617                              area->x - border_width, 
4618                              area->y - border_width,
4619                              area->width, area->height);
4620
4621       /* draw list shadow/border */
4622       gtk_draw_shadow (widget->style, widget->window,
4623                        GTK_STATE_NORMAL, clist->shadow_type,
4624                        0, 0, 
4625                        clist->clist_window_width +
4626                        (2 * widget->style->klass->xthickness),
4627                        clist->clist_window_height +
4628                        (2 * widget->style->klass->ythickness) +
4629                        clist->column_title_area.height);
4630
4631       gdk_window_clear_area (clist->clist_window, 0, 0, -1, -1);
4632       draw_rows (clist, NULL);
4633
4634       for (i = 0; i < clist->columns; i++)
4635         {
4636           if (!clist->column[i].visible)
4637             continue;
4638           if (clist->column[i].button &&
4639               gtk_widget_intersect(clist->column[i].button, area, &child_area))
4640             gtk_widget_draw (clist->column[i].button, &child_area);
4641         }
4642     }
4643 }
4644
4645 static gint
4646 gtk_clist_expose (GtkWidget      *widget,
4647                   GdkEventExpose *event)
4648 {
4649   GtkCList *clist;
4650
4651   g_return_val_if_fail (widget != NULL, FALSE);
4652   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4653   g_return_val_if_fail (event != NULL, FALSE);
4654
4655   if (GTK_WIDGET_DRAWABLE (widget))
4656     {
4657       clist = GTK_CLIST (widget);
4658
4659       /* draw border */
4660       if (event->window == widget->window)
4661         gtk_draw_shadow (widget->style, widget->window,
4662                          GTK_STATE_NORMAL, clist->shadow_type,
4663                          0, 0,
4664                          clist->clist_window_width +
4665                          (2 * widget->style->klass->xthickness),
4666                          clist->clist_window_height +
4667                          (2 * widget->style->klass->ythickness) +
4668                          clist->column_title_area.height);
4669
4670       /* exposure events on the list */
4671       if (event->window == clist->clist_window)
4672         draw_rows (clist, &event->area);
4673     }
4674
4675   return FALSE;
4676 }
4677
4678 static void
4679 gtk_clist_style_set (GtkWidget *widget,
4680                      GtkStyle  *previous_style)
4681 {
4682   GtkCList *clist;
4683
4684   g_return_if_fail (widget != NULL);
4685   g_return_if_fail (GTK_IS_CLIST (widget));
4686
4687   if (GTK_WIDGET_CLASS (parent_class)->style_set)
4688     (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
4689
4690   clist = GTK_CLIST (widget);
4691
4692   /* Fill in data after widget has correct style */
4693
4694   /* text properties */
4695   if (!GTK_CLIST_ROW_HEIGHT_SET (clist))
4696     {
4697       clist->row_height = (widget->style->font->ascent +
4698                            widget->style->font->descent + 1);
4699       clist->row_center_offset = widget->style->font->ascent + 1.5;
4700     }
4701   else
4702     clist->row_center_offset = 1.5 + (clist->row_height +
4703                                       widget->style->font->ascent -
4704                                       widget->style->font->descent - 1) / 2;
4705
4706   /* Column widths */
4707   if (!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
4708     {
4709       gint width;
4710       gint i;
4711
4712       for (i = 0; i < clist->columns; i++)
4713         if (clist->column[i].auto_resize)
4714           {
4715             width = gtk_clist_optimal_column_width (clist, i);
4716             if (width != clist->column[i].width)
4717               gtk_clist_set_column_width (clist, i, width);
4718           }
4719     }
4720 }
4721
4722 static gint
4723 gtk_clist_key_press (GtkWidget   *widget,
4724                      GdkEventKey *event)
4725 {
4726   g_return_val_if_fail (widget != NULL, FALSE);
4727   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4728   g_return_val_if_fail (event != NULL, FALSE);
4729
4730   if (GTK_WIDGET_CLASS (parent_class)->key_press_event &&
4731       GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
4732     return TRUE;
4733
4734   switch (event->keyval)
4735     {
4736     case GDK_Tab:
4737     case GDK_ISO_Left_Tab:
4738       if (event->state & GDK_SHIFT_MASK)
4739         return gtk_container_focus (GTK_CONTAINER (widget),
4740                                     GTK_DIR_TAB_BACKWARD);
4741       else
4742         return gtk_container_focus (GTK_CONTAINER (widget),
4743                                     GTK_DIR_TAB_FORWARD);
4744     default:
4745       break;
4746     }
4747   return FALSE;
4748 }
4749
4750 static gint
4751 gtk_clist_button_press (GtkWidget      *widget,
4752                         GdkEventButton *event)
4753 {
4754   gint i;
4755   GtkCList *clist;
4756   gint x;
4757   gint y;
4758   gint row;
4759   gint column;
4760
4761   g_return_val_if_fail (widget != NULL, FALSE);
4762   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4763   g_return_val_if_fail (event != NULL, FALSE);
4764
4765   clist = GTK_CLIST (widget);
4766
4767   /* we don't handle button 2 and 3 */
4768   if (event->button != 1)
4769     return FALSE;
4770   
4771   /* selections on the list */
4772   if (event->window == clist->clist_window)
4773     {
4774       x = event->x;
4775       y = event->y;
4776
4777       if (get_selection_info (clist, x, y, &row, &column))
4778         {
4779           gint old_row = clist->focus_row;
4780
4781           if (clist->focus_row == -1)
4782             old_row = row;
4783
4784           if (event->type == GDK_BUTTON_PRESS)
4785             {
4786               GTK_CLIST_SET_FLAG (clist, CLIST_DRAG_SELECTION);
4787               gdk_pointer_grab (clist->clist_window, FALSE,
4788                                 GDK_POINTER_MOTION_HINT_MASK |
4789                                 GDK_BUTTON1_MOTION_MASK |
4790                                 GDK_BUTTON_RELEASE_MASK,
4791                                 NULL, NULL, event->time);
4792               gtk_grab_add (widget);
4793             }
4794           else if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (widget))
4795             {
4796               GTK_CLIST_UNSET_FLAG (clist, CLIST_DRAG_SELECTION);
4797               gtk_grab_remove (widget);
4798               gdk_pointer_ungrab (event->time);
4799             }
4800
4801           if (GTK_CLIST_ADD_MODE (clist))
4802             {
4803               GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
4804               if (GTK_WIDGET_HAS_FOCUS (widget))
4805                 {
4806                   gtk_clist_draw_focus (widget);
4807                   gdk_gc_set_line_attributes (clist->xor_gc, 1,
4808                                               GDK_LINE_SOLID, 0, 0);
4809                   clist->focus_row = row;
4810                   gtk_clist_draw_focus (widget);
4811                 }
4812               else
4813                 {
4814                   gdk_gc_set_line_attributes (clist->xor_gc, 1,
4815                                               GDK_LINE_SOLID, 0, 0);
4816                   clist->focus_row = row;
4817                 }
4818             }
4819           else if (row != clist->focus_row)
4820             {
4821               if (GTK_WIDGET_HAS_FOCUS (widget))
4822                 {
4823                   gtk_clist_draw_focus (widget);
4824                   clist->focus_row = row;
4825                   gtk_clist_draw_focus (widget);
4826                 }
4827               else
4828                 clist->focus_row = row;
4829             }
4830
4831           if (!GTK_WIDGET_HAS_FOCUS (widget))
4832             gtk_widget_grab_focus (widget);
4833
4834           switch (clist->selection_mode)
4835             {
4836             case GTK_SELECTION_SINGLE:
4837             case GTK_SELECTION_MULTIPLE:
4838               if (event->type != GDK_BUTTON_PRESS)
4839                 gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
4840                                  row, column, event);
4841               else
4842                 clist->anchor = row;
4843               break;
4844             case GTK_SELECTION_BROWSE:
4845               gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
4846                                row, column, event);
4847               break;
4848             case GTK_SELECTION_EXTENDED:
4849               if (event->type != GDK_BUTTON_PRESS)
4850                 {
4851                   if (clist->anchor != -1)
4852                     {
4853                       update_extended_selection (clist, clist->focus_row);
4854                       GTK_CLIST_CLASS_FW (clist)->resync_selection
4855                         (clist, (GdkEvent *) event);
4856                     }
4857                   gtk_signal_emit (GTK_OBJECT (clist),
4858                                    clist_signals[SELECT_ROW],
4859                                    row, column, event);
4860                   break;
4861                 }
4862               
4863               if (event->state & GDK_CONTROL_MASK)
4864                 {
4865                   if (event->state & GDK_SHIFT_MASK)
4866                     {
4867                       if (clist->anchor < 0)
4868                         {
4869                           g_list_free (clist->undo_selection);
4870                           g_list_free (clist->undo_unselection);
4871                           clist->undo_selection = NULL;
4872                           clist->undo_unselection = NULL;
4873                           clist->anchor = old_row;
4874                           clist->drag_pos = old_row;
4875                           clist->undo_anchor = old_row;
4876                         }
4877                       update_extended_selection (clist, clist->focus_row);
4878                     }
4879                   else
4880                     {
4881                       if (clist->anchor == -1)
4882                         set_anchor (clist, TRUE, row, old_row);
4883                       else
4884                         update_extended_selection (clist, clist->focus_row);
4885                     }
4886                   break;
4887                 }
4888
4889               if (event->state & GDK_SHIFT_MASK)
4890                 {
4891                   set_anchor (clist, FALSE, old_row, old_row);
4892                   update_extended_selection (clist, clist->focus_row);
4893                   break;
4894                 }
4895
4896               if (clist->anchor == -1)
4897                 set_anchor (clist, FALSE, row, old_row);
4898               else
4899                 update_extended_selection (clist, clist->focus_row);
4900               break;
4901             default:
4902               break;
4903             }
4904         }
4905
4906       return FALSE;
4907     }
4908
4909   /* press on resize windows */
4910   for (i = 0; i < clist->columns; i++)
4911     if (clist->column[i].resizeable && clist->column[i].window &&
4912         event->window == clist->column[i].window)
4913       {
4914         gdk_pointer_grab (clist->column[i].window, FALSE,
4915                           GDK_POINTER_MOTION_HINT_MASK |
4916                           GDK_BUTTON1_MOTION_MASK |
4917                           GDK_BUTTON_RELEASE_MASK,
4918                           NULL, NULL, event->time);
4919         gtk_grab_add (widget);
4920         GTK_CLIST_SET_FLAG (clist, CLIST_IN_DRAG);
4921
4922         if (!GTK_WIDGET_HAS_FOCUS (widget))
4923           gtk_widget_grab_focus (widget);
4924
4925         clist->drag_pos = i;
4926         clist->x_drag = (COLUMN_LEFT_XPIXEL(clist, i) + COLUMN_INSET +
4927                          clist->column[i].area.width + CELL_SPACING);
4928
4929         if (GTK_CLIST_ADD_MODE (clist))
4930           gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
4931         draw_xor_line (clist);
4932
4933         return FALSE;
4934       }
4935   return FALSE;
4936 }
4937
4938 static gint
4939 gtk_clist_button_release (GtkWidget      *widget,
4940                           GdkEventButton *event)
4941 {
4942   GtkCList *clist;
4943
4944   g_return_val_if_fail (widget != NULL, FALSE);
4945   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4946   g_return_val_if_fail (event != NULL, FALSE);
4947
4948   clist = GTK_CLIST (widget);
4949
4950   /* we don't handle button 2 and 3 */
4951   if (event->button != 1)
4952     return FALSE;
4953
4954   /* release on resize windows */
4955   if (GTK_CLIST_IN_DRAG (clist))
4956     {
4957       gint width;
4958       gint x;
4959       gint i;
4960
4961       i = clist->drag_pos;
4962       clist->drag_pos = -1;
4963
4964       GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
4965       gtk_widget_get_pointer (widget, &x, NULL);
4966       gtk_grab_remove (widget);
4967       gdk_pointer_ungrab (event->time);
4968
4969       if (clist->x_drag >= 0)
4970         draw_xor_line (clist);
4971
4972       if (GTK_CLIST_ADD_MODE (clist))
4973         {
4974           gdk_gc_set_line_attributes (clist->xor_gc, 1,
4975                                       GDK_LINE_ON_OFF_DASH, 0, 0);
4976           gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
4977         }
4978
4979       width = new_column_width (clist, i, &x);
4980       gtk_clist_set_column_width (clist, i, width);
4981       return FALSE;
4982     }
4983
4984   if (GTK_CLIST_DRAG_SELECTION (clist))
4985     {
4986       gint row;
4987       gint column;
4988
4989       GTK_CLIST_UNSET_FLAG (clist, CLIST_DRAG_SELECTION);
4990       gtk_grab_remove (widget);
4991       gdk_pointer_ungrab (event->time);
4992       if (clist->htimer)
4993         {
4994           gtk_timeout_remove (clist->htimer);
4995           clist->htimer = 0;
4996         }
4997       if (clist->vtimer)
4998         {
4999           gtk_timeout_remove (clist->vtimer);
5000           clist->vtimer = 0;
5001         }
5002       switch (clist->selection_mode)
5003         {
5004         case GTK_SELECTION_EXTENDED:
5005           if (!(event->state & GDK_SHIFT_MASK) ||
5006               event->x < 0 || event->x >= clist->clist_window_width ||
5007               event->y < 0 || event->y >= clist->clist_window_height)
5008             GTK_CLIST_CLASS_FW (clist)->resync_selection
5009               (clist, (GdkEvent *) event);
5010           break;
5011
5012         case GTK_SELECTION_SINGLE:
5013         case GTK_SELECTION_MULTIPLE:
5014           if (get_selection_info (clist, event->x, event->y, &row, &column))
5015             {
5016               if (row >= 0 && row < clist->rows && clist->anchor == row)
5017                 toggle_row (clist, row, column, (GdkEvent *) event);
5018             }
5019           clist->anchor = -1;
5020           break;
5021
5022         default:
5023           break;
5024         }
5025     }
5026
5027   return FALSE;
5028 }
5029
5030 static gint
5031 horizontal_timeout (GtkCList *clist)
5032 {
5033   gint x, y;
5034   GdkEventMotion event;
5035   GdkModifierType mask;
5036
5037   g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
5038
5039   clist->htimer = 0;
5040   gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
5041
5042   event.is_hint = 0;
5043   event.x = x;
5044   event.y = y;
5045   event.state = mask;
5046
5047   gtk_clist_motion (GTK_WIDGET (clist), &event);
5048
5049   return FALSE;
5050 }
5051
5052 static gint
5053 vertical_timeout (GtkCList *clist)
5054 {
5055   gint x, y;
5056   GdkEventMotion event;
5057   GdkModifierType mask;
5058
5059   g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
5060
5061   clist->vtimer = 0;
5062   gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
5063
5064   event.is_hint = 0;
5065   event.x = x;
5066   event.y = y;
5067   event.state = mask;
5068
5069   gtk_clist_motion (GTK_WIDGET (clist), &event);
5070
5071   return FALSE;
5072 }
5073
5074 static void
5075 move_vertical (GtkCList *clist,
5076                gint      row,
5077                gfloat    align)
5078 {
5079   gfloat value;
5080
5081   if (!clist->vadjustment)
5082     return;
5083
5084   value = (ROW_TOP_YPIXEL (clist, row) - clist->voffset -
5085            align * (clist->clist_window_height - clist->row_height) +
5086            (2 * align - 1) * CELL_SPACING);
5087
5088   if (value + clist->vadjustment->page_size > clist->vadjustment->upper)
5089     value = clist->vadjustment->upper - clist->vadjustment->page_size;
5090
5091   gtk_adjustment_set_value(clist->vadjustment, value);
5092 }
5093
5094 static void
5095 move_horizontal (GtkCList *clist,
5096                  gint      diff)
5097 {
5098   gfloat value;
5099
5100   if (!clist->hadjustment)
5101     return;
5102
5103   value = CLAMP (clist->hadjustment->value + diff, 0.0,
5104                  clist->hadjustment->upper - clist->hadjustment->page_size);
5105   gtk_adjustment_set_value(clist->hadjustment, value);
5106 }
5107
5108 static gint
5109 gtk_clist_motion (GtkWidget      *widget,
5110                   GdkEventMotion *event)
5111 {
5112   GtkCList *clist;
5113   gint x;
5114   gint y;
5115   gint row;
5116   gint new_width;
5117
5118   g_return_val_if_fail (widget != NULL, FALSE);
5119   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
5120
5121   clist = GTK_CLIST (widget);
5122   if (!(gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)))
5123     return FALSE;
5124
5125   if (GTK_CLIST_IN_DRAG (clist))
5126     {
5127       if (event->is_hint || event->window != widget->window)
5128         gtk_widget_get_pointer (widget, &x, NULL);
5129       else
5130         x = event->x;
5131       
5132       new_width = new_column_width (clist, clist->drag_pos, &x);
5133       if (x != clist->x_drag)
5134         {
5135           /* x_drag < 0 indicates that the xor line is already invisible */
5136           if (clist->x_drag >= 0)
5137             draw_xor_line (clist);
5138
5139           clist->x_drag = x;
5140
5141           if (clist->x_drag >= 0)
5142             draw_xor_line (clist);
5143         }
5144
5145       if (new_width <= MAX (COLUMN_MIN_WIDTH + 1,
5146                             clist->column[clist->drag_pos].min_width + 1))
5147         {
5148           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) < 0 && x < 0)
5149             gtk_clist_moveto (clist, -1, clist->drag_pos, 0, 0);
5150           return FALSE;
5151         }
5152       if (clist->column[clist->drag_pos].max_width >= COLUMN_MIN_WIDTH &&
5153           new_width >= clist->column[clist->drag_pos].max_width)
5154         {
5155           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) + new_width >
5156               clist->clist_window_width && x < 0)
5157             move_horizontal (clist,
5158                              COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) +
5159                              new_width - clist->clist_window_width +
5160                              COLUMN_INSET + CELL_SPACING);
5161           return FALSE;
5162         }
5163     }
5164
5165   if (event->is_hint || event->window != clist->clist_window)
5166     gdk_window_get_pointer (clist->clist_window, &x, &y, NULL);
5167
5168   /* horizontal autoscrolling */
5169   if (clist->hadjustment && LIST_WIDTH (clist) > clist->clist_window_width &&
5170       (x < 0 || x >= clist->clist_window_width))
5171     {
5172       if (clist->htimer)
5173         return FALSE;
5174
5175       clist->htimer = gtk_timeout_add
5176         (SCROLL_TIME, (GtkFunction) horizontal_timeout, clist);
5177
5178       if (!((x < 0 && clist->hadjustment->value == 0) ||
5179             (x >= clist->clist_window_width &&
5180              clist->hadjustment->value ==
5181              LIST_WIDTH (clist) - clist->clist_window_width)))
5182         {
5183           if (x < 0)
5184             move_horizontal (clist, -1 + (x/2));
5185           else
5186             move_horizontal (clist, 1 + (x - clist->clist_window_width) / 2);
5187         }
5188     }
5189
5190   if (GTK_CLIST_IN_DRAG (clist))
5191     return FALSE;
5192
5193   /* vertical autoscrolling */
5194   row = ROW_FROM_YPIXEL (clist, y);
5195
5196   /* don't scroll on last pixel row if it's a cell spacing */
5197   if (y == clist->clist_window_height - 1 &&
5198       y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height)
5199     return FALSE;
5200
5201   if (LIST_HEIGHT (clist) > clist->clist_window_height &&
5202       (y < 0 || y >= clist->clist_window_height))
5203     {
5204       if (clist->vtimer)
5205         return FALSE;
5206
5207       clist->vtimer = gtk_timeout_add (SCROLL_TIME,
5208                                        (GtkFunction) vertical_timeout, clist);
5209
5210       if (GTK_CLIST_DRAG_SELECTION (clist))
5211         {
5212           if ((y < 0 && clist->focus_row == 0) ||
5213               (y >= clist->clist_window_height &&
5214                clist->focus_row == clist->rows-1))
5215             return FALSE;
5216         }
5217     }
5218
5219   row = CLAMP (row, 0, clist->rows - 1);
5220
5221   if (GTK_CLIST_DRAG_SELECTION (clist))
5222     {
5223       if (row == clist->focus_row)
5224         return FALSE;
5225
5226       gtk_clist_draw_focus (widget);
5227       clist->focus_row = row;
5228       gtk_clist_draw_focus (widget);
5229
5230       switch (clist->selection_mode)
5231         {
5232         case GTK_SELECTION_BROWSE:
5233           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
5234                            clist->focus_row, -1, event);
5235           break;
5236         case GTK_SELECTION_EXTENDED:
5237           update_extended_selection (clist, clist->focus_row);
5238           break;
5239         default:
5240           break;
5241         }
5242     }
5243   
5244   if (ROW_TOP_YPIXEL(clist, row) < 0)
5245     move_vertical (clist, row, 0);
5246   else if (ROW_TOP_YPIXEL(clist, row) + clist->row_height >
5247            clist->clist_window_height)
5248     move_vertical (clist, row, 1);
5249
5250   return FALSE;
5251 }
5252
5253 static void
5254 gtk_clist_size_request (GtkWidget      *widget,
5255                         GtkRequisition *requisition)
5256 {
5257   GtkCList *clist;
5258   gint i;
5259
5260   g_return_if_fail (widget != NULL);
5261   g_return_if_fail (GTK_IS_CLIST (widget));
5262   g_return_if_fail (requisition != NULL);
5263
5264   clist = GTK_CLIST (widget);
5265
5266   requisition->width = 0;
5267   requisition->height = 0;
5268
5269   /* compute the size of the column title (title) area */
5270   clist->column_title_area.height = 0;
5271   if (GTK_CLIST_SHOW_TITLES (clist))
5272     for (i = 0; i < clist->columns; i++)
5273       if (clist->column[i].button)
5274         {
5275           gtk_widget_size_request (clist->column[i].button,
5276                                    &clist->column[i].button->requisition);
5277           clist->column_title_area.height =
5278             MAX (clist->column_title_area.height,
5279                  clist->column[i].button->requisition.height);
5280         }
5281
5282   requisition->width += (widget->style->klass->xthickness +
5283                          GTK_CONTAINER (widget)->border_width) * 2;
5284   requisition->height += (clist->column_title_area.height +
5285                           (widget->style->klass->ythickness +
5286                            GTK_CONTAINER (widget)->border_width) * 2);
5287
5288   /* if (!clist->hadjustment) */
5289   requisition->width += list_requisition_width (clist);
5290   /* if (!clist->vadjustment) */
5291   requisition->height += LIST_HEIGHT (clist);
5292 }
5293
5294 static void
5295 gtk_clist_size_allocate (GtkWidget     *widget,
5296                          GtkAllocation *allocation)
5297 {
5298   GtkCList *clist;
5299   GtkAllocation clist_allocation;
5300   gint border_width;
5301
5302   g_return_if_fail (widget != NULL);
5303   g_return_if_fail (GTK_IS_CLIST (widget));
5304   g_return_if_fail (allocation != NULL);
5305
5306   clist = GTK_CLIST (widget);
5307   widget->allocation = *allocation;
5308   border_width = GTK_CONTAINER (widget)->border_width;
5309
5310   if (GTK_WIDGET_REALIZED (widget))
5311     {
5312       gdk_window_move_resize (widget->window,
5313                               allocation->x + border_width,
5314                               allocation->y + border_width,
5315                               allocation->width - border_width * 2,
5316                               allocation->height - border_width * 2);
5317     }
5318
5319   /* use internal allocation structure for all the math
5320    * because it's easier than always subtracting the container
5321    * border width */
5322   clist->internal_allocation.x = 0;
5323   clist->internal_allocation.y = 0;
5324   clist->internal_allocation.width = MAX (1, allocation->width -
5325                                           border_width * 2);
5326   clist->internal_allocation.height = MAX (1, allocation->height -
5327                                            border_width * 2);
5328         
5329   /* allocate clist window assuming no scrollbars */
5330   clist_allocation.x = (clist->internal_allocation.x +
5331                         widget->style->klass->xthickness);
5332   clist_allocation.y = (clist->internal_allocation.y +
5333                         widget->style->klass->ythickness +
5334                         clist->column_title_area.height);
5335   clist_allocation.width = MAX (1, clist->internal_allocation.width - 
5336                                 (2 * widget->style->klass->xthickness));
5337   clist_allocation.height = MAX (1, clist->internal_allocation.height -
5338                                  (2 * widget->style->klass->ythickness) -
5339                                  clist->column_title_area.height);
5340   
5341   clist->clist_window_width = clist_allocation.width;
5342   clist->clist_window_height = clist_allocation.height;
5343   
5344   if (GTK_WIDGET_REALIZED (widget))
5345     {
5346       gdk_window_move_resize (clist->clist_window,
5347                               clist_allocation.x,
5348                               clist_allocation.y,
5349                               clist_allocation.width,
5350                               clist_allocation.height);
5351     }
5352   
5353   /* position the window which holds the column title buttons */
5354   clist->column_title_area.x = widget->style->klass->xthickness;
5355   clist->column_title_area.y = widget->style->klass->ythickness;
5356   clist->column_title_area.width = clist_allocation.width;
5357   
5358   if (GTK_WIDGET_REALIZED (widget))
5359     {
5360       gdk_window_move_resize (clist->title_window,
5361                               clist->column_title_area.x,
5362                               clist->column_title_area.y,
5363                               clist->column_title_area.width,
5364                               clist->column_title_area.height);
5365     }
5366   
5367   /* column button allocation */
5368   size_allocate_columns (clist, FALSE);
5369   size_allocate_title_buttons (clist);
5370
5371   adjust_adjustments (clist, TRUE);
5372 }
5373
5374 /* GTKCONTAINER
5375  *   gtk_clist_forall
5376  */
5377 static void
5378 gtk_clist_forall (GtkContainer *container,
5379                   gboolean      include_internals,
5380                   GtkCallback   callback,
5381                   gpointer      callback_data)
5382 {
5383   GtkCList *clist;
5384   guint i;
5385
5386   g_return_if_fail (container != NULL);
5387   g_return_if_fail (GTK_IS_CLIST (container));
5388   g_return_if_fail (callback != NULL);
5389
5390   if (!include_internals)
5391     return;
5392
5393   clist = GTK_CLIST (container);
5394       
5395   /* callback for the column buttons */
5396   for (i = 0; i < clist->columns; i++)
5397     if (clist->column[i].button)
5398       (*callback) (clist->column[i].button, callback_data);
5399 }
5400
5401 /* PRIVATE DRAWING FUNCTIONS
5402  *   get_cell_style
5403  *   draw_cell_pixmap
5404  *   draw_row
5405  *   draw_rows
5406  *   draw_xor_line
5407  *   clist_refresh
5408  */
5409 static void
5410 get_cell_style (GtkCList     *clist,
5411                 GtkCListRow  *clist_row,
5412                 gint          state,
5413                 gint          column,
5414                 GtkStyle    **style,
5415                 GdkGC       **fg_gc,
5416                 GdkGC       **bg_gc)
5417 {
5418   gint fg_state;
5419
5420   if ((state == GTK_STATE_PRELIGHT) &&
5421       (GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
5422     fg_state = GTK_STATE_INSENSITIVE;
5423   else
5424     fg_state = state;
5425
5426   if (clist_row->cell[column].style)
5427     {
5428       if (style)
5429         *style = clist_row->cell[column].style;
5430       if (fg_gc)
5431         *fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
5432       if (bg_gc)
5433         *bg_gc = clist_row->cell[column].style->bg_gc[state];
5434     }
5435   else if (clist_row->style)
5436     {
5437       if (style)
5438         *style = clist_row->style;
5439       if (fg_gc)
5440         *fg_gc = clist_row->style->fg_gc[fg_state];
5441       if (bg_gc)
5442         *bg_gc = clist_row->style->bg_gc[state];
5443     }
5444   else
5445     {
5446       if (style)
5447         *style = GTK_WIDGET (clist)->style;
5448       if (fg_gc)
5449         *fg_gc = GTK_WIDGET (clist)->style->fg_gc[fg_state];
5450       if (bg_gc)
5451         *bg_gc = GTK_WIDGET (clist)->style->bg_gc[state];
5452
5453       if (state != GTK_STATE_SELECTED)
5454         {
5455           if (fg_gc && clist_row->fg_set)
5456             *fg_gc = clist->fg_gc;
5457           if (bg_gc && clist_row->bg_set)
5458             *bg_gc = clist->bg_gc;
5459         }
5460     }
5461 }
5462
5463 static gint
5464 draw_cell_pixmap (GdkWindow    *window,
5465                   GdkRectangle *clip_rectangle,
5466                   GdkGC        *fg_gc,
5467                   GdkPixmap    *pixmap,
5468                   GdkBitmap    *mask,
5469                   gint          x,
5470                   gint          y,
5471                   gint          width,
5472                   gint          height)
5473 {
5474   gint xsrc = 0;
5475   gint ysrc = 0;
5476
5477   if (mask)
5478     {
5479       gdk_gc_set_clip_mask (fg_gc, mask);
5480       gdk_gc_set_clip_origin (fg_gc, x, y);
5481     }
5482
5483   if (x < clip_rectangle->x)
5484     {
5485       xsrc = clip_rectangle->x - x;
5486       width -= xsrc;
5487       x = clip_rectangle->x;
5488     }
5489   if (x + width > clip_rectangle->x + clip_rectangle->width)
5490     width = clip_rectangle->x + clip_rectangle->width - x;
5491
5492   if (y < clip_rectangle->y)
5493     {
5494       ysrc = clip_rectangle->y - y;
5495       height -= ysrc;
5496       y = clip_rectangle->y;
5497     }
5498   if (y + height > clip_rectangle->y + clip_rectangle->height)
5499     height = clip_rectangle->y + clip_rectangle->height - y;
5500
5501   gdk_draw_pixmap (window, fg_gc, pixmap, xsrc, ysrc, x, y, width, height);
5502   gdk_gc_set_clip_origin (fg_gc, 0, 0);
5503
5504   return x + MAX (width, 0);
5505 }
5506
5507 static void
5508 draw_row (GtkCList     *clist,
5509           GdkRectangle *area,
5510           gint          row,
5511           GtkCListRow  *clist_row)
5512 {
5513   GtkWidget *widget;
5514   GdkRectangle *rect;
5515   GdkRectangle row_rectangle;
5516   GdkRectangle cell_rectangle;
5517   GdkRectangle clip_rectangle;
5518   GdkRectangle intersect_rectangle;
5519   gint state;
5520   gint i;
5521
5522   g_return_if_fail (clist != NULL);
5523
5524   /* bail now if we arn't drawable yet */
5525   if (!GTK_WIDGET_DRAWABLE (clist) || row < 0 || row >= clist->rows)
5526     return;
5527
5528   widget = GTK_WIDGET (clist);
5529
5530   /* if the function is passed the pointer to the row instead of null,
5531    * it avoids this expensive lookup */
5532   if (!clist_row)
5533     clist_row = (g_list_nth (clist->row_list, row))->data;
5534
5535   /* rectangle of the entire row */
5536   row_rectangle.x = 0;
5537   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
5538   row_rectangle.width = clist->clist_window_width;
5539   row_rectangle.height = clist->row_height;
5540
5541   /* rectangle of the cell spacing above the row */
5542   cell_rectangle.x = 0;
5543   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
5544   cell_rectangle.width = row_rectangle.width;
5545   cell_rectangle.height = CELL_SPACING;
5546
5547   /* rectangle used to clip drawing operations, it's y and height
5548    * positions only need to be set once, so we set them once here. 
5549    * the x and width are set withing the drawing loop below once per
5550    * column */
5551   clip_rectangle.y = row_rectangle.y;
5552   clip_rectangle.height = row_rectangle.height;
5553
5554   if (clist_row->state == GTK_STATE_NORMAL)
5555     {
5556       state = GTK_STATE_PRELIGHT;
5557       if (clist_row->fg_set)
5558         gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
5559       if (clist_row->bg_set)
5560         gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
5561     }
5562   else
5563     state = clist_row->state;
5564
5565   /* draw the cell borders and background */
5566   if (area)
5567     {
5568       rect = &intersect_rectangle;
5569       if (gdk_rectangle_intersect (area, &cell_rectangle,
5570                                    &intersect_rectangle))
5571         gdk_draw_rectangle (clist->clist_window,
5572                             widget->style->base_gc[GTK_STATE_NORMAL],
5573                             TRUE,
5574                             intersect_rectangle.x,
5575                             intersect_rectangle.y,
5576                             intersect_rectangle.width,
5577                             intersect_rectangle.height);
5578
5579       /* the last row has to clear it's bottom cell spacing too */
5580       if (clist_row == clist->row_list_end->data)
5581         {
5582           cell_rectangle.y += clist->row_height + CELL_SPACING;
5583
5584           if (gdk_rectangle_intersect (area, &cell_rectangle,
5585                                        &intersect_rectangle))
5586             gdk_draw_rectangle (clist->clist_window,
5587                                 widget->style->base_gc[GTK_STATE_NORMAL],
5588                                 TRUE,
5589                                 intersect_rectangle.x,
5590                                 intersect_rectangle.y,
5591                                 intersect_rectangle.width,
5592                                 intersect_rectangle.height);
5593         }
5594
5595       if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
5596         return;
5597
5598     }
5599   else
5600     {
5601       rect = &clip_rectangle;
5602       gdk_draw_rectangle (clist->clist_window,
5603                           widget->style->base_gc[GTK_STATE_NORMAL],
5604                           TRUE,
5605                           cell_rectangle.x,
5606                           cell_rectangle.y,
5607                           cell_rectangle.width,
5608                           cell_rectangle.height);
5609
5610       /* the last row has to clear it's bottom cell spacing too */
5611       if (clist_row == clist->row_list_end->data)
5612         {
5613           cell_rectangle.y += clist->row_height + CELL_SPACING;
5614
5615           gdk_draw_rectangle (clist->clist_window,
5616                               widget->style->base_gc[GTK_STATE_NORMAL],
5617                               TRUE,
5618                               cell_rectangle.x,
5619                               cell_rectangle.y,
5620                               cell_rectangle.width,
5621                               cell_rectangle.height);     
5622         }         
5623     }
5624   
5625   /* iterate and draw all the columns (row cells) and draw their contents */
5626   for (i = 0; i < clist->columns; i++)
5627     {
5628       GtkStyle *style;
5629       GdkGC *fg_gc;
5630       GdkGC *bg_gc;
5631
5632       gint width;
5633       gint height;
5634       gint pixmap_width;
5635       gint offset = 0;
5636       gint row_center_offset;
5637
5638       if (!clist->column[i].visible)
5639         continue;
5640
5641       get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
5642
5643       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
5644       clip_rectangle.width = clist->column[i].area.width;
5645
5646       /* calculate clipping region clipping region */
5647       clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
5648       clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
5649                                (i + 1 == clist->columns) * CELL_SPACING);
5650       
5651       if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
5652                                             &intersect_rectangle))
5653         continue;
5654
5655       gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
5656                           rect->x, rect->y, rect->width, rect->height);
5657
5658       clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
5659       clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
5660                                (i + 1 == clist->columns) * CELL_SPACING);
5661
5662       /* calculate real width for column justification */
5663       pixmap_width = 0;
5664       offset = 0;
5665       switch (clist_row->cell[i].type)
5666         {
5667         case GTK_CELL_TEXT:
5668           width = gdk_string_width (style->font,
5669                                     GTK_CELL_TEXT (clist_row->cell[i])->text);
5670           break;
5671         case GTK_CELL_PIXMAP:
5672           gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5673                                &pixmap_width, &height);
5674           width = pixmap_width;
5675           break;
5676         case GTK_CELL_PIXTEXT:
5677           gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5678                                &pixmap_width, &height);
5679           width = (pixmap_width +
5680                    GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing +
5681                    gdk_string_width (style->font,
5682                                      GTK_CELL_PIXTEXT
5683                                      (clist_row->cell[i])->text));
5684           break;
5685         default:
5686           continue;
5687           break;
5688         }
5689
5690       switch (clist->column[i].justification)
5691         {
5692         case GTK_JUSTIFY_LEFT:
5693           offset = clip_rectangle.x + clist_row->cell[i].horizontal;
5694           break;
5695         case GTK_JUSTIFY_RIGHT:
5696           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5697                     clip_rectangle.width - width);
5698           break;
5699         case GTK_JUSTIFY_CENTER:
5700         case GTK_JUSTIFY_FILL:
5701           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5702                     (clip_rectangle.width / 2) - (width / 2));
5703           break;
5704         };
5705
5706       /* Draw Text and/or Pixmap */
5707       switch (clist_row->cell[i].type)
5708         {
5709         case GTK_CELL_PIXMAP:
5710           draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5711                             GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5712                             GTK_CELL_PIXMAP (clist_row->cell[i])->mask,
5713                             offset,
5714                             clip_rectangle.y + clist_row->cell[i].vertical +
5715                             (clip_rectangle.height - height) / 2,
5716                             pixmap_width, height);
5717           break;
5718         case GTK_CELL_PIXTEXT:
5719           offset =
5720             draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5721                               GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5722                               GTK_CELL_PIXTEXT (clist_row->cell[i])->mask,
5723                               offset,
5724                               clip_rectangle.y + clist_row->cell[i].vertical+
5725                               (clip_rectangle.height - height) / 2,
5726                               pixmap_width, height);
5727           offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
5728         case GTK_CELL_TEXT:
5729           if (style != GTK_WIDGET (clist)->style)
5730             row_center_offset = (((clist->row_height - style->font->ascent -
5731                                   style->font->descent - 1) / 2) + 1.5 +
5732                                  style->font->ascent);
5733           else
5734             row_center_offset = clist->row_center_offset;
5735
5736           gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
5737           gdk_draw_string (clist->clist_window, style->font, fg_gc,
5738                            offset,
5739                            row_rectangle.y + row_center_offset + 
5740                            clist_row->cell[i].vertical,
5741                            (clist_row->cell[i].type == GTK_CELL_PIXTEXT) ?
5742                            GTK_CELL_PIXTEXT (clist_row->cell[i])->text :
5743                            GTK_CELL_TEXT (clist_row->cell[i])->text);
5744           gdk_gc_set_clip_rectangle (fg_gc, NULL);
5745           break;
5746         default:
5747           break;
5748         }
5749     }
5750
5751   /* draw focus rectangle */
5752   if (clist->focus_row == row && GTK_WIDGET_HAS_FOCUS (widget))
5753     {
5754       if (!area)
5755         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5756                             row_rectangle.x, row_rectangle.y,
5757                             row_rectangle.width - 1, row_rectangle.height - 1);
5758       else if (gdk_rectangle_intersect (area, &row_rectangle,
5759                                         &intersect_rectangle))
5760         {
5761           gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
5762           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5763                               row_rectangle.x, row_rectangle.y,
5764                               row_rectangle.width - 1,
5765                               row_rectangle.height - 1);
5766           gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
5767         }
5768     }
5769 }
5770
5771 static void
5772 draw_rows (GtkCList     *clist,
5773            GdkRectangle *area)
5774 {
5775   GList *list;
5776   GtkCListRow *clist_row;
5777   gint i;
5778   gint first_row;
5779   gint last_row;
5780
5781   g_return_if_fail (clist != NULL);
5782   g_return_if_fail (GTK_IS_CLIST (clist));
5783
5784   if (clist->row_height == 0 ||
5785       !GTK_WIDGET_DRAWABLE (clist))
5786     return;
5787
5788   if (area)
5789     {
5790       first_row = ROW_FROM_YPIXEL (clist, area->y);
5791       last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
5792     }
5793   else
5794     {
5795       first_row = ROW_FROM_YPIXEL (clist, 0);
5796       last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
5797     }
5798
5799   /* this is a small special case which exposes the bottom cell line
5800    * on the last row -- it might go away if I change the wall the cell
5801    * spacings are drawn
5802    */
5803   if (clist->rows == first_row)
5804     first_row--;
5805
5806   list = g_list_nth (clist->row_list, first_row);
5807   i = first_row;
5808   while (list)
5809     {
5810       clist_row = list->data;
5811       list = list->next;
5812
5813       if (i > last_row)
5814         return;
5815
5816       GTK_CLIST_CLASS_FW (clist)->draw_row (clist, area, i, clist_row);
5817       i++;
5818     }
5819
5820   if (!area)
5821     gdk_window_clear_area (clist->clist_window,
5822                            0, ROW_TOP_YPIXEL (clist, i), -1, -1);
5823 }
5824
5825 static void                          
5826 draw_xor_line (GtkCList *clist)
5827 {
5828   GtkWidget *widget;
5829
5830   g_return_if_fail (clist != NULL);
5831
5832   widget = GTK_WIDGET (clist);
5833
5834   gdk_draw_line (widget->window, clist->xor_gc,
5835                  clist->x_drag,
5836                  widget->style->klass->ythickness,
5837                  clist->x_drag,
5838                  clist->column_title_area.height +
5839                  clist->clist_window_height + 1);
5840 }
5841
5842 static void
5843 clist_refresh (GtkCList *clist)
5844 {
5845   g_return_if_fail (clist != NULL);
5846   g_return_if_fail (GTK_IS_CLIST (clist));
5847   
5848   if (CLIST_UNFROZEN (clist))
5849     { 
5850       adjust_adjustments (clist, FALSE);
5851       draw_rows (clist, NULL);
5852     }
5853 }
5854
5855 /* get cell from coordinates
5856  *   get_selection_info
5857  *   gtk_clist_get_selection_info
5858  */
5859 static gint
5860 get_selection_info (GtkCList *clist,
5861                     gint      x,
5862                     gint      y,
5863                     gint     *row,
5864                     gint     *column)
5865 {
5866   gint trow, tcol;
5867
5868   g_return_val_if_fail (clist != NULL, 0);
5869   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
5870
5871   /* bounds checking, return false if the user clicked 
5872    * on a blank area */
5873   trow = ROW_FROM_YPIXEL (clist, y);
5874   if (trow >= clist->rows)
5875     return 0;
5876
5877   if (row)
5878     *row = trow;
5879
5880   tcol = COLUMN_FROM_XPIXEL (clist, x);
5881   if (tcol >= clist->columns)
5882     return 0;
5883
5884   if (column)
5885     *column = tcol;
5886
5887   return 1;
5888 }
5889
5890 gint
5891 gtk_clist_get_selection_info (GtkCList *clist, 
5892                               gint      x, 
5893                               gint      y, 
5894                               gint     *row, 
5895                               gint     *column)
5896 {
5897   g_return_val_if_fail (clist != NULL, 0);
5898   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
5899   return get_selection_info (clist, x, y, row, column);
5900 }
5901
5902 /* 
5903  * SCROLLBARS
5904  *
5905  * functions:
5906  *   adjust_adjustments
5907  *   vadjustment_changed
5908  *   hadjustment_changed
5909  *   vadjustment_value_changed
5910  *   hadjustment_value_changed 
5911  *   check_exposures
5912  */
5913 static void
5914 adjust_adjustments (GtkCList *clist,
5915                     gboolean  block_resize)
5916 {
5917   if (clist->vadjustment)
5918     {
5919       clist->vadjustment->page_size = clist->clist_window_height;
5920       clist->vadjustment->page_increment = clist->clist_window_height / 2;
5921       clist->vadjustment->step_increment = clist->row_height;
5922       clist->vadjustment->lower = 0;
5923       clist->vadjustment->upper = LIST_HEIGHT (clist);
5924
5925       if (clist->clist_window_height - clist->voffset > LIST_HEIGHT (clist))
5926         {
5927           clist->vadjustment->value = MAX (0, (LIST_HEIGHT (clist) -
5928                                                clist->clist_window_height));
5929           gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment),
5930                                    "value_changed");
5931         }
5932       gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment), "changed");
5933     }
5934
5935   if (clist->hadjustment)
5936     {
5937       clist->hadjustment->page_size = clist->clist_window_width;
5938       clist->hadjustment->page_increment = clist->clist_window_width / 2;
5939       clist->hadjustment->step_increment = 10;
5940       clist->hadjustment->lower = 0;
5941       clist->hadjustment->upper = LIST_WIDTH (clist);
5942
5943       if (clist->clist_window_width - clist->hoffset > LIST_WIDTH (clist))
5944         {
5945           clist->hadjustment->value = MAX (0, (LIST_WIDTH (clist) -
5946                                                clist->clist_window_width));
5947           gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment),
5948                                    "value_changed");
5949         }
5950       gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment), "changed");
5951     }
5952
5953   if (!block_resize && (!clist->vadjustment || !clist->hadjustment))
5954     {
5955       GtkWidget *widget;
5956
5957       widget = GTK_WIDGET (clist);
5958       gtk_widget_size_request (widget, &widget->requisition);
5959
5960       if ((!clist->hadjustment &&
5961            widget->requisition.width != widget->allocation.width) ||
5962           (!clist->vadjustment &&
5963            widget->requisition.height != widget->allocation.height))
5964         gtk_widget_queue_resize (widget);
5965     }
5966 }
5967
5968 static void
5969 vadjustment_changed (GtkAdjustment *adjustment,
5970                      gpointer       data)
5971 {
5972   GtkCList *clist;
5973
5974   g_return_if_fail (adjustment != NULL);
5975   g_return_if_fail (data != NULL);
5976
5977   clist = GTK_CLIST (data);
5978 }
5979
5980 static void
5981 hadjustment_changed (GtkAdjustment *adjustment,
5982                      gpointer       data)
5983 {
5984   GtkCList *clist;
5985
5986   g_return_if_fail (adjustment != NULL);
5987   g_return_if_fail (data != NULL);
5988
5989   clist = GTK_CLIST (data);
5990 }
5991
5992 static void
5993 vadjustment_value_changed (GtkAdjustment *adjustment,
5994                            gpointer       data)
5995 {
5996   GtkCList *clist;
5997   GdkRectangle area;
5998   gint diff, value;
5999
6000   g_return_if_fail (adjustment != NULL);
6001   g_return_if_fail (data != NULL);
6002   g_return_if_fail (GTK_IS_CLIST (data));
6003
6004   clist = GTK_CLIST (data);
6005
6006   if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->vadjustment)
6007     return;
6008
6009   value = adjustment->value;
6010
6011   if (value > -clist->voffset)
6012     {
6013       /* scroll down */
6014       diff = value + clist->voffset;
6015
6016       /* we have to re-draw the whole screen here... */
6017       if (diff >= clist->clist_window_height)
6018         {
6019           clist->voffset = -value;
6020           draw_rows (clist, NULL);
6021           return;
6022         }
6023
6024       if ((diff != 0) && (diff != clist->clist_window_height))
6025         gdk_window_copy_area (clist->clist_window, clist->fg_gc,
6026                               0, 0, clist->clist_window, 0, diff,
6027                               clist->clist_window_width,
6028                               clist->clist_window_height - diff);
6029
6030       area.x = 0;
6031       area.y = clist->clist_window_height - diff;
6032       area.width = clist->clist_window_width;
6033       area.height = diff;
6034     }
6035   else
6036     {
6037       /* scroll up */
6038       diff = -clist->voffset - value;
6039
6040       /* we have to re-draw the whole screen here... */
6041       if (diff >= clist->clist_window_height)
6042         {
6043           clist->voffset = -value;
6044           draw_rows (clist, NULL);
6045           return;
6046         }
6047
6048       if ((diff != 0) && (diff != clist->clist_window_height))
6049         gdk_window_copy_area (clist->clist_window, clist->fg_gc,
6050                               0, diff, clist->clist_window, 0, 0,
6051                               clist->clist_window_width,
6052                               clist->clist_window_height - diff);
6053
6054       area.x = 0;
6055       area.y = 0;
6056       area.width = clist->clist_window_width;
6057       area.height = diff;
6058     }
6059
6060   clist->voffset = -value;
6061   if ((diff != 0) && (diff != clist->clist_window_height))
6062     check_exposures (clist);
6063
6064   draw_rows (clist, &area);
6065 }
6066
6067 static void
6068 hadjustment_value_changed (GtkAdjustment *adjustment,
6069                            gpointer       data)
6070 {
6071   GtkCList *clist;
6072   GdkRectangle area;
6073   gint i;
6074   gint y = 0;
6075   gint diff = 0;
6076   gint value;
6077
6078   g_return_if_fail (adjustment != NULL);
6079   g_return_if_fail (data != NULL);
6080   g_return_if_fail (GTK_IS_CLIST (data));
6081
6082   clist = GTK_CLIST (data);
6083
6084   if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->hadjustment)
6085     return;
6086
6087   value = adjustment->value;
6088
6089   /* move the column buttons and resize windows */
6090   for (i = 0; i < clist->columns; i++)
6091     {
6092       if (clist->column[i].button)
6093         {
6094           clist->column[i].button->allocation.x -= value + clist->hoffset;
6095           
6096           if (clist->column[i].button->window)
6097             {
6098               gdk_window_move (clist->column[i].button->window,
6099                                clist->column[i].button->allocation.x,
6100                                clist->column[i].button->allocation.y);
6101               
6102               if (clist->column[i].window)
6103                 gdk_window_move (clist->column[i].window,
6104                                  clist->column[i].button->allocation.x +
6105                                  clist->column[i].button->allocation.width - 
6106                                  (DRAG_WIDTH / 2), 0); 
6107             }
6108         }
6109     }
6110
6111   if (value > -clist->hoffset)
6112     {
6113       /* scroll right */
6114       diff = value + clist->hoffset;
6115       
6116       clist->hoffset = -value;
6117       
6118       /* we have to re-draw the whole screen here... */
6119       if (diff >= clist->clist_window_width)
6120         {
6121           draw_rows (clist, NULL);
6122           return;
6123         }
6124
6125       if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
6126           GTK_CLIST_ADD_MODE (clist))
6127         {
6128           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6129               
6130           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
6131                               clist->clist_window_width - 1,
6132                               clist->row_height - 1);
6133         }
6134       gdk_window_copy_area (clist->clist_window,
6135                             clist->fg_gc,
6136                             0, 0,
6137                             clist->clist_window,
6138                             diff,
6139                             0,
6140                             clist->clist_window_width - diff,
6141                             clist->clist_window_height);
6142
6143       area.x = clist->clist_window_width - diff;
6144     }
6145   else
6146     {
6147       /* scroll left */
6148       if (!(diff = -clist->hoffset - value))
6149         return;
6150
6151       clist->hoffset = -value;
6152       
6153       /* we have to re-draw the whole screen here... */
6154       if (diff >= clist->clist_window_width)
6155         {
6156           draw_rows (clist, NULL);
6157           return;
6158         }
6159       
6160       if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
6161           GTK_CLIST_ADD_MODE (clist))
6162         {
6163           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6164           
6165           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
6166                               clist->clist_window_width - 1,
6167                               clist->row_height - 1);
6168         }
6169
6170       gdk_window_copy_area (clist->clist_window,
6171                             clist->fg_gc,
6172                             diff, 0,
6173                             clist->clist_window,
6174                             0,
6175                             0,
6176                             clist->clist_window_width - diff,
6177                             clist->clist_window_height);
6178           
6179       area.x = 0;
6180     }
6181
6182   area.y = 0;
6183   area.width = diff;
6184   area.height = clist->clist_window_height;
6185
6186   check_exposures (clist);
6187
6188   if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist))
6189     {
6190       if (GTK_CLIST_ADD_MODE (clist))
6191         {
6192           gint focus_row;
6193           
6194           focus_row = clist->focus_row;
6195           clist->focus_row = -1;
6196           draw_rows (clist, &area);
6197           clist->focus_row = focus_row;
6198           
6199           gdk_draw_rectangle (clist->clist_window, clist->xor_gc,
6200                               FALSE, 0, y, clist->clist_window_width - 1,
6201                               clist->row_height - 1);
6202           return;
6203         }
6204       else
6205         {
6206           gint x0;
6207           gint x1;
6208           
6209           if (area.x == 0)
6210             {
6211               x0 = clist->clist_window_width - 1;
6212               x1 = diff;
6213             }
6214           else
6215             {
6216               x0 = 0;
6217               x1 = area.x - 1;
6218             }
6219           
6220           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6221           gdk_draw_line (clist->clist_window, clist->xor_gc,
6222                          x0, y + 1, x0, y + clist->row_height - 2);
6223           gdk_draw_line (clist->clist_window, clist->xor_gc,
6224                          x1, y + 1, x1, y + clist->row_height - 2);
6225           
6226         }
6227     }
6228   draw_rows (clist, &area);
6229 }
6230
6231 static void
6232 check_exposures (GtkCList *clist)
6233 {
6234   GdkEvent *event;
6235
6236   if (!GTK_WIDGET_REALIZED (clist))
6237     return;
6238
6239   /* Make sure graphics expose events are processed before scrolling
6240    * again */
6241   while ((event = gdk_event_get_graphics_expose (clist->clist_window)) != NULL)
6242     {
6243       gtk_widget_event (GTK_WIDGET (clist), event);
6244       if (event->expose.count == 0)
6245         {
6246           gdk_event_free (event);
6247           break;
6248         }
6249       gdk_event_free (event);
6250     }
6251 }
6252
6253 /* 
6254  * Memory Allocation/Distruction Routines for GtkCList stuctures
6255  *
6256  * functions:
6257  *   columns_new
6258  *   column_title_new
6259  *   columns_delete
6260  *   row_new
6261  *   row_delete
6262  */
6263 static GtkCListColumn *
6264 columns_new (GtkCList *clist)
6265 {
6266   GtkCListColumn *column;
6267   gint i;
6268
6269   column = g_new (GtkCListColumn, clist->columns);
6270
6271   for (i = 0; i < clist->columns; i++)
6272     {
6273       column[i].area.x = 0;
6274       column[i].area.y = 0;
6275       column[i].area.width = 0;
6276       column[i].area.height = 0;
6277       column[i].title = NULL;
6278       column[i].button = NULL;
6279       column[i].window = NULL;
6280       column[i].width = 0;
6281       column[i].min_width = -1;
6282       column[i].max_width = -1;
6283       column[i].visible = TRUE;
6284       column[i].width_set = FALSE;
6285       column[i].resizeable = TRUE;
6286       column[i].auto_resize = FALSE;
6287       column[i].justification = GTK_JUSTIFY_LEFT;
6288     }
6289
6290   return column;
6291 }
6292
6293 static void
6294 column_title_new (GtkCList    *clist,
6295                   gint         column,
6296                   const gchar *title)
6297 {
6298   if (clist->column[column].title)
6299     g_free (clist->column[column].title);
6300
6301   clist->column[column].title = g_strdup (title);
6302 }
6303
6304 static void
6305 columns_delete (GtkCList *clist)
6306 {
6307   gint i;
6308
6309   for (i = 0; i < clist->columns; i++)
6310     if (clist->column[i].title)
6311       g_free (clist->column[i].title);
6312       
6313   g_free (clist->column);
6314 }
6315
6316 static GtkCListRow *
6317 row_new (GtkCList *clist)
6318 {
6319   int i;
6320   GtkCListRow *clist_row;
6321
6322   clist_row = g_chunk_new (GtkCListRow, clist->row_mem_chunk);
6323   clist_row->cell = g_chunk_new (GtkCell, clist->cell_mem_chunk);
6324
6325   for (i = 0; i < clist->columns; i++)
6326     {
6327       clist_row->cell[i].type = GTK_CELL_EMPTY;
6328       clist_row->cell[i].vertical = 0;
6329       clist_row->cell[i].horizontal = 0;
6330       clist_row->cell[i].style = NULL;
6331     }
6332
6333   clist_row->fg_set = FALSE;
6334   clist_row->bg_set = FALSE;
6335   clist_row->style = NULL;
6336   clist_row->selectable = TRUE;
6337   clist_row->state = GTK_STATE_NORMAL;
6338   clist_row->data = NULL;
6339   clist_row->destroy = NULL;
6340
6341   return clist_row;
6342 }
6343
6344 static void
6345 row_delete (GtkCList    *clist,
6346             GtkCListRow *clist_row)
6347 {
6348   gint i;
6349
6350   for (i = 0; i < clist->columns; i++)
6351     {
6352       GTK_CLIST_CLASS_FW (clist)->set_cell_contents
6353         (clist, clist_row, i, GTK_CELL_EMPTY, NULL, 0, NULL, NULL);
6354       if (clist_row->cell[i].style)
6355         {
6356           if (GTK_WIDGET_REALIZED (clist))
6357             gtk_style_detach (clist_row->cell[i].style);
6358           gtk_style_unref (clist_row->cell[i].style);
6359         }
6360     }
6361
6362   if (clist_row->style)
6363     {
6364       if (GTK_WIDGET_REALIZED (clist))
6365         gtk_style_detach (clist_row->style);
6366       gtk_style_unref (clist_row->style);
6367     }
6368
6369   if (clist_row->destroy)
6370     clist_row->destroy (clist_row->data);
6371
6372   g_mem_chunk_free (clist->cell_mem_chunk, clist_row->cell);
6373   g_mem_chunk_free (clist->row_mem_chunk, clist_row);
6374 }
6375
6376 /* FOCUS FUNCTIONS
6377  *   gtk_clist_focus
6378  *   gtk_clist_draw_focus
6379  *   gtk_clist_focus_in
6380  *   gtk_clist_focus_out
6381  *   gtk_clist_set_focus_child
6382  *   title_focus
6383  */
6384 static gint
6385 gtk_clist_focus (GtkContainer     *container,
6386                  GtkDirectionType  direction)
6387 {
6388   GtkCList *clist;
6389   GtkWidget *focus_child;
6390   gint old_row;
6391
6392   g_return_val_if_fail (container != NULL, FALSE);
6393   g_return_val_if_fail (GTK_IS_CLIST (container), FALSE);
6394
6395   if (!GTK_WIDGET_SENSITIVE (container))
6396     return FALSE;
6397   
6398   clist = GTK_CLIST (container);
6399   focus_child = container->focus_child;
6400   old_row = clist->focus_row;
6401
6402   switch (direction)
6403     {
6404     case GTK_DIR_LEFT:
6405     case GTK_DIR_RIGHT:
6406       if (GTK_CLIST_CHILD_HAS_FOCUS (clist))
6407         {
6408           if (title_focus (clist, direction))
6409             return TRUE;
6410           gtk_container_set_focus_child (container, NULL);
6411           return FALSE;
6412          }
6413       gtk_widget_grab_focus (GTK_WIDGET (container));
6414       return TRUE;
6415     case GTK_DIR_DOWN:
6416     case GTK_DIR_TAB_FORWARD:
6417       if (GTK_CLIST_CHILD_HAS_FOCUS (clist))
6418         {
6419           gboolean tf = FALSE;
6420
6421           if (((focus_child && direction == GTK_DIR_DOWN) ||
6422                !(tf = title_focus (clist, GTK_DIR_TAB_FORWARD)))
6423               && clist->rows)
6424             {
6425               if (clist->focus_row < 0)
6426                 {
6427                   clist->focus_row = 0;
6428
6429                   if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6430                        clist->selection_mode == GTK_SELECTION_EXTENDED) &&
6431                       !clist->selection)
6432                     gtk_signal_emit (GTK_OBJECT (clist),
6433                                      clist_signals[SELECT_ROW],
6434                                      clist->focus_row, -1, NULL);
6435                 }
6436               gtk_widget_grab_focus (GTK_WIDGET (container));
6437               return TRUE;
6438             }
6439
6440           if (tf)
6441             return TRUE;
6442         }
6443       
6444       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
6445       break;
6446     case GTK_DIR_UP:
6447     case GTK_DIR_TAB_BACKWARD:
6448       if (!focus_child &&
6449           GTK_CLIST_CHILD_HAS_FOCUS (clist) && clist->rows)
6450         {
6451           if (clist->focus_row < 0)
6452             {
6453               clist->focus_row = 0;
6454               if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6455                    clist->selection_mode == GTK_SELECTION_EXTENDED) &&
6456                   !clist->selection)
6457                 gtk_signal_emit (GTK_OBJECT (clist),
6458                                  clist_signals[SELECT_ROW],
6459                                  clist->focus_row, -1, NULL);
6460             }
6461           gtk_widget_grab_focus (GTK_WIDGET (container));
6462           return TRUE;
6463         }
6464
6465       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
6466
6467       if (title_focus (clist, direction))
6468         return TRUE;
6469
6470       break;
6471     default:
6472       break;
6473     }
6474
6475   gtk_container_set_focus_child (container, NULL);
6476   return FALSE;
6477 }
6478
6479 static void
6480 gtk_clist_draw_focus (GtkWidget *widget)
6481 {
6482   GtkCList *clist;
6483
6484   g_return_if_fail (widget != NULL);
6485   g_return_if_fail (GTK_IS_CLIST (widget));
6486
6487   if (!GTK_WIDGET_DRAWABLE (widget))
6488     return;
6489
6490   clist = GTK_CLIST (widget);
6491   if (clist->focus_row >= 0)
6492     gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
6493                         0, ROW_TOP_YPIXEL(clist, clist->focus_row),
6494                         clist->clist_window_width - 1,
6495                         clist->row_height - 1);
6496 }
6497
6498 static gint
6499 gtk_clist_focus_in (GtkWidget     *widget,
6500                     GdkEventFocus *event)
6501 {
6502   GtkCList *clist;
6503
6504   g_return_val_if_fail (widget != NULL, FALSE);
6505   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
6506   g_return_val_if_fail (event != NULL, FALSE);
6507
6508   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
6509   GTK_CLIST_UNSET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
6510
6511   clist = GTK_CLIST (widget);
6512
6513   if (clist->selection_mode == GTK_SELECTION_BROWSE &&
6514       clist->selection == NULL && clist->focus_row > -1)
6515     {
6516       GList *list;
6517
6518       list = g_list_nth (clist->row_list, clist->focus_row);
6519       if (list && GTK_CLIST_ROW (list)->selectable)
6520         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6521                          clist->focus_row, -1, event);
6522       else
6523         gtk_widget_draw_focus (widget);
6524     }
6525   else
6526     gtk_widget_draw_focus (widget);
6527
6528   return FALSE;
6529 }
6530
6531 static gint
6532 gtk_clist_focus_out (GtkWidget     *widget,
6533                      GdkEventFocus *event)
6534 {
6535   GtkCList *clist;
6536
6537   g_return_val_if_fail (widget != NULL, FALSE);
6538   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
6539   g_return_val_if_fail (event != NULL, FALSE);
6540
6541   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
6542   GTK_CLIST_SET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
6543
6544   gtk_widget_draw_focus (widget);
6545   
6546   clist = GTK_CLIST (widget);
6547
6548   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_EXTENDED)
6549     GTK_CLIST_CLASS_FW (widget)->resync_selection (clist, (GdkEvent *) event);
6550
6551   return FALSE;
6552 }
6553
6554 static void
6555 gtk_clist_set_focus_child (GtkContainer *container,
6556                            GtkWidget    *child)
6557 {
6558   g_return_if_fail (container != NULL);
6559   g_return_if_fail (GTK_IS_CLIST (container));
6560
6561   if (child)
6562     {
6563       g_return_if_fail (GTK_IS_WIDGET (child));
6564       GTK_CLIST_SET_FLAG (container, CLIST_CHILD_HAS_FOCUS);
6565     }
6566
6567   parent_class->set_focus_child (container, child);
6568 }
6569
6570 static gboolean
6571 title_focus (GtkCList *clist,
6572              gint      dir)
6573 {
6574   GtkWidget *focus_child;
6575   gboolean return_val = FALSE;
6576   gint d = 1;
6577   gint i = 0;
6578   gint j;
6579
6580   if (!GTK_CLIST_SHOW_TITLES (clist))
6581     return FALSE;
6582
6583   focus_child = GTK_CONTAINER (clist)->focus_child;
6584   
6585   switch (dir)
6586     {
6587     case GTK_DIR_TAB_BACKWARD:
6588     case GTK_DIR_UP:
6589       if (!focus_child || !GTK_CLIST_CHILD_HAS_FOCUS (clist))
6590         {
6591           if (dir == GTK_DIR_UP)
6592             i = COLUMN_FROM_XPIXEL (clist, 0);
6593           else
6594             i = clist->columns - 1;
6595           focus_child = clist->column[i].button;
6596           dir = GTK_DIR_TAB_FORWARD;
6597         }
6598       else
6599         d = -1;
6600       break;
6601     case GTK_DIR_LEFT:
6602       d = -1;
6603       if (!focus_child)
6604         {
6605           i = clist->columns - 1;
6606           focus_child = clist->column[i].button;
6607         }
6608       break;
6609     case GTK_DIR_RIGHT:
6610       if (!focus_child)
6611         {
6612           i = 0;
6613           focus_child = clist->column[i].button;
6614         }
6615       break;
6616     }
6617
6618   if (focus_child)
6619     while (i < clist->columns)
6620       {
6621         if (clist->column[i].button == focus_child)
6622           {
6623             if (clist->column[i].button && 
6624                 GTK_WIDGET_VISIBLE (clist->column[i].button) &&
6625                 GTK_IS_CONTAINER (clist->column[i].button) &&
6626                 !GTK_WIDGET_HAS_FOCUS (clist->column[i].button))
6627               if (gtk_container_focus 
6628                   (GTK_CONTAINER (clist->column[i].button), dir))
6629                 {
6630                   return_val = TRUE;
6631                   i -= d;
6632                 }
6633             if (!return_val && dir == GTK_DIR_UP)
6634               return FALSE;
6635             i += d;
6636             break;
6637           }
6638         i++;
6639       }
6640
6641   j = i;
6642
6643   if (!return_val)
6644     while (j >= 0 && j < clist->columns)
6645       {
6646         if (clist->column[j].button &&
6647             GTK_WIDGET_VISIBLE (clist->column[j].button))
6648           {
6649             if (GTK_IS_CONTAINER (clist->column[j].button) &&
6650                 gtk_container_focus 
6651                 (GTK_CONTAINER (clist->column[j].button), dir))
6652               {
6653                 return_val = TRUE;
6654                 break;
6655               }
6656             else if (GTK_WIDGET_CAN_FOCUS (clist->column[j].button))
6657               {
6658                 gtk_widget_grab_focus (clist->column[j].button);
6659                 return_val = TRUE;
6660                 break;
6661               }
6662           }
6663         j += d;
6664       }
6665   
6666   if (return_val)
6667     {
6668       if (COLUMN_LEFT_XPIXEL (clist, j) < CELL_SPACING + COLUMN_INSET)
6669         gtk_clist_moveto (clist, -1, j, 0, 0);
6670       else if (COLUMN_LEFT_XPIXEL(clist, j) + clist->column[j].area.width >
6671                clist->clist_window_width)
6672         {
6673           if (j == clist->columns-1)
6674             gtk_clist_moveto (clist, -1, j, 0, 0);
6675           else
6676             gtk_clist_moveto (clist, -1, j, 0, 1);
6677         }
6678     }
6679   return return_val;
6680 }
6681
6682 /* SCROLLING FUNCTIONS
6683  *   move_focus_row
6684  *   scroll_horizontal
6685  *   scroll_vertical
6686  */
6687 static void
6688 move_focus_row (GtkCList      *clist,
6689                 GtkScrollType  scroll_type,
6690                 gfloat         position)
6691 {
6692   GtkWidget *widget;
6693
6694   g_return_if_fail (clist != 0);
6695   g_return_if_fail (GTK_IS_CLIST (clist));
6696
6697   widget = GTK_WIDGET (clist);
6698
6699   switch (scroll_type)
6700     {
6701     case GTK_SCROLL_STEP_BACKWARD:
6702       if (clist->focus_row <= 0)
6703         return;
6704       gtk_clist_draw_focus (widget);
6705       clist->focus_row--;
6706       gtk_clist_draw_focus (widget);
6707       break;
6708     case GTK_SCROLL_STEP_FORWARD:
6709       if (clist->focus_row >= clist->rows - 1)
6710         return;
6711       gtk_clist_draw_focus (widget);
6712       clist->focus_row++;
6713       gtk_clist_draw_focus (widget);
6714       break;
6715     case GTK_SCROLL_PAGE_BACKWARD:
6716       if (clist->focus_row <= 0)
6717         return;
6718       gtk_clist_draw_focus (widget);
6719       clist->focus_row = MAX (0, clist->focus_row -
6720                               (2 * clist->clist_window_height -
6721                                clist->row_height - CELL_SPACING) / 
6722                               (2 * (clist->row_height + CELL_SPACING)));
6723       gtk_clist_draw_focus (widget);
6724       break;
6725     case GTK_SCROLL_PAGE_FORWARD:
6726       if (clist->focus_row >= clist->rows - 1)
6727         return;
6728       gtk_clist_draw_focus (widget);
6729       clist->focus_row = MIN (clist->rows - 1, clist->focus_row + 
6730                               (2 * clist->clist_window_height -
6731                                clist->row_height - CELL_SPACING) / 
6732                               (2 * (clist->row_height + CELL_SPACING)));
6733       gtk_clist_draw_focus (widget);
6734       break;
6735     case GTK_SCROLL_JUMP:
6736       if (position >= 0 && position <= 1)
6737         {
6738           gtk_clist_draw_focus (widget);
6739           clist->focus_row = position * (clist->rows - 1);
6740           gtk_clist_draw_focus (widget);
6741         }
6742       break;
6743     default:
6744       break;
6745     }
6746 }
6747
6748 static void
6749 scroll_horizontal (GtkCList      *clist,
6750                    GtkScrollType  scroll_type,
6751                    gfloat         position)
6752 {
6753   gint column = 0;
6754
6755   g_return_if_fail (clist != 0);
6756   g_return_if_fail (GTK_IS_CLIST (clist));
6757
6758   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
6759     return;
6760
6761   switch (scroll_type)
6762     {
6763     case GTK_SCROLL_STEP_BACKWARD:
6764       column = COLUMN_FROM_XPIXEL (clist, 0);
6765       if (COLUMN_LEFT_XPIXEL (clist, column) - CELL_SPACING - COLUMN_INSET >= 0
6766           && column > 0)
6767         column--;
6768       break;
6769     case GTK_SCROLL_STEP_FORWARD:
6770       column =  COLUMN_FROM_XPIXEL (clist, clist->clist_window_width);
6771       if (column < 0)
6772         return;
6773       if (COLUMN_LEFT_XPIXEL (clist, column) +
6774           clist->column[column].area.width +
6775           CELL_SPACING + COLUMN_INSET - 1 <= clist->clist_window_width &&
6776           column < clist->columns - 1)
6777         column++;
6778       break;
6779     case GTK_SCROLL_PAGE_BACKWARD:
6780     case GTK_SCROLL_PAGE_FORWARD:
6781       return;
6782     case GTK_SCROLL_JUMP:
6783       if (position >= 0 && position <= 1)
6784         column = position * (clist->columns - 1);
6785       else
6786         return;
6787       break;
6788     default:
6789       break;
6790     }
6791
6792   if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET)
6793     gtk_clist_moveto (clist, -1, column, 0, 0);
6794   else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1
6795            + clist->column[column].area.width > clist->clist_window_width)
6796     {
6797       if (column == clist->columns - 1)
6798         gtk_clist_moveto (clist, -1, column, 0, 0);
6799       else
6800         gtk_clist_moveto (clist, -1, column, 0, 1);
6801     }
6802 }
6803
6804 static void
6805 scroll_vertical (GtkCList      *clist,
6806                  GtkScrollType  scroll_type,
6807                  gfloat         position)
6808 {
6809   gint old_focus_row;
6810
6811   g_return_if_fail (clist != NULL);
6812   g_return_if_fail (GTK_IS_CLIST (clist));
6813
6814   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
6815     return;
6816
6817   switch (clist->selection_mode)
6818     {
6819     case GTK_SELECTION_EXTENDED:
6820       if (clist->anchor >= 0)
6821         return;
6822     case GTK_SELECTION_BROWSE:
6823
6824       old_focus_row = clist->focus_row;
6825       move_focus_row (clist, scroll_type, position);
6826
6827       if (old_focus_row != clist->focus_row)
6828         {
6829           if (clist->selection_mode == GTK_SELECTION_BROWSE)
6830             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
6831                              old_focus_row, -1, NULL);
6832           else if (!GTK_CLIST_ADD_MODE (clist))
6833             {
6834               gtk_clist_unselect_all (clist);
6835               clist->undo_anchor = old_focus_row;
6836             }
6837         }
6838
6839       switch (gtk_clist_row_is_visible (clist, clist->focus_row))
6840         {
6841         case GTK_VISIBILITY_NONE:
6842           if (old_focus_row != clist->focus_row &&
6843               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
6844                 GTK_CLIST_ADD_MODE (clist)))
6845             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6846                              clist->focus_row, -1, NULL);
6847           switch (scroll_type)
6848             {
6849             case GTK_SCROLL_STEP_BACKWARD:
6850             case GTK_SCROLL_PAGE_BACKWARD:
6851               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6852               break;
6853             case GTK_SCROLL_STEP_FORWARD:
6854             case GTK_SCROLL_PAGE_FORWARD:
6855               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6856               break;
6857             case GTK_SCROLL_JUMP:
6858               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
6859               break;
6860             default:
6861               break;
6862             }
6863           break;
6864         case GTK_VISIBILITY_PARTIAL:
6865           switch (scroll_type)
6866             {
6867             case GTK_SCROLL_STEP_BACKWARD:
6868             case GTK_SCROLL_PAGE_BACKWARD:
6869               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6870               break;
6871             case GTK_SCROLL_STEP_FORWARD:
6872             case GTK_SCROLL_PAGE_FORWARD:
6873               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6874               break;
6875             case GTK_SCROLL_JUMP:
6876               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
6877               break;
6878             default:
6879               break;
6880             }
6881         default:
6882           if (old_focus_row != clist->focus_row &&
6883               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
6884                 GTK_CLIST_ADD_MODE (clist)))
6885             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6886                              clist->focus_row, -1, NULL);
6887           break;
6888         }
6889       break;
6890     default:
6891       move_focus_row (clist, scroll_type, position);
6892
6893       if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
6894           clist->clist_window_height)
6895         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6896       else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
6897         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6898       break;
6899     }
6900 }
6901
6902 /* PUBLIC SORTING FUNCTIONS
6903  * gtk_clist_sort
6904  * gtk_clist_set_compare_func
6905  * gtk_clist_set_auto_sort
6906  * gtk_clist_set_sort_type
6907  * gtk_clist_set_sort_column
6908  */
6909 void
6910 gtk_clist_sort (GtkCList *clist)
6911 {
6912   g_return_if_fail (clist != NULL);
6913   g_return_if_fail (GTK_IS_CLIST (clist));
6914
6915   GTK_CLIST_CLASS_FW (clist)->sort_list (clist);
6916 }
6917
6918 void
6919 gtk_clist_set_compare_func (GtkCList            *clist,
6920                             GtkCListCompareFunc  cmp_func)
6921 {
6922   g_return_if_fail (clist != NULL);
6923   g_return_if_fail (GTK_IS_CLIST (clist));
6924
6925   clist->compare = (cmp_func) ? cmp_func : default_compare;
6926 }
6927
6928 void       
6929 gtk_clist_set_auto_sort (GtkCList *clist,
6930                          gboolean  auto_sort)
6931 {
6932   g_return_if_fail (clist != NULL);
6933   g_return_if_fail (GTK_IS_CLIST (clist));
6934   
6935   if (GTK_CLIST_AUTO_SORT (clist) && !auto_sort)
6936     GTK_CLIST_UNSET_FLAG (clist, CLIST_AUTO_SORT);
6937   else if (!GTK_CLIST_AUTO_SORT (clist) && auto_sort)
6938     {
6939       GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_SORT);
6940       gtk_clist_sort (clist);
6941     }
6942 }
6943
6944 void       
6945 gtk_clist_set_sort_type (GtkCList    *clist,
6946                          GtkSortType  sort_type)
6947 {
6948   g_return_if_fail (clist != NULL);
6949   g_return_if_fail (GTK_IS_CLIST (clist));
6950   
6951   clist->sort_type = sort_type;
6952 }
6953
6954 void
6955 gtk_clist_set_sort_column (GtkCList *clist,
6956                            gint      column)
6957 {
6958   g_return_if_fail (clist != NULL);
6959   g_return_if_fail (GTK_IS_CLIST (clist));
6960
6961   if (column < 0 || column >= clist->columns)
6962     return;
6963
6964   clist->sort_column = column;
6965 }
6966
6967 /* PRIVATE SORTING FUNCTIONS
6968  *   default_compare
6969  *   real_sort_list
6970  *   gtk_clist_merge
6971  *   gtk_clist_mergesort
6972  */
6973 static gint
6974 default_compare (GtkCList      *clist,
6975                  gconstpointer  ptr1,
6976                  gconstpointer  ptr2)
6977 {
6978   char *text1 = NULL;
6979   char *text2 = NULL;
6980
6981   GtkCListRow *row1 = (GtkCListRow *) ptr1;
6982   GtkCListRow *row2 = (GtkCListRow *) ptr2;
6983
6984   switch (row1->cell[clist->sort_column].type)
6985     {
6986     case GTK_CELL_TEXT:
6987       text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
6988       break;
6989     case GTK_CELL_PIXTEXT:
6990       text1 = GTK_CELL_PIXTEXT (row1->cell[clist->sort_column])->text;
6991       break;
6992     default:
6993       break;
6994     }
6995  
6996   switch (row2->cell[clist->sort_column].type)
6997     {
6998     case GTK_CELL_TEXT:
6999       text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
7000       break;
7001     case GTK_CELL_PIXTEXT:
7002       text2 = GTK_CELL_PIXTEXT (row2->cell[clist->sort_column])->text;
7003       break;
7004     default:
7005       break;
7006     }
7007
7008   if (!text2)
7009     return (text1 != NULL);
7010
7011   if (!text1)
7012     return -1;
7013
7014   return strcmp (text1, text2);
7015 }
7016
7017 static void
7018 real_sort_list (GtkCList *clist)
7019 {
7020   GList *list;
7021   GList *work;
7022   gint i;
7023
7024   g_return_if_fail (clist != NULL);
7025   g_return_if_fail (GTK_IS_CLIST (clist));
7026
7027   if (clist->rows <= 1)
7028     return;
7029
7030   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
7031     return;
7032
7033   gtk_clist_freeze (clist);
7034
7035   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_EXTENDED)
7036     {
7037       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
7038       g_list_free (clist->undo_selection);
7039       g_list_free (clist->undo_unselection);
7040       clist->undo_selection = NULL;
7041       clist->undo_unselection = NULL;
7042     }
7043    
7044   clist->row_list = gtk_clist_mergesort (clist, clist->row_list, clist->rows);
7045
7046   work = clist->selection;
7047
7048   for (i = 0, list = clist->row_list; i < clist->rows; i++, list = list->next)
7049     {
7050       if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
7051         {
7052           work->data = GINT_TO_POINTER (i);
7053           work = work->next;
7054         }
7055       
7056       if (i == clist->rows - 1)
7057         clist->row_list_end = list;
7058     }
7059
7060   gtk_clist_thaw (clist);
7061 }
7062
7063 static GList *
7064 gtk_clist_merge (GtkCList *clist,
7065                  GList    *a,         /* first list to merge */
7066                  GList    *b)         /* second list to merge */
7067 {
7068   GList z = { 0 };                    /* auxiliary node */
7069   GList *c;
7070   gint cmp;
7071
7072   c = &z;
7073
7074   while (a || b)
7075     {
7076       if (a && !b)
7077         {
7078           c->next = a;
7079           a->prev = c;
7080           c = a;
7081           a = a->next;
7082           break;
7083         }
7084       else if (!a && b)
7085         {
7086           c->next = b;
7087           b->prev = c;
7088           c = b;
7089           b = b->next;
7090           break;
7091         }
7092       else /* a && b */
7093         {
7094           cmp = clist->compare (clist, GTK_CLIST_ROW (a), GTK_CLIST_ROW (b));
7095           if ((cmp >= 0 && clist->sort_type == GTK_SORT_DESCENDING) ||
7096               (cmp <= 0 && clist->sort_type == GTK_SORT_ASCENDING) ||
7097               (a && !b))
7098             {
7099               c->next = a;
7100               a->prev = c;
7101               c = a;
7102               a = a->next;
7103             }
7104           else
7105             {
7106               c->next = b;
7107               b->prev = c;
7108               c = b;
7109               b = b->next;
7110             }
7111         }
7112     }
7113
7114   return z.next;
7115 }
7116
7117 static GList *
7118 gtk_clist_mergesort (GtkCList *clist,
7119                      GList    *list,         /* the list to sort */
7120                      gint      num)          /* the list's length */
7121 {
7122   GList *half;
7123   gint i;
7124
7125   if (num == 1)
7126     {
7127       return list;
7128     }
7129   else
7130     {
7131       /* move "half" to the middle */
7132       half = list;
7133       for (i = 0; i < num / 2; i++)
7134         half = half->next;
7135
7136       /* cut the list in two */
7137       half->prev->next = NULL;
7138       half->prev = NULL;
7139
7140       /* recursively sort both lists */
7141       return gtk_clist_merge (clist,
7142                        gtk_clist_mergesort (clist, list, num / 2),
7143                        gtk_clist_mergesort (clist, half, num - num / 2));
7144     }
7145 }