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