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