2011-10-25 [colin] 3.7.10cvs49
[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 #include <gtk/gtk.h>
34 #include "claws-marshal.h"
35 #include "gtkcmclist.h"
36 #include <gdk/gdkkeysyms.h>
37 #include "utils.h"
38 #include "gtkutils.h"
39
40 /* length of button_actions array */
41 #define MAX_BUTTON 5
42
43 /* the number rows memchunk expands at a time */
44 #define CMCLIST_OPTIMUM_SIZE 64
45
46 /* the width of the column resize windows */
47 #define DRAG_WIDTH  6
48
49 /* minimum allowed width of a column */
50 #define COLUMN_MIN_WIDTH 5
51
52 /* this defigns the base grid spacing */
53 #define CELL_SPACING 1
54
55 /* added the horizontal space at the beginning and end of a row*/
56 #define COLUMN_INSET 3
57
58 /* used for auto-scrolling */
59 #define SCROLL_TIME  100
60
61 /* gives the top pixel of the given row in context of
62  * the clist's voffset */
63 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
64                                     (((row) + 1) * CELL_SPACING) + \
65                                     (clist)->voffset)
66
67 /* returns the row index from a y pixel location in the 
68  * context of the clist's voffset */
69 #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
70                                     ((clist)->row_height + CELL_SPACING))
71
72 /* gives the left pixel of the given column in context of
73  * the clist's hoffset */
74 #define COLUMN_LEFT_XPIXEL(clist, colnum)  ((clist)->column[(colnum)].area.x + \
75                                             (clist)->hoffset)
76
77 /* returns the column index from a x pixel location in the 
78  * context of the clist's hoffset */
79 static inline gint
80 COLUMN_FROM_XPIXEL (GtkCMCList * clist,
81                     gint x)
82 {
83   gint i, cx;
84
85   for (i = 0; i < clist->columns; i++)
86     if (clist->column[i].visible)
87       {
88         cx = clist->column[i].area.x + clist->hoffset;
89
90         if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
91             x <= (cx + clist->column[i].area.width + COLUMN_INSET))
92           return i;
93       }
94
95   /* no match */
96   return -1;
97 }
98
99 /* returns the top pixel of the given row in the context of
100  * the list height */
101 #define ROW_TOP(clist, row)        (((clist)->row_height + CELL_SPACING) * (row))
102
103 /* returns the left pixel of the given column in the context of
104  * the list width */
105 #define COLUMN_LEFT(clist, colnum) ((clist)->column[(colnum)].area.x)
106
107 /* returns the total height of the list */
108 #define LIST_HEIGHT(clist)         (((clist)->row_height * ((clist)->rows)) + \
109                                     (CELL_SPACING * ((clist)->rows + 1)))
110
111
112 /* returns the total width of the list */
113 static inline gint
114 LIST_WIDTH (GtkCMCList * clist) 
115 {
116   gint last_column;
117
118   for (last_column = clist->columns - 1;
119        last_column >= 0 && !clist->column[last_column].visible; last_column--);
120
121   if (last_column >= 0)
122     return (clist->column[last_column].area.x +
123             clist->column[last_column].area.width +
124             COLUMN_INSET + CELL_SPACING);
125   return 0;
126 }
127
128 /* returns the GList item for the nth row */
129 #define ROW_ELEMENT(clist, row) (((row) == (clist)->rows - 1) ? \
130                                  (clist)->row_list_end : \
131                                  g_list_nth ((clist)->row_list, (row)))
132
133
134 /* redraw the list if it's not frozen */
135 #define CLIST_UNFROZEN(clist)     (((GtkCMCList*) (clist))->freeze_count == 0)
136 #define CLIST_REFRESH(clist)    G_STMT_START { \
137   if (CLIST_UNFROZEN (clist)) \
138     GTK_CMCLIST_GET_CLASS (clist)->refresh ((GtkCMCList*) (clist)); \
139 } G_STMT_END
140
141
142 /* Signals */
143 enum {
144   SELECT_ROW,
145   UNSELECT_ROW,
146   ROW_MOVE,
147   CLICK_COLUMN,
148   RESIZE_COLUMN,
149   TOGGLE_FOCUS_ROW,
150   SELECT_ALL,
151   UNSELECT_ALL,
152   UNDO_SELECTION,
153   START_SELECTION,
154   END_SELECTION,
155   TOGGLE_ADD_MODE,
156   EXTEND_SELECTION,
157   SCROLL_VERTICAL,
158   SCROLL_HORIZONTAL,
159   ABORT_COLUMN_RESIZE,
160   LAST_SIGNAL
161 };
162
163 enum {
164   SYNC_REMOVE,
165   SYNC_INSERT
166 };
167
168 enum {
169   ARG_0,
170   ARG_N_COLUMNS,
171   ARG_SHADOW_TYPE,
172   ARG_SELECTION_MODE,
173   ARG_ROW_HEIGHT,
174   ARG_TITLES_ACTIVE,
175   ARG_REORDERABLE,
176   ARG_USE_DRAG_ICONS,
177   ARG_SORT_TYPE
178 };
179
180 /* GtkCMCList Methods */
181 static void     gtk_cmclist_class_init  (GtkCMCListClass         *klass);
182 static void     gtk_cmclist_init        (GtkCMCList              *clist);
183 static GObject* gtk_cmclist_constructor (GType                  type,
184                                        guint                  n_construct_properties,
185                                        GObjectConstructParam *construct_params);
186
187 /* GtkObject Methods */
188 static void gtk_cmclist_destroy  (GtkObject *object);
189 static void gtk_cmclist_finalize (GObject   *object);
190 static void gtk_cmclist_set_arg  (GObject *object,
191                                 guint      arg_id,
192                                 const GValue *value,
193                                 GParamSpec *spec);
194 static void gtk_cmclist_get_arg  (GObject *object,
195                                 guint      arg_id,
196                                 GValue *value,
197                                 GParamSpec *spec);
198
199 /* GtkWidget Methods */
200 static void gtk_cmclist_set_scroll_adjustments (GtkCMCList      *clist,
201                                               GtkAdjustment *hadjustment,
202                                               GtkAdjustment *vadjustment);
203 static void gtk_cmclist_realize         (GtkWidget        *widget);
204 static void gtk_cmclist_unrealize       (GtkWidget        *widget);
205 static void gtk_cmclist_map             (GtkWidget        *widget);
206 static void gtk_cmclist_unmap           (GtkWidget        *widget);
207 static gint gtk_cmclist_expose          (GtkWidget        *widget,
208                                        GdkEventExpose   *event);
209 static gint gtk_cmclist_button_press    (GtkWidget        *widget,
210                                        GdkEventButton   *event);
211 static gint gtk_cmclist_button_release  (GtkWidget        *widget,
212                                        GdkEventButton   *event);
213 static gint gtk_cmclist_motion          (GtkWidget        *widget, 
214                                        GdkEventMotion   *event);
215 static void gtk_cmclist_size_request    (GtkWidget        *widget,
216                                        GtkRequisition   *requisition);
217 static void gtk_cmclist_size_allocate   (GtkWidget        *widget,
218                                        GtkAllocation    *allocation);
219 static void gtk_cmclist_undraw_focus      (GtkWidget        *widget);
220 static void gtk_cmclist_draw_focus      (GtkWidget        *widget);
221 static gint gtk_cmclist_focus_in        (GtkWidget        *widget,
222                                        GdkEventFocus    *event);
223 static gint gtk_cmclist_focus_out       (GtkWidget        *widget,
224                                        GdkEventFocus    *event);
225 static gint gtk_cmclist_focus           (GtkWidget        *widget,
226                                        GtkDirectionType  direction);
227 static void gtk_cmclist_set_focus_child (GtkContainer     *container,
228                                        GtkWidget        *child);
229 static void gtk_cmclist_style_set       (GtkWidget        *widget,
230                                        GtkStyle         *previous_style);
231 static void gtk_cmclist_drag_begin      (GtkWidget        *widget,
232                                        GdkDragContext   *context);
233 static gint gtk_cmclist_drag_motion     (GtkWidget        *widget,
234                                        GdkDragContext   *context,
235                                        gint              x,
236                                        gint              y,
237                                        guint             time);
238 static void gtk_cmclist_drag_leave      (GtkWidget        *widget,
239                                        GdkDragContext   *context,
240                                        guint             time);
241 static void gtk_cmclist_drag_end        (GtkWidget        *widget,
242                                        GdkDragContext   *context);
243 static gboolean gtk_cmclist_drag_drop   (GtkWidget      *widget,
244                                        GdkDragContext *context,
245                                        gint            x,
246                                        gint            y,
247                                        guint           time);
248 static void gtk_cmclist_drag_data_get   (GtkWidget        *widget,
249                                        GdkDragContext   *context,
250                                        GtkSelectionData *selection_data,
251                                        guint             info,
252                                        guint             time);
253 static void gtk_cmclist_drag_data_received (GtkWidget        *widget,
254                                           GdkDragContext   *context,
255                                           gint              x,
256                                           gint              y,
257                                           GtkSelectionData *selection_data,
258                                           guint             info,
259                                           guint             time);
260
261 /* GtkContainer Methods */
262 static void gtk_cmclist_forall          (GtkContainer  *container,
263                                        gboolean       include_internals,
264                                        GtkCallback    callback,
265                                        gpointer       callback_data);
266
267 /* Selection */
268 static void toggle_row                (GtkCMCList      *clist,
269                                        gint           row,
270                                        gint           column,
271                                        GdkEvent      *event);
272 static void real_select_row           (GtkCMCList      *clist,
273                                        gint           row,
274                                        gint           column,
275                                        GdkEvent      *event);
276 static void real_unselect_row         (GtkCMCList      *clist,
277                                        gint           row,
278                                        gint           column,
279                                        GdkEvent      *event);
280 static void update_extended_selection (GtkCMCList      *clist,
281                                        gint           row);
282 static GList *selection_find          (GtkCMCList      *clist,
283                                        gint           row_number,
284                                        GList         *row_list_element);
285 static void real_select_all           (GtkCMCList      *clist);
286 static void real_unselect_all         (GtkCMCList      *clist);
287 static void move_vertical             (GtkCMCList      *clist,
288                                        gint           row,
289                                        gfloat         align);
290 static void move_horizontal           (GtkCMCList      *clist,
291                                        gint           diff);
292 static void real_undo_selection       (GtkCMCList      *clist);
293 static void fake_unselect_all         (GtkCMCList      *clist,
294                                        gint           row);
295 static void fake_toggle_row           (GtkCMCList      *clist,
296                                        gint           row);
297 static void resync_selection          (GtkCMCList      *clist,
298                                        GdkEvent      *event);
299 static void sync_selection            (GtkCMCList      *clist,
300                                        gint           row,
301                                        gint           mode);
302 static void set_anchor                (GtkCMCList      *clist,
303                                        gboolean       add_mode,
304                                        gint           anchor,
305                                        gint           undo_anchor);
306 static void start_selection           (GtkCMCList      *clist);
307 static void end_selection             (GtkCMCList      *clist);
308 static void toggle_add_mode           (GtkCMCList      *clist);
309 static void toggle_focus_row          (GtkCMCList      *clist);
310 static void extend_selection          (GtkCMCList      *clist,
311                                        GtkScrollType  scroll_type,
312                                        gfloat         position,
313                                        gboolean       auto_start_selection);
314 static gint get_selection_info        (GtkCMCList       *clist,
315                                        gint            x,
316                                        gint            y,
317                                        gint           *row,
318                                        gint           *column);
319
320 /* Scrolling */
321 static void move_focus_row     (GtkCMCList      *clist,
322                                 GtkScrollType  scroll_type,
323                                 gfloat         position);
324 static void scroll_horizontal  (GtkCMCList      *clist,
325                                 GtkScrollType  scroll_type,
326                                 gfloat         position);
327 static void scroll_vertical    (GtkCMCList      *clist,
328                                 GtkScrollType  scroll_type,
329                                 gfloat         position);
330 static void move_horizontal    (GtkCMCList      *clist,
331                                 gint           diff);
332 static void move_vertical      (GtkCMCList      *clist,
333                                 gint           row,
334                                 gfloat         align);
335 static gint horizontal_timeout (GtkCMCList      *clist);
336 static gint vertical_timeout   (GtkCMCList      *clist);
337 static void remove_grab        (GtkCMCList      *clist);
338
339
340 /* Resize Columns */
341 static void draw_xor_line             (GtkCMCList       *clist);
342 static gint new_column_width          (GtkCMCList       *clist,
343                                        gint            column,
344                                        gint           *x);
345 static void column_auto_resize        (GtkCMCList       *clist,
346                                        GtkCMCListRow    *clist_row,
347                                        gint            column,
348                                        gint            old_width);
349 static void real_resize_column        (GtkCMCList       *clist,
350                                        gint            column,
351                                        gint            width);
352 static void abort_column_resize       (GtkCMCList       *clist);
353 static void cell_size_request         (GtkCMCList       *clist,
354                                        GtkCMCListRow    *clist_row,
355                                        gint            column,
356                                        GtkRequisition *requisition);
357
358 /* Buttons */
359 static void column_button_create      (GtkCMCList       *clist,
360                                        gint            column);
361 static void column_button_clicked     (GtkWidget      *widget,
362                                        gpointer        data);
363
364 /* Adjustments */
365 static void adjust_adjustments        (GtkCMCList       *clist,
366                                        gboolean        block_resize);
367 static void vadjustment_changed       (GtkAdjustment  *adjustment,
368                                        gpointer        data);
369 static void vadjustment_value_changed (GtkAdjustment  *adjustment,
370                                        gpointer        data);
371 static void hadjustment_changed       (GtkAdjustment  *adjustment,
372                                        gpointer        data);
373 static void hadjustment_value_changed (GtkAdjustment  *adjustment,
374                                        gpointer        data);
375
376 /* Drawing */
377 static void get_cell_style   (GtkCMCList      *clist,
378                               GtkCMCListRow   *clist_row,
379                               gint           state,
380                               gint           column,
381                               GtkStyle     **style);
382 static gint draw_cell_pixbuf (GdkWindow     *window,
383                               GdkRectangle  *clip_rectangle,
384                               cairo_t       *cr,
385                               GdkPixbuf     *pixbuf,
386                               gint           x,
387                               gint           y,
388                               gint           width,
389                               gint           height);
390 static void draw_row         (GtkCMCList      *clist,
391                               GdkRectangle  *area,
392                               gint           row,
393                               GtkCMCListRow   *clist_row);
394 static void draw_rows        (GtkCMCList      *clist,
395                               GdkRectangle  *area);
396 static void clist_refresh    (GtkCMCList      *clist);
397      
398 /* Size Allocation / Requisition */
399 static void size_allocate_title_buttons (GtkCMCList *clist);
400 static void size_allocate_columns       (GtkCMCList *clist,
401                                          gboolean  block_resize);
402 static gint list_requisition_width      (GtkCMCList *clist);
403
404 /* Memory Allocation/Distruction Routines */
405 static GtkCMCListColumn *columns_new (GtkCMCList      *clist);
406 static void column_title_new       (GtkCMCList      *clist,
407                                     gint           column,
408                                     const gchar   *title);
409 static void columns_delete         (GtkCMCList      *clist);
410 static GtkCMCListRow *row_new        (GtkCMCList      *clist);
411 static void row_delete             (GtkCMCList      *clist,
412                                     GtkCMCListRow   *clist_row);
413 static void set_cell_contents      (GtkCMCList      *clist,
414                                     GtkCMCListRow   *clist_row,
415                                     gint           column,
416                                     GtkCMCellType    type,
417                                     const gchar   *text,
418                                     guint8         spacing,
419                                     GdkPixbuf     *pixbuf);
420 static gint real_insert_row        (GtkCMCList      *clist,
421                                     gint           row,
422                                     gchar         *text[]);
423 static void real_remove_row        (GtkCMCList      *clist,
424                                     gint           row);
425 static void real_clear             (GtkCMCList      *clist);
426
427 /* Sorting */
428 static gint default_compare        (GtkCMCList      *clist,
429                                     gconstpointer  row1,
430                                     gconstpointer  row2);
431 static void real_sort_list         (GtkCMCList      *clist);
432 static GList *gtk_cmclist_merge      (GtkCMCList      *clist,
433                                     GList         *a,
434                                     GList         *b);
435 static GList *gtk_cmclist_mergesort  (GtkCMCList      *clist,
436                                     GList         *list,
437                                     gint           num);
438 /* Misc */
439 static gboolean title_focus_in   (GtkCMCList *clist,
440                                   gint      dir);
441 static gboolean title_focus_move (GtkCMCList *clist,
442                                   gint      dir);
443
444 static void real_row_move             (GtkCMCList  *clist,
445                                        gint       source_row,
446                                        gint       dest_row);
447 static gint column_title_passive_func (GtkWidget *widget, 
448                                        GdkEvent  *event,
449                                        gpointer   data);
450 static void drag_dest_cell            (GtkCMCList         *clist,
451                                        gint              x,
452                                        gint              y,
453                                        GtkCMCListDestInfo *dest_info);
454
455
456
457 static GtkContainerClass *parent_class = NULL;
458 static guint clist_signals[LAST_SIGNAL] = {0};
459
460 static const GtkTargetEntry clist_target_table = { "gtk-clist-drag-reorder", 0, 0};
461
462 GType
463 gtk_cmclist_get_type (void)
464 {
465   static GType clist_type = 0;
466
467   if (!clist_type)
468     {
469       static const GTypeInfo clist_info =
470       {
471                         sizeof (GtkCMCListClass),
472
473                         (GBaseInitFunc) NULL,
474                         (GBaseFinalizeFunc) NULL,
475
476                         (GClassInitFunc) gtk_cmclist_class_init,
477                         (GClassFinalizeFunc) NULL,
478                         NULL,   /* class_data */
479
480                         sizeof (GtkCMCList),
481                         0,      /* n_preallocs */
482                         (GInstanceInitFunc) gtk_cmclist_init,
483         };
484         clist_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkCMCList", &clist_info, (GTypeFlags)0);
485     }
486
487   return clist_type;
488 }
489
490 static void
491 gtk_cmclist_class_init (GtkCMCListClass *klass)
492 {
493   GObjectClass *object_class = G_OBJECT_CLASS (klass);
494   GtkObjectClass *gtk_object_class;
495   GtkWidgetClass *widget_class;
496   GtkContainerClass *container_class;
497   GtkBindingSet *binding_set;
498
499   object_class->constructor = gtk_cmclist_constructor;
500
501   gtk_object_class = (GtkObjectClass *) klass;
502   widget_class = (GtkWidgetClass *) klass;
503   container_class = (GtkContainerClass *) klass;
504
505   parent_class = g_type_class_peek (GTK_TYPE_CONTAINER);
506
507   object_class->finalize = gtk_cmclist_finalize;
508   gtk_object_class->destroy = gtk_cmclist_destroy;
509   object_class->set_property = gtk_cmclist_set_arg;
510   object_class->get_property = gtk_cmclist_get_arg;
511   
512
513   widget_class->realize = gtk_cmclist_realize;
514   widget_class->unrealize = gtk_cmclist_unrealize;
515   widget_class->map = gtk_cmclist_map;
516   widget_class->unmap = gtk_cmclist_unmap;
517   widget_class->button_press_event = gtk_cmclist_button_press;
518   widget_class->button_release_event = gtk_cmclist_button_release;
519   widget_class->motion_notify_event = gtk_cmclist_motion;
520   widget_class->expose_event = gtk_cmclist_expose;
521   widget_class->size_request = gtk_cmclist_size_request;
522   widget_class->size_allocate = gtk_cmclist_size_allocate;
523   widget_class->focus_in_event = gtk_cmclist_focus_in;
524   widget_class->focus_out_event = gtk_cmclist_focus_out;
525   widget_class->style_set = gtk_cmclist_style_set;
526   widget_class->drag_begin = gtk_cmclist_drag_begin;
527   widget_class->drag_end = gtk_cmclist_drag_end;
528   widget_class->drag_motion = gtk_cmclist_drag_motion;
529   widget_class->drag_leave = gtk_cmclist_drag_leave;
530   widget_class->drag_drop = gtk_cmclist_drag_drop;
531   widget_class->drag_data_get = gtk_cmclist_drag_data_get;
532   widget_class->drag_data_received = gtk_cmclist_drag_data_received;
533   widget_class->focus = gtk_cmclist_focus;
534   
535   /* container_class->add = NULL; use the default GtkContainerClass warning */
536   /* container_class->remove=NULL; use the default GtkContainerClass warning */
537
538   container_class->forall = gtk_cmclist_forall;
539   container_class->set_focus_child = gtk_cmclist_set_focus_child;
540
541   klass->set_scroll_adjustments = gtk_cmclist_set_scroll_adjustments;
542   klass->refresh = clist_refresh;
543   klass->select_row = real_select_row;
544   klass->unselect_row = real_unselect_row;
545   klass->row_move = real_row_move;
546   klass->undo_selection = real_undo_selection;
547   klass->resync_selection = resync_selection;
548   klass->selection_find = selection_find;
549   klass->click_column = NULL;
550   klass->resize_column = real_resize_column;
551   klass->draw_row = draw_row;
552   klass->insert_row = real_insert_row;
553   klass->remove_row = real_remove_row;
554   klass->clear = real_clear;
555   klass->sort_list = real_sort_list;
556   klass->select_all = real_select_all;
557   klass->unselect_all = real_unselect_all;
558   klass->fake_unselect_all = fake_unselect_all;
559   klass->scroll_horizontal = scroll_horizontal;
560   klass->scroll_vertical = scroll_vertical;
561   klass->extend_selection = extend_selection;
562   klass->toggle_focus_row = toggle_focus_row;
563   klass->toggle_add_mode = toggle_add_mode;
564   klass->start_selection = start_selection;
565   klass->end_selection = end_selection;
566   klass->abort_column_resize = abort_column_resize;
567   klass->set_cell_contents = set_cell_contents;
568   klass->cell_size_request = cell_size_request;
569
570   g_object_class_install_property (object_class,
571                                 ARG_N_COLUMNS,
572                                 g_param_spec_uint ("n-columns",
573                                 "N-Columns",
574                                 "N-Columns",
575                                 1,
576                                 G_MAXINT,
577                                 1,
578                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
579   g_object_class_install_property (object_class,
580                                 ARG_SHADOW_TYPE,
581                                 g_param_spec_enum ("shadow-type",
582                                 "shadow-type",
583                                 "shadow-type",
584                                 GTK_TYPE_SHADOW_TYPE, 0,
585                                 G_PARAM_READWRITE));
586   g_object_class_install_property (object_class,
587                                 ARG_SELECTION_MODE,
588                                 g_param_spec_enum ("selection-mode",
589                                 "selection-mode",
590                                 "selection-mode",
591                                 GTK_TYPE_SELECTION_MODE, 0,
592                                 G_PARAM_READWRITE));
593   g_object_class_install_property (object_class,
594                                 ARG_ROW_HEIGHT,
595                                 g_param_spec_uint ("row-height",
596                                 "row-height",
597                                 "row-height",
598                                 0,
599                                 G_MAXINT,
600                                 0,
601                                 G_PARAM_READWRITE));
602   g_object_class_install_property (object_class,
603                                 ARG_REORDERABLE,
604                                 g_param_spec_boolean ("reorderable",
605                                 "reorderable",
606                                 "reorderable",
607                                 TRUE,
608                                 G_PARAM_READWRITE));
609   g_object_class_install_property (object_class,
610                                 ARG_TITLES_ACTIVE,
611                                 g_param_spec_boolean ("titles-active",
612                                 "titles-active",
613                                 "titles-active",
614                                 TRUE,
615                                 G_PARAM_READWRITE));
616   g_object_class_install_property (object_class,
617                                 ARG_USE_DRAG_ICONS,
618                                 g_param_spec_boolean ("use-drag-icons",
619                                 "use-drag-icons",
620                                 "use-drag-icons",
621                                 TRUE,
622                                 G_PARAM_READWRITE));
623   g_object_class_install_property (object_class,
624                                 ARG_SORT_TYPE,
625                                 g_param_spec_enum ("sort-type",
626                                 "sort-type",
627                                 "sort-type",
628                                 GTK_TYPE_SORT_TYPE, 0,
629                                 G_PARAM_READWRITE));
630   widget_class->set_scroll_adjustments_signal =
631                 g_signal_new ("set_scroll_adjustments",
632                               G_TYPE_FROM_CLASS (object_class),
633                               G_SIGNAL_RUN_LAST,
634                               G_STRUCT_OFFSET (GtkCMCListClass, set_scroll_adjustments),
635                               NULL, NULL,
636                               claws_marshal_VOID__OBJECT_OBJECT,
637                               G_TYPE_NONE, 2,
638                               GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
639
640   clist_signals[SELECT_ROW] =
641                 g_signal_new ("select_row",
642                               G_TYPE_FROM_CLASS (object_class),
643                               G_SIGNAL_RUN_FIRST,
644                               G_STRUCT_OFFSET (GtkCMCListClass, select_row),
645                               NULL, NULL,
646                               claws_marshal_VOID__INT_INT_BOXED,
647                               G_TYPE_NONE, 3,
648                               G_TYPE_INT,
649                               G_TYPE_INT,
650                               GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
651   clist_signals[UNSELECT_ROW] =
652                 g_signal_new ("unselect_row",
653                               G_TYPE_FROM_CLASS (object_class),
654                               G_SIGNAL_RUN_FIRST,
655                               G_STRUCT_OFFSET (GtkCMCListClass, unselect_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);
662   clist_signals[ROW_MOVE] =
663                 g_signal_new ("row_move",
664                               G_TYPE_FROM_CLASS (object_class),
665                               G_SIGNAL_RUN_LAST,
666                               G_STRUCT_OFFSET (GtkCMCListClass, row_move),
667                               NULL, NULL,
668                               claws_marshal_VOID__INT_INT,
669                               G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
670   clist_signals[CLICK_COLUMN] =
671                 g_signal_new ("click_column",
672                               G_TYPE_FROM_CLASS (object_class),
673                               G_SIGNAL_RUN_FIRST,
674                               G_STRUCT_OFFSET (GtkCMCListClass, click_column),
675                               NULL, NULL,
676                               claws_marshal_VOID__INT,
677                               G_TYPE_NONE, 1, G_TYPE_INT);
678   clist_signals[RESIZE_COLUMN] =
679                 g_signal_new ("resize_column",
680                               G_TYPE_FROM_CLASS (object_class),
681                               G_SIGNAL_RUN_LAST,
682                               G_STRUCT_OFFSET (GtkCMCListClass, resize_column),
683                               NULL, NULL,
684                               claws_marshal_VOID__INT_INT,
685                               G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
686
687   clist_signals[TOGGLE_FOCUS_ROW] =
688                 g_signal_new ("toggle_focus_row",
689                               G_TYPE_FROM_CLASS (object_class),
690                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
691                               G_STRUCT_OFFSET (GtkCMCListClass, toggle_focus_row),
692                               NULL, NULL,
693                               claws_marshal_VOID__VOID,
694                               G_TYPE_NONE, 0);
695   clist_signals[SELECT_ALL] =
696                 g_signal_new ("select_all",
697                               G_TYPE_FROM_CLASS (object_class),
698                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
699                               G_STRUCT_OFFSET (GtkCMCListClass, select_all),
700                               NULL, NULL,
701                               claws_marshal_VOID__VOID,
702                               G_TYPE_NONE, 0);
703   clist_signals[UNSELECT_ALL] =
704                 g_signal_new ("unselect_all",
705                               G_TYPE_FROM_CLASS (object_class),
706                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
707                               G_STRUCT_OFFSET (GtkCMCListClass, unselect_all),
708                               NULL, NULL,
709                               claws_marshal_VOID__VOID,
710                               G_TYPE_NONE, 0);
711   clist_signals[UNDO_SELECTION] =
712                 g_signal_new ("undo_selection",
713                               G_TYPE_FROM_CLASS (object_class),
714                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
715                               G_STRUCT_OFFSET (GtkCMCListClass, undo_selection),
716                               NULL, NULL,
717                               claws_marshal_VOID__VOID,
718                               G_TYPE_NONE, 0);
719   clist_signals[START_SELECTION] =
720                 g_signal_new ("start_selection",
721                               G_TYPE_FROM_CLASS (object_class),
722                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
723                               G_STRUCT_OFFSET (GtkCMCListClass, start_selection),
724                               NULL, NULL,
725                               claws_marshal_VOID__VOID,
726                               G_TYPE_NONE, 0);
727   clist_signals[END_SELECTION] =
728                 g_signal_new ("end_selection",
729                               G_TYPE_FROM_CLASS (object_class),
730                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
731                               G_STRUCT_OFFSET (GtkCMCListClass, end_selection),
732                               NULL, NULL,
733                               claws_marshal_VOID__VOID,
734                               G_TYPE_NONE, 0);
735   clist_signals[TOGGLE_ADD_MODE] =
736                 g_signal_new ("toggle_add_mode",
737                               G_TYPE_FROM_CLASS (object_class),
738                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
739                               G_STRUCT_OFFSET (GtkCMCListClass, toggle_add_mode),
740                               NULL, NULL,
741                               claws_marshal_VOID__VOID,
742                               G_TYPE_NONE, 0);
743   clist_signals[EXTEND_SELECTION] =
744                 g_signal_new ("extend_selection",
745                               G_TYPE_FROM_CLASS (object_class),
746                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
747                               G_STRUCT_OFFSET (GtkCMCListClass, extend_selection),
748                               NULL, NULL,
749                               claws_marshal_VOID__ENUM_FLOAT_BOOLEAN,
750                               G_TYPE_NONE, 3, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT, G_TYPE_BOOLEAN);
751   clist_signals[SCROLL_VERTICAL] =
752                 g_signal_new ("scroll_vertical",
753                               G_TYPE_FROM_CLASS (object_class),
754                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
755                               G_STRUCT_OFFSET (GtkCMCListClass, scroll_vertical),
756                               NULL, NULL,
757                               claws_marshal_VOID__ENUM_FLOAT,
758                               G_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT);
759   clist_signals[SCROLL_HORIZONTAL] =
760                 g_signal_new ("scroll_horizontal",
761                               G_TYPE_FROM_CLASS (object_class),
762                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
763                               G_STRUCT_OFFSET (GtkCMCListClass, scroll_horizontal),
764                               NULL, NULL,
765                               claws_marshal_VOID__ENUM_FLOAT,
766                               G_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT);
767   clist_signals[ABORT_COLUMN_RESIZE] =
768                 g_signal_new ("abort_column_resize",
769                               G_TYPE_FROM_CLASS (object_class),
770                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
771                               G_STRUCT_OFFSET (GtkCMCListClass, abort_column_resize),
772                               NULL, NULL,
773                               claws_marshal_VOID__VOID,
774                               G_TYPE_NONE, 0);
775
776   binding_set = gtk_binding_set_by_class (klass);
777   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0,
778                                 "scroll_vertical", 2,
779                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
780                                 G_TYPE_FLOAT, 0.0);
781   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0,
782                                 "scroll_vertical", 2,
783                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
784                                 G_TYPE_FLOAT, 0.0);
785   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0,
786                                 "scroll_vertical", 2,
787                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
788                                 G_TYPE_FLOAT, 0.0);
789   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0,
790                                 "scroll_vertical", 2,
791                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
792                                 G_TYPE_FLOAT, 0.0);
793   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
794                                 "scroll_vertical", 2,
795                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
796                                 G_TYPE_FLOAT, 0.0);
797   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0,
798                                 "scroll_vertical", 2,
799                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
800                                 G_TYPE_FLOAT, 0.0);
801   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
802                                 "scroll_vertical", 2,
803                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
804                                 G_TYPE_FLOAT, 0.0);
805   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0,
806                                 "scroll_vertical", 2,
807                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
808                                 G_TYPE_FLOAT, 0.0);
809   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK,
810                                 "scroll_vertical", 2,
811                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
812                                 G_TYPE_FLOAT, 0.0);
813   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
814                                 "scroll_vertical", 2,
815                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
816                                 G_TYPE_FLOAT, 0.0);
817   gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, GDK_CONTROL_MASK,
818                                 "scroll_vertical", 2,
819                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
820                                 G_TYPE_FLOAT, 1.0);
821   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK,
822                                 "scroll_vertical", 2,
823                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
824                                 G_TYPE_FLOAT, 1.0);
825   
826   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_SHIFT_MASK,
827                                 "extend_selection", 3,
828                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
829                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
830   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_SHIFT_MASK,
831                                 "extend_selection", 3,
832                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
833                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
834   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_SHIFT_MASK,
835                                 "extend_selection", 3,
836                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
837                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
838   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_SHIFT_MASK,
839                                 "extend_selection", 3,
840                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
841                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
842   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, GDK_SHIFT_MASK,
843                                 "extend_selection", 3,
844                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
845                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
846   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, GDK_SHIFT_MASK,
847                                 "extend_selection", 3,
848                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
849                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
850   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, GDK_SHIFT_MASK,
851                                 "extend_selection", 3,
852                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
853                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
854   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, GDK_SHIFT_MASK,
855                                 "extend_selection", 3,
856                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
857                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
858   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home,
859                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
860                                 "extend_selection", 3,
861                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
862                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
863   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home,
864                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
865                                 "extend_selection", 3,
866                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
867                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
868   gtk_binding_entry_add_signal (binding_set, GDK_KEY_End,
869                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
870                                 "extend_selection", 3,
871                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
872                                 G_TYPE_FLOAT, 1.0, G_TYPE_BOOLEAN, TRUE);
873   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End,
874                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
875                                 "extend_selection", 3,
876                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
877                                 G_TYPE_FLOAT, 1.0, G_TYPE_BOOLEAN, TRUE);
878
879   
880   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, 0,
881                                 "scroll_horizontal", 2,
882                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
883                                 G_TYPE_FLOAT, 0.0);
884   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, 0,
885                                 "scroll_horizontal", 2,
886                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
887                                 G_TYPE_FLOAT, 0.0);
888   
889   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, 0,
890                                 "scroll_horizontal", 2,
891                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
892                                 G_TYPE_FLOAT, 0.0);
893   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, 0,
894                                 "scroll_horizontal", 2,
895                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
896                                 G_TYPE_FLOAT, 0.0);
897
898   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0,
899                                 "scroll_horizontal", 2,
900                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
901                                 G_TYPE_FLOAT, 0.0);
902   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0,
903                                 "scroll_horizontal", 2,
904                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
905                                 G_TYPE_FLOAT, 0.0);
906   
907   gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0,
908                                 "scroll_horizontal", 2,
909                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
910                                 G_TYPE_FLOAT, 1.0);
911
912   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0,
913                                 "scroll_horizontal", 2,
914                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
915                                 G_TYPE_FLOAT, 1.0);
916   
917   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
918                                 "undo_selection", 0);
919   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
920                                 "abort_column_resize", 0);
921   gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
922                                 "toggle_focus_row", 0);
923   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0,
924                                 "toggle_focus_row", 0);  
925   gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
926                                 "toggle_add_mode", 0);
927   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
928                                 "toggle_add_mode", 0);
929   gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
930                                 "select_all", 0);
931   gtk_binding_entry_add_signal (binding_set, GDK_KP_Divide, GDK_CONTROL_MASK,
932                                 "select_all", 0);
933   gtk_binding_entry_add_signal (binding_set, '\\', GDK_CONTROL_MASK,
934                                 "unselect_all", 0);
935   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_L,
936                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK,
937                                 "end_selection", 0);
938   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_R,
939                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK,
940                                 "end_selection", 0);
941   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_L,
942                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK |
943                                 GDK_CONTROL_MASK,
944                                 "end_selection", 0);
945   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_R,
946                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK |
947                                 GDK_CONTROL_MASK,
948                                 "end_selection", 0);
949 }
950
951 static void
952 gtk_cmclist_set_arg (GObject *object,
953                                 guint      arg_id,
954                                 const GValue *value,
955                                 GParamSpec *spec)
956 {
957   GtkCMCList *clist;
958
959   clist = GTK_CMCLIST (object);
960
961   switch (arg_id)
962     {
963     case ARG_N_COLUMNS: /* only set at construction time */
964       clist->columns = MAX (1, g_value_get_uint (value));
965       break;
966     case ARG_SHADOW_TYPE:
967       gtk_cmclist_set_shadow_type (clist, g_value_get_enum (value));
968       break;
969     case ARG_SELECTION_MODE:
970       gtk_cmclist_set_selection_mode (clist, g_value_get_enum (value));
971       break;
972     case ARG_ROW_HEIGHT:
973       gtk_cmclist_set_row_height (clist, g_value_get_uint (value));
974       break;
975     case ARG_REORDERABLE:
976       gtk_cmclist_set_reorderable (clist, g_value_get_boolean (value));
977       break;
978     case ARG_TITLES_ACTIVE:
979       if (g_value_get_boolean (value))
980         gtk_cmclist_column_titles_active (clist);
981       else
982         gtk_cmclist_column_titles_passive (clist);
983       break;
984     case ARG_USE_DRAG_ICONS:
985       gtk_cmclist_set_use_drag_icons (clist, g_value_get_boolean (value));
986       break;
987     case ARG_SORT_TYPE:
988       gtk_cmclist_set_sort_type (clist, g_value_get_enum (value));
989       break;
990     }
991 }
992
993 static void
994 gtk_cmclist_get_arg (GObject *object,
995                                 guint      arg_id,
996                                 GValue *value,
997                                 GParamSpec *spec)
998 {
999   GtkCMCList *clist;
1000
1001   clist = GTK_CMCLIST (object);
1002
1003   switch (arg_id)
1004     {
1005       guint i;
1006
1007     case ARG_N_COLUMNS:
1008       g_value_set_uint(value, clist->columns);
1009       break;
1010     case ARG_SHADOW_TYPE:
1011       g_value_set_enum(value, clist->shadow_type);
1012       break;
1013     case ARG_SELECTION_MODE:
1014       g_value_set_enum(value, clist->selection_mode);
1015       break;
1016     case ARG_ROW_HEIGHT:
1017       g_value_set_uint(value, GTK_CMCLIST_ROW_HEIGHT_SET(clist) ? clist->row_height : 0);
1018       break;
1019     case ARG_REORDERABLE:
1020       g_value_set_boolean(value, GTK_CMCLIST_REORDERABLE (clist));
1021       break;
1022     case ARG_TITLES_ACTIVE:
1023       g_value_set_boolean(value, TRUE);
1024       for (i = 0; i < clist->columns; i++)
1025         if (clist->column[i].button &&
1026             !gtk_widget_get_sensitive (clist->column[i].button))
1027           {
1028             g_value_set_boolean(value, FALSE);
1029             break;
1030           }
1031       break;
1032     case ARG_USE_DRAG_ICONS:
1033       g_value_set_boolean(value, GTK_CMCLIST_USE_DRAG_ICONS (clist));
1034       break;
1035     case ARG_SORT_TYPE:
1036       g_value_set_enum(value, clist->sort_type);
1037       break;
1038     default:
1039       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, spec);
1040       break;
1041     }
1042 }
1043
1044 static void
1045 gtk_cmclist_init (GtkCMCList *clist)
1046 {
1047   clist->flags = 0;
1048
1049   gtkut_widget_set_has_window (GTK_WIDGET(clist), TRUE);
1050   gtkut_widget_set_can_focus (GTK_WIDGET(clist), TRUE);
1051   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_DRAW_DRAG_LINE);
1052   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_USE_DRAG_ICONS);
1053
1054
1055 #if !GLIB_CHECK_VERSION(2,10,0)
1056   clist->row_mem_chunk = NULL;
1057   clist->cell_mem_chunk = NULL;
1058 #endif
1059
1060   clist->freeze_count = 0;
1061
1062   clist->rows = 0;
1063   clist->row_height = 0;
1064   clist->row_list = NULL;
1065   clist->row_list_end = NULL;
1066
1067   clist->columns = 0;
1068
1069   clist->title_window = NULL;
1070   clist->column_title_area.x = 0;
1071   clist->column_title_area.y = 0;
1072   clist->column_title_area.width = 1;
1073   clist->column_title_area.height = 1;
1074
1075   clist->clist_window = NULL;
1076   clist->clist_window_width = 1;
1077   clist->clist_window_height = 1;
1078
1079   clist->hoffset = 0;
1080   clist->voffset = 0;
1081
1082   clist->shadow_type = GTK_SHADOW_IN;
1083   clist->vadjustment = NULL;
1084   clist->hadjustment = NULL;
1085
1086   clist->button_actions[0] = GTK_CMBUTTON_SELECTS | GTK_CMBUTTON_DRAGS;
1087   clist->button_actions[1] = GTK_CMBUTTON_IGNORED;
1088   clist->button_actions[2] = GTK_CMBUTTON_IGNORED;
1089   clist->button_actions[3] = GTK_CMBUTTON_IGNORED;
1090   clist->button_actions[4] = GTK_CMBUTTON_IGNORED;
1091
1092   clist->cursor_drag = NULL;
1093   clist->x_drag = 0;
1094
1095   clist->selection_mode = GTK_SELECTION_SINGLE;
1096   clist->selection = NULL;
1097   clist->selection_end = NULL;
1098   clist->undo_selection = NULL;
1099   clist->undo_unselection = NULL;
1100
1101   clist->focus_row = -1;
1102   clist->focus_header_column = -1;
1103   clist->undo_anchor = -1;
1104
1105   clist->anchor = -1;
1106   clist->anchor_state = GTK_STATE_SELECTED;
1107   clist->drag_pos = -1;
1108   clist->htimer = 0;
1109   clist->vtimer = 0;
1110
1111   clist->click_cell.row = -1;
1112   clist->click_cell.column = -1;
1113
1114   clist->compare = default_compare;
1115   clist->sort_type = GTK_SORT_ASCENDING;
1116   clist->sort_column = 0;
1117
1118   clist->drag_highlight_row = -1;
1119 }
1120
1121 /* Constructor */
1122 static GObject*
1123 gtk_cmclist_constructor (GType                  type,
1124                        guint                  n_construct_properties,
1125                        GObjectConstructParam *construct_properties)
1126 {
1127   GObject *object = G_OBJECT_CLASS (parent_class)->constructor (type,
1128                                                                 n_construct_properties,
1129                                                                 construct_properties);
1130   GtkCMCList *clist = GTK_CMCLIST (object);
1131   
1132 #if !GLIB_CHECK_VERSION(2,10,0)
1133   if (!clist->row_mem_chunk)
1134     clist->row_mem_chunk = g_mem_chunk_new ("clist row mem chunk",
1135                                             sizeof (GtkCMCListRow),
1136                                             sizeof (GtkCMCListRow) *
1137                                             CMCLIST_OPTIMUM_SIZE, 
1138                                             G_ALLOC_AND_FREE);
1139   
1140   if (!clist->cell_mem_chunk)
1141     clist->cell_mem_chunk = g_mem_chunk_new ("clist cell mem chunk",
1142                                              sizeof (GtkCMCell) * clist->columns,
1143                                              sizeof (GtkCMCell) * clist->columns *
1144                                              CMCLIST_OPTIMUM_SIZE, 
1145                                              G_ALLOC_AND_FREE);
1146 #endif
1147
1148   /* allocate memory for columns */
1149   clist->column = columns_new (clist);
1150   
1151   /* there needs to be at least one column button 
1152    * because there is alot of code that will break if it
1153    * isn't there
1154    */
1155   column_button_create (clist, 0);
1156   
1157   return object;
1158 }
1159
1160 /* GTKCLIST PUBLIC INTERFACE
1161  *   gtk_cmclist_new
1162  *   gtk_cmclist_new_with_titles
1163  *   gtk_cmclist_set_hadjustment
1164  *   gtk_cmclist_set_vadjustment
1165  *   gtk_cmclist_get_hadjustment
1166  *   gtk_cmclist_get_vadjustment
1167  *   gtk_cmclist_set_shadow_type
1168  *   gtk_cmclist_set_selection_mode
1169  *   gtk_cmclist_freeze
1170  *   gtk_cmclist_thaw
1171  */
1172 GtkWidget*
1173 gtk_cmclist_new (gint columns)
1174 {
1175   return gtk_cmclist_new_with_titles (columns, NULL);
1176 }
1177  
1178 GtkWidget*
1179 gtk_cmclist_new_with_titles (gint   columns,
1180                            gchar *titles[])
1181 {
1182   GtkCMCList *clist;
1183
1184   clist = g_object_new (GTK_TYPE_CMCLIST,
1185                         "n_columns", columns,
1186                         NULL);
1187   if (titles)
1188     {
1189       guint i;
1190
1191       for (i = 0; i < clist->columns; i++)
1192         gtk_cmclist_set_column_title (clist, i, titles[i]);
1193       gtk_cmclist_column_titles_show (clist);
1194     }
1195   else
1196     gtk_cmclist_column_titles_hide (clist);
1197
1198   return GTK_WIDGET (clist);
1199 }
1200
1201 void
1202 gtk_cmclist_set_hadjustment (GtkCMCList      *clist,
1203                            GtkAdjustment *adjustment)
1204 {
1205   GtkAdjustment *old_adjustment;
1206
1207   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1208   if (adjustment)
1209     cm_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1210   
1211   if (clist->hadjustment == adjustment)
1212     return;
1213   
1214   old_adjustment = clist->hadjustment;
1215
1216   if (clist->hadjustment)
1217     {
1218       g_signal_handlers_disconnect_matched(G_OBJECT (clist->hadjustment), G_SIGNAL_MATCH_DATA,
1219                         0, 0, 0, 0, clist);
1220
1221       g_object_unref (G_OBJECT (clist->hadjustment));
1222     }
1223
1224   clist->hadjustment = adjustment;
1225
1226   if (clist->hadjustment)
1227     {
1228 #if GLIB_CHECK_VERSION(2,10,0)
1229       g_object_ref_sink (clist->hadjustment);
1230 #else
1231       gtk_object_ref (G_OBJECT (clist->hadjustment));
1232       gtk_object_sink (G_OBJECT (clist->hadjustment));
1233 #endif
1234       g_signal_connect (G_OBJECT (clist->hadjustment), "changed",
1235                           G_CALLBACK( hadjustment_changed),
1236                           (gpointer) clist);
1237       g_signal_connect (G_OBJECT (clist->hadjustment), "value_changed",
1238                           G_CALLBACK( hadjustment_value_changed),
1239                           (gpointer) clist);
1240     }
1241
1242   if (!clist->hadjustment || !old_adjustment)
1243     gtk_widget_queue_resize (GTK_WIDGET (clist));
1244 }
1245
1246 GtkAdjustment *
1247 gtk_cmclist_get_hadjustment (GtkCMCList *clist)
1248 {
1249   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1250
1251   return clist->hadjustment;
1252 }
1253
1254 void
1255 gtk_cmclist_set_vadjustment (GtkCMCList      *clist,
1256                            GtkAdjustment *adjustment)
1257 {
1258   GtkAdjustment *old_adjustment;
1259
1260   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1261   if (adjustment)
1262     cm_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1263
1264   if (clist->vadjustment == adjustment)
1265     return;
1266   
1267   old_adjustment = clist->vadjustment;
1268
1269   if (clist->vadjustment)
1270     {
1271       g_signal_handlers_disconnect_matched(G_OBJECT (clist->vadjustment), G_SIGNAL_MATCH_DATA,
1272                         0, 0, 0, 0, clist);
1273       g_object_unref (G_OBJECT (clist->vadjustment));
1274     }
1275
1276   clist->vadjustment = adjustment;
1277
1278   if (clist->vadjustment)
1279     {
1280 #if GLIB_CHECK_VERSION(2,10,0)
1281       g_object_ref_sink (clist->vadjustment);
1282 #else
1283       gtk_object_ref (G_OBJECT (clist->vadjustment));
1284       gtk_object_sink (G_OBJECT (clist->vadjustment));
1285 #endif
1286
1287       g_signal_connect (G_OBJECT (clist->vadjustment), "changed",
1288                           G_CALLBACK(vadjustment_changed),
1289                           (gpointer) clist);
1290       g_signal_connect (G_OBJECT (clist->vadjustment), "value_changed",
1291                           G_CALLBACK(vadjustment_value_changed),
1292                           (gpointer) clist);
1293     }
1294
1295   if (!clist->vadjustment || !old_adjustment)
1296     gtk_widget_queue_resize (GTK_WIDGET (clist));
1297 }
1298
1299 GtkAdjustment *
1300 gtk_cmclist_get_vadjustment (GtkCMCList *clist)
1301 {
1302   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1303
1304   return clist->vadjustment;
1305 }
1306
1307 static void
1308 gtk_cmclist_set_scroll_adjustments (GtkCMCList      *clist,
1309                                   GtkAdjustment *hadjustment,
1310                                   GtkAdjustment *vadjustment)
1311 {
1312   if (clist->hadjustment != hadjustment)
1313     gtk_cmclist_set_hadjustment (clist, hadjustment);
1314   if (clist->vadjustment != vadjustment)
1315     gtk_cmclist_set_vadjustment (clist, vadjustment);
1316 }
1317
1318 void
1319 gtk_cmclist_set_shadow_type (GtkCMCList      *clist,
1320                            GtkShadowType  type)
1321 {
1322   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1323
1324   clist->shadow_type = type;
1325
1326   if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1327     gtk_widget_queue_resize (GTK_WIDGET (clist));
1328 }
1329
1330 void
1331 gtk_cmclist_set_selection_mode (GtkCMCList         *clist,
1332                               GtkSelectionMode  mode)
1333 {
1334   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1335   cm_return_if_fail (mode != GTK_SELECTION_NONE);
1336
1337   if (mode == clist->selection_mode)
1338     return;
1339
1340   clist->selection_mode = mode;
1341   clist->anchor = -1;
1342   clist->anchor_state = GTK_STATE_SELECTED;
1343   clist->drag_pos = -1;
1344   clist->undo_anchor = clist->focus_row;
1345
1346   g_list_free (clist->undo_selection);
1347   g_list_free (clist->undo_unselection);
1348   clist->undo_selection = NULL;
1349   clist->undo_unselection = NULL;
1350
1351   switch (mode)
1352     {
1353     case GTK_SELECTION_MULTIPLE:
1354       return;
1355     case GTK_SELECTION_BROWSE:
1356     case GTK_SELECTION_SINGLE:
1357       gtk_cmclist_unselect_all (clist);
1358       break;
1359     default:
1360       /* Someone set it by hand */
1361       g_assert_not_reached ();
1362     }
1363 }
1364
1365 void
1366 gtk_cmclist_freeze (GtkCMCList *clist)
1367 {
1368   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1369
1370   clist->freeze_count++;
1371 }
1372
1373 void
1374 gtk_cmclist_thaw (GtkCMCList *clist)
1375 {
1376   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1377
1378   if (clist->freeze_count)
1379     {
1380       clist->freeze_count--;
1381       CLIST_REFRESH (clist);
1382     }
1383 }
1384
1385 /* PUBLIC COLUMN FUNCTIONS
1386  *   gtk_cmclist_column_titles_show
1387  *   gtk_cmclist_column_titles_hide
1388  *   gtk_cmclist_column_title_active
1389  *   gtk_cmclist_column_title_passive
1390  *   gtk_cmclist_column_titles_active
1391  *   gtk_cmclist_column_titles_passive
1392  *   gtk_cmclist_set_column_title
1393  *   gtk_cmclist_get_column_title
1394  *   gtk_cmclist_set_column_widget
1395  *   gtk_cmclist_set_column_justification
1396  *   gtk_cmclist_set_column_visibility
1397  *   gtk_cmclist_set_column_resizeable
1398  *   gtk_cmclist_set_column_auto_resize
1399  *   gtk_cmclist_optimal_column_width
1400  *   gtk_cmclist_set_column_width
1401  *   gtk_cmclist_set_column_min_width
1402  *   gtk_cmclist_set_column_max_width
1403  */
1404 void
1405 gtk_cmclist_column_titles_show (GtkCMCList *clist)
1406 {
1407   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1408
1409   if (!GTK_CMCLIST_SHOW_TITLES(clist))
1410     {
1411       GTK_CMCLIST_SET_FLAG (clist, CMCLIST_SHOW_TITLES);
1412       if (clist->title_window)
1413         gdk_window_show (clist->title_window);
1414       gtk_widget_queue_resize (GTK_WIDGET (clist));
1415     }
1416 }
1417
1418 void 
1419 gtk_cmclist_column_titles_hide (GtkCMCList *clist)
1420 {
1421   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1422
1423   if (GTK_CMCLIST_SHOW_TITLES(clist))
1424     {
1425       GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_SHOW_TITLES);
1426       if (clist->title_window)
1427         gdk_window_hide (clist->title_window);
1428       gtk_widget_queue_resize (GTK_WIDGET (clist));
1429     }
1430 }
1431
1432 void
1433 gtk_cmclist_column_title_active (GtkCMCList *clist,
1434                                gint      column)
1435 {
1436   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1437
1438   if (column < 0 || column >= clist->columns)
1439     return;
1440   if (!clist->column[column].button || !clist->column[column].button_passive)
1441     return;
1442
1443   clist->column[column].button_passive = FALSE;
1444
1445   g_signal_handlers_disconnect_matched(G_OBJECT (clist->column[column].button), G_SIGNAL_MATCH_FUNC,
1446                     0, 0, 0, column_title_passive_func, 0);
1447
1448   gtkut_widget_set_can_focus (clist->column[column].button, TRUE);
1449   if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1450     gtk_widget_queue_draw (clist->column[column].button);
1451 }
1452
1453 void
1454 gtk_cmclist_column_title_passive (GtkCMCList *clist,
1455                                 gint      column)
1456 {
1457   GtkToggleButton *button;
1458
1459   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1460
1461   if (column < 0 || column >= clist->columns)
1462     return;
1463   if (!clist->column[column].button || clist->column[column].button_passive)
1464     return;
1465
1466   button = GTK_TOGGLE_BUTTON (clist->column[column].button);
1467
1468   clist->column[column].button_passive = TRUE;
1469
1470   if (gtk_toggle_button_get_active(button))
1471         g_signal_connect(G_OBJECT (clist->column[column].button),
1472                          "button-release-event",
1473                          G_CALLBACK(column_title_passive_func),
1474                          NULL);
1475   if (gtk_widget_is_focus(gtk_bin_get_child(GTK_BIN(button))))
1476         g_signal_connect(G_OBJECT (clist->column[column].button),
1477                          "leave-notify-event",
1478                          G_CALLBACK(column_title_passive_func),
1479                          NULL);
1480
1481   g_signal_connect (G_OBJECT (clist->column[column].button), "event",
1482                       G_CALLBACK(column_title_passive_func), NULL);
1483
1484   gtkut_widget_set_can_focus (clist->column[column].button, FALSE);
1485   if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1486     gtk_widget_queue_draw (clist->column[column].button);
1487 }
1488
1489 void
1490 gtk_cmclist_column_titles_active (GtkCMCList *clist)
1491 {
1492   gint i;
1493
1494   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1495
1496   for (i = 0; i < clist->columns; i++)
1497     gtk_cmclist_column_title_active (clist, i);
1498 }
1499
1500 void
1501 gtk_cmclist_column_titles_passive (GtkCMCList *clist)
1502 {
1503   gint i;
1504
1505   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1506
1507   for (i = 0; i < clist->columns; i++)
1508     gtk_cmclist_column_title_passive (clist, i);
1509 }
1510
1511 void
1512 gtk_cmclist_set_column_title (GtkCMCList    *clist,
1513                             gint         column,
1514                             const gchar *title)
1515 {
1516   gint new_button = 0;
1517   GtkWidget *old_widget;
1518   GtkWidget *alignment = NULL;
1519   GtkWidget *label;
1520
1521   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1522
1523   if (column < 0 || column >= clist->columns)
1524     return;
1525
1526   /* if the column button doesn't currently exist,
1527    * it has to be created first */
1528   if (!clist->column[column].button)
1529     {
1530       column_button_create (clist, column);
1531       new_button = 1;
1532     }
1533
1534   column_title_new (clist, column, title);
1535
1536   /* remove and destroy the old widget */
1537   old_widget = gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1538   if (old_widget)
1539     gtk_container_remove (GTK_CONTAINER (clist->column[column].button), old_widget);
1540
1541   /* create new alignment based no column justification */
1542   switch (clist->column[column].justification)
1543     {
1544     case GTK_JUSTIFY_LEFT:
1545       alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
1546       break;
1547
1548     case GTK_JUSTIFY_RIGHT:
1549       alignment = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
1550       break;
1551
1552     case GTK_JUSTIFY_CENTER:
1553       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1554       break;
1555
1556     case GTK_JUSTIFY_FILL:
1557       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1558       break;
1559     }
1560
1561   gtk_widget_push_composite_child ();
1562   label = gtk_label_new (clist->column[column].title);
1563   gtk_widget_pop_composite_child ();
1564   gtk_container_add (GTK_CONTAINER (alignment), label);
1565   gtk_container_add (GTK_CONTAINER (clist->column[column].button), alignment);
1566   gtk_widget_show (label);
1567   gtk_widget_show (alignment);
1568
1569   /* if this button didn't previously exist, then the
1570    * column button positions have to be re-computed */
1571   if (gtk_widget_get_visible (GTK_WIDGET(clist)) && new_button)
1572     size_allocate_title_buttons (clist);
1573 }
1574
1575 gchar *
1576 gtk_cmclist_get_column_title (GtkCMCList *clist,
1577                             gint      column)
1578 {
1579   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1580
1581   if (column < 0 || column >= clist->columns)
1582     return NULL;
1583
1584   return clist->column[column].title;
1585 }
1586
1587 void
1588 gtk_cmclist_set_column_widget (GtkCMCList  *clist,
1589                              gint       column,
1590                              GtkWidget *widget)
1591 {
1592   gint new_button = 0;
1593   GtkWidget *old_widget;
1594
1595   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1596
1597   if (column < 0 || column >= clist->columns)
1598     return;
1599
1600   /* if the column button doesn't currently exist,
1601    * it has to be created first */
1602   if (!clist->column[column].button)
1603     {
1604       column_button_create (clist, column);
1605       new_button = 1;
1606     }
1607
1608   column_title_new (clist, column, NULL);
1609
1610   /* remove and destroy the old widget */
1611   old_widget = gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1612   if (old_widget)
1613     gtk_container_remove (GTK_CONTAINER (clist->column[column].button),
1614                           old_widget);
1615
1616   /* add and show the widget */
1617   if (widget)
1618     {
1619       gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget);
1620       gtk_widget_show (widget);
1621     }
1622
1623   /* if this button didn't previously exist, then the
1624    * column button positions have to be re-computed */
1625   if (gtk_widget_get_visible (GTK_WIDGET(clist)) && new_button)
1626     size_allocate_title_buttons (clist);
1627 }
1628
1629 GtkWidget *
1630 gtk_cmclist_get_column_widget (GtkCMCList *clist,
1631                              gint      column)
1632 {
1633   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1634
1635   if (column < 0 || column >= clist->columns)
1636     return NULL;
1637
1638   if (clist->column[column].button)
1639         return gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1640
1641   return NULL;
1642 }
1643
1644 void
1645 gtk_cmclist_set_column_justification (GtkCMCList         *clist,
1646                                     gint              column,
1647                                     GtkJustification  justification)
1648 {
1649   GtkWidget *alignment;
1650
1651   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1652
1653   if (column < 0 || column >= clist->columns)
1654     return;
1655
1656   clist->column[column].justification = justification;
1657
1658   /* change the alinment of the button title if it's not a
1659    * custom widget */
1660   if (clist->column[column].title)
1661     {
1662       alignment = gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1663
1664       switch (clist->column[column].justification)
1665         {
1666         case GTK_JUSTIFY_LEFT:
1667           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.0, 0.5, 0.0, 0.0);
1668           break;
1669
1670         case GTK_JUSTIFY_RIGHT:
1671           gtk_alignment_set (GTK_ALIGNMENT (alignment), 1.0, 0.5, 0.0, 0.0);
1672           break;
1673
1674         case GTK_JUSTIFY_CENTER:
1675           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1676           break;
1677
1678         case GTK_JUSTIFY_FILL:
1679           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1680           break;
1681
1682         default:
1683           break;
1684         }
1685     }
1686
1687   if (CLIST_UNFROZEN (clist))
1688     draw_rows (clist, NULL);
1689 }
1690
1691 void
1692 gtk_cmclist_set_column_visibility (GtkCMCList *clist,
1693                                  gint      column,
1694                                  gboolean  visible)
1695 {
1696   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1697
1698   if (column < 0 || column >= clist->columns)
1699     return;
1700   if (clist->column[column].visible == visible)
1701     return;
1702
1703   /* don't hide last visible column */
1704   if (!visible)
1705     {
1706       gint i;
1707       gint vis_columns = 0;
1708
1709       for (i = 0, vis_columns = 0; i < clist->columns && vis_columns < 2; i++)
1710         if (clist->column[i].visible)
1711           vis_columns++;
1712
1713       if (vis_columns < 2)
1714         return;
1715     }
1716
1717   clist->column[column].visible = visible;
1718
1719   if (clist->column[column].button)
1720     {
1721       if (visible)
1722         gtk_widget_show (clist->column[column].button);
1723       else
1724         gtk_widget_hide (clist->column[column].button);
1725     }
1726   
1727   gtk_widget_queue_resize (GTK_WIDGET(clist));
1728 }
1729
1730 void
1731 gtk_cmclist_set_column_resizeable (GtkCMCList *clist,
1732                                  gint      column,
1733                                  gboolean  resizeable)
1734 {
1735   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1736
1737   if (column < 0 || column >= clist->columns)
1738     return;
1739   if (clist->column[column].resizeable == resizeable)
1740     return;
1741
1742   clist->column[column].resizeable = resizeable;
1743   if (resizeable)
1744     clist->column[column].auto_resize = FALSE;
1745
1746   if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1747     size_allocate_title_buttons (clist);
1748 }
1749
1750 void
1751 gtk_cmclist_set_column_auto_resize (GtkCMCList *clist,
1752                                   gint      column,
1753                                   gboolean  auto_resize)
1754 {
1755   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1756
1757   if (column < 0 || column >= clist->columns)
1758     return;
1759   if (clist->column[column].auto_resize == auto_resize)
1760     return;
1761
1762   clist->column[column].auto_resize = auto_resize;
1763   if (auto_resize)
1764     {
1765       clist->column[column].resizeable = FALSE;
1766       if (!GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1767         {
1768           gint width;
1769
1770           width = gtk_cmclist_optimal_column_width (clist, column);
1771           gtk_cmclist_set_column_width (clist, column, width);
1772         }
1773     }
1774
1775   if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1776     size_allocate_title_buttons (clist);
1777 }
1778
1779 gint
1780 gtk_cmclist_columns_autosize (GtkCMCList *clist)
1781 {
1782   gint i;
1783   gint width;
1784
1785   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
1786
1787   gtk_cmclist_freeze (clist);
1788   width = 0;
1789   for (i = 0; i < clist->columns; i++)
1790     {
1791       gtk_cmclist_set_column_width (clist, i,
1792                                   gtk_cmclist_optimal_column_width (clist, i));
1793
1794       width += clist->column[i].width;
1795     }
1796
1797   gtk_cmclist_thaw (clist);
1798   return width;
1799 }
1800
1801 gint
1802 gtk_cmclist_optimal_column_width (GtkCMCList *clist,
1803                                 gint      column)
1804 {
1805   GtkRequisition requisition;
1806   GList *list;
1807   gint width;
1808
1809   cm_return_val_if_fail (GTK_CMCLIST (clist), 0);
1810
1811   if (column < 0 || column >= clist->columns)
1812     return 0;
1813
1814   if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[column].button)
1815     {
1816       gtk_widget_get_requisition (clist->column[column].button, &requisition);
1817       width = requisition.width
1818 #if 0
1819              (CELL_SPACING + (2 * COLUMN_INSET)))
1820 #endif
1821                 ;
1822     }
1823   else
1824     width = 0;
1825
1826   for (list = clist->row_list; list; list = list->next)
1827     {
1828   GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
1829         (clist, GTK_CMCLIST_ROW (list), column, &requisition);
1830       width = MAX (width, requisition.width);
1831     }
1832
1833   return width;
1834 }
1835
1836 void
1837 gtk_cmclist_set_column_width (GtkCMCList *clist,
1838                             gint      column,
1839                             gint      width)
1840 {
1841   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1842
1843   if (column < 0 || column >= clist->columns)
1844     return;
1845
1846   g_signal_emit (G_OBJECT (clist), clist_signals[RESIZE_COLUMN], 0,
1847                    column, width);
1848 }
1849
1850 void
1851 gtk_cmclist_set_column_min_width (GtkCMCList *clist,
1852                                 gint      column,
1853                                 gint      min_width)
1854 {
1855   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1856
1857   if (column < 0 || column >= clist->columns)
1858     return;
1859   if (clist->column[column].min_width == min_width)
1860     return;
1861
1862   if (clist->column[column].max_width >= 0  &&
1863       clist->column[column].max_width < min_width)
1864     clist->column[column].min_width = clist->column[column].max_width;
1865   else
1866     clist->column[column].min_width = min_width;
1867
1868   if (clist->column[column].area.width < clist->column[column].min_width)
1869     gtk_cmclist_set_column_width (clist, column,clist->column[column].min_width);
1870 }
1871
1872 void
1873 gtk_cmclist_set_column_max_width (GtkCMCList *clist,
1874                                 gint      column,
1875                                 gint      max_width)
1876 {
1877   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1878
1879   if (column < 0 || column >= clist->columns)
1880     return;
1881   if (clist->column[column].max_width == max_width)
1882     return;
1883
1884   if (clist->column[column].min_width >= 0 && max_width >= 0 &&
1885       clist->column[column].min_width > max_width)
1886     clist->column[column].max_width = clist->column[column].min_width;
1887   else
1888     clist->column[column].max_width = max_width;
1889   
1890   if (clist->column[column].area.width > clist->column[column].max_width)
1891     gtk_cmclist_set_column_width (clist, column,clist->column[column].max_width);
1892 }
1893
1894 /* PRIVATE COLUMN FUNCTIONS
1895  *   column_auto_resize
1896  *   real_resize_column
1897  *   abort_column_resize
1898  *   size_allocate_title_buttons
1899  *   size_allocate_columns
1900  *   list_requisition_width
1901  *   new_column_width
1902  *   column_button_create
1903  *   column_button_clicked
1904  *   column_title_passive_func
1905  */
1906 static void
1907 column_auto_resize (GtkCMCList    *clist,
1908                     GtkCMCListRow *clist_row,
1909                     gint         column,
1910                     gint         old_width)
1911 {
1912   /* resize column if needed for auto_resize */
1913   GtkRequisition requisition;
1914
1915   if (!clist->column[column].auto_resize ||
1916       GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1917     return;
1918
1919   if (clist_row)
1920     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
1921                                                    column, &requisition);
1922   else
1923     requisition.width = 0;
1924
1925   if (requisition.width > clist->column[column].width)
1926     gtk_cmclist_set_column_width (clist, column, requisition.width);
1927   else if (requisition.width < old_width &&
1928            old_width == clist->column[column].width)
1929     {
1930       GList *list;
1931       gint new_width = 0;
1932
1933       /* run a "gtk_cmclist_optimal_column_width" but break, if
1934        * the column doesn't shrink */
1935       if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[column].button)
1936     {
1937         gtk_widget_get_requisition (clist->column[column].button, &requisition);
1938         new_width = (requisition.width -
1939                      (CELL_SPACING + (2 * COLUMN_INSET)));
1940     }
1941       else
1942         new_width = 0;
1943
1944       for (list = clist->row_list; list; list = list->next)
1945         {
1946           GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
1947             (clist, GTK_CMCLIST_ROW (list), column, &requisition);
1948           new_width = MAX (new_width, requisition.width);
1949           if (new_width == clist->column[column].width)
1950             break;
1951         }
1952       if (new_width < clist->column[column].width)
1953         gtk_cmclist_set_column_width
1954           (clist, column, MAX (new_width, clist->column[column].min_width));
1955     }
1956 }
1957
1958 static void
1959 real_resize_column (GtkCMCList *clist,
1960                     gint      column,
1961                     gint      width)
1962 {
1963   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1964
1965   if (column < 0 || column >= clist->columns)
1966     return;
1967   
1968   if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
1969     width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
1970   if (clist->column[column].max_width >= 0 &&
1971       width > clist->column[column].max_width)
1972     width = clist->column[column].max_width;
1973
1974   clist->column[column].width = width;
1975   clist->column[column].width_set = TRUE;
1976
1977   /* FIXME: this is quite expensive to do if the widget hasn't
1978    *        been size_allocated yet, and pointless. Should
1979    *        a flag be kept
1980    */
1981   size_allocate_columns (clist, TRUE);
1982   size_allocate_title_buttons (clist);
1983
1984   CLIST_REFRESH (clist);
1985 }
1986
1987 static void
1988 abort_column_resize (GtkCMCList *clist)
1989 {
1990   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1991
1992   if (!GTK_CMCLIST_IN_DRAG(clist))
1993     return;
1994
1995   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_IN_DRAG);
1996   gtk_grab_remove (GTK_WIDGET (clist));
1997   gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (clist)),
1998                               GDK_CURRENT_TIME);
1999   clist->drag_pos = -1;
2000
2001   if (clist->x_drag >= 0 && clist->x_drag <= clist->clist_window_width - 1)
2002     clist_refresh(clist);
2003 }
2004
2005 static void
2006 size_allocate_title_buttons (GtkCMCList *clist)
2007 {
2008   GtkAllocation button_allocation;
2009   gint last_column;
2010   gint last_button = 0;
2011   gint i;
2012
2013   if (!gtk_widget_get_realized (GTK_WIDGET(clist)))
2014     return;
2015
2016   button_allocation.x = clist->hoffset;
2017   button_allocation.y = 0;
2018   button_allocation.width = 0;
2019   button_allocation.height = clist->column_title_area.height;
2020
2021   /* find last visible column */
2022   for (last_column = clist->columns - 1; last_column >= 0; last_column--)
2023     if (clist->column[last_column].visible)
2024       break;
2025
2026   for (i = 0; i < last_column; i++)
2027     {
2028       if (!clist->column[i].visible)
2029         {
2030           last_button = i + 1;
2031           gdk_window_hide (clist->column[i].window);
2032           continue;
2033         }
2034
2035       button_allocation.width += (clist->column[i].area.width +
2036                                   CELL_SPACING + 2 * COLUMN_INSET);
2037
2038       if (!clist->column[i + 1].button)
2039         {
2040           gdk_window_hide (clist->column[i].window);
2041           continue;
2042         }
2043
2044       gtk_widget_size_allocate (clist->column[last_button].button,
2045                                 &button_allocation);
2046       button_allocation.x += button_allocation.width;
2047       button_allocation.width = 0;
2048
2049       if (clist->column[last_button].resizeable)
2050         {
2051           gdk_window_show (clist->column[last_button].window);
2052           gdk_window_move_resize (clist->column[last_button].window,
2053                                   button_allocation.x - (DRAG_WIDTH / 2), 
2054                                   0, DRAG_WIDTH,
2055                                   clist->column_title_area.height);
2056         }
2057       else
2058         gdk_window_hide (clist->column[last_button].window);
2059
2060       last_button = i + 1;
2061     }
2062
2063   button_allocation.width += (clist->column[last_column].area.width +
2064                               2 * (CELL_SPACING + COLUMN_INSET));
2065   gtk_widget_size_allocate (clist->column[last_button].button,
2066                             &button_allocation);
2067
2068   if (clist->column[last_button].resizeable)
2069     {
2070       button_allocation.x += button_allocation.width;
2071
2072       gdk_window_show (clist->column[last_button].window);
2073       gdk_window_move_resize (clist->column[last_button].window,
2074                               button_allocation.x - (DRAG_WIDTH / 2), 
2075                               0, DRAG_WIDTH, clist->column_title_area.height);
2076     }
2077   else
2078     gdk_window_hide (clist->column[last_button].window);
2079 }
2080
2081 static void
2082 size_allocate_columns (GtkCMCList *clist,
2083                        gboolean  block_resize)
2084 {
2085   GtkRequisition requisition;
2086   gint xoffset = CELL_SPACING + COLUMN_INSET;
2087   gint last_column;
2088   gint i;
2089
2090   /* find last visible column and calculate correct column width */
2091   for (last_column = clist->columns - 1;
2092        last_column >= 0 && !clist->column[last_column].visible; last_column--);
2093
2094   if (last_column < 0)
2095     return;
2096
2097   for (i = 0; i <= last_column; i++)
2098     {
2099       if (!clist->column[i].visible)
2100         continue;
2101       clist->column[i].area.x = xoffset;
2102       if (clist->column[i].width_set)
2103         {
2104           if (!block_resize && GTK_CMCLIST_SHOW_TITLES(clist) &&
2105               clist->column[i].auto_resize && clist->column[i].button)
2106             {
2107               gint width;
2108
2109               gtk_widget_get_requisition (clist->column[i].button, &requisition);
2110               width = (requisition.width -
2111                        (CELL_SPACING + (2 * COLUMN_INSET)));
2112
2113               if (width > clist->column[i].width)
2114                 gtk_cmclist_set_column_width (clist, i, width);
2115             }
2116
2117           clist->column[i].area.width = clist->column[i].width;
2118           xoffset += clist->column[i].width + CELL_SPACING + (2* COLUMN_INSET);
2119         }
2120       else if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button)
2121         {
2122           gtk_widget_get_requisition (clist->column[i].button, &requisition);
2123           clist->column[i].area.width =
2124             requisition.width -
2125             (CELL_SPACING + (2 * COLUMN_INSET));
2126           xoffset += requisition.width;
2127         }
2128     }
2129
2130   clist->column[last_column].area.width = clist->column[last_column].area.width
2131     + MAX (0, clist->clist_window_width + COLUMN_INSET - xoffset);
2132 }
2133
2134 static gint
2135 list_requisition_width (GtkCMCList *clist) 
2136 {
2137   GtkRequisition requisition;
2138   gint width = CELL_SPACING;
2139   gint i;
2140
2141   for (i = clist->columns - 1; i >= 0; i--)
2142     {
2143       if (!clist->column[i].visible)
2144         continue;
2145
2146       if (clist->column[i].width_set)
2147         width += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
2148       else if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button)
2149     {
2150         gtk_widget_get_requisition (clist->column[i].button, &requisition);
2151         width += requisition.width;
2152     }
2153     }
2154
2155   return width;
2156 }
2157
2158 /* this function returns the new width of the column being resized given
2159  * the column and x position of the cursor; the x cursor position is passed
2160  * in as a pointer and automagicly corrected if it's beyond min/max limits */
2161 static gint
2162 new_column_width (GtkCMCList *clist,
2163                   gint      column,
2164                   gint     *x)
2165 {
2166   gint xthickness = gtk_widget_get_style (GTK_WIDGET (clist))->xthickness;
2167   gint width;
2168   gint cx;
2169   gint dx;
2170   gint last_column;
2171
2172   /* first translate the x position from widget->window
2173    * to clist->clist_window */
2174   cx = *x - xthickness;
2175
2176   for (last_column = clist->columns - 1;
2177        last_column >= 0 && !clist->column[last_column].visible; last_column--);
2178
2179   /* calculate new column width making sure it doesn't end up
2180    * less than the minimum width */
2181   dx = (COLUMN_LEFT_XPIXEL (clist, column) + COLUMN_INSET +
2182         (column < last_column) * CELL_SPACING);
2183   width = cx - dx;
2184
2185   if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
2186     {
2187       width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
2188       cx = dx + width;
2189       *x = cx + xthickness;
2190     }
2191   else if (clist->column[column].max_width >= COLUMN_MIN_WIDTH &&
2192            width > clist->column[column].max_width)
2193     {
2194       width = clist->column[column].max_width;
2195       cx = dx + clist->column[column].max_width;
2196       *x = cx + xthickness;
2197     }      
2198
2199   if (cx < 0 || cx > clist->clist_window_width)
2200     *x = -1;
2201
2202   return width;
2203 }
2204
2205 static void
2206 column_button_create (GtkCMCList *clist,
2207                       gint      column)
2208 {
2209   GtkWidget *button;
2210
2211   gtk_widget_push_composite_child ();
2212   button = clist->column[column].button = gtk_button_new ();
2213   GtkRcStyle *style = gtk_rc_style_new();
2214   style->ythickness = 0;
2215   gtk_widget_modify_style(clist->column[column].button, style);
2216   g_object_unref(style);
2217   gtk_container_set_border_width(GTK_CONTAINER(button), 0);
2218   gtk_widget_pop_composite_child ();
2219
2220   if (gtk_widget_get_realized (GTK_WIDGET(clist)) && clist->title_window)
2221     gtk_widget_set_parent_window (clist->column[column].button,
2222                                   clist->title_window);
2223   gtk_widget_set_parent (button, GTK_WIDGET (clist));
2224
2225   g_signal_connect (G_OBJECT (button), "clicked",
2226                       G_CALLBACK(column_button_clicked),
2227                       (gpointer) clist);
2228   gtk_widget_show (button);
2229 }
2230
2231 static void
2232 column_button_clicked (GtkWidget *widget,
2233                        gpointer   data)
2234 {
2235   gint i;
2236   GtkCMCList *clist;
2237
2238   cm_return_if_fail (widget != NULL);
2239   cm_return_if_fail (GTK_IS_CMCLIST (data));
2240
2241   clist = GTK_CMCLIST (data);
2242
2243   /* find the column who's button was pressed */
2244   for (i = 0; i < clist->columns; i++)
2245     if (clist->column[i].button == widget)
2246       break;
2247
2248   g_signal_emit (G_OBJECT (clist), clist_signals[CLICK_COLUMN], 0, i);
2249 }
2250
2251 static gint
2252 column_title_passive_func (GtkWidget *widget, 
2253                            GdkEvent  *event,
2254                            gpointer   data)
2255 {
2256   cm_return_val_if_fail (event != NULL, FALSE);
2257   
2258   switch (event->type)
2259     {
2260     case GDK_MOTION_NOTIFY:
2261     case GDK_BUTTON_PRESS:
2262     case GDK_2BUTTON_PRESS:
2263     case GDK_3BUTTON_PRESS:
2264     case GDK_BUTTON_RELEASE:
2265     case GDK_ENTER_NOTIFY:
2266     case GDK_LEAVE_NOTIFY:
2267       return TRUE;
2268     default:
2269       break;
2270     }
2271   return FALSE;
2272 }
2273
2274
2275 /* PUBLIC CELL FUNCTIONS
2276  *   gtk_cmclist_get_cell_type
2277  *   gtk_cmclist_set_text
2278  *   gtk_cmclist_get_text
2279  *   gtk_cmclist_set_pixbuf
2280  *   gtk_cmclist_get_pixbuf
2281  *   gtk_cmclist_set_pixtext
2282  *   gtk_cmclist_get_pixtext
2283  *   gtk_cmclist_set_shift
2284  */
2285 GtkCMCellType 
2286 gtk_cmclist_get_cell_type (GtkCMCList *clist,
2287                          gint      row,
2288                          gint      column)
2289 {
2290   GtkCMCListRow *clist_row;
2291
2292   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2293
2294   if (row < 0 || row >= clist->rows)
2295     return -1;
2296   if (column < 0 || column >= clist->columns)
2297     return -1;
2298
2299   clist_row = ROW_ELEMENT (clist, row)->data;
2300
2301   return clist_row->cell[column].type;
2302 }
2303
2304 void
2305 gtk_cmclist_set_text (GtkCMCList    *clist,
2306                     gint         row,
2307                     gint         column,
2308                     const gchar *text)
2309 {
2310   GtkCMCListRow *clist_row;
2311
2312   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2313
2314   if (row < 0 || row >= clist->rows)
2315     return;
2316   if (column < 0 || column >= clist->columns)
2317     return;
2318
2319   clist_row = ROW_ELEMENT (clist, row)->data;
2320
2321   /* if text is null, then the cell is empty */
2322   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2323     (clist, clist_row, column, GTK_CMCELL_TEXT, text, 0, NULL);
2324
2325   /* redraw the list if it's not frozen */
2326   if (CLIST_UNFROZEN (clist))
2327     {
2328       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2329         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2330     }
2331 }
2332
2333 gint
2334 gtk_cmclist_get_text (GtkCMCList  *clist,
2335                     gint       row,
2336                     gint       column,
2337                     gchar    **text)
2338 {
2339   GtkCMCListRow *clist_row;
2340
2341   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
2342
2343   if (row < 0 || row >= clist->rows)
2344     return 0;
2345   if (column < 0 || column >= clist->columns)
2346     return 0;
2347
2348   clist_row = ROW_ELEMENT (clist, row)->data;
2349
2350   if (clist_row->cell[column].type != GTK_CMCELL_TEXT)
2351     return 0;
2352
2353   if (text)
2354     *text = GTK_CMCELL_TEXT (clist_row->cell[column])->text;
2355
2356   return 1;
2357 }
2358
2359 void
2360 gtk_cmclist_set_pixbuf (GtkCMCList  *clist,
2361                       gint       row,
2362                       gint       column,
2363                       GdkPixbuf *pixbuf)
2364 {
2365   GtkCMCListRow *clist_row;
2366
2367   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2368
2369   if (row < 0 || row >= clist->rows)
2370     return;
2371   if (column < 0 || column >= clist->columns)
2372     return;
2373
2374   clist_row = ROW_ELEMENT (clist, row)->data;
2375   
2376   g_object_ref (pixbuf);
2377   
2378   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2379     (clist, clist_row, column, GTK_CMCELL_PIXBUF, NULL, 0, pixbuf);
2380
2381   /* redraw the list if it's not frozen */
2382   if (CLIST_UNFROZEN (clist))
2383     {
2384       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2385         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2386     }
2387 }
2388
2389 gint
2390 gtk_cmclist_get_pixbuf (GtkCMCList   *clist,
2391                       gint        row,
2392                       gint        column,
2393                       GdkPixbuf **pixbuf)
2394 {
2395   GtkCMCListRow *clist_row;
2396
2397   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
2398
2399   if (row < 0 || row >= clist->rows)
2400     return 0;
2401   if (column < 0 || column >= clist->columns)
2402     return 0;
2403
2404   clist_row = ROW_ELEMENT (clist, row)->data;
2405
2406   if (clist_row->cell[column].type != GTK_CMCELL_PIXBUF)
2407     return 0;
2408
2409   if (pixbuf)
2410   {
2411     *pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf;
2412   }
2413
2414   return 1;
2415 }
2416
2417 void
2418 gtk_cmclist_set_pixtext (GtkCMCList    *clist,
2419                        gint         row,
2420                        gint         column,
2421                        const gchar *text,
2422                        guint8       spacing,
2423                        GdkPixbuf   *pixbuf)
2424 {
2425   GtkCMCListRow *clist_row;
2426
2427   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2428
2429   if (row < 0 || row >= clist->rows)
2430     return;
2431   if (column < 0 || column >= clist->columns)
2432     return;
2433
2434   clist_row = ROW_ELEMENT (clist, row)->data;
2435   
2436   g_object_ref (pixbuf);
2437   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2438     (clist, clist_row, column, GTK_CMCELL_PIXTEXT, text, spacing, pixbuf);
2439
2440   /* redraw the list if it's not frozen */
2441   if (CLIST_UNFROZEN (clist))
2442     {
2443       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2444         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2445     }
2446 }
2447
2448 gint
2449 gtk_cmclist_get_pixtext (GtkCMCList   *clist,
2450                        gint        row,
2451                        gint        column,
2452                        gchar     **text,
2453                        guint8     *spacing,
2454                        GdkPixbuf **pixbuf)
2455 {
2456   GtkCMCListRow *clist_row;
2457
2458   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
2459
2460   if (row < 0 || row >= clist->rows)
2461     return 0;
2462   if (column < 0 || column >= clist->columns)
2463     return 0;
2464
2465   clist_row = ROW_ELEMENT (clist, row)->data;
2466
2467   if (clist_row->cell[column].type != GTK_CMCELL_PIXTEXT)
2468     return 0;
2469
2470   if (text)
2471     *text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text;
2472   if (spacing)
2473     *spacing = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing;
2474   if (pixbuf)
2475     *pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf;
2476
2477   return 1;
2478 }
2479
2480 void
2481 gtk_cmclist_set_shift (GtkCMCList *clist,
2482                      gint      row,
2483                      gint      column,
2484                      gint      vertical,
2485                      gint      horizontal)
2486 {
2487   GtkRequisition requisition = { 0 };
2488   GtkCMCListRow *clist_row;
2489
2490   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2491
2492   if (row < 0 || row >= clist->rows)
2493     return;
2494   if (column < 0 || column >= clist->columns)
2495     return;
2496
2497   clist_row = ROW_ELEMENT (clist, row)->data;
2498
2499   if (clist->column[column].auto_resize &&
2500       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2501     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2502                                                    column, &requisition);
2503
2504   clist_row->cell[column].vertical = vertical;
2505   clist_row->cell[column].horizontal = horizontal;
2506
2507   column_auto_resize (clist, clist_row, column, requisition.width);
2508
2509   if (CLIST_UNFROZEN (clist) && gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2510     GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2511 }
2512
2513 /* PRIVATE CELL FUNCTIONS
2514  *   set_cell_contents
2515  *   cell_size_request
2516  */
2517 static void
2518 set_cell_contents (GtkCMCList    *clist,
2519                    GtkCMCListRow *clist_row,
2520                    gint         column,
2521                    GtkCMCellType  type,
2522                    const gchar *text,
2523                    guint8       spacing,
2524                    GdkPixbuf   *pixbuf)
2525 {
2526   GtkRequisition requisition;
2527   gchar *old_text = NULL;
2528   GdkPixbuf *old_pixbuf = NULL;
2529   
2530   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2531   cm_return_if_fail (clist_row != NULL);
2532
2533   if (clist->column[column].auto_resize &&
2534       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2535     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2536                                                    column, &requisition);
2537
2538   switch (clist_row->cell[column].type)
2539     {
2540     case GTK_CMCELL_EMPTY:
2541       break;
2542     case GTK_CMCELL_TEXT:
2543       old_text = GTK_CMCELL_TEXT (clist_row->cell[column])->text;
2544       break;
2545     case GTK_CMCELL_PIXBUF:
2546       old_pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf;
2547       break;
2548     case GTK_CMCELL_PIXTEXT:
2549       old_text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text;
2550       old_pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf;
2551       break;
2552     case GTK_CMCELL_WIDGET:
2553       /* unimplemented */
2554       break;
2555     default:
2556       break;
2557     }
2558
2559   clist_row->cell[column].type = GTK_CMCELL_EMPTY;
2560
2561   /* Note that pixbuf and mask were already ref'ed by the caller
2562    */
2563   switch (type)
2564     {
2565     case GTK_CMCELL_TEXT:
2566       if (text)
2567         {
2568           clist_row->cell[column].type = GTK_CMCELL_TEXT;
2569           GTK_CMCELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
2570         }
2571       break;
2572     case GTK_CMCELL_PIXBUF:
2573       if (pixbuf)
2574         {
2575           clist_row->cell[column].type = GTK_CMCELL_PIXBUF;
2576           GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf = pixbuf;
2577         }
2578       break;
2579     case GTK_CMCELL_PIXTEXT:
2580       if (text && pixbuf)
2581         {
2582           clist_row->cell[column].type = GTK_CMCELL_PIXTEXT;
2583           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
2584           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
2585           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf;
2586         }
2587       break;
2588     default:
2589       break;
2590     }
2591
2592   if (clist->column[column].auto_resize &&
2593       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2594     column_auto_resize (clist, clist_row, column, requisition.width);
2595
2596   g_free (old_text);
2597   if (old_pixbuf)
2598     g_object_unref (old_pixbuf);
2599 }
2600
2601 PangoLayout *
2602 _gtk_cmclist_create_cell_layout (GtkCMCList       *clist,
2603                                GtkCMCListRow    *clist_row,
2604                                gint            column)
2605 {
2606   PangoLayout *layout;
2607   GtkStyle *style;
2608   GtkCMCell *cell;
2609   gchar *text;
2610   
2611   get_cell_style (clist, clist_row, GTK_STATE_NORMAL, column, &style);
2612
2613
2614   cell = &clist_row->cell[column];
2615   switch (cell->type)
2616     {
2617     case GTK_CMCELL_TEXT:
2618     case GTK_CMCELL_PIXTEXT:
2619       text = ((cell->type == GTK_CMCELL_PIXTEXT) ?
2620               GTK_CMCELL_PIXTEXT (*cell)->text :
2621               GTK_CMCELL_TEXT (*cell)->text);
2622
2623       if (!text)
2624         return NULL;
2625       
2626       layout = gtk_widget_create_pango_layout (GTK_WIDGET (clist),
2627                                                ((cell->type == GTK_CMCELL_PIXTEXT) ?
2628                                                 GTK_CMCELL_PIXTEXT (*cell)->text :
2629                                                 GTK_CMCELL_TEXT (*cell)->text));
2630       pango_layout_set_font_description (layout, style->font_desc);
2631       
2632       return layout;
2633       
2634     default:
2635       return NULL;
2636     }
2637 }
2638
2639 static void
2640 cell_size_request (GtkCMCList       *clist,
2641                    GtkCMCListRow    *clist_row,
2642                    gint            column,
2643                    GtkRequisition *requisition)
2644 {
2645   gint width;
2646   gint height;
2647   PangoLayout *layout;
2648   PangoRectangle logical_rect;
2649
2650   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2651   cm_return_if_fail (requisition != NULL);
2652
2653   layout = _gtk_cmclist_create_cell_layout (clist, clist_row, column);
2654   if (layout)
2655     {
2656       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2657       
2658       requisition->width = logical_rect.width;
2659       requisition->height = logical_rect.height;
2660       
2661       g_object_unref (G_OBJECT (layout));
2662     }
2663   else
2664     {
2665       requisition->width  = 0;
2666       requisition->height = 0;
2667     }
2668
2669   if (layout && clist_row->cell[column].type == GTK_CMCELL_PIXTEXT)
2670     requisition->width += GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing;
2671
2672   switch (clist_row->cell[column].type)
2673     {
2674     case GTK_CMCELL_PIXTEXT:
2675       width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2676       height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2677       requisition->width += width;
2678       requisition->height = MAX (requisition->height, height);      
2679       break;
2680     case GTK_CMCELL_PIXBUF:
2681       width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2682       height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2683       requisition->width += width;
2684       requisition->height = MAX (requisition->height, height);
2685       break;
2686       
2687     default:
2688       break;
2689     }
2690
2691   requisition->width  += clist_row->cell[column].horizontal;
2692   requisition->height += clist_row->cell[column].vertical;
2693 }
2694
2695 /* PUBLIC INSERT/REMOVE ROW FUNCTIONS
2696  *   gtk_cmclist_prepend
2697  *   gtk_cmclist_append
2698  *   gtk_cmclist_insert
2699  *   gtk_cmclist_remove
2700  *   gtk_cmclist_clear
2701  */
2702 gint
2703 gtk_cmclist_prepend (GtkCMCList    *clist,
2704                    gchar       *text[])
2705 {
2706   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2707   cm_return_val_if_fail (text != NULL, -1);
2708
2709   return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, 0, text);
2710 }
2711
2712 gint
2713 gtk_cmclist_append (GtkCMCList    *clist,
2714                   gchar       *text[])
2715 {
2716   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2717   cm_return_val_if_fail (text != NULL, -1);
2718
2719   return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, clist->rows, text);
2720 }
2721
2722 gint
2723 gtk_cmclist_insert (GtkCMCList    *clist,
2724                   gint         row,
2725                   gchar       *text[])
2726 {
2727   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2728   cm_return_val_if_fail (text != NULL, -1);
2729
2730   if (row < 0 || row > clist->rows)
2731     row = clist->rows;
2732
2733   return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, row, text);
2734 }
2735
2736 void
2737 gtk_cmclist_remove (GtkCMCList *clist,
2738                   gint      row)
2739 {
2740   GTK_CMCLIST_GET_CLASS (clist)->remove_row (clist, row);
2741 }
2742
2743 void
2744 gtk_cmclist_clear (GtkCMCList *clist)
2745 {
2746   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2747   
2748   GTK_CMCLIST_GET_CLASS (clist)->clear (clist);
2749 }
2750
2751 /* PRIVATE INSERT/REMOVE ROW FUNCTIONS
2752  *   real_insert_row
2753  *   real_remove_row
2754  *   real_clear
2755  *   real_row_move
2756  */
2757 static gint
2758 real_insert_row (GtkCMCList *clist,
2759                  gint      row,
2760                  gchar    *text[])
2761 {
2762   gint i;
2763   GtkCMCListRow *clist_row;
2764
2765   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2766   cm_return_val_if_fail (text != NULL, -1);
2767
2768   /* return if out of bounds */
2769   if (row < 0 || row > clist->rows)
2770     return -1;
2771
2772   /* create the row */
2773   clist_row = row_new (clist);
2774
2775   /* set the text in the row's columns */
2776   for (i = 0; i < clist->columns; i++)
2777     if (text[i])
2778       GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2779         (clist, clist_row, i, GTK_CMCELL_TEXT, text[i], 0, NULL);
2780
2781   if (!clist->rows)
2782     {
2783       clist->row_list = g_list_append (clist->row_list, clist_row);
2784       clist->row_list_end = clist->row_list;
2785     }
2786   else
2787     {
2788       if (GTK_CMCLIST_AUTO_SORT(clist))   /* override insertion pos */
2789         {
2790           GList *work;
2791           
2792           row = 0;
2793           work = clist->row_list;
2794           
2795           if (clist->sort_type == GTK_SORT_ASCENDING)
2796             {
2797               while (row < clist->rows &&
2798                      clist->compare (clist, clist_row,
2799                                      GTK_CMCLIST_ROW (work)) > 0)
2800                 {
2801                   row++;
2802                   work = work->next;
2803                 }
2804             }
2805           else
2806             {
2807               while (row < clist->rows &&
2808                      clist->compare (clist, clist_row,
2809                                      GTK_CMCLIST_ROW (work)) < 0)
2810                 {
2811                   row++;
2812                   work = work->next;
2813                 }
2814             }
2815         }
2816       
2817       /* reset the row end pointer if we're inserting at the end of the list */
2818       if (row == clist->rows)
2819         clist->row_list_end = (g_list_append (clist->row_list_end,
2820                                               clist_row))->next;
2821       else
2822         clist->row_list = g_list_insert (clist->row_list, clist_row, row);
2823
2824     }
2825   clist->rows++;
2826
2827   if (row < ROW_FROM_YPIXEL (clist, 0))
2828     clist->voffset -= (clist->row_height + CELL_SPACING);
2829
2830   /* syncronize the selection list */
2831   sync_selection (clist, row, SYNC_INSERT);
2832
2833   if (clist->rows == 1)
2834     {
2835       clist->focus_row = 0;
2836       if (clist->selection_mode == GTK_SELECTION_BROWSE)
2837         gtk_cmclist_select_row (clist, 0, -1);
2838     }
2839
2840   /* redraw the list if it isn't frozen */
2841   if (CLIST_UNFROZEN (clist))
2842     {
2843       adjust_adjustments (clist, FALSE);
2844
2845       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2846         draw_rows (clist, NULL);
2847     }
2848
2849   return row;
2850 }
2851
2852 static void
2853 real_remove_row (GtkCMCList *clist,
2854                  gint      row)
2855 {
2856   gint was_visible;
2857   GList *list;
2858   GtkCMCListRow *clist_row;
2859
2860   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2861
2862   /* return if out of bounds */
2863   if (row < 0 || row > (clist->rows - 1))
2864     return;
2865
2866   was_visible = (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE);
2867
2868   /* get the row we're going to delete */
2869   list = ROW_ELEMENT (clist, row);
2870   g_assert (list != NULL);
2871   clist_row = list->data;
2872
2873   /* if we're removing a selected row, we have to make sure
2874    * it's properly unselected, and then sync up the clist->selected
2875    * list to reflect the deincrimented indexies of rows after the
2876    * removal */
2877   if (clist_row->state == GTK_STATE_SELECTED)
2878     g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
2879                      row, -1, NULL);
2880
2881   sync_selection (clist, row, SYNC_REMOVE);
2882
2883   /* reset the row end pointer if we're removing at the end of the list */
2884   clist->rows--;
2885   if (clist->row_list == list)
2886     clist->row_list = g_list_next (list);
2887   if (clist->row_list_end == list)
2888     clist->row_list_end = g_list_previous (list);
2889   list = g_list_remove (list, clist_row);
2890
2891   if (row < ROW_FROM_YPIXEL (clist, 0))
2892     clist->voffset += clist->row_height + CELL_SPACING;
2893
2894   if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection &&
2895       clist->focus_row >= 0)
2896     g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
2897                      clist->focus_row, -1, NULL);
2898
2899   /* toast the row */
2900   row_delete (clist, clist_row);
2901
2902   /* redraw the row if it isn't frozen */
2903   if (CLIST_UNFROZEN (clist))
2904     {
2905       adjust_adjustments (clist, FALSE);
2906
2907       if (was_visible)
2908         draw_rows (clist, NULL);
2909     }
2910 }
2911
2912 static void
2913 real_clear (GtkCMCList *clist)
2914 {
2915   GList *list;
2916   GList *free_list;
2917   GtkRequisition requisition;
2918   gint i;
2919
2920   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2921
2922   /* free up the selection list */
2923   g_list_free (clist->selection);
2924   g_list_free (clist->undo_selection);
2925   g_list_free (clist->undo_unselection);
2926
2927   clist->selection = NULL;
2928   clist->selection_end = NULL;
2929   clist->undo_selection = NULL;
2930   clist->undo_unselection = NULL;
2931   clist->voffset = 0;
2932   clist->focus_row = -1;
2933   clist->anchor = -1;
2934   clist->undo_anchor = -1;
2935   clist->anchor_state = GTK_STATE_SELECTED;
2936   clist->drag_pos = -1;
2937
2938   /* remove all the rows */
2939   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
2940   free_list = clist->row_list;
2941   clist->row_list = NULL;
2942   clist->row_list_end = NULL;
2943   clist->rows = 0;
2944   for (list = free_list; list; list = list->next)
2945     row_delete (clist, GTK_CMCLIST_ROW (list));
2946   g_list_free (free_list);
2947   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
2948   for (i = 0; i < clist->columns; i++)
2949     if (clist->column[i].auto_resize)
2950       {
2951         if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button)
2952     {
2953         gtk_widget_get_requisition (clist->column[i].button, &requisition);
2954           gtk_cmclist_set_column_width
2955             (clist, i, (requisition.width -
2956                         (CELL_SPACING + (2 * COLUMN_INSET))));
2957     }
2958         else
2959           gtk_cmclist_set_column_width (clist, i, 0);
2960       }
2961   /* zero-out the scrollbars */
2962   if (clist->vadjustment)
2963     {
2964       gtk_adjustment_set_value (clist->vadjustment, 0.0);
2965       CLIST_REFRESH (clist);
2966     }
2967   else
2968     gtk_widget_queue_resize (GTK_WIDGET (clist));
2969 }
2970
2971 static void
2972 real_row_move (GtkCMCList *clist,
2973                gint      source_row,
2974                gint      dest_row)
2975 {
2976   GtkCMCListRow *clist_row;
2977   GList *list;
2978   gint first, last;
2979   gint d;
2980
2981   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2982
2983   if (GTK_CMCLIST_AUTO_SORT(clist))
2984     return;
2985
2986   if (source_row < 0 || source_row >= clist->rows ||
2987       dest_row   < 0 || dest_row   >= clist->rows ||
2988       source_row == dest_row)
2989     return;
2990
2991   gtk_cmclist_freeze (clist);
2992
2993   /* unlink source row */
2994   clist_row = ROW_ELEMENT (clist, source_row)->data;
2995   if (source_row == clist->rows - 1)
2996     clist->row_list_end = clist->row_list_end->prev;
2997   clist->row_list = g_list_remove (clist->row_list, clist_row);
2998   clist->rows--;
2999
3000   /* relink source row */
3001   clist->row_list = g_list_insert (clist->row_list, clist_row, dest_row);
3002   if (dest_row == clist->rows)
3003     clist->row_list_end = clist->row_list_end->next;
3004   clist->rows++;
3005
3006   /* sync selection */
3007   if (source_row > dest_row)
3008     {
3009       first = dest_row;
3010       last  = source_row;
3011       d = 1;
3012     }
3013   else
3014     {
3015       first = source_row;
3016       last  = dest_row;
3017       d = -1;
3018     }
3019
3020   for (list = clist->selection; list; list = list->next)
3021     {
3022       if (list->data == GINT_TO_POINTER (source_row))
3023         list->data = GINT_TO_POINTER (dest_row);
3024       else if (first <= GPOINTER_TO_INT (list->data) &&
3025                last >= GPOINTER_TO_INT (list->data))
3026         list->data = GINT_TO_POINTER (GPOINTER_TO_INT (list->data) + d);
3027     }
3028   
3029   if (clist->focus_row == source_row)
3030     clist->focus_row = dest_row;
3031   else if (clist->focus_row > first)
3032     clist->focus_row += d;
3033
3034   gtk_cmclist_thaw (clist);
3035 }
3036
3037 /* PUBLIC ROW FUNCTIONS
3038  *   gtk_cmclist_moveto
3039  *   gtk_cmclist_set_row_height
3040  *   gtk_cmclist_set_row_data
3041  *   gtk_cmclist_set_row_data_full
3042  *   gtk_cmclist_get_row_data
3043  *   gtk_cmclist_find_row_from_data
3044  *   gtk_cmclist_swap_rows
3045  *   gtk_cmclist_row_move
3046  *   gtk_cmclist_row_is_visible
3047  *   gtk_cmclist_set_foreground
3048  *   gtk_cmclist_set_background
3049  */
3050 void
3051 gtk_cmclist_moveto (GtkCMCList *clist,
3052                   gint      row,
3053                   gint      column,
3054                   gfloat    row_align,
3055                   gfloat    col_align)
3056 {
3057   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3058
3059   if (row < -1 || row >= clist->rows)
3060     return;
3061   if (column < -1 || column >= clist->columns)
3062     return;
3063
3064   row_align = CLAMP (row_align, 0, 1);
3065   col_align = CLAMP (col_align, 0, 1);
3066
3067   /* adjust horizontal scrollbar */
3068   if (clist->hadjustment && column >= 0)
3069     {
3070       gint x;
3071
3072       x = (COLUMN_LEFT (clist, column) - CELL_SPACING - COLUMN_INSET -
3073            (col_align * (clist->clist_window_width - 2 * COLUMN_INSET -
3074                          CELL_SPACING - clist->column[column].area.width)));
3075       if (x < 0)
3076         gtk_adjustment_set_value (clist->hadjustment, 0.0);
3077       else if (x > LIST_WIDTH (clist) - clist->clist_window_width)
3078         gtk_adjustment_set_value 
3079           (clist->hadjustment, LIST_WIDTH (clist) - clist->clist_window_width);
3080       else
3081         gtk_adjustment_set_value (clist->hadjustment, x);
3082     }
3083
3084   /* adjust vertical scrollbar */
3085   if (clist->vadjustment && row >= 0)
3086     move_vertical (clist, row, row_align);
3087 }
3088
3089 void
3090 gtk_cmclist_set_row_height (GtkCMCList *clist,
3091                           guint     height)
3092 {
3093   GtkStyle *style;
3094   GtkWidget *widget;
3095
3096   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3097
3098   widget = GTK_WIDGET (clist);
3099
3100   style = gtk_widget_get_style (widget);
3101
3102   if (height > 0)
3103     {
3104       clist->row_height = height;
3105       GTK_CMCLIST_SET_FLAG (clist, CMCLIST_ROW_HEIGHT_SET);
3106     }
3107   else
3108     {
3109       GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_ROW_HEIGHT_SET);
3110       clist->row_height = 0;
3111     }
3112
3113   if (style->font_desc)
3114     {
3115       PangoContext *context = gtk_widget_get_pango_context (widget);
3116       PangoFontMetrics *metrics;
3117
3118       metrics = pango_context_get_metrics (context,
3119                                            style->font_desc,
3120                                            pango_context_get_language (context));
3121
3122       if (!GTK_CMCLIST_ROW_HEIGHT_SET(clist))
3123         {
3124           clist->row_height = (pango_font_metrics_get_ascent (metrics) +
3125                                pango_font_metrics_get_descent (metrics));
3126           clist->row_height = PANGO_PIXELS (clist->row_height) + 1;
3127         }
3128
3129       pango_font_metrics_unref (metrics);
3130     }
3131
3132   CLIST_REFRESH (clist);
3133 }
3134
3135 void
3136 gtk_cmclist_set_row_data (GtkCMCList *clist,
3137                         gint      row,
3138                         gpointer  data)
3139 {
3140   gtk_cmclist_set_row_data_full (clist, row, data, NULL);
3141 }
3142
3143 void
3144 gtk_cmclist_set_row_data_full (GtkCMCList         *clist,
3145                              gint              row,
3146                              gpointer          data,
3147                              GDestroyNotify  destroy)
3148 {
3149   GtkCMCListRow *clist_row;
3150
3151   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3152
3153   if (row < 0 || row > (clist->rows - 1))
3154     return;
3155
3156   clist_row = ROW_ELEMENT (clist, row)->data;
3157
3158   if (clist_row->destroy)
3159     clist_row->destroy (clist_row->data);
3160   
3161   clist_row->data = data;
3162   clist_row->destroy = destroy;
3163 }
3164
3165 gpointer
3166 gtk_cmclist_get_row_data (GtkCMCList *clist,
3167                         gint      row)
3168 {
3169   GtkCMCListRow *clist_row;
3170
3171   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
3172
3173   if (row < 0 || row > (clist->rows - 1))
3174     return NULL;
3175
3176   clist_row = ROW_ELEMENT (clist, row)->data;
3177   return clist_row->data;
3178 }
3179
3180 gint
3181 gtk_cmclist_find_row_from_data (GtkCMCList *clist,
3182                               gpointer  data)
3183 {
3184   GList *list;
3185   gint n;
3186
3187   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
3188
3189   for (n = 0, list = clist->row_list; list; n++, list = list->next)
3190     if (GTK_CMCLIST_ROW (list)->data == data)
3191       return n;
3192
3193   return -1;
3194 }
3195
3196 void 
3197 gtk_cmclist_swap_rows (GtkCMCList *clist,
3198                      gint      row1, 
3199                      gint      row2)
3200 {
3201   gint first, last;
3202
3203   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3204   cm_return_if_fail (row1 != row2);
3205
3206   if (GTK_CMCLIST_AUTO_SORT(clist))
3207     return;
3208
3209   gtk_cmclist_freeze (clist);
3210
3211   first = MIN (row1, row2);
3212   last  = MAX (row1, row2);
3213
3214   gtk_cmclist_row_move (clist, last, first);
3215   gtk_cmclist_row_move (clist, first + 1, last);
3216   
3217   gtk_cmclist_thaw (clist);
3218 }
3219
3220 void
3221 gtk_cmclist_row_move (GtkCMCList *clist,
3222                     gint      source_row,
3223                     gint      dest_row)
3224 {
3225   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3226
3227   if (GTK_CMCLIST_AUTO_SORT(clist))
3228     return;
3229
3230   if (source_row < 0 || source_row >= clist->rows ||
3231       dest_row   < 0 || dest_row   >= clist->rows ||
3232       source_row == dest_row)
3233     return;
3234
3235   g_signal_emit (G_OBJECT (clist), clist_signals[ROW_MOVE], 0,
3236                    source_row, dest_row);
3237 }
3238
3239 GtkVisibility
3240 gtk_cmclist_row_is_visible (GtkCMCList *clist,
3241                           gint      row)
3242 {
3243   gint top;
3244
3245   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
3246
3247   if (row < 0 || row >= clist->rows)
3248     return GTK_VISIBILITY_NONE;
3249
3250   if (clist->row_height == 0)
3251     return GTK_VISIBILITY_NONE;
3252
3253   if (row < ROW_FROM_YPIXEL (clist, 0))
3254     return GTK_VISIBILITY_NONE;
3255
3256   if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height))
3257     return GTK_VISIBILITY_NONE;
3258
3259   top = ROW_TOP_YPIXEL (clist, row);
3260
3261   if ((top < 0)
3262       || ((top + clist->row_height) >= clist->clist_window_height))
3263     return GTK_VISIBILITY_PARTIAL;
3264
3265   return GTK_VISIBILITY_FULL;
3266 }
3267
3268 void
3269 gtk_cmclist_set_foreground (GtkCMCList       *clist,
3270                           gint            row,
3271                           const GdkColor *color)
3272 {
3273   GtkCMCListRow *clist_row;
3274
3275   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3276
3277   if (row < 0 || row >= clist->rows)
3278     return;
3279
3280   clist_row = ROW_ELEMENT (clist, row)->data;
3281
3282   if (color)
3283     {
3284       clist_row->foreground = *color;
3285       clist_row->fg_set = TRUE;
3286       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3287         gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (clist)),
3288                          &clist_row->foreground, TRUE, TRUE);
3289     }
3290   else
3291     clist_row->fg_set = FALSE;
3292
3293   if (CLIST_UNFROZEN (clist) && gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3294     GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3295 }
3296
3297 void
3298 gtk_cmclist_set_background (GtkCMCList       *clist,
3299                           gint            row,
3300                           const GdkColor *color)
3301 {
3302   GtkCMCListRow *clist_row;
3303
3304   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3305
3306   if (row < 0 || row >= clist->rows)
3307     return;
3308
3309   clist_row = ROW_ELEMENT (clist, row)->data;
3310
3311   if (color)
3312     {
3313       clist_row->background = *color;
3314       clist_row->bg_set = TRUE;
3315       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3316         gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (clist)),
3317                          &clist_row->background, TRUE, TRUE);
3318     }
3319   else
3320     clist_row->bg_set = FALSE;
3321
3322   if (CLIST_UNFROZEN (clist)
3323       && (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3324     GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3325 }
3326
3327 /* PUBLIC ROW/CELL STYLE FUNCTIONS
3328  *   gtk_cmclist_set_cell_style
3329  *   gtk_cmclist_get_cell_style
3330  *   gtk_cmclist_set_row_style
3331  *   gtk_cmclist_get_row_style
3332  */
3333 void
3334 gtk_cmclist_set_cell_style (GtkCMCList *clist,
3335                           gint      row,
3336                           gint      column,
3337                           GtkStyle *style)
3338 {
3339   GtkRequisition requisition = { 0 };
3340   GtkCMCListRow *clist_row;
3341
3342   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3343
3344   if (row < 0 || row >= clist->rows)
3345     return;
3346   if (column < 0 || column >= clist->columns)
3347     return;
3348
3349   clist_row = ROW_ELEMENT (clist, row)->data;
3350
3351   if (clist_row->cell[column].style == style)
3352     return;
3353
3354   if (clist->column[column].auto_resize &&
3355       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3356     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3357                                                    column, &requisition);
3358
3359   if (clist_row->cell[column].style)
3360     {
3361       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3362         gtk_style_detach (clist_row->cell[column].style);
3363       g_object_unref (clist_row->cell[column].style);
3364     }
3365
3366   clist_row->cell[column].style = style;
3367
3368   if (clist_row->cell[column].style)
3369     {
3370       g_object_ref (clist_row->cell[column].style);
3371       
3372       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3373         clist_row->cell[column].style =
3374           gtk_style_attach (clist_row->cell[column].style,
3375                             clist->clist_window);
3376     }
3377
3378   column_auto_resize (clist, clist_row, column, requisition.width);
3379
3380   /* redraw the list if it's not frozen */
3381   if (CLIST_UNFROZEN (clist))
3382     {
3383       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3384         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3385     }
3386 }
3387
3388 GtkStyle *
3389 gtk_cmclist_get_cell_style (GtkCMCList *clist,
3390                           gint      row,
3391                           gint      column)
3392 {
3393   GtkCMCListRow *clist_row;
3394
3395   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
3396
3397   if (row < 0 || row >= clist->rows || column < 0 || column >= clist->columns)
3398     return NULL;
3399
3400   clist_row = ROW_ELEMENT (clist, row)->data;
3401
3402   return clist_row->cell[column].style;
3403 }
3404
3405 void
3406 gtk_cmclist_set_row_style (GtkCMCList *clist,
3407                          gint      row,
3408                          GtkStyle *style)
3409 {
3410   GtkRequisition requisition;
3411   GtkCMCListRow *clist_row;
3412   gint *old_width;
3413   gint i;
3414
3415   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3416
3417   if (row < 0 || row >= clist->rows)
3418     return;
3419
3420   clist_row = ROW_ELEMENT (clist, row)->data;
3421
3422   if (clist_row->style == style)
3423     return;
3424
3425   old_width = g_new (gint, clist->columns);
3426
3427   if (!GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3428     {
3429       for (i = 0; i < clist->columns; i++)
3430         if (clist->column[i].auto_resize)
3431           {
3432             GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3433                                                            i, &requisition);
3434             old_width[i] = requisition.width;
3435           }
3436     }
3437
3438   if (clist_row->style)
3439     {
3440       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3441         gtk_style_detach (clist_row->style);
3442       g_object_unref (clist_row->style);
3443     }
3444
3445   clist_row->style = style;
3446
3447   if (clist_row->style)
3448     {
3449       g_object_ref (clist_row->style);
3450       
3451       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3452         clist_row->style = gtk_style_attach (clist_row->style,
3453                                              clist->clist_window);
3454     }
3455
3456   if (GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3457     for (i = 0; i < clist->columns; i++)
3458       column_auto_resize (clist, clist_row, i, old_width[i]);
3459
3460   g_free (old_width);
3461
3462   /* redraw the list if it's not frozen */
3463   if (CLIST_UNFROZEN (clist))
3464     {
3465       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3466         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3467     }
3468 }
3469
3470 GtkStyle *
3471 gtk_cmclist_get_row_style (GtkCMCList *clist,
3472                          gint      row)
3473 {
3474   GtkCMCListRow *clist_row;
3475
3476   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
3477
3478   if (row < 0 || row >= clist->rows)
3479     return NULL;
3480
3481   clist_row = ROW_ELEMENT (clist, row)->data;
3482
3483   return clist_row->style;
3484 }
3485
3486 /* PUBLIC SELECTION FUNCTIONS
3487  *   gtk_cmclist_set_selectable
3488  *   gtk_cmclist_get_selectable
3489  *   gtk_cmclist_select_row
3490  *   gtk_cmclist_unselect_row
3491  *   gtk_cmclist_select_all
3492  *   gtk_cmclist_unselect_all
3493  *   gtk_cmclist_undo_selection
3494  */
3495 void
3496 gtk_cmclist_set_selectable (GtkCMCList *clist,
3497                           gint      row,
3498                           gboolean  selectable)
3499 {
3500   GtkCMCListRow *clist_row;
3501
3502   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3503
3504   if (row < 0 || row >= clist->rows)
3505     return;
3506
3507   clist_row = ROW_ELEMENT (clist, row)->data;
3508
3509   if (selectable == clist_row->selectable)
3510     return;
3511
3512   clist_row->selectable = selectable;
3513
3514   if (!selectable && clist_row->state == GTK_STATE_SELECTED)
3515     {
3516       if (clist->anchor >= 0 &&
3517           clist->selection_mode == GTK_SELECTION_MULTIPLE)
3518         {
3519           clist->drag_button = 0;
3520           remove_grab (clist);
3521           GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3522         }
3523       g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3524                        row, -1, NULL);
3525     }      
3526 }
3527
3528 gboolean
3529 gtk_cmclist_get_selectable (GtkCMCList *clist,
3530                           gint      row)
3531 {
3532   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), FALSE);
3533
3534   if (row < 0 || row >= clist->rows)
3535     return FALSE;
3536
3537   return GTK_CMCLIST_ROW (ROW_ELEMENT (clist, row))->selectable;
3538 }
3539
3540 void
3541 gtk_cmclist_select_row (GtkCMCList *clist,
3542                       gint      row,
3543                       gint      column)
3544 {
3545   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3546
3547   if (row < 0 || row >= clist->rows)
3548     return;
3549   if (column < -1 || column >= clist->columns)
3550     return;
3551
3552   g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
3553                    row, column, NULL);
3554 }
3555
3556 void
3557 gtk_cmclist_unselect_row (GtkCMCList *clist,
3558                         gint      row,
3559                         gint      column)
3560 {
3561   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3562
3563   if (row < 0 || row >= clist->rows)
3564     return;
3565   if (column < -1 || column >= clist->columns)
3566     return;
3567
3568   g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3569                    row, column, NULL);
3570 }
3571
3572 void
3573 gtk_cmclist_select_all (GtkCMCList *clist)
3574 {
3575   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3576
3577   GTK_CMCLIST_GET_CLASS (clist)->select_all (clist);
3578 }
3579
3580 void
3581 gtk_cmclist_unselect_all (GtkCMCList *clist)
3582 {
3583   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3584
3585   GTK_CMCLIST_GET_CLASS (clist)->unselect_all (clist);
3586 }
3587
3588 void
3589 gtk_cmclist_undo_selection (GtkCMCList *clist)
3590 {
3591   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3592
3593   if (clist->selection_mode == GTK_SELECTION_MULTIPLE &&
3594       (clist->undo_selection || clist->undo_unselection))
3595     g_signal_emit (G_OBJECT (clist), clist_signals[UNDO_SELECTION], 0);
3596 }
3597
3598 /* PRIVATE SELECTION FUNCTIONS
3599  *   selection_find
3600  *   toggle_row
3601  *   fake_toggle_row
3602  *   toggle_focus_row
3603  *   toggle_add_mode
3604  *   real_select_row
3605  *   real_unselect_row
3606  *   real_select_all
3607  *   real_unselect_all
3608  *   fake_unselect_all
3609  *   real_undo_selection
3610  *   set_anchor
3611  *   resync_selection
3612  *   update_extended_selection
3613  *   start_selection
3614  *   end_selection
3615 &