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