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