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