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