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