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