]> Pileus Git - ~andy/gtk/blob - gtk/gtkclist.c
Mark child->window as destroyed so we don't destroy it again.
[~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 = GINT_TO_POINTER (GPOINTER_TO_INT (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   if (GTK_WIDGET_REALIZED (widget))
4693     {
4694       gtk_style_set_background (widget->style, widget->window, widget->state);
4695       gtk_style_set_background (widget->style, clist->title_window, GTK_STATE_SELECTED);
4696       gdk_window_set_background (clist->clist_window, &widget->style->bg[GTK_STATE_PRELIGHT]);
4697     }
4698
4699   /* Fill in data after widget has correct style */
4700
4701   /* text properties */
4702   if (!GTK_CLIST_ROW_HEIGHT_SET (clist))
4703     {
4704       clist->row_height = (widget->style->font->ascent +
4705                            widget->style->font->descent + 1);
4706       clist->row_center_offset = widget->style->font->ascent + 1.5;
4707     }
4708   else
4709     clist->row_center_offset = 1.5 + (clist->row_height +
4710                                       widget->style->font->ascent -
4711                                       widget->style->font->descent - 1) / 2;
4712
4713   /* Column widths */
4714   if (!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
4715     {
4716       gint width;
4717       gint i;
4718
4719       for (i = 0; i < clist->columns; i++)
4720         if (clist->column[i].auto_resize)
4721           {
4722             width = gtk_clist_optimal_column_width (clist, i);
4723             if (width != clist->column[i].width)
4724               gtk_clist_set_column_width (clist, i, width);
4725           }
4726     }
4727 }
4728
4729 static gint
4730 gtk_clist_key_press (GtkWidget   *widget,
4731                      GdkEventKey *event)
4732 {
4733   g_return_val_if_fail (widget != NULL, FALSE);
4734   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4735   g_return_val_if_fail (event != NULL, FALSE);
4736
4737   if (GTK_WIDGET_CLASS (parent_class)->key_press_event &&
4738       GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
4739     return TRUE;
4740
4741   switch (event->keyval)
4742     {
4743     case GDK_Tab:
4744     case GDK_ISO_Left_Tab:
4745       if (event->state & GDK_SHIFT_MASK)
4746         return gtk_container_focus (GTK_CONTAINER (widget),
4747                                     GTK_DIR_TAB_BACKWARD);
4748       else
4749         return gtk_container_focus (GTK_CONTAINER (widget),
4750                                     GTK_DIR_TAB_FORWARD);
4751     default:
4752       break;
4753     }
4754   return FALSE;
4755 }
4756
4757 static gint
4758 gtk_clist_button_press (GtkWidget      *widget,
4759                         GdkEventButton *event)
4760 {
4761   gint i;
4762   GtkCList *clist;
4763   gint x;
4764   gint y;
4765   gint row;
4766   gint column;
4767
4768   g_return_val_if_fail (widget != NULL, FALSE);
4769   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4770   g_return_val_if_fail (event != NULL, FALSE);
4771
4772   clist = GTK_CLIST (widget);
4773
4774   /* we don't handle button 2 and 3 */
4775   if (event->button != 1)
4776     return FALSE;
4777   
4778   /* selections on the list */
4779   if (event->window == clist->clist_window)
4780     {
4781       x = event->x;
4782       y = event->y;
4783
4784       if (get_selection_info (clist, x, y, &row, &column))
4785         {
4786           gint old_row = clist->focus_row;
4787
4788           if (clist->focus_row == -1)
4789             old_row = row;
4790
4791           if (event->type == GDK_BUTTON_PRESS)
4792             {
4793               GTK_CLIST_SET_FLAG (clist, CLIST_DRAG_SELECTION);
4794               gdk_pointer_grab (clist->clist_window, FALSE,
4795                                 GDK_POINTER_MOTION_HINT_MASK |
4796                                 GDK_BUTTON1_MOTION_MASK |
4797                                 GDK_BUTTON_RELEASE_MASK,
4798                                 NULL, NULL, event->time);
4799               gtk_grab_add (widget);
4800             }
4801           else if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (widget))
4802             {
4803               GTK_CLIST_UNSET_FLAG (clist, CLIST_DRAG_SELECTION);
4804               gtk_grab_remove (widget);
4805               gdk_pointer_ungrab (event->time);
4806             }
4807
4808           if (GTK_CLIST_ADD_MODE (clist))
4809             {
4810               GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
4811               if (GTK_WIDGET_HAS_FOCUS (widget))
4812                 {
4813                   gtk_clist_draw_focus (widget);
4814                   gdk_gc_set_line_attributes (clist->xor_gc, 1,
4815                                               GDK_LINE_SOLID, 0, 0);
4816                   clist->focus_row = row;
4817                   gtk_clist_draw_focus (widget);
4818                 }
4819               else
4820                 {
4821                   gdk_gc_set_line_attributes (clist->xor_gc, 1,
4822                                               GDK_LINE_SOLID, 0, 0);
4823                   clist->focus_row = row;
4824                 }
4825             }
4826           else if (row != clist->focus_row)
4827             {
4828               if (GTK_WIDGET_HAS_FOCUS (widget))
4829                 {
4830                   gtk_clist_draw_focus (widget);
4831                   clist->focus_row = row;
4832                   gtk_clist_draw_focus (widget);
4833                 }
4834               else
4835                 clist->focus_row = row;
4836             }
4837
4838           if (!GTK_WIDGET_HAS_FOCUS (widget))
4839             gtk_widget_grab_focus (widget);
4840
4841           switch (clist->selection_mode)
4842             {
4843             case GTK_SELECTION_SINGLE:
4844             case GTK_SELECTION_MULTIPLE:
4845               if (event->type != GDK_BUTTON_PRESS)
4846                 gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
4847                                  row, column, event);
4848               else
4849                 clist->anchor = row;
4850               break;
4851             case GTK_SELECTION_BROWSE:
4852               gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
4853                                row, column, event);
4854               break;
4855             case GTK_SELECTION_EXTENDED:
4856               if (event->type != GDK_BUTTON_PRESS)
4857                 {
4858                   if (clist->anchor != -1)
4859                     {
4860                       update_extended_selection (clist, clist->focus_row);
4861                       GTK_CLIST_CLASS_FW (clist)->resync_selection
4862                         (clist, (GdkEvent *) event);
4863                     }
4864                   gtk_signal_emit (GTK_OBJECT (clist),
4865                                    clist_signals[SELECT_ROW],
4866                                    row, column, event);
4867                   break;
4868                 }
4869               
4870               if (event->state & GDK_CONTROL_MASK)
4871                 {
4872                   if (event->state & GDK_SHIFT_MASK)
4873                     {
4874                       if (clist->anchor < 0)
4875                         {
4876                           g_list_free (clist->undo_selection);
4877                           g_list_free (clist->undo_unselection);
4878                           clist->undo_selection = NULL;
4879                           clist->undo_unselection = NULL;
4880                           clist->anchor = old_row;
4881                           clist->drag_pos = old_row;
4882                           clist->undo_anchor = old_row;
4883                         }
4884                       update_extended_selection (clist, clist->focus_row);
4885                     }
4886                   else
4887                     {
4888                       if (clist->anchor == -1)
4889                         set_anchor (clist, TRUE, row, old_row);
4890                       else
4891                         update_extended_selection (clist, clist->focus_row);
4892                     }
4893                   break;
4894                 }
4895
4896               if (event->state & GDK_SHIFT_MASK)
4897                 {
4898                   set_anchor (clist, FALSE, old_row, old_row);
4899                   update_extended_selection (clist, clist->focus_row);
4900                   break;
4901                 }
4902
4903               if (clist->anchor == -1)
4904                 set_anchor (clist, FALSE, row, old_row);
4905               else
4906                 update_extended_selection (clist, clist->focus_row);
4907               break;
4908             default:
4909               break;
4910             }
4911         }
4912
4913       return FALSE;
4914     }
4915
4916   /* press on resize windows */
4917   for (i = 0; i < clist->columns; i++)
4918     if (clist->column[i].resizeable && clist->column[i].window &&
4919         event->window == clist->column[i].window)
4920       {
4921         gdk_pointer_grab (clist->column[i].window, FALSE,
4922                           GDK_POINTER_MOTION_HINT_MASK |
4923                           GDK_BUTTON1_MOTION_MASK |
4924                           GDK_BUTTON_RELEASE_MASK,
4925                           NULL, NULL, event->time);
4926         gtk_grab_add (widget);
4927         GTK_CLIST_SET_FLAG (clist, CLIST_IN_DRAG);
4928
4929         if (!GTK_WIDGET_HAS_FOCUS (widget))
4930           gtk_widget_grab_focus (widget);
4931
4932         clist->drag_pos = i;
4933         clist->x_drag = (COLUMN_LEFT_XPIXEL(clist, i) + COLUMN_INSET +
4934                          clist->column[i].area.width + CELL_SPACING);
4935
4936         if (GTK_CLIST_ADD_MODE (clist))
4937           gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
4938         draw_xor_line (clist);
4939
4940         return FALSE;
4941       }
4942   return FALSE;
4943 }
4944
4945 static gint
4946 gtk_clist_button_release (GtkWidget      *widget,
4947                           GdkEventButton *event)
4948 {
4949   GtkCList *clist;
4950
4951   g_return_val_if_fail (widget != NULL, FALSE);
4952   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4953   g_return_val_if_fail (event != NULL, FALSE);
4954
4955   clist = GTK_CLIST (widget);
4956
4957   /* we don't handle button 2 and 3 */
4958   if (event->button != 1)
4959     return FALSE;
4960
4961   /* release on resize windows */
4962   if (GTK_CLIST_IN_DRAG (clist))
4963     {
4964       gint width;
4965       gint x;
4966       gint i;
4967
4968       i = clist->drag_pos;
4969       clist->drag_pos = -1;
4970
4971       GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
4972       gtk_widget_get_pointer (widget, &x, NULL);
4973       gtk_grab_remove (widget);
4974       gdk_pointer_ungrab (event->time);
4975
4976       if (clist->x_drag >= 0)
4977         draw_xor_line (clist);
4978
4979       if (GTK_CLIST_ADD_MODE (clist))
4980         {
4981           gdk_gc_set_line_attributes (clist->xor_gc, 1,
4982                                       GDK_LINE_ON_OFF_DASH, 0, 0);
4983           gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
4984         }
4985
4986       width = new_column_width (clist, i, &x);
4987       gtk_clist_set_column_width (clist, i, width);
4988       return FALSE;
4989     }
4990
4991   if (GTK_CLIST_DRAG_SELECTION (clist))
4992     {
4993       gint row;
4994       gint column;
4995
4996       GTK_CLIST_UNSET_FLAG (clist, CLIST_DRAG_SELECTION);
4997       gtk_grab_remove (widget);
4998       gdk_pointer_ungrab (event->time);
4999       if (clist->htimer)
5000         {
5001           gtk_timeout_remove (clist->htimer);
5002           clist->htimer = 0;
5003         }
5004       if (clist->vtimer)
5005         {
5006           gtk_timeout_remove (clist->vtimer);
5007           clist->vtimer = 0;
5008         }
5009       switch (clist->selection_mode)
5010         {
5011         case GTK_SELECTION_EXTENDED:
5012           if (!(event->state & GDK_SHIFT_MASK) ||
5013               event->x < 0 || event->x >= clist->clist_window_width ||
5014               event->y < 0 || event->y >= clist->clist_window_height)
5015             GTK_CLIST_CLASS_FW (clist)->resync_selection
5016               (clist, (GdkEvent *) event);
5017           break;
5018
5019         case GTK_SELECTION_SINGLE:
5020         case GTK_SELECTION_MULTIPLE:
5021           if (get_selection_info (clist, event->x, event->y, &row, &column))
5022             {
5023               if (row >= 0 && row < clist->rows && clist->anchor == row)
5024                 toggle_row (clist, row, column, (GdkEvent *) event);
5025             }
5026           clist->anchor = -1;
5027           break;
5028
5029         default:
5030           break;
5031         }
5032     }
5033
5034   return FALSE;
5035 }
5036
5037 static gint
5038 horizontal_timeout (GtkCList *clist)
5039 {
5040   gint x, y;
5041   GdkEventMotion event;
5042   GdkModifierType mask;
5043
5044   g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
5045
5046   clist->htimer = 0;
5047   gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
5048
5049   event.is_hint = 0;
5050   event.x = x;
5051   event.y = y;
5052   event.state = mask;
5053
5054   gtk_clist_motion (GTK_WIDGET (clist), &event);
5055
5056   return FALSE;
5057 }
5058
5059 static gint
5060 vertical_timeout (GtkCList *clist)
5061 {
5062   gint x, y;
5063   GdkEventMotion event;
5064   GdkModifierType mask;
5065
5066   g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
5067
5068   clist->vtimer = 0;
5069   gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
5070
5071   event.is_hint = 0;
5072   event.x = x;
5073   event.y = y;
5074   event.state = mask;
5075
5076   gtk_clist_motion (GTK_WIDGET (clist), &event);
5077
5078   return FALSE;
5079 }
5080
5081 static void
5082 move_vertical (GtkCList *clist,
5083                gint      row,
5084                gfloat    align)
5085 {
5086   gfloat value;
5087
5088   if (!clist->vadjustment)
5089     return;
5090
5091   value = (ROW_TOP_YPIXEL (clist, row) - clist->voffset -
5092            align * (clist->clist_window_height - clist->row_height) +
5093            (2 * align - 1) * CELL_SPACING);
5094
5095   if (value + clist->vadjustment->page_size > clist->vadjustment->upper)
5096     value = clist->vadjustment->upper - clist->vadjustment->page_size;
5097
5098   gtk_adjustment_set_value(clist->vadjustment, value);
5099 }
5100
5101 static void
5102 move_horizontal (GtkCList *clist,
5103                  gint      diff)
5104 {
5105   gfloat value;
5106
5107   if (!clist->hadjustment)
5108     return;
5109
5110   value = CLAMP (clist->hadjustment->value + diff, 0.0,
5111                  clist->hadjustment->upper - clist->hadjustment->page_size);
5112   gtk_adjustment_set_value(clist->hadjustment, value);
5113 }
5114
5115 static gint
5116 gtk_clist_motion (GtkWidget      *widget,
5117                   GdkEventMotion *event)
5118 {
5119   GtkCList *clist;
5120   gint x;
5121   gint y;
5122   gint row;
5123   gint new_width;
5124
5125   g_return_val_if_fail (widget != NULL, FALSE);
5126   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
5127
5128   clist = GTK_CLIST (widget);
5129   if (!(gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)))
5130     return FALSE;
5131
5132   if (GTK_CLIST_IN_DRAG (clist))
5133     {
5134       if (event->is_hint || event->window != widget->window)
5135         gtk_widget_get_pointer (widget, &x, NULL);
5136       else
5137         x = event->x;
5138       
5139       new_width = new_column_width (clist, clist->drag_pos, &x);
5140       if (x != clist->x_drag)
5141         {
5142           /* x_drag < 0 indicates that the xor line is already invisible */
5143           if (clist->x_drag >= 0)
5144             draw_xor_line (clist);
5145
5146           clist->x_drag = x;
5147
5148           if (clist->x_drag >= 0)
5149             draw_xor_line (clist);
5150         }
5151
5152       if (new_width <= MAX (COLUMN_MIN_WIDTH + 1,
5153                             clist->column[clist->drag_pos].min_width + 1))
5154         {
5155           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) < 0 && x < 0)
5156             gtk_clist_moveto (clist, -1, clist->drag_pos, 0, 0);
5157           return FALSE;
5158         }
5159       if (clist->column[clist->drag_pos].max_width >= COLUMN_MIN_WIDTH &&
5160           new_width >= clist->column[clist->drag_pos].max_width)
5161         {
5162           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) + new_width >
5163               clist->clist_window_width && x < 0)
5164             move_horizontal (clist,
5165                              COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) +
5166                              new_width - clist->clist_window_width +
5167                              COLUMN_INSET + CELL_SPACING);
5168           return FALSE;
5169         }
5170     }
5171
5172   if (event->is_hint || event->window != clist->clist_window)
5173     gdk_window_get_pointer (clist->clist_window, &x, &y, NULL);
5174
5175   /* horizontal autoscrolling */
5176   if (clist->hadjustment && LIST_WIDTH (clist) > clist->clist_window_width &&
5177       (x < 0 || x >= clist->clist_window_width))
5178     {
5179       if (clist->htimer)
5180         return FALSE;
5181
5182       clist->htimer = gtk_timeout_add
5183         (SCROLL_TIME, (GtkFunction) horizontal_timeout, clist);
5184
5185       if (!((x < 0 && clist->hadjustment->value == 0) ||
5186             (x >= clist->clist_window_width &&
5187              clist->hadjustment->value ==
5188              LIST_WIDTH (clist) - clist->clist_window_width)))
5189         {
5190           if (x < 0)
5191             move_horizontal (clist, -1 + (x/2));
5192           else
5193             move_horizontal (clist, 1 + (x - clist->clist_window_width) / 2);
5194         }
5195     }
5196
5197   if (GTK_CLIST_IN_DRAG (clist))
5198     return FALSE;
5199
5200   /* vertical autoscrolling */
5201   row = ROW_FROM_YPIXEL (clist, y);
5202
5203   /* don't scroll on last pixel row if it's a cell spacing */
5204   if (y == clist->clist_window_height - 1 &&
5205       y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height)
5206     return FALSE;
5207
5208   if (LIST_HEIGHT (clist) > clist->clist_window_height &&
5209       (y < 0 || y >= clist->clist_window_height))
5210     {
5211       if (clist->vtimer)
5212         return FALSE;
5213
5214       clist->vtimer = gtk_timeout_add (SCROLL_TIME,
5215                                        (GtkFunction) vertical_timeout, clist);
5216
5217       if (GTK_CLIST_DRAG_SELECTION (clist))
5218         {
5219           if ((y < 0 && clist->focus_row == 0) ||
5220               (y >= clist->clist_window_height &&
5221                clist->focus_row == clist->rows-1))
5222             return FALSE;
5223         }
5224     }
5225
5226   row = CLAMP (row, 0, clist->rows - 1);
5227
5228   if (GTK_CLIST_DRAG_SELECTION (clist))
5229     {
5230       if (row == clist->focus_row)
5231         return FALSE;
5232
5233       gtk_clist_draw_focus (widget);
5234       clist->focus_row = row;
5235       gtk_clist_draw_focus (widget);
5236
5237       switch (clist->selection_mode)
5238         {
5239         case GTK_SELECTION_BROWSE:
5240           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
5241                            clist->focus_row, -1, event);
5242           break;
5243         case GTK_SELECTION_EXTENDED:
5244           update_extended_selection (clist, clist->focus_row);
5245           break;
5246         default:
5247           break;
5248         }
5249     }
5250   
5251   if (ROW_TOP_YPIXEL(clist, row) < 0)
5252     move_vertical (clist, row, 0);
5253   else if (ROW_TOP_YPIXEL(clist, row) + clist->row_height >
5254            clist->clist_window_height)
5255     move_vertical (clist, row, 1);
5256
5257   return FALSE;
5258 }
5259
5260 static void
5261 gtk_clist_size_request (GtkWidget      *widget,
5262                         GtkRequisition *requisition)
5263 {
5264   GtkCList *clist;
5265   gint i;
5266
5267   g_return_if_fail (widget != NULL);
5268   g_return_if_fail (GTK_IS_CLIST (widget));
5269   g_return_if_fail (requisition != NULL);
5270
5271   clist = GTK_CLIST (widget);
5272
5273   requisition->width = 0;
5274   requisition->height = 0;
5275
5276   /* compute the size of the column title (title) area */
5277   clist->column_title_area.height = 0;
5278   if (GTK_CLIST_SHOW_TITLES (clist))
5279     for (i = 0; i < clist->columns; i++)
5280       if (clist->column[i].button)
5281         {
5282           gtk_widget_size_request (clist->column[i].button,
5283                                    &clist->column[i].button->requisition);
5284           clist->column_title_area.height =
5285             MAX (clist->column_title_area.height,
5286                  clist->column[i].button->requisition.height);
5287         }
5288
5289   requisition->width += (widget->style->klass->xthickness +
5290                          GTK_CONTAINER (widget)->border_width) * 2;
5291   requisition->height += (clist->column_title_area.height +
5292                           (widget->style->klass->ythickness +
5293                            GTK_CONTAINER (widget)->border_width) * 2);
5294
5295   /* if (!clist->hadjustment) */
5296   requisition->width += list_requisition_width (clist);
5297   /* if (!clist->vadjustment) */
5298   requisition->height += LIST_HEIGHT (clist);
5299 }
5300
5301 static void
5302 gtk_clist_size_allocate (GtkWidget     *widget,
5303                          GtkAllocation *allocation)
5304 {
5305   GtkCList *clist;
5306   GtkAllocation clist_allocation;
5307   gint border_width;
5308
5309   g_return_if_fail (widget != NULL);
5310   g_return_if_fail (GTK_IS_CLIST (widget));
5311   g_return_if_fail (allocation != NULL);
5312
5313   clist = GTK_CLIST (widget);
5314   widget->allocation = *allocation;
5315   border_width = GTK_CONTAINER (widget)->border_width;
5316
5317   if (GTK_WIDGET_REALIZED (widget))
5318     {
5319       gdk_window_move_resize (widget->window,
5320                               allocation->x + border_width,
5321                               allocation->y + border_width,
5322                               allocation->width - border_width * 2,
5323                               allocation->height - border_width * 2);
5324     }
5325
5326   /* use internal allocation structure for all the math
5327    * because it's easier than always subtracting the container
5328    * border width */
5329   clist->internal_allocation.x = 0;
5330   clist->internal_allocation.y = 0;
5331   clist->internal_allocation.width = MAX (1, allocation->width -
5332                                           border_width * 2);
5333   clist->internal_allocation.height = MAX (1, allocation->height -
5334                                            border_width * 2);
5335         
5336   /* allocate clist window assuming no scrollbars */
5337   clist_allocation.x = (clist->internal_allocation.x +
5338                         widget->style->klass->xthickness);
5339   clist_allocation.y = (clist->internal_allocation.y +
5340                         widget->style->klass->ythickness +
5341                         clist->column_title_area.height);
5342   clist_allocation.width = MAX (1, clist->internal_allocation.width - 
5343                                 (2 * widget->style->klass->xthickness));
5344   clist_allocation.height = MAX (1, clist->internal_allocation.height -
5345                                  (2 * widget->style->klass->ythickness) -
5346                                  clist->column_title_area.height);
5347   
5348   clist->clist_window_width = clist_allocation.width;
5349   clist->clist_window_height = clist_allocation.height;
5350   
5351   if (GTK_WIDGET_REALIZED (widget))
5352     {
5353       gdk_window_move_resize (clist->clist_window,
5354                               clist_allocation.x,
5355                               clist_allocation.y,
5356                               clist_allocation.width,
5357                               clist_allocation.height);
5358     }
5359   
5360   /* position the window which holds the column title buttons */
5361   clist->column_title_area.x = widget->style->klass->xthickness;
5362   clist->column_title_area.y = widget->style->klass->ythickness;
5363   clist->column_title_area.width = clist_allocation.width;
5364   
5365   if (GTK_WIDGET_REALIZED (widget))
5366     {
5367       gdk_window_move_resize (clist->title_window,
5368                               clist->column_title_area.x,
5369                               clist->column_title_area.y,
5370                               clist->column_title_area.width,
5371                               clist->column_title_area.height);
5372     }
5373   
5374   /* column button allocation */
5375   size_allocate_columns (clist, FALSE);
5376   size_allocate_title_buttons (clist);
5377
5378   adjust_adjustments (clist, TRUE);
5379 }
5380
5381 /* GTKCONTAINER
5382  *   gtk_clist_forall
5383  */
5384 static void
5385 gtk_clist_forall (GtkContainer *container,
5386                   gboolean      include_internals,
5387                   GtkCallback   callback,
5388                   gpointer      callback_data)
5389 {
5390   GtkCList *clist;
5391   guint i;
5392
5393   g_return_if_fail (container != NULL);
5394   g_return_if_fail (GTK_IS_CLIST (container));
5395   g_return_if_fail (callback != NULL);
5396
5397   if (!include_internals)
5398     return;
5399
5400   clist = GTK_CLIST (container);
5401       
5402   /* callback for the column buttons */
5403   for (i = 0; i < clist->columns; i++)
5404     if (clist->column[i].button)
5405       (*callback) (clist->column[i].button, callback_data);
5406 }
5407
5408 /* PRIVATE DRAWING FUNCTIONS
5409  *   get_cell_style
5410  *   draw_cell_pixmap
5411  *   draw_row
5412  *   draw_rows
5413  *   draw_xor_line
5414  *   clist_refresh
5415  */
5416 static void
5417 get_cell_style (GtkCList     *clist,
5418                 GtkCListRow  *clist_row,
5419                 gint          state,
5420                 gint          column,
5421                 GtkStyle    **style,
5422                 GdkGC       **fg_gc,
5423                 GdkGC       **bg_gc)
5424 {
5425   gint fg_state;
5426
5427   if ((state == GTK_STATE_PRELIGHT) &&
5428       (GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
5429     fg_state = GTK_STATE_INSENSITIVE;
5430   else
5431     fg_state = state;
5432
5433   if (clist_row->cell[column].style)
5434     {
5435       if (style)
5436         *style = clist_row->cell[column].style;
5437       if (fg_gc)
5438         *fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
5439       if (bg_gc)
5440         *bg_gc = clist_row->cell[column].style->bg_gc[state];
5441     }
5442   else if (clist_row->style)
5443     {
5444       if (style)
5445         *style = clist_row->style;
5446       if (fg_gc)
5447         *fg_gc = clist_row->style->fg_gc[fg_state];
5448       if (bg_gc)
5449         *bg_gc = clist_row->style->bg_gc[state];
5450     }
5451   else
5452     {
5453       if (style)
5454         *style = GTK_WIDGET (clist)->style;
5455       if (fg_gc)
5456         *fg_gc = GTK_WIDGET (clist)->style->fg_gc[fg_state];
5457       if (bg_gc)
5458         *bg_gc = GTK_WIDGET (clist)->style->bg_gc[state];
5459
5460       if (state != GTK_STATE_SELECTED)
5461         {
5462           if (fg_gc && clist_row->fg_set)
5463             *fg_gc = clist->fg_gc;
5464           if (bg_gc && clist_row->bg_set)
5465             *bg_gc = clist->bg_gc;
5466         }
5467     }
5468 }
5469
5470 static gint
5471 draw_cell_pixmap (GdkWindow    *window,
5472                   GdkRectangle *clip_rectangle,
5473                   GdkGC        *fg_gc,
5474                   GdkPixmap    *pixmap,
5475                   GdkBitmap    *mask,
5476                   gint          x,
5477                   gint          y,
5478                   gint          width,
5479                   gint          height)
5480 {
5481   gint xsrc = 0;
5482   gint ysrc = 0;
5483
5484   if (mask)
5485     {
5486       gdk_gc_set_clip_mask (fg_gc, mask);
5487       gdk_gc_set_clip_origin (fg_gc, x, y);
5488     }
5489
5490   if (x < clip_rectangle->x)
5491     {
5492       xsrc = clip_rectangle->x - x;
5493       width -= xsrc;
5494       x = clip_rectangle->x;
5495     }
5496   if (x + width > clip_rectangle->x + clip_rectangle->width)
5497     width = clip_rectangle->x + clip_rectangle->width - x;
5498
5499   if (y < clip_rectangle->y)
5500     {
5501       ysrc = clip_rectangle->y - y;
5502       height -= ysrc;
5503       y = clip_rectangle->y;
5504     }
5505   if (y + height > clip_rectangle->y + clip_rectangle->height)
5506     height = clip_rectangle->y + clip_rectangle->height - y;
5507
5508   gdk_draw_pixmap (window, fg_gc, pixmap, xsrc, ysrc, x, y, width, height);
5509   gdk_gc_set_clip_origin (fg_gc, 0, 0);
5510
5511   return x + MAX (width, 0);
5512 }
5513
5514 static void
5515 draw_row (GtkCList     *clist,
5516           GdkRectangle *area,
5517           gint          row,
5518           GtkCListRow  *clist_row)
5519 {
5520   GtkWidget *widget;
5521   GdkRectangle *rect;
5522   GdkRectangle row_rectangle;
5523   GdkRectangle cell_rectangle;
5524   GdkRectangle clip_rectangle;
5525   GdkRectangle intersect_rectangle;
5526   gint state;
5527   gint i;
5528
5529   g_return_if_fail (clist != NULL);
5530
5531   /* bail now if we arn't drawable yet */
5532   if (!GTK_WIDGET_DRAWABLE (clist) || row < 0 || row >= clist->rows)
5533     return;
5534
5535   widget = GTK_WIDGET (clist);
5536
5537   /* if the function is passed the pointer to the row instead of null,
5538    * it avoids this expensive lookup */
5539   if (!clist_row)
5540     clist_row = (g_list_nth (clist->row_list, row))->data;
5541
5542   /* rectangle of the entire row */
5543   row_rectangle.x = 0;
5544   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
5545   row_rectangle.width = clist->clist_window_width;
5546   row_rectangle.height = clist->row_height;
5547
5548   /* rectangle of the cell spacing above the row */
5549   cell_rectangle.x = 0;
5550   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
5551   cell_rectangle.width = row_rectangle.width;
5552   cell_rectangle.height = CELL_SPACING;
5553
5554   /* rectangle used to clip drawing operations, it's y and height
5555    * positions only need to be set once, so we set them once here. 
5556    * the x and width are set withing the drawing loop below once per
5557    * column */
5558   clip_rectangle.y = row_rectangle.y;
5559   clip_rectangle.height = row_rectangle.height;
5560
5561   if (clist_row->state == GTK_STATE_NORMAL)
5562     {
5563       state = GTK_STATE_PRELIGHT;
5564       if (clist_row->fg_set)
5565         gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
5566       if (clist_row->bg_set)
5567         gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
5568     }
5569   else
5570     state = clist_row->state;
5571
5572   /* draw the cell borders and background */
5573   if (area)
5574     {
5575       rect = &intersect_rectangle;
5576       if (gdk_rectangle_intersect (area, &cell_rectangle,
5577                                    &intersect_rectangle))
5578         gdk_draw_rectangle (clist->clist_window,
5579                             widget->style->base_gc[GTK_STATE_NORMAL],
5580                             TRUE,
5581                             intersect_rectangle.x,
5582                             intersect_rectangle.y,
5583                             intersect_rectangle.width,
5584                             intersect_rectangle.height);
5585
5586       /* the last row has to clear it's bottom cell spacing too */
5587       if (clist_row == clist->row_list_end->data)
5588         {
5589           cell_rectangle.y += clist->row_height + CELL_SPACING;
5590
5591           if (gdk_rectangle_intersect (area, &cell_rectangle,
5592                                        &intersect_rectangle))
5593             gdk_draw_rectangle (clist->clist_window,
5594                                 widget->style->base_gc[GTK_STATE_NORMAL],
5595                                 TRUE,
5596                                 intersect_rectangle.x,
5597                                 intersect_rectangle.y,
5598                                 intersect_rectangle.width,
5599                                 intersect_rectangle.height);
5600         }
5601
5602       if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
5603         return;
5604
5605     }
5606   else
5607     {
5608       rect = &clip_rectangle;
5609       gdk_draw_rectangle (clist->clist_window,
5610                           widget->style->base_gc[GTK_STATE_NORMAL],
5611                           TRUE,
5612                           cell_rectangle.x,
5613                           cell_rectangle.y,
5614                           cell_rectangle.width,
5615                           cell_rectangle.height);
5616
5617       /* the last row has to clear it's bottom cell spacing too */
5618       if (clist_row == clist->row_list_end->data)
5619         {
5620           cell_rectangle.y += clist->row_height + CELL_SPACING;
5621
5622           gdk_draw_rectangle (clist->clist_window,
5623                               widget->style->base_gc[GTK_STATE_NORMAL],
5624                               TRUE,
5625                               cell_rectangle.x,
5626                               cell_rectangle.y,
5627                               cell_rectangle.width,
5628                               cell_rectangle.height);     
5629         }         
5630     }
5631   
5632   /* iterate and draw all the columns (row cells) and draw their contents */
5633   for (i = 0; i < clist->columns; i++)
5634     {
5635       GtkStyle *style;
5636       GdkGC *fg_gc;
5637       GdkGC *bg_gc;
5638
5639       gint width;
5640       gint height;
5641       gint pixmap_width;
5642       gint offset = 0;
5643       gint row_center_offset;
5644
5645       if (!clist->column[i].visible)
5646         continue;
5647
5648       get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
5649
5650       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
5651       clip_rectangle.width = clist->column[i].area.width;
5652
5653       /* calculate clipping region clipping region */
5654       clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
5655       clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
5656                                (i + 1 == clist->columns) * CELL_SPACING);
5657       
5658       if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
5659                                             &intersect_rectangle))
5660         continue;
5661
5662       gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
5663                           rect->x, rect->y, rect->width, rect->height);
5664
5665       clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
5666       clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
5667                                (i + 1 == clist->columns) * CELL_SPACING);
5668
5669       /* calculate real width for column justification */
5670       pixmap_width = 0;
5671       offset = 0;
5672       switch (clist_row->cell[i].type)
5673         {
5674         case GTK_CELL_TEXT:
5675           width = gdk_string_width (style->font,
5676                                     GTK_CELL_TEXT (clist_row->cell[i])->text);
5677           break;
5678         case GTK_CELL_PIXMAP:
5679           gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5680                                &pixmap_width, &height);
5681           width = pixmap_width;
5682           break;
5683         case GTK_CELL_PIXTEXT:
5684           gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5685                                &pixmap_width, &height);
5686           width = (pixmap_width +
5687                    GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing +
5688                    gdk_string_width (style->font,
5689                                      GTK_CELL_PIXTEXT
5690                                      (clist_row->cell[i])->text));
5691           break;
5692         default:
5693           continue;
5694           break;
5695         }
5696
5697       switch (clist->column[i].justification)
5698         {
5699         case GTK_JUSTIFY_LEFT:
5700           offset = clip_rectangle.x + clist_row->cell[i].horizontal;
5701           break;
5702         case GTK_JUSTIFY_RIGHT:
5703           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5704                     clip_rectangle.width - width);
5705           break;
5706         case GTK_JUSTIFY_CENTER:
5707         case GTK_JUSTIFY_FILL:
5708           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5709                     (clip_rectangle.width / 2) - (width / 2));
5710           break;
5711         };
5712
5713       /* Draw Text and/or Pixmap */
5714       switch (clist_row->cell[i].type)
5715         {
5716         case GTK_CELL_PIXMAP:
5717           draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5718                             GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5719                             GTK_CELL_PIXMAP (clist_row->cell[i])->mask,
5720                             offset,
5721                             clip_rectangle.y + clist_row->cell[i].vertical +
5722                             (clip_rectangle.height - height) / 2,
5723                             pixmap_width, height);
5724           break;
5725         case GTK_CELL_PIXTEXT:
5726           offset =
5727             draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5728                               GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5729                               GTK_CELL_PIXTEXT (clist_row->cell[i])->mask,
5730                               offset,
5731                               clip_rectangle.y + clist_row->cell[i].vertical+
5732                               (clip_rectangle.height - height) / 2,
5733                               pixmap_width, height);
5734           offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
5735         case GTK_CELL_TEXT:
5736           if (style != GTK_WIDGET (clist)->style)
5737             row_center_offset = (((clist->row_height - style->font->ascent -
5738                                   style->font->descent - 1) / 2) + 1.5 +
5739                                  style->font->ascent);
5740           else
5741             row_center_offset = clist->row_center_offset;
5742
5743           gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
5744           gdk_draw_string (clist->clist_window, style->font, fg_gc,
5745                            offset,
5746                            row_rectangle.y + row_center_offset + 
5747                            clist_row->cell[i].vertical,
5748                            (clist_row->cell[i].type == GTK_CELL_PIXTEXT) ?
5749                            GTK_CELL_PIXTEXT (clist_row->cell[i])->text :
5750                            GTK_CELL_TEXT (clist_row->cell[i])->text);
5751           gdk_gc_set_clip_rectangle (fg_gc, NULL);
5752           break;
5753         default:
5754           break;
5755         }
5756     }
5757
5758   /* draw focus rectangle */
5759   if (clist->focus_row == row && GTK_WIDGET_HAS_FOCUS (widget))
5760     {
5761       if (!area)
5762         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5763                             row_rectangle.x, row_rectangle.y,
5764                             row_rectangle.width - 1, row_rectangle.height - 1);
5765       else if (gdk_rectangle_intersect (area, &row_rectangle,
5766                                         &intersect_rectangle))
5767         {
5768           gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
5769           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5770                               row_rectangle.x, row_rectangle.y,
5771                               row_rectangle.width - 1,
5772                               row_rectangle.height - 1);
5773           gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
5774         }
5775     }
5776 }
5777
5778 static void
5779 draw_rows (GtkCList     *clist,
5780            GdkRectangle *area)
5781 {
5782   GList *list;
5783   GtkCListRow *clist_row;
5784   gint i;
5785   gint first_row;
5786   gint last_row;
5787
5788   g_return_if_fail (clist != NULL);
5789   g_return_if_fail (GTK_IS_CLIST (clist));
5790
5791   if (clist->row_height == 0 ||
5792       !GTK_WIDGET_DRAWABLE (clist))
5793     return;
5794
5795   if (area)
5796     {
5797       first_row = ROW_FROM_YPIXEL (clist, area->y);
5798       last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
5799     }
5800   else
5801     {
5802       first_row = ROW_FROM_YPIXEL (clist, 0);
5803       last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
5804     }
5805
5806   /* this is a small special case which exposes the bottom cell line
5807    * on the last row -- it might go away if I change the way the cell
5808    * spacings are drawn
5809    */
5810   if (clist->rows == first_row)
5811     first_row--;
5812
5813   list = g_list_nth (clist->row_list, first_row);
5814   i = first_row;
5815   while (list)
5816     {
5817       clist_row = list->data;
5818       list = list->next;
5819
5820       if (i > last_row)
5821         return;
5822
5823       GTK_CLIST_CLASS_FW (clist)->draw_row (clist, area, i, clist_row);
5824       i++;
5825     }
5826
5827   if (!area)
5828     gdk_window_clear_area (clist->clist_window,
5829                            0, ROW_TOP_YPIXEL (clist, i), -1, -1);
5830 }
5831
5832 static void                          
5833 draw_xor_line (GtkCList *clist)
5834 {
5835   GtkWidget *widget;
5836
5837   g_return_if_fail (clist != NULL);
5838
5839   widget = GTK_WIDGET (clist);
5840
5841   gdk_draw_line (widget->window, clist->xor_gc,
5842                  clist->x_drag,
5843                  widget->style->klass->ythickness,
5844                  clist->x_drag,
5845                  clist->column_title_area.height +
5846                  clist->clist_window_height + 1);
5847 }
5848
5849 static void
5850 clist_refresh (GtkCList *clist)
5851 {
5852   g_return_if_fail (clist != NULL);
5853   g_return_if_fail (GTK_IS_CLIST (clist));
5854   
5855   if (CLIST_UNFROZEN (clist))
5856     { 
5857       adjust_adjustments (clist, FALSE);
5858       draw_rows (clist, NULL);
5859     }
5860 }
5861
5862 /* get cell from coordinates
5863  *   get_selection_info
5864  *   gtk_clist_get_selection_info
5865  */
5866 static gint
5867 get_selection_info (GtkCList *clist,
5868                     gint      x,
5869                     gint      y,
5870                     gint     *row,
5871                     gint     *column)
5872 {
5873   gint trow, tcol;
5874
5875   g_return_val_if_fail (clist != NULL, 0);
5876   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
5877
5878   /* bounds checking, return false if the user clicked 
5879    * on a blank area */
5880   trow = ROW_FROM_YPIXEL (clist, y);
5881   if (trow >= clist->rows)
5882     return 0;
5883
5884   if (row)
5885     *row = trow;
5886
5887   tcol = COLUMN_FROM_XPIXEL (clist, x);
5888   if (tcol >= clist->columns)
5889     return 0;
5890
5891   if (column)
5892     *column = tcol;
5893
5894   return 1;
5895 }
5896
5897 gint
5898 gtk_clist_get_selection_info (GtkCList *clist, 
5899                               gint      x, 
5900                               gint      y, 
5901                               gint     *row, 
5902                               gint     *column)
5903 {
5904   g_return_val_if_fail (clist != NULL, 0);
5905   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
5906   return get_selection_info (clist, x, y, row, column);
5907 }
5908
5909 /* 
5910  * SCROLLBARS
5911  *
5912  * functions:
5913  *   adjust_adjustments
5914  *   vadjustment_changed
5915  *   hadjustment_changed
5916  *   vadjustment_value_changed
5917  *   hadjustment_value_changed 
5918  *   check_exposures
5919  */
5920 static void
5921 adjust_adjustments (GtkCList *clist,
5922                     gboolean  block_resize)
5923 {
5924   if (clist->vadjustment)
5925     {
5926       clist->vadjustment->page_size = clist->clist_window_height;
5927       clist->vadjustment->page_increment = clist->clist_window_height / 2;
5928       clist->vadjustment->step_increment = clist->row_height;
5929       clist->vadjustment->lower = 0;
5930       clist->vadjustment->upper = LIST_HEIGHT (clist);
5931
5932       if (clist->clist_window_height - clist->voffset > LIST_HEIGHT (clist))
5933         {
5934           clist->vadjustment->value = MAX (0, (LIST_HEIGHT (clist) -
5935                                                clist->clist_window_height));
5936           gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment),
5937                                    "value_changed");
5938         }
5939       gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment), "changed");
5940     }
5941
5942   if (clist->hadjustment)
5943     {
5944       clist->hadjustment->page_size = clist->clist_window_width;
5945       clist->hadjustment->page_increment = clist->clist_window_width / 2;
5946       clist->hadjustment->step_increment = 10;
5947       clist->hadjustment->lower = 0;
5948       clist->hadjustment->upper = LIST_WIDTH (clist);
5949
5950       if (clist->clist_window_width - clist->hoffset > LIST_WIDTH (clist))
5951         {
5952           clist->hadjustment->value = MAX (0, (LIST_WIDTH (clist) -
5953                                                clist->clist_window_width));
5954           gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment),
5955                                    "value_changed");
5956         }
5957       gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment), "changed");
5958     }
5959
5960   if (!block_resize && (!clist->vadjustment || !clist->hadjustment))
5961     {
5962       GtkWidget *widget;
5963
5964       widget = GTK_WIDGET (clist);
5965       gtk_widget_size_request (widget, &widget->requisition);
5966
5967       if ((!clist->hadjustment &&
5968            widget->requisition.width != widget->allocation.width) ||
5969           (!clist->vadjustment &&
5970            widget->requisition.height != widget->allocation.height))
5971         gtk_widget_queue_resize (widget);
5972     }
5973 }
5974
5975 static void
5976 vadjustment_changed (GtkAdjustment *adjustment,
5977                      gpointer       data)
5978 {
5979   GtkCList *clist;
5980
5981   g_return_if_fail (adjustment != NULL);
5982   g_return_if_fail (data != NULL);
5983
5984   clist = GTK_CLIST (data);
5985 }
5986
5987 static void
5988 hadjustment_changed (GtkAdjustment *adjustment,
5989                      gpointer       data)
5990 {
5991   GtkCList *clist;
5992
5993   g_return_if_fail (adjustment != NULL);
5994   g_return_if_fail (data != NULL);
5995
5996   clist = GTK_CLIST (data);
5997 }
5998
5999 static void
6000 vadjustment_value_changed (GtkAdjustment *adjustment,
6001                            gpointer       data)
6002 {
6003   GtkCList *clist;
6004   GdkRectangle area;
6005   gint diff, value;
6006
6007   g_return_if_fail (adjustment != NULL);
6008   g_return_if_fail (data != NULL);
6009   g_return_if_fail (GTK_IS_CLIST (data));
6010
6011   clist = GTK_CLIST (data);
6012
6013   if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->vadjustment)
6014     return;
6015
6016   value = adjustment->value;
6017
6018   if (value > -clist->voffset)
6019     {
6020       /* scroll down */
6021       diff = value + clist->voffset;
6022
6023       /* we have to re-draw the whole screen here... */
6024       if (diff >= clist->clist_window_height)
6025         {
6026           clist->voffset = -value;
6027           draw_rows (clist, NULL);
6028           return;
6029         }
6030
6031       if ((diff != 0) && (diff != clist->clist_window_height))
6032         gdk_window_copy_area (clist->clist_window, clist->fg_gc,
6033                               0, 0, clist->clist_window, 0, diff,
6034                               clist->clist_window_width,
6035                               clist->clist_window_height - diff);
6036
6037       area.x = 0;
6038       area.y = clist->clist_window_height - diff;
6039       area.width = clist->clist_window_width;
6040       area.height = diff;
6041     }
6042   else
6043     {
6044       /* scroll up */
6045       diff = -clist->voffset - value;
6046
6047       /* we have to re-draw the whole screen here... */
6048       if (diff >= clist->clist_window_height)
6049         {
6050           clist->voffset = -value;
6051           draw_rows (clist, NULL);
6052           return;
6053         }
6054
6055       if ((diff != 0) && (diff != clist->clist_window_height))
6056         gdk_window_copy_area (clist->clist_window, clist->fg_gc,
6057                               0, diff, clist->clist_window, 0, 0,
6058                               clist->clist_window_width,
6059                               clist->clist_window_height - diff);
6060
6061       area.x = 0;
6062       area.y = 0;
6063       area.width = clist->clist_window_width;
6064       area.height = diff;
6065     }
6066
6067   clist->voffset = -value;
6068   if ((diff != 0) && (diff != clist->clist_window_height))
6069     check_exposures (clist);
6070
6071   draw_rows (clist, &area);
6072 }
6073
6074 static void
6075 hadjustment_value_changed (GtkAdjustment *adjustment,
6076                            gpointer       data)
6077 {
6078   GtkCList *clist;
6079   GdkRectangle area;
6080   gint i;
6081   gint y = 0;
6082   gint diff = 0;
6083   gint value;
6084
6085   g_return_if_fail (adjustment != NULL);
6086   g_return_if_fail (data != NULL);
6087   g_return_if_fail (GTK_IS_CLIST (data));
6088
6089   clist = GTK_CLIST (data);
6090
6091   if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->hadjustment)
6092     return;
6093
6094   value = adjustment->value;
6095
6096   /* move the column buttons and resize windows */
6097   for (i = 0; i < clist->columns; i++)
6098     {
6099       if (clist->column[i].button)
6100         {
6101           clist->column[i].button->allocation.x -= value + clist->hoffset;
6102           
6103           if (clist->column[i].button->window)
6104             {
6105               gdk_window_move (clist->column[i].button->window,
6106                                clist->column[i].button->allocation.x,
6107                                clist->column[i].button->allocation.y);
6108               
6109               if (clist->column[i].window)
6110                 gdk_window_move (clist->column[i].window,
6111                                  clist->column[i].button->allocation.x +
6112                                  clist->column[i].button->allocation.width - 
6113                                  (DRAG_WIDTH / 2), 0); 
6114             }
6115         }
6116     }
6117
6118   if (value > -clist->hoffset)
6119     {
6120       /* scroll right */
6121       diff = value + clist->hoffset;
6122       
6123       clist->hoffset = -value;
6124       
6125       /* we have to re-draw the whole screen here... */
6126       if (diff >= clist->clist_window_width)
6127         {
6128           draw_rows (clist, NULL);
6129           return;
6130         }
6131
6132       if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
6133           GTK_CLIST_ADD_MODE (clist))
6134         {
6135           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6136               
6137           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
6138                               clist->clist_window_width - 1,
6139                               clist->row_height - 1);
6140         }
6141       gdk_window_copy_area (clist->clist_window,
6142                             clist->fg_gc,
6143                             0, 0,
6144                             clist->clist_window,
6145                             diff,
6146                             0,
6147                             clist->clist_window_width - diff,
6148                             clist->clist_window_height);
6149
6150       area.x = clist->clist_window_width - diff;
6151     }
6152   else
6153     {
6154       /* scroll left */
6155       if (!(diff = -clist->hoffset - value))
6156         return;
6157
6158       clist->hoffset = -value;
6159       
6160       /* we have to re-draw the whole screen here... */
6161       if (diff >= clist->clist_window_width)
6162         {
6163           draw_rows (clist, NULL);
6164           return;
6165         }
6166       
6167       if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist) &&
6168           GTK_CLIST_ADD_MODE (clist))
6169         {
6170           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6171           
6172           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
6173                               clist->clist_window_width - 1,
6174                               clist->row_height - 1);
6175         }
6176
6177       gdk_window_copy_area (clist->clist_window,
6178                             clist->fg_gc,
6179                             diff, 0,
6180                             clist->clist_window,
6181                             0,
6182                             0,
6183                             clist->clist_window_width - diff,
6184                             clist->clist_window_height);
6185           
6186       area.x = 0;
6187     }
6188
6189   area.y = 0;
6190   area.width = diff;
6191   area.height = clist->clist_window_height;
6192
6193   check_exposures (clist);
6194
6195   if (GTK_WIDGET_HAS_FOCUS (clist) && !GTK_CLIST_CHILD_HAS_FOCUS (clist))
6196     {
6197       if (GTK_CLIST_ADD_MODE (clist))
6198         {
6199           gint focus_row;
6200           
6201           focus_row = clist->focus_row;
6202           clist->focus_row = -1;
6203           draw_rows (clist, &area);
6204           clist->focus_row = focus_row;
6205           
6206           gdk_draw_rectangle (clist->clist_window, clist->xor_gc,
6207                               FALSE, 0, y, clist->clist_window_width - 1,
6208                               clist->row_height - 1);
6209           return;
6210         }
6211       else
6212         {
6213           gint x0;
6214           gint x1;
6215           
6216           if (area.x == 0)
6217             {
6218               x0 = clist->clist_window_width - 1;
6219               x1 = diff;
6220             }
6221           else
6222             {
6223               x0 = 0;
6224               x1 = area.x - 1;
6225             }
6226           
6227           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6228           gdk_draw_line (clist->clist_window, clist->xor_gc,
6229                          x0, y + 1, x0, y + clist->row_height - 2);
6230           gdk_draw_line (clist->clist_window, clist->xor_gc,
6231                          x1, y + 1, x1, y + clist->row_height - 2);
6232           
6233         }
6234     }
6235   draw_rows (clist, &area);
6236 }
6237
6238 static void
6239 check_exposures (GtkCList *clist)
6240 {
6241   GdkEvent *event;
6242
6243   if (!GTK_WIDGET_REALIZED (clist))
6244     return;
6245
6246   /* Make sure graphics expose events are processed before scrolling
6247    * again */
6248   while ((event = gdk_event_get_graphics_expose (clist->clist_window)) != NULL)
6249     {
6250       gtk_widget_event (GTK_WIDGET (clist), event);
6251       if (event->expose.count == 0)
6252         {
6253           gdk_event_free (event);
6254           break;
6255         }
6256       gdk_event_free (event);
6257     }
6258 }
6259
6260 /* 
6261  * Memory Allocation/Distruction Routines for GtkCList stuctures
6262  *
6263  * functions:
6264  *   columns_new
6265  *   column_title_new
6266  *   columns_delete
6267  *   row_new
6268  *   row_delete
6269  */
6270 static GtkCListColumn *
6271 columns_new (GtkCList *clist)
6272 {
6273   GtkCListColumn *column;
6274   gint i;
6275
6276   column = g_new (GtkCListColumn, clist->columns);
6277
6278   for (i = 0; i < clist->columns; i++)
6279     {
6280       column[i].area.x = 0;
6281       column[i].area.y = 0;
6282       column[i].area.width = 0;
6283       column[i].area.height = 0;
6284       column[i].title = NULL;
6285       column[i].button = NULL;
6286       column[i].window = NULL;
6287       column[i].width = 0;
6288       column[i].min_width = -1;
6289       column[i].max_width = -1;
6290       column[i].visible = TRUE;
6291       column[i].width_set = FALSE;
6292       column[i].resizeable = TRUE;
6293       column[i].auto_resize = FALSE;
6294       column[i].justification = GTK_JUSTIFY_LEFT;
6295     }
6296
6297   return column;
6298 }
6299
6300 static void
6301 column_title_new (GtkCList    *clist,
6302                   gint         column,
6303                   const gchar *title)
6304 {
6305   if (clist->column[column].title)
6306     g_free (clist->column[column].title);
6307
6308   clist->column[column].title = g_strdup (title);
6309 }
6310
6311 static void
6312 columns_delete (GtkCList *clist)
6313 {
6314   gint i;
6315
6316   for (i = 0; i < clist->columns; i++)
6317     if (clist->column[i].title)
6318       g_free (clist->column[i].title);
6319       
6320   g_free (clist->column);
6321 }
6322
6323 static GtkCListRow *
6324 row_new (GtkCList *clist)
6325 {
6326   int i;
6327   GtkCListRow *clist_row;
6328
6329   clist_row = g_chunk_new (GtkCListRow, clist->row_mem_chunk);
6330   clist_row->cell = g_chunk_new (GtkCell, clist->cell_mem_chunk);
6331
6332   for (i = 0; i < clist->columns; i++)
6333     {
6334       clist_row->cell[i].type = GTK_CELL_EMPTY;
6335       clist_row->cell[i].vertical = 0;
6336       clist_row->cell[i].horizontal = 0;
6337       clist_row->cell[i].style = NULL;
6338     }
6339
6340   clist_row->fg_set = FALSE;
6341   clist_row->bg_set = FALSE;
6342   clist_row->style = NULL;
6343   clist_row->selectable = TRUE;
6344   clist_row->state = GTK_STATE_NORMAL;
6345   clist_row->data = NULL;
6346   clist_row->destroy = NULL;
6347
6348   return clist_row;
6349 }
6350
6351 static void
6352 row_delete (GtkCList    *clist,
6353             GtkCListRow *clist_row)
6354 {
6355   gint i;
6356
6357   for (i = 0; i < clist->columns; i++)
6358     {
6359       GTK_CLIST_CLASS_FW (clist)->set_cell_contents
6360         (clist, clist_row, i, GTK_CELL_EMPTY, NULL, 0, NULL, NULL);
6361       if (clist_row->cell[i].style)
6362         {
6363           if (GTK_WIDGET_REALIZED (clist))
6364             gtk_style_detach (clist_row->cell[i].style);
6365           gtk_style_unref (clist_row->cell[i].style);
6366         }
6367     }
6368
6369   if (clist_row->style)
6370     {
6371       if (GTK_WIDGET_REALIZED (clist))
6372         gtk_style_detach (clist_row->style);
6373       gtk_style_unref (clist_row->style);
6374     }
6375
6376   if (clist_row->destroy)
6377     clist_row->destroy (clist_row->data);
6378
6379   g_mem_chunk_free (clist->cell_mem_chunk, clist_row->cell);
6380   g_mem_chunk_free (clist->row_mem_chunk, clist_row);
6381 }
6382
6383 /* FOCUS FUNCTIONS
6384  *   gtk_clist_focus
6385  *   gtk_clist_draw_focus
6386  *   gtk_clist_focus_in
6387  *   gtk_clist_focus_out
6388  *   gtk_clist_set_focus_child
6389  *   title_focus
6390  */
6391 static gint
6392 gtk_clist_focus (GtkContainer     *container,
6393                  GtkDirectionType  direction)
6394 {
6395   GtkCList *clist;
6396   GtkWidget *focus_child;
6397   gint old_row;
6398
6399   g_return_val_if_fail (container != NULL, FALSE);
6400   g_return_val_if_fail (GTK_IS_CLIST (container), FALSE);
6401
6402   if (!GTK_WIDGET_SENSITIVE (container))
6403     return FALSE;
6404   
6405   clist = GTK_CLIST (container);
6406   focus_child = container->focus_child;
6407   old_row = clist->focus_row;
6408
6409   switch (direction)
6410     {
6411     case GTK_DIR_LEFT:
6412     case GTK_DIR_RIGHT:
6413       if (GTK_CLIST_CHILD_HAS_FOCUS (clist))
6414         {
6415           if (title_focus (clist, direction))
6416             return TRUE;
6417           gtk_container_set_focus_child (container, NULL);
6418           return FALSE;
6419          }
6420       gtk_widget_grab_focus (GTK_WIDGET (container));
6421       return TRUE;
6422     case GTK_DIR_DOWN:
6423     case GTK_DIR_TAB_FORWARD:
6424       if (GTK_CLIST_CHILD_HAS_FOCUS (clist))
6425         {
6426           gboolean tf = FALSE;
6427
6428           if (((focus_child && direction == GTK_DIR_DOWN) ||
6429                !(tf = title_focus (clist, GTK_DIR_TAB_FORWARD)))
6430               && clist->rows)
6431             {
6432               if (clist->focus_row < 0)
6433                 {
6434                   clist->focus_row = 0;
6435
6436                   if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6437                        clist->selection_mode == GTK_SELECTION_EXTENDED) &&
6438                       !clist->selection)
6439                     gtk_signal_emit (GTK_OBJECT (clist),
6440                                      clist_signals[SELECT_ROW],
6441                                      clist->focus_row, -1, NULL);
6442                 }
6443               gtk_widget_grab_focus (GTK_WIDGET (container));
6444               return TRUE;
6445             }
6446
6447           if (tf)
6448             return TRUE;
6449         }
6450       
6451       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
6452       break;
6453     case GTK_DIR_UP:
6454     case GTK_DIR_TAB_BACKWARD:
6455       if (!focus_child &&
6456           GTK_CLIST_CHILD_HAS_FOCUS (clist) && clist->rows)
6457         {
6458           if (clist->focus_row < 0)
6459             {
6460               clist->focus_row = 0;
6461               if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6462                    clist->selection_mode == GTK_SELECTION_EXTENDED) &&
6463                   !clist->selection)
6464                 gtk_signal_emit (GTK_OBJECT (clist),
6465                                  clist_signals[SELECT_ROW],
6466                                  clist->focus_row, -1, NULL);
6467             }
6468           gtk_widget_grab_focus (GTK_WIDGET (container));
6469           return TRUE;
6470         }
6471
6472       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
6473
6474       if (title_focus (clist, direction))
6475         return TRUE;
6476
6477       break;
6478     default:
6479       break;
6480     }
6481
6482   gtk_container_set_focus_child (container, NULL);
6483   return FALSE;
6484 }
6485
6486 static void
6487 gtk_clist_draw_focus (GtkWidget *widget)
6488 {
6489   GtkCList *clist;
6490
6491   g_return_if_fail (widget != NULL);
6492   g_return_if_fail (GTK_IS_CLIST (widget));
6493
6494   if (!GTK_WIDGET_DRAWABLE (widget))
6495     return;
6496
6497   clist = GTK_CLIST (widget);
6498   if (clist->focus_row >= 0)
6499     gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
6500                         0, ROW_TOP_YPIXEL(clist, clist->focus_row),
6501                         clist->clist_window_width - 1,
6502                         clist->row_height - 1);
6503 }
6504
6505 static gint
6506 gtk_clist_focus_in (GtkWidget     *widget,
6507                     GdkEventFocus *event)
6508 {
6509   GtkCList *clist;
6510
6511   g_return_val_if_fail (widget != NULL, FALSE);
6512   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
6513   g_return_val_if_fail (event != NULL, FALSE);
6514
6515   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
6516   GTK_CLIST_UNSET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
6517
6518   clist = GTK_CLIST (widget);
6519
6520   if (clist->selection_mode == GTK_SELECTION_BROWSE &&
6521       clist->selection == NULL && clist->focus_row > -1)
6522     {
6523       GList *list;
6524
6525       list = g_list_nth (clist->row_list, clist->focus_row);
6526       if (list && GTK_CLIST_ROW (list)->selectable)
6527         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6528                          clist->focus_row, -1, event);
6529       else
6530         gtk_widget_draw_focus (widget);
6531     }
6532   else
6533     gtk_widget_draw_focus (widget);
6534
6535   return FALSE;
6536 }
6537
6538 static gint
6539 gtk_clist_focus_out (GtkWidget     *widget,
6540                      GdkEventFocus *event)
6541 {
6542   GtkCList *clist;
6543
6544   g_return_val_if_fail (widget != NULL, FALSE);
6545   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
6546   g_return_val_if_fail (event != NULL, FALSE);
6547
6548   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
6549   GTK_CLIST_SET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
6550
6551   gtk_widget_draw_focus (widget);
6552   
6553   clist = GTK_CLIST (widget);
6554
6555   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_EXTENDED)
6556     GTK_CLIST_CLASS_FW (widget)->resync_selection (clist, (GdkEvent *) event);
6557
6558   return FALSE;
6559 }
6560
6561 static void
6562 gtk_clist_set_focus_child (GtkContainer *container,
6563                            GtkWidget    *child)
6564 {
6565   g_return_if_fail (container != NULL);
6566   g_return_if_fail (GTK_IS_CLIST (container));
6567
6568   if (child)
6569     {
6570       g_return_if_fail (GTK_IS_WIDGET (child));
6571       GTK_CLIST_SET_FLAG (container, CLIST_CHILD_HAS_FOCUS);
6572     }
6573
6574   parent_class->set_focus_child (container, child);
6575 }
6576
6577 static gboolean
6578 title_focus (GtkCList *clist,
6579              gint      dir)
6580 {
6581   GtkWidget *focus_child;
6582   gboolean return_val = FALSE;
6583   gint d = 1;
6584   gint i = 0;
6585   gint j;
6586
6587   if (!GTK_CLIST_SHOW_TITLES (clist))
6588     return FALSE;
6589
6590   focus_child = GTK_CONTAINER (clist)->focus_child;
6591   
6592   switch (dir)
6593     {
6594     case GTK_DIR_TAB_BACKWARD:
6595     case GTK_DIR_UP:
6596       if (!focus_child || !GTK_CLIST_CHILD_HAS_FOCUS (clist))
6597         {
6598           if (dir == GTK_DIR_UP)
6599             i = COLUMN_FROM_XPIXEL (clist, 0);
6600           else
6601             i = clist->columns - 1;
6602           focus_child = clist->column[i].button;
6603           dir = GTK_DIR_TAB_FORWARD;
6604         }
6605       else
6606         d = -1;
6607       break;
6608     case GTK_DIR_LEFT:
6609       d = -1;
6610       if (!focus_child)
6611         {
6612           i = clist->columns - 1;
6613           focus_child = clist->column[i].button;
6614         }
6615       break;
6616     case GTK_DIR_RIGHT:
6617       if (!focus_child)
6618         {
6619           i = 0;
6620           focus_child = clist->column[i].button;
6621         }
6622       break;
6623     }
6624
6625   if (focus_child)
6626     while (i < clist->columns)
6627       {
6628         if (clist->column[i].button == focus_child)
6629           {
6630             if (clist->column[i].button && 
6631                 GTK_WIDGET_VISIBLE (clist->column[i].button) &&
6632                 GTK_IS_CONTAINER (clist->column[i].button) &&
6633                 !GTK_WIDGET_HAS_FOCUS (clist->column[i].button))
6634               if (gtk_container_focus 
6635                   (GTK_CONTAINER (clist->column[i].button), dir))
6636                 {
6637                   return_val = TRUE;
6638                   i -= d;
6639                 }
6640             if (!return_val && dir == GTK_DIR_UP)
6641               return FALSE;
6642             i += d;
6643             break;
6644           }
6645         i++;
6646       }
6647
6648   j = i;
6649
6650   if (!return_val)
6651     while (j >= 0 && j < clist->columns)
6652       {
6653         if (clist->column[j].button &&
6654             GTK_WIDGET_VISIBLE (clist->column[j].button))
6655           {
6656             if (GTK_IS_CONTAINER (clist->column[j].button) &&
6657                 gtk_container_focus 
6658                 (GTK_CONTAINER (clist->column[j].button), dir))
6659               {
6660                 return_val = TRUE;
6661                 break;
6662               }
6663             else if (GTK_WIDGET_CAN_FOCUS (clist->column[j].button))
6664               {
6665                 gtk_widget_grab_focus (clist->column[j].button);
6666                 return_val = TRUE;
6667                 break;
6668               }
6669           }
6670         j += d;
6671       }
6672   
6673   if (return_val)
6674     {
6675       if (COLUMN_LEFT_XPIXEL (clist, j) < CELL_SPACING + COLUMN_INSET)
6676         gtk_clist_moveto (clist, -1, j, 0, 0);
6677       else if (COLUMN_LEFT_XPIXEL(clist, j) + clist->column[j].area.width >
6678                clist->clist_window_width)
6679         {
6680           if (j == clist->columns-1)
6681             gtk_clist_moveto (clist, -1, j, 0, 0);
6682           else
6683             gtk_clist_moveto (clist, -1, j, 0, 1);
6684         }
6685     }
6686   return return_val;
6687 }
6688
6689 /* SCROLLING FUNCTIONS
6690  *   move_focus_row
6691  *   scroll_horizontal
6692  *   scroll_vertical
6693  */
6694 static void
6695 move_focus_row (GtkCList      *clist,
6696                 GtkScrollType  scroll_type,
6697                 gfloat         position)
6698 {
6699   GtkWidget *widget;
6700
6701   g_return_if_fail (clist != 0);
6702   g_return_if_fail (GTK_IS_CLIST (clist));
6703
6704   widget = GTK_WIDGET (clist);
6705
6706   switch (scroll_type)
6707     {
6708     case GTK_SCROLL_STEP_BACKWARD:
6709       if (clist->focus_row <= 0)
6710         return;
6711       gtk_clist_draw_focus (widget);
6712       clist->focus_row--;
6713       gtk_clist_draw_focus (widget);
6714       break;
6715     case GTK_SCROLL_STEP_FORWARD:
6716       if (clist->focus_row >= clist->rows - 1)
6717         return;
6718       gtk_clist_draw_focus (widget);
6719       clist->focus_row++;
6720       gtk_clist_draw_focus (widget);
6721       break;
6722     case GTK_SCROLL_PAGE_BACKWARD:
6723       if (clist->focus_row <= 0)
6724         return;
6725       gtk_clist_draw_focus (widget);
6726       clist->focus_row = MAX (0, clist->focus_row -
6727                               (2 * clist->clist_window_height -
6728                                clist->row_height - CELL_SPACING) / 
6729                               (2 * (clist->row_height + CELL_SPACING)));
6730       gtk_clist_draw_focus (widget);
6731       break;
6732     case GTK_SCROLL_PAGE_FORWARD:
6733       if (clist->focus_row >= clist->rows - 1)
6734         return;
6735       gtk_clist_draw_focus (widget);
6736       clist->focus_row = MIN (clist->rows - 1, clist->focus_row + 
6737                               (2 * clist->clist_window_height -
6738                                clist->row_height - CELL_SPACING) / 
6739                               (2 * (clist->row_height + CELL_SPACING)));
6740       gtk_clist_draw_focus (widget);
6741       break;
6742     case GTK_SCROLL_JUMP:
6743       if (position >= 0 && position <= 1)
6744         {
6745           gtk_clist_draw_focus (widget);
6746           clist->focus_row = position * (clist->rows - 1);
6747           gtk_clist_draw_focus (widget);
6748         }
6749       break;
6750     default:
6751       break;
6752     }
6753 }
6754
6755 static void
6756 scroll_horizontal (GtkCList      *clist,
6757                    GtkScrollType  scroll_type,
6758                    gfloat         position)
6759 {
6760   gint column = 0;
6761
6762   g_return_if_fail (clist != 0);
6763   g_return_if_fail (GTK_IS_CLIST (clist));
6764
6765   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
6766     return;
6767
6768   switch (scroll_type)
6769     {
6770     case GTK_SCROLL_STEP_BACKWARD:
6771       column = COLUMN_FROM_XPIXEL (clist, 0);
6772       if (COLUMN_LEFT_XPIXEL (clist, column) - CELL_SPACING - COLUMN_INSET >= 0
6773           && column > 0)
6774         column--;
6775       break;
6776     case GTK_SCROLL_STEP_FORWARD:
6777       column =  COLUMN_FROM_XPIXEL (clist, clist->clist_window_width);
6778       if (column < 0)
6779         return;
6780       if (COLUMN_LEFT_XPIXEL (clist, column) +
6781           clist->column[column].area.width +
6782           CELL_SPACING + COLUMN_INSET - 1 <= clist->clist_window_width &&
6783           column < clist->columns - 1)
6784         column++;
6785       break;
6786     case GTK_SCROLL_PAGE_BACKWARD:
6787     case GTK_SCROLL_PAGE_FORWARD:
6788       return;
6789     case GTK_SCROLL_JUMP:
6790       if (position >= 0 && position <= 1)
6791         column = position * (clist->columns - 1);
6792       else
6793         return;
6794       break;
6795     default:
6796       break;
6797     }
6798
6799   if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET)
6800     gtk_clist_moveto (clist, -1, column, 0, 0);
6801   else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1
6802            + clist->column[column].area.width > clist->clist_window_width)
6803     {
6804       if (column == clist->columns - 1)
6805         gtk_clist_moveto (clist, -1, column, 0, 0);
6806       else
6807         gtk_clist_moveto (clist, -1, column, 0, 1);
6808     }
6809 }
6810
6811 static void
6812 scroll_vertical (GtkCList      *clist,
6813                  GtkScrollType  scroll_type,
6814                  gfloat         position)
6815 {
6816   gint old_focus_row;
6817
6818   g_return_if_fail (clist != NULL);
6819   g_return_if_fail (GTK_IS_CLIST (clist));
6820
6821   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
6822     return;
6823
6824   switch (clist->selection_mode)
6825     {
6826     case GTK_SELECTION_EXTENDED:
6827       if (clist->anchor >= 0)
6828         return;
6829     case GTK_SELECTION_BROWSE:
6830
6831       old_focus_row = clist->focus_row;
6832       move_focus_row (clist, scroll_type, position);
6833
6834       if (old_focus_row != clist->focus_row)
6835         {
6836           if (clist->selection_mode == GTK_SELECTION_BROWSE)
6837             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
6838                              old_focus_row, -1, NULL);
6839           else if (!GTK_CLIST_ADD_MODE (clist))
6840             {
6841               gtk_clist_unselect_all (clist);
6842               clist->undo_anchor = old_focus_row;
6843             }
6844         }
6845
6846       switch (gtk_clist_row_is_visible (clist, clist->focus_row))
6847         {
6848         case GTK_VISIBILITY_NONE:
6849           if (old_focus_row != clist->focus_row &&
6850               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
6851                 GTK_CLIST_ADD_MODE (clist)))
6852             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6853                              clist->focus_row, -1, NULL);
6854           switch (scroll_type)
6855             {
6856             case GTK_SCROLL_STEP_BACKWARD:
6857             case GTK_SCROLL_PAGE_BACKWARD:
6858               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6859               break;
6860             case GTK_SCROLL_STEP_FORWARD:
6861             case GTK_SCROLL_PAGE_FORWARD:
6862               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6863               break;
6864             case GTK_SCROLL_JUMP:
6865               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
6866               break;
6867             default:
6868               break;
6869             }
6870           break;
6871         case GTK_VISIBILITY_PARTIAL:
6872           switch (scroll_type)
6873             {
6874             case GTK_SCROLL_STEP_BACKWARD:
6875             case GTK_SCROLL_PAGE_BACKWARD:
6876               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6877               break;
6878             case GTK_SCROLL_STEP_FORWARD:
6879             case GTK_SCROLL_PAGE_FORWARD:
6880               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6881               break;
6882             case GTK_SCROLL_JUMP:
6883               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
6884               break;
6885             default:
6886               break;
6887             }
6888         default:
6889           if (old_focus_row != clist->focus_row &&
6890               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
6891                 GTK_CLIST_ADD_MODE (clist)))
6892             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6893                              clist->focus_row, -1, NULL);
6894           break;
6895         }
6896       break;
6897     default:
6898       move_focus_row (clist, scroll_type, position);
6899
6900       if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
6901           clist->clist_window_height)
6902         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6903       else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
6904         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6905       break;
6906     }
6907 }
6908
6909 /* PUBLIC SORTING FUNCTIONS
6910  * gtk_clist_sort
6911  * gtk_clist_set_compare_func
6912  * gtk_clist_set_auto_sort
6913  * gtk_clist_set_sort_type
6914  * gtk_clist_set_sort_column
6915  */
6916 void
6917 gtk_clist_sort (GtkCList *clist)
6918 {
6919   g_return_if_fail (clist != NULL);
6920   g_return_if_fail (GTK_IS_CLIST (clist));
6921
6922   GTK_CLIST_CLASS_FW (clist)->sort_list (clist);
6923 }
6924
6925 void
6926 gtk_clist_set_compare_func (GtkCList            *clist,
6927                             GtkCListCompareFunc  cmp_func)
6928 {
6929   g_return_if_fail (clist != NULL);
6930   g_return_if_fail (GTK_IS_CLIST (clist));
6931
6932   clist->compare = (cmp_func) ? cmp_func : default_compare;
6933 }
6934
6935 void       
6936 gtk_clist_set_auto_sort (GtkCList *clist,
6937                          gboolean  auto_sort)
6938 {
6939   g_return_if_fail (clist != NULL);
6940   g_return_if_fail (GTK_IS_CLIST (clist));
6941   
6942   if (GTK_CLIST_AUTO_SORT (clist) && !auto_sort)
6943     GTK_CLIST_UNSET_FLAG (clist, CLIST_AUTO_SORT);
6944   else if (!GTK_CLIST_AUTO_SORT (clist) && auto_sort)
6945     {
6946       GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_SORT);
6947       gtk_clist_sort (clist);
6948     }
6949 }
6950
6951 void       
6952 gtk_clist_set_sort_type (GtkCList    *clist,
6953                          GtkSortType  sort_type)
6954 {
6955   g_return_if_fail (clist != NULL);
6956   g_return_if_fail (GTK_IS_CLIST (clist));
6957   
6958   clist->sort_type = sort_type;
6959 }
6960
6961 void
6962 gtk_clist_set_sort_column (GtkCList *clist,
6963                            gint      column)
6964 {
6965   g_return_if_fail (clist != NULL);
6966   g_return_if_fail (GTK_IS_CLIST (clist));
6967
6968   if (column < 0 || column >= clist->columns)
6969     return;
6970
6971   clist->sort_column = column;
6972 }
6973
6974 /* PRIVATE SORTING FUNCTIONS
6975  *   default_compare
6976  *   real_sort_list
6977  *   gtk_clist_merge
6978  *   gtk_clist_mergesort
6979  */
6980 static gint
6981 default_compare (GtkCList      *clist,
6982                  gconstpointer  ptr1,
6983                  gconstpointer  ptr2)
6984 {
6985   char *text1 = NULL;
6986   char *text2 = NULL;
6987
6988   GtkCListRow *row1 = (GtkCListRow *) ptr1;
6989   GtkCListRow *row2 = (GtkCListRow *) ptr2;
6990
6991   switch (row1->cell[clist->sort_column].type)
6992     {
6993     case GTK_CELL_TEXT:
6994       text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
6995       break;
6996     case GTK_CELL_PIXTEXT:
6997       text1 = GTK_CELL_PIXTEXT (row1->cell[clist->sort_column])->text;
6998       break;
6999     default:
7000       break;
7001     }
7002  
7003   switch (row2->cell[clist->sort_column].type)
7004     {
7005     case GTK_CELL_TEXT:
7006       text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
7007       break;
7008     case GTK_CELL_PIXTEXT:
7009       text2 = GTK_CELL_PIXTEXT (row2->cell[clist->sort_column])->text;
7010       break;
7011     default:
7012       break;
7013     }
7014
7015   if (!text2)
7016     return (text1 != NULL);
7017
7018   if (!text1)
7019     return -1;
7020
7021   return strcmp (text1, text2);
7022 }
7023
7024 static void
7025 real_sort_list (GtkCList *clist)
7026 {
7027   GList *list;
7028   GList *work;
7029   gint i;
7030
7031   g_return_if_fail (clist != NULL);
7032   g_return_if_fail (GTK_IS_CLIST (clist));
7033
7034   if (clist->rows <= 1)
7035     return;
7036
7037   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
7038     return;
7039
7040   gtk_clist_freeze (clist);
7041
7042   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_EXTENDED)
7043     {
7044       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
7045       g_list_free (clist->undo_selection);
7046       g_list_free (clist->undo_unselection);
7047       clist->undo_selection = NULL;
7048       clist->undo_unselection = NULL;
7049     }
7050    
7051   clist->row_list = gtk_clist_mergesort (clist, clist->row_list, clist->rows);
7052
7053   work = clist->selection;
7054
7055   for (i = 0, list = clist->row_list; i < clist->rows; i++, list = list->next)
7056     {
7057       if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
7058         {
7059           work->data = GINT_TO_POINTER (i);
7060           work = work->next;
7061         }
7062       
7063       if (i == clist->rows - 1)
7064         clist->row_list_end = list;
7065     }
7066
7067   gtk_clist_thaw (clist);
7068 }
7069
7070 static GList *
7071 gtk_clist_merge (GtkCList *clist,
7072                  GList    *a,         /* first list to merge */
7073                  GList    *b)         /* second list to merge */
7074 {
7075   GList z = { 0 };                    /* auxiliary node */
7076   GList *c;
7077   gint cmp;
7078
7079   c = &z;
7080
7081   while (a || b)
7082     {
7083       if (a && !b)
7084         {
7085           c->next = a;
7086           a->prev = c;
7087           c = a;
7088           a = a->next;
7089           break;
7090         }
7091       else if (!a && b)
7092         {
7093           c->next = b;
7094           b->prev = c;
7095           c = b;
7096           b = b->next;
7097           break;
7098         }
7099       else /* a && b */
7100         {
7101           cmp = clist->compare (clist, GTK_CLIST_ROW (a), GTK_CLIST_ROW (b));
7102           if ((cmp >= 0 && clist->sort_type == GTK_SORT_DESCENDING) ||
7103               (cmp <= 0 && clist->sort_type == GTK_SORT_ASCENDING) ||
7104               (a && !b))
7105             {
7106               c->next = a;
7107               a->prev = c;
7108               c = a;
7109               a = a->next;
7110             }
7111           else
7112             {
7113               c->next = b;
7114               b->prev = c;
7115               c = b;
7116               b = b->next;
7117             }
7118         }
7119     }
7120
7121   return z.next;
7122 }
7123
7124 static GList *
7125 gtk_clist_mergesort (GtkCList *clist,
7126                      GList    *list,         /* the list to sort */
7127                      gint      num)          /* the list's length */
7128 {
7129   GList *half;
7130   gint i;
7131
7132   if (num == 1)
7133     {
7134       return list;
7135     }
7136   else
7137     {
7138       /* move "half" to the middle */
7139       half = list;
7140       for (i = 0; i < num / 2; i++)
7141         half = half->next;
7142
7143       /* cut the list in two */
7144       half->prev->next = NULL;
7145       half->prev = NULL;
7146
7147       /* recursively sort both lists */
7148       return gtk_clist_merge (clist,
7149                        gtk_clist_mergesort (clist, list, num / 2),
7150                        gtk_clist_mergesort (clist, half, num - num / 2));
7151     }
7152 }