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