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