]> Pileus Git - ~andy/gtk/blob - gtk/gtkclist.c
Check for features.h.
[~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       gdk_window_show (widget->window);
4619       gdk_window_show (clist->title_window);
4620       gdk_window_show (clist->clist_window);
4621
4622       /* map column buttons */
4623       for (i = 0; i < clist->columns; i++)
4624         if (clist->column[i].button &&
4625             GTK_WIDGET_VISIBLE (clist->column[i].button) &&
4626             !GTK_WIDGET_MAPPED (clist->column[i].button))
4627           gtk_widget_map (clist->column[i].button);
4628       
4629       /* map resize windows AFTER column buttons (above) */
4630       for (i = 0; i < clist->columns; i++)
4631         if (clist->column[i].window && clist->column[i].button)
4632           gdk_window_show (clist->column[i].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       if (clist->drag_button)
4859         return FALSE;
4860
4861       x = event->x;
4862       y = event->y;
4863
4864       if (get_selection_info (clist, x, y, &row, &column))
4865         {
4866           gint old_row = clist->focus_row;
4867
4868           if (clist->focus_row == -1)
4869             old_row = row;
4870
4871           if (event->type == GDK_BUTTON_PRESS)
4872             {
4873               GdkEventMask mask = ((1 << (4 + event->button)) |
4874                                    GDK_POINTER_MOTION_HINT_MASK |
4875                                    GDK_BUTTON_RELEASE_MASK);
4876
4877               clist->click_cell.row = row;
4878               clist->click_cell.column = column;
4879                   
4880               clist->drag_button = event->button;
4881               gdk_pointer_grab (clist->clist_window, FALSE, mask,
4882                                 NULL, NULL, event->time);
4883               gtk_grab_add (widget);
4884             }
4885           else
4886             {
4887               if (clist->click_cell.row >= 0 || clist->click_cell.column >= 0)
4888                 return FALSE;
4889
4890               clist->click_cell.row = -1;
4891               clist->click_cell.column = -1;
4892
4893               clist->drag_button = 0;
4894               remove_grab (clist);
4895             }
4896
4897           if (button_actions & GTK_BUTTON_SELECTS)
4898             {
4899               if (GTK_CLIST_ADD_MODE(clist))
4900                 {
4901                   GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
4902                   if (GTK_WIDGET_HAS_FOCUS(widget))
4903                     {
4904                       gtk_clist_draw_focus (widget);
4905                       gdk_gc_set_line_attributes (clist->xor_gc, 1,
4906                                                   GDK_LINE_SOLID, 0, 0);
4907                       clist->focus_row = row;
4908                       gtk_clist_draw_focus (widget);
4909                     }
4910                   else
4911                     {
4912                       gdk_gc_set_line_attributes (clist->xor_gc, 1,
4913                                                   GDK_LINE_SOLID, 0, 0);
4914                       clist->focus_row = row;
4915                     }
4916                 }
4917               else if (row != clist->focus_row)
4918                 {
4919                   if (GTK_WIDGET_HAS_FOCUS(widget))
4920                     {
4921                       gtk_clist_draw_focus (widget);
4922                       clist->focus_row = row;
4923                       gtk_clist_draw_focus (widget);
4924                     }
4925                   else
4926                     clist->focus_row = row;
4927                 }
4928             }
4929
4930           if (!GTK_WIDGET_HAS_FOCUS(widget))
4931             gtk_widget_grab_focus (widget);
4932
4933           if (button_actions & GTK_BUTTON_SELECTS)
4934             {
4935               switch (clist->selection_mode)
4936                 {
4937                 case GTK_SELECTION_SINGLE:
4938                 case GTK_SELECTION_MULTIPLE:
4939                   if (event->type != GDK_BUTTON_PRESS)
4940                     gtk_signal_emit (GTK_OBJECT (clist),
4941                                      clist_signals[SELECT_ROW],
4942                                      row, column, event);
4943                   else
4944                     clist->anchor = row;
4945                   break;
4946                 case GTK_SELECTION_BROWSE:
4947                   gtk_signal_emit (GTK_OBJECT (clist),
4948                                    clist_signals[SELECT_ROW],
4949                                    row, column, event);
4950                   break;
4951                 case GTK_SELECTION_EXTENDED:
4952                   if (event->type != GDK_BUTTON_PRESS)
4953                     {
4954                       if (clist->anchor != -1)
4955                         {
4956                           update_extended_selection (clist, clist->focus_row);
4957                           GTK_CLIST_CLASS_FW (clist)->resync_selection
4958                             (clist, (GdkEvent *) event);
4959                         }
4960                       gtk_signal_emit (GTK_OBJECT (clist),
4961                                        clist_signals[SELECT_ROW],
4962                                        row, column, event);
4963                       break;
4964                     }
4965               
4966                   if (event->state & GDK_CONTROL_MASK)
4967                     {
4968                       if (event->state & GDK_SHIFT_MASK)
4969                         {
4970                           if (clist->anchor < 0)
4971                             {
4972                               g_list_free (clist->undo_selection);
4973                               g_list_free (clist->undo_unselection);
4974                               clist->undo_selection = NULL;
4975                               clist->undo_unselection = NULL;
4976                               clist->anchor = old_row;
4977                               clist->drag_pos = old_row;
4978                               clist->undo_anchor = old_row;
4979                             }
4980                           update_extended_selection (clist, clist->focus_row);
4981                         }
4982                       else
4983                         {
4984                           if (clist->anchor == -1)
4985                             set_anchor (clist, TRUE, row, old_row);
4986                           else
4987                             update_extended_selection (clist,
4988                                                        clist->focus_row);
4989                         }
4990                       break;
4991                     }
4992
4993                   if (event->state & GDK_SHIFT_MASK)
4994                     {
4995                       set_anchor (clist, FALSE, old_row, old_row);
4996                       update_extended_selection (clist, clist->focus_row);
4997                       break;
4998                     }
4999
5000                   if (clist->anchor == -1)
5001                     set_anchor (clist, FALSE, row, old_row);
5002                   else
5003                     update_extended_selection (clist, clist->focus_row);
5004                   break;
5005                 default:
5006                   break;
5007                 }
5008             }
5009         }
5010       return FALSE;
5011     }
5012
5013   /* press on resize windows */
5014   for (i = 0; i < clist->columns; i++)
5015     if (clist->column[i].resizeable && clist->column[i].window &&
5016         event->window == clist->column[i].window)
5017       {
5018         gdk_pointer_grab (clist->column[i].window, FALSE,
5019                           GDK_POINTER_MOTION_HINT_MASK |
5020                           GDK_BUTTON1_MOTION_MASK |
5021                           GDK_BUTTON_RELEASE_MASK,
5022                           NULL, NULL, event->time);
5023         gtk_grab_add (widget);
5024         GTK_CLIST_SET_FLAG (clist, CLIST_IN_DRAG);
5025
5026         if (!GTK_WIDGET_HAS_FOCUS(widget))
5027           gtk_widget_grab_focus (widget);
5028
5029         clist->drag_pos = i;
5030         clist->x_drag = (COLUMN_LEFT_XPIXEL(clist, i) + COLUMN_INSET +
5031                          clist->column[i].area.width + CELL_SPACING);
5032
5033         if (GTK_CLIST_ADD_MODE(clist))
5034           gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
5035         draw_xor_line (clist);
5036       }
5037   return FALSE;
5038 }
5039
5040 static gint
5041 gtk_clist_button_release (GtkWidget      *widget,
5042                           GdkEventButton *event)
5043 {
5044   GtkCList *clist;
5045   gint button_actions;
5046
5047   g_return_val_if_fail (widget != NULL, FALSE);
5048   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
5049   g_return_val_if_fail (event != NULL, FALSE);
5050
5051   clist = GTK_CLIST (widget);
5052
5053   button_actions = clist->button_actions[event->button - 1];
5054   if (button_actions == GTK_BUTTON_IGNORED)
5055     return FALSE;
5056
5057   /* release on resize windows */
5058   if (GTK_CLIST_IN_DRAG(clist))
5059     {
5060       gint width;
5061       gint x;
5062       gint i;
5063
5064       i = clist->drag_pos;
5065       clist->drag_pos = -1;
5066
5067       GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
5068       gtk_widget_get_pointer (widget, &x, NULL);
5069       gtk_grab_remove (widget);
5070       gdk_pointer_ungrab (event->time);
5071
5072       if (clist->x_drag >= 0)
5073         draw_xor_line (clist);
5074
5075       if (GTK_CLIST_ADD_MODE(clist))
5076         {
5077           gdk_gc_set_line_attributes (clist->xor_gc, 1,
5078                                       GDK_LINE_ON_OFF_DASH, 0, 0);
5079           gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
5080         }
5081
5082       width = new_column_width (clist, i, &x);
5083       gtk_clist_set_column_width (clist, i, width);
5084       return FALSE;
5085     }
5086
5087   if (clist->drag_button == event->button)
5088     {
5089       gint row;
5090       gint column;
5091
5092       clist->drag_button = 0;
5093       clist->click_cell.row = -1;
5094       clist->click_cell.column = -1;
5095
5096       remove_grab (clist);
5097
5098       if (button_actions & GTK_BUTTON_SELECTS)
5099         {
5100           switch (clist->selection_mode)
5101             {
5102             case GTK_SELECTION_EXTENDED:
5103               if (!(event->state & GDK_SHIFT_MASK) ||
5104                   event->x < 0 || event->x >= clist->clist_window_width ||
5105                   event->y < 0 || event->y >= clist->clist_window_height)
5106                 GTK_CLIST_CLASS_FW (clist)->resync_selection
5107                   (clist, (GdkEvent *) event);
5108               break;
5109             case GTK_SELECTION_SINGLE:
5110             case GTK_SELECTION_MULTIPLE:
5111               if (get_selection_info (clist, event->x, event->y,
5112                                       &row, &column))
5113                 {
5114                   if (row >= 0 && row < clist->rows && clist->anchor == row)
5115                     toggle_row (clist, row, column, (GdkEvent *) event);
5116                 }
5117               clist->anchor = -1;
5118               break;
5119             default:
5120               break;
5121             }
5122         }
5123     }
5124   return FALSE;
5125 }
5126
5127 static gint
5128 gtk_clist_motion (GtkWidget      *widget,
5129                   GdkEventMotion *event)
5130 {
5131   GtkCList *clist;
5132   gint x;
5133   gint y;
5134   gint row;
5135   gint new_width;
5136   gint button_actions = 0;
5137
5138   g_return_val_if_fail (widget != NULL, FALSE);
5139   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
5140
5141   clist = GTK_CLIST (widget);
5142   if (!(gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)))
5143     return FALSE;
5144
5145   if (clist->drag_button > 0)
5146     button_actions = clist->button_actions[clist->drag_button - 1];
5147
5148   if (GTK_CLIST_IN_DRAG(clist))
5149     {
5150       if (event->is_hint || event->window != widget->window)
5151         gtk_widget_get_pointer (widget, &x, NULL);
5152       else
5153         x = event->x;
5154       
5155       new_width = new_column_width (clist, clist->drag_pos, &x);
5156       if (x != clist->x_drag)
5157         {
5158           /* x_drag < 0 indicates that the xor line is already invisible */
5159           if (clist->x_drag >= 0)
5160             draw_xor_line (clist);
5161
5162           clist->x_drag = x;
5163
5164           if (clist->x_drag >= 0)
5165             draw_xor_line (clist);
5166         }
5167
5168       if (new_width <= MAX (COLUMN_MIN_WIDTH + 1,
5169                             clist->column[clist->drag_pos].min_width + 1))
5170         {
5171           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) < 0 && x < 0)
5172             gtk_clist_moveto (clist, -1, clist->drag_pos, 0, 0);
5173           return FALSE;
5174         }
5175       if (clist->column[clist->drag_pos].max_width >= COLUMN_MIN_WIDTH &&
5176           new_width >= clist->column[clist->drag_pos].max_width)
5177         {
5178           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) + new_width >
5179               clist->clist_window_width && x < 0)
5180             move_horizontal (clist,
5181                              COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) +
5182                              new_width - clist->clist_window_width +
5183                              COLUMN_INSET + CELL_SPACING);
5184           return FALSE;
5185         }
5186     }
5187
5188   if (event->is_hint || event->window != clist->clist_window)
5189     gdk_window_get_pointer (clist->clist_window, &x, &y, NULL);
5190
5191   if (GTK_CLIST_REORDERABLE(clist) && button_actions & GTK_BUTTON_DRAGS)
5192     {
5193       /* delayed drag start */
5194       if (event->window == clist->clist_window &&
5195           clist->click_cell.row >= 0 && clist->click_cell.column >= 0 &&
5196           (y < 0 || y >= clist->clist_window_height ||
5197            x < 0 || x >= clist->clist_window_width  ||
5198            y < ROW_TOP_YPIXEL (clist, clist->click_cell.row) ||
5199            y >= (ROW_TOP_YPIXEL (clist, clist->click_cell.row) +
5200                  clist->row_height) ||
5201            x < COLUMN_LEFT_XPIXEL (clist, clist->click_cell.column) ||
5202            x >= (COLUMN_LEFT_XPIXEL(clist, clist->click_cell.column) + 
5203                  clist->column[clist->click_cell.column].area.width)))
5204         {
5205           GdkDragContext *context;
5206           GtkTargetList  *target_list;
5207           gint button;
5208
5209           button = clist->drag_button;
5210           clist->drag_button = 0;
5211           remove_grab (clist);
5212
5213           switch (clist->selection_mode)
5214             {
5215             case GTK_SELECTION_EXTENDED:
5216               update_extended_selection (clist, clist->focus_row);
5217               GTK_CLIST_CLASS_FW (clist)->resync_selection
5218                 (clist, (GdkEvent *) event);
5219               break;
5220             case GTK_SELECTION_SINGLE:
5221             case GTK_SELECTION_MULTIPLE:
5222               clist->anchor = -1;
5223             case GTK_SELECTION_BROWSE:
5224               break;
5225             }
5226
5227           target_list = gtk_target_list_new (&clist_target_table, 1);
5228           context = gtk_drag_begin (widget, target_list, GDK_ACTION_MOVE,
5229                                     button, (GdkEvent *)event);
5230
5231         }
5232       return TRUE;
5233     }
5234
5235   /* horizontal autoscrolling */
5236   if (clist->hadjustment && LIST_WIDTH (clist) > clist->clist_window_width &&
5237       (x < 0 || x >= clist->clist_window_width))
5238     {
5239       if (clist->htimer)
5240         return FALSE;
5241
5242       clist->htimer = gtk_timeout_add
5243         (SCROLL_TIME, (GtkFunction) horizontal_timeout, clist);
5244
5245       if (!((x < 0 && clist->hadjustment->value == 0) ||
5246             (x >= clist->clist_window_width &&
5247              clist->hadjustment->value ==
5248              LIST_WIDTH (clist) - clist->clist_window_width)))
5249         {
5250           if (x < 0)
5251             move_horizontal (clist, -1 + (x/2));
5252           else
5253             move_horizontal (clist, 1 + (x - clist->clist_window_width) / 2);
5254         }
5255     }
5256
5257   if (GTK_CLIST_IN_DRAG(clist))
5258     return FALSE;
5259
5260   /* vertical autoscrolling */
5261   row = ROW_FROM_YPIXEL (clist, y);
5262
5263   /* don't scroll on last pixel row if it's a cell spacing */
5264   if (y == clist->clist_window_height - 1 &&
5265       y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height)
5266     return FALSE;
5267
5268   if (LIST_HEIGHT (clist) > clist->clist_window_height &&
5269       (y < 0 || y >= clist->clist_window_height))
5270     {
5271       if (clist->vtimer)
5272         return FALSE;
5273
5274       clist->vtimer = gtk_timeout_add (SCROLL_TIME,
5275                                        (GtkFunction) vertical_timeout, clist);
5276
5277       if (clist->drag_button &&
5278           ((y < 0 && clist->focus_row == 0) ||
5279            (y >= clist->clist_window_height &&
5280             clist->focus_row == clist->rows - 1)))
5281         return FALSE;
5282     }
5283
5284   row = CLAMP (row, 0, clist->rows - 1);
5285
5286   if (button_actions & GTK_BUTTON_SELECTS)
5287     {
5288       if (row == clist->focus_row)
5289         return FALSE;
5290
5291       gtk_clist_draw_focus (widget);
5292       clist->focus_row = row;
5293       gtk_clist_draw_focus (widget);
5294
5295       switch (clist->selection_mode)
5296         {
5297         case GTK_SELECTION_BROWSE:
5298           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
5299                            clist->focus_row, -1, event);
5300           break;
5301         case GTK_SELECTION_EXTENDED:
5302           update_extended_selection (clist, clist->focus_row);
5303           break;
5304         default:
5305           break;
5306         }
5307     }
5308   
5309   if (ROW_TOP_YPIXEL(clist, row) < 0)
5310     move_vertical (clist, row, 0);
5311   else if (ROW_TOP_YPIXEL(clist, row) + clist->row_height >
5312            clist->clist_window_height)
5313     move_vertical (clist, row, 1);
5314
5315   return FALSE;
5316 }
5317
5318 static void
5319 gtk_clist_size_request (GtkWidget      *widget,
5320                         GtkRequisition *requisition)
5321 {
5322   GtkCList *clist;
5323   gint i;
5324
5325   g_return_if_fail (widget != NULL);
5326   g_return_if_fail (GTK_IS_CLIST (widget));
5327   g_return_if_fail (requisition != NULL);
5328
5329   clist = GTK_CLIST (widget);
5330
5331   requisition->width = 0;
5332   requisition->height = 0;
5333
5334   /* compute the size of the column title (title) area */
5335   clist->column_title_area.height = 0;
5336   if (GTK_CLIST_SHOW_TITLES(clist))
5337     for (i = 0; i < clist->columns; i++)
5338       if (clist->column[i].button)
5339         {
5340           gtk_widget_size_request (clist->column[i].button,
5341                                    &clist->column[i].button->requisition);
5342           clist->column_title_area.height =
5343             MAX (clist->column_title_area.height,
5344                  clist->column[i].button->requisition.height);
5345         }
5346
5347   requisition->width += (widget->style->klass->xthickness +
5348                          GTK_CONTAINER (widget)->border_width) * 2;
5349   requisition->height += (clist->column_title_area.height +
5350                           (widget->style->klass->ythickness +
5351                            GTK_CONTAINER (widget)->border_width) * 2);
5352
5353   /* if (!clist->hadjustment) */
5354   requisition->width += list_requisition_width (clist);
5355   /* if (!clist->vadjustment) */
5356   requisition->height += LIST_HEIGHT (clist);
5357 }
5358
5359 static void
5360 gtk_clist_size_allocate (GtkWidget     *widget,
5361                          GtkAllocation *allocation)
5362 {
5363   GtkCList *clist;
5364   GtkAllocation clist_allocation;
5365   gint border_width;
5366
5367   g_return_if_fail (widget != NULL);
5368   g_return_if_fail (GTK_IS_CLIST (widget));
5369   g_return_if_fail (allocation != NULL);
5370
5371   clist = GTK_CLIST (widget);
5372   widget->allocation = *allocation;
5373   border_width = GTK_CONTAINER (widget)->border_width;
5374
5375   if (GTK_WIDGET_REALIZED (widget))
5376     {
5377       gdk_window_move_resize (widget->window,
5378                               allocation->x + border_width,
5379                               allocation->y + border_width,
5380                               allocation->width - border_width * 2,
5381                               allocation->height - border_width * 2);
5382     }
5383
5384   /* use internal allocation structure for all the math
5385    * because it's easier than always subtracting the container
5386    * border width */
5387   clist->internal_allocation.x = 0;
5388   clist->internal_allocation.y = 0;
5389   clist->internal_allocation.width = MAX (1, allocation->width -
5390                                           border_width * 2);
5391   clist->internal_allocation.height = MAX (1, allocation->height -
5392                                            border_width * 2);
5393         
5394   /* allocate clist window assuming no scrollbars */
5395   clist_allocation.x = (clist->internal_allocation.x +
5396                         widget->style->klass->xthickness);
5397   clist_allocation.y = (clist->internal_allocation.y +
5398                         widget->style->klass->ythickness +
5399                         clist->column_title_area.height);
5400   clist_allocation.width = MAX (1, clist->internal_allocation.width - 
5401                                 (2 * widget->style->klass->xthickness));
5402   clist_allocation.height = MAX (1, clist->internal_allocation.height -
5403                                  (2 * widget->style->klass->ythickness) -
5404                                  clist->column_title_area.height);
5405   
5406   clist->clist_window_width = clist_allocation.width;
5407   clist->clist_window_height = clist_allocation.height;
5408   
5409   if (GTK_WIDGET_REALIZED (widget))
5410     {
5411       gdk_window_move_resize (clist->clist_window,
5412                               clist_allocation.x,
5413                               clist_allocation.y,
5414                               clist_allocation.width,
5415                               clist_allocation.height);
5416     }
5417   
5418   /* position the window which holds the column title buttons */
5419   clist->column_title_area.x = widget->style->klass->xthickness;
5420   clist->column_title_area.y = widget->style->klass->ythickness;
5421   clist->column_title_area.width = clist_allocation.width;
5422   
5423   if (GTK_WIDGET_REALIZED (widget))
5424     {
5425       gdk_window_move_resize (clist->title_window,
5426                               clist->column_title_area.x,
5427                               clist->column_title_area.y,
5428                               clist->column_title_area.width,
5429                               clist->column_title_area.height);
5430     }
5431   
5432   /* column button allocation */
5433   size_allocate_columns (clist, FALSE);
5434   size_allocate_title_buttons (clist);
5435
5436   adjust_adjustments (clist, TRUE);
5437 }
5438
5439 /* GTKCONTAINER
5440  *   gtk_clist_forall
5441  */
5442 static void
5443 gtk_clist_forall (GtkContainer *container,
5444                   gboolean      include_internals,
5445                   GtkCallback   callback,
5446                   gpointer      callback_data)
5447 {
5448   GtkCList *clist;
5449   guint i;
5450
5451   g_return_if_fail (container != NULL);
5452   g_return_if_fail (GTK_IS_CLIST (container));
5453   g_return_if_fail (callback != NULL);
5454
5455   if (!include_internals)
5456     return;
5457
5458   clist = GTK_CLIST (container);
5459       
5460   /* callback for the column buttons */
5461   for (i = 0; i < clist->columns; i++)
5462     if (clist->column[i].button)
5463       (*callback) (clist->column[i].button, callback_data);
5464 }
5465
5466 /* PRIVATE DRAWING FUNCTIONS
5467  *   get_cell_style
5468  *   draw_cell_pixmap
5469  *   draw_row
5470  *   draw_rows
5471  *   draw_xor_line
5472  *   clist_refresh
5473  */
5474 static void
5475 get_cell_style (GtkCList     *clist,
5476                 GtkCListRow  *clist_row,
5477                 gint          state,
5478                 gint          column,
5479                 GtkStyle    **style,
5480                 GdkGC       **fg_gc,
5481                 GdkGC       **bg_gc)
5482 {
5483   gint fg_state;
5484
5485   if ((state == GTK_STATE_NORMAL) &&
5486       (GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
5487     fg_state = GTK_STATE_INSENSITIVE;
5488   else
5489     fg_state = state;
5490
5491   if (clist_row->cell[column].style)
5492     {
5493       if (style)
5494         *style = clist_row->cell[column].style;
5495       if (fg_gc)
5496         *fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
5497       if (bg_gc) {
5498         if (state == GTK_STATE_SELECTED)
5499           *bg_gc = clist_row->cell[column].style->bg_gc[state];
5500         else
5501           *bg_gc = clist_row->cell[column].style->base_gc[state];
5502       }
5503     }
5504   else if (clist_row->style)
5505     {
5506       if (style)
5507         *style = clist_row->style;
5508       if (fg_gc)
5509         *fg_gc = clist_row->style->fg_gc[fg_state];
5510       if (bg_gc) {
5511         if (state == GTK_STATE_SELECTED)
5512           *bg_gc = clist_row->style->bg_gc[state];
5513         else
5514           *bg_gc = clist_row->style->base_gc[state];
5515       }
5516     }
5517   else
5518     {
5519       if (style)
5520         *style = GTK_WIDGET (clist)->style;
5521       if (fg_gc)
5522         *fg_gc = GTK_WIDGET (clist)->style->fg_gc[fg_state];
5523       if (bg_gc) {
5524         if (state == GTK_STATE_SELECTED)
5525           *bg_gc = GTK_WIDGET (clist)->style->bg_gc[state];
5526         else
5527           *bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
5528       }
5529
5530       if (state != GTK_STATE_SELECTED)
5531         {
5532           if (fg_gc && clist_row->fg_set)
5533             *fg_gc = clist->fg_gc;
5534           if (bg_gc && clist_row->bg_set)
5535             *bg_gc = clist->bg_gc;
5536         }
5537     }
5538 }
5539
5540 static gint
5541 draw_cell_pixmap (GdkWindow    *window,
5542                   GdkRectangle *clip_rectangle,
5543                   GdkGC        *fg_gc,
5544                   GdkPixmap    *pixmap,
5545                   GdkBitmap    *mask,
5546                   gint          x,
5547                   gint          y,
5548                   gint          width,
5549                   gint          height)
5550 {
5551   gint xsrc = 0;
5552   gint ysrc = 0;
5553
5554   if (mask)
5555     {
5556       gdk_gc_set_clip_mask (fg_gc, mask);
5557       gdk_gc_set_clip_origin (fg_gc, x, y);
5558     }
5559
5560   if (x < clip_rectangle->x)
5561     {
5562       xsrc = clip_rectangle->x - x;
5563       width -= xsrc;
5564       x = clip_rectangle->x;
5565     }
5566   if (x + width > clip_rectangle->x + clip_rectangle->width)
5567     width = clip_rectangle->x + clip_rectangle->width - x;
5568
5569   if (y < clip_rectangle->y)
5570     {
5571       ysrc = clip_rectangle->y - y;
5572       height -= ysrc;
5573       y = clip_rectangle->y;
5574     }
5575   if (y + height > clip_rectangle->y + clip_rectangle->height)
5576     height = clip_rectangle->y + clip_rectangle->height - y;
5577
5578   gdk_draw_pixmap (window, fg_gc, pixmap, xsrc, ysrc, x, y, width, height);
5579   gdk_gc_set_clip_origin (fg_gc, 0, 0);
5580
5581   return x + MAX (width, 0);
5582 }
5583
5584 static void
5585 draw_row (GtkCList     *clist,
5586           GdkRectangle *area,
5587           gint          row,
5588           GtkCListRow  *clist_row)
5589 {
5590   GtkWidget *widget;
5591   GdkRectangle *rect;
5592   GdkRectangle row_rectangle;
5593   GdkRectangle cell_rectangle;
5594   GdkRectangle clip_rectangle;
5595   GdkRectangle intersect_rectangle;
5596   gint state;
5597   gint i;
5598
5599   g_return_if_fail (clist != NULL);
5600
5601   /* bail now if we arn't drawable yet */
5602   if (!GTK_WIDGET_DRAWABLE (clist) || row < 0 || row >= clist->rows)
5603     return;
5604
5605   widget = GTK_WIDGET (clist);
5606
5607   /* if the function is passed the pointer to the row instead of null,
5608    * it avoids this expensive lookup */
5609   if (!clist_row)
5610     clist_row = (g_list_nth (clist->row_list, row))->data;
5611
5612   /* rectangle of the entire row */
5613   row_rectangle.x = 0;
5614   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
5615   row_rectangle.width = clist->clist_window_width;
5616   row_rectangle.height = clist->row_height;
5617
5618   /* rectangle of the cell spacing above the row */
5619   cell_rectangle.x = 0;
5620   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
5621   cell_rectangle.width = row_rectangle.width;
5622   cell_rectangle.height = CELL_SPACING;
5623
5624   /* rectangle used to clip drawing operations, it's y and height
5625    * positions only need to be set once, so we set them once here. 
5626    * the x and width are set withing the drawing loop below once per
5627    * column */
5628   clip_rectangle.y = row_rectangle.y;
5629   clip_rectangle.height = row_rectangle.height;
5630
5631   if (clist_row->state == GTK_STATE_NORMAL)
5632     {
5633       if (clist_row->fg_set)
5634         gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
5635       if (clist_row->bg_set)
5636         gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
5637     }
5638
5639   state = clist_row->state;
5640
5641   /* draw the cell borders and background */
5642   if (area)
5643     {
5644       rect = &intersect_rectangle;
5645       if (gdk_rectangle_intersect (area, &cell_rectangle,
5646                                    &intersect_rectangle))
5647         gdk_draw_rectangle (clist->clist_window,
5648                             widget->style->base_gc[GTK_STATE_ACTIVE],
5649                             TRUE,
5650                             intersect_rectangle.x,
5651                             intersect_rectangle.y,
5652                             intersect_rectangle.width,
5653                             intersect_rectangle.height);
5654
5655       /* the last row has to clear it's bottom cell spacing too */
5656       if (clist_row == clist->row_list_end->data)
5657         {
5658           cell_rectangle.y += clist->row_height + CELL_SPACING;
5659
5660           if (gdk_rectangle_intersect (area, &cell_rectangle,
5661                                        &intersect_rectangle))
5662             gdk_draw_rectangle (clist->clist_window,
5663                                 widget->style->base_gc[GTK_STATE_ACTIVE],
5664                                 TRUE,
5665                                 intersect_rectangle.x,
5666                                 intersect_rectangle.y,
5667                                 intersect_rectangle.width,
5668                                 intersect_rectangle.height);
5669         }
5670
5671       if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
5672         return;
5673
5674     }
5675   else
5676     {
5677       rect = &clip_rectangle;
5678       gdk_draw_rectangle (clist->clist_window,
5679                           widget->style->base_gc[GTK_STATE_ACTIVE],
5680                           TRUE,
5681                           cell_rectangle.x,
5682                           cell_rectangle.y,
5683                           cell_rectangle.width,
5684                           cell_rectangle.height);
5685
5686       /* the last row has to clear it's bottom cell spacing too */
5687       if (clist_row == clist->row_list_end->data)
5688         {
5689           cell_rectangle.y += clist->row_height + CELL_SPACING;
5690
5691           gdk_draw_rectangle (clist->clist_window,
5692                               widget->style->base_gc[GTK_STATE_ACTIVE],
5693                               TRUE,
5694                               cell_rectangle.x,
5695                               cell_rectangle.y,
5696                               cell_rectangle.width,
5697                               cell_rectangle.height);     
5698         }         
5699     }
5700   
5701   /* iterate and draw all the columns (row cells) and draw their contents */
5702   for (i = 0; i < clist->columns; i++)
5703     {
5704       GtkStyle *style;
5705       GdkGC *fg_gc;
5706       GdkGC *bg_gc;
5707
5708       gint width;
5709       gint height;
5710       gint pixmap_width;
5711       gint offset = 0;
5712       gint row_center_offset;
5713
5714       if (!clist->column[i].visible)
5715         continue;
5716
5717       get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
5718
5719       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
5720       clip_rectangle.width = clist->column[i].area.width;
5721
5722       /* calculate clipping region clipping region */
5723       clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
5724       clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
5725                                (i + 1 == clist->columns) * CELL_SPACING);
5726       
5727       if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
5728                                             &intersect_rectangle))
5729         continue;
5730
5731       gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
5732                           rect->x, rect->y, rect->width, rect->height);
5733
5734       clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
5735       clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
5736                                (i + 1 == clist->columns) * CELL_SPACING);
5737
5738       /* calculate real width for column justification */
5739       pixmap_width = 0;
5740       offset = 0;
5741       switch (clist_row->cell[i].type)
5742         {
5743         case GTK_CELL_TEXT:
5744           width = gdk_string_width (style->font,
5745                                     GTK_CELL_TEXT (clist_row->cell[i])->text);
5746           break;
5747         case GTK_CELL_PIXMAP:
5748           gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5749                                &pixmap_width, &height);
5750           width = pixmap_width;
5751           break;
5752         case GTK_CELL_PIXTEXT:
5753           gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5754                                &pixmap_width, &height);
5755           width = (pixmap_width +
5756                    GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing +
5757                    gdk_string_width (style->font,
5758                                      GTK_CELL_PIXTEXT
5759                                      (clist_row->cell[i])->text));
5760           break;
5761         default:
5762           continue;
5763           break;
5764         }
5765
5766       switch (clist->column[i].justification)
5767         {
5768         case GTK_JUSTIFY_LEFT:
5769           offset = clip_rectangle.x + clist_row->cell[i].horizontal;
5770           break;
5771         case GTK_JUSTIFY_RIGHT:
5772           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5773                     clip_rectangle.width - width);
5774           break;
5775         case GTK_JUSTIFY_CENTER:
5776         case GTK_JUSTIFY_FILL:
5777           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5778                     (clip_rectangle.width / 2) - (width / 2));
5779           break;
5780         };
5781
5782       /* Draw Text and/or Pixmap */
5783       switch (clist_row->cell[i].type)
5784         {
5785         case GTK_CELL_PIXMAP:
5786           draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5787                             GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5788                             GTK_CELL_PIXMAP (clist_row->cell[i])->mask,
5789                             offset,
5790                             clip_rectangle.y + clist_row->cell[i].vertical +
5791                             (clip_rectangle.height - height) / 2,
5792                             pixmap_width, height);
5793           break;
5794         case GTK_CELL_PIXTEXT:
5795           offset =
5796             draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5797                               GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5798                               GTK_CELL_PIXTEXT (clist_row->cell[i])->mask,
5799                               offset,
5800                               clip_rectangle.y + clist_row->cell[i].vertical+
5801                               (clip_rectangle.height - height) / 2,
5802                               pixmap_width, height);
5803           offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
5804         case GTK_CELL_TEXT:
5805           if (style != GTK_WIDGET (clist)->style)
5806             row_center_offset = (((clist->row_height - style->font->ascent -
5807                                   style->font->descent - 1) / 2) + 1.5 +
5808                                  style->font->ascent);
5809           else
5810             row_center_offset = clist->row_center_offset;
5811
5812           gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
5813           gdk_draw_string (clist->clist_window, style->font, fg_gc,
5814                            offset,
5815                            row_rectangle.y + row_center_offset + 
5816                            clist_row->cell[i].vertical,
5817                            (clist_row->cell[i].type == GTK_CELL_PIXTEXT) ?
5818                            GTK_CELL_PIXTEXT (clist_row->cell[i])->text :
5819                            GTK_CELL_TEXT (clist_row->cell[i])->text);
5820           gdk_gc_set_clip_rectangle (fg_gc, NULL);
5821           break;
5822         default:
5823           break;
5824         }
5825     }
5826
5827   /* draw focus rectangle */
5828   if (clist->focus_row == row && GTK_WIDGET_HAS_FOCUS(widget))
5829     {
5830       if (!area)
5831         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5832                             row_rectangle.x, row_rectangle.y,
5833                             row_rectangle.width - 1, row_rectangle.height - 1);
5834       else if (gdk_rectangle_intersect (area, &row_rectangle,
5835                                         &intersect_rectangle))
5836         {
5837           gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
5838           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5839                               row_rectangle.x, row_rectangle.y,
5840                               row_rectangle.width - 1,
5841                               row_rectangle.height - 1);
5842           gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
5843         }
5844     }
5845 }
5846
5847 static void
5848 draw_rows (GtkCList     *clist,
5849            GdkRectangle *area)
5850 {
5851   GList *list;
5852   GtkCListRow *clist_row;
5853   gint i;
5854   gint first_row;
5855   gint last_row;
5856
5857   g_return_if_fail (clist != NULL);
5858   g_return_if_fail (GTK_IS_CLIST (clist));
5859
5860   if (clist->row_height == 0 ||
5861       !GTK_WIDGET_DRAWABLE (clist))
5862     return;
5863
5864   if (area)
5865     {
5866       first_row = ROW_FROM_YPIXEL (clist, area->y);
5867       last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
5868     }
5869   else
5870     {
5871       first_row = ROW_FROM_YPIXEL (clist, 0);
5872       last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
5873     }
5874
5875   /* this is a small special case which exposes the bottom cell line
5876    * on the last row -- it might go away if I change the wall the cell
5877    * spacings are drawn
5878    */
5879   if (clist->rows == first_row)
5880     first_row--;
5881
5882   list = g_list_nth (clist->row_list, first_row);
5883   i = first_row;
5884   while (list)
5885     {
5886       clist_row = list->data;
5887       list = list->next;
5888
5889       if (i > last_row)
5890         return;
5891
5892       GTK_CLIST_CLASS_FW (clist)->draw_row (clist, area, i, clist_row);
5893       i++;
5894     }
5895
5896   if (!area)
5897     gdk_window_clear_area (clist->clist_window,
5898                            0, ROW_TOP_YPIXEL (clist, i), -1, -1);
5899 }
5900
5901 static void                          
5902 draw_xor_line (GtkCList *clist)
5903 {
5904   GtkWidget *widget;
5905
5906   g_return_if_fail (clist != NULL);
5907
5908   widget = GTK_WIDGET (clist);
5909
5910   gdk_draw_line (widget->window, clist->xor_gc,
5911                  clist->x_drag,
5912                  widget->style->klass->ythickness,
5913                  clist->x_drag,
5914                  clist->column_title_area.height +
5915                  clist->clist_window_height + 1);
5916 }
5917
5918 static void
5919 clist_refresh (GtkCList *clist)
5920 {
5921   g_return_if_fail (clist != NULL);
5922   g_return_if_fail (GTK_IS_CLIST (clist));
5923   
5924   if (CLIST_UNFROZEN (clist))
5925     { 
5926       adjust_adjustments (clist, FALSE);
5927       draw_rows (clist, NULL);
5928     }
5929 }
5930
5931 /* get cell from coordinates
5932  *   get_selection_info
5933  *   gtk_clist_get_selection_info
5934  */
5935 static gint
5936 get_selection_info (GtkCList *clist,
5937                     gint      x,
5938                     gint      y,
5939                     gint     *row,
5940                     gint     *column)
5941 {
5942   gint trow, tcol;
5943
5944   g_return_val_if_fail (clist != NULL, 0);
5945   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
5946
5947   /* bounds checking, return false if the user clicked 
5948    * on a blank area */
5949   trow = ROW_FROM_YPIXEL (clist, y);
5950   if (trow >= clist->rows)
5951     return 0;
5952
5953   if (row)
5954     *row = trow;
5955
5956   tcol = COLUMN_FROM_XPIXEL (clist, x);
5957   if (tcol >= clist->columns)
5958     return 0;
5959
5960   if (column)
5961     *column = tcol;
5962
5963   return 1;
5964 }
5965
5966 gint
5967 gtk_clist_get_selection_info (GtkCList *clist, 
5968                               gint      x, 
5969                               gint      y, 
5970                               gint     *row, 
5971                               gint     *column)
5972 {
5973   g_return_val_if_fail (clist != NULL, 0);
5974   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
5975   return get_selection_info (clist, x, y, row, column);
5976 }
5977
5978 /* PRIVATE ADJUSTMENT FUNCTIONS
5979  *   adjust_adjustments
5980  *   vadjustment_changed
5981  *   hadjustment_changed
5982  *   vadjustment_value_changed
5983  *   hadjustment_value_changed 
5984  *   check_exposures
5985  */
5986 static void
5987 adjust_adjustments (GtkCList *clist,
5988                     gboolean  block_resize)
5989 {
5990   if (clist->vadjustment)
5991     {
5992       clist->vadjustment->page_size = clist->clist_window_height;
5993       clist->vadjustment->page_increment = clist->clist_window_height / 2;
5994       clist->vadjustment->step_increment = clist->row_height;
5995       clist->vadjustment->lower = 0;
5996       clist->vadjustment->upper = LIST_HEIGHT (clist);
5997
5998       if (clist->clist_window_height - clist->voffset > LIST_HEIGHT (clist))
5999         {
6000           clist->vadjustment->value = MAX (0, (LIST_HEIGHT (clist) -
6001                                                clist->clist_window_height));
6002           gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment),
6003                                    "value_changed");
6004         }
6005       gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment), "changed");
6006     }
6007
6008   if (clist->hadjustment)
6009     {
6010       clist->hadjustment->page_size = clist->clist_window_width;
6011       clist->hadjustment->page_increment = clist->clist_window_width / 2;
6012       clist->hadjustment->step_increment = 10;
6013       clist->hadjustment->lower = 0;
6014       clist->hadjustment->upper = LIST_WIDTH (clist);
6015
6016       if (clist->clist_window_width - clist->hoffset > LIST_WIDTH (clist))
6017         {
6018           clist->hadjustment->value = MAX (0, (LIST_WIDTH (clist) -
6019                                                clist->clist_window_width));
6020           gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment),
6021                                    "value_changed");
6022         }
6023       gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment), "changed");
6024     }
6025
6026   if (!block_resize && (!clist->vadjustment || !clist->hadjustment))
6027     {
6028       GtkWidget *widget;
6029
6030       widget = GTK_WIDGET (clist);
6031       gtk_widget_size_request (widget, &widget->requisition);
6032
6033       if ((!clist->hadjustment &&
6034            widget->requisition.width != widget->allocation.width) ||
6035           (!clist->vadjustment &&
6036            widget->requisition.height != widget->allocation.height))
6037         gtk_widget_queue_resize (widget);
6038     }
6039 }
6040
6041 static void
6042 vadjustment_changed (GtkAdjustment *adjustment,
6043                      gpointer       data)
6044 {
6045   GtkCList *clist;
6046
6047   g_return_if_fail (adjustment != NULL);
6048   g_return_if_fail (data != NULL);
6049
6050   clist = GTK_CLIST (data);
6051 }
6052
6053 static void
6054 hadjustment_changed (GtkAdjustment *adjustment,
6055                      gpointer       data)
6056 {
6057   GtkCList *clist;
6058
6059   g_return_if_fail (adjustment != NULL);
6060   g_return_if_fail (data != NULL);
6061
6062   clist = GTK_CLIST (data);
6063 }
6064
6065 static void
6066 vadjustment_value_changed (GtkAdjustment *adjustment,
6067                            gpointer       data)
6068 {
6069   GtkCList *clist;
6070   GdkRectangle area;
6071   gint diff, value;
6072
6073   g_return_if_fail (adjustment != NULL);
6074   g_return_if_fail (data != NULL);
6075   g_return_if_fail (GTK_IS_CLIST (data));
6076
6077   clist = GTK_CLIST (data);
6078
6079   if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->vadjustment)
6080     return;
6081
6082   value = adjustment->value;
6083
6084   if (value > -clist->voffset)
6085     {
6086       /* scroll down */
6087       diff = value + clist->voffset;
6088
6089       /* we have to re-draw the whole screen here... */
6090       if (diff >= clist->clist_window_height)
6091         {
6092           clist->voffset = -value;
6093           draw_rows (clist, NULL);
6094           return;
6095         }
6096
6097       if ((diff != 0) && (diff != clist->clist_window_height))
6098         gdk_window_copy_area (clist->clist_window, clist->fg_gc,
6099                               0, 0, clist->clist_window, 0, diff,
6100                               clist->clist_window_width,
6101                               clist->clist_window_height - diff);
6102
6103       area.x = 0;
6104       area.y = clist->clist_window_height - diff;
6105       area.width = clist->clist_window_width;
6106       area.height = diff;
6107     }
6108   else
6109     {
6110       /* scroll up */
6111       diff = -clist->voffset - value;
6112
6113       /* we have to re-draw the whole screen here... */
6114       if (diff >= clist->clist_window_height)
6115         {
6116           clist->voffset = -value;
6117           draw_rows (clist, NULL);
6118           return;
6119         }
6120
6121       if ((diff != 0) && (diff != clist->clist_window_height))
6122         gdk_window_copy_area (clist->clist_window, clist->fg_gc,
6123                               0, diff, clist->clist_window, 0, 0,
6124                               clist->clist_window_width,
6125                               clist->clist_window_height - diff);
6126
6127       area.x = 0;
6128       area.y = 0;
6129       area.width = clist->clist_window_width;
6130       area.height = diff;
6131     }
6132
6133   clist->voffset = -value;
6134   if ((diff != 0) && (diff != clist->clist_window_height))
6135     check_exposures (clist);
6136
6137   draw_rows (clist, &area);
6138 }
6139
6140 static void
6141 hadjustment_value_changed (GtkAdjustment *adjustment,
6142                            gpointer       data)
6143 {
6144   GtkCList *clist;
6145   GdkRectangle area;
6146   gint i;
6147   gint y = 0;
6148   gint diff = 0;
6149   gint value;
6150
6151   g_return_if_fail (adjustment != NULL);
6152   g_return_if_fail (data != NULL);
6153   g_return_if_fail (GTK_IS_CLIST (data));
6154
6155   clist = GTK_CLIST (data);
6156
6157   if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->hadjustment)
6158     return;
6159
6160   value = adjustment->value;
6161
6162   /* move the column buttons and resize windows */
6163   for (i = 0; i < clist->columns; i++)
6164     {
6165       if (clist->column[i].button)
6166         {
6167           clist->column[i].button->allocation.x -= value + clist->hoffset;
6168           
6169           if (clist->column[i].button->window)
6170             {
6171               gdk_window_move (clist->column[i].button->window,
6172                                clist->column[i].button->allocation.x,
6173                                clist->column[i].button->allocation.y);
6174               
6175               if (clist->column[i].window)
6176                 gdk_window_move (clist->column[i].window,
6177                                  clist->column[i].button->allocation.x +
6178                                  clist->column[i].button->allocation.width - 
6179                                  (DRAG_WIDTH / 2), 0); 
6180             }
6181         }
6182     }
6183
6184   if (value > -clist->hoffset)
6185     {
6186       /* scroll right */
6187       diff = value + clist->hoffset;
6188       
6189       clist->hoffset = -value;
6190       
6191       /* we have to re-draw the whole screen here... */
6192       if (diff >= clist->clist_window_width)
6193         {
6194           draw_rows (clist, NULL);
6195           return;
6196         }
6197
6198       if (GTK_WIDGET_HAS_FOCUS(clist) && !GTK_CLIST_CHILD_HAS_FOCUS(clist) &&
6199           GTK_CLIST_ADD_MODE(clist))
6200         {
6201           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6202               
6203           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
6204                               clist->clist_window_width - 1,
6205                               clist->row_height - 1);
6206         }
6207       gdk_window_copy_area (clist->clist_window,
6208                             clist->fg_gc,
6209                             0, 0,
6210                             clist->clist_window,
6211                             diff,
6212                             0,
6213                             clist->clist_window_width - diff,
6214                             clist->clist_window_height);
6215
6216       area.x = clist->clist_window_width - diff;
6217     }
6218   else
6219     {
6220       /* scroll left */
6221       if (!(diff = -clist->hoffset - value))
6222         return;
6223
6224       clist->hoffset = -value;
6225       
6226       /* we have to re-draw the whole screen here... */
6227       if (diff >= clist->clist_window_width)
6228         {
6229           draw_rows (clist, NULL);
6230           return;
6231         }
6232       
6233       if (GTK_WIDGET_HAS_FOCUS(clist) && !GTK_CLIST_CHILD_HAS_FOCUS(clist) &&
6234           GTK_CLIST_ADD_MODE(clist))
6235         {
6236           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6237           
6238           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
6239                               clist->clist_window_width - 1,
6240                               clist->row_height - 1);
6241         }
6242
6243       gdk_window_copy_area (clist->clist_window,
6244                             clist->fg_gc,
6245                             diff, 0,
6246                             clist->clist_window,
6247                             0,
6248                             0,
6249                             clist->clist_window_width - diff,
6250                             clist->clist_window_height);
6251           
6252       area.x = 0;
6253     }
6254
6255   area.y = 0;
6256   area.width = diff;
6257   area.height = clist->clist_window_height;
6258
6259   check_exposures (clist);
6260
6261   if (GTK_WIDGET_HAS_FOCUS(clist) && !GTK_CLIST_CHILD_HAS_FOCUS(clist))
6262     {
6263       if (GTK_CLIST_ADD_MODE(clist))
6264         {
6265           gint focus_row;
6266           
6267           focus_row = clist->focus_row;
6268           clist->focus_row = -1;
6269           draw_rows (clist, &area);
6270           clist->focus_row = focus_row;
6271           
6272           gdk_draw_rectangle (clist->clist_window, clist->xor_gc,
6273                               FALSE, 0, y, clist->clist_window_width - 1,
6274                               clist->row_height - 1);
6275           return;
6276         }
6277       else
6278         {
6279           gint x0;
6280           gint x1;
6281           
6282           if (area.x == 0)
6283             {
6284               x0 = clist->clist_window_width - 1;
6285               x1 = diff;
6286             }
6287           else
6288             {
6289               x0 = 0;
6290               x1 = area.x - 1;
6291             }
6292           
6293           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6294           gdk_draw_line (clist->clist_window, clist->xor_gc,
6295                          x0, y + 1, x0, y + clist->row_height - 2);
6296           gdk_draw_line (clist->clist_window, clist->xor_gc,
6297                          x1, y + 1, x1, y + clist->row_height - 2);
6298           
6299         }
6300     }
6301   draw_rows (clist, &area);
6302 }
6303
6304 static void
6305 check_exposures (GtkCList *clist)
6306 {
6307   GdkEvent *event;
6308
6309   if (!GTK_WIDGET_REALIZED (clist))
6310     return;
6311
6312   /* Make sure graphics expose events are processed before scrolling
6313    * again */
6314   while ((event = gdk_event_get_graphics_expose (clist->clist_window)) != NULL)
6315     {
6316       gtk_widget_event (GTK_WIDGET (clist), event);
6317       if (event->expose.count == 0)
6318         {
6319           gdk_event_free (event);
6320           break;
6321         }
6322       gdk_event_free (event);
6323     }
6324 }
6325
6326 /* PRIVATE 
6327  * Memory Allocation/Distruction Routines for GtkCList stuctures
6328  *
6329  * functions:
6330  *   columns_new
6331  *   column_title_new
6332  *   columns_delete
6333  *   row_new
6334  *   row_delete
6335  */
6336 static GtkCListColumn *
6337 columns_new (GtkCList *clist)
6338 {
6339   GtkCListColumn *column;
6340   gint i;
6341
6342   column = g_new (GtkCListColumn, clist->columns);
6343
6344   for (i = 0; i < clist->columns; i++)
6345     {
6346       column[i].area.x = 0;
6347       column[i].area.y = 0;
6348       column[i].area.width = 0;
6349       column[i].area.height = 0;
6350       column[i].title = NULL;
6351       column[i].button = NULL;
6352       column[i].window = NULL;
6353       column[i].width = 0;
6354       column[i].min_width = -1;
6355       column[i].max_width = -1;
6356       column[i].visible = TRUE;
6357       column[i].width_set = FALSE;
6358       column[i].resizeable = TRUE;
6359       column[i].auto_resize = FALSE;
6360       column[i].justification = GTK_JUSTIFY_LEFT;
6361     }
6362
6363   return column;
6364 }
6365
6366 static void
6367 column_title_new (GtkCList    *clist,
6368                   gint         column,
6369                   const gchar *title)
6370 {
6371   if (clist->column[column].title)
6372     g_free (clist->column[column].title);
6373
6374   clist->column[column].title = g_strdup (title);
6375 }
6376
6377 static void
6378 columns_delete (GtkCList *clist)
6379 {
6380   gint i;
6381
6382   for (i = 0; i < clist->columns; i++)
6383     if (clist->column[i].title)
6384       g_free (clist->column[i].title);
6385       
6386   g_free (clist->column);
6387 }
6388
6389 static GtkCListRow *
6390 row_new (GtkCList *clist)
6391 {
6392   int i;
6393   GtkCListRow *clist_row;
6394
6395   clist_row = g_chunk_new (GtkCListRow, clist->row_mem_chunk);
6396   clist_row->cell = g_chunk_new (GtkCell, clist->cell_mem_chunk);
6397
6398   for (i = 0; i < clist->columns; i++)
6399     {
6400       clist_row->cell[i].type = GTK_CELL_EMPTY;
6401       clist_row->cell[i].vertical = 0;
6402       clist_row->cell[i].horizontal = 0;
6403       clist_row->cell[i].style = NULL;
6404     }
6405
6406   clist_row->fg_set = FALSE;
6407   clist_row->bg_set = FALSE;
6408   clist_row->style = NULL;
6409   clist_row->selectable = TRUE;
6410   clist_row->state = GTK_STATE_NORMAL;
6411   clist_row->data = NULL;
6412   clist_row->destroy = NULL;
6413
6414   return clist_row;
6415 }
6416
6417 static void
6418 row_delete (GtkCList    *clist,
6419             GtkCListRow *clist_row)
6420 {
6421   gint i;
6422
6423   for (i = 0; i < clist->columns; i++)
6424     {
6425       GTK_CLIST_CLASS_FW (clist)->set_cell_contents
6426         (clist, clist_row, i, GTK_CELL_EMPTY, NULL, 0, NULL, NULL);
6427       if (clist_row->cell[i].style)
6428         {
6429           if (GTK_WIDGET_REALIZED (clist))
6430             gtk_style_detach (clist_row->cell[i].style);
6431           gtk_style_unref (clist_row->cell[i].style);
6432         }
6433     }
6434
6435   if (clist_row->style)
6436     {
6437       if (GTK_WIDGET_REALIZED (clist))
6438         gtk_style_detach (clist_row->style);
6439       gtk_style_unref (clist_row->style);
6440     }
6441
6442   if (clist_row->destroy)
6443     clist_row->destroy (clist_row->data);
6444
6445   g_mem_chunk_free (clist->cell_mem_chunk, clist_row->cell);
6446   g_mem_chunk_free (clist->row_mem_chunk, clist_row);
6447 }
6448
6449 /* FOCUS FUNCTIONS
6450  *   gtk_clist_focus
6451  *   gtk_clist_draw_focus
6452  *   gtk_clist_focus_in
6453  *   gtk_clist_focus_out
6454  *   gtk_clist_set_focus_child
6455  *   title_focus
6456  */
6457 static gint
6458 gtk_clist_focus (GtkContainer     *container,
6459                  GtkDirectionType  direction)
6460 {
6461   GtkCList *clist;
6462   GtkWidget *focus_child;
6463   gint old_row;
6464
6465   g_return_val_if_fail (container != NULL, FALSE);
6466   g_return_val_if_fail (GTK_IS_CLIST (container), FALSE);
6467
6468   if (!GTK_WIDGET_SENSITIVE (container))
6469     return FALSE;
6470   
6471   clist = GTK_CLIST (container);
6472   focus_child = container->focus_child;
6473   old_row = clist->focus_row;
6474
6475   switch (direction)
6476     {
6477     case GTK_DIR_LEFT:
6478     case GTK_DIR_RIGHT:
6479       if (GTK_CLIST_CHILD_HAS_FOCUS(clist))
6480         {
6481           if (title_focus (clist, direction))
6482             return TRUE;
6483           gtk_container_set_focus_child (container, NULL);
6484           return FALSE;
6485          }
6486       gtk_widget_grab_focus (GTK_WIDGET (container));
6487       return TRUE;
6488     case GTK_DIR_DOWN:
6489     case GTK_DIR_TAB_FORWARD:
6490       if (GTK_CLIST_CHILD_HAS_FOCUS(clist))
6491         {
6492           gboolean tf = FALSE;
6493
6494           if (((focus_child && direction == GTK_DIR_DOWN) ||
6495                !(tf = title_focus (clist, GTK_DIR_TAB_FORWARD)))
6496               && clist->rows)
6497             {
6498               if (clist->focus_row < 0)
6499                 {
6500                   clist->focus_row = 0;
6501
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           if (tf)
6514             return TRUE;
6515         }
6516       
6517       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
6518       break;
6519     case GTK_DIR_UP:
6520     case GTK_DIR_TAB_BACKWARD:
6521       if (!focus_child &&
6522           GTK_CLIST_CHILD_HAS_FOCUS(clist) && clist->rows)
6523         {
6524           if (clist->focus_row < 0)
6525             {
6526               clist->focus_row = 0;
6527               if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6528                    clist->selection_mode == GTK_SELECTION_EXTENDED) &&
6529                   !clist->selection)
6530                 gtk_signal_emit (GTK_OBJECT (clist),
6531                                  clist_signals[SELECT_ROW],
6532                                  clist->focus_row, -1, NULL);
6533             }
6534           gtk_widget_grab_focus (GTK_WIDGET (container));
6535           return TRUE;
6536         }
6537
6538       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
6539
6540       if (title_focus (clist, direction))
6541         return TRUE;
6542
6543       break;
6544     default:
6545       break;
6546     }
6547
6548   gtk_container_set_focus_child (container, NULL);
6549   return FALSE;
6550 }
6551
6552 static void
6553 gtk_clist_draw_focus (GtkWidget *widget)
6554 {
6555   GtkCList *clist;
6556
6557   g_return_if_fail (widget != NULL);
6558   g_return_if_fail (GTK_IS_CLIST (widget));
6559
6560   if (!GTK_WIDGET_DRAWABLE (widget))
6561     return;
6562
6563   clist = GTK_CLIST (widget);
6564   if (clist->focus_row >= 0)
6565     gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
6566                         0, ROW_TOP_YPIXEL(clist, clist->focus_row),
6567                         clist->clist_window_width - 1,
6568                         clist->row_height - 1);
6569 }
6570
6571 static gint
6572 gtk_clist_focus_in (GtkWidget     *widget,
6573                     GdkEventFocus *event)
6574 {
6575   GtkCList *clist;
6576
6577   g_return_val_if_fail (widget != NULL, FALSE);
6578   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
6579   g_return_val_if_fail (event != NULL, FALSE);
6580
6581   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
6582   GTK_CLIST_UNSET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
6583
6584   clist = GTK_CLIST (widget);
6585
6586   if (clist->selection_mode == GTK_SELECTION_BROWSE &&
6587       clist->selection == NULL && clist->focus_row > -1)
6588     {
6589       GList *list;
6590
6591       list = g_list_nth (clist->row_list, clist->focus_row);
6592       if (list && GTK_CLIST_ROW (list)->selectable)
6593         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6594                          clist->focus_row, -1, event);
6595       else
6596         gtk_widget_draw_focus (widget);
6597     }
6598   else
6599     gtk_widget_draw_focus (widget);
6600
6601   return FALSE;
6602 }
6603
6604 static gint
6605 gtk_clist_focus_out (GtkWidget     *widget,
6606                      GdkEventFocus *event)
6607 {
6608   GtkCList *clist;
6609
6610   g_return_val_if_fail (widget != NULL, FALSE);
6611   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
6612   g_return_val_if_fail (event != NULL, FALSE);
6613
6614   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
6615   GTK_CLIST_SET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
6616
6617   gtk_widget_draw_focus (widget);
6618   
6619   clist = GTK_CLIST (widget);
6620
6621   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_EXTENDED)
6622     GTK_CLIST_CLASS_FW (widget)->resync_selection (clist, (GdkEvent *) event);
6623
6624   return FALSE;
6625 }
6626
6627 static void
6628 gtk_clist_set_focus_child (GtkContainer *container,
6629                            GtkWidget    *child)
6630 {
6631   g_return_if_fail (container != NULL);
6632   g_return_if_fail (GTK_IS_CLIST (container));
6633
6634   if (child)
6635     {
6636       g_return_if_fail (GTK_IS_WIDGET (child));
6637       GTK_CLIST_SET_FLAG (container, CLIST_CHILD_HAS_FOCUS);
6638     }
6639
6640   parent_class->set_focus_child (container, child);
6641 }
6642
6643 static gboolean
6644 title_focus (GtkCList *clist,
6645              gint      dir)
6646 {
6647   GtkWidget *focus_child;
6648   gboolean return_val = FALSE;
6649   gint d = 1;
6650   gint i = 0;
6651   gint j;
6652
6653   if (!GTK_CLIST_SHOW_TITLES(clist))
6654     return FALSE;
6655
6656   focus_child = GTK_CONTAINER (clist)->focus_child;
6657   
6658   switch (dir)
6659     {
6660     case GTK_DIR_TAB_BACKWARD:
6661     case GTK_DIR_UP:
6662       if (!focus_child || !GTK_CLIST_CHILD_HAS_FOCUS(clist))
6663         {
6664           if (dir == GTK_DIR_UP)
6665             i = COLUMN_FROM_XPIXEL (clist, 0);
6666           else
6667             i = clist->columns - 1;
6668           focus_child = clist->column[i].button;
6669           dir = GTK_DIR_TAB_FORWARD;
6670         }
6671       else
6672         d = -1;
6673       break;
6674     case GTK_DIR_LEFT:
6675       d = -1;
6676       if (!focus_child)
6677         {
6678           i = clist->columns - 1;
6679           focus_child = clist->column[i].button;
6680         }
6681       break;
6682     case GTK_DIR_RIGHT:
6683       if (!focus_child)
6684         {
6685           i = 0;
6686           focus_child = clist->column[i].button;
6687         }
6688       break;
6689     }
6690
6691   if (focus_child)
6692     while (i < clist->columns)
6693       {
6694         if (clist->column[i].button == focus_child)
6695           {
6696             if (clist->column[i].button && 
6697                 GTK_WIDGET_VISIBLE (clist->column[i].button) &&
6698                 GTK_IS_CONTAINER (clist->column[i].button) &&
6699                 !GTK_WIDGET_HAS_FOCUS(clist->column[i].button))
6700               if (gtk_container_focus 
6701                   (GTK_CONTAINER (clist->column[i].button), dir))
6702                 {
6703                   return_val = TRUE;
6704                   i -= d;
6705                 }
6706             if (!return_val && dir == GTK_DIR_UP)
6707               return FALSE;
6708             i += d;
6709             break;
6710           }
6711         i++;
6712       }
6713
6714   j = i;
6715
6716   if (!return_val)
6717     while (j >= 0 && j < clist->columns)
6718       {
6719         if (clist->column[j].button &&
6720             GTK_WIDGET_VISIBLE (clist->column[j].button))
6721           {
6722             if (GTK_IS_CONTAINER (clist->column[j].button) &&
6723                 gtk_container_focus 
6724                 (GTK_CONTAINER (clist->column[j].button), dir))
6725               {
6726                 return_val = TRUE;
6727                 break;
6728               }
6729             else if (GTK_WIDGET_CAN_FOCUS (clist->column[j].button))
6730               {
6731                 gtk_widget_grab_focus (clist->column[j].button);
6732                 return_val = TRUE;
6733                 break;
6734               }
6735           }
6736         j += d;
6737       }
6738   
6739   if (return_val)
6740     {
6741       if (COLUMN_LEFT_XPIXEL (clist, j) < CELL_SPACING + COLUMN_INSET)
6742         gtk_clist_moveto (clist, -1, j, 0, 0);
6743       else if (COLUMN_LEFT_XPIXEL(clist, j) + clist->column[j].area.width >
6744                clist->clist_window_width)
6745         {
6746           if (j == clist->columns-1)
6747             gtk_clist_moveto (clist, -1, j, 0, 0);
6748           else
6749             gtk_clist_moveto (clist, -1, j, 0, 1);
6750         }
6751     }
6752   return return_val;
6753 }
6754
6755 /* PRIVATE SCROLLING FUNCTIONS
6756  *   move_focus_row
6757  *   scroll_horizontal
6758  *   scroll_vertical
6759  *   move_horizontal
6760  *   move_vertical
6761  *   horizontal_timeout
6762  *   vertical_timeout
6763  *   remove_grab
6764  */
6765 static void
6766 move_focus_row (GtkCList      *clist,
6767                 GtkScrollType  scroll_type,
6768                 gfloat         position)
6769 {
6770   GtkWidget *widget;
6771
6772   g_return_if_fail (clist != 0);
6773   g_return_if_fail (GTK_IS_CLIST (clist));
6774
6775   widget = GTK_WIDGET (clist);
6776
6777   switch (scroll_type)
6778     {
6779     case GTK_SCROLL_STEP_BACKWARD:
6780       if (clist->focus_row <= 0)
6781         return;
6782       gtk_clist_draw_focus (widget);
6783       clist->focus_row--;
6784       gtk_clist_draw_focus (widget);
6785       break;
6786     case GTK_SCROLL_STEP_FORWARD:
6787       if (clist->focus_row >= clist->rows - 1)
6788         return;
6789       gtk_clist_draw_focus (widget);
6790       clist->focus_row++;
6791       gtk_clist_draw_focus (widget);
6792       break;
6793     case GTK_SCROLL_PAGE_BACKWARD:
6794       if (clist->focus_row <= 0)
6795         return;
6796       gtk_clist_draw_focus (widget);
6797       clist->focus_row = MAX (0, clist->focus_row -
6798                               (2 * clist->clist_window_height -
6799                                clist->row_height - CELL_SPACING) / 
6800                               (2 * (clist->row_height + CELL_SPACING)));
6801       gtk_clist_draw_focus (widget);
6802       break;
6803     case GTK_SCROLL_PAGE_FORWARD:
6804       if (clist->focus_row >= clist->rows - 1)
6805         return;
6806       gtk_clist_draw_focus (widget);
6807       clist->focus_row = MIN (clist->rows - 1, clist->focus_row + 
6808                               (2 * clist->clist_window_height -
6809                                clist->row_height - CELL_SPACING) / 
6810                               (2 * (clist->row_height + CELL_SPACING)));
6811       gtk_clist_draw_focus (widget);
6812       break;
6813     case GTK_SCROLL_JUMP:
6814       if (position >= 0 && position <= 1)
6815         {
6816           gtk_clist_draw_focus (widget);
6817           clist->focus_row = position * (clist->rows - 1);
6818           gtk_clist_draw_focus (widget);
6819         }
6820       break;
6821     default:
6822       break;
6823     }
6824 }
6825
6826 static void
6827 scroll_horizontal (GtkCList      *clist,
6828                    GtkScrollType  scroll_type,
6829                    gfloat         position)
6830 {
6831   gint column = 0;
6832
6833   g_return_if_fail (clist != 0);
6834   g_return_if_fail (GTK_IS_CLIST (clist));
6835
6836   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
6837     return;
6838
6839   switch (scroll_type)
6840     {
6841     case GTK_SCROLL_STEP_BACKWARD:
6842       column = COLUMN_FROM_XPIXEL (clist, 0);
6843       if (COLUMN_LEFT_XPIXEL (clist, column) - CELL_SPACING - COLUMN_INSET >= 0
6844           && column > 0)
6845         column--;
6846       break;
6847     case GTK_SCROLL_STEP_FORWARD:
6848       column =  COLUMN_FROM_XPIXEL (clist, clist->clist_window_width);
6849       if (column < 0)
6850         return;
6851       if (COLUMN_LEFT_XPIXEL (clist, column) +
6852           clist->column[column].area.width +
6853           CELL_SPACING + COLUMN_INSET - 1 <= clist->clist_window_width &&
6854           column < clist->columns - 1)
6855         column++;
6856       break;
6857     case GTK_SCROLL_PAGE_BACKWARD:
6858     case GTK_SCROLL_PAGE_FORWARD:
6859       return;
6860     case GTK_SCROLL_JUMP:
6861       if (position >= 0 && position <= 1)
6862         column = position * (clist->columns - 1);
6863       else
6864         return;
6865       break;
6866     default:
6867       break;
6868     }
6869
6870   if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET)
6871     gtk_clist_moveto (clist, -1, column, 0, 0);
6872   else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1
6873            + clist->column[column].area.width > clist->clist_window_width)
6874     {
6875       if (column == clist->columns - 1)
6876         gtk_clist_moveto (clist, -1, column, 0, 0);
6877       else
6878         gtk_clist_moveto (clist, -1, column, 0, 1);
6879     }
6880 }
6881
6882 static void
6883 scroll_vertical (GtkCList      *clist,
6884                  GtkScrollType  scroll_type,
6885                  gfloat         position)
6886 {
6887   gint old_focus_row;
6888
6889   g_return_if_fail (clist != NULL);
6890   g_return_if_fail (GTK_IS_CLIST (clist));
6891
6892   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
6893     return;
6894
6895   switch (clist->selection_mode)
6896     {
6897     case GTK_SELECTION_EXTENDED:
6898       if (clist->anchor >= 0)
6899         return;
6900     case GTK_SELECTION_BROWSE:
6901
6902       old_focus_row = clist->focus_row;
6903       move_focus_row (clist, scroll_type, position);
6904
6905       if (old_focus_row != clist->focus_row)
6906         {
6907           if (clist->selection_mode == GTK_SELECTION_BROWSE)
6908             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
6909                              old_focus_row, -1, NULL);
6910           else if (!GTK_CLIST_ADD_MODE(clist))
6911             {
6912               gtk_clist_unselect_all (clist);
6913               clist->undo_anchor = old_focus_row;
6914             }
6915         }
6916
6917       switch (gtk_clist_row_is_visible (clist, clist->focus_row))
6918         {
6919         case GTK_VISIBILITY_NONE:
6920           if (old_focus_row != clist->focus_row &&
6921               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
6922                 GTK_CLIST_ADD_MODE(clist)))
6923             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6924                              clist->focus_row, -1, NULL);
6925           switch (scroll_type)
6926             {
6927             case GTK_SCROLL_STEP_BACKWARD:
6928             case GTK_SCROLL_PAGE_BACKWARD:
6929               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6930               break;
6931             case GTK_SCROLL_STEP_FORWARD:
6932             case GTK_SCROLL_PAGE_FORWARD:
6933               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6934               break;
6935             case GTK_SCROLL_JUMP:
6936               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
6937               break;
6938             default:
6939               break;
6940             }
6941           break;
6942         case GTK_VISIBILITY_PARTIAL:
6943           switch (scroll_type)
6944             {
6945             case GTK_SCROLL_STEP_BACKWARD:
6946             case GTK_SCROLL_PAGE_BACKWARD:
6947               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6948               break;
6949             case GTK_SCROLL_STEP_FORWARD:
6950             case GTK_SCROLL_PAGE_FORWARD:
6951               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6952               break;
6953             case GTK_SCROLL_JUMP:
6954               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
6955               break;
6956             default:
6957               break;
6958             }
6959         default:
6960           if (old_focus_row != clist->focus_row &&
6961               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
6962                 GTK_CLIST_ADD_MODE(clist)))
6963             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6964                              clist->focus_row, -1, NULL);
6965           break;
6966         }
6967       break;
6968     default:
6969       move_focus_row (clist, scroll_type, position);
6970
6971       if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
6972           clist->clist_window_height)
6973         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
6974       else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
6975         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
6976       break;
6977     }
6978 }
6979
6980 static void
6981 move_horizontal (GtkCList *clist,
6982                  gint      diff)
6983 {
6984   gfloat value;
6985
6986   if (!clist->hadjustment)
6987     return;
6988
6989   value = CLAMP (clist->hadjustment->value + diff, 0.0,
6990                  clist->hadjustment->upper - clist->hadjustment->page_size);
6991   gtk_adjustment_set_value(clist->hadjustment, value);
6992 }
6993
6994 static void
6995 move_vertical (GtkCList *clist,
6996                gint      row,
6997                gfloat    align)
6998 {
6999   gfloat value;
7000
7001   if (!clist->vadjustment)
7002     return;
7003
7004   value = (ROW_TOP_YPIXEL (clist, row) - clist->voffset -
7005            align * (clist->clist_window_height - clist->row_height) +
7006            (2 * align - 1) * CELL_SPACING);
7007
7008   if (value + clist->vadjustment->page_size > clist->vadjustment->upper)
7009     value = clist->vadjustment->upper - clist->vadjustment->page_size;
7010
7011   gtk_adjustment_set_value(clist->vadjustment, value);
7012 }
7013
7014 static gint
7015 horizontal_timeout (GtkCList *clist)
7016 {
7017   gint x, y;
7018   GdkEventMotion event;
7019   GdkModifierType mask;
7020
7021   GDK_THREADS_ENTER ();
7022
7023   clist->htimer = 0;
7024   gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
7025
7026   event.is_hint = 0;
7027   event.x = x;
7028   event.y = y;
7029   event.state = mask;
7030
7031   gtk_clist_motion (GTK_WIDGET (clist), &event);
7032
7033   GDK_THREADS_LEAVE ();
7034   
7035   return FALSE;
7036 }
7037
7038 static gint
7039 vertical_timeout (GtkCList *clist)
7040 {
7041   gint x, y;
7042   GdkEventMotion event;
7043   GdkModifierType mask;
7044
7045   GDK_THREADS_ENTER ();
7046
7047   clist->vtimer = 0;
7048   gdk_window_get_pointer (clist->clist_window, &x, &y, &mask);
7049
7050   event.is_hint = 0;
7051   event.x = x;
7052   event.y = y;
7053   event.state = mask;
7054
7055   gtk_clist_motion (GTK_WIDGET (clist), &event);
7056
7057   GDK_THREADS_LEAVE ();
7058
7059   return FALSE;
7060 }
7061
7062 static void
7063 remove_grab (GtkCList *clist)
7064 {
7065   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
7066     {
7067       gtk_grab_remove (GTK_WIDGET (clist));
7068       gdk_pointer_ungrab (GDK_CURRENT_TIME);
7069     }
7070
7071   if (clist->htimer)
7072     {
7073       gtk_timeout_remove (clist->htimer);
7074       clist->htimer = 0;
7075     }
7076
7077   if (clist->vtimer)
7078     {
7079       gtk_timeout_remove (clist->vtimer);
7080       clist->vtimer = 0;
7081     }
7082 }
7083
7084 /* PUBLIC SORTING FUNCTIONS
7085  * gtk_clist_sort
7086  * gtk_clist_set_compare_func
7087  * gtk_clist_set_auto_sort
7088  * gtk_clist_set_sort_type
7089  * gtk_clist_set_sort_column
7090  */
7091 void
7092 gtk_clist_sort (GtkCList *clist)
7093 {
7094   g_return_if_fail (clist != NULL);
7095   g_return_if_fail (GTK_IS_CLIST (clist));
7096
7097   GTK_CLIST_CLASS_FW (clist)->sort_list (clist);
7098 }
7099
7100 void
7101 gtk_clist_set_compare_func (GtkCList            *clist,
7102                             GtkCListCompareFunc  cmp_func)
7103 {
7104   g_return_if_fail (clist != NULL);
7105   g_return_if_fail (GTK_IS_CLIST (clist));
7106
7107   clist->compare = (cmp_func) ? cmp_func : default_compare;
7108 }
7109
7110 void       
7111 gtk_clist_set_auto_sort (GtkCList *clist,
7112                          gboolean  auto_sort)
7113 {
7114   g_return_if_fail (clist != NULL);
7115   g_return_if_fail (GTK_IS_CLIST (clist));
7116   
7117   if (GTK_CLIST_AUTO_SORT(clist) && !auto_sort)
7118     GTK_CLIST_UNSET_FLAG (clist, CLIST_AUTO_SORT);
7119   else if (!GTK_CLIST_AUTO_SORT(clist) && auto_sort)
7120     {
7121       GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_SORT);
7122       gtk_clist_sort (clist);
7123     }
7124 }
7125
7126 void       
7127 gtk_clist_set_sort_type (GtkCList    *clist,
7128                          GtkSortType  sort_type)
7129 {
7130   g_return_if_fail (clist != NULL);
7131   g_return_if_fail (GTK_IS_CLIST (clist));
7132   
7133   clist->sort_type = sort_type;
7134 }
7135
7136 void
7137 gtk_clist_set_sort_column (GtkCList *clist,
7138                            gint      column)
7139 {
7140   g_return_if_fail (clist != NULL);
7141   g_return_if_fail (GTK_IS_CLIST (clist));
7142
7143   if (column < 0 || column >= clist->columns)
7144     return;
7145
7146   clist->sort_column = column;
7147 }
7148
7149 /* PRIVATE SORTING FUNCTIONS
7150  *   default_compare
7151  *   real_sort_list
7152  *   gtk_clist_merge
7153  *   gtk_clist_mergesort
7154  */
7155 static gint
7156 default_compare (GtkCList      *clist,
7157                  gconstpointer  ptr1,
7158                  gconstpointer  ptr2)
7159 {
7160   char *text1 = NULL;
7161   char *text2 = NULL;
7162
7163   GtkCListRow *row1 = (GtkCListRow *) ptr1;
7164   GtkCListRow *row2 = (GtkCListRow *) ptr2;
7165
7166   switch (row1->cell[clist->sort_column].type)
7167     {
7168     case GTK_CELL_TEXT:
7169       text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
7170       break;
7171     case GTK_CELL_PIXTEXT:
7172       text1 = GTK_CELL_PIXTEXT (row1->cell[clist->sort_column])->text;
7173       break;
7174     default:
7175       break;
7176     }
7177  
7178   switch (row2->cell[clist->sort_column].type)
7179     {
7180     case GTK_CELL_TEXT:
7181       text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
7182       break;
7183     case GTK_CELL_PIXTEXT:
7184       text2 = GTK_CELL_PIXTEXT (row2->cell[clist->sort_column])->text;
7185       break;
7186     default:
7187       break;
7188     }
7189
7190   if (!text2)
7191     return (text1 != NULL);
7192
7193   if (!text1)
7194     return -1;
7195
7196   return strcmp (text1, text2);
7197 }
7198
7199 static void
7200 real_sort_list (GtkCList *clist)
7201 {
7202   GList *list;
7203   GList *work;
7204   gint i;
7205
7206   g_return_if_fail (clist != NULL);
7207   g_return_if_fail (GTK_IS_CLIST (clist));
7208
7209   if (clist->rows <= 1)
7210     return;
7211
7212   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
7213     return;
7214
7215   gtk_clist_freeze (clist);
7216
7217   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_EXTENDED)
7218     {
7219       GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
7220       g_list_free (clist->undo_selection);
7221       g_list_free (clist->undo_unselection);
7222       clist->undo_selection = NULL;
7223       clist->undo_unselection = NULL;
7224     }
7225    
7226   clist->row_list = gtk_clist_mergesort (clist, clist->row_list, clist->rows);
7227
7228   work = clist->selection;
7229
7230   for (i = 0, list = clist->row_list; i < clist->rows; i++, list = list->next)
7231     {
7232       if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
7233         {
7234           work->data = GINT_TO_POINTER (i);
7235           work = work->next;
7236         }
7237       
7238       if (i == clist->rows - 1)
7239         clist->row_list_end = list;
7240     }
7241
7242   gtk_clist_thaw (clist);
7243 }
7244
7245 static GList *
7246 gtk_clist_merge (GtkCList *clist,
7247                  GList    *a,         /* first list to merge */
7248                  GList    *b)         /* second list to merge */
7249 {
7250   GList z = { 0 };                    /* auxiliary node */
7251   GList *c;
7252   gint cmp;
7253
7254   c = &z;
7255
7256   while (a || b)
7257     {
7258       if (a && !b)
7259         {
7260           c->next = a;
7261           a->prev = c;
7262           c = a;
7263           a = a->next;
7264           break;
7265         }
7266       else if (!a && b)
7267         {
7268           c->next = b;
7269           b->prev = c;
7270           c = b;
7271           b = b->next;
7272           break;
7273         }
7274       else /* a && b */
7275         {
7276           cmp = clist->compare (clist, GTK_CLIST_ROW (a), GTK_CLIST_ROW (b));
7277           if ((cmp >= 0 && clist->sort_type == GTK_SORT_DESCENDING) ||
7278               (cmp <= 0 && clist->sort_type == GTK_SORT_ASCENDING) ||
7279               (a && !b))
7280             {
7281               c->next = a;
7282               a->prev = c;
7283               c = a;
7284               a = a->next;
7285             }
7286           else
7287             {
7288               c->next = b;
7289               b->prev = c;
7290               c = b;
7291               b = b->next;
7292             }
7293         }
7294     }
7295
7296   return z.next;
7297 }
7298
7299 static GList *
7300 gtk_clist_mergesort (GtkCList *clist,
7301                      GList    *list,         /* the list to sort */
7302                      gint      num)          /* the list's length */
7303 {
7304   GList *half;
7305   gint i;
7306
7307   if (num == 1)
7308     {
7309       return list;
7310     }
7311   else
7312     {
7313       /* move "half" to the middle */
7314       half = list;
7315       for (i = 0; i < num / 2; i++)
7316         half = half->next;
7317
7318       /* cut the list in two */
7319       half->prev->next = NULL;
7320       half->prev = NULL;
7321
7322       /* recursively sort both lists */
7323       return gtk_clist_merge (clist,
7324                        gtk_clist_mergesort (clist, list, num / 2),
7325                        gtk_clist_mergesort (clist, half, num - num / 2));
7326     }
7327 }
7328
7329 /************************/
7330
7331 static void
7332 drag_source_info_destroy (gpointer data)
7333 {
7334   GtkCListCellInfo *info = data;
7335
7336   g_free (info);
7337 }
7338
7339 static void
7340 drag_dest_info_destroy (gpointer data)
7341 {
7342   GtkCListDestInfo *info = data;
7343
7344   g_free (info);
7345 }
7346
7347 static void
7348 gtk_clist_drag_begin (GtkWidget      *widget,
7349                       GdkDragContext *context)
7350 {
7351   GtkCList *clist;
7352   GtkCListCellInfo *info;
7353
7354   g_return_if_fail (widget != NULL);
7355   g_return_if_fail (GTK_IS_CLIST (widget));
7356   g_return_if_fail (context != NULL);
7357
7358   clist = GTK_CLIST (widget);
7359
7360   info = g_dataset_get_data (context, "gtk-clist-drag-source");
7361
7362   if (!info)
7363     {
7364       info = g_new (GtkCListCellInfo, 1);
7365
7366       if (clist->click_cell.row < 0)
7367         clist->click_cell.row = 0;
7368       else if (clist->click_cell.row >= clist->rows)
7369         clist->click_cell.row = clist->rows - 1;
7370       info->row = clist->click_cell.row;
7371       info->column = clist->click_cell.column;
7372
7373       g_dataset_set_data_full (context, "gtk-clist-drag-source", info,
7374                                drag_source_info_destroy);
7375     }
7376
7377   if (GTK_CLIST_USE_DRAG_ICONS (clist))
7378     gtk_drag_set_icon_default (context);
7379 }
7380
7381 static void
7382 gtk_clist_drag_end (GtkWidget      *widget,
7383                     GdkDragContext *context)
7384 {
7385   GtkCList *clist;
7386
7387   g_return_if_fail (widget != NULL);
7388   g_return_if_fail (GTK_IS_CLIST (widget));
7389   g_return_if_fail (context != NULL);
7390
7391   clist = GTK_CLIST (widget);
7392
7393   clist->click_cell.row = -1;
7394   clist->click_cell.column = -1;
7395 }
7396
7397 static void
7398 gtk_clist_drag_leave (GtkWidget      *widget,
7399                       GdkDragContext *context,
7400                       guint           time)
7401 {
7402   GtkCList *clist;
7403   GtkCListDestInfo *dest_info;
7404
7405   g_return_if_fail (widget != NULL);
7406   g_return_if_fail (GTK_IS_CLIST (widget));
7407   g_return_if_fail (context != NULL);
7408
7409   clist = GTK_CLIST (widget);
7410
7411   dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7412   
7413   if (dest_info)
7414     {
7415       if (dest_info->cell.row >= 0 &&
7416           GTK_CLIST_REORDERABLE(clist) &&
7417           gtk_drag_get_source_widget (context) == widget)
7418         {
7419           GList *list;
7420           GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
7421
7422           list = context->targets;
7423           while (list)
7424             {
7425               if (atom == GPOINTER_TO_INT (list->data))
7426                 {
7427                   GTK_CLIST_CLASS_FW (clist)->draw_drag_highlight
7428                     (clist,
7429                      g_list_nth (clist->row_list, dest_info->cell.row)->data,
7430                      dest_info->cell.row, dest_info->insert_pos);
7431                   break;
7432                 }
7433               list = list->next;
7434             }
7435         }
7436       g_dataset_remove_data (context, "gtk-clist-drag-dest");
7437     }
7438 }
7439
7440 static gint
7441 gtk_clist_drag_motion (GtkWidget      *widget,
7442                        GdkDragContext *context,
7443                        gint            x,
7444                        gint            y,
7445                        guint           time)
7446 {
7447   GtkCList *clist;
7448   gint row, column;
7449   GtkCListDestInfo *dest_info;
7450   gint h = 0;
7451   gint insert_pos = GTK_CLIST_DRAG_NONE;
7452   gint y_delta;
7453
7454   g_return_val_if_fail (widget != NULL, FALSE);
7455   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
7456
7457   clist = GTK_CLIST (widget);
7458
7459   y -= (GTK_CONTAINER (widget)->border_width +
7460         widget->style->klass->ythickness + clist->column_title_area.height);
7461   row = ROW_FROM_YPIXEL (clist, y);
7462
7463   if (row >= clist->rows)
7464     {
7465       row = clist->rows - 1;
7466       y = ROW_TOP_YPIXEL (clist, row) + clist->row_height;
7467     }
7468   if (row < -1)
7469     row = -1;
7470
7471   x -= GTK_CONTAINER (widget)->border_width + widget->style->klass->xthickness;
7472   column = COLUMN_FROM_XPIXEL (clist, x);
7473
7474   if (row >= 0)
7475     {
7476       y_delta = y - ROW_TOP_YPIXEL (clist, row);
7477       
7478       if (GTK_CLIST_DRAW_DRAG_RECT(clist))
7479         {
7480           insert_pos = GTK_CLIST_DRAG_INTO;
7481           h = clist->row_height / 4;
7482         }
7483       else if (GTK_CLIST_DRAW_DRAG_LINE(clist))
7484         {
7485           insert_pos = GTK_CLIST_DRAG_BEFORE;
7486           h = clist->row_height / 2;
7487         }
7488
7489       if (GTK_CLIST_DRAW_DRAG_LINE(clist))
7490         {
7491           if (y_delta < h)
7492             insert_pos = GTK_CLIST_DRAG_BEFORE;
7493           else if (clist->row_height - y_delta < h)
7494             insert_pos = GTK_CLIST_DRAG_AFTER;
7495         }
7496     }
7497
7498   dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7499
7500   if (!dest_info)
7501     {
7502       dest_info = g_new (GtkCListDestInfo, 1);
7503
7504       dest_info->insert_pos  = GTK_CLIST_DRAG_NONE;
7505       dest_info->cell.row    = -1;
7506       dest_info->cell.column = -1;
7507
7508       g_dataset_set_data_full (context, "gtk-clist-drag-dest", dest_info,
7509                                drag_dest_info_destroy);
7510     }
7511
7512   if (GTK_CLIST_REORDERABLE (clist))
7513     {
7514       GList *list;
7515       GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
7516
7517       list = context->targets;
7518       while (list)
7519         {
7520           if (atom == GPOINTER_TO_INT (list->data))
7521             break;
7522           list = list->next;
7523         }
7524
7525       if (list)
7526         {
7527           if (gtk_drag_get_source_widget (context) != widget ||
7528               insert_pos == GTK_CLIST_DRAG_NONE ||
7529               row == clist->click_cell.row ||
7530               (row == clist->click_cell.row - 1 &&
7531                insert_pos == GTK_CLIST_DRAG_AFTER) ||
7532               (row == clist->click_cell.row + 1 &&
7533                insert_pos == GTK_CLIST_DRAG_BEFORE))
7534             {
7535               if (dest_info->cell.row < 0)
7536                 {
7537                   gdk_drag_status (context, GDK_ACTION_DEFAULT, time);
7538                   return FALSE;
7539                 }
7540               return TRUE;
7541             }
7542                 
7543           if (row != dest_info->cell.row ||
7544               (row == dest_info->cell.row &&
7545                dest_info->insert_pos != insert_pos))
7546             {
7547               if (dest_info->cell.row >= 0)
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               dest_info->insert_pos  = insert_pos;
7554               dest_info->cell.row    = row;
7555               dest_info->cell.column = column;
7556               
7557               GTK_CLIST_CLASS_FW (clist)->draw_drag_highlight
7558                 (clist, g_list_nth (clist->row_list,
7559                                     dest_info->cell.row)->data,
7560                  dest_info->cell.row, dest_info->insert_pos);
7561
7562               gdk_drag_status (context, context->suggested_action, time);
7563             }
7564           return TRUE;
7565         }
7566     }
7567
7568   dest_info->insert_pos  = insert_pos;
7569   dest_info->cell.row    = row;
7570   dest_info->cell.column = column;
7571   return TRUE;
7572 }
7573
7574 static gboolean
7575 gtk_clist_drag_drop (GtkWidget      *widget,
7576                      GdkDragContext *context,
7577                      gint            x,
7578                      gint            y,
7579                      guint           time)
7580 {
7581   g_return_val_if_fail (widget != NULL, FALSE);
7582   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
7583   g_return_val_if_fail (context != NULL, FALSE);
7584
7585   if (GTK_CLIST_REORDERABLE (widget) &&
7586       gtk_drag_get_source_widget (context) == widget)
7587     {
7588       GList *list;
7589       GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
7590
7591       list = context->targets;
7592       while (list)
7593         {
7594           if (atom == GPOINTER_TO_INT (list->data))
7595             return TRUE;
7596           list = list->next;
7597         }
7598     }
7599   return FALSE;
7600 }
7601
7602 static void
7603 gtk_clist_drag_data_received (GtkWidget        *widget,
7604                               GdkDragContext   *context,
7605                               gint              x,
7606                               gint              y,
7607                               GtkSelectionData *selection_data,
7608                               guint             info,
7609                               guint32           time)
7610 {
7611   GtkCList *clist;
7612
7613   g_return_if_fail (widget != NULL);
7614   g_return_if_fail (GTK_IS_CLIST (widget));
7615   g_return_if_fail (context != NULL);
7616   g_return_if_fail (selection_data != NULL);
7617
7618   clist = GTK_CLIST (widget);
7619
7620   if (GTK_CLIST_REORDERABLE (clist) &&
7621       gtk_drag_get_source_widget (context) == widget &&
7622       selection_data->target ==
7623       gdk_atom_intern ("gtk-clist-drag-reorder", FALSE) &&
7624       selection_data->format == GTK_TYPE_POINTER &&
7625       selection_data->length == sizeof (GtkCListCellInfo))
7626     {
7627       GtkCListCellInfo *source_info;
7628       GtkCListDestInfo *dest_info;
7629
7630       source_info = (GtkCListCellInfo *)(selection_data->data);
7631       dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7632
7633       if (dest_info && source_info)
7634         {
7635           if (dest_info->insert_pos == GTK_CLIST_DRAG_AFTER)
7636             dest_info->cell.row++;
7637           if (source_info->row < dest_info->cell.row)
7638             dest_info->cell.row--;
7639
7640           if (dest_info->cell.row != source_info->row)
7641             gtk_clist_row_move (GTK_CLIST (widget),
7642                                 source_info->row,
7643                                 dest_info->cell.row);
7644           g_dataset_remove_data (context, "gtk-clist-drag-dest");
7645         }
7646     }
7647 }
7648
7649 static void  
7650 gtk_clist_drag_data_get (GtkWidget        *widget,
7651                          GdkDragContext   *context,
7652                          GtkSelectionData *selection_data,
7653                          guint             info,
7654                          guint             time)
7655 {
7656   g_return_if_fail (widget != NULL);
7657   g_return_if_fail (GTK_IS_CLIST (widget));
7658   g_return_if_fail (context != NULL);
7659   g_return_if_fail (selection_data != NULL);
7660
7661   if (selection_data->target ==
7662       gdk_atom_intern ("gtk-clist-drag-reorder", FALSE))
7663     {
7664       GtkCListCellInfo *info;
7665
7666       info = g_dataset_get_data (context, "gtk-clist-drag-source");
7667
7668       if (info)
7669         {
7670           GtkCListCellInfo *ret_info;
7671
7672           ret_info = g_new (GtkCListCellInfo, 1);
7673           ret_info->row = info->row;
7674           ret_info->column = info->column;
7675
7676           gtk_selection_data_set (selection_data,
7677                                   selection_data->target,
7678                                   GTK_TYPE_POINTER,
7679                                   (guchar *) ret_info,
7680                                   sizeof (GtkCListCellInfo));
7681         }
7682       else
7683         gtk_selection_data_set (selection_data, selection_data->target,
7684                                 GTK_TYPE_POINTER, NULL, 0);
7685     }
7686 }
7687
7688 static void
7689 draw_drag_highlight (GtkCList        *clist,
7690                      GtkCListRow     *dest_row,
7691                      gint             dest_row_number,
7692                      GtkCListDragPos  drag_pos)
7693 {
7694   gint y;
7695
7696   y = ROW_TOP_YPIXEL (clist, dest_row_number) - 1;
7697
7698   switch (drag_pos)
7699     {
7700     case GTK_CLIST_DRAG_NONE:
7701       break;
7702     case GTK_CLIST_DRAG_AFTER:
7703       y += clist->row_height + 1;
7704     case GTK_CLIST_DRAG_BEFORE:
7705       gdk_draw_line (clist->clist_window, clist->xor_gc,
7706                      0, y, clist->clist_window_width, y);
7707       break;
7708     case GTK_CLIST_DRAG_INTO:
7709       gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
7710                           clist->clist_window_width - 1, clist->row_height);
7711       break;
7712     }
7713 }
7714
7715 void
7716 gtk_clist_set_reorderable (GtkCList *clist, 
7717                            gboolean  reorderable)
7718 {
7719   GtkWidget *widget;
7720
7721   g_return_if_fail (clist != NULL);
7722   g_return_if_fail (GTK_IS_CLIST (clist));
7723
7724   if ((GTK_CLIST_REORDERABLE(clist) != 0) == reorderable)
7725     return;
7726
7727   widget = GTK_WIDGET (clist);
7728
7729   if (reorderable)
7730     {
7731       GTK_CLIST_SET_FLAG (clist, CLIST_REORDERABLE);
7732       gtk_drag_dest_set (widget,
7733                          GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
7734                          &clist_target_table, 1, GDK_ACTION_MOVE);
7735     }
7736   else
7737     {
7738       GTK_CLIST_UNSET_FLAG (clist, CLIST_REORDERABLE);
7739       gtk_drag_dest_unset (GTK_WIDGET (clist));
7740     }
7741 }
7742
7743 void
7744 gtk_clist_set_use_drag_icons (GtkCList *clist,
7745                               gboolean  use_icons)
7746 {
7747   g_return_if_fail (clist != NULL);
7748   g_return_if_fail (GTK_IS_CLIST (clist));
7749
7750   if (use_icons != 0)
7751     GTK_CLIST_SET_FLAG (clist, CLIST_USE_DRAG_ICONS);
7752   else
7753     GTK_CLIST_UNSET_FLAG (clist, CLIST_USE_DRAG_ICONS);
7754 }
7755
7756 void
7757 gtk_clist_set_button_actions (GtkCList *clist,
7758                               guint     button,
7759                               guint8    button_actions)
7760 {
7761   g_return_if_fail (clist != NULL);
7762   g_return_if_fail (GTK_IS_CLIST (clist));
7763   
7764   if (button <= MAX_BUTTON)
7765     {
7766       if (gdk_pointer_is_grabbed () || GTK_WIDGET_HAS_GRAB (clist))
7767         {
7768           remove_grab (clist);
7769           clist->drag_button = 0;
7770         }
7771
7772       if (clist->anchor >= 0 &&
7773           clist->selection_mode == GTK_SELECTION_EXTENDED)
7774         GTK_CLIST_CLASS_FW (clist)->resync_selection (clist, NULL);
7775
7776       clist->button_actions[button] = button_actions;
7777     }
7778 }