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