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