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