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