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