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