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