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