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