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