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