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