]> Pileus Git - ~andy/gtk/blob - gtk/gtkclist.c
Remove all references to offscreen flag which was no longer used.
[~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->klass->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       context = gtk_widget_create_pango_context (GTK_WIDGET (clist));
2526       pango_context_set_font_description (context, style->font_desc);
2527       
2528       layout = pango_layout_new (context);
2529       pango_layout_set_text (layout, ((cell->type == GTK_CELL_PIXTEXT) ?
2530                                       GTK_CELL_PIXTEXT (*cell)->text :
2531                                       GTK_CELL_TEXT (*cell)->text), -1);
2532       
2533       pango_context_unref (context);
2534
2535       return layout;
2536       
2537     default:
2538       return NULL;
2539     }
2540 }
2541
2542 static void
2543 cell_size_request (GtkCList       *clist,
2544                    GtkCListRow    *clist_row,
2545                    gint            column,
2546                    GtkRequisition *requisition)
2547 {
2548   gint width;
2549   gint height;
2550   PangoLayout *layout;
2551   PangoRectangle logical_rect;
2552
2553   g_return_if_fail (clist != NULL);
2554   g_return_if_fail (GTK_IS_CLIST (clist));
2555   g_return_if_fail (requisition != NULL);
2556
2557   layout = _gtk_clist_create_cell_layout (clist, clist_row, column);
2558   if (layout)
2559     {
2560       pango_layout_get_extents (layout, NULL, &logical_rect);
2561       
2562       requisition->width = logical_rect.width / PANGO_SCALE;
2563       requisition->height = logical_rect.height / PANGO_SCALE;
2564       
2565       pango_layout_unref (layout);
2566     }
2567   else
2568     {
2569       requisition->width  = 0;
2570       requisition->height = 0;
2571     }
2572
2573   if (layout && clist_row->cell[column].type == GTK_CELL_PIXTEXT)
2574     requisition->width += GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing;
2575
2576   switch (clist_row->cell[column].type)
2577     {
2578     case GTK_CELL_PIXTEXT:
2579     case GTK_CELL_PIXMAP:
2580       gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap,
2581                            &width, &height);
2582       requisition->width += width;
2583       requisition->height = MAX (requisition->height, height);
2584       break;
2585       
2586     default:
2587       break;
2588     }
2589
2590   requisition->width  += clist_row->cell[column].horizontal;
2591   requisition->height += clist_row->cell[column].vertical;
2592 }
2593
2594 /* PUBLIC INSERT/REMOVE ROW FUNCTIONS
2595  *   gtk_clist_prepend
2596  *   gtk_clist_append
2597  *   gtk_clist_insert
2598  *   gtk_clist_remove
2599  *   gtk_clist_clear
2600  */
2601 gint
2602 gtk_clist_prepend (GtkCList    *clist,
2603                    gchar       *text[])
2604 {
2605   g_return_val_if_fail (clist != NULL, -1);
2606   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2607   g_return_val_if_fail (text != NULL, -1);
2608
2609   return GTK_CLIST_GET_CLASS (clist)->insert_row (clist, 0, text);
2610 }
2611
2612 gint
2613 gtk_clist_append (GtkCList    *clist,
2614                   gchar       *text[])
2615 {
2616   g_return_val_if_fail (clist != NULL, -1);
2617   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2618   g_return_val_if_fail (text != NULL, -1);
2619
2620   return GTK_CLIST_GET_CLASS (clist)->insert_row (clist, clist->rows, text);
2621 }
2622
2623 gint
2624 gtk_clist_insert (GtkCList    *clist,
2625                   gint         row,
2626                   gchar       *text[])
2627 {
2628   g_return_val_if_fail (clist != NULL, -1);
2629   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2630   g_return_val_if_fail (text != NULL, -1);
2631
2632   if (row < 0 || row > clist->rows)
2633     row = clist->rows;
2634
2635   return GTK_CLIST_GET_CLASS (clist)->insert_row (clist, row, text);
2636 }
2637
2638 void
2639 gtk_clist_remove (GtkCList *clist,
2640                   gint      row)
2641 {
2642   GTK_CLIST_GET_CLASS (clist)->remove_row (clist, row);
2643 }
2644
2645 void
2646 gtk_clist_clear (GtkCList *clist)
2647 {
2648   g_return_if_fail (clist != NULL);
2649   g_return_if_fail (GTK_IS_CLIST (clist));
2650   
2651   GTK_CLIST_GET_CLASS (clist)->clear (clist);
2652 }
2653
2654 /* PRIVATE INSERT/REMOVE ROW FUNCTIONS
2655  *   real_insert_row
2656  *   real_remove_row
2657  *   real_clear
2658  *   real_row_move
2659  */
2660 static gint
2661 real_insert_row (GtkCList *clist,
2662                  gint      row,
2663                  gchar    *text[])
2664 {
2665   gint i;
2666   GtkCListRow *clist_row;
2667
2668   g_return_val_if_fail (clist != NULL, -1);
2669   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
2670   g_return_val_if_fail (text != NULL, -1);
2671
2672   /* return if out of bounds */
2673   if (row < 0 || row > clist->rows)
2674     return -1;
2675
2676   /* create the row */
2677   clist_row = row_new (clist);
2678
2679   /* set the text in the row's columns */
2680   for (i = 0; i < clist->columns; i++)
2681     if (text[i])
2682       GTK_CLIST_GET_CLASS (clist)->set_cell_contents
2683         (clist, clist_row, i, GTK_CELL_TEXT, text[i], 0, NULL ,NULL);
2684
2685   if (!clist->rows)
2686     {
2687       clist->row_list = g_list_append (clist->row_list, clist_row);
2688       clist->row_list_end = clist->row_list;
2689     }
2690   else
2691     {
2692       if (GTK_CLIST_AUTO_SORT(clist))   /* override insertion pos */
2693         {
2694           GList *work;
2695           
2696           row = 0;
2697           work = clist->row_list;
2698           
2699           if (clist->sort_type == GTK_SORT_ASCENDING)
2700             {
2701               while (row < clist->rows &&
2702                      clist->compare (clist, clist_row,
2703                                      GTK_CLIST_ROW (work)) > 0)
2704                 {
2705                   row++;
2706                   work = work->next;
2707                 }
2708             }
2709           else
2710             {
2711               while (row < clist->rows &&
2712                      clist->compare (clist, clist_row,
2713                                      GTK_CLIST_ROW (work)) < 0)
2714                 {
2715                   row++;
2716                   work = work->next;
2717                 }
2718             }
2719         }
2720       
2721       /* reset the row end pointer if we're inserting at the end of the list */
2722       if (row == clist->rows)
2723         clist->row_list_end = (g_list_append (clist->row_list_end,
2724                                               clist_row))->next;
2725       else
2726         clist->row_list = g_list_insert (clist->row_list, clist_row, row);
2727
2728     }
2729   clist->rows++;
2730
2731   if (row < ROW_FROM_YPIXEL (clist, 0))
2732     clist->voffset -= (clist->row_height + CELL_SPACING);
2733
2734   /* syncronize the selection list */
2735   sync_selection (clist, row, SYNC_INSERT);
2736
2737   if (clist->rows == 1)
2738     {
2739       clist->focus_row = 0;
2740       if (clist->selection_mode == GTK_SELECTION_BROWSE)
2741         gtk_clist_select_row (clist, 0, -1);
2742     }
2743
2744   /* redraw the list if it isn't frozen */
2745   if (CLIST_UNFROZEN (clist))
2746     {
2747       adjust_adjustments (clist, FALSE);
2748
2749       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2750         draw_rows (clist, NULL);
2751     }
2752
2753   return row;
2754 }
2755
2756 static void
2757 real_remove_row (GtkCList *clist,
2758                  gint      row)
2759 {
2760   gint was_visible, was_selected;
2761   GList *list;
2762   GtkCListRow *clist_row;
2763
2764   g_return_if_fail (clist != NULL);
2765   g_return_if_fail (GTK_IS_CLIST (clist));
2766
2767   /* return if out of bounds */
2768   if (row < 0 || row > (clist->rows - 1))
2769     return;
2770
2771   was_visible = (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE);
2772   was_selected = 0;
2773
2774   /* get the row we're going to delete */
2775   list = ROW_ELEMENT (clist, row);
2776   g_assert (list != NULL);
2777   clist_row = list->data;
2778
2779   /* if we're removing a selected row, we have to make sure
2780    * it's properly unselected, and then sync up the clist->selected
2781    * list to reflect the deincrimented indexies of rows after the
2782    * removal */
2783   if (clist_row->state == GTK_STATE_SELECTED)
2784     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
2785                      row, -1, NULL);
2786
2787   /* reset the row end pointer if we're removing at the end of the list */
2788   clist->rows--;
2789   if (clist->row_list == list)
2790     clist->row_list = g_list_next (list);
2791   if (clist->row_list_end == list)
2792     clist->row_list_end = g_list_previous (list);
2793   g_list_remove (list, clist_row);
2794
2795   /*if (clist->focus_row >=0 &&
2796       (row <= clist->focus_row || clist->focus_row >= clist->rows))
2797       clist->focus_row--;*/
2798
2799   if (row < ROW_FROM_YPIXEL (clist, 0))
2800     clist->voffset += clist->row_height + CELL_SPACING;
2801
2802   sync_selection (clist, row, SYNC_REMOVE);
2803
2804   if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection &&
2805       clist->focus_row >= 0)
2806     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
2807                      clist->focus_row, -1, NULL);
2808
2809   /* toast the row */
2810   row_delete (clist, clist_row);
2811
2812   /* redraw the row if it isn't frozen */
2813   if (CLIST_UNFROZEN (clist))
2814     {
2815       adjust_adjustments (clist, FALSE);
2816
2817       if (was_visible)
2818         draw_rows (clist, NULL);
2819     }
2820 }
2821
2822 static void
2823 real_clear (GtkCList *clist)
2824 {
2825   GList *list;
2826   GList *free_list;
2827   gint i;
2828
2829   g_return_if_fail (clist != NULL);
2830   g_return_if_fail (GTK_IS_CLIST (clist));
2831
2832   /* free up the selection list */
2833   g_list_free (clist->selection);
2834   g_list_free (clist->undo_selection);
2835   g_list_free (clist->undo_unselection);
2836
2837   clist->selection = NULL;
2838   clist->selection_end = NULL;
2839   clist->undo_selection = NULL;
2840   clist->undo_unselection = NULL;
2841   clist->voffset = 0;
2842   clist->focus_row = -1;
2843   clist->anchor = -1;
2844   clist->undo_anchor = -1;
2845   clist->anchor_state = GTK_STATE_SELECTED;
2846   clist->drag_pos = -1;
2847
2848   /* remove all the rows */
2849   GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
2850   free_list = clist->row_list;
2851   clist->row_list = NULL;
2852   clist->row_list_end = NULL;
2853   clist->rows = 0;
2854   for (list = free_list; list; list = list->next)
2855     row_delete (clist, GTK_CLIST_ROW (list));
2856   g_list_free (free_list);
2857   GTK_CLIST_UNSET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
2858   for (i = 0; i < clist->columns; i++)
2859     if (clist->column[i].auto_resize)
2860       {
2861         if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button)
2862           gtk_clist_set_column_width
2863             (clist, i, (clist->column[i].button->requisition.width -
2864                         (CELL_SPACING + (2 * COLUMN_INSET))));
2865         else
2866           gtk_clist_set_column_width (clist, i, 0);
2867       }
2868   /* zero-out the scrollbars */
2869   if (clist->vadjustment)
2870     {
2871       gtk_adjustment_set_value (clist->vadjustment, 0.0);
2872       CLIST_REFRESH (clist);
2873     }
2874   else
2875     gtk_widget_queue_resize (GTK_WIDGET (clist));
2876 }
2877
2878 static void
2879 real_row_move (GtkCList *clist,
2880                gint      source_row,
2881                gint      dest_row)
2882 {
2883   GtkCListRow *clist_row;
2884   GList *list;
2885   gint first, last;
2886   gint d;
2887
2888   g_return_if_fail (clist != NULL);
2889   g_return_if_fail (GTK_IS_CLIST (clist));
2890
2891   if (GTK_CLIST_AUTO_SORT(clist))
2892     return;
2893
2894   if (source_row < 0 || source_row >= clist->rows ||
2895       dest_row   < 0 || dest_row   >= clist->rows ||
2896       source_row == dest_row)
2897     return;
2898
2899   gtk_clist_freeze (clist);
2900
2901   /* unlink source row */
2902   clist_row = ROW_ELEMENT (clist, source_row)->data;
2903   if (source_row == clist->rows - 1)
2904     clist->row_list_end = clist->row_list_end->prev;
2905   clist->row_list = g_list_remove (clist->row_list, clist_row);
2906   clist->rows--;
2907
2908   /* relink source row */
2909   clist->row_list = g_list_insert (clist->row_list, clist_row, dest_row);
2910   if (dest_row == clist->rows)
2911     clist->row_list_end = clist->row_list_end->next;
2912   clist->rows++;
2913
2914   /* sync selection */
2915   if (source_row > dest_row)
2916     {
2917       first = dest_row;
2918       last  = source_row;
2919       d = 1;
2920     }
2921   else
2922     {
2923       first = source_row;
2924       last  = dest_row;
2925       d = -1;
2926     }
2927
2928   for (list = clist->selection; list; list = list->next)
2929     {
2930       if (list->data == GINT_TO_POINTER (source_row))
2931         list->data = GINT_TO_POINTER (dest_row);
2932       else if (first <= GPOINTER_TO_INT (list->data) &&
2933                last >= GPOINTER_TO_INT (list->data))
2934         list->data = GINT_TO_POINTER (GPOINTER_TO_INT (list->data) + d);
2935     }
2936   
2937   if (clist->focus_row == source_row)
2938     clist->focus_row = dest_row;
2939   else if (clist->focus_row > first)
2940     clist->focus_row += d;
2941
2942   gtk_clist_thaw (clist);
2943 }
2944
2945 /* PUBLIC ROW FUNCTIONS
2946  *   gtk_clist_moveto
2947  *   gtk_clist_set_row_height
2948  *   gtk_clist_set_row_data
2949  *   gtk_clist_set_row_data_full
2950  *   gtk_clist_get_row_data
2951  *   gtk_clist_find_row_from_data
2952  *   gtk_clist_swap_rows
2953  *   gtk_clist_row_move
2954  *   gtk_clist_row_is_visible
2955  *   gtk_clist_set_foreground
2956  *   gtk_clist_set_background
2957  */
2958 void
2959 gtk_clist_moveto (GtkCList *clist,
2960                   gint      row,
2961                   gint      column,
2962                   gfloat    row_align,
2963                   gfloat    col_align)
2964 {
2965   g_return_if_fail (clist != NULL);
2966   g_return_if_fail (GTK_IS_CLIST (clist));
2967
2968   if (row < -1 || row >= clist->rows)
2969     return;
2970   if (column < -1 || column >= clist->columns)
2971     return;
2972
2973   row_align = CLAMP (row_align, 0, 1);
2974   col_align = CLAMP (col_align, 0, 1);
2975
2976   /* adjust horizontal scrollbar */
2977   if (clist->hadjustment && column >= 0)
2978     {
2979       gint x;
2980
2981       x = (COLUMN_LEFT (clist, column) - CELL_SPACING - COLUMN_INSET -
2982            (col_align * (clist->clist_window_width - 2 * COLUMN_INSET -
2983                          CELL_SPACING - clist->column[column].area.width)));
2984       if (x < 0)
2985         gtk_adjustment_set_value (clist->hadjustment, 0.0);
2986       else if (x > LIST_WIDTH (clist) - clist->clist_window_width)
2987         gtk_adjustment_set_value 
2988           (clist->hadjustment, LIST_WIDTH (clist) - clist->clist_window_width);
2989       else
2990         gtk_adjustment_set_value (clist->hadjustment, x);
2991     }
2992
2993   /* adjust vertical scrollbar */
2994   if (clist->vadjustment && row >= 0)
2995     move_vertical (clist, row, row_align);
2996 }
2997
2998 void
2999 gtk_clist_set_row_height (GtkCList *clist,
3000                           guint     height)
3001 {
3002   GtkWidget *widget;
3003
3004   g_return_if_fail (clist != NULL);
3005   g_return_if_fail (GTK_IS_CLIST (clist));
3006
3007   widget = GTK_WIDGET (clist);
3008
3009   if (height > 0)
3010     {
3011       clist->row_height = height;
3012       GTK_CLIST_SET_FLAG (clist, CLIST_ROW_HEIGHT_SET);
3013     }
3014   else
3015     {
3016       GTK_CLIST_UNSET_FLAG (clist, CLIST_ROW_HEIGHT_SET);
3017       clist->row_height = 0;
3018     }
3019
3020   if (widget->style->font_desc)
3021     {
3022       PangoContext *context = gtk_widget_create_pango_context (widget);
3023       PangoFontMetrics metrics;
3024       PangoFont *font = pango_context_load_font (context, widget->style->font_desc);
3025       gchar *lang = pango_context_get_lang (context);
3026
3027       pango_font_get_metrics (font, lang, &metrics);
3028       
3029       g_free (lang);
3030       g_object_unref (G_OBJECT (font));
3031       pango_context_unref (context);
3032       
3033       if (!GTK_CLIST_ROW_HEIGHT_SET(clist))
3034         clist->row_height = (metrics.ascent + metrics.descent) / PANGO_SCALE;
3035     }
3036       
3037   CLIST_REFRESH (clist);
3038 }
3039
3040 void
3041 gtk_clist_set_row_data (GtkCList *clist,
3042                         gint      row,
3043                         gpointer  data)
3044 {
3045   gtk_clist_set_row_data_full (clist, row, data, NULL);
3046 }
3047
3048 void
3049 gtk_clist_set_row_data_full (GtkCList         *clist,
3050                              gint              row,
3051                              gpointer          data,
3052                              GtkDestroyNotify  destroy)
3053 {
3054   GtkCListRow *clist_row;
3055
3056   g_return_if_fail (clist != NULL);
3057   g_return_if_fail (GTK_IS_CLIST (clist));
3058
3059   if (row < 0 || row > (clist->rows - 1))
3060     return;
3061
3062   clist_row = ROW_ELEMENT (clist, row)->data;
3063
3064   if (clist_row->destroy)
3065     clist_row->destroy (clist_row->data);
3066   
3067   clist_row->data = data;
3068   clist_row->destroy = destroy;
3069 }
3070
3071 gpointer
3072 gtk_clist_get_row_data (GtkCList *clist,
3073                         gint      row)
3074 {
3075   GtkCListRow *clist_row;
3076
3077   g_return_val_if_fail (clist != NULL, NULL);
3078   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
3079
3080   if (row < 0 || row > (clist->rows - 1))
3081     return NULL;
3082
3083   clist_row = ROW_ELEMENT (clist, row)->data;
3084   return clist_row->data;
3085 }
3086
3087 gint
3088 gtk_clist_find_row_from_data (GtkCList *clist,
3089                               gpointer  data)
3090 {
3091   GList *list;
3092   gint n;
3093
3094   g_return_val_if_fail (clist != NULL, -1);
3095   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
3096
3097   for (n = 0, list = clist->row_list; list; n++, list = list->next)
3098     if (GTK_CLIST_ROW (list)->data == data)
3099       return n;
3100
3101   return -1;
3102 }
3103
3104 void 
3105 gtk_clist_swap_rows (GtkCList *clist,
3106                      gint      row1, 
3107                      gint      row2)
3108 {
3109   gint first, last;
3110
3111   g_return_if_fail (clist != NULL);
3112   g_return_if_fail (GTK_IS_CLIST (clist));
3113   g_return_if_fail (row1 != row2);
3114
3115   if (GTK_CLIST_AUTO_SORT(clist))
3116     return;
3117
3118   gtk_clist_freeze (clist);
3119
3120   first = MIN (row1, row2);
3121   last  = MAX (row1, row2);
3122
3123   gtk_clist_row_move (clist, last, first);
3124   gtk_clist_row_move (clist, first + 1, last);
3125   
3126   gtk_clist_thaw (clist);
3127 }
3128
3129 void
3130 gtk_clist_row_move (GtkCList *clist,
3131                     gint      source_row,
3132                     gint      dest_row)
3133 {
3134   g_return_if_fail (clist != NULL);
3135   g_return_if_fail (GTK_IS_CLIST (clist));
3136
3137   if (GTK_CLIST_AUTO_SORT(clist))
3138     return;
3139
3140   if (source_row < 0 || source_row >= clist->rows ||
3141       dest_row   < 0 || dest_row   >= clist->rows ||
3142       source_row == dest_row)
3143     return;
3144
3145   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[ROW_MOVE],
3146                    source_row, dest_row);
3147 }
3148
3149 GtkVisibility
3150 gtk_clist_row_is_visible (GtkCList *clist,
3151                           gint      row)
3152 {
3153   gint top;
3154
3155   g_return_val_if_fail (clist != NULL, 0);
3156   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
3157
3158   if (row < 0 || row >= clist->rows)
3159     return GTK_VISIBILITY_NONE;
3160
3161   if (clist->row_height == 0)
3162     return GTK_VISIBILITY_NONE;
3163
3164   if (row < ROW_FROM_YPIXEL (clist, 0))
3165     return GTK_VISIBILITY_NONE;
3166
3167   if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height))
3168     return GTK_VISIBILITY_NONE;
3169
3170   top = ROW_TOP_YPIXEL (clist, row);
3171
3172   if ((top < 0)
3173       || ((top + clist->row_height) >= clist->clist_window_height))
3174     return GTK_VISIBILITY_PARTIAL;
3175
3176   return GTK_VISIBILITY_FULL;
3177 }
3178
3179 void
3180 gtk_clist_set_foreground (GtkCList *clist,
3181                           gint      row,
3182                           GdkColor *color)
3183 {
3184   GtkCListRow *clist_row;
3185
3186   g_return_if_fail (clist != NULL);
3187   g_return_if_fail (GTK_IS_CLIST (clist));
3188
3189   if (row < 0 || row >= clist->rows)
3190     return;
3191
3192   clist_row = ROW_ELEMENT (clist, row)->data;
3193
3194   if (color)
3195     {
3196       clist_row->foreground = *color;
3197       clist_row->fg_set = TRUE;
3198       if (GTK_WIDGET_REALIZED (clist))
3199         gdk_color_alloc (gtk_widget_get_colormap (GTK_WIDGET (clist)),
3200                          &clist_row->foreground);
3201     }
3202   else
3203     clist_row->fg_set = FALSE;
3204
3205   if (CLIST_UNFROZEN (clist) && gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3206     GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3207 }
3208
3209 void
3210 gtk_clist_set_background (GtkCList *clist,
3211                           gint      row,
3212                           GdkColor *color)
3213 {
3214   GtkCListRow *clist_row;
3215
3216   g_return_if_fail (clist != NULL);
3217   g_return_if_fail (GTK_IS_CLIST (clist));
3218
3219   if (row < 0 || row >= clist->rows)
3220     return;
3221
3222   clist_row = ROW_ELEMENT (clist, row)->data;
3223
3224   if (color)
3225     {
3226       clist_row->background = *color;
3227       clist_row->bg_set = TRUE;
3228       if (GTK_WIDGET_REALIZED (clist))
3229         gdk_color_alloc (gtk_widget_get_colormap (GTK_WIDGET (clist)),
3230                          &clist_row->background);
3231     }
3232   else
3233     clist_row->bg_set = FALSE;
3234
3235   if (CLIST_UNFROZEN (clist)
3236       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3237     GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3238 }
3239
3240 /* PUBLIC ROW/CELL STYLE FUNCTIONS
3241  *   gtk_clist_set_cell_style
3242  *   gtk_clist_get_cell_style
3243  *   gtk_clist_set_row_style
3244  *   gtk_clist_get_row_style
3245  */
3246 void
3247 gtk_clist_set_cell_style (GtkCList *clist,
3248                           gint      row,
3249                           gint      column,
3250                           GtkStyle *style)
3251 {
3252   GtkRequisition requisition = { 0 };
3253   GtkCListRow *clist_row;
3254
3255   g_return_if_fail (clist != NULL);
3256   g_return_if_fail (GTK_IS_CLIST (clist));
3257
3258   if (row < 0 || row >= clist->rows)
3259     return;
3260   if (column < 0 || column >= clist->columns)
3261     return;
3262
3263   clist_row = ROW_ELEMENT (clist, row)->data;
3264
3265   if (clist_row->cell[column].style == style)
3266     return;
3267
3268   if (clist->column[column].auto_resize &&
3269       !GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
3270     GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3271                                                    column, &requisition);
3272
3273   if (clist_row->cell[column].style)
3274     {
3275       if (GTK_WIDGET_REALIZED (clist))
3276         gtk_style_detach (clist_row->cell[column].style);
3277       gtk_style_unref (clist_row->cell[column].style);
3278     }
3279
3280   clist_row->cell[column].style = style;
3281
3282   if (clist_row->cell[column].style)
3283     {
3284       gtk_style_ref (clist_row->cell[column].style);
3285       
3286       if (GTK_WIDGET_REALIZED (clist))
3287         clist_row->cell[column].style =
3288           gtk_style_attach (clist_row->cell[column].style,
3289                             clist->clist_window);
3290     }
3291
3292   column_auto_resize (clist, clist_row, column, requisition.width);
3293
3294   /* redraw the list if it's not frozen */
3295   if (CLIST_UNFROZEN (clist))
3296     {
3297       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3298         GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3299     }
3300 }
3301
3302 GtkStyle *
3303 gtk_clist_get_cell_style (GtkCList *clist,
3304                           gint      row,
3305                           gint      column)
3306 {
3307   GtkCListRow *clist_row;
3308
3309   g_return_val_if_fail (clist != NULL, NULL);
3310   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
3311
3312   if (row < 0 || row >= clist->rows || column < 0 || column >= clist->columns)
3313     return NULL;
3314
3315   clist_row = ROW_ELEMENT (clist, row)->data;
3316
3317   return clist_row->cell[column].style;
3318 }
3319
3320 void
3321 gtk_clist_set_row_style (GtkCList *clist,
3322                          gint      row,
3323                          GtkStyle *style)
3324 {
3325   GtkRequisition requisition;
3326   GtkCListRow *clist_row;
3327   gint *old_width;
3328   gint i;
3329
3330   g_return_if_fail (clist != NULL);
3331   g_return_if_fail (GTK_IS_CLIST (clist));
3332
3333   if (row < 0 || row >= clist->rows)
3334     return;
3335
3336   clist_row = ROW_ELEMENT (clist, row)->data;
3337
3338   if (clist_row->style == style)
3339     return;
3340
3341   old_width = g_new (gint, clist->columns);
3342
3343   if (!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
3344     {
3345       for (i = 0; i < clist->columns; i++)
3346         if (clist->column[i].auto_resize)
3347           {
3348             GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3349                                                            i, &requisition);
3350             old_width[i] = requisition.width;
3351           }
3352     }
3353
3354   if (clist_row->style)
3355     {
3356       if (GTK_WIDGET_REALIZED (clist))
3357         gtk_style_detach (clist_row->style);
3358       gtk_style_unref (clist_row->style);
3359     }
3360
3361   clist_row->style = style;
3362
3363   if (clist_row->style)
3364     {
3365       gtk_style_ref (clist_row->style);
3366       
3367       if (GTK_WIDGET_REALIZED (clist))
3368         clist_row->style = gtk_style_attach (clist_row->style,
3369                                              clist->clist_window);
3370     }
3371
3372   if (GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
3373     for (i = 0; i < clist->columns; i++)
3374       column_auto_resize (clist, clist_row, i, old_width[i]);
3375
3376   g_free (old_width);
3377
3378   /* redraw the list if it's not frozen */
3379   if (CLIST_UNFROZEN (clist))
3380     {
3381       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3382         GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3383     }
3384 }
3385
3386 GtkStyle *
3387 gtk_clist_get_row_style (GtkCList *clist,
3388                          gint      row)
3389 {
3390   GtkCListRow *clist_row;
3391
3392   g_return_val_if_fail (clist != NULL, NULL);
3393   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
3394
3395   if (row < 0 || row >= clist->rows)
3396     return NULL;
3397
3398   clist_row = ROW_ELEMENT (clist, row)->data;
3399
3400   return clist_row->style;
3401 }
3402
3403 /* PUBLIC SELECTION FUNCTIONS
3404  *   gtk_clist_set_selectable
3405  *   gtk_clist_get_selectable
3406  *   gtk_clist_select_row
3407  *   gtk_clist_unselect_row
3408  *   gtk_clist_select_all
3409  *   gtk_clist_unselect_all
3410  *   gtk_clist_undo_selection
3411  */
3412 void
3413 gtk_clist_set_selectable (GtkCList *clist,
3414                           gint      row,
3415                           gboolean  selectable)
3416 {
3417   GtkCListRow *clist_row;
3418
3419   g_return_if_fail (clist != NULL);
3420   g_return_if_fail (GTK_IS_CLIST (clist));
3421
3422   if (row < 0 || row >= clist->rows)
3423     return;
3424
3425   clist_row = ROW_ELEMENT (clist, row)->data;
3426
3427   if (selectable == clist_row->selectable)
3428     return;
3429
3430   clist_row->selectable = selectable;
3431
3432   if (!selectable && clist_row->state == GTK_STATE_SELECTED)
3433     {
3434       if (clist->anchor >= 0 &&
3435           clist->selection_mode == GTK_SELECTION_EXTENDED)
3436         {
3437           clist->drag_button = 0;
3438           remove_grab (clist);
3439           GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3440         }
3441       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3442                        row, -1, NULL);
3443     }      
3444 }
3445
3446 gboolean
3447 gtk_clist_get_selectable (GtkCList *clist,
3448                           gint      row)
3449 {
3450   g_return_val_if_fail (clist != NULL, FALSE);
3451   g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
3452
3453   if (row < 0 || row >= clist->rows)
3454     return FALSE;
3455
3456   return GTK_CLIST_ROW (ROW_ELEMENT (clist, row))->selectable;
3457 }
3458
3459 void
3460 gtk_clist_select_row (GtkCList *clist,
3461                       gint      row,
3462                       gint      column)
3463 {
3464   g_return_if_fail (clist != NULL);
3465   g_return_if_fail (GTK_IS_CLIST (clist));
3466
3467   if (row < 0 || row >= clist->rows)
3468     return;
3469   if (column < -1 || column >= clist->columns)
3470     return;
3471
3472   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3473                    row, column, NULL);
3474 }
3475
3476 void
3477 gtk_clist_unselect_row (GtkCList *clist,
3478                         gint      row,
3479                         gint      column)
3480 {
3481   g_return_if_fail (clist != NULL);
3482   g_return_if_fail (GTK_IS_CLIST (clist));
3483
3484   if (row < 0 || row >= clist->rows)
3485     return;
3486   if (column < -1 || column >= clist->columns)
3487     return;
3488
3489   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3490                    row, column, NULL);
3491 }
3492
3493 void
3494 gtk_clist_select_all (GtkCList *clist)
3495 {
3496   g_return_if_fail (clist != NULL);
3497   g_return_if_fail (GTK_IS_CLIST (clist));
3498
3499   GTK_CLIST_GET_CLASS (clist)->select_all (clist);
3500 }
3501
3502 void
3503 gtk_clist_unselect_all (GtkCList *clist)
3504 {
3505   g_return_if_fail (clist != NULL);
3506   g_return_if_fail (GTK_IS_CLIST (clist));
3507
3508   GTK_CLIST_GET_CLASS (clist)->unselect_all (clist);
3509 }
3510
3511 void
3512 gtk_clist_undo_selection (GtkCList *clist)
3513 {
3514   g_return_if_fail (clist != NULL);
3515   g_return_if_fail (GTK_IS_CLIST (clist));
3516
3517   if (clist->selection_mode == GTK_SELECTION_EXTENDED &&
3518       (clist->undo_selection || clist->undo_unselection))
3519     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNDO_SELECTION]);
3520 }
3521
3522 /* PRIVATE SELECTION FUNCTIONS
3523  *   selection_find
3524  *   toggle_row
3525  *   fake_toggle_row
3526  *   toggle_focus_row
3527  *   toggle_add_mode
3528  *   real_select_row
3529  *   real_unselect_row
3530  *   real_select_all
3531  *   real_unselect_all
3532  *   fake_unselect_all
3533  *   real_undo_selection
3534  *   set_anchor
3535  *   resync_selection
3536  *   update_extended_selection
3537  *   start_selection
3538  *   end_selection
3539  *   extend_selection
3540  *   sync_selection
3541  */
3542 static GList *
3543 selection_find (GtkCList *clist,
3544                 gint      row_number,
3545                 GList    *row_list_element)
3546 {
3547   return g_list_find (clist->selection, GINT_TO_POINTER (row_number));
3548 }
3549
3550 static void
3551 toggle_row (GtkCList *clist,
3552             gint      row,
3553             gint      column,
3554             GdkEvent *event)
3555 {
3556   GtkCListRow *clist_row;
3557
3558   switch (clist->selection_mode)
3559     {
3560     case GTK_SELECTION_EXTENDED:
3561     case GTK_SELECTION_MULTIPLE:
3562     case GTK_SELECTION_SINGLE:
3563       clist_row = ROW_ELEMENT (clist, row)->data;
3564
3565       if (!clist_row)
3566         return;
3567
3568       if (clist_row->state == GTK_STATE_SELECTED)
3569         {
3570           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
3571                            row, column, event);
3572           return;
3573         }
3574     case GTK_SELECTION_BROWSE:
3575       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3576                        row, column, event);
3577       break;
3578     }
3579 }
3580
3581 static void
3582 fake_toggle_row (GtkCList *clist,
3583                  gint      row)
3584 {
3585   GList *work;
3586
3587   work = ROW_ELEMENT (clist, row);
3588
3589   if (!work || !GTK_CLIST_ROW (work)->selectable)
3590     return;
3591   
3592   if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL)
3593     clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
3594   else
3595     clist->anchor_state = GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
3596   
3597   if (CLIST_UNFROZEN (clist) &&
3598       gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3599     GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
3600                                           GTK_CLIST_ROW (work));
3601 }
3602
3603 static void
3604 toggle_focus_row (GtkCList *clist)
3605 {
3606   g_return_if_fail (clist != 0);
3607   g_return_if_fail (GTK_IS_CLIST (clist));
3608
3609   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
3610       clist->focus_row < 0 || clist->focus_row >= clist->rows)
3611     return;
3612
3613   switch (clist->selection_mode)
3614     {
3615     case  GTK_SELECTION_SINGLE:
3616     case  GTK_SELECTION_MULTIPLE:
3617       toggle_row (clist, clist->focus_row, 0, NULL);
3618       break;
3619     case GTK_SELECTION_EXTENDED:
3620       g_list_free (clist->undo_selection);
3621       g_list_free (clist->undo_unselection);
3622       clist->undo_selection = NULL;
3623       clist->undo_unselection = NULL;
3624
3625       clist->anchor = clist->focus_row;
3626       clist->drag_pos = clist->focus_row;
3627       clist->undo_anchor = clist->focus_row;
3628       
3629       if (GTK_CLIST_ADD_MODE(clist))
3630         fake_toggle_row (clist, clist->focus_row);
3631       else
3632         GTK_CLIST_GET_CLASS (clist)->fake_unselect_all (clist,clist->focus_row);
3633
3634       GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3635       break;
3636     default:
3637       break;
3638     }
3639 }
3640
3641 static void
3642 toggle_add_mode (GtkCList *clist)
3643 {
3644   g_return_if_fail (clist != 0);
3645   g_return_if_fail (GTK_IS_CLIST (clist));
3646   
3647   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
3648       clist->selection_mode != GTK_SELECTION_EXTENDED)
3649     return;
3650
3651   gtk_clist_draw_focus (GTK_WIDGET (clist));
3652   if (!GTK_CLIST_ADD_MODE(clist))
3653     {
3654       GTK_CLIST_SET_FLAG (clist, CLIST_ADD_MODE);
3655       gdk_gc_set_line_attributes (clist->xor_gc, 1,
3656                                   GDK_LINE_ON_OFF_DASH, 0, 0);
3657       gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
3658     }
3659   else
3660     {
3661       GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
3662       gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
3663       clist->anchor_state = GTK_STATE_SELECTED;
3664     }
3665   gtk_clist_draw_focus (GTK_WIDGET (clist));
3666 }
3667
3668 static void
3669 real_select_row (GtkCList *clist,
3670                  gint      row,
3671                  gint      column,
3672                  GdkEvent *event)
3673 {
3674   GtkCListRow *clist_row;
3675   GList *list;
3676   gint sel_row;
3677   gboolean row_selected;
3678
3679   g_return_if_fail (clist != NULL);
3680   g_return_if_fail (GTK_IS_CLIST (clist));
3681
3682   if (row < 0 || row > (clist->rows - 1))
3683     return;
3684
3685   switch (clist->selection_mode)
3686     {
3687     case GTK_SELECTION_SINGLE:
3688     case GTK_SELECTION_BROWSE:
3689
3690       row_selected = FALSE;
3691       list = clist->selection;
3692
3693       while (list)
3694         {
3695           sel_row = GPOINTER_TO_INT (list->data);
3696           list = list->next;
3697
3698           if (row == sel_row)
3699             row_selected = TRUE;
3700           else
3701             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3702                              sel_row, column, event);
3703         }
3704
3705       if (row_selected)
3706         return;
3707       
3708     default:
3709       break;
3710     }
3711
3712   clist_row = ROW_ELEMENT (clist, row)->data;
3713
3714   if (clist_row->state != GTK_STATE_NORMAL || !clist_row->selectable)
3715     return;
3716
3717   clist_row->state = GTK_STATE_SELECTED;
3718   if (!clist->selection)
3719     {
3720       clist->selection = g_list_append (clist->selection,
3721                                         GINT_TO_POINTER (row));
3722       clist->selection_end = clist->selection;
3723     }
3724   else
3725     clist->selection_end = 
3726       g_list_append (clist->selection_end, GINT_TO_POINTER (row))->next;
3727   
3728   if (CLIST_UNFROZEN (clist)
3729       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3730     GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3731 }
3732
3733 static void
3734 real_unselect_row (GtkCList *clist,
3735                    gint      row,
3736                    gint      column,
3737                    GdkEvent *event)
3738 {
3739   GtkCListRow *clist_row;
3740
3741   g_return_if_fail (clist != NULL);
3742   g_return_if_fail (GTK_IS_CLIST (clist));
3743
3744   if (row < 0 || row > (clist->rows - 1))
3745     return;
3746
3747   clist_row = ROW_ELEMENT (clist, row)->data;
3748
3749   if (clist_row->state == GTK_STATE_SELECTED)
3750     {
3751       clist_row->state = GTK_STATE_NORMAL;
3752
3753       if (clist->selection_end && 
3754           clist->selection_end->data == GINT_TO_POINTER (row))
3755         clist->selection_end = clist->selection_end->prev;
3756
3757       clist->selection = g_list_remove (clist->selection,
3758                                         GINT_TO_POINTER (row));
3759       
3760       if (CLIST_UNFROZEN (clist)
3761           && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3762         GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3763     }
3764 }
3765
3766 static void
3767 real_select_all (GtkCList *clist)
3768 {
3769   GList *list;
3770   gint i;
3771  
3772   g_return_if_fail (clist != NULL);
3773   g_return_if_fail (GTK_IS_CLIST (clist));
3774
3775   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
3776     return;
3777
3778   switch (clist->selection_mode)
3779     {
3780     case GTK_SELECTION_SINGLE:
3781     case GTK_SELECTION_BROWSE:
3782       return;
3783
3784     case GTK_SELECTION_EXTENDED:
3785       g_list_free (clist->undo_selection);
3786       g_list_free (clist->undo_unselection);
3787       clist->undo_selection = NULL;
3788       clist->undo_unselection = NULL;
3789           
3790       if (clist->rows &&
3791           ((GtkCListRow *) (clist->row_list->data))->state !=
3792           GTK_STATE_SELECTED)
3793         fake_toggle_row (clist, 0);
3794
3795       clist->anchor_state =  GTK_STATE_SELECTED;
3796       clist->anchor = 0;
3797       clist->drag_pos = 0;
3798       clist->undo_anchor = clist->focus_row;
3799       update_extended_selection (clist, clist->rows);
3800       GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3801       return;
3802
3803     case GTK_SELECTION_MULTIPLE:
3804       for (i = 0, list = clist->row_list; list; i++, list = list->next)
3805         {
3806           if (((GtkCListRow *)(list->data))->state == GTK_STATE_NORMAL)
3807             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3808                              i, -1, NULL);
3809         }
3810       return;
3811     }
3812 }
3813
3814 static void
3815 real_unselect_all (GtkCList *clist)
3816 {
3817   GList *list;
3818   gint i;
3819  
3820   g_return_if_fail (clist != NULL);
3821   g_return_if_fail (GTK_IS_CLIST (clist));
3822
3823   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
3824     return;
3825
3826   switch (clist->selection_mode)
3827     {
3828     case GTK_SELECTION_BROWSE:
3829       if (clist->focus_row >= 0)
3830         {
3831           gtk_signal_emit (GTK_OBJECT (clist),
3832                            clist_signals[SELECT_ROW],
3833                            clist->focus_row, -1, NULL);
3834           return;
3835         }
3836       break;
3837     case GTK_SELECTION_EXTENDED:
3838       g_list_free (clist->undo_selection);
3839       g_list_free (clist->undo_unselection);
3840       clist->undo_selection = NULL;
3841       clist->undo_unselection = NULL;
3842
3843       clist->anchor = -1;
3844       clist->drag_pos = -1;
3845       clist->undo_anchor = clist->focus_row;
3846       break;
3847     default:
3848       break;
3849     }
3850
3851   list = clist->selection;
3852   while (list)
3853     {
3854       i = GPOINTER_TO_INT (list->data);
3855       list = list->next;
3856       gtk_signal_emit (GTK_OBJECT (clist),
3857                        clist_signals[UNSELECT_ROW], i, -1, NULL);
3858     }
3859 }
3860
3861 static void
3862 fake_unselect_all (GtkCList *clist,
3863                    gint      row)
3864 {
3865   GList *list;
3866   GList *work;
3867   gint i;
3868
3869   if (row >= 0 && (work = ROW_ELEMENT (clist, row)))
3870     {
3871       if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL &&
3872           GTK_CLIST_ROW (work)->selectable)
3873         {
3874           GTK_CLIST_ROW (work)->state = GTK_STATE_SELECTED;
3875           
3876           if (CLIST_UNFROZEN (clist) &&
3877               gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3878             GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
3879                                                   GTK_CLIST_ROW (work));
3880         }  
3881     }
3882
3883   clist->undo_selection = clist->selection;
3884   clist->selection = NULL;
3885   clist->selection_end = NULL;
3886
3887   for (list = clist->undo_selection; list; list = list->next)
3888     {
3889       if ((i = GPOINTER_TO_INT (list->data)) == row ||
3890           !(work = g_list_nth (clist->row_list, i)))
3891         continue;
3892
3893       GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
3894       if (CLIST_UNFROZEN (clist) &&
3895           gtk_clist_row_is_visible (clist, i) != GTK_VISIBILITY_NONE)
3896         GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, i,
3897                                               GTK_CLIST_ROW (work));
3898     }
3899 }
3900
3901 static void
3902 real_undo_selection (GtkCList *clist)
3903 {
3904   GList *work;
3905
3906   g_return_if_fail (clist != NULL);
3907   g_return_if_fail (GTK_IS_CLIST (clist));
3908
3909   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
3910       clist->selection_mode != GTK_SELECTION_EXTENDED)
3911     return;
3912
3913   GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3914
3915   if (!(clist->undo_selection || clist->undo_unselection))
3916     {
3917       gtk_clist_unselect_all (clist);
3918       return;
3919     }
3920
3921   for (work = clist->undo_selection; work; work = work->next)
3922     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
3923                      GPOINTER_TO_INT (work->data), -1, NULL);
3924
3925   for (work = clist->undo_unselection; work; work = work->next)
3926     {
3927       /* g_print ("unselect %d\n",GPOINTER_TO_INT (work->data)); */
3928       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3929                        GPOINTER_TO_INT (work->data), -1, NULL);
3930     }
3931
3932   if (GTK_WIDGET_HAS_FOCUS(clist) && clist->focus_row != clist->undo_anchor)
3933     {
3934       gtk_clist_draw_focus (GTK_WIDGET (clist));
3935       clist->focus_row = clist->undo_anchor;
3936       gtk_clist_draw_focus (GTK_WIDGET (clist));
3937     }
3938   else
3939     clist->focus_row = clist->undo_anchor;
3940   
3941   clist->undo_anchor = -1;
3942  
3943   g_list_free (clist->undo_selection);
3944   g_list_free (clist->undo_unselection);
3945   clist->undo_selection = NULL;
3946   clist->undo_unselection = NULL;
3947
3948   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
3949       clist->clist_window_height)
3950     gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
3951   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
3952     gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
3953 }
3954
3955 static void
3956 set_anchor (GtkCList *clist,
3957             gboolean  add_mode,
3958             gint      anchor,
3959             gint      undo_anchor)
3960 {
3961   g_return_if_fail (clist != NULL);
3962   g_return_if_fail (GTK_IS_CLIST (clist));
3963   
3964   if (clist->selection_mode != GTK_SELECTION_EXTENDED || clist->anchor >= 0)
3965     return;
3966
3967   g_list_free (clist->undo_selection);
3968   g_list_free (clist->undo_unselection);
3969   clist->undo_selection = NULL;
3970   clist->undo_unselection = NULL;
3971
3972   if (add_mode)
3973     fake_toggle_row (clist, anchor);
3974   else
3975     {
3976       GTK_CLIST_GET_CLASS (clist)->fake_unselect_all (clist, anchor);
3977       clist->anchor_state = GTK_STATE_SELECTED;
3978     }
3979
3980   clist->anchor = anchor;
3981   clist->drag_pos = anchor;
3982   clist->undo_anchor = undo_anchor;
3983 }
3984
3985 static void
3986 resync_selection (GtkCList *clist,
3987                   GdkEvent *event)
3988 {
3989   gint i;
3990   gint e;
3991   gint row;
3992   GList *list;
3993   GtkCListRow *clist_row;
3994
3995   if (clist->selection_mode != GTK_SELECTION_EXTENDED)
3996     return;
3997
3998   if (clist->anchor < 0 || clist->drag_pos < 0)
3999     return;
4000
4001   gtk_clist_freeze (clist);
4002
4003   i = MIN (clist->anchor, clist->drag_pos);
4004   e = MAX (clist->anchor, clist->drag_pos);
4005
4006   if (clist->undo_selection)
4007     {
4008       list = clist->selection;
4009       clist->selection = clist->undo_selection;
4010       clist->selection_end = g_list_last (clist->selection);
4011       clist->undo_selection = list;
4012       list = clist->selection;
4013       while (list)
4014         {
4015           row = GPOINTER_TO_INT (list->data);
4016           list = list->next;
4017           if (row < i || row > e)
4018             {
4019               clist_row = g_list_nth (clist->row_list, row)->data;
4020               if (clist_row->selectable)
4021                 {
4022                   clist_row->state = GTK_STATE_SELECTED;
4023                   gtk_signal_emit (GTK_OBJECT (clist),
4024                                    clist_signals[UNSELECT_ROW],
4025                                    row, -1, event);
4026                   clist->undo_selection = g_list_prepend
4027                     (clist->undo_selection, GINT_TO_POINTER (row));
4028                 }
4029             }
4030         }
4031     }    
4032
4033   if (clist->anchor < clist->drag_pos)
4034     {
4035       for (list = g_list_nth (clist->row_list, i); i <= e;
4036            i++, list = list->next)
4037         if (GTK_CLIST_ROW (list)->selectable)
4038           {
4039             if (g_list_find (clist->selection, GINT_TO_POINTER(i)))
4040               {
4041                 if (GTK_CLIST_ROW (list)->state == GTK_STATE_NORMAL)
4042                   {
4043                     GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
4044                     gtk_signal_emit (GTK_OBJECT (clist),
4045                                      clist_signals[UNSELECT_ROW],
4046                                      i, -1, event);
4047                     clist->undo_selection =
4048                       g_list_prepend (clist->undo_selection,
4049                                       GINT_TO_POINTER (i));
4050                   }
4051               }
4052             else if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
4053               {
4054                 GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
4055                 clist->undo_unselection =
4056                   g_list_prepend (clist->undo_unselection,
4057                                   GINT_TO_POINTER (i));
4058               }
4059           }
4060     }
4061   else
4062     {
4063       for (list = g_list_nth (clist->row_list, e); i <= e;
4064            e--, list = list->prev)
4065         if (GTK_CLIST_ROW (list)->selectable)
4066           {
4067             if (g_list_find (clist->selection, GINT_TO_POINTER(e)))
4068               {
4069                 if (GTK_CLIST_ROW (list)->state == GTK_STATE_NORMAL)
4070                   {
4071                     GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
4072                     gtk_signal_emit (GTK_OBJECT (clist),
4073                                      clist_signals[UNSELECT_ROW],
4074                                      e, -1, event);
4075                     clist->undo_selection =
4076                       g_list_prepend (clist->undo_selection,
4077                                       GINT_TO_POINTER (e));
4078                   }
4079               }
4080             else if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
4081               {
4082                 GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
4083                 clist->undo_unselection =
4084                   g_list_prepend (clist->undo_unselection,
4085                                   GINT_TO_POINTER (e));
4086               }
4087           }
4088     }
4089   
4090   clist->undo_unselection = g_list_reverse (clist->undo_unselection);
4091   for (list = clist->undo_unselection; list; list = list->next)
4092     gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
4093                      GPOINTER_TO_INT (list->data), -1, event);
4094
4095   clist->anchor = -1;
4096   clist->drag_pos = -1;
4097
4098   gtk_clist_thaw (clist);
4099 }
4100
4101 static void
4102 update_extended_selection (GtkCList *clist,
4103                            gint      row)
4104 {
4105   gint i;
4106   GList *list;
4107   GdkRectangle area;
4108   gint s1 = -1;
4109   gint s2 = -1;
4110   gint e1 = -1;
4111   gint e2 = -1;
4112   gint y1 = clist->clist_window_height;
4113   gint y2 = clist->clist_window_height;
4114   gint h1 = 0;
4115   gint h2 = 0;
4116   gint top;
4117
4118   if (clist->selection_mode != GTK_SELECTION_EXTENDED || clist->anchor == -1)
4119     return;
4120
4121   if (row < 0)
4122     row = 0;
4123   if (row >= clist->rows)
4124     row = clist->rows - 1;
4125
4126   /* extending downwards */
4127   if (row > clist->drag_pos && clist->anchor <= clist->drag_pos)
4128     {
4129       s2 = clist->drag_pos + 1;
4130       e2 = row;
4131     }
4132   /* extending upwards */
4133   else if (row < clist->drag_pos && clist->anchor >= clist->drag_pos)
4134     {
4135       s2 = row;
4136       e2 = clist->drag_pos - 1;
4137     }
4138   else if (row < clist->drag_pos && clist->anchor < clist->drag_pos)
4139     {
4140       e1 = clist->drag_pos;
4141       /* row and drag_pos on different sides of anchor :
4142          take back the selection between anchor and drag_pos,
4143          select between anchor and row */
4144       if (row < clist->anchor)
4145         {
4146           s1 = clist->anchor + 1;
4147           s2 = row;
4148           e2 = clist->anchor - 1;
4149         }
4150       /* take back the selection between anchor and drag_pos */
4151       else
4152         s1 = row + 1;
4153     }
4154   else if (row > clist->drag_pos && clist->anchor > clist->drag_pos)
4155     {
4156       s1 = clist->drag_pos;
4157       /* row and drag_pos on different sides of anchor :
4158          take back the selection between anchor and drag_pos,
4159          select between anchor and row */
4160       if (row > clist->anchor)
4161         {
4162           e1 = clist->anchor - 1;
4163           s2 = clist->anchor + 1;
4164           e2 = row;
4165         }
4166       /* take back the selection between anchor and drag_pos */
4167       else
4168         e1 = row - 1;
4169     }
4170
4171   clist->drag_pos = row;
4172
4173   area.x = 0;
4174   area.width = clist->clist_window_width;
4175
4176   /* restore the elements between s1 and e1 */
4177   if (s1 >= 0)
4178     {
4179       for (i = s1, list = g_list_nth (clist->row_list, i); i <= e1;
4180            i++, list = list->next)
4181         if (GTK_CLIST_ROW (list)->selectable)
4182           {
4183             if (GTK_CLIST_GET_CLASS (clist)->selection_find (clist, i, list))
4184               GTK_CLIST_ROW (list)->state = GTK_STATE_SELECTED;
4185             else
4186               GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
4187           }
4188
4189       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4190
4191       if (top + clist->row_height <= 0)
4192         {
4193           area.y = 0;
4194           area.height = ROW_TOP_YPIXEL (clist, e1) + clist->row_height;
4195           draw_rows (clist, &area);
4196           gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4197         }
4198       else if (top >= clist->clist_window_height)
4199         {
4200           area.y = ROW_TOP_YPIXEL (clist, s1) - 1;
4201           area.height = clist->clist_window_height - area.y;
4202           draw_rows (clist, &area);
4203           gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4204         }
4205       else if (top < 0)
4206         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4207       else if (top + clist->row_height > clist->clist_window_height)
4208         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4209
4210       y1 = ROW_TOP_YPIXEL (clist, s1) - 1;
4211       h1 = (e1 - s1 + 1) * (clist->row_height + CELL_SPACING);
4212     }
4213
4214   /* extend the selection between s2 and e2 */
4215   if (s2 >= 0)
4216     {
4217       for (i = s2, list = g_list_nth (clist->row_list, i); i <= e2;
4218            i++, list = list->next)
4219         if (GTK_CLIST_ROW (list)->selectable &&
4220             GTK_CLIST_ROW (list)->state != clist->anchor_state)
4221           GTK_CLIST_ROW (list)->state = clist->anchor_state;
4222
4223       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4224
4225       if (top + clist->row_height <= 0)
4226         {
4227           area.y = 0;
4228           area.height = ROW_TOP_YPIXEL (clist, e2) + clist->row_height;
4229           draw_rows (clist, &area);
4230           gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4231         }
4232       else if (top >= clist->clist_window_height)
4233         {
4234           area.y = ROW_TOP_YPIXEL (clist, s2) - 1;
4235           area.height = clist->clist_window_height - area.y;
4236           draw_rows (clist, &area);
4237           gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4238         }
4239       else if (top < 0)
4240         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4241       else if (top + clist->row_height > clist->clist_window_height)
4242         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4243
4244       y2 = ROW_TOP_YPIXEL (clist, s2) - 1;
4245       h2 = (e2 - s2 + 1) * (clist->row_height + CELL_SPACING);
4246     }
4247
4248   area.y = MAX (0, MIN (y1, y2));
4249   if (area.y > clist->clist_window_height)
4250     area.y = 0;
4251   area.height = MIN (clist->clist_window_height, h1 + h2);
4252   if (s1 >= 0 && s2 >= 0)
4253     area.height += (clist->row_height + CELL_SPACING);
4254   draw_rows (clist, &area);
4255 }
4256
4257 static void
4258 start_selection (GtkCList *clist)
4259 {
4260   g_return_if_fail (clist != NULL);
4261   g_return_if_fail (GTK_IS_CLIST (clist));
4262
4263   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
4264     return;
4265
4266   set_anchor (clist, GTK_CLIST_ADD_MODE(clist), clist->focus_row,
4267               clist->focus_row);
4268 }
4269
4270 static void
4271 end_selection (GtkCList *clist)
4272 {
4273   g_return_if_fail (clist != NULL);
4274   g_return_if_fail (GTK_IS_CLIST (clist));
4275
4276   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_FOCUS(clist))
4277     return;
4278
4279   GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4280 }
4281
4282 static void
4283 extend_selection (GtkCList      *clist,
4284                   GtkScrollType  scroll_type,
4285                   gfloat         position,
4286                   gboolean       auto_start_selection)
4287 {
4288   g_return_if_fail (clist != NULL);
4289   g_return_if_fail (GTK_IS_CLIST (clist));
4290
4291   if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
4292       clist->selection_mode != GTK_SELECTION_EXTENDED)
4293     return;
4294
4295   if (auto_start_selection)
4296     set_anchor (clist, GTK_CLIST_ADD_MODE(clist), clist->focus_row,
4297                 clist->focus_row);
4298   else if (clist->anchor == -1)
4299     return;
4300
4301   move_focus_row (clist, scroll_type, position);
4302
4303   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
4304       clist->clist_window_height)
4305     gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
4306   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
4307     gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
4308
4309   update_extended_selection (clist, clist->focus_row);
4310 }
4311
4312 static void
4313 sync_selection (GtkCList *clist,
4314                 gint      row,
4315                 gint      mode)
4316 {
4317   GList *list;
4318   gint d;
4319
4320   if (mode == SYNC_INSERT)
4321     d = 1;
4322   else
4323     d = -1;
4324       
4325   if (clist->focus_row >= row)
4326     {
4327       if (d > 0 || clist->focus_row > row)
4328         clist->focus_row += d;
4329       if (clist->focus_row == -1 && clist->rows >= 1)
4330         clist->focus_row = 0;
4331       else if (clist->focus_row >= clist->rows)
4332         clist->focus_row = clist->rows - 1;
4333     }
4334
4335   GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4336
4337   g_list_free (clist->undo_selection);
4338   g_list_free (clist->undo_unselection);
4339   clist->undo_selection = NULL;
4340   clist->undo_unselection = NULL;
4341
4342   clist->anchor = -1;
4343   clist->drag_pos = -1;
4344   clist->undo_anchor = clist->focus_row;
4345
4346   list = clist->selection;
4347
4348   while (list)
4349     {
4350       if (GPOINTER_TO_INT (list->data) >= row)
4351         list->data = ((gchar*) list->data) + d;
4352       list = list->next;
4353     }
4354 }
4355
4356 /* GTKOBJECT
4357  *   gtk_clist_destroy
4358  *   gtk_clist_finalize
4359  */
4360 static void
4361 gtk_clist_destroy (GtkObject *object)
4362 {
4363   gint i;
4364   GtkCList *clist;
4365
4366   g_return_if_fail (object != NULL);
4367   g_return_if_fail (GTK_IS_CLIST (object));
4368
4369   clist = GTK_CLIST (object);
4370
4371   /* freeze the list */
4372   clist->freeze_count++;
4373
4374   /* get rid of all the rows */
4375   gtk_clist_clear (clist);
4376
4377   /* Since we don't have a _remove method, unparent the children
4378    * instead of destroying them so the focus will be unset properly.
4379    * (For other containers, the _remove method takes care of the
4380    * unparent) The destroy will happen when the refcount drops
4381    * to zero.
4382    */
4383
4384   /* unref adjustments */
4385   if (clist->hadjustment)
4386     {
4387       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->hadjustment), clist);
4388       gtk_object_unref (GTK_OBJECT (clist->hadjustment));
4389       clist->hadjustment = NULL;
4390     }
4391   if (clist->vadjustment)
4392     {
4393       gtk_signal_disconnect_by_data (GTK_OBJECT (clist->vadjustment), clist);
4394       gtk_object_unref (GTK_OBJECT (clist->vadjustment));
4395       clist->vadjustment = NULL;
4396     }
4397
4398   remove_grab (clist);
4399
4400   /* destroy the column buttons */
4401   for (i = 0; i < clist->columns; i++)
4402     if (clist->column[i].button)
4403       {
4404         gtk_widget_unparent (clist->column[i].button);
4405         clist->column[i].button = NULL;
4406       }
4407
4408   if (GTK_OBJECT_CLASS (parent_class)->destroy)
4409     (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
4410 }
4411
4412 static void
4413 gtk_clist_finalize (GObject *object)
4414 {
4415   GtkCList *clist;
4416
4417   g_return_if_fail (GTK_IS_CLIST (object));
4418
4419   clist = GTK_CLIST (object);
4420
4421   columns_delete (clist);
4422
4423   g_mem_chunk_destroy (clist->cell_mem_chunk);
4424   g_mem_chunk_destroy (clist->row_mem_chunk);
4425
4426   G_OBJECT_CLASS (parent_class)->finalize (object);
4427 }
4428
4429 /* GTKWIDGET
4430  *   gtk_clist_realize
4431  *   gtk_clist_unrealize
4432  *   gtk_clist_map
4433  *   gtk_clist_unmap
4434  *   gtk_clist_draw
4435  *   gtk_clist_expose
4436  *   gtk_clist_style_set
4437  *   gtk_clist_key_press
4438  *   gtk_clist_button_press
4439  *   gtk_clist_button_release
4440  *   gtk_clist_motion
4441  *   gtk_clist_size_request
4442  *   gtk_clist_size_allocate
4443  */
4444 static void
4445 gtk_clist_realize (GtkWidget *widget)
4446 {
4447   GtkCList *clist;
4448   GdkWindowAttr attributes;
4449   GdkGCValues values;
4450   GtkCListRow *clist_row;
4451   GList *list;
4452   gint attributes_mask;
4453   gint border_width;
4454   gint i;
4455   gint j;
4456
4457   g_return_if_fail (widget != NULL);
4458   g_return_if_fail (GTK_IS_CLIST (widget));
4459
4460   clist = GTK_CLIST (widget);
4461
4462   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
4463
4464   border_width = GTK_CONTAINER (widget)->border_width;
4465   
4466   attributes.window_type = GDK_WINDOW_CHILD;
4467   attributes.x = widget->allocation.x + border_width;
4468   attributes.y = widget->allocation.y + border_width;
4469   attributes.width = widget->allocation.width - border_width * 2;
4470   attributes.height = widget->allocation.height - border_width * 2;
4471   attributes.wclass = GDK_INPUT_OUTPUT;
4472   attributes.visual = gtk_widget_get_visual (widget);
4473   attributes.colormap = gtk_widget_get_colormap (widget);
4474   attributes.event_mask = gtk_widget_get_events (widget);
4475   attributes.event_mask |= (GDK_EXPOSURE_MASK |
4476                             GDK_BUTTON_PRESS_MASK |
4477                             GDK_BUTTON_RELEASE_MASK |
4478                             GDK_KEY_PRESS_MASK |
4479                             GDK_KEY_RELEASE_MASK);
4480   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
4481
4482   /* main window */
4483   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
4484                                    &attributes, attributes_mask);
4485   gdk_window_set_user_data (widget->window, clist);
4486
4487   widget->style = gtk_style_attach (widget->style, widget->window);
4488
4489   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
4490
4491   /* column-title window */
4492
4493   attributes.x = clist->column_title_area.x;
4494   attributes.y = clist->column_title_area.y;
4495   attributes.width = clist->column_title_area.width;
4496   attributes.height = clist->column_title_area.height;
4497   
4498   clist->title_window = gdk_window_new (widget->window, &attributes,
4499                                         attributes_mask);
4500   gdk_window_set_user_data (clist->title_window, clist);
4501
4502   gtk_style_set_background (widget->style, clist->title_window,
4503                             GTK_STATE_NORMAL);
4504   gdk_window_show (clist->title_window);
4505
4506   /* set things up so column buttons are drawn in title window */
4507   for (i = 0; i < clist->columns; i++)
4508     if (clist->column[i].button)
4509       gtk_widget_set_parent_window (clist->column[i].button,
4510                                     clist->title_window);
4511
4512   /* clist-window */
4513   attributes.x = (clist->internal_allocation.x +
4514                   widget->style->klass->xthickness);
4515   attributes.y = (clist->internal_allocation.y +
4516                   widget->style->klass->ythickness +
4517                   clist->column_title_area.height);
4518   attributes.width = clist->clist_window_width;
4519   attributes.height = clist->clist_window_height;
4520   
4521   clist->clist_window = gdk_window_new (widget->window, &attributes,
4522                                         attributes_mask);
4523   gdk_window_set_user_data (clist->clist_window, clist);
4524
4525   gdk_window_set_background (clist->clist_window,
4526                              &widget->style->base[GTK_STATE_NORMAL]);
4527   gdk_window_show (clist->clist_window);
4528   gdk_window_get_size (clist->clist_window, &clist->clist_window_width,
4529                        &clist->clist_window_height);
4530
4531   /* create resize windows */
4532   attributes.wclass = GDK_INPUT_ONLY;
4533   attributes.event_mask = (GDK_BUTTON_PRESS_MASK |
4534                            GDK_BUTTON_RELEASE_MASK |
4535                            GDK_POINTER_MOTION_MASK |
4536                            GDK_POINTER_MOTION_HINT_MASK |
4537                            GDK_KEY_PRESS_MASK);
4538   attributes_mask = GDK_WA_CURSOR;
4539   attributes.cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
4540   clist->cursor_drag = attributes.cursor;
4541
4542   attributes.x =  LIST_WIDTH (clist) + 1;
4543   attributes.y = 0;
4544   attributes.width = 0;
4545   attributes.height = 0;
4546
4547   for (i = 0; i < clist->columns; i++)
4548     {
4549       clist->column[i].window = gdk_window_new (clist->title_window,
4550                                                 &attributes, attributes_mask);
4551       gdk_window_set_user_data (clist->column[i].window, clist);
4552     }
4553
4554   /* This is slightly less efficient than creating them with the
4555    * right size to begin with, but easier
4556    */
4557   size_allocate_title_buttons (clist);
4558
4559   /* GCs */
4560   clist->fg_gc = gdk_gc_new (widget->window);
4561   clist->bg_gc = gdk_gc_new (widget->window);
4562   
4563   /* We'll use this gc to do scrolling as well */
4564   gdk_gc_set_exposures (clist->fg_gc, TRUE);
4565
4566   values.foreground = (widget->style->white.pixel==0 ?
4567                        widget->style->black:widget->style->white);
4568   values.function = GDK_XOR;
4569   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
4570   clist->xor_gc = gdk_gc_new_with_values (widget->window,
4571                                           &values,
4572                                           GDK_GC_FOREGROUND |
4573                                           GDK_GC_FUNCTION |
4574                                           GDK_GC_SUBWINDOW);
4575
4576   /* attach optional row/cell styles, allocate foreground/background colors */
4577   list = clist->row_list;
4578   for (i = 0; i < clist->rows; i++)
4579     {
4580       clist_row = list->data;
4581       list = list->next;
4582
4583       if (clist_row->style)
4584         clist_row->style = gtk_style_attach (clist_row->style,
4585                                              clist->clist_window);
4586
4587       if (clist_row->fg_set || clist_row->bg_set)
4588         {
4589           GdkColormap *colormap;
4590
4591           colormap = gtk_widget_get_colormap (widget);
4592           if (clist_row->fg_set)
4593             gdk_color_alloc (colormap, &clist_row->foreground);
4594           if (clist_row->bg_set)
4595             gdk_color_alloc (colormap, &clist_row->background);
4596         }
4597       
4598       for (j = 0; j < clist->columns; j++)
4599         if  (clist_row->cell[j].style)
4600           clist_row->cell[j].style =
4601             gtk_style_attach (clist_row->cell[j].style, clist->clist_window);
4602     }
4603 }
4604
4605 static void
4606 gtk_clist_unrealize (GtkWidget *widget)
4607 {
4608   gint i;
4609   GtkCList *clist;
4610
4611   g_return_if_fail (widget != NULL);
4612   g_return_if_fail (GTK_IS_CLIST (widget));
4613
4614   clist = GTK_CLIST (widget);
4615
4616   /* freeze the list */
4617   clist->freeze_count++;
4618
4619   if (GTK_WIDGET_MAPPED (widget))
4620     gtk_clist_unmap (widget);
4621
4622   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
4623
4624   /* detach optional row/cell styles */
4625   if (GTK_WIDGET_REALIZED (widget))
4626     {
4627       GtkCListRow *clist_row;
4628       GList *list;
4629       gint j;
4630
4631       list = clist->row_list;
4632       for (i = 0; i < clist->rows; i++)
4633         {
4634           clist_row = list->data;
4635           list = list->next;
4636
4637           if (clist_row->style)
4638             gtk_style_detach (clist_row->style);
4639           for (j = 0; j < clist->columns; j++)
4640             if  (clist_row->cell[j].style)
4641               gtk_style_detach (clist_row->cell[j].style);
4642         }
4643     }
4644
4645   gdk_cursor_destroy (clist->cursor_drag);
4646   gdk_gc_destroy (clist->xor_gc);
4647   gdk_gc_destroy (clist->fg_gc);
4648   gdk_gc_destroy (clist->bg_gc);
4649
4650   for (i = 0; i < clist->columns; i++)
4651     {
4652       if (clist->column[i].button)
4653         gtk_widget_unrealize (clist->column[i].button);
4654       if (clist->column[i].window)
4655         {
4656           gdk_window_set_user_data (clist->column[i].window, NULL);
4657           gdk_window_destroy (clist->column[i].window);
4658           clist->column[i].window = NULL;
4659         }
4660     }
4661
4662   gdk_window_set_user_data (clist->clist_window, NULL);
4663   gdk_window_destroy (clist->clist_window);
4664   clist->clist_window = NULL;
4665
4666   gdk_window_set_user_data (clist->title_window, NULL);
4667   gdk_window_destroy (clist->title_window);
4668   clist->title_window = NULL;
4669
4670   clist->cursor_drag = NULL;
4671   clist->xor_gc = NULL;
4672   clist->fg_gc = NULL;
4673   clist->bg_gc = NULL;
4674
4675   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
4676     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
4677 }
4678
4679 static void
4680 gtk_clist_map (GtkWidget *widget)
4681 {
4682   gint i;
4683   GtkCList *clist;
4684
4685   g_return_if_fail (widget != NULL);
4686   g_return_if_fail (GTK_IS_CLIST (widget));
4687
4688   clist = GTK_CLIST (widget);
4689
4690   if (!GTK_WIDGET_MAPPED (widget))
4691     {
4692       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
4693
4694       /* map column buttons */
4695       for (i = 0; i < clist->columns; i++)
4696         {
4697           if (clist->column[i].button &&
4698               GTK_WIDGET_VISIBLE (clist->column[i].button) &&
4699               !GTK_WIDGET_MAPPED (clist->column[i].button))
4700             gtk_widget_map (clist->column[i].button);
4701         }
4702       
4703       for (i = 0; i < clist->columns; i++)
4704         if (clist->column[i].window && clist->column[i].button)
4705           {
4706             gdk_window_raise (clist->column[i].window);
4707             gdk_window_show (clist->column[i].window);
4708           }
4709
4710       gdk_window_show (clist->title_window);
4711       gdk_window_show (clist->clist_window);
4712       gdk_window_show (widget->window);
4713
4714       /* unfreeze the list */
4715       clist->freeze_count = 0;
4716     }
4717 }
4718
4719 static void
4720 gtk_clist_unmap (GtkWidget *widget)
4721 {
4722   gint i;
4723   GtkCList *clist;
4724
4725   g_return_if_fail (widget != NULL);
4726   g_return_if_fail (GTK_IS_CLIST (widget));
4727
4728   clist = GTK_CLIST (widget);
4729
4730   if (GTK_WIDGET_MAPPED (widget))
4731     {
4732       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
4733
4734       if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
4735         {
4736           remove_grab (clist);
4737
4738           GTK_CLIST_GET_CLASS (widget)->resync_selection (clist, NULL);
4739
4740           clist->click_cell.row = -1;
4741           clist->click_cell.column = -1;
4742           clist->drag_button = 0;
4743
4744           if (GTK_CLIST_IN_DRAG(clist))
4745             {
4746               gpointer drag_data;
4747
4748               GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
4749               drag_data = gtk_object_get_data (GTK_OBJECT (clist),
4750                                                "gtk-site-data");
4751               if (drag_data)
4752                 gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist),
4753                                                     drag_data);
4754             }
4755         }
4756
4757       for (i = 0; i < clist->columns; i++)
4758         if (clist->column[i].window)
4759           gdk_window_hide (clist->column[i].window);
4760
4761       gdk_window_hide (clist->clist_window);
4762       gdk_window_hide (clist->title_window);
4763       gdk_window_hide (widget->window);
4764
4765       /* unmap column buttons */
4766       for (i = 0; i < clist->columns; i++)
4767         if (clist->column[i].button &&
4768             GTK_WIDGET_MAPPED (clist->column[i].button))
4769           gtk_widget_unmap (clist->column[i].button);
4770
4771       /* freeze the list */
4772       clist->freeze_count++;
4773     }
4774 }
4775
4776 static void
4777 gtk_clist_draw (GtkWidget    *widget,
4778                 GdkRectangle *area)
4779 {
4780   GtkCList *clist;
4781   gint border_width;
4782   GdkRectangle child_area;
4783   int i;
4784
4785   g_return_if_fail (widget != NULL);
4786   g_return_if_fail (GTK_IS_CLIST (widget));
4787   g_return_if_fail (area != NULL);
4788
4789   if (GTK_WIDGET_DRAWABLE (widget))
4790     {
4791       clist = GTK_CLIST (widget);
4792       border_width = GTK_CONTAINER (widget)->border_width;
4793
4794       gdk_window_clear_area (widget->window,
4795                              area->x - border_width, 
4796                              area->y - border_width,
4797                              area->width, area->height);
4798
4799       /* draw list shadow/border */
4800       gtk_draw_shadow (widget->style, widget->window,
4801                        GTK_STATE_NORMAL, clist->shadow_type,
4802                        0, 0, 
4803                        clist->clist_window_width +
4804                        (2 * widget->style->klass->xthickness),
4805                        clist->clist_window_height +
4806                        (2 * widget->style->klass->ythickness) +
4807                        clist->column_title_area.height);
4808
4809       gdk_window_clear_area (clist->clist_window, 0, 0, 0, 0);
4810       draw_rows (clist, NULL);
4811
4812       for (i = 0; i < clist->columns; i++)
4813         {
4814           if (!clist->column[i].visible)
4815             continue;
4816           if (clist->column[i].button &&
4817               gtk_widget_intersect(clist->column[i].button, area, &child_area))
4818             gtk_widget_draw (clist->column[i].button, &child_area);
4819         }
4820     }
4821 }
4822
4823 static gint
4824 gtk_clist_expose (GtkWidget      *widget,
4825                   GdkEventExpose *event)
4826 {
4827   GtkCList *clist;
4828
4829   g_return_val_if_fail (widget != NULL, FALSE);
4830   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4831   g_return_val_if_fail (event != NULL, FALSE);
4832
4833   if (GTK_WIDGET_DRAWABLE (widget))
4834     {
4835       clist = GTK_CLIST (widget);
4836
4837       /* draw border */
4838       if (event->window == widget->window)
4839         gtk_draw_shadow (widget->style, widget->window,
4840                          GTK_STATE_NORMAL, clist->shadow_type,
4841                          0, 0,
4842                          clist->clist_window_width +
4843                          (2 * widget->style->klass->xthickness),
4844                          clist->clist_window_height +
4845                          (2 * widget->style->klass->ythickness) +
4846                          clist->column_title_area.height);
4847
4848       /* exposure events on the list */
4849       if (event->window == clist->clist_window)
4850         draw_rows (clist, &event->area);
4851     }
4852
4853   return FALSE;
4854 }
4855
4856 static void
4857 gtk_clist_style_set (GtkWidget *widget,
4858                      GtkStyle  *previous_style)
4859 {
4860   GtkCList *clist;
4861
4862   g_return_if_fail (widget != NULL);
4863   g_return_if_fail (GTK_IS_CLIST (widget));
4864
4865   if (GTK_WIDGET_CLASS (parent_class)->style_set)
4866     (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
4867
4868   clist = GTK_CLIST (widget);
4869
4870   if (GTK_WIDGET_REALIZED (widget))
4871     {
4872       gtk_style_set_background (widget->style, widget->window, widget->state);
4873       gtk_style_set_background (widget->style, clist->title_window, GTK_STATE_SELECTED);
4874       gdk_window_set_background (clist->clist_window, &widget->style->base[GTK_STATE_NORMAL]);
4875     }
4876
4877   /* Fill in data after widget has correct style */
4878
4879   /* text properties */
4880   if (!GTK_CLIST_ROW_HEIGHT_SET(clist))
4881     /* Reset clist->row_height */
4882     gtk_clist_set_row_height (clist, 0);
4883
4884   /* Column widths */
4885   if (!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
4886     {
4887       gint width;
4888       gint i;
4889
4890       for (i = 0; i < clist->columns; i++)
4891         if (clist->column[i].auto_resize)
4892           {
4893             width = gtk_clist_optimal_column_width (clist, i);
4894             if (width != clist->column[i].width)
4895               gtk_clist_set_column_width (clist, i, width);
4896           }
4897     }
4898 }
4899
4900 static gint
4901 gtk_clist_key_press (GtkWidget   *widget,
4902                      GdkEventKey *event)
4903 {
4904   g_return_val_if_fail (widget != NULL, FALSE);
4905   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4906   g_return_val_if_fail (event != NULL, FALSE);
4907
4908   if (GTK_WIDGET_CLASS (parent_class)->key_press_event &&
4909       GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
4910     return TRUE;
4911
4912   switch (event->keyval)
4913     {
4914     case GDK_Tab:
4915     case GDK_ISO_Left_Tab:
4916       if (event->state & GDK_SHIFT_MASK)
4917         return gtk_container_focus (GTK_CONTAINER (widget),
4918                                     GTK_DIR_TAB_BACKWARD);
4919       else
4920         return gtk_container_focus (GTK_CONTAINER (widget),
4921                                     GTK_DIR_TAB_FORWARD);
4922     default:
4923       break;
4924     }
4925   return FALSE;
4926 }
4927
4928 static gint
4929 gtk_clist_button_press (GtkWidget      *widget,
4930                         GdkEventButton *event)
4931 {
4932   gint i;
4933   GtkCList *clist;
4934   gint x;
4935   gint y;
4936   gint row;
4937   gint column;
4938   gint button_actions;
4939
4940   g_return_val_if_fail (widget != NULL, FALSE);
4941   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
4942   g_return_val_if_fail (event != NULL, FALSE);
4943
4944   clist = GTK_CLIST (widget);
4945
4946   button_actions = clist->button_actions[event->button - 1];
4947
4948   if (button_actions == GTK_BUTTON_IGNORED)
4949     return FALSE;
4950
4951   /* selections on the list */
4952   if (event->window == clist->clist_window)
4953     {
4954       x = event->x;
4955       y = event->y;
4956
4957       if (get_selection_info (clist, x, y, &row, &column))
4958         {
4959           gint old_row = clist->focus_row;
4960
4961           if (clist->focus_row == -1)
4962             old_row = row;
4963
4964           if (event->type == GDK_BUTTON_PRESS)
4965             {
4966               GdkEventMask mask = ((1 << (4 + event->button)) |
4967                                    GDK_POINTER_MOTION_HINT_MASK |
4968                                    GDK_BUTTON_RELEASE_MASK);
4969
4970               if (gdk_pointer_grab (clist->clist_window, FALSE, mask,
4971                                     NULL, NULL, event->time))
4972                 return FALSE;
4973               gtk_grab_add (widget);
4974
4975               clist->click_cell.row = row;
4976               clist->click_cell.column = column;
4977               clist->drag_button = event->button;
4978             }
4979           else
4980             {
4981               clist->click_cell.row = -1;
4982               clist->click_cell.column = -1;
4983
4984               clist->drag_button = 0;
4985               remove_grab (clist);
4986             }
4987
4988           if (button_actions & GTK_BUTTON_SELECTS)
4989             {
4990               if (GTK_CLIST_ADD_MODE(clist))
4991                 {
4992                   GTK_CLIST_UNSET_FLAG (clist, CLIST_ADD_MODE);
4993                   if (GTK_WIDGET_HAS_FOCUS(widget))
4994                     {
4995                       gtk_clist_draw_focus (widget);
4996                       gdk_gc_set_line_attributes (clist->xor_gc, 1,
4997                                                   GDK_LINE_SOLID, 0, 0);
4998                       clist->focus_row = row;
4999                       gtk_clist_draw_focus (widget);
5000                     }
5001                   else
5002                     {
5003                       gdk_gc_set_line_attributes (clist->xor_gc, 1,
5004                                                   GDK_LINE_SOLID, 0, 0);
5005                       clist->focus_row = row;
5006                     }
5007                 }
5008               else if (row != clist->focus_row)
5009                 {
5010                   if (GTK_WIDGET_HAS_FOCUS(widget))
5011                     {
5012                       gtk_clist_draw_focus (widget);
5013                       clist->focus_row = row;
5014                       gtk_clist_draw_focus (widget);
5015                     }
5016                   else
5017                     clist->focus_row = row;
5018                 }
5019             }
5020
5021           if (!GTK_WIDGET_HAS_FOCUS(widget))
5022             gtk_widget_grab_focus (widget);
5023
5024           if (button_actions & GTK_BUTTON_SELECTS)
5025             {
5026               switch (clist->selection_mode)
5027                 {
5028                 case GTK_SELECTION_SINGLE:
5029                 case GTK_SELECTION_MULTIPLE:
5030                   if (event->type != GDK_BUTTON_PRESS)
5031                     {
5032                       gtk_signal_emit (GTK_OBJECT (clist),
5033                                        clist_signals[SELECT_ROW],
5034                                        row, column, event);
5035                       clist->anchor = -1;
5036                     }
5037                   else
5038                     clist->anchor = row;
5039                   break;
5040                 case GTK_SELECTION_BROWSE:
5041                   gtk_signal_emit (GTK_OBJECT (clist),
5042                                    clist_signals[SELECT_ROW],
5043                                    row, column, event);
5044                   break;
5045                 case GTK_SELECTION_EXTENDED:
5046                   if (event->type != GDK_BUTTON_PRESS)
5047                     {
5048                       if (clist->anchor != -1)
5049                         {
5050                           update_extended_selection (clist, clist->focus_row);
5051                           GTK_CLIST_GET_CLASS (clist)->resync_selection
5052                             (clist, (GdkEvent *) event);
5053                         }
5054                       gtk_signal_emit (GTK_OBJECT (clist),
5055                                        clist_signals[SELECT_ROW],
5056                                        row, column, event);
5057                       break;
5058                     }
5059               
5060                   if (event->state & GDK_CONTROL_MASK)
5061                     {
5062                       if (event->state & GDK_SHIFT_MASK)
5063                         {
5064                           if (clist->anchor < 0)
5065                             {
5066                               g_list_free (clist->undo_selection);
5067                               g_list_free (clist->undo_unselection);
5068                               clist->undo_selection = NULL;
5069                               clist->undo_unselection = NULL;
5070                               clist->anchor = old_row;
5071                               clist->drag_pos = old_row;
5072                               clist->undo_anchor = old_row;
5073                             }
5074                           update_extended_selection (clist, clist->focus_row);
5075                         }
5076                       else
5077                         {
5078                           if (clist->anchor == -1)
5079                             set_anchor (clist, TRUE, row, old_row);
5080                           else
5081                             update_extended_selection (clist,
5082                                                        clist->focus_row);
5083                         }
5084                       break;
5085                     }
5086
5087                   if (event->state & GDK_SHIFT_MASK)
5088                     {
5089                       set_anchor (clist, FALSE, old_row, old_row);
5090                       update_extended_selection (clist, clist->focus_row);
5091                       break;
5092                     }
5093
5094                   if (clist->anchor == -1)
5095                     set_anchor (clist, FALSE, row, old_row);
5096                   else
5097                     update_extended_selection (clist, clist->focus_row);
5098                   break;
5099                 default:
5100                   break;
5101                 }
5102             }
5103         }
5104       return FALSE;
5105     }
5106
5107   /* press on resize windows */
5108   for (i = 0; i < clist->columns; i++)
5109     if (clist->column[i].resizeable && clist->column[i].window &&
5110         event->window == clist->column[i].window)
5111       {
5112         gpointer drag_data;
5113
5114         if (gdk_pointer_grab (clist->column[i].window, FALSE,
5115                               GDK_POINTER_MOTION_HINT_MASK |
5116                               GDK_BUTTON1_MOTION_MASK |
5117                               GDK_BUTTON_RELEASE_MASK,
5118                               NULL, NULL, event->time))
5119           return FALSE;
5120
5121         gtk_grab_add (widget);
5122         GTK_CLIST_SET_FLAG (clist, CLIST_IN_DRAG);
5123
5124         /* block attached dnd signal handler */
5125         drag_data = gtk_object_get_data (GTK_OBJECT (clist), "gtk-site-data");
5126         if (drag_data)
5127           gtk_signal_handler_block_by_data (GTK_OBJECT (clist), drag_data);
5128
5129         if (!GTK_WIDGET_HAS_FOCUS(widget))
5130           gtk_widget_grab_focus (widget);
5131
5132         clist->drag_pos = i;
5133         clist->x_drag = (COLUMN_LEFT_XPIXEL(clist, i) + COLUMN_INSET +
5134                          clist->column[i].area.width + CELL_SPACING);
5135
5136         if (GTK_CLIST_ADD_MODE(clist))
5137           gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
5138         draw_xor_line (clist);
5139       }
5140   return FALSE;
5141 }
5142
5143 static gint
5144 gtk_clist_button_release (GtkWidget      *widget,
5145                           GdkEventButton *event)
5146 {
5147   GtkCList *clist;
5148   gint button_actions;
5149
5150   g_return_val_if_fail (widget != NULL, FALSE);
5151   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
5152   g_return_val_if_fail (event != NULL, FALSE);
5153
5154   clist = GTK_CLIST (widget);
5155
5156   button_actions = clist->button_actions[event->button - 1];
5157   if (button_actions == GTK_BUTTON_IGNORED)
5158     return FALSE;
5159
5160   /* release on resize windows */
5161   if (GTK_CLIST_IN_DRAG(clist))
5162     {
5163       gpointer drag_data;
5164       gint width;
5165       gint x;
5166       gint i;
5167
5168       i = clist->drag_pos;
5169       clist->drag_pos = -1;
5170
5171       /* unblock attached dnd signal handler */
5172       drag_data = gtk_object_get_data (GTK_OBJECT (clist), "gtk-site-data");
5173       if (drag_data)
5174         gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist), drag_data);
5175
5176       GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
5177       gtk_widget_get_pointer (widget, &x, NULL);
5178       gtk_grab_remove (widget);
5179       gdk_pointer_ungrab (event->time);
5180
5181       if (clist->x_drag >= 0)
5182         draw_xor_line (clist);
5183
5184       if (GTK_CLIST_ADD_MODE(clist))
5185         {
5186           gdk_gc_set_line_attributes (clist->xor_gc, 1,
5187                                       GDK_LINE_ON_OFF_DASH, 0, 0);
5188           gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
5189         }
5190
5191       width = new_column_width (clist, i, &x);
5192       gtk_clist_set_column_width (clist, i, width);
5193       return FALSE;
5194     }
5195
5196   if (clist->drag_button == event->button)
5197     {
5198       gint row;
5199       gint column;
5200
5201       clist->drag_button = 0;
5202       clist->click_cell.row = -1;
5203       clist->click_cell.column = -1;
5204
5205       remove_grab (clist);
5206
5207       if (button_actions & GTK_BUTTON_SELECTS)
5208         {
5209           switch (clist->selection_mode)
5210             {
5211             case GTK_SELECTION_EXTENDED:
5212               if (!(event->state & GDK_SHIFT_MASK) ||
5213                   !GTK_WIDGET_CAN_FOCUS (widget) ||
5214                   event->x < 0 || event->x >= clist->clist_window_width ||
5215                   event->y < 0 || event->y >= clist->clist_window_height)
5216                 GTK_CLIST_GET_CLASS (clist)->resync_selection
5217                   (clist, (GdkEvent *) event);
5218               break;
5219             case GTK_SELECTION_SINGLE:
5220             case GTK_SELECTION_MULTIPLE:
5221               if (get_selection_info (clist, event->x, event->y,
5222                                       &row, &column))
5223                 {
5224                   if (row >= 0 && row < clist->rows && clist->anchor == row)
5225                     toggle_row (clist, row, column, (GdkEvent *) event);
5226                 }
5227               clist->anchor = -1;
5228               break;
5229             default:
5230               break;
5231             }
5232         }
5233     }
5234   return FALSE;
5235 }
5236
5237 static gint
5238 gtk_clist_motion (GtkWidget      *widget,
5239                   GdkEventMotion *event)
5240 {
5241   GtkCList *clist;
5242   gint x;
5243   gint y;
5244   gint row;
5245   gint new_width;
5246   gint button_actions = 0;
5247
5248   g_return_val_if_fail (widget != NULL, FALSE);
5249   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
5250
5251   clist = GTK_CLIST (widget);
5252   if (!(gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)))
5253     return FALSE;
5254
5255   if (clist->drag_button > 0)
5256     button_actions = clist->button_actions[clist->drag_button - 1];
5257
5258   if (GTK_CLIST_IN_DRAG(clist))
5259     {
5260       if (event->is_hint || event->window != widget->window)
5261         gtk_widget_get_pointer (widget, &x, NULL);
5262       else
5263         x = event->x;
5264       
5265       new_width = new_column_width (clist, clist->drag_pos, &x);
5266       if (x != clist->x_drag)
5267         {
5268           /* x_drag < 0 indicates that the xor line is already invisible */
5269           if (clist->x_drag >= 0)
5270             draw_xor_line (clist);
5271
5272           clist->x_drag = x;
5273
5274           if (clist->x_drag >= 0)
5275             draw_xor_line (clist);
5276         }
5277
5278       if (new_width <= MAX (COLUMN_MIN_WIDTH + 1,
5279                             clist->column[clist->drag_pos].min_width + 1))
5280         {
5281           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) < 0 && x < 0)
5282             gtk_clist_moveto (clist, -1, clist->drag_pos, 0, 0);
5283           return FALSE;
5284         }
5285       if (clist->column[clist->drag_pos].max_width >= COLUMN_MIN_WIDTH &&
5286           new_width >= clist->column[clist->drag_pos].max_width)
5287         {
5288           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) + new_width >
5289               clist->clist_window_width && x < 0)
5290             move_horizontal (clist,
5291                              COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) +
5292                              new_width - clist->clist_window_width +
5293                              COLUMN_INSET + CELL_SPACING);
5294           return FALSE;
5295         }
5296     }
5297
5298   if (event->is_hint || event->window != clist->clist_window)
5299     gdk_window_get_pointer (clist->clist_window, &x, &y, NULL);
5300   else
5301     {
5302       x = event->x;
5303       y = event->y;
5304     }
5305
5306   if (GTK_CLIST_REORDERABLE(clist) && button_actions & GTK_BUTTON_DRAGS)
5307     {
5308       /* delayed drag start */
5309       if (event->window == clist->clist_window &&
5310           clist->click_cell.row >= 0 && clist->click_cell.column >= 0 &&
5311           (y < 0 || y >= clist->clist_window_height ||
5312            x < 0 || x >= clist->clist_window_width  ||
5313            y < ROW_TOP_YPIXEL (clist, clist->click_cell.row) ||
5314            y >= (ROW_TOP_YPIXEL (clist, clist->click_cell.row) +
5315                  clist->row_height) ||
5316            x < COLUMN_LEFT_XPIXEL (clist, clist->click_cell.column) ||
5317            x >= (COLUMN_LEFT_XPIXEL(clist, clist->click_cell.column) + 
5318                  clist->column[clist->click_cell.column].area.width)))
5319         {
5320           GtkTargetList  *target_list;
5321
5322           target_list = gtk_target_list_new (&clist_target_table, 1);
5323           gtk_drag_begin (widget, target_list, GDK_ACTION_MOVE,
5324                           clist->drag_button, (GdkEvent *)event);
5325
5326         }
5327       return TRUE;
5328     }
5329
5330   /* horizontal autoscrolling */
5331   if (clist->hadjustment && LIST_WIDTH (clist) > clist->clist_window_width &&
5332       (x < 0 || x >= clist->clist_window_width))
5333     {
5334       if (clist->htimer)
5335         return FALSE;
5336
5337       clist->htimer = gtk_timeout_add
5338         (SCROLL_TIME, (GtkFunction) horizontal_timeout, clist);
5339
5340       if (!((x < 0 && clist->hadjustment->value == 0) ||
5341             (x >= clist->clist_window_width &&
5342              clist->hadjustment->value ==
5343              LIST_WIDTH (clist) - clist->clist_window_width)))
5344         {
5345           if (x < 0)
5346             move_horizontal (clist, -1 + (x/2));
5347           else
5348             move_horizontal (clist, 1 + (x - clist->clist_window_width) / 2);
5349         }
5350     }
5351
5352   if (GTK_CLIST_IN_DRAG(clist))
5353     return FALSE;
5354
5355   /* vertical autoscrolling */
5356   row = ROW_FROM_YPIXEL (clist, y);
5357
5358   /* don't scroll on last pixel row if it's a cell spacing */
5359   if (y == clist->clist_window_height - 1 &&
5360       y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height)
5361     return FALSE;
5362
5363   if (LIST_HEIGHT (clist) > clist->clist_window_height &&
5364       (y < 0 || y >= clist->clist_window_height))
5365     {
5366       if (clist->vtimer)
5367         return FALSE;
5368
5369       clist->vtimer = gtk_timeout_add (SCROLL_TIME,
5370                                        (GtkFunction) vertical_timeout, clist);
5371
5372       if (clist->drag_button &&
5373           ((y < 0 && clist->focus_row == 0) ||
5374            (y >= clist->clist_window_height &&
5375             clist->focus_row == clist->rows - 1)))
5376         return FALSE;
5377     }
5378
5379   row = CLAMP (row, 0, clist->rows - 1);
5380
5381   if (button_actions & GTK_BUTTON_SELECTS &
5382       !gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"))
5383     {
5384       if (row == clist->focus_row)
5385         return FALSE;
5386
5387       gtk_clist_draw_focus (widget);
5388       clist->focus_row = row;
5389       gtk_clist_draw_focus (widget);
5390
5391       switch (clist->selection_mode)
5392         {
5393         case GTK_SELECTION_BROWSE:
5394           gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
5395                            clist->focus_row, -1, event);
5396           break;
5397         case GTK_SELECTION_EXTENDED:
5398           update_extended_selection (clist, clist->focus_row);
5399           break;
5400         default:
5401           break;
5402         }
5403     }
5404   
5405   if (ROW_TOP_YPIXEL(clist, row) < 0)
5406     move_vertical (clist, row, 0);
5407   else if (ROW_TOP_YPIXEL(clist, row) + clist->row_height >
5408            clist->clist_window_height)
5409     move_vertical (clist, row, 1);
5410
5411   return FALSE;
5412 }
5413
5414 static void
5415 gtk_clist_size_request (GtkWidget      *widget,
5416                         GtkRequisition *requisition)
5417 {
5418   GtkCList *clist;
5419   gint i;
5420
5421   g_return_if_fail (widget != NULL);
5422   g_return_if_fail (GTK_IS_CLIST (widget));
5423   g_return_if_fail (requisition != NULL);
5424
5425   clist = GTK_CLIST (widget);
5426
5427   requisition->width = 0;
5428   requisition->height = 0;
5429
5430   /* compute the size of the column title (title) area */
5431   clist->column_title_area.height = 0;
5432   if (GTK_CLIST_SHOW_TITLES(clist))
5433     for (i = 0; i < clist->columns; i++)
5434       if (clist->column[i].button)
5435         {
5436           GtkRequisition child_requisition;
5437           
5438           gtk_widget_size_request (clist->column[i].button,
5439                                    &child_requisition);
5440           clist->column_title_area.height =
5441             MAX (clist->column_title_area.height,
5442                  child_requisition.height);
5443         }
5444
5445   requisition->width += (widget->style->klass->xthickness +
5446                          GTK_CONTAINER (widget)->border_width) * 2;
5447   requisition->height += (clist->column_title_area.height +
5448                           (widget->style->klass->ythickness +
5449                            GTK_CONTAINER (widget)->border_width) * 2);
5450
5451   /* if (!clist->hadjustment) */
5452   requisition->width += list_requisition_width (clist);
5453   /* if (!clist->vadjustment) */
5454   requisition->height += LIST_HEIGHT (clist);
5455 }
5456
5457 static void
5458 gtk_clist_size_allocate (GtkWidget     *widget,
5459                          GtkAllocation *allocation)
5460 {
5461   GtkCList *clist;
5462   GtkAllocation clist_allocation;
5463   gint border_width;
5464
5465   g_return_if_fail (widget != NULL);
5466   g_return_if_fail (GTK_IS_CLIST (widget));
5467   g_return_if_fail (allocation != NULL);
5468
5469   clist = GTK_CLIST (widget);
5470   widget->allocation = *allocation;
5471   border_width = GTK_CONTAINER (widget)->border_width;
5472
5473   if (GTK_WIDGET_REALIZED (widget))
5474     {
5475       gdk_window_move_resize (widget->window,
5476                               allocation->x + border_width,
5477                               allocation->y + border_width,
5478                               allocation->width - border_width * 2,
5479                               allocation->height - border_width * 2);
5480     }
5481
5482   /* use internal allocation structure for all the math
5483    * because it's easier than always subtracting the container
5484    * border width */
5485   clist->internal_allocation.x = 0;
5486   clist->internal_allocation.y = 0;
5487   clist->internal_allocation.width = MAX (1, (gint)allocation->width -
5488                                           border_width * 2);
5489   clist->internal_allocation.height = MAX (1, (gint)allocation->height -
5490                                            border_width * 2);
5491         
5492   /* allocate clist window assuming no scrollbars */
5493   clist_allocation.x = (clist->internal_allocation.x +
5494                         widget->style->klass->xthickness);
5495   clist_allocation.y = (clist->internal_allocation.y +
5496                         widget->style->klass->ythickness +
5497                         clist->column_title_area.height);
5498   clist_allocation.width = MAX (1, (gint)clist->internal_allocation.width - 
5499                                 (2 * (gint)widget->style->klass->xthickness));
5500   clist_allocation.height = MAX (1, (gint)clist->internal_allocation.height -
5501                                  (2 * (gint)widget->style->klass->ythickness) -
5502                                  (gint)clist->column_title_area.height);
5503   
5504   clist->clist_window_width = clist_allocation.width;
5505   clist->clist_window_height = clist_allocation.height;
5506   
5507   if (GTK_WIDGET_REALIZED (widget))
5508     {
5509       gdk_window_move_resize (clist->clist_window,
5510                               clist_allocation.x,
5511                               clist_allocation.y,
5512                               clist_allocation.width,
5513                               clist_allocation.height);
5514     }
5515   
5516   /* position the window which holds the column title buttons */
5517   clist->column_title_area.x = widget->style->klass->xthickness;
5518   clist->column_title_area.y = widget->style->klass->ythickness;
5519   clist->column_title_area.width = clist_allocation.width;
5520   
5521   if (GTK_WIDGET_REALIZED (widget))
5522     {
5523       gdk_window_move_resize (clist->title_window,
5524                               clist->column_title_area.x,
5525                               clist->column_title_area.y,
5526                               clist->column_title_area.width,
5527                               clist->column_title_area.height);
5528     }
5529   
5530   /* column button allocation */
5531   size_allocate_columns (clist, FALSE);
5532   size_allocate_title_buttons (clist);
5533
5534   adjust_adjustments (clist, TRUE);
5535 }
5536
5537 /* GTKCONTAINER
5538  *   gtk_clist_forall
5539  */
5540 static void
5541 gtk_clist_forall (GtkContainer *container,
5542                   gboolean      include_internals,
5543                   GtkCallback   callback,
5544                   gpointer      callback_data)
5545 {
5546   GtkCList *clist;
5547   guint i;
5548
5549   g_return_if_fail (container != NULL);
5550   g_return_if_fail (GTK_IS_CLIST (container));
5551   g_return_if_fail (callback != NULL);
5552
5553   if (!include_internals)
5554     return;
5555
5556   clist = GTK_CLIST (container);
5557       
5558   /* callback for the column buttons */
5559   for (i = 0; i < clist->columns; i++)
5560     if (clist->column[i].button)
5561       (*callback) (clist->column[i].button, callback_data);
5562 }
5563
5564 /* PRIVATE DRAWING FUNCTIONS
5565  *   get_cell_style
5566  *   draw_cell_pixmap
5567  *   draw_row
5568  *   draw_rows
5569  *   draw_xor_line
5570  *   clist_refresh
5571  */
5572 static void
5573 get_cell_style (GtkCList     *clist,
5574                 GtkCListRow  *clist_row,
5575                 gint          state,
5576                 gint          column,
5577                 GtkStyle    **style,
5578                 GdkGC       **fg_gc,
5579                 GdkGC       **bg_gc)
5580 {
5581   gint fg_state;
5582
5583   if ((state == GTK_STATE_NORMAL) &&
5584       (GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
5585     fg_state = GTK_STATE_INSENSITIVE;
5586   else
5587     fg_state = state;
5588
5589   if (clist_row->cell[column].style)
5590     {
5591       if (style)
5592         *style = clist_row->cell[column].style;
5593       if (fg_gc)
5594         *fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
5595       if (bg_gc) {
5596         if (state == GTK_STATE_SELECTED)
5597           *bg_gc = clist_row->cell[column].style->bg_gc[state];
5598         else
5599           *bg_gc = clist_row->cell[column].style->base_gc[state];
5600       }
5601     }
5602   else if (clist_row->style)
5603     {
5604       if (style)
5605         *style = clist_row->style;
5606       if (fg_gc)
5607         *fg_gc = clist_row->style->fg_gc[fg_state];
5608       if (bg_gc) {
5609         if (state == GTK_STATE_SELECTED)
5610           *bg_gc = clist_row->style->bg_gc[state];
5611         else
5612           *bg_gc = clist_row->style->base_gc[state];
5613       }
5614     }
5615   else
5616     {
5617       if (style)
5618         *style = GTK_WIDGET (clist)->style;
5619       if (fg_gc)
5620         *fg_gc = GTK_WIDGET (clist)->style->fg_gc[fg_state];
5621       if (bg_gc) {
5622         if (state == GTK_STATE_SELECTED)
5623           *bg_gc = GTK_WIDGET (clist)->style->bg_gc[state];
5624         else
5625           *bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
5626       }
5627
5628       if (state != GTK_STATE_SELECTED)
5629         {
5630           if (fg_gc && clist_row->fg_set)
5631             *fg_gc = clist->fg_gc;
5632           if (bg_gc && clist_row->bg_set)
5633             *bg_gc = clist->bg_gc;
5634         }
5635     }
5636 }
5637
5638 static gint
5639 draw_cell_pixmap (GdkWindow    *window,
5640                   GdkRectangle *clip_rectangle,
5641                   GdkGC        *fg_gc,
5642                   GdkPixmap    *pixmap,
5643                   GdkBitmap    *mask,
5644                   gint          x,
5645                   gint          y,
5646                   gint          width,
5647                   gint          height)
5648 {
5649   gint xsrc = 0;
5650   gint ysrc = 0;
5651
5652   if (mask)
5653     {
5654       gdk_gc_set_clip_mask (fg_gc, mask);
5655       gdk_gc_set_clip_origin (fg_gc, x, y);
5656     }
5657
5658   if (x < clip_rectangle->x)
5659     {
5660       xsrc = clip_rectangle->x - x;
5661       width -= xsrc;
5662       x = clip_rectangle->x;
5663     }
5664   if (x + width > clip_rectangle->x + clip_rectangle->width)
5665     width = clip_rectangle->x + clip_rectangle->width - x;
5666
5667   if (y < clip_rectangle->y)
5668     {
5669       ysrc = clip_rectangle->y - y;
5670       height -= ysrc;
5671       y = clip_rectangle->y;
5672     }
5673   if (y + height > clip_rectangle->y + clip_rectangle->height)
5674     height = clip_rectangle->y + clip_rectangle->height - y;
5675
5676   gdk_draw_pixmap (window, fg_gc, pixmap, xsrc, ysrc, x, y, width, height);
5677   gdk_gc_set_clip_origin (fg_gc, 0, 0);
5678   if (mask)
5679     gdk_gc_set_clip_mask (fg_gc, NULL);
5680
5681   return x + MAX (width, 0);
5682 }
5683
5684 static void
5685 draw_row (GtkCList     *clist,
5686           GdkRectangle *area,
5687           gint          row,
5688           GtkCListRow  *clist_row)
5689 {
5690   GtkWidget *widget;
5691   GdkRectangle *rect;
5692   GdkRectangle row_rectangle;
5693   GdkRectangle cell_rectangle;
5694   GdkRectangle clip_rectangle;
5695   GdkRectangle intersect_rectangle;
5696   gint last_column;
5697   gint state;
5698   gint i;
5699
5700   g_return_if_fail (clist != NULL);
5701
5702   /* bail now if we arn't drawable yet */
5703   if (!GTK_WIDGET_DRAWABLE (clist) || row < 0 || row >= clist->rows)
5704     return;
5705
5706   widget = GTK_WIDGET (clist);
5707
5708   /* if the function is passed the pointer to the row instead of null,
5709    * it avoids this expensive lookup */
5710   if (!clist_row)
5711     clist_row = ROW_ELEMENT (clist, row)->data;
5712
5713   /* rectangle of the entire row */
5714   row_rectangle.x = 0;
5715   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
5716   row_rectangle.width = clist->clist_window_width;
5717   row_rectangle.height = clist->row_height;
5718
5719   /* rectangle of the cell spacing above the row */
5720   cell_rectangle.x = 0;
5721   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
5722   cell_rectangle.width = row_rectangle.width;
5723   cell_rectangle.height = CELL_SPACING;
5724
5725   /* rectangle used to clip drawing operations, its y and height
5726    * positions only need to be set once, so we set them once here. 
5727    * the x and width are set withing the drawing loop below once per
5728    * column */
5729   clip_rectangle.y = row_rectangle.y;
5730   clip_rectangle.height = row_rectangle.height;
5731
5732   if (clist_row->state == GTK_STATE_NORMAL)
5733     {
5734       if (clist_row->fg_set)
5735         gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
5736       if (clist_row->bg_set)
5737         gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
5738     }
5739
5740   state = clist_row->state;
5741
5742   /* draw the cell borders and background */
5743   if (area)
5744     {
5745       rect = &intersect_rectangle;
5746       if (gdk_rectangle_intersect (area, &cell_rectangle,
5747                                    &intersect_rectangle))
5748         gdk_draw_rectangle (clist->clist_window,
5749                             widget->style->base_gc[GTK_STATE_ACTIVE],
5750                             TRUE,
5751                             intersect_rectangle.x,
5752                             intersect_rectangle.y,
5753                             intersect_rectangle.width,
5754                             intersect_rectangle.height);
5755
5756       /* the last row has to clear its bottom cell spacing too */
5757       if (clist_row == clist->row_list_end->data)
5758         {
5759           cell_rectangle.y += clist->row_height + CELL_SPACING;
5760
5761           if (gdk_rectangle_intersect (area, &cell_rectangle,
5762                                        &intersect_rectangle))
5763             gdk_draw_rectangle (clist->clist_window,
5764                                 widget->style->base_gc[GTK_STATE_ACTIVE],
5765                                 TRUE,
5766                                 intersect_rectangle.x,
5767                                 intersect_rectangle.y,
5768                                 intersect_rectangle.width,
5769                                 intersect_rectangle.height);
5770         }
5771
5772       if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
5773         return;
5774
5775     }
5776   else
5777     {
5778       rect = &clip_rectangle;
5779       gdk_draw_rectangle (clist->clist_window,
5780                           widget->style->base_gc[GTK_STATE_ACTIVE],
5781                           TRUE,
5782                           cell_rectangle.x,
5783                           cell_rectangle.y,
5784                           cell_rectangle.width,
5785                           cell_rectangle.height);
5786
5787       /* the last row has to clear its bottom cell spacing too */
5788       if (clist_row == clist->row_list_end->data)
5789         {
5790           cell_rectangle.y += clist->row_height + CELL_SPACING;
5791
5792           gdk_draw_rectangle (clist->clist_window,
5793                               widget->style->base_gc[GTK_STATE_ACTIVE],
5794                               TRUE,
5795                               cell_rectangle.x,
5796                               cell_rectangle.y,
5797                               cell_rectangle.width,
5798                               cell_rectangle.height);     
5799         }         
5800     }
5801   
5802   for (last_column = clist->columns - 1;
5803        last_column >= 0 && !clist->column[last_column].visible; last_column--)
5804     ;
5805
5806   /* iterate and draw all the columns (row cells) and draw their contents */
5807   for (i = 0; i < clist->columns; i++)
5808     {
5809       GtkStyle *style;
5810       GdkGC *fg_gc;
5811       GdkGC *bg_gc;
5812       PangoLayout *layout;
5813       PangoRectangle logical_rect;
5814
5815       gint width;
5816       gint height;
5817       gint pixmap_width;
5818       gint offset = 0;
5819
5820       if (!clist->column[i].visible)
5821         continue;
5822
5823       get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
5824
5825       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
5826       clip_rectangle.width = clist->column[i].area.width;
5827
5828       /* calculate clipping region clipping region */
5829       clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
5830       clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
5831                                (i == last_column) * CELL_SPACING);
5832       
5833       if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
5834                                             &intersect_rectangle))
5835         continue;
5836
5837       gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
5838                           rect->x, rect->y, rect->width, rect->height);
5839
5840       clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
5841       clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
5842                                (i == last_column) * CELL_SPACING);
5843
5844
5845       /* calculate real width for column justification */
5846       
5847       layout = _gtk_clist_create_cell_layout (clist, clist_row, i);
5848       if (layout)
5849         {
5850           pango_layout_get_extents (layout, NULL, &logical_rect);
5851           width = logical_rect.width / PANGO_SCALE;
5852         }
5853       else
5854         width = 0;
5855
5856       pixmap_width = 0;
5857       offset = 0;
5858       switch (clist_row->cell[i].type)
5859         {
5860         case GTK_CELL_PIXMAP:
5861           gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5862                                &pixmap_width, &height);
5863           width += pixmap_width;
5864           break;
5865         case GTK_CELL_PIXTEXT:
5866           gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5867                                &pixmap_width, &height);
5868           width += pixmap_width + GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
5869           break;
5870         default:
5871           break;
5872         }
5873
5874       switch (clist->column[i].justification)
5875         {
5876         case GTK_JUSTIFY_LEFT:
5877           offset = clip_rectangle.x + clist_row->cell[i].horizontal;
5878           break;
5879         case GTK_JUSTIFY_RIGHT:
5880           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5881                     clip_rectangle.width - width);
5882           break;
5883         case GTK_JUSTIFY_CENTER:
5884         case GTK_JUSTIFY_FILL:
5885           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5886                     (clip_rectangle.width / 2) - (width / 2));
5887           break;
5888         };
5889
5890       /* Draw Text and/or Pixmap */
5891       switch (clist_row->cell[i].type)
5892         {
5893         case GTK_CELL_PIXMAP:
5894           draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5895                             GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
5896                             GTK_CELL_PIXMAP (clist_row->cell[i])->mask,
5897                             offset,
5898                             clip_rectangle.y + clist_row->cell[i].vertical +
5899                             (clip_rectangle.height - height) / 2,
5900                             pixmap_width, height);
5901           break;
5902         case GTK_CELL_PIXTEXT:
5903           offset =
5904             draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
5905                               GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
5906                               GTK_CELL_PIXTEXT (clist_row->cell[i])->mask,
5907                               offset,
5908                               clip_rectangle.y + clist_row->cell[i].vertical+
5909                               (clip_rectangle.height - height) / 2,
5910                               pixmap_width, height);
5911           offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
5912
5913           /* Fall through */
5914         case GTK_CELL_TEXT:
5915           if (layout)
5916             {
5917               gint row_center_offset = 1.5 + (clist->row_height - logical_rect.height / PANGO_SCALE - 1) / 2;
5918
5919               gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
5920               gdk_draw_layout (clist->clist_window, fg_gc,
5921                                offset,
5922                                row_rectangle.y + row_center_offset + clist_row->cell[i].vertical,
5923                                layout);
5924               pango_layout_unref (layout);
5925               gdk_gc_set_clip_rectangle (fg_gc, NULL);
5926             }
5927           break;
5928         default:
5929           break;
5930         }
5931     }
5932
5933   /* draw focus rectangle */
5934   if (clist->focus_row == row &&
5935       GTK_WIDGET_CAN_FOCUS (widget) && GTK_WIDGET_HAS_FOCUS(widget))
5936     {
5937       if (!area)
5938         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5939                             row_rectangle.x, row_rectangle.y,
5940                             row_rectangle.width - 1, row_rectangle.height - 1);
5941       else if (gdk_rectangle_intersect (area, &row_rectangle,
5942                                         &intersect_rectangle))
5943         {
5944           gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
5945           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
5946                               row_rectangle.x, row_rectangle.y,
5947                               row_rectangle.width - 1,
5948                               row_rectangle.height - 1);
5949           gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
5950         }
5951     }
5952 }
5953
5954 static void
5955 draw_rows (GtkCList     *clist,
5956            GdkRectangle *area)
5957 {
5958   GList *list;
5959   GtkCListRow *clist_row;
5960   gint i;
5961   gint first_row;
5962   gint last_row;
5963
5964   g_return_if_fail (clist != NULL);
5965   g_return_if_fail (GTK_IS_CLIST (clist));
5966
5967   if (clist->row_height == 0 ||
5968       !GTK_WIDGET_DRAWABLE (clist))
5969     return;
5970
5971   if (area)
5972     {
5973       first_row = ROW_FROM_YPIXEL (clist, area->y);
5974       last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
5975     }
5976   else
5977     {
5978       first_row = ROW_FROM_YPIXEL (clist, 0);
5979       last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
5980     }
5981
5982   /* this is a small special case which exposes the bottom cell line
5983    * on the last row -- it might go away if I change the wall the cell
5984    * spacings are drawn
5985    */
5986   if (clist->rows == first_row)
5987     first_row--;
5988
5989   list = ROW_ELEMENT (clist, first_row);
5990   i = first_row;
5991   while (list)
5992     {
5993       clist_row = list->data;
5994       list = list->next;
5995
5996       if (i > last_row)
5997         return;
5998
5999       GTK_CLIST_GET_CLASS (clist)->draw_row (clist, area, i, clist_row);
6000       i++;
6001     }
6002
6003   if (!area)
6004     gdk_window_clear_area (clist->clist_window, 0,
6005                            ROW_TOP_YPIXEL (clist, i), 0, 0);
6006 }
6007
6008 static void                          
6009 draw_xor_line (GtkCList *clist)
6010 {
6011   GtkWidget *widget;
6012
6013   g_return_if_fail (clist != NULL);
6014
6015   widget = GTK_WIDGET (clist);
6016
6017   gdk_draw_line (widget->window, clist->xor_gc,
6018                  clist->x_drag,
6019                  widget->style->klass->ythickness,
6020                  clist->x_drag,
6021                  clist->column_title_area.height +
6022                  clist->clist_window_height + 1);
6023 }
6024
6025 static void
6026 clist_refresh (GtkCList *clist)
6027 {
6028   g_return_if_fail (clist != NULL);
6029   g_return_if_fail (GTK_IS_CLIST (clist));
6030   
6031   if (CLIST_UNFROZEN (clist))
6032     { 
6033       adjust_adjustments (clist, FALSE);
6034       draw_rows (clist, NULL);
6035     }
6036 }
6037
6038 /* get cell from coordinates
6039  *   get_selection_info
6040  *   gtk_clist_get_selection_info
6041  */
6042 static gint
6043 get_selection_info (GtkCList *clist,
6044                     gint      x,
6045                     gint      y,
6046                     gint     *row,
6047                     gint     *column)
6048 {
6049   gint trow, tcol;
6050
6051   g_return_val_if_fail (clist != NULL, 0);
6052   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
6053
6054   /* bounds checking, return false if the user clicked 
6055    * on a blank area */
6056   trow = ROW_FROM_YPIXEL (clist, y);
6057   if (trow >= clist->rows)
6058     return 0;
6059
6060   if (row)
6061     *row = trow;
6062
6063   tcol = COLUMN_FROM_XPIXEL (clist, x);
6064   if (tcol >= clist->columns)
6065     return 0;
6066
6067   if (column)
6068     *column = tcol;
6069
6070   return 1;
6071 }
6072
6073 gint
6074 gtk_clist_get_selection_info (GtkCList *clist, 
6075                               gint      x, 
6076                               gint      y, 
6077                               gint     *row, 
6078                               gint     *column)
6079 {
6080   g_return_val_if_fail (clist != NULL, 0);
6081   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
6082   return get_selection_info (clist, x, y, row, column);
6083 }
6084
6085 /* PRIVATE ADJUSTMENT FUNCTIONS
6086  *   adjust_adjustments
6087  *   vadjustment_changed
6088  *   hadjustment_changed
6089  *   vadjustment_value_changed
6090  *   hadjustment_value_changed 
6091  *   check_exposures
6092  */
6093 static void
6094 adjust_adjustments (GtkCList *clist,
6095                     gboolean  block_resize)
6096 {
6097   if (clist->vadjustment)
6098     {
6099       clist->vadjustment->page_size = clist->clist_window_height;
6100       clist->vadjustment->page_increment = clist->clist_window_height / 2;
6101       clist->vadjustment->step_increment = clist->row_height;
6102       clist->vadjustment->lower = 0;
6103       clist->vadjustment->upper = LIST_HEIGHT (clist);
6104
6105       if (clist->clist_window_height - clist->voffset > LIST_HEIGHT (clist) ||
6106           (clist->voffset + (gint)clist->vadjustment->value) != 0)
6107         {
6108           clist->vadjustment->value = MAX (0, (LIST_HEIGHT (clist) -
6109                                                clist->clist_window_height));
6110           gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment),
6111                                    "value_changed");
6112         }
6113       gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment), "changed");
6114     }
6115
6116   if (clist->hadjustment)
6117     {
6118       clist->hadjustment->page_size = clist->clist_window_width;
6119       clist->hadjustment->page_increment = clist->clist_window_width / 2;
6120       clist->hadjustment->step_increment = 10;
6121       clist->hadjustment->lower = 0;
6122       clist->hadjustment->upper = LIST_WIDTH (clist);
6123
6124       if (clist->clist_window_width - clist->hoffset > LIST_WIDTH (clist) ||
6125           (clist->hoffset + (gint)clist->hadjustment->value) != 0)
6126         {
6127           clist->hadjustment->value = MAX (0, (LIST_WIDTH (clist) -
6128                                                clist->clist_window_width));
6129           gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment),
6130                                    "value_changed");
6131         }
6132       gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment), "changed");
6133     }
6134
6135   if (!block_resize && (!clist->vadjustment || !clist->hadjustment))
6136     {
6137       GtkWidget *widget;
6138       GtkRequisition requisition;
6139
6140       widget = GTK_WIDGET (clist);
6141       gtk_widget_size_request (widget, &requisition);
6142
6143       if ((!clist->hadjustment &&
6144            requisition.width != widget->allocation.width) ||
6145           (!clist->vadjustment &&
6146            requisition.height != widget->allocation.height))
6147         gtk_widget_queue_resize (widget);
6148     }
6149 }
6150
6151 static void
6152 vadjustment_changed (GtkAdjustment *adjustment,
6153                      gpointer       data)
6154 {
6155   GtkCList *clist;
6156
6157   g_return_if_fail (adjustment != NULL);
6158   g_return_if_fail (data != NULL);
6159
6160   clist = GTK_CLIST (data);
6161 }
6162
6163 static void
6164 hadjustment_changed (GtkAdjustment *adjustment,
6165                      gpointer       data)
6166 {
6167   GtkCList *clist;
6168
6169   g_return_if_fail (adjustment != NULL);
6170   g_return_if_fail (data != NULL);
6171
6172   clist = GTK_CLIST (data);
6173 }
6174
6175 static void
6176 vadjustment_value_changed (GtkAdjustment *adjustment,
6177                            gpointer       data)
6178 {
6179   GtkCList *clist;
6180   GdkRectangle area;
6181   gint diff, value;
6182
6183   g_return_if_fail (adjustment != NULL);
6184   g_return_if_fail (data != NULL);
6185   g_return_if_fail (GTK_IS_CLIST (data));
6186
6187   clist = GTK_CLIST (data);
6188
6189   if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->vadjustment)
6190     return;
6191
6192   value = adjustment->value;
6193
6194   if (value > -clist->voffset)
6195     {
6196       /* scroll down */
6197       diff = value + clist->voffset;
6198
6199       /* we have to re-draw the whole screen here... */
6200       if (diff >= clist->clist_window_height)
6201         {
6202           clist->voffset = -value;
6203           draw_rows (clist, NULL);
6204           return;
6205         }
6206
6207       if ((diff != 0) && (diff != clist->clist_window_height))
6208         gdk_window_copy_area (clist->clist_window, clist->fg_gc,
6209                               0, 0, clist->clist_window, 0, diff,
6210                               clist->clist_window_width,
6211                               clist->clist_window_height - diff);
6212
6213       area.x = 0;
6214       area.y = clist->clist_window_height - diff;
6215       area.width = clist->clist_window_width;
6216       area.height = diff;
6217     }
6218   else
6219     {
6220       /* scroll up */
6221       diff = -clist->voffset - value;
6222
6223       /* we have to re-draw the whole screen here... */
6224       if (diff >= clist->clist_window_height)
6225         {
6226           clist->voffset = -value;
6227           draw_rows (clist, NULL);
6228           return;
6229         }
6230
6231       if ((diff != 0) && (diff != clist->clist_window_height))
6232         gdk_window_copy_area (clist->clist_window, clist->fg_gc,
6233                               0, diff, clist->clist_window, 0, 0,
6234                               clist->clist_window_width,
6235                               clist->clist_window_height - diff);
6236
6237       area.x = 0;
6238       area.y = 0;
6239       area.width = clist->clist_window_width;
6240       area.height = diff;
6241     }
6242
6243   clist->voffset = -value;
6244   if ((diff != 0) && (diff != clist->clist_window_height))
6245     check_exposures (clist);
6246
6247   draw_rows (clist, &area);
6248 }
6249
6250 static void
6251 hadjustment_value_changed (GtkAdjustment *adjustment,
6252                            gpointer       data)
6253 {
6254   GtkCList *clist;
6255   GdkRectangle area;
6256   gint i;
6257   gint y = 0;
6258   gint diff = 0;
6259   gint value;
6260
6261   g_return_if_fail (adjustment != NULL);
6262   g_return_if_fail (data != NULL);
6263   g_return_if_fail (GTK_IS_CLIST (data));
6264
6265   clist = GTK_CLIST (data);
6266
6267   if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->hadjustment)
6268     return;
6269
6270   value = adjustment->value;
6271
6272   /* move the column buttons and resize windows */
6273   for (i = 0; i < clist->columns; i++)
6274     {
6275       if (clist->column[i].button)
6276         {
6277           clist->column[i].button->allocation.x -= value + clist->hoffset;
6278           
6279           if (clist->column[i].button->window)
6280             {
6281               gdk_window_move (clist->column[i].button->window,
6282                                clist->column[i].button->allocation.x,
6283                                clist->column[i].button->allocation.y);
6284               
6285               if (clist->column[i].window)
6286                 gdk_window_move (clist->column[i].window,
6287                                  clist->column[i].button->allocation.x +
6288                                  clist->column[i].button->allocation.width - 
6289                                  (DRAG_WIDTH / 2), 0); 
6290             }
6291         }
6292     }
6293
6294   if (value > -clist->hoffset)
6295     {
6296       /* scroll right */
6297       diff = value + clist->hoffset;
6298       
6299       clist->hoffset = -value;
6300       
6301       /* we have to re-draw the whole screen here... */
6302       if (diff >= clist->clist_window_width)
6303         {
6304           draw_rows (clist, NULL);
6305           return;
6306         }
6307
6308       if (GTK_WIDGET_CAN_FOCUS(clist) && GTK_WIDGET_HAS_FOCUS(clist) &&
6309           !GTK_CLIST_CHILD_HAS_FOCUS(clist) && GTK_CLIST_ADD_MODE(clist))
6310         {
6311           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6312               
6313           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
6314                               clist->clist_window_width - 1,
6315                               clist->row_height - 1);
6316         }
6317       gdk_window_copy_area (clist->clist_window,
6318                             clist->fg_gc,
6319                             0, 0,
6320                             clist->clist_window,
6321                             diff,
6322                             0,
6323                             clist->clist_window_width - diff,
6324                             clist->clist_window_height);
6325
6326       area.x = clist->clist_window_width - diff;
6327     }
6328   else
6329     {
6330       /* scroll left */
6331       if (!(diff = -clist->hoffset - value))
6332         return;
6333
6334       clist->hoffset = -value;
6335       
6336       /* we have to re-draw the whole screen here... */
6337       if (diff >= clist->clist_window_width)
6338         {
6339           draw_rows (clist, NULL);
6340           return;
6341         }
6342       
6343       if (GTK_WIDGET_CAN_FOCUS(clist) && GTK_WIDGET_HAS_FOCUS(clist) &&
6344           !GTK_CLIST_CHILD_HAS_FOCUS(clist) && GTK_CLIST_ADD_MODE(clist))
6345         {
6346           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6347           
6348           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
6349                               clist->clist_window_width - 1,
6350                               clist->row_height - 1);
6351         }
6352
6353       gdk_window_copy_area (clist->clist_window,
6354                             clist->fg_gc,
6355                             diff, 0,
6356                             clist->clist_window,
6357                             0,
6358                             0,
6359                             clist->clist_window_width - diff,
6360                             clist->clist_window_height);
6361           
6362       area.x = 0;
6363     }
6364
6365   area.y = 0;
6366   area.width = diff;
6367   area.height = clist->clist_window_height;
6368
6369   check_exposures (clist);
6370
6371   if (GTK_WIDGET_CAN_FOCUS(clist) && GTK_WIDGET_HAS_FOCUS(clist) &&
6372       !GTK_CLIST_CHILD_HAS_FOCUS(clist))
6373     {
6374       if (GTK_CLIST_ADD_MODE(clist))
6375         {
6376           gint focus_row;
6377           
6378           focus_row = clist->focus_row;
6379           clist->focus_row = -1;
6380           draw_rows (clist, &area);
6381           clist->focus_row = focus_row;
6382           
6383           gdk_draw_rectangle (clist->clist_window, clist->xor_gc,
6384                               FALSE, 0, y, clist->clist_window_width - 1,
6385                               clist->row_height - 1);
6386           return;
6387         }
6388       else
6389         {
6390           gint x0;
6391           gint x1;
6392           
6393           if (area.x == 0)
6394             {
6395               x0 = clist->clist_window_width - 1;
6396               x1 = diff;
6397             }
6398           else
6399             {
6400               x0 = 0;
6401               x1 = area.x - 1;
6402             }
6403           
6404           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6405           gdk_draw_line (clist->clist_window, clist->xor_gc,
6406                          x0, y + 1, x0, y + clist->row_height - 2);
6407           gdk_draw_line (clist->clist_window, clist->xor_gc,
6408                          x1, y + 1, x1, y + clist->row_height - 2);
6409           
6410         }
6411     }
6412   draw_rows (clist, &area);
6413 }
6414
6415 static void
6416 check_exposures (GtkCList *clist)
6417 {
6418   GdkEvent *event;
6419
6420   if (!GTK_WIDGET_REALIZED (clist))
6421     return;
6422
6423   /* Make sure graphics expose events are processed before scrolling
6424    * again */
6425   while ((event = gdk_event_get_graphics_expose (clist->clist_window)) != NULL)
6426     {
6427       gtk_widget_event (GTK_WIDGET (clist), event);
6428       if (event->expose.count == 0)
6429         {
6430           gdk_event_free (event);
6431           break;
6432         }
6433       gdk_event_free (event);
6434     }
6435 }
6436
6437 /* PRIVATE 
6438  * Memory Allocation/Distruction Routines for GtkCList stuctures
6439  *
6440  * functions:
6441  *   columns_new
6442  *   column_title_new
6443  *   columns_delete
6444  *   row_new
6445  *   row_delete
6446  */
6447 static GtkCListColumn *
6448 columns_new (GtkCList *clist)
6449 {
6450   GtkCListColumn *column;
6451   gint i;
6452
6453   column = g_new (GtkCListColumn, clist->columns);
6454
6455   for (i = 0; i < clist->columns; i++)
6456     {
6457       column[i].area.x = 0;
6458       column[i].area.y = 0;
6459       column[i].area.width = 0;
6460       column[i].area.height = 0;
6461       column[i].title = NULL;
6462       column[i].button = NULL;
6463       column[i].window = NULL;
6464       column[i].width = 0;
6465       column[i].min_width = -1;
6466       column[i].max_width = -1;
6467       column[i].visible = TRUE;
6468       column[i].width_set = FALSE;
6469       column[i].resizeable = TRUE;
6470       column[i].auto_resize = FALSE;
6471       column[i].button_passive = FALSE;
6472       column[i].justification = GTK_JUSTIFY_LEFT;
6473     }
6474
6475   return column;
6476 }
6477
6478 static void
6479 column_title_new (GtkCList    *clist,
6480                   gint         column,
6481                   const gchar *title)
6482 {
6483   if (clist->column[column].title)
6484     g_free (clist->column[column].title);
6485
6486   clist->column[column].title = g_strdup (title);
6487 }
6488
6489 static void
6490 columns_delete (GtkCList *clist)
6491 {
6492   gint i;
6493
6494   for (i = 0; i < clist->columns; i++)
6495     if (clist->column[i].title)
6496       g_free (clist->column[i].title);
6497       
6498   g_free (clist->column);
6499 }
6500
6501 static GtkCListRow *
6502 row_new (GtkCList *clist)
6503 {
6504   int i;
6505   GtkCListRow *clist_row;
6506
6507   clist_row = g_chunk_new (GtkCListRow, clist->row_mem_chunk);
6508   clist_row->cell = g_chunk_new (GtkCell, clist->cell_mem_chunk);
6509
6510   for (i = 0; i < clist->columns; i++)
6511     {
6512       clist_row->cell[i].type = GTK_CELL_EMPTY;
6513       clist_row->cell[i].vertical = 0;
6514       clist_row->cell[i].horizontal = 0;
6515       clist_row->cell[i].style = NULL;
6516     }
6517
6518   clist_row->fg_set = FALSE;
6519   clist_row->bg_set = FALSE;
6520   clist_row->style = NULL;
6521   clist_row->selectable = TRUE;
6522   clist_row->state = GTK_STATE_NORMAL;
6523   clist_row->data = NULL;
6524   clist_row->destroy = NULL;
6525
6526   return clist_row;
6527 }
6528
6529 static void
6530 row_delete (GtkCList    *clist,
6531             GtkCListRow *clist_row)
6532 {
6533   gint i;
6534
6535   for (i = 0; i < clist->columns; i++)
6536     {
6537       GTK_CLIST_GET_CLASS (clist)->set_cell_contents
6538         (clist, clist_row, i, GTK_CELL_EMPTY, NULL, 0, NULL, NULL);
6539       if (clist_row->cell[i].style)
6540         {
6541           if (GTK_WIDGET_REALIZED (clist))
6542             gtk_style_detach (clist_row->cell[i].style);
6543           gtk_style_unref (clist_row->cell[i].style);
6544         }
6545     }
6546
6547   if (clist_row->style)
6548     {
6549       if (GTK_WIDGET_REALIZED (clist))
6550         gtk_style_detach (clist_row->style);
6551       gtk_style_unref (clist_row->style);
6552     }
6553
6554   if (clist_row->destroy)
6555     clist_row->destroy (clist_row->data);
6556
6557   g_mem_chunk_free (clist->cell_mem_chunk, clist_row->cell);
6558   g_mem_chunk_free (clist->row_mem_chunk, clist_row);
6559 }
6560
6561 /* FOCUS FUNCTIONS
6562  *   gtk_clist_focus
6563  *   gtk_clist_draw_focus
6564  *   gtk_clist_focus_in
6565  *   gtk_clist_focus_out
6566  *   gtk_clist_set_focus_child
6567  *   title_focus
6568  */
6569 static gint
6570 gtk_clist_focus (GtkContainer     *container,
6571                  GtkDirectionType  direction)
6572 {
6573   GtkCList *clist;
6574   GtkWidget *focus_child;
6575   gint old_row;
6576
6577   g_return_val_if_fail (container != NULL, FALSE);
6578   g_return_val_if_fail (GTK_IS_CLIST (container), FALSE);
6579
6580   if (!GTK_WIDGET_IS_SENSITIVE (container))
6581     return FALSE;
6582   
6583   clist = GTK_CLIST (container);
6584   focus_child = container->focus_child;
6585   old_row = clist->focus_row;
6586
6587   switch (direction)
6588     {
6589     case GTK_DIR_LEFT:
6590     case GTK_DIR_RIGHT:
6591       if (GTK_CLIST_CHILD_HAS_FOCUS(clist))
6592         {
6593           if (title_focus (clist, direction))
6594             return TRUE;
6595           gtk_container_set_focus_child (container, NULL);
6596           return FALSE;
6597          }
6598       gtk_widget_grab_focus (GTK_WIDGET (container));
6599       return TRUE;
6600     case GTK_DIR_DOWN:
6601     case GTK_DIR_TAB_FORWARD:
6602       if (GTK_CLIST_CHILD_HAS_FOCUS(clist))
6603         {
6604           gboolean tf = FALSE;
6605
6606           if (((focus_child && direction == GTK_DIR_DOWN) ||
6607                !(tf = title_focus (clist, GTK_DIR_TAB_FORWARD)))
6608               && clist->rows)
6609             {
6610               if (clist->focus_row < 0)
6611                 {
6612                   clist->focus_row = 0;
6613
6614                   if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6615                        clist->selection_mode == GTK_SELECTION_EXTENDED) &&
6616                       !clist->selection)
6617                     gtk_signal_emit (GTK_OBJECT (clist),
6618                                      clist_signals[SELECT_ROW],
6619                                      clist->focus_row, -1, NULL);
6620                 }
6621               gtk_widget_grab_focus (GTK_WIDGET (container));
6622               return TRUE;
6623             }
6624
6625           if (tf)
6626             return TRUE;
6627         }
6628       
6629       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
6630       break;
6631     case GTK_DIR_UP:
6632     case GTK_DIR_TAB_BACKWARD:
6633       if (!focus_child &&
6634           GTK_CLIST_CHILD_HAS_FOCUS(clist) && clist->rows)
6635         {
6636           if (clist->focus_row < 0)
6637             {
6638               clist->focus_row = 0;
6639               if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6640                    clist->selection_mode == GTK_SELECTION_EXTENDED) &&
6641                   !clist->selection)
6642                 gtk_signal_emit (GTK_OBJECT (clist),
6643                                  clist_signals[SELECT_ROW],
6644                                  clist->focus_row, -1, NULL);
6645             }
6646           gtk_widget_grab_focus (GTK_WIDGET (container));
6647           return TRUE;
6648         }
6649
6650       GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
6651
6652       if (title_focus (clist, direction))
6653         return TRUE;
6654
6655       break;
6656     default:
6657       break;
6658     }
6659
6660   gtk_container_set_focus_child (container, NULL);
6661   return FALSE;
6662 }
6663
6664 static void
6665 gtk_clist_draw_focus (GtkWidget *widget)
6666 {
6667   GtkCList *clist;
6668
6669   g_return_if_fail (widget != NULL);
6670   g_return_if_fail (GTK_IS_CLIST (widget));
6671
6672   if (!GTK_WIDGET_DRAWABLE (widget) || !GTK_WIDGET_CAN_FOCUS (widget))
6673     return;
6674
6675   clist = GTK_CLIST (widget);
6676   if (clist->focus_row >= 0)
6677     gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
6678                         0, ROW_TOP_YPIXEL(clist, clist->focus_row),
6679                         clist->clist_window_width - 1,
6680                         clist->row_height - 1);
6681 }
6682
6683 static gint
6684 gtk_clist_focus_in (GtkWidget     *widget,
6685                     GdkEventFocus *event)
6686 {
6687   GtkCList *clist;
6688
6689   g_return_val_if_fail (widget != NULL, FALSE);
6690   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
6691   g_return_val_if_fail (event != NULL, FALSE);
6692
6693   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
6694   GTK_CLIST_UNSET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
6695
6696   clist = GTK_CLIST (widget);
6697
6698   if (clist->selection_mode == GTK_SELECTION_BROWSE &&
6699       clist->selection == NULL && clist->focus_row > -1)
6700     {
6701       GList *list;
6702
6703       list = g_list_nth (clist->row_list, clist->focus_row);
6704       if (list && GTK_CLIST_ROW (list)->selectable)
6705         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
6706                          clist->focus_row, -1, event);
6707       else
6708         gtk_widget_draw_focus (widget);
6709     }
6710   else
6711     gtk_widget_draw_focus (widget);
6712
6713   return FALSE;
6714 }
6715
6716 static gint
6717 gtk_clist_focus_out (GtkWidget     *widget,
6718                      GdkEventFocus *event)
6719 {
6720   GtkCList *clist;
6721
6722   g_return_val_if_fail (widget != NULL, FALSE);
6723   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
6724   g_return_val_if_fail (event != NULL, FALSE);
6725
6726   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
6727   GTK_CLIST_SET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
6728
6729   gtk_widget_draw_focus (widget);
6730   
6731   clist = GTK_CLIST (widget);
6732
6733   GTK_CLIST_GET_CLASS (widget)->resync_selection (clist, (GdkEvent *) event);
6734
6735   return FALSE;
6736 }
6737
6738 static void
6739 gtk_clist_set_focus_child (GtkContainer *container,
6740                            GtkWidget    *child)
6741 {
6742   g_return_if_fail (container != NULL);
6743   g_return_if_fail (GTK_IS_CLIST (container));
6744
6745   if (child)
6746     {
6747       g_return_if_fail (GTK_IS_WIDGET (child));
6748       GTK_CLIST_SET_FLAG (container, CLIST_CHILD_HAS_FOCUS);
6749     }
6750
6751   parent_class->set_focus_child (container, child);
6752 }
6753
6754 static gboolean
6755 title_focus (GtkCList *clist,
6756              gint      dir)
6757 {
6758   GtkWidget *focus_child;
6759   gboolean return_val = FALSE;
6760   gint last_column;
6761   gint d = 1;
6762   gint i = 0;
6763   gint j;
6764
6765   if (!GTK_CLIST_SHOW_TITLES(clist))
6766     return FALSE;
6767
6768   focus_child = GTK_CONTAINER (clist)->focus_child;
6769
6770   for (last_column = clist->columns - 1;
6771        last_column >= 0 && !clist->column[last_column].visible; last_column--)
6772     ;
6773   
6774   switch (dir)
6775     {
6776     case GTK_DIR_TAB_BACKWARD:
6777     case GTK_DIR_UP:
6778       if (!focus_child || !GTK_CLIST_CHILD_HAS_FOCUS(clist))
6779         {
6780           if (dir == GTK_DIR_UP)
6781             i = COLUMN_FROM_XPIXEL (clist, 0);
6782           else
6783             i = last_column;
6784           focus_child = clist->column[i].button;
6785           dir = GTK_DIR_TAB_FORWARD;
6786         }
6787       else
6788         d = -1;
6789       break;
6790     case GTK_DIR_LEFT:
6791       d = -1;
6792       if (!focus_child)
6793         {
6794           i = last_column;
6795           focus_child = clist->column[i].button;
6796         }
6797       break;
6798     case GTK_DIR_RIGHT:
6799       if (!focus_child)
6800         {
6801           i = 0;
6802           focus_child = clist->column[i].button;
6803         }
6804       break;
6805     }
6806
6807   if (focus_child)
6808     while (i < clist->columns)
6809       {
6810         if (clist->column[i].button == focus_child)
6811           {
6812             if (clist->column[i].button && 
6813                 GTK_WIDGET_VISIBLE (clist->column[i].button) &&
6814                 GTK_IS_CONTAINER (clist->column[i].button) &&
6815                 !GTK_WIDGET_HAS_FOCUS(clist->column[i].button))
6816               if (gtk_container_focus 
6817                   (GTK_CONTAINER (clist->column[i].button), dir))
6818                 {
6819                   return_val = TRUE;
6820                   i -= d;
6821                 }
6822             if (!return_val && dir == GTK_DIR_UP)
6823               return FALSE;
6824             i += d;
6825             break;
6826           }
6827         i++;
6828       }
6829
6830   j = i;
6831
6832   if (!return_val)
6833     while (j >= 0 && j < clist->columns)
6834       {
6835         if (clist->column[j].button &&
6836             GTK_WIDGET_VISIBLE (clist->column[j].button))
6837           {
6838             if (GTK_IS_CONTAINER (clist->column[j].button) &&
6839                 gtk_container_focus 
6840                 (GTK_CONTAINER (clist->column[j].button), dir))
6841               {
6842                 return_val = TRUE;
6843                 break;
6844               }
6845             else if (GTK_WIDGET_CAN_FOCUS (clist->column[j].button))
6846               {
6847                 gtk_widget_grab_focus (clist->column[j].button);
6848                 return_val = TRUE;
6849                 break;
6850               }
6851           }
6852         j += d;
6853       }
6854   
6855   if (return_val)
6856     {
6857       if (COLUMN_LEFT_XPIXEL (clist, j) < CELL_SPACING + COLUMN_INSET)
6858         gtk_clist_moveto (clist, -1, j, 0, 0);
6859       else if (COLUMN_LEFT_XPIXEL(clist, j) + clist->column[j].area.width >
6860                clist->clist_window_width)
6861         {
6862           if (j == last_column)
6863             gtk_clist_moveto (clist, -1, j, 0, 0);
6864           else
6865             gtk_clist_moveto (clist, -1, j, 0, 1);
6866         }
6867     }
6868   return return_val;
6869 }
6870
6871 /* PRIVATE SCROLLING FUNCTIONS
6872  *   move_focus_row
6873  *   scroll_horizontal
6874  *   scroll_vertical
6875  *   move_horizontal
6876  *   move_vertical
6877  *   horizontal_timeout
6878  *   vertical_timeout
6879  *   remove_grab
6880  */
6881 static void
6882 move_focus_row (GtkCList      *clist,
6883                 GtkScrollType  scroll_type,
6884                 gfloat         position)
6885 {
6886   GtkWidget *widget;
6887
6888   g_return_if_fail (clist != 0);
6889   g_return_if_fail (GTK_IS_CLIST (clist));
6890
6891   widget = GTK_WIDGET (clist);
6892
6893   switch (scroll_type)
6894     {
6895     case GTK_SCROLL_STEP_BACKWARD:
6896       if (clist->focus_row <= 0)
6897         return;
6898       gtk_clist_draw_focus (widget);
6899       clist->focus_row--;
6900       gtk_clist_draw_focus (widget);
6901       break;
6902     case GTK_SCROLL_STEP_FORWARD:
6903       if (clist->focus_row >= clist->rows - 1)
6904         return;
6905       gtk_clist_draw_focus (widget);
6906       clist->focus_row++;
6907       gtk_clist_draw_focus (widget);
6908       break;
6909     case GTK_SCROLL_PAGE_BACKWARD:
6910       if (clist->focus_row <= 0)
6911         return;
6912       gtk_clist_draw_focus (widget);
6913       clist->focus_row = MAX (0, clist->focus_row -
6914                               (2 * clist->clist_window_height -
6915                                clist->row_height - CELL_SPACING) / 
6916                               (2 * (clist->row_height + CELL_SPACING)));
6917       gtk_clist_draw_focus (widget);
6918       break;
6919     case GTK_SCROLL_PAGE_FORWARD:
6920       if (clist->focus_row >= clist->rows - 1)
6921         return;
6922       gtk_clist_draw_focus (widget);
6923       clist->focus_row = MIN (clist->rows - 1, clist->focus_row + 
6924                               (2 * clist->clist_window_height -
6925                                clist->row_height - CELL_SPACING) / 
6926                               (2 * (clist->row_height + CELL_SPACING)));
6927       gtk_clist_draw_focus (widget);
6928       break;
6929     case GTK_SCROLL_JUMP:
6930       if (position >= 0 && position <= 1)
6931         {
6932           gtk_clist_draw_focus (widget);
6933           clist->focus_row = position * (clist->rows - 1);
6934           gtk_clist_draw_focus (widget);
6935         }
6936       break;
6937     default:
6938       break;
6939     }
6940 }
6941
6942 static void
6943 scroll_horizontal (GtkCList      *clist,
6944                    GtkScrollType  scroll_type,
6945                    gfloat         position)
6946 {
6947   gint column = 0;
6948   gint last_column;
6949
6950   g_return_if_fail (clist != 0);
6951   g_return_if_fail (GTK_IS_CLIST (clist));
6952
6953   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
6954     return;
6955
6956   for (last_column = clist->columns - 1;
6957        last_column >= 0 && !clist->column[last_column].visible; last_column--)
6958     ;
6959
6960   switch (scroll_type)
6961     {
6962     case GTK_SCROLL_STEP_BACKWARD:
6963       column = COLUMN_FROM_XPIXEL (clist, 0);
6964       if (COLUMN_LEFT_XPIXEL (clist, column) - CELL_SPACING - COLUMN_INSET >= 0
6965           && column > 0)
6966         column--;
6967       break;
6968     case GTK_SCROLL_STEP_FORWARD:
6969       column = COLUMN_FROM_XPIXEL (clist, clist->clist_window_width);
6970       if (column < 0)
6971         return;
6972       if (COLUMN_LEFT_XPIXEL (clist, column) +
6973           clist->column[column].area.width +
6974           CELL_SPACING + COLUMN_INSET - 1 <= clist->clist_window_width &&
6975           column < last_column)
6976         column++;
6977       break;
6978     case GTK_SCROLL_PAGE_BACKWARD:
6979     case GTK_SCROLL_PAGE_FORWARD:
6980       return;
6981     case GTK_SCROLL_JUMP:
6982       if (position >= 0 && position <= 1)
6983         {
6984           gint vis_columns = 0;
6985           gint i;
6986
6987           for (i = 0; i <= last_column; i++)
6988             if (clist->column[i].visible)
6989               vis_columns++;
6990
6991           column = position * vis_columns;
6992
6993           for (i = 0; i <= last_column && column > 0; i++)
6994             if (clist->column[i].visible)
6995               column--;
6996
6997           column = i;
6998         }
6999       else
7000         return;
7001       break;
7002     default:
7003       break;
7004     }
7005
7006   if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET)
7007     gtk_clist_moveto (clist, -1, column, 0, 0);
7008   else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1
7009            + clist->column[column].area.width > clist->clist_window_width)
7010     {
7011       if (column == last_column)
7012         gtk_clist_moveto (clist, -1, column, 0, 0);
7013       else
7014         gtk_clist_moveto (clist, -1, column, 0, 1);
7015     }
7016 }
7017
7018 static void
7019 scroll_vertical (GtkCList      *clist,
7020                  GtkScrollType  scroll_type,
7021                  gfloat         position)
7022 {
7023   gint old_focus_row;
7024
7025   g_return_if_fail (clist != NULL);
7026   g_return_if_fail (GTK_IS_CLIST (clist));
7027
7028   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
7029     return;
7030
7031   switch (clist->selection_mode)
7032     {
7033     case GTK_SELECTION_EXTENDED:
7034       if (clist->anchor >= 0)
7035         return;
7036     case GTK_SELECTION_BROWSE:
7037
7038       old_focus_row = clist->focus_row;
7039       move_focus_row (clist, scroll_type, position);
7040
7041       if (old_focus_row != clist->focus_row)
7042         {
7043           if (clist->selection_mode == GTK_SELECTION_BROWSE)
7044             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
7045                              old_focus_row, -1, NULL);
7046           else if (!GTK_CLIST_ADD_MODE(clist))
7047             {
7048               gtk_clist_unselect_all (clist);
7049               clist->undo_anchor = old_focus_row;
7050             }
7051         }
7052
7053       switch (gtk_clist_row_is_visible (clist, clist->focus_row))
7054         {
7055         case GTK_VISIBILITY_NONE:
7056           if (old_focus_row != clist->focus_row &&
7057               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
7058                 GTK_CLIST_ADD_MODE(clist)))
7059             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
7060                              clist->focus_row, -1, NULL);
7061           switch (scroll_type)
7062             {
7063             case GTK_SCROLL_STEP_BACKWARD:
7064             case GTK_SCROLL_PAGE_BACKWARD:
7065               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
7066               break;
7067             case GTK_SCROLL_STEP_FORWARD:
7068             case GTK_SCROLL_PAGE_FORWARD:
7069               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
7070               break;
7071             case GTK_SCROLL_JUMP:
7072               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
7073               break;
7074             default:
7075               break;
7076             }
7077           break;
7078         case GTK_VISIBILITY_PARTIAL:
7079           switch (scroll_type)
7080             {
7081             case GTK_SCROLL_STEP_BACKWARD:
7082             case GTK_SCROLL_PAGE_BACKWARD:
7083               gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
7084               break;
7085             case GTK_SCROLL_STEP_FORWARD:
7086             case GTK_SCROLL_PAGE_FORWARD:
7087               gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
7088               break;
7089             case GTK_SCROLL_JUMP:
7090               gtk_clist_moveto (clist, clist->focus_row, -1, 0.5, 0);
7091               break;
7092             default:
7093               break;
7094             }
7095         default:
7096           if (old_focus_row != clist->focus_row &&
7097               !(clist->selection_mode == GTK_SELECTION_EXTENDED &&
7098                 GTK_CLIST_ADD_MODE(clist)))
7099             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
7100                              clist->focus_row, -1, NULL);
7101           break;
7102         }
7103       break;
7104     default:
7105       move_focus_row (clist, scroll_type, position);
7106
7107       if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
7108           clist->clist_window_height)
7109         gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
7110       else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
7111         gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
7112       break;
7113     }
7114 }
7115
7116 static void
7117 move_horizontal (GtkCList *clist,
7118                  gint      diff)
7119 {
7120   gfloat value;
7121
7122   if (!clist->hadjustment)
7123     return;
7124
7125   value = CLAMP (clist->hadjustment->value + diff, 0.0,
7126                  clist->hadjustment->upper - clist->hadjustment->page_size);
7127   gtk_adjustment_set_value(clist->hadjustment, value);
7128 }
7129
7130 static void
7131 move_vertical (GtkCList *clist,
7132                gint      row,
7133                gfloat    align)
7134 {
7135   gfloat value;
7136
7137   if (!clist->vadjustment)
7138     return;
7139
7140   value = (ROW_TOP_YPIXEL (clist, row) - clist->voffset -
7141            align * (clist->clist_window_height - clist->row_height) +
7142            (2 * align - 1) * CELL_SPACING);
7143
7144   if (value + clist->vadjustment->page_size > clist->vadjustment->upper)
7145     value = clist->vadjustment->upper - clist->vadjustment->page_size;
7146
7147   gtk_adjustment_set_value(clist->vadjustment, value);
7148 }
7149
7150 static gint
7151 horizontal_timeout (GtkCList *clist)
7152 {
7153   GdkEventMotion event = { 0 };
7154
7155   GDK_THREADS_ENTER ();
7156
7157   clist->htimer = 0;
7158
7159   event.type = GDK_MOTION_NOTIFY;
7160   event.send_event = TRUE;
7161
7162   gtk_clist_motion (GTK_WIDGET (clist), &event);
7163
7164   GDK_THREADS_LEAVE ();
7165   
7166   return FALSE;
7167 }
7168
7169 static gint
7170 vertical_timeout (GtkCList *clist)
7171 {
7172   GdkEventMotion event = { 0 };
7173
7174   GDK_THREADS_ENTER ();
7175
7176   clist->vtimer = 0;
7177
7178   event.type = GDK_MOTION_NOTIFY;
7179   event.send_event = TRUE;
7180
7181   gtk_clist_motion (GTK_WIDGET (clist), &event);
7182
7183   GDK_THREADS_LEAVE ();
7184
7185   return FALSE;
7186 }
7187
7188 static void
7189 remove_grab (GtkCList *clist)
7190 {
7191   if (GTK_WIDGET_HAS_GRAB (clist))
7192     {
7193       gtk_grab_remove (GTK_WIDGET (clist));
7194       if (gdk_pointer_is_grabbed ())
7195         gdk_pointer_ungrab (GDK_CURRENT_TIME);
7196     }
7197
7198   if (clist->htimer)
7199     {
7200       gtk_timeout_remove (clist->htimer);
7201       clist->htimer = 0;
7202     }
7203
7204   if (clist->vtimer)
7205     {
7206       gtk_timeout_remove (clist->vtimer);
7207       clist->vtimer = 0;
7208     }
7209 }
7210
7211 /* PUBLIC SORTING FUNCTIONS
7212  * gtk_clist_sort
7213  * gtk_clist_set_compare_func
7214  * gtk_clist_set_auto_sort
7215  * gtk_clist_set_sort_type
7216  * gtk_clist_set_sort_column
7217  */
7218 void
7219 gtk_clist_sort (GtkCList *clist)
7220 {
7221   g_return_if_fail (clist != NULL);
7222   g_return_if_fail (GTK_IS_CLIST (clist));
7223
7224   GTK_CLIST_GET_CLASS (clist)->sort_list (clist);
7225 }
7226
7227 void
7228 gtk_clist_set_compare_func (GtkCList            *clist,
7229                             GtkCListCompareFunc  cmp_func)
7230 {
7231   g_return_if_fail (clist != NULL);
7232   g_return_if_fail (GTK_IS_CLIST (clist));
7233
7234   clist->compare = (cmp_func) ? cmp_func : default_compare;
7235 }
7236
7237 void       
7238 gtk_clist_set_auto_sort (GtkCList *clist,
7239                          gboolean  auto_sort)
7240 {
7241   g_return_if_fail (clist != NULL);
7242   g_return_if_fail (GTK_IS_CLIST (clist));
7243   
7244   if (GTK_CLIST_AUTO_SORT(clist) && !auto_sort)
7245     GTK_CLIST_UNSET_FLAG (clist, CLIST_AUTO_SORT);
7246   else if (!GTK_CLIST_AUTO_SORT(clist) && auto_sort)
7247     {
7248       GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_SORT);
7249       gtk_clist_sort (clist);
7250     }
7251 }
7252
7253 void       
7254 gtk_clist_set_sort_type (GtkCList    *clist,
7255                          GtkSortType  sort_type)
7256 {
7257   g_return_if_fail (clist != NULL);
7258   g_return_if_fail (GTK_IS_CLIST (clist));
7259   
7260   clist->sort_type = sort_type;
7261 }
7262
7263 void
7264 gtk_clist_set_sort_column (GtkCList *clist,
7265                            gint      column)
7266 {
7267   g_return_if_fail (clist != NULL);
7268   g_return_if_fail (GTK_IS_CLIST (clist));
7269
7270   if (column < 0 || column >= clist->columns)
7271     return;
7272
7273   clist->sort_column = column;
7274 }
7275
7276 /* PRIVATE SORTING FUNCTIONS
7277  *   default_compare
7278  *   real_sort_list
7279  *   gtk_clist_merge
7280  *   gtk_clist_mergesort
7281  */
7282 static gint
7283 default_compare (GtkCList      *clist,
7284                  gconstpointer  ptr1,
7285                  gconstpointer  ptr2)
7286 {
7287   char *text1 = NULL;
7288   char *text2 = NULL;
7289
7290   GtkCListRow *row1 = (GtkCListRow *) ptr1;
7291   GtkCListRow *row2 = (GtkCListRow *) ptr2;
7292
7293   switch (row1->cell[clist->sort_column].type)
7294     {
7295     case GTK_CELL_TEXT:
7296       text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
7297       break;
7298     case GTK_CELL_PIXTEXT:
7299       text1 = GTK_CELL_PIXTEXT (row1->cell[clist->sort_column])->text;
7300       break;
7301     default:
7302       break;
7303     }
7304  
7305   switch (row2->cell[clist->sort_column].type)
7306     {
7307     case GTK_CELL_TEXT:
7308       text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
7309       break;
7310     case GTK_CELL_PIXTEXT:
7311       text2 = GTK_CELL_PIXTEXT (row2->cell[clist->sort_column])->text;
7312       break;
7313     default:
7314       break;
7315     }
7316
7317   if (!text2)
7318     return (text1 != NULL);
7319
7320   if (!text1)
7321     return -1;
7322
7323   return strcmp (text1, text2);
7324 }
7325
7326 static void
7327 real_sort_list (GtkCList *clist)
7328 {
7329   GList *list;
7330   GList *work;
7331   gint i;
7332
7333   g_return_if_fail (clist != NULL);
7334   g_return_if_fail (GTK_IS_CLIST (clist));
7335
7336   if (clist->rows <= 1)
7337     return;
7338
7339   if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
7340     return;
7341
7342   gtk_clist_freeze (clist);
7343
7344   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_EXTENDED)
7345     {
7346       GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7347       g_list_free (clist->undo_selection);
7348       g_list_free (clist->undo_unselection);
7349       clist->undo_selection = NULL;
7350       clist->undo_unselection = NULL;
7351     }
7352    
7353   clist->row_list = gtk_clist_mergesort (clist, clist->row_list, clist->rows);
7354
7355   work = clist->selection;
7356
7357   for (i = 0, list = clist->row_list; i < clist->rows; i++, list = list->next)
7358     {
7359       if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
7360         {
7361           work->data = GINT_TO_POINTER (i);
7362           work = work->next;
7363         }
7364       
7365       if (i == clist->rows - 1)
7366         clist->row_list_end = list;
7367     }
7368
7369   gtk_clist_thaw (clist);
7370 }
7371
7372 static GList *
7373 gtk_clist_merge (GtkCList *clist,
7374                  GList    *a,         /* first list to merge */
7375                  GList    *b)         /* second list to merge */
7376 {
7377   GList z = { 0 };                    /* auxiliary node */
7378   GList *c;
7379   gint cmp;
7380
7381   c = &z;
7382
7383   while (a || b)
7384     {
7385       if (a && !b)
7386         {
7387           c->next = a;
7388           a->prev = c;
7389           c = a;
7390           a = a->next;
7391           break;
7392         }
7393       else if (!a && b)
7394         {
7395           c->next = b;
7396           b->prev = c;
7397           c = b;
7398           b = b->next;
7399           break;
7400         }
7401       else /* a && b */
7402         {
7403           cmp = clist->compare (clist, GTK_CLIST_ROW (a), GTK_CLIST_ROW (b));
7404           if ((cmp >= 0 && clist->sort_type == GTK_SORT_DESCENDING) ||
7405               (cmp <= 0 && clist->sort_type == GTK_SORT_ASCENDING) ||
7406               (a && !b))
7407             {
7408               c->next = a;
7409               a->prev = c;
7410               c = a;
7411               a = a->next;
7412             }
7413           else
7414             {
7415               c->next = b;
7416               b->prev = c;
7417               c = b;
7418               b = b->next;
7419             }
7420         }
7421     }
7422
7423   return z.next;
7424 }
7425
7426 static GList *
7427 gtk_clist_mergesort (GtkCList *clist,
7428                      GList    *list,         /* the list to sort */
7429                      gint      num)          /* the list's length */
7430 {
7431   GList *half;
7432   gint i;
7433
7434   if (num == 1)
7435     {
7436       return list;
7437     }
7438   else
7439     {
7440       /* move "half" to the middle */
7441       half = list;
7442       for (i = 0; i < num / 2; i++)
7443         half = half->next;
7444
7445       /* cut the list in two */
7446       half->prev->next = NULL;
7447       half->prev = NULL;
7448
7449       /* recursively sort both lists */
7450       return gtk_clist_merge (clist,
7451                        gtk_clist_mergesort (clist, list, num / 2),
7452                        gtk_clist_mergesort (clist, half, num - num / 2));
7453     }
7454 }
7455
7456 /************************/
7457
7458 static void
7459 drag_source_info_destroy (gpointer data)
7460 {
7461   GtkCListCellInfo *info = data;
7462
7463   g_free (info);
7464 }
7465
7466 static void
7467 drag_dest_info_destroy (gpointer data)
7468 {
7469   GtkCListDestInfo *info = data;
7470
7471   g_free (info);
7472 }
7473
7474 static void
7475 drag_dest_cell (GtkCList         *clist,
7476                 gint              x,
7477                 gint              y,
7478                 GtkCListDestInfo *dest_info)
7479 {
7480   GtkWidget *widget;
7481
7482   widget = GTK_WIDGET (clist);
7483
7484   dest_info->insert_pos = GTK_CLIST_DRAG_NONE;
7485
7486   y -= (GTK_CONTAINER (clist)->border_width +
7487         widget->style->klass->ythickness +
7488         clist->column_title_area.height);
7489
7490   dest_info->cell.row = ROW_FROM_YPIXEL (clist, y);
7491   if (dest_info->cell.row >= clist->rows)
7492     {
7493       dest_info->cell.row = clist->rows - 1;
7494       y = ROW_TOP_YPIXEL (clist, dest_info->cell.row) + clist->row_height;
7495     }
7496   if (dest_info->cell.row < -1)
7497     dest_info->cell.row = -1;
7498
7499   x -= GTK_CONTAINER (widget)->border_width + widget->style->klass->xthickness;
7500   dest_info->cell.column = COLUMN_FROM_XPIXEL (clist, x);
7501
7502   if (dest_info->cell.row >= 0)
7503     {
7504       gint y_delta;
7505       gint h = 0;
7506
7507       y_delta = y - ROW_TOP_YPIXEL (clist, dest_info->cell.row);
7508       
7509       if (GTK_CLIST_DRAW_DRAG_RECT(clist))
7510         {
7511           dest_info->insert_pos = GTK_CLIST_DRAG_INTO;
7512           h = clist->row_height / 4;
7513         }
7514       else if (GTK_CLIST_DRAW_DRAG_LINE(clist))
7515         {
7516           dest_info->insert_pos = GTK_CLIST_DRAG_BEFORE;
7517           h = clist->row_height / 2;
7518         }
7519
7520       if (GTK_CLIST_DRAW_DRAG_LINE(clist))
7521         {
7522           if (y_delta < h)
7523             dest_info->insert_pos = GTK_CLIST_DRAG_BEFORE;
7524           else if (clist->row_height - y_delta < h)
7525             dest_info->insert_pos = GTK_CLIST_DRAG_AFTER;
7526         }
7527     }
7528 }
7529
7530 static void
7531 gtk_clist_drag_begin (GtkWidget      *widget,
7532                       GdkDragContext *context)
7533 {
7534   GtkCList *clist;
7535   GtkCListCellInfo *info;
7536
7537   g_return_if_fail (widget != NULL);
7538   g_return_if_fail (GTK_IS_CLIST (widget));
7539   g_return_if_fail (context != NULL);
7540
7541   clist = GTK_CLIST (widget);
7542
7543   clist->drag_button = 0;
7544   remove_grab (clist);
7545
7546   switch (clist->selection_mode)
7547     {
7548     case GTK_SELECTION_EXTENDED:
7549       update_extended_selection (clist, clist->focus_row);
7550       GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7551       break;
7552     case GTK_SELECTION_SINGLE:
7553     case GTK_SELECTION_MULTIPLE:
7554       clist->anchor = -1;
7555     case GTK_SELECTION_BROWSE:
7556       break;
7557     }
7558
7559   info = g_dataset_get_data (context, "gtk-clist-drag-source");
7560
7561   if (!info)
7562     {
7563       info = g_new (GtkCListCellInfo, 1);
7564
7565       if (clist->click_cell.row < 0)
7566         clist->click_cell.row = 0;
7567       else if (clist->click_cell.row >= clist->rows)
7568         clist->click_cell.row = clist->rows - 1;
7569       info->row = clist->click_cell.row;
7570       info->column = clist->click_cell.column;
7571
7572       g_dataset_set_data_full (context, "gtk-clist-drag-source", info,
7573                                drag_source_info_destroy);
7574     }
7575
7576   if (GTK_CLIST_USE_DRAG_ICONS (clist))
7577     gtk_drag_set_icon_default (context);
7578 }
7579
7580 static void
7581 gtk_clist_drag_end (GtkWidget      *widget,
7582                     GdkDragContext *context)
7583 {
7584   GtkCList *clist;
7585
7586   g_return_if_fail (widget != NULL);
7587   g_return_if_fail (GTK_IS_CLIST (widget));
7588   g_return_if_fail (context != NULL);
7589
7590   clist = GTK_CLIST (widget);
7591
7592   clist->click_cell.row = -1;
7593   clist->click_cell.column = -1;
7594 }
7595
7596 static void
7597 gtk_clist_drag_leave (GtkWidget      *widget,
7598                       GdkDragContext *context,
7599                       guint           time)
7600 {
7601   GtkCList *clist;
7602   GtkCListDestInfo *dest_info;
7603
7604   g_return_if_fail (widget != NULL);
7605   g_return_if_fail (GTK_IS_CLIST (widget));
7606   g_return_if_fail (context != NULL);
7607
7608   clist = GTK_CLIST (widget);
7609
7610   dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7611   
7612   if (dest_info)
7613     {
7614       if (dest_info->cell.row >= 0 &&
7615           GTK_CLIST_REORDERABLE(clist) &&
7616           gtk_drag_get_source_widget (context) == widget)
7617         {
7618           GList *list;
7619           GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
7620
7621           list = context->targets;
7622           while (list)
7623             {
7624               if (atom == GPOINTER_TO_INT (list->data))
7625                 {
7626                   GTK_CLIST_GET_CLASS (clist)->draw_drag_highlight
7627                     (clist,
7628                      g_list_nth (clist->row_list, dest_info->cell.row)->data,
7629                      dest_info->cell.row, dest_info->insert_pos);
7630                   break;
7631                 }
7632               list = list->next;
7633             }
7634         }
7635       g_dataset_remove_data (context, "gtk-clist-drag-dest");
7636     }
7637 }
7638
7639 static gint
7640 gtk_clist_drag_motion (GtkWidget      *widget,
7641                        GdkDragContext *context,
7642                        gint            x,
7643                        gint            y,
7644                        guint           time)
7645 {
7646   GtkCList *clist;
7647   GtkCListDestInfo new_info;
7648   GtkCListDestInfo *dest_info;
7649
7650   g_return_val_if_fail (widget != NULL, FALSE);
7651   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
7652
7653   clist = GTK_CLIST (widget);
7654
7655   dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7656
7657   if (!dest_info)
7658     {
7659       dest_info = g_new (GtkCListDestInfo, 1);
7660
7661       dest_info->insert_pos  = GTK_CLIST_DRAG_NONE;
7662       dest_info->cell.row    = -1;
7663       dest_info->cell.column = -1;
7664
7665       g_dataset_set_data_full (context, "gtk-clist-drag-dest", dest_info,
7666                                drag_dest_info_destroy);
7667     }
7668
7669   drag_dest_cell (clist, x, y, &new_info);
7670
7671   if (GTK_CLIST_REORDERABLE (clist))
7672     {
7673       GList *list;
7674       GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
7675
7676       list = context->targets;
7677       while (list)
7678         {
7679           if (atom == GPOINTER_TO_INT (list->data))
7680             break;
7681           list = list->next;
7682         }
7683
7684       if (list)
7685         {
7686           if (gtk_drag_get_source_widget (context) != widget ||
7687               new_info.insert_pos == GTK_CLIST_DRAG_NONE ||
7688               new_info.cell.row == clist->click_cell.row ||
7689               (new_info.cell.row == clist->click_cell.row - 1 &&
7690                new_info.insert_pos == GTK_CLIST_DRAG_AFTER) ||
7691               (new_info.cell.row == clist->click_cell.row + 1 &&
7692                new_info.insert_pos == GTK_CLIST_DRAG_BEFORE))
7693             {
7694               if (dest_info->cell.row < 0)
7695                 {
7696                   gdk_drag_status (context, GDK_ACTION_DEFAULT, time);
7697                   return FALSE;
7698                 }
7699               return TRUE;
7700             }
7701                 
7702           if (new_info.cell.row != dest_info->cell.row ||
7703               (new_info.cell.row == dest_info->cell.row &&
7704                dest_info->insert_pos != new_info.insert_pos))
7705             {
7706               if (dest_info->cell.row >= 0)
7707                 GTK_CLIST_GET_CLASS (clist)->draw_drag_highlight
7708                   (clist, g_list_nth (clist->row_list,
7709                                       dest_info->cell.row)->data,
7710                    dest_info->cell.row, dest_info->insert_pos);
7711
7712               dest_info->insert_pos  = new_info.insert_pos;
7713               dest_info->cell.row    = new_info.cell.row;
7714               dest_info->cell.column = new_info.cell.column;
7715               
7716               GTK_CLIST_GET_CLASS (clist)->draw_drag_highlight
7717                 (clist, g_list_nth (clist->row_list,
7718                                     dest_info->cell.row)->data,
7719                  dest_info->cell.row, dest_info->insert_pos);
7720
7721               gdk_drag_status (context, context->suggested_action, time);
7722             }
7723           return TRUE;
7724         }
7725     }
7726
7727   dest_info->insert_pos  = new_info.insert_pos;
7728   dest_info->cell.row    = new_info.cell.row;
7729   dest_info->cell.column = new_info.cell.column;
7730   return TRUE;
7731 }
7732
7733 static gboolean
7734 gtk_clist_drag_drop (GtkWidget      *widget,
7735                      GdkDragContext *context,
7736                      gint            x,
7737                      gint            y,
7738                      guint           time)
7739 {
7740   g_return_val_if_fail (widget != NULL, FALSE);
7741   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
7742   g_return_val_if_fail (context != NULL, FALSE);
7743
7744   if (GTK_CLIST_REORDERABLE (widget) &&
7745       gtk_drag_get_source_widget (context) == widget)
7746     {
7747       GList *list;
7748       GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
7749
7750       list = context->targets;
7751       while (list)
7752         {
7753           if (atom == GPOINTER_TO_INT (list->data))
7754             return TRUE;
7755           list = list->next;
7756         }
7757     }
7758   return FALSE;
7759 }
7760
7761 static void
7762 gtk_clist_drag_data_received (GtkWidget        *widget,
7763                               GdkDragContext   *context,
7764                               gint              x,
7765                               gint              y,
7766                               GtkSelectionData *selection_data,
7767                               guint             info,
7768                               guint             time)
7769 {
7770   GtkCList *clist;
7771
7772   g_return_if_fail (widget != NULL);
7773   g_return_if_fail (GTK_IS_CLIST (widget));
7774   g_return_if_fail (context != NULL);
7775   g_return_if_fail (selection_data != NULL);
7776
7777   clist = GTK_CLIST (widget);
7778
7779   if (GTK_CLIST_REORDERABLE (clist) &&
7780       gtk_drag_get_source_widget (context) == widget &&
7781       selection_data->target ==
7782       gdk_atom_intern ("gtk-clist-drag-reorder", FALSE) &&
7783       selection_data->format == GTK_TYPE_POINTER &&
7784       selection_data->length == sizeof (GtkCListCellInfo))
7785     {
7786       GtkCListCellInfo *source_info;
7787
7788       source_info = (GtkCListCellInfo *)(selection_data->data);
7789       if (source_info)
7790         {
7791           GtkCListDestInfo dest_info;
7792
7793           drag_dest_cell (clist, x, y, &dest_info);
7794
7795           if (dest_info.insert_pos == GTK_CLIST_DRAG_AFTER)
7796             dest_info.cell.row++;
7797           if (source_info->row < dest_info.cell.row)
7798             dest_info.cell.row--;
7799           if (dest_info.cell.row != source_info->row)
7800             gtk_clist_row_move (clist, source_info->row, dest_info.cell.row);
7801
7802           g_dataset_remove_data (context, "gtk-clist-drag-dest");
7803         }
7804     }
7805 }
7806
7807 static void  
7808 gtk_clist_drag_data_get (GtkWidget        *widget,
7809                          GdkDragContext   *context,
7810                          GtkSelectionData *selection_data,
7811                          guint             info,
7812                          guint             time)
7813 {
7814   g_return_if_fail (widget != NULL);
7815   g_return_if_fail (GTK_IS_CLIST (widget));
7816   g_return_if_fail (context != NULL);
7817   g_return_if_fail (selection_data != NULL);
7818
7819   if (selection_data->target ==
7820       gdk_atom_intern ("gtk-clist-drag-reorder", FALSE))
7821     {
7822       GtkCListCellInfo *info;
7823
7824       info = g_dataset_get_data (context, "gtk-clist-drag-source");
7825
7826       if (info)
7827         {
7828           GtkCListCellInfo ret_info;
7829
7830           ret_info.row = info->row;
7831           ret_info.column = info->column;
7832
7833           gtk_selection_data_set (selection_data, selection_data->target,
7834                                   GTK_TYPE_POINTER, (guchar *) &ret_info,
7835                                   sizeof (GtkCListCellInfo));
7836         }
7837       else
7838         gtk_selection_data_set (selection_data, selection_data->target,
7839                                 GTK_TYPE_POINTER, NULL, 0);
7840     }
7841 }
7842
7843 static void
7844 draw_drag_highlight (GtkCList        *clist,
7845                      GtkCListRow     *dest_row,
7846                      gint             dest_row_number,
7847                      GtkCListDragPos  drag_pos)
7848 {
7849   gint y;
7850
7851   y = ROW_TOP_YPIXEL (clist, dest_row_number) - 1;
7852
7853   switch (drag_pos)
7854     {
7855     case GTK_CLIST_DRAG_NONE:
7856       break;
7857     case GTK_CLIST_DRAG_AFTER:
7858       y += clist->row_height + 1;
7859     case GTK_CLIST_DRAG_BEFORE:
7860       gdk_draw_line (clist->clist_window, clist->xor_gc,
7861                      0, y, clist->clist_window_width, y);
7862       break;
7863     case GTK_CLIST_DRAG_INTO:
7864       gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
7865                           clist->clist_window_width - 1, clist->row_height);
7866       break;
7867     }
7868 }
7869
7870 void
7871 gtk_clist_set_reorderable (GtkCList *clist, 
7872                            gboolean  reorderable)
7873 {
7874   GtkWidget *widget;
7875
7876   g_return_if_fail (clist != NULL);
7877   g_return_if_fail (GTK_IS_CLIST (clist));
7878
7879   if ((GTK_CLIST_REORDERABLE(clist) != 0) == reorderable)
7880     return;
7881
7882   widget = GTK_WIDGET (clist);
7883
7884   if (reorderable)
7885     {
7886       GTK_CLIST_SET_FLAG (clist, CLIST_REORDERABLE);
7887       gtk_drag_dest_set (widget,
7888                          GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
7889                          &clist_target_table, 1, GDK_ACTION_MOVE);
7890     }
7891   else
7892     {
7893       GTK_CLIST_UNSET_FLAG (clist, CLIST_REORDERABLE);
7894       gtk_drag_dest_unset (GTK_WIDGET (clist));
7895     }
7896 }
7897
7898 void
7899 gtk_clist_set_use_drag_icons (GtkCList *clist,
7900                               gboolean  use_icons)
7901 {
7902   g_return_if_fail (clist != NULL);
7903   g_return_if_fail (GTK_IS_CLIST (clist));
7904
7905   if (use_icons != 0)
7906     GTK_CLIST_SET_FLAG (clist, CLIST_USE_DRAG_ICONS);
7907   else
7908     GTK_CLIST_UNSET_FLAG (clist, CLIST_USE_DRAG_ICONS);
7909 }
7910
7911 void
7912 gtk_clist_set_button_actions (GtkCList *clist,
7913                               guint     button,
7914                               guint8    button_actions)
7915 {
7916   g_return_if_fail (clist != NULL);
7917   g_return_if_fail (GTK_IS_CLIST (clist));
7918   
7919   if (button < MAX_BUTTON)
7920     {
7921       if (gdk_pointer_is_grabbed () || GTK_WIDGET_HAS_GRAB (clist))
7922         {
7923           remove_grab (clist);
7924           clist->drag_button = 0;
7925         }
7926
7927       GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7928
7929       clist->button_actions[button] = button_actions;
7930     }
7931 }