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