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