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