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