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