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