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