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