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