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