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