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