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