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