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