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