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