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