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