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