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