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