060e66bdfe67cec749aa2d4677006f23917c8423
[claws.git] / src / gtk / gtkcmclist.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball, Josh MacDonald, 
3  * Copyright (C) 1997-1998 Jay Painter <jpaint@serv.net><jpaint@gimp.org>  
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
26  */
27
28 #include <config.h>
29
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <gtk/gtk.h>
34 #include "claws-marshal.h"
35 #include "gtkcmclist.h"
36 #include <gdk/gdkkeysyms.h>
37 #include "utils.h"
38 #include "gtkutils.h"
39
40 /* length of button_actions array */
41 #define MAX_BUTTON 5
42
43 /* the number rows memchunk expands at a time */
44 #define CMCLIST_OPTIMUM_SIZE 64
45
46 /* the width of the column resize windows */
47 #define DRAG_WIDTH  6
48
49 /* minimum allowed width of a column */
50 #define COLUMN_MIN_WIDTH 5
51
52 /* this defigns the base grid spacing */
53 #define CELL_SPACING 1
54
55 /* added the horizontal space at the beginning and end of a row*/
56 #define COLUMN_INSET 3
57
58 /* used for auto-scrolling */
59 #define SCROLL_TIME  100
60
61 /* gives the top pixel of the given row in context of
62  * the clist's voffset */
63 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
64                                     (((row) + 1) * CELL_SPACING) + \
65                                     (clist)->voffset)
66
67 /* returns the row index from a y pixel location in the 
68  * context of the clist's voffset */
69 #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
70                                     ((clist)->row_height + CELL_SPACING))
71
72 /* gives the left pixel of the given column in context of
73  * the clist's hoffset */
74 #define COLUMN_LEFT_XPIXEL(clist, colnum)  ((clist)->column[(colnum)].area.x + \
75                                             (clist)->hoffset)
76
77 /* returns the column index from a x pixel location in the 
78  * context of the clist's hoffset */
79 static inline gint
80 COLUMN_FROM_XPIXEL (GtkCMCList * clist,
81                     gint x)
82 {
83   gint i, cx;
84
85   for (i = 0; i < clist->columns; i++)
86     if (clist->column[i].visible)
87       {
88         cx = clist->column[i].area.x + clist->hoffset;
89
90         if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
91             x <= (cx + clist->column[i].area.width + COLUMN_INSET))
92           return i;
93       }
94
95   /* no match */
96   return -1;
97 }
98
99 /* returns the top pixel of the given row in the context of
100  * the list height */
101 #define ROW_TOP(clist, row)        (((clist)->row_height + CELL_SPACING) * (row))
102
103 /* returns the left pixel of the given column in the context of
104  * the list width */
105 #define COLUMN_LEFT(clist, colnum) ((clist)->column[(colnum)].area.x)
106
107 /* returns the total height of the list */
108 #define LIST_HEIGHT(clist)         (((clist)->row_height * ((clist)->rows)) + \
109                                     (CELL_SPACING * ((clist)->rows + 1)))
110
111
112 /* returns the total width of the list */
113 static inline gint
114 LIST_WIDTH (GtkCMCList * clist) 
115 {
116   gint last_column;
117
118   for (last_column = clist->columns - 1;
119        last_column >= 0 && !clist->column[last_column].visible; last_column--);
120
121   if (last_column >= 0)
122     return (clist->column[last_column].area.x +
123             clist->column[last_column].area.width +
124             COLUMN_INSET + CELL_SPACING);
125   return 0;
126 }
127
128 /* returns the GList item for the nth row */
129 #define ROW_ELEMENT(clist, row) (((row) == (clist)->rows - 1) ? \
130                                  (clist)->row_list_end : \
131                                  g_list_nth ((clist)->row_list, (row)))
132
133
134 /* redraw the list if it's not frozen */
135 #define CLIST_UNFROZEN(clist)     (((GtkCMCList*) (clist))->freeze_count == 0)
136 #define CLIST_REFRESH(clist)    G_STMT_START { \
137   if (CLIST_UNFROZEN (clist)) \
138     GTK_CMCLIST_GET_CLASS (clist)->refresh ((GtkCMCList*) (clist)); \
139 } G_STMT_END
140
141
142 /* Signals */
143 enum {
144   SELECT_ROW,
145   UNSELECT_ROW,
146   ROW_MOVE,
147   CLICK_COLUMN,
148   RESIZE_COLUMN,
149   TOGGLE_FOCUS_ROW,
150   SELECT_ALL,
151   UNSELECT_ALL,
152   UNDO_SELECTION,
153   START_SELECTION,
154   END_SELECTION,
155   TOGGLE_ADD_MODE,
156   EXTEND_SELECTION,
157   SCROLL_VERTICAL,
158   SCROLL_HORIZONTAL,
159   ABORT_COLUMN_RESIZE,
160   LAST_SIGNAL
161 };
162
163 enum {
164   SYNC_REMOVE,
165   SYNC_INSERT
166 };
167
168 enum {
169   ARG_0,
170   ARG_N_COLUMNS,
171   ARG_SHADOW_TYPE,
172   ARG_SELECTION_MODE,
173   ARG_ROW_HEIGHT,
174   ARG_TITLES_ACTIVE,
175   ARG_REORDERABLE,
176   ARG_USE_DRAG_ICONS,
177   ARG_SORT_TYPE
178 };
179
180 /* GtkCMCList Methods */
181 static void     gtk_cmclist_class_init  (GtkCMCListClass         *klass);
182 static void     gtk_cmclist_init        (GtkCMCList              *clist);
183 static GObject* gtk_cmclist_constructor (GType                  type,
184                                        guint                  n_construct_properties,
185                                        GObjectConstructParam *construct_params);
186
187 /* GtkObject Methods */
188 static void gtk_cmclist_destroy  (GtkObject *object);
189 static void gtk_cmclist_finalize (GObject   *object);
190 static void gtk_cmclist_set_arg  (GObject *object,
191                                 guint      arg_id,
192                                 const GValue *value,
193                                 GParamSpec *spec);
194 static void gtk_cmclist_get_arg  (GObject *object,
195                                 guint      arg_id,
196                                 GValue *value,
197                                 GParamSpec *spec);
198
199 /* GtkWidget Methods */
200 static void gtk_cmclist_set_scroll_adjustments (GtkCMCList      *clist,
201                                               GtkAdjustment *hadjustment,
202                                               GtkAdjustment *vadjustment);
203 static void gtk_cmclist_realize         (GtkWidget        *widget);
204 static void gtk_cmclist_unrealize       (GtkWidget        *widget);
205 static void gtk_cmclist_map             (GtkWidget        *widget);
206 static void gtk_cmclist_unmap           (GtkWidget        *widget);
207 static gint gtk_cmclist_expose          (GtkWidget        *widget,
208                                        GdkEventExpose   *event);
209 static gint gtk_cmclist_button_press    (GtkWidget        *widget,
210                                        GdkEventButton   *event);
211 static gint gtk_cmclist_button_release  (GtkWidget        *widget,
212                                        GdkEventButton   *event);
213 static gint gtk_cmclist_motion          (GtkWidget        *widget, 
214                                        GdkEventMotion   *event);
215 static void gtk_cmclist_size_request    (GtkWidget        *widget,
216                                        GtkRequisition   *requisition);
217 static void gtk_cmclist_size_allocate   (GtkWidget        *widget,
218                                        GtkAllocation    *allocation);
219 static void gtk_cmclist_undraw_focus      (GtkWidget        *widget);
220 static void gtk_cmclist_draw_focus      (GtkWidget        *widget);
221 static gint gtk_cmclist_focus_in        (GtkWidget        *widget,
222                                        GdkEventFocus    *event);
223 static gint gtk_cmclist_focus_out       (GtkWidget        *widget,
224                                        GdkEventFocus    *event);
225 static gint gtk_cmclist_focus           (GtkWidget        *widget,
226                                        GtkDirectionType  direction);
227 static void gtk_cmclist_set_focus_child (GtkContainer     *container,
228                                        GtkWidget        *child);
229 static void gtk_cmclist_style_set       (GtkWidget        *widget,
230                                        GtkStyle         *previous_style);
231 static void gtk_cmclist_drag_begin      (GtkWidget        *widget,
232                                        GdkDragContext   *context);
233 static gint gtk_cmclist_drag_motion     (GtkWidget        *widget,
234                                        GdkDragContext   *context,
235                                        gint              x,
236                                        gint              y,
237                                        guint             time);
238 static void gtk_cmclist_drag_leave      (GtkWidget        *widget,
239                                        GdkDragContext   *context,
240                                        guint             time);
241 static void gtk_cmclist_drag_end        (GtkWidget        *widget,
242                                        GdkDragContext   *context);
243 static gboolean gtk_cmclist_drag_drop   (GtkWidget      *widget,
244                                        GdkDragContext *context,
245                                        gint            x,
246                                        gint            y,
247                                        guint           time);
248 static void gtk_cmclist_drag_data_get   (GtkWidget        *widget,
249                                        GdkDragContext   *context,
250                                        GtkSelectionData *selection_data,
251                                        guint             info,
252                                        guint             time);
253 static void gtk_cmclist_drag_data_received (GtkWidget        *widget,
254                                           GdkDragContext   *context,
255                                           gint              x,
256                                           gint              y,
257                                           GtkSelectionData *selection_data,
258                                           guint             info,
259                                           guint             time);
260
261 /* GtkContainer Methods */
262 static void gtk_cmclist_forall          (GtkContainer  *container,
263                                        gboolean       include_internals,
264                                        GtkCallback    callback,
265                                        gpointer       callback_data);
266
267 /* Selection */
268 static void toggle_row                (GtkCMCList      *clist,
269                                        gint           row,
270                                        gint           column,
271                                        GdkEvent      *event);
272 static void real_select_row           (GtkCMCList      *clist,
273                                        gint           row,
274                                        gint           column,
275                                        GdkEvent      *event);
276 static void real_unselect_row         (GtkCMCList      *clist,
277                                        gint           row,
278                                        gint           column,
279                                        GdkEvent      *event);
280 static void update_extended_selection (GtkCMCList      *clist,
281                                        gint           row);
282 static GList *selection_find          (GtkCMCList      *clist,
283                                        gint           row_number,
284                                        GList         *row_list_element);
285 static void real_select_all           (GtkCMCList      *clist);
286 static void real_unselect_all         (GtkCMCList      *clist);
287 static void move_vertical             (GtkCMCList      *clist,
288                                        gint           row,
289                                        gfloat         align);
290 static void move_horizontal           (GtkCMCList      *clist,
291                                        gint           diff);
292 static void real_undo_selection       (GtkCMCList      *clist);
293 static void fake_unselect_all         (GtkCMCList      *clist,
294                                        gint           row);
295 static void fake_toggle_row           (GtkCMCList      *clist,
296                                        gint           row);
297 static void resync_selection          (GtkCMCList      *clist,
298                                        GdkEvent      *event);
299 static void sync_selection            (GtkCMCList      *clist,
300                                        gint           row,
301                                        gint           mode);
302 static void set_anchor                (GtkCMCList      *clist,
303                                        gboolean       add_mode,
304                                        gint           anchor,
305                                        gint           undo_anchor);
306 static void start_selection           (GtkCMCList      *clist);
307 static void end_selection             (GtkCMCList      *clist);
308 static void toggle_add_mode           (GtkCMCList      *clist);
309 static void toggle_focus_row          (GtkCMCList      *clist);
310 static void extend_selection          (GtkCMCList      *clist,
311                                        GtkScrollType  scroll_type,
312                                        gfloat         position,
313                                        gboolean       auto_start_selection);
314 static gint get_selection_info        (GtkCMCList       *clist,
315                                        gint            x,
316                                        gint            y,
317                                        gint           *row,
318                                        gint           *column);
319
320 /* Scrolling */
321 static void move_focus_row     (GtkCMCList      *clist,
322                                 GtkScrollType  scroll_type,
323                                 gfloat         position);
324 static void scroll_horizontal  (GtkCMCList      *clist,
325                                 GtkScrollType  scroll_type,
326                                 gfloat         position);
327 static void scroll_vertical    (GtkCMCList      *clist,
328                                 GtkScrollType  scroll_type,
329                                 gfloat         position);
330 static void move_horizontal    (GtkCMCList      *clist,
331                                 gint           diff);
332 static void move_vertical      (GtkCMCList      *clist,
333                                 gint           row,
334                                 gfloat         align);
335 static gint horizontal_timeout (GtkCMCList      *clist);
336 static gint vertical_timeout   (GtkCMCList      *clist);
337 static void remove_grab        (GtkCMCList      *clist);
338
339
340 /* Resize Columns */
341 static void draw_xor_line             (GtkCMCList       *clist);
342 static gint new_column_width          (GtkCMCList       *clist,
343                                        gint            column,
344                                        gint           *x);
345 static void column_auto_resize        (GtkCMCList       *clist,
346                                        GtkCMCListRow    *clist_row,
347                                        gint            column,
348                                        gint            old_width);
349 static void real_resize_column        (GtkCMCList       *clist,
350                                        gint            column,
351                                        gint            width);
352 static void abort_column_resize       (GtkCMCList       *clist);
353 static void cell_size_request         (GtkCMCList       *clist,
354                                        GtkCMCListRow    *clist_row,
355                                        gint            column,
356                                        GtkRequisition *requisition);
357
358 /* Buttons */
359 static void column_button_create      (GtkCMCList       *clist,
360                                        gint            column);
361 static void column_button_clicked     (GtkWidget      *widget,
362                                        gpointer        data);
363
364 /* Adjustments */
365 static void adjust_adjustments        (GtkCMCList       *clist,
366                                        gboolean        block_resize);
367 static void vadjustment_changed       (GtkAdjustment  *adjustment,
368                                        gpointer        data);
369 static void vadjustment_value_changed (GtkAdjustment  *adjustment,
370                                        gpointer        data);
371 static void hadjustment_changed       (GtkAdjustment  *adjustment,
372                                        gpointer        data);
373 static void hadjustment_value_changed (GtkAdjustment  *adjustment,
374                                        gpointer        data);
375
376 /* Drawing */
377 static void get_cell_style   (GtkCMCList      *clist,
378                               GtkCMCListRow   *clist_row,
379                               gint           state,
380                               gint           column,
381                               GtkStyle     **style);
382 static gint draw_cell_pixbuf (GdkWindow     *window,
383                               GdkRectangle  *clip_rectangle,
384                               cairo_t       *cr,
385                               GdkPixbuf     *pixbuf,
386                               gint           x,
387                               gint           y,
388                               gint           width,
389                               gint           height);
390 static void draw_row         (GtkCMCList      *clist,
391                               GdkRectangle  *area,
392                               gint           row,
393                               GtkCMCListRow   *clist_row);
394 static void draw_rows        (GtkCMCList      *clist,
395                               GdkRectangle  *area);
396 static void clist_refresh    (GtkCMCList      *clist);
397      
398 /* Size Allocation / Requisition */
399 static void size_allocate_title_buttons (GtkCMCList *clist);
400 static void size_allocate_columns       (GtkCMCList *clist,
401                                          gboolean  block_resize);
402 static gint list_requisition_width      (GtkCMCList *clist);
403
404 /* Memory Allocation/Distruction Routines */
405 static GtkCMCListColumn *columns_new (GtkCMCList      *clist);
406 static void column_title_new       (GtkCMCList      *clist,
407                                     gint           column,
408                                     const gchar   *title);
409 static void columns_delete         (GtkCMCList      *clist);
410 static GtkCMCListRow *row_new        (GtkCMCList      *clist);
411 static void row_delete             (GtkCMCList      *clist,
412                                     GtkCMCListRow   *clist_row);
413 static void set_cell_contents      (GtkCMCList      *clist,
414                                     GtkCMCListRow   *clist_row,
415                                     gint           column,
416                                     GtkCMCellType    type,
417                                     const gchar   *text,
418                                     guint8         spacing,
419                                     GdkPixbuf     *pixbuf);
420 static gint real_insert_row        (GtkCMCList      *clist,
421                                     gint           row,
422                                     gchar         *text[]);
423 static void real_remove_row        (GtkCMCList      *clist,
424                                     gint           row);
425 static void real_clear             (GtkCMCList      *clist);
426
427 /* Sorting */
428 static gint default_compare        (GtkCMCList      *clist,
429                                     gconstpointer  row1,
430                                     gconstpointer  row2);
431 static void real_sort_list         (GtkCMCList      *clist);
432 static GList *gtk_cmclist_merge      (GtkCMCList      *clist,
433                                     GList         *a,
434                                     GList         *b);
435 static GList *gtk_cmclist_mergesort  (GtkCMCList      *clist,
436                                     GList         *list,
437                                     gint           num);
438 /* Misc */
439 static gboolean title_focus_in   (GtkCMCList *clist,
440                                   gint      dir);
441 static gboolean title_focus_move (GtkCMCList *clist,
442                                   gint      dir);
443
444 static void real_row_move             (GtkCMCList  *clist,
445                                        gint       source_row,
446                                        gint       dest_row);
447 static gint column_title_passive_func (GtkWidget *widget, 
448                                        GdkEvent  *event,
449                                        gpointer   data);
450 static void drag_dest_cell            (GtkCMCList         *clist,
451                                        gint              x,
452                                        gint              y,
453                                        GtkCMCListDestInfo *dest_info);
454
455
456
457 static GtkContainerClass *parent_class = NULL;
458 static guint clist_signals[LAST_SIGNAL] = {0};
459
460 static const GtkTargetEntry clist_target_table = { "gtk-clist-drag-reorder", 0, 0};
461
462 GType
463 gtk_cmclist_get_type (void)
464 {
465   static GType clist_type = 0;
466
467   if (!clist_type)
468     {
469       static const GTypeInfo clist_info =
470       {
471                         sizeof (GtkCMCListClass),
472
473                         (GBaseInitFunc) NULL,
474                         (GBaseFinalizeFunc) NULL,
475
476                         (GClassInitFunc) gtk_cmclist_class_init,
477                         (GClassFinalizeFunc) NULL,
478                         NULL,   /* class_data */
479
480                         sizeof (GtkCMCList),
481                         0,      /* n_preallocs */
482                         (GInstanceInitFunc) gtk_cmclist_init,
483         };
484         clist_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkCMCList", &clist_info, (GTypeFlags)0);
485     }
486
487   return clist_type;
488 }
489
490 static void
491 gtk_cmclist_class_init (GtkCMCListClass *klass)
492 {
493   GObjectClass *object_class = G_OBJECT_CLASS (klass);
494   GtkObjectClass *gtk_object_class;
495   GtkWidgetClass *widget_class;
496   GtkContainerClass *container_class;
497   GtkBindingSet *binding_set;
498
499   object_class->constructor = gtk_cmclist_constructor;
500
501   gtk_object_class = (GtkObjectClass *) klass;
502   widget_class = (GtkWidgetClass *) klass;
503   container_class = (GtkContainerClass *) klass;
504
505   parent_class = g_type_class_peek (GTK_TYPE_CONTAINER);
506
507   object_class->finalize = gtk_cmclist_finalize;
508   gtk_object_class->destroy = gtk_cmclist_destroy;
509   object_class->set_property = gtk_cmclist_set_arg;
510   object_class->get_property = gtk_cmclist_get_arg;
511   
512
513   widget_class->realize = gtk_cmclist_realize;
514   widget_class->unrealize = gtk_cmclist_unrealize;
515   widget_class->map = gtk_cmclist_map;
516   widget_class->unmap = gtk_cmclist_unmap;
517   widget_class->button_press_event = gtk_cmclist_button_press;
518   widget_class->button_release_event = gtk_cmclist_button_release;
519   widget_class->motion_notify_event = gtk_cmclist_motion;
520   widget_class->expose_event = gtk_cmclist_expose;
521   widget_class->size_request = gtk_cmclist_size_request;
522   widget_class->size_allocate = gtk_cmclist_size_allocate;
523   widget_class->focus_in_event = gtk_cmclist_focus_in;
524   widget_class->focus_out_event = gtk_cmclist_focus_out;
525   widget_class->style_set = gtk_cmclist_style_set;
526   widget_class->drag_begin = gtk_cmclist_drag_begin;
527   widget_class->drag_end = gtk_cmclist_drag_end;
528   widget_class->drag_motion = gtk_cmclist_drag_motion;
529   widget_class->drag_leave = gtk_cmclist_drag_leave;
530   widget_class->drag_drop = gtk_cmclist_drag_drop;
531   widget_class->drag_data_get = gtk_cmclist_drag_data_get;
532   widget_class->drag_data_received = gtk_cmclist_drag_data_received;
533   widget_class->focus = gtk_cmclist_focus;
534   
535   /* container_class->add = NULL; use the default GtkContainerClass warning */
536   /* container_class->remove=NULL; use the default GtkContainerClass warning */
537
538   container_class->forall = gtk_cmclist_forall;
539   container_class->set_focus_child = gtk_cmclist_set_focus_child;
540
541   klass->set_scroll_adjustments = gtk_cmclist_set_scroll_adjustments;
542   klass->refresh = clist_refresh;
543   klass->select_row = real_select_row;
544   klass->unselect_row = real_unselect_row;
545   klass->row_move = real_row_move;
546   klass->undo_selection = real_undo_selection;
547   klass->resync_selection = resync_selection;
548   klass->selection_find = selection_find;
549   klass->click_column = NULL;
550   klass->resize_column = real_resize_column;
551   klass->draw_row = draw_row;
552   klass->insert_row = real_insert_row;
553   klass->remove_row = real_remove_row;
554   klass->clear = real_clear;
555   klass->sort_list = real_sort_list;
556   klass->select_all = real_select_all;
557   klass->unselect_all = real_unselect_all;
558   klass->fake_unselect_all = fake_unselect_all;
559   klass->scroll_horizontal = scroll_horizontal;
560   klass->scroll_vertical = scroll_vertical;
561   klass->extend_selection = extend_selection;
562   klass->toggle_focus_row = toggle_focus_row;
563   klass->toggle_add_mode = toggle_add_mode;
564   klass->start_selection = start_selection;
565   klass->end_selection = end_selection;
566   klass->abort_column_resize = abort_column_resize;
567   klass->set_cell_contents = set_cell_contents;
568   klass->cell_size_request = cell_size_request;
569
570   g_object_class_install_property (object_class,
571                                 ARG_N_COLUMNS,
572                                 g_param_spec_uint ("n-columns",
573                                 "N-Columns",
574                                 "N-Columns",
575                                 1,
576                                 G_MAXINT,
577                                 1,
578                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
579   g_object_class_install_property (object_class,
580                                 ARG_SHADOW_TYPE,
581                                 g_param_spec_enum ("shadow-type",
582                                 "shadow-type",
583                                 "shadow-type",
584                                 GTK_TYPE_SHADOW_TYPE, 0,
585                                 G_PARAM_READWRITE));
586   g_object_class_install_property (object_class,
587                                 ARG_SELECTION_MODE,
588                                 g_param_spec_enum ("selection-mode",
589                                 "selection-mode",
590                                 "selection-mode",
591                                 GTK_TYPE_SELECTION_MODE, 0,
592                                 G_PARAM_READWRITE));
593   g_object_class_install_property (object_class,
594                                 ARG_ROW_HEIGHT,
595                                 g_param_spec_uint ("row-height",
596                                 "row-height",
597                                 "row-height",
598                                 0,
599                                 G_MAXINT,
600                                 0,
601                                 G_PARAM_READWRITE));
602   g_object_class_install_property (object_class,
603                                 ARG_REORDERABLE,
604                                 g_param_spec_boolean ("reorderable",
605                                 "reorderable",
606                                 "reorderable",
607                                 TRUE,
608                                 G_PARAM_READWRITE));
609   g_object_class_install_property (object_class,
610                                 ARG_TITLES_ACTIVE,
611                                 g_param_spec_boolean ("titles-active",
612                                 "titles-active",
613                                 "titles-active",
614                                 TRUE,
615                                 G_PARAM_READWRITE));
616   g_object_class_install_property (object_class,
617                                 ARG_USE_DRAG_ICONS,
618                                 g_param_spec_boolean ("use-drag-icons",
619                                 "use-drag-icons",
620                                 "use-drag-icons",
621                                 TRUE,
622                                 G_PARAM_READWRITE));
623   g_object_class_install_property (object_class,
624                                 ARG_SORT_TYPE,
625                                 g_param_spec_enum ("sort-type",
626                                 "sort-type",
627                                 "sort-type",
628                                 GTK_TYPE_SORT_TYPE, 0,
629                                 G_PARAM_READWRITE));
630   widget_class->set_scroll_adjustments_signal =
631                 g_signal_new ("set_scroll_adjustments",
632                               G_TYPE_FROM_CLASS (object_class),
633                               G_SIGNAL_RUN_LAST,
634                               G_STRUCT_OFFSET (GtkCMCListClass, set_scroll_adjustments),
635                               NULL, NULL,
636                               claws_marshal_VOID__OBJECT_OBJECT,
637                               G_TYPE_NONE, 2,
638                               GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
639
640   clist_signals[SELECT_ROW] =
641                 g_signal_new ("select_row",
642                               G_TYPE_FROM_CLASS (object_class),
643                               G_SIGNAL_RUN_FIRST,
644                               G_STRUCT_OFFSET (GtkCMCListClass, select_row),
645                               NULL, NULL,
646                               claws_marshal_VOID__INT_INT_BOXED,
647                               G_TYPE_NONE, 3,
648                               G_TYPE_INT,
649                               G_TYPE_INT,
650                               GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
651   clist_signals[UNSELECT_ROW] =
652                 g_signal_new ("unselect_row",
653                               G_TYPE_FROM_CLASS (object_class),
654                               G_SIGNAL_RUN_FIRST,
655                               G_STRUCT_OFFSET (GtkCMCListClass, unselect_row),
656                               NULL, NULL,
657                               claws_marshal_VOID__INT_INT_BOXED,
658                               G_TYPE_NONE, 3,
659                               G_TYPE_INT,
660                               G_TYPE_INT,
661                               GDK_TYPE_EVENT);
662   clist_signals[ROW_MOVE] =
663                 g_signal_new ("row_move",
664                               G_TYPE_FROM_CLASS (object_class),
665                               G_SIGNAL_RUN_LAST,
666                               G_STRUCT_OFFSET (GtkCMCListClass, row_move),
667                               NULL, NULL,
668                               claws_marshal_VOID__INT_INT,
669                               G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
670   clist_signals[CLICK_COLUMN] =
671                 g_signal_new ("click_column",
672                               G_TYPE_FROM_CLASS (object_class),
673                               G_SIGNAL_RUN_FIRST,
674                               G_STRUCT_OFFSET (GtkCMCListClass, click_column),
675                               NULL, NULL,
676                               claws_marshal_VOID__INT,
677                               G_TYPE_NONE, 1, G_TYPE_INT);
678   clist_signals[RESIZE_COLUMN] =
679                 g_signal_new ("resize_column",
680                               G_TYPE_FROM_CLASS (object_class),
681                               G_SIGNAL_RUN_LAST,
682                               G_STRUCT_OFFSET (GtkCMCListClass, resize_column),
683                               NULL, NULL,
684                               claws_marshal_VOID__INT_INT,
685                               G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
686
687   clist_signals[TOGGLE_FOCUS_ROW] =
688                 g_signal_new ("toggle_focus_row",
689                               G_TYPE_FROM_CLASS (object_class),
690                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
691                               G_STRUCT_OFFSET (GtkCMCListClass, toggle_focus_row),
692                               NULL, NULL,
693                               claws_marshal_VOID__VOID,
694                               G_TYPE_NONE, 0);
695   clist_signals[SELECT_ALL] =
696                 g_signal_new ("select_all",
697                               G_TYPE_FROM_CLASS (object_class),
698                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
699                               G_STRUCT_OFFSET (GtkCMCListClass, select_all),
700                               NULL, NULL,
701                               claws_marshal_VOID__VOID,
702                               G_TYPE_NONE, 0);
703   clist_signals[UNSELECT_ALL] =
704                 g_signal_new ("unselect_all",
705                               G_TYPE_FROM_CLASS (object_class),
706                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
707                               G_STRUCT_OFFSET (GtkCMCListClass, unselect_all),
708                               NULL, NULL,
709                               claws_marshal_VOID__VOID,
710                               G_TYPE_NONE, 0);
711   clist_signals[UNDO_SELECTION] =
712                 g_signal_new ("undo_selection",
713                               G_TYPE_FROM_CLASS (object_class),
714                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
715                               G_STRUCT_OFFSET (GtkCMCListClass, undo_selection),
716                               NULL, NULL,
717                               claws_marshal_VOID__VOID,
718                               G_TYPE_NONE, 0);
719   clist_signals[START_SELECTION] =
720                 g_signal_new ("start_selection",
721                               G_TYPE_FROM_CLASS (object_class),
722                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
723                               G_STRUCT_OFFSET (GtkCMCListClass, start_selection),
724                               NULL, NULL,
725                               claws_marshal_VOID__VOID,
726                               G_TYPE_NONE, 0);
727   clist_signals[END_SELECTION] =
728                 g_signal_new ("end_selection",
729                               G_TYPE_FROM_CLASS (object_class),
730                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
731                               G_STRUCT_OFFSET (GtkCMCListClass, end_selection),
732                               NULL, NULL,
733                               claws_marshal_VOID__VOID,
734                               G_TYPE_NONE, 0);
735   clist_signals[TOGGLE_ADD_MODE] =
736                 g_signal_new ("toggle_add_mode",
737                               G_TYPE_FROM_CLASS (object_class),
738                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
739                               G_STRUCT_OFFSET (GtkCMCListClass, toggle_add_mode),
740                               NULL, NULL,
741                               claws_marshal_VOID__VOID,
742                               G_TYPE_NONE, 0);
743   clist_signals[EXTEND_SELECTION] =
744                 g_signal_new ("extend_selection",
745                               G_TYPE_FROM_CLASS (object_class),
746                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
747                               G_STRUCT_OFFSET (GtkCMCListClass, extend_selection),
748                               NULL, NULL,
749                               claws_marshal_VOID__ENUM_FLOAT_BOOLEAN,
750                               G_TYPE_NONE, 3, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT, G_TYPE_BOOLEAN);
751   clist_signals[SCROLL_VERTICAL] =
752                 g_signal_new ("scroll_vertical",
753                               G_TYPE_FROM_CLASS (object_class),
754                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
755                               G_STRUCT_OFFSET (GtkCMCListClass, scroll_vertical),
756                               NULL, NULL,
757                               claws_marshal_VOID__ENUM_FLOAT,
758                               G_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT);
759   clist_signals[SCROLL_HORIZONTAL] =
760                 g_signal_new ("scroll_horizontal",
761                               G_TYPE_FROM_CLASS (object_class),
762                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
763                               G_STRUCT_OFFSET (GtkCMCListClass, scroll_horizontal),
764                               NULL, NULL,
765                               claws_marshal_VOID__ENUM_FLOAT,
766                               G_TYPE_NONE, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_FLOAT);
767   clist_signals[ABORT_COLUMN_RESIZE] =
768                 g_signal_new ("abort_column_resize",
769                               G_TYPE_FROM_CLASS (object_class),
770                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
771                               G_STRUCT_OFFSET (GtkCMCListClass, abort_column_resize),
772                               NULL, NULL,
773                               claws_marshal_VOID__VOID,
774                               G_TYPE_NONE, 0);
775
776   binding_set = gtk_binding_set_by_class (klass);
777   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0,
778                                 "scroll_vertical", 2,
779                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
780                                 G_TYPE_FLOAT, 0.0);
781   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0,
782                                 "scroll_vertical", 2,
783                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
784                                 G_TYPE_FLOAT, 0.0);
785   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0,
786                                 "scroll_vertical", 2,
787                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
788                                 G_TYPE_FLOAT, 0.0);
789   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0,
790                                 "scroll_vertical", 2,
791                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
792                                 G_TYPE_FLOAT, 0.0);
793   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
794                                 "scroll_vertical", 2,
795                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
796                                 G_TYPE_FLOAT, 0.0);
797   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0,
798                                 "scroll_vertical", 2,
799                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
800                                 G_TYPE_FLOAT, 0.0);
801   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
802                                 "scroll_vertical", 2,
803                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
804                                 G_TYPE_FLOAT, 0.0);
805   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0,
806                                 "scroll_vertical", 2,
807                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
808                                 G_TYPE_FLOAT, 0.0);
809   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK,
810                                 "scroll_vertical", 2,
811                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
812                                 G_TYPE_FLOAT, 0.0);
813   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
814                                 "scroll_vertical", 2,
815                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
816                                 G_TYPE_FLOAT, 0.0);
817   gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, GDK_CONTROL_MASK,
818                                 "scroll_vertical", 2,
819                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
820                                 G_TYPE_FLOAT, 1.0);
821   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK,
822                                 "scroll_vertical", 2,
823                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
824                                 G_TYPE_FLOAT, 1.0);
825   
826   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_SHIFT_MASK,
827                                 "extend_selection", 3,
828                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
829                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
830   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_SHIFT_MASK,
831                                 "extend_selection", 3,
832                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
833                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
834   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_SHIFT_MASK,
835                                 "extend_selection", 3,
836                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
837                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
838   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_SHIFT_MASK,
839                                 "extend_selection", 3,
840                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
841                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
842   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, GDK_SHIFT_MASK,
843                                 "extend_selection", 3,
844                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
845                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
846   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, GDK_SHIFT_MASK,
847                                 "extend_selection", 3,
848                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_BACKWARD,
849                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
850   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, GDK_SHIFT_MASK,
851                                 "extend_selection", 3,
852                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
853                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
854   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, GDK_SHIFT_MASK,
855                                 "extend_selection", 3,
856                                 G_TYPE_ENUM, GTK_SCROLL_PAGE_FORWARD,
857                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
858   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home,
859                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
860                                 "extend_selection", 3,
861                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
862                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
863   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home,
864                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
865                                 "extend_selection", 3,
866                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
867                                 G_TYPE_FLOAT, 0.0, G_TYPE_BOOLEAN, TRUE);
868   gtk_binding_entry_add_signal (binding_set, GDK_KEY_End,
869                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
870                                 "extend_selection", 3,
871                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
872                                 G_TYPE_FLOAT, 1.0, G_TYPE_BOOLEAN, TRUE);
873   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End,
874                                 GDK_SHIFT_MASK | GDK_CONTROL_MASK,
875                                 "extend_selection", 3,
876                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
877                                 G_TYPE_FLOAT, 1.0, G_TYPE_BOOLEAN, TRUE);
878
879   
880   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, 0,
881                                 "scroll_horizontal", 2,
882                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
883                                 G_TYPE_FLOAT, 0.0);
884   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, 0,
885                                 "scroll_horizontal", 2,
886                                 G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
887                                 G_TYPE_FLOAT, 0.0);
888   
889   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, 0,
890                                 "scroll_horizontal", 2,
891                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
892                                 G_TYPE_FLOAT, 0.0);
893   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, 0,
894                                 "scroll_horizontal", 2,
895                                 G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
896                                 G_TYPE_FLOAT, 0.0);
897
898   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0,
899                                 "scroll_horizontal", 2,
900                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
901                                 G_TYPE_FLOAT, 0.0);
902   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0,
903                                 "scroll_horizontal", 2,
904                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
905                                 G_TYPE_FLOAT, 0.0);
906   
907   gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0,
908                                 "scroll_horizontal", 2,
909                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
910                                 G_TYPE_FLOAT, 1.0);
911
912   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0,
913                                 "scroll_horizontal", 2,
914                                 G_TYPE_ENUM, GTK_SCROLL_JUMP,
915                                 G_TYPE_FLOAT, 1.0);
916   
917   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
918                                 "undo_selection", 0);
919   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
920                                 "abort_column_resize", 0);
921   gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
922                                 "toggle_focus_row", 0);
923   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0,
924                                 "toggle_focus_row", 0);  
925   gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
926                                 "toggle_add_mode", 0);
927   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
928                                 "toggle_add_mode", 0);
929   gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK,
930                                 "select_all", 0);
931   gtk_binding_entry_add_signal (binding_set, GDK_KP_Divide, GDK_CONTROL_MASK,
932                                 "select_all", 0);
933   gtk_binding_entry_add_signal (binding_set, '\\', GDK_CONTROL_MASK,
934                                 "unselect_all", 0);
935   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_L,
936                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK,
937                                 "end_selection", 0);
938   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_R,
939                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK,
940                                 "end_selection", 0);
941   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_L,
942                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK |
943                                 GDK_CONTROL_MASK,
944                                 "end_selection", 0);
945   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Shift_R,
946                                 GDK_RELEASE_MASK | GDK_SHIFT_MASK |
947                                 GDK_CONTROL_MASK,
948                                 "end_selection", 0);
949 }
950
951 static void
952 gtk_cmclist_set_arg (GObject *object,
953                                 guint      arg_id,
954                                 const GValue *value,
955                                 GParamSpec *spec)
956 {
957   GtkCMCList *clist;
958
959   clist = GTK_CMCLIST (object);
960
961   switch (arg_id)
962     {
963     case ARG_N_COLUMNS: /* only set at construction time */
964       clist->columns = MAX (1, g_value_get_uint (value));
965       break;
966     case ARG_SHADOW_TYPE:
967       gtk_cmclist_set_shadow_type (clist, g_value_get_enum (value));
968       break;
969     case ARG_SELECTION_MODE:
970       gtk_cmclist_set_selection_mode (clist, g_value_get_enum (value));
971       break;
972     case ARG_ROW_HEIGHT:
973       gtk_cmclist_set_row_height (clist, g_value_get_uint (value));
974       break;
975     case ARG_REORDERABLE:
976       gtk_cmclist_set_reorderable (clist, g_value_get_boolean (value));
977       break;
978     case ARG_TITLES_ACTIVE:
979       if (g_value_get_boolean (value))
980         gtk_cmclist_column_titles_active (clist);
981       else
982         gtk_cmclist_column_titles_passive (clist);
983       break;
984     case ARG_USE_DRAG_ICONS:
985       gtk_cmclist_set_use_drag_icons (clist, g_value_get_boolean (value));
986       break;
987     case ARG_SORT_TYPE:
988       gtk_cmclist_set_sort_type (clist, g_value_get_enum (value));
989       break;
990     }
991 }
992
993 static void
994 gtk_cmclist_get_arg (GObject *object,
995                                 guint      arg_id,
996                                 GValue *value,
997                                 GParamSpec *spec)
998 {
999   GtkCMCList *clist;
1000
1001   clist = GTK_CMCLIST (object);
1002
1003   switch (arg_id)
1004     {
1005       guint i;
1006
1007     case ARG_N_COLUMNS:
1008       g_value_set_uint(value, clist->columns);
1009       break;
1010     case ARG_SHADOW_TYPE:
1011       g_value_set_enum(value, clist->shadow_type);
1012       break;
1013     case ARG_SELECTION_MODE:
1014       g_value_set_enum(value, clist->selection_mode);
1015       break;
1016     case ARG_ROW_HEIGHT:
1017       g_value_set_uint(value, GTK_CMCLIST_ROW_HEIGHT_SET(clist) ? clist->row_height : 0);
1018       break;
1019     case ARG_REORDERABLE:
1020       g_value_set_boolean(value, GTK_CMCLIST_REORDERABLE (clist));
1021       break;
1022     case ARG_TITLES_ACTIVE:
1023       g_value_set_boolean(value, TRUE);
1024       for (i = 0; i < clist->columns; i++)
1025         if (clist->column[i].button &&
1026             !gtk_widget_get_sensitive (clist->column[i].button))
1027           {
1028             g_value_set_boolean(value, FALSE);
1029             break;
1030           }
1031       break;
1032     case ARG_USE_DRAG_ICONS:
1033       g_value_set_boolean(value, GTK_CMCLIST_USE_DRAG_ICONS (clist));
1034       break;
1035     case ARG_SORT_TYPE:
1036       g_value_set_enum(value, clist->sort_type);
1037       break;
1038     default:
1039       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, spec);
1040       break;
1041     }
1042 }
1043
1044 static void
1045 gtk_cmclist_init (GtkCMCList *clist)
1046 {
1047   clist->flags = 0;
1048
1049   gtkut_widget_set_has_window (GTK_WIDGET(clist), TRUE);
1050   gtkut_widget_set_can_focus (GTK_WIDGET(clist), TRUE);
1051   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_DRAW_DRAG_LINE);
1052   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_USE_DRAG_ICONS);
1053
1054
1055 #if !GLIB_CHECK_VERSION(2,10,0)
1056   clist->row_mem_chunk = NULL;
1057   clist->cell_mem_chunk = NULL;
1058 #endif
1059
1060   clist->freeze_count = 0;
1061
1062   clist->rows = 0;
1063   clist->row_height = 0;
1064   clist->row_list = NULL;
1065   clist->row_list_end = NULL;
1066
1067   clist->columns = 0;
1068
1069   clist->title_window = NULL;
1070   clist->column_title_area.x = 0;
1071   clist->column_title_area.y = 0;
1072   clist->column_title_area.width = 1;
1073   clist->column_title_area.height = 1;
1074
1075   clist->clist_window = NULL;
1076   clist->clist_window_width = 1;
1077   clist->clist_window_height = 1;
1078
1079   clist->hoffset = 0;
1080   clist->voffset = 0;
1081
1082   clist->shadow_type = GTK_SHADOW_IN;
1083   clist->vadjustment = NULL;
1084   clist->hadjustment = NULL;
1085
1086   clist->button_actions[0] = GTK_CMBUTTON_SELECTS | GTK_CMBUTTON_DRAGS;
1087   clist->button_actions[1] = GTK_CMBUTTON_IGNORED;
1088   clist->button_actions[2] = GTK_CMBUTTON_IGNORED;
1089   clist->button_actions[3] = GTK_CMBUTTON_IGNORED;
1090   clist->button_actions[4] = GTK_CMBUTTON_IGNORED;
1091
1092   clist->cursor_drag = NULL;
1093   clist->x_drag = 0;
1094
1095   clist->selection_mode = GTK_SELECTION_SINGLE;
1096   clist->selection = NULL;
1097   clist->selection_end = NULL;
1098   clist->undo_selection = NULL;
1099   clist->undo_unselection = NULL;
1100
1101   clist->focus_row = -1;
1102   clist->focus_header_column = -1;
1103   clist->undo_anchor = -1;
1104
1105   clist->anchor = -1;
1106   clist->anchor_state = GTK_STATE_SELECTED;
1107   clist->drag_pos = -1;
1108   clist->htimer = 0;
1109   clist->vtimer = 0;
1110
1111   clist->click_cell.row = -1;
1112   clist->click_cell.column = -1;
1113
1114   clist->compare = default_compare;
1115   clist->sort_type = GTK_SORT_ASCENDING;
1116   clist->sort_column = 0;
1117
1118   clist->drag_highlight_row = -1;
1119 }
1120
1121 /* Constructor */
1122 static GObject*
1123 gtk_cmclist_constructor (GType                  type,
1124                        guint                  n_construct_properties,
1125                        GObjectConstructParam *construct_properties)
1126 {
1127   GObject *object = G_OBJECT_CLASS (parent_class)->constructor (type,
1128                                                                 n_construct_properties,
1129                                                                 construct_properties);
1130   GtkCMCList *clist = GTK_CMCLIST (object);
1131   
1132 #if !GLIB_CHECK_VERSION(2,10,0)
1133   if (!clist->row_mem_chunk)
1134     clist->row_mem_chunk = g_mem_chunk_new ("clist row mem chunk",
1135                                             sizeof (GtkCMCListRow),
1136                                             sizeof (GtkCMCListRow) *
1137                                             CMCLIST_OPTIMUM_SIZE, 
1138                                             G_ALLOC_AND_FREE);
1139   
1140   if (!clist->cell_mem_chunk)
1141     clist->cell_mem_chunk = g_mem_chunk_new ("clist cell mem chunk",
1142                                              sizeof (GtkCMCell) * clist->columns,
1143                                              sizeof (GtkCMCell) * clist->columns *
1144                                              CMCLIST_OPTIMUM_SIZE, 
1145                                              G_ALLOC_AND_FREE);
1146 #endif
1147
1148   /* allocate memory for columns */
1149   clist->column = columns_new (clist);
1150   
1151   /* there needs to be at least one column button 
1152    * because there is alot of code that will break if it
1153    * isn't there
1154    */
1155   column_button_create (clist, 0);
1156   
1157   return object;
1158 }
1159
1160 /* GTKCLIST PUBLIC INTERFACE
1161  *   gtk_cmclist_new
1162  *   gtk_cmclist_new_with_titles
1163  *   gtk_cmclist_set_hadjustment
1164  *   gtk_cmclist_set_vadjustment
1165  *   gtk_cmclist_get_hadjustment
1166  *   gtk_cmclist_get_vadjustment
1167  *   gtk_cmclist_set_shadow_type
1168  *   gtk_cmclist_set_selection_mode
1169  *   gtk_cmclist_freeze
1170  *   gtk_cmclist_thaw
1171  */
1172 GtkWidget*
1173 gtk_cmclist_new (gint columns)
1174 {
1175   return gtk_cmclist_new_with_titles (columns, NULL);
1176 }
1177  
1178 GtkWidget*
1179 gtk_cmclist_new_with_titles (gint   columns,
1180                            gchar *titles[])
1181 {
1182   GtkCMCList *clist;
1183
1184   clist = g_object_new (GTK_TYPE_CMCLIST,
1185                         "n_columns", columns,
1186                         NULL);
1187   if (titles)
1188     {
1189       guint i;
1190
1191       for (i = 0; i < clist->columns; i++)
1192         gtk_cmclist_set_column_title (clist, i, titles[i]);
1193       gtk_cmclist_column_titles_show (clist);
1194     }
1195   else
1196     gtk_cmclist_column_titles_hide (clist);
1197
1198   return GTK_WIDGET (clist);
1199 }
1200
1201 void
1202 gtk_cmclist_set_hadjustment (GtkCMCList      *clist,
1203                            GtkAdjustment *adjustment)
1204 {
1205   GtkAdjustment *old_adjustment;
1206
1207   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1208   if (adjustment)
1209     cm_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1210   
1211   if (clist->hadjustment == adjustment)
1212     return;
1213   
1214   old_adjustment = clist->hadjustment;
1215
1216   if (clist->hadjustment)
1217     {
1218       g_signal_handlers_disconnect_matched(G_OBJECT (clist->hadjustment), G_SIGNAL_MATCH_DATA,
1219                         0, 0, 0, 0, clist);
1220
1221       g_object_unref (G_OBJECT (clist->hadjustment));
1222     }
1223
1224   clist->hadjustment = adjustment;
1225
1226   if (clist->hadjustment)
1227     {
1228 #if GLIB_CHECK_VERSION(2,10,0)
1229       g_object_ref_sink (clist->hadjustment);
1230 #else
1231       gtk_object_ref (G_OBJECT (clist->hadjustment));
1232       gtk_object_sink (G_OBJECT (clist->hadjustment));
1233 #endif
1234       g_signal_connect (G_OBJECT (clist->hadjustment), "changed",
1235                           G_CALLBACK( hadjustment_changed),
1236                           (gpointer) clist);
1237       g_signal_connect (G_OBJECT (clist->hadjustment), "value_changed",
1238                           G_CALLBACK( hadjustment_value_changed),
1239                           (gpointer) clist);
1240     }
1241
1242   if (!clist->hadjustment || !old_adjustment)
1243     gtk_widget_queue_resize (GTK_WIDGET (clist));
1244 }
1245
1246 GtkAdjustment *
1247 gtk_cmclist_get_hadjustment (GtkCMCList *clist)
1248 {
1249   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1250
1251   return clist->hadjustment;
1252 }
1253
1254 void
1255 gtk_cmclist_set_vadjustment (GtkCMCList      *clist,
1256                            GtkAdjustment *adjustment)
1257 {
1258   GtkAdjustment *old_adjustment;
1259
1260   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1261   if (adjustment)
1262     cm_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
1263
1264   if (clist->vadjustment == adjustment)
1265     return;
1266   
1267   old_adjustment = clist->vadjustment;
1268
1269   if (clist->vadjustment)
1270     {
1271       g_signal_handlers_disconnect_matched(G_OBJECT (clist->vadjustment), G_SIGNAL_MATCH_DATA,
1272                         0, 0, 0, 0, clist);
1273       g_object_unref (G_OBJECT (clist->vadjustment));
1274     }
1275
1276   clist->vadjustment = adjustment;
1277
1278   if (clist->vadjustment)
1279     {
1280 #if GLIB_CHECK_VERSION(2,10,0)
1281       g_object_ref_sink (clist->vadjustment);
1282 #else
1283       gtk_object_ref (G_OBJECT (clist->vadjustment));
1284       gtk_object_sink (G_OBJECT (clist->vadjustment));
1285 #endif
1286
1287       g_signal_connect (G_OBJECT (clist->vadjustment), "changed",
1288                           G_CALLBACK(vadjustment_changed),
1289                           (gpointer) clist);
1290       g_signal_connect (G_OBJECT (clist->vadjustment), "value_changed",
1291                           G_CALLBACK(vadjustment_value_changed),
1292                           (gpointer) clist);
1293     }
1294
1295   if (!clist->vadjustment || !old_adjustment)
1296     gtk_widget_queue_resize (GTK_WIDGET (clist));
1297 }
1298
1299 GtkAdjustment *
1300 gtk_cmclist_get_vadjustment (GtkCMCList *clist)
1301 {
1302   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1303
1304   return clist->vadjustment;
1305 }
1306
1307 static void
1308 gtk_cmclist_set_scroll_adjustments (GtkCMCList      *clist,
1309                                   GtkAdjustment *hadjustment,
1310                                   GtkAdjustment *vadjustment)
1311 {
1312   if (clist->hadjustment != hadjustment)
1313     gtk_cmclist_set_hadjustment (clist, hadjustment);
1314   if (clist->vadjustment != vadjustment)
1315     gtk_cmclist_set_vadjustment (clist, vadjustment);
1316 }
1317
1318 void
1319 gtk_cmclist_set_shadow_type (GtkCMCList      *clist,
1320                            GtkShadowType  type)
1321 {
1322   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1323
1324   clist->shadow_type = type;
1325
1326   if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1327     gtk_widget_queue_resize (GTK_WIDGET (clist));
1328 }
1329
1330 void
1331 gtk_cmclist_set_selection_mode (GtkCMCList         *clist,
1332                               GtkSelectionMode  mode)
1333 {
1334   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1335   cm_return_if_fail (mode != GTK_SELECTION_NONE);
1336
1337   if (mode == clist->selection_mode)
1338     return;
1339
1340   clist->selection_mode = mode;
1341   clist->anchor = -1;
1342   clist->anchor_state = GTK_STATE_SELECTED;
1343   clist->drag_pos = -1;
1344   clist->undo_anchor = clist->focus_row;
1345
1346   g_list_free (clist->undo_selection);
1347   g_list_free (clist->undo_unselection);
1348   clist->undo_selection = NULL;
1349   clist->undo_unselection = NULL;
1350
1351   switch (mode)
1352     {
1353     case GTK_SELECTION_MULTIPLE:
1354       return;
1355     case GTK_SELECTION_BROWSE:
1356     case GTK_SELECTION_SINGLE:
1357       gtk_cmclist_unselect_all (clist);
1358       break;
1359     default:
1360       /* Someone set it by hand */
1361       g_assert_not_reached ();
1362     }
1363 }
1364
1365 void
1366 gtk_cmclist_freeze (GtkCMCList *clist)
1367 {
1368   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1369
1370   clist->freeze_count++;
1371 }
1372
1373 void
1374 gtk_cmclist_thaw (GtkCMCList *clist)
1375 {
1376   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1377
1378   if (clist->freeze_count)
1379     {
1380       clist->freeze_count--;
1381       CLIST_REFRESH (clist);
1382     }
1383 }
1384
1385 /* PUBLIC COLUMN FUNCTIONS
1386  *   gtk_cmclist_column_titles_show
1387  *   gtk_cmclist_column_titles_hide
1388  *   gtk_cmclist_column_title_active
1389  *   gtk_cmclist_column_title_passive
1390  *   gtk_cmclist_column_titles_active
1391  *   gtk_cmclist_column_titles_passive
1392  *   gtk_cmclist_set_column_title
1393  *   gtk_cmclist_get_column_title
1394  *   gtk_cmclist_set_column_widget
1395  *   gtk_cmclist_set_column_justification
1396  *   gtk_cmclist_set_column_visibility
1397  *   gtk_cmclist_set_column_resizeable
1398  *   gtk_cmclist_set_column_auto_resize
1399  *   gtk_cmclist_optimal_column_width
1400  *   gtk_cmclist_set_column_width
1401  *   gtk_cmclist_set_column_min_width
1402  *   gtk_cmclist_set_column_max_width
1403  */
1404 void
1405 gtk_cmclist_column_titles_show (GtkCMCList *clist)
1406 {
1407   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1408
1409   if (!GTK_CMCLIST_SHOW_TITLES(clist))
1410     {
1411       GTK_CMCLIST_SET_FLAG (clist, CMCLIST_SHOW_TITLES);
1412       if (clist->title_window)
1413         gdk_window_show (clist->title_window);
1414       gtk_widget_queue_resize (GTK_WIDGET (clist));
1415     }
1416 }
1417
1418 void 
1419 gtk_cmclist_column_titles_hide (GtkCMCList *clist)
1420 {
1421   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1422
1423   if (GTK_CMCLIST_SHOW_TITLES(clist))
1424     {
1425       GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_SHOW_TITLES);
1426       if (clist->title_window)
1427         gdk_window_hide (clist->title_window);
1428       gtk_widget_queue_resize (GTK_WIDGET (clist));
1429     }
1430 }
1431
1432 void
1433 gtk_cmclist_column_title_active (GtkCMCList *clist,
1434                                gint      column)
1435 {
1436   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1437
1438   if (column < 0 || column >= clist->columns)
1439     return;
1440   if (!clist->column[column].button || !clist->column[column].button_passive)
1441     return;
1442
1443   clist->column[column].button_passive = FALSE;
1444
1445   g_signal_handlers_disconnect_matched(G_OBJECT (clist->column[column].button), G_SIGNAL_MATCH_FUNC,
1446                     0, 0, 0, column_title_passive_func, 0);
1447
1448   gtkut_widget_set_can_focus (clist->column[column].button, TRUE);
1449   if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1450     gtk_widget_queue_draw (clist->column[column].button);
1451 }
1452
1453 void
1454 gtk_cmclist_column_title_passive (GtkCMCList *clist,
1455                                 gint      column)
1456 {
1457   GtkToggleButton *button;
1458
1459   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1460
1461   if (column < 0 || column >= clist->columns)
1462     return;
1463   if (!clist->column[column].button || clist->column[column].button_passive)
1464     return;
1465
1466   button = GTK_TOGGLE_BUTTON (clist->column[column].button);
1467
1468   clist->column[column].button_passive = TRUE;
1469
1470   if (gtk_toggle_button_get_active(button))
1471         g_signal_connect(G_OBJECT (clist->column[column].button),
1472                          "button-release-event",
1473                          G_CALLBACK(column_title_passive_func),
1474                          NULL);
1475   if (gtk_widget_is_focus(gtk_bin_get_child(GTK_BIN(button))))
1476         g_signal_connect(G_OBJECT (clist->column[column].button),
1477                          "leave-notify-event",
1478                          G_CALLBACK(column_title_passive_func),
1479                          NULL);
1480
1481   g_signal_connect (G_OBJECT (clist->column[column].button), "event",
1482                       G_CALLBACK(column_title_passive_func), NULL);
1483
1484   gtkut_widget_set_can_focus (clist->column[column].button, FALSE);
1485   if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1486     gtk_widget_queue_draw (clist->column[column].button);
1487 }
1488
1489 void
1490 gtk_cmclist_column_titles_active (GtkCMCList *clist)
1491 {
1492   gint i;
1493
1494   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1495
1496   for (i = 0; i < clist->columns; i++)
1497     gtk_cmclist_column_title_active (clist, i);
1498 }
1499
1500 void
1501 gtk_cmclist_column_titles_passive (GtkCMCList *clist)
1502 {
1503   gint i;
1504
1505   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1506
1507   for (i = 0; i < clist->columns; i++)
1508     gtk_cmclist_column_title_passive (clist, i);
1509 }
1510
1511 void
1512 gtk_cmclist_set_column_title (GtkCMCList    *clist,
1513                             gint         column,
1514                             const gchar *title)
1515 {
1516   gint new_button = 0;
1517   GtkWidget *old_widget;
1518   GtkWidget *alignment = NULL;
1519   GtkWidget *label;
1520
1521   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1522
1523   if (column < 0 || column >= clist->columns)
1524     return;
1525
1526   /* if the column button doesn't currently exist,
1527    * it has to be created first */
1528   if (!clist->column[column].button)
1529     {
1530       column_button_create (clist, column);
1531       new_button = 1;
1532     }
1533
1534   column_title_new (clist, column, title);
1535
1536   /* remove and destroy the old widget */
1537   old_widget = gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1538   if (old_widget)
1539     gtk_container_remove (GTK_CONTAINER (clist->column[column].button), old_widget);
1540
1541   /* create new alignment based no column justification */
1542   switch (clist->column[column].justification)
1543     {
1544     case GTK_JUSTIFY_LEFT:
1545       alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
1546       break;
1547
1548     case GTK_JUSTIFY_RIGHT:
1549       alignment = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
1550       break;
1551
1552     case GTK_JUSTIFY_CENTER:
1553       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1554       break;
1555
1556     case GTK_JUSTIFY_FILL:
1557       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1558       break;
1559     }
1560
1561   gtk_widget_push_composite_child ();
1562   label = gtk_label_new (clist->column[column].title);
1563   gtk_widget_pop_composite_child ();
1564   gtk_container_add (GTK_CONTAINER (alignment), label);
1565   gtk_container_add (GTK_CONTAINER (clist->column[column].button), alignment);
1566   gtk_widget_show (label);
1567   gtk_widget_show (alignment);
1568
1569   /* if this button didn't previously exist, then the
1570    * column button positions have to be re-computed */
1571   if (gtk_widget_get_visible (GTK_WIDGET(clist)) && new_button)
1572     size_allocate_title_buttons (clist);
1573 }
1574
1575 gchar *
1576 gtk_cmclist_get_column_title (GtkCMCList *clist,
1577                             gint      column)
1578 {
1579   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1580
1581   if (column < 0 || column >= clist->columns)
1582     return NULL;
1583
1584   return clist->column[column].title;
1585 }
1586
1587 void
1588 gtk_cmclist_set_column_widget (GtkCMCList  *clist,
1589                              gint       column,
1590                              GtkWidget *widget)
1591 {
1592   gint new_button = 0;
1593   GtkWidget *old_widget;
1594
1595   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1596
1597   if (column < 0 || column >= clist->columns)
1598     return;
1599
1600   /* if the column button doesn't currently exist,
1601    * it has to be created first */
1602   if (!clist->column[column].button)
1603     {
1604       column_button_create (clist, column);
1605       new_button = 1;
1606     }
1607
1608   column_title_new (clist, column, NULL);
1609
1610   /* remove and destroy the old widget */
1611   old_widget = gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1612   if (old_widget)
1613     gtk_container_remove (GTK_CONTAINER (clist->column[column].button),
1614                           old_widget);
1615
1616   /* add and show the widget */
1617   if (widget)
1618     {
1619       gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget);
1620       gtk_widget_show (widget);
1621     }
1622
1623   /* if this button didn't previously exist, then the
1624    * column button positions have to be re-computed */
1625   if (gtk_widget_get_visible (GTK_WIDGET(clist)) && new_button)
1626     size_allocate_title_buttons (clist);
1627 }
1628
1629 GtkWidget *
1630 gtk_cmclist_get_column_widget (GtkCMCList *clist,
1631                              gint      column)
1632 {
1633   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
1634
1635   if (column < 0 || column >= clist->columns)
1636     return NULL;
1637
1638   if (clist->column[column].button)
1639         return gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1640
1641   return NULL;
1642 }
1643
1644 void
1645 gtk_cmclist_set_column_justification (GtkCMCList         *clist,
1646                                     gint              column,
1647                                     GtkJustification  justification)
1648 {
1649   GtkWidget *alignment;
1650
1651   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1652
1653   if (column < 0 || column >= clist->columns)
1654     return;
1655
1656   clist->column[column].justification = justification;
1657
1658   /* change the alinment of the button title if it's not a
1659    * custom widget */
1660   if (clist->column[column].title)
1661     {
1662       alignment = gtk_bin_get_child (GTK_BIN (clist->column[column].button));
1663
1664       switch (clist->column[column].justification)
1665         {
1666         case GTK_JUSTIFY_LEFT:
1667           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.0, 0.5, 0.0, 0.0);
1668           break;
1669
1670         case GTK_JUSTIFY_RIGHT:
1671           gtk_alignment_set (GTK_ALIGNMENT (alignment), 1.0, 0.5, 0.0, 0.0);
1672           break;
1673
1674         case GTK_JUSTIFY_CENTER:
1675           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1676           break;
1677
1678         case GTK_JUSTIFY_FILL:
1679           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
1680           break;
1681
1682         default:
1683           break;
1684         }
1685     }
1686
1687   if (CLIST_UNFROZEN (clist))
1688     draw_rows (clist, NULL);
1689 }
1690
1691 void
1692 gtk_cmclist_set_column_visibility (GtkCMCList *clist,
1693                                  gint      column,
1694                                  gboolean  visible)
1695 {
1696   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1697
1698   if (column < 0 || column >= clist->columns)
1699     return;
1700   if (clist->column[column].visible == visible)
1701     return;
1702
1703   /* don't hide last visible column */
1704   if (!visible)
1705     {
1706       gint i;
1707       gint vis_columns = 0;
1708
1709       for (i = 0, vis_columns = 0; i < clist->columns && vis_columns < 2; i++)
1710         if (clist->column[i].visible)
1711           vis_columns++;
1712
1713       if (vis_columns < 2)
1714         return;
1715     }
1716
1717   clist->column[column].visible = visible;
1718
1719   if (clist->column[column].button)
1720     {
1721       if (visible)
1722         gtk_widget_show (clist->column[column].button);
1723       else
1724         gtk_widget_hide (clist->column[column].button);
1725     }
1726   
1727   gtk_widget_queue_resize (GTK_WIDGET(clist));
1728 }
1729
1730 void
1731 gtk_cmclist_set_column_resizeable (GtkCMCList *clist,
1732                                  gint      column,
1733                                  gboolean  resizeable)
1734 {
1735   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1736
1737   if (column < 0 || column >= clist->columns)
1738     return;
1739   if (clist->column[column].resizeable == resizeable)
1740     return;
1741
1742   clist->column[column].resizeable = resizeable;
1743   if (resizeable)
1744     clist->column[column].auto_resize = FALSE;
1745
1746   if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1747     size_allocate_title_buttons (clist);
1748 }
1749
1750 void
1751 gtk_cmclist_set_column_auto_resize (GtkCMCList *clist,
1752                                   gint      column,
1753                                   gboolean  auto_resize)
1754 {
1755   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1756
1757   if (column < 0 || column >= clist->columns)
1758     return;
1759   if (clist->column[column].auto_resize == auto_resize)
1760     return;
1761
1762   clist->column[column].auto_resize = auto_resize;
1763   if (auto_resize)
1764     {
1765       clist->column[column].resizeable = FALSE;
1766       if (!GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1767         {
1768           gint width;
1769
1770           width = gtk_cmclist_optimal_column_width (clist, column);
1771           gtk_cmclist_set_column_width (clist, column, width);
1772         }
1773     }
1774
1775   if (gtk_widget_get_visible (GTK_WIDGET(clist)))
1776     size_allocate_title_buttons (clist);
1777 }
1778
1779 gint
1780 gtk_cmclist_columns_autosize (GtkCMCList *clist)
1781 {
1782   gint i;
1783   gint width;
1784
1785   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
1786
1787   gtk_cmclist_freeze (clist);
1788   width = 0;
1789   for (i = 0; i < clist->columns; i++)
1790     {
1791       gtk_cmclist_set_column_width (clist, i,
1792                                   gtk_cmclist_optimal_column_width (clist, i));
1793
1794       width += clist->column[i].width;
1795     }
1796
1797   gtk_cmclist_thaw (clist);
1798   return width;
1799 }
1800
1801 gint
1802 gtk_cmclist_optimal_column_width (GtkCMCList *clist,
1803                                 gint      column)
1804 {
1805   GtkRequisition requisition;
1806   GList *list;
1807   gint width;
1808
1809   cm_return_val_if_fail (GTK_CMCLIST (clist), 0);
1810
1811   if (column < 0 || column >= clist->columns)
1812     return 0;
1813
1814   if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[column].button)
1815     {
1816       gtk_widget_get_requisition (clist->column[column].button, &requisition);
1817       width = requisition.width
1818 #if 0
1819              (CELL_SPACING + (2 * COLUMN_INSET)))
1820 #endif
1821                 ;
1822     }
1823   else
1824     width = 0;
1825
1826   for (list = clist->row_list; list; list = list->next)
1827     {
1828   GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
1829         (clist, GTK_CMCLIST_ROW (list), column, &requisition);
1830       width = MAX (width, requisition.width);
1831     }
1832
1833   return width;
1834 }
1835
1836 void
1837 gtk_cmclist_set_column_width (GtkCMCList *clist,
1838                             gint      column,
1839                             gint      width)
1840 {
1841   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1842
1843   if (column < 0 || column >= clist->columns)
1844     return;
1845
1846   g_signal_emit (G_OBJECT (clist), clist_signals[RESIZE_COLUMN], 0,
1847                    column, width);
1848 }
1849
1850 void
1851 gtk_cmclist_set_column_min_width (GtkCMCList *clist,
1852                                 gint      column,
1853                                 gint      min_width)
1854 {
1855   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1856
1857   if (column < 0 || column >= clist->columns)
1858     return;
1859   if (clist->column[column].min_width == min_width)
1860     return;
1861
1862   if (clist->column[column].max_width >= 0  &&
1863       clist->column[column].max_width < min_width)
1864     clist->column[column].min_width = clist->column[column].max_width;
1865   else
1866     clist->column[column].min_width = min_width;
1867
1868   if (clist->column[column].area.width < clist->column[column].min_width)
1869     gtk_cmclist_set_column_width (clist, column,clist->column[column].min_width);
1870 }
1871
1872 void
1873 gtk_cmclist_set_column_max_width (GtkCMCList *clist,
1874                                 gint      column,
1875                                 gint      max_width)
1876 {
1877   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1878
1879   if (column < 0 || column >= clist->columns)
1880     return;
1881   if (clist->column[column].max_width == max_width)
1882     return;
1883
1884   if (clist->column[column].min_width >= 0 && max_width >= 0 &&
1885       clist->column[column].min_width > max_width)
1886     clist->column[column].max_width = clist->column[column].min_width;
1887   else
1888     clist->column[column].max_width = max_width;
1889   
1890   if (clist->column[column].area.width > clist->column[column].max_width)
1891     gtk_cmclist_set_column_width (clist, column,clist->column[column].max_width);
1892 }
1893
1894 /* PRIVATE COLUMN FUNCTIONS
1895  *   column_auto_resize
1896  *   real_resize_column
1897  *   abort_column_resize
1898  *   size_allocate_title_buttons
1899  *   size_allocate_columns
1900  *   list_requisition_width
1901  *   new_column_width
1902  *   column_button_create
1903  *   column_button_clicked
1904  *   column_title_passive_func
1905  */
1906 static void
1907 column_auto_resize (GtkCMCList    *clist,
1908                     GtkCMCListRow *clist_row,
1909                     gint         column,
1910                     gint         old_width)
1911 {
1912   /* resize column if needed for auto_resize */
1913   GtkRequisition requisition;
1914
1915   if (!clist->column[column].auto_resize ||
1916       GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1917     return;
1918
1919   if (clist_row)
1920     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
1921                                                    column, &requisition);
1922   else
1923     requisition.width = 0;
1924
1925   if (requisition.width > clist->column[column].width)
1926     gtk_cmclist_set_column_width (clist, column, requisition.width);
1927   else if (requisition.width < old_width &&
1928            old_width == clist->column[column].width)
1929     {
1930       GList *list;
1931       gint new_width = 0;
1932
1933       /* run a "gtk_cmclist_optimal_column_width" but break, if
1934        * the column doesn't shrink */
1935       if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[column].button)
1936     {
1937         gtk_widget_get_requisition (clist->column[column].button, &requisition);
1938         new_width = (requisition.width -
1939                      (CELL_SPACING + (2 * COLUMN_INSET)));
1940     }
1941       else
1942         new_width = 0;
1943
1944       for (list = clist->row_list; list; list = list->next)
1945         {
1946           GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
1947             (clist, GTK_CMCLIST_ROW (list), column, &requisition);
1948           new_width = MAX (new_width, requisition.width);
1949           if (new_width == clist->column[column].width)
1950             break;
1951         }
1952       if (new_width < clist->column[column].width)
1953         gtk_cmclist_set_column_width
1954           (clist, column, MAX (new_width, clist->column[column].min_width));
1955     }
1956 }
1957
1958 static void
1959 real_resize_column (GtkCMCList *clist,
1960                     gint      column,
1961                     gint      width)
1962 {
1963   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1964
1965   if (column < 0 || column >= clist->columns)
1966     return;
1967   
1968   if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
1969     width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
1970   if (clist->column[column].max_width >= 0 &&
1971       width > clist->column[column].max_width)
1972     width = clist->column[column].max_width;
1973
1974   clist->column[column].width = width;
1975   clist->column[column].width_set = TRUE;
1976
1977   /* FIXME: this is quite expensive to do if the widget hasn't
1978    *        been size_allocated yet, and pointless. Should
1979    *        a flag be kept
1980    */
1981   size_allocate_columns (clist, TRUE);
1982   size_allocate_title_buttons (clist);
1983
1984   CLIST_REFRESH (clist);
1985 }
1986
1987 static void
1988 abort_column_resize (GtkCMCList *clist)
1989 {
1990   cm_return_if_fail (GTK_IS_CMCLIST (clist));
1991
1992   if (!GTK_CMCLIST_IN_DRAG(clist))
1993     return;
1994
1995   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_IN_DRAG);
1996   gtk_grab_remove (GTK_WIDGET (clist));
1997   gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (clist)),
1998                               GDK_CURRENT_TIME);
1999   clist->drag_pos = -1;
2000
2001   if (clist->x_drag >= 0 && clist->x_drag <= clist->clist_window_width - 1)
2002     clist_refresh(clist);
2003 }
2004
2005 static void
2006 size_allocate_title_buttons (GtkCMCList *clist)
2007 {
2008   GtkAllocation button_allocation;
2009   gint last_column;
2010   gint last_button = 0;
2011   gint i;
2012
2013   if (!gtk_widget_get_realized (GTK_WIDGET(clist)))
2014     return;
2015
2016   button_allocation.x = clist->hoffset;
2017   button_allocation.y = 0;
2018   button_allocation.width = 0;
2019   button_allocation.height = clist->column_title_area.height;
2020
2021   /* find last visible column */
2022   for (last_column = clist->columns - 1; last_column >= 0; last_column--)
2023     if (clist->column[last_column].visible)
2024       break;
2025
2026   for (i = 0; i < last_column; i++)
2027     {
2028       if (!clist->column[i].visible)
2029         {
2030           last_button = i + 1;
2031           gdk_window_hide (clist->column[i].window);
2032           continue;
2033         }
2034
2035       button_allocation.width += (clist->column[i].area.width +
2036                                   CELL_SPACING + 2 * COLUMN_INSET);
2037
2038       if (!clist->column[i + 1].button)
2039         {
2040           gdk_window_hide (clist->column[i].window);
2041           continue;
2042         }
2043
2044       gtk_widget_size_allocate (clist->column[last_button].button,
2045                                 &button_allocation);
2046       button_allocation.x += button_allocation.width;
2047       button_allocation.width = 0;
2048
2049       if (clist->column[last_button].resizeable)
2050         {
2051           gdk_window_show (clist->column[last_button].window);
2052           gdk_window_move_resize (clist->column[last_button].window,
2053                                   button_allocation.x - (DRAG_WIDTH / 2), 
2054                                   0, DRAG_WIDTH,
2055                                   clist->column_title_area.height);
2056         }
2057       else
2058         gdk_window_hide (clist->column[last_button].window);
2059
2060       last_button = i + 1;
2061     }
2062
2063   button_allocation.width += (clist->column[last_column].area.width +
2064                               2 * (CELL_SPACING + COLUMN_INSET));
2065   gtk_widget_size_allocate (clist->column[last_button].button,
2066                             &button_allocation);
2067
2068   if (clist->column[last_button].resizeable)
2069     {
2070       button_allocation.x += button_allocation.width;
2071
2072       gdk_window_show (clist->column[last_button].window);
2073       gdk_window_move_resize (clist->column[last_button].window,
2074                               button_allocation.x - (DRAG_WIDTH / 2), 
2075                               0, DRAG_WIDTH, clist->column_title_area.height);
2076     }
2077   else
2078     gdk_window_hide (clist->column[last_button].window);
2079 }
2080
2081 static void
2082 size_allocate_columns (GtkCMCList *clist,
2083                        gboolean  block_resize)
2084 {
2085   GtkRequisition requisition;
2086   gint xoffset = CELL_SPACING + COLUMN_INSET;
2087   gint last_column;
2088   gint i;
2089
2090   /* find last visible column and calculate correct column width */
2091   for (last_column = clist->columns - 1;
2092        last_column >= 0 && !clist->column[last_column].visible; last_column--);
2093
2094   if (last_column < 0)
2095     return;
2096
2097   for (i = 0; i <= last_column; i++)
2098     {
2099       if (!clist->column[i].visible)
2100         continue;
2101       clist->column[i].area.x = xoffset;
2102       if (clist->column[i].width_set)
2103         {
2104           if (!block_resize && GTK_CMCLIST_SHOW_TITLES(clist) &&
2105               clist->column[i].auto_resize && clist->column[i].button)
2106             {
2107               gint width;
2108
2109               gtk_widget_get_requisition (clist->column[i].button, &requisition);
2110               width = (requisition.width -
2111                        (CELL_SPACING + (2 * COLUMN_INSET)));
2112
2113               if (width > clist->column[i].width)
2114                 gtk_cmclist_set_column_width (clist, i, width);
2115             }
2116
2117           clist->column[i].area.width = clist->column[i].width;
2118           xoffset += clist->column[i].width + CELL_SPACING + (2* COLUMN_INSET);
2119         }
2120       else if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button)
2121         {
2122           gtk_widget_get_requisition (clist->column[i].button, &requisition);
2123           clist->column[i].area.width =
2124             requisition.width -
2125             (CELL_SPACING + (2 * COLUMN_INSET));
2126           xoffset += requisition.width;
2127         }
2128     }
2129
2130   clist->column[last_column].area.width = clist->column[last_column].area.width
2131     + MAX (0, clist->clist_window_width + COLUMN_INSET - xoffset);
2132 }
2133
2134 static gint
2135 list_requisition_width (GtkCMCList *clist) 
2136 {
2137   GtkRequisition requisition;
2138   gint width = CELL_SPACING;
2139   gint i;
2140
2141   for (i = clist->columns - 1; i >= 0; i--)
2142     {
2143       if (!clist->column[i].visible)
2144         continue;
2145
2146       if (clist->column[i].width_set)
2147         width += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
2148       else if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button)
2149     {
2150         gtk_widget_get_requisition (clist->column[i].button, &requisition);
2151         width += requisition.width;
2152     }
2153     }
2154
2155   return width;
2156 }
2157
2158 /* this function returns the new width of the column being resized given
2159  * the column and x position of the cursor; the x cursor position is passed
2160  * in as a pointer and automagicly corrected if it's beyond min/max limits */
2161 static gint
2162 new_column_width (GtkCMCList *clist,
2163                   gint      column,
2164                   gint     *x)
2165 {
2166   gint xthickness = gtk_widget_get_style (GTK_WIDGET (clist))->xthickness;
2167   gint width;
2168   gint cx;
2169   gint dx;
2170   gint last_column;
2171
2172   /* first translate the x position from widget->window
2173    * to clist->clist_window */
2174   cx = *x - xthickness;
2175
2176   for (last_column = clist->columns - 1;
2177        last_column >= 0 && !clist->column[last_column].visible; last_column--);
2178
2179   /* calculate new column width making sure it doesn't end up
2180    * less than the minimum width */
2181   dx = (COLUMN_LEFT_XPIXEL (clist, column) + COLUMN_INSET +
2182         (column < last_column) * CELL_SPACING);
2183   width = cx - dx;
2184
2185   if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
2186     {
2187       width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
2188       cx = dx + width;
2189       *x = cx + xthickness;
2190     }
2191   else if (clist->column[column].max_width >= COLUMN_MIN_WIDTH &&
2192            width > clist->column[column].max_width)
2193     {
2194       width = clist->column[column].max_width;
2195       cx = dx + clist->column[column].max_width;
2196       *x = cx + xthickness;
2197     }      
2198
2199   if (cx < 0 || cx > clist->clist_window_width)
2200     *x = -1;
2201
2202   return width;
2203 }
2204
2205 static void
2206 column_button_create (GtkCMCList *clist,
2207                       gint      column)
2208 {
2209   GtkWidget *button;
2210
2211   gtk_widget_push_composite_child ();
2212   button = clist->column[column].button = gtk_button_new ();
2213   GtkRcStyle *style = gtk_rc_style_new();
2214   style->ythickness = 0;
2215   gtk_widget_modify_style(clist->column[column].button, style);
2216   g_object_unref(style);
2217   gtk_container_set_border_width(GTK_CONTAINER(button), 0);
2218   gtk_widget_pop_composite_child ();
2219
2220   if (gtk_widget_get_realized (GTK_WIDGET(clist)) && clist->title_window)
2221     gtk_widget_set_parent_window (clist->column[column].button,
2222                                   clist->title_window);
2223   gtk_widget_set_parent (button, GTK_WIDGET (clist));
2224
2225   g_signal_connect (G_OBJECT (button), "clicked",
2226                       G_CALLBACK(column_button_clicked),
2227                       (gpointer) clist);
2228   gtk_widget_show (button);
2229 }
2230
2231 static void
2232 column_button_clicked (GtkWidget *widget,
2233                        gpointer   data)
2234 {
2235   gint i;
2236   GtkCMCList *clist;
2237
2238   cm_return_if_fail (widget != NULL);
2239   cm_return_if_fail (GTK_IS_CMCLIST (data));
2240
2241   clist = GTK_CMCLIST (data);
2242
2243   /* find the column who's button was pressed */
2244   for (i = 0; i < clist->columns; i++)
2245     if (clist->column[i].button == widget)
2246       break;
2247
2248   g_signal_emit (G_OBJECT (clist), clist_signals[CLICK_COLUMN], 0, i);
2249 }
2250
2251 static gint
2252 column_title_passive_func (GtkWidget *widget, 
2253                            GdkEvent  *event,
2254                            gpointer   data)
2255 {
2256   cm_return_val_if_fail (event != NULL, FALSE);
2257   
2258   switch (event->type)
2259     {
2260     case GDK_MOTION_NOTIFY:
2261     case GDK_BUTTON_PRESS:
2262     case GDK_2BUTTON_PRESS:
2263     case GDK_3BUTTON_PRESS:
2264     case GDK_BUTTON_RELEASE:
2265     case GDK_ENTER_NOTIFY:
2266     case GDK_LEAVE_NOTIFY:
2267       return TRUE;
2268     default:
2269       break;
2270     }
2271   return FALSE;
2272 }
2273
2274
2275 /* PUBLIC CELL FUNCTIONS
2276  *   gtk_cmclist_get_cell_type
2277  *   gtk_cmclist_set_text
2278  *   gtk_cmclist_get_text
2279  *   gtk_cmclist_set_pixbuf
2280  *   gtk_cmclist_get_pixbuf
2281  *   gtk_cmclist_set_pixtext
2282  *   gtk_cmclist_get_pixtext
2283  *   gtk_cmclist_set_shift
2284  */
2285 GtkCMCellType 
2286 gtk_cmclist_get_cell_type (GtkCMCList *clist,
2287                          gint      row,
2288                          gint      column)
2289 {
2290   GtkCMCListRow *clist_row;
2291
2292   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2293
2294   if (row < 0 || row >= clist->rows)
2295     return -1;
2296   if (column < 0 || column >= clist->columns)
2297     return -1;
2298
2299   clist_row = ROW_ELEMENT (clist, row)->data;
2300
2301   return clist_row->cell[column].type;
2302 }
2303
2304 void
2305 gtk_cmclist_set_text (GtkCMCList    *clist,
2306                     gint         row,
2307                     gint         column,
2308                     const gchar *text)
2309 {
2310   GtkCMCListRow *clist_row;
2311
2312   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2313
2314   if (row < 0 || row >= clist->rows)
2315     return;
2316   if (column < 0 || column >= clist->columns)
2317     return;
2318
2319   clist_row = ROW_ELEMENT (clist, row)->data;
2320
2321   /* if text is null, then the cell is empty */
2322   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2323     (clist, clist_row, column, GTK_CMCELL_TEXT, text, 0, NULL);
2324
2325   /* redraw the list if it's not frozen */
2326   if (CLIST_UNFROZEN (clist))
2327     {
2328       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2329         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2330     }
2331 }
2332
2333 gint
2334 gtk_cmclist_get_text (GtkCMCList  *clist,
2335                     gint       row,
2336                     gint       column,
2337                     gchar    **text)
2338 {
2339   GtkCMCListRow *clist_row;
2340
2341   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
2342
2343   if (row < 0 || row >= clist->rows)
2344     return 0;
2345   if (column < 0 || column >= clist->columns)
2346     return 0;
2347
2348   clist_row = ROW_ELEMENT (clist, row)->data;
2349
2350   if (clist_row->cell[column].type != GTK_CMCELL_TEXT)
2351     return 0;
2352
2353   if (text)
2354     *text = GTK_CMCELL_TEXT (clist_row->cell[column])->text;
2355
2356   return 1;
2357 }
2358
2359 void
2360 gtk_cmclist_set_pixbuf (GtkCMCList  *clist,
2361                       gint       row,
2362                       gint       column,
2363                       GdkPixbuf *pixbuf)
2364 {
2365   GtkCMCListRow *clist_row;
2366
2367   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2368
2369   if (row < 0 || row >= clist->rows)
2370     return;
2371   if (column < 0 || column >= clist->columns)
2372     return;
2373
2374   clist_row = ROW_ELEMENT (clist, row)->data;
2375   
2376   g_object_ref (pixbuf);
2377   
2378   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2379     (clist, clist_row, column, GTK_CMCELL_PIXBUF, NULL, 0, pixbuf);
2380
2381   /* redraw the list if it's not frozen */
2382   if (CLIST_UNFROZEN (clist))
2383     {
2384       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2385         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2386     }
2387 }
2388
2389 gint
2390 gtk_cmclist_get_pixbuf (GtkCMCList   *clist,
2391                       gint        row,
2392                       gint        column,
2393                       GdkPixbuf **pixbuf)
2394 {
2395   GtkCMCListRow *clist_row;
2396
2397   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
2398
2399   if (row < 0 || row >= clist->rows)
2400     return 0;
2401   if (column < 0 || column >= clist->columns)
2402     return 0;
2403
2404   clist_row = ROW_ELEMENT (clist, row)->data;
2405
2406   if (clist_row->cell[column].type != GTK_CMCELL_PIXBUF)
2407     return 0;
2408
2409   if (pixbuf)
2410   {
2411     *pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf;
2412   }
2413
2414   return 1;
2415 }
2416
2417 void
2418 gtk_cmclist_set_pixtext (GtkCMCList    *clist,
2419                        gint         row,
2420                        gint         column,
2421                        const gchar *text,
2422                        guint8       spacing,
2423                        GdkPixbuf   *pixbuf)
2424 {
2425   GtkCMCListRow *clist_row;
2426
2427   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2428
2429   if (row < 0 || row >= clist->rows)
2430     return;
2431   if (column < 0 || column >= clist->columns)
2432     return;
2433
2434   clist_row = ROW_ELEMENT (clist, row)->data;
2435   
2436   g_object_ref (pixbuf);
2437   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2438     (clist, clist_row, column, GTK_CMCELL_PIXTEXT, text, spacing, pixbuf);
2439
2440   /* redraw the list if it's not frozen */
2441   if (CLIST_UNFROZEN (clist))
2442     {
2443       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2444         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2445     }
2446 }
2447
2448 gint
2449 gtk_cmclist_get_pixtext (GtkCMCList   *clist,
2450                        gint        row,
2451                        gint        column,
2452                        gchar     **text,
2453                        guint8     *spacing,
2454                        GdkPixbuf **pixbuf)
2455 {
2456   GtkCMCListRow *clist_row;
2457
2458   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
2459
2460   if (row < 0 || row >= clist->rows)
2461     return 0;
2462   if (column < 0 || column >= clist->columns)
2463     return 0;
2464
2465   clist_row = ROW_ELEMENT (clist, row)->data;
2466
2467   if (clist_row->cell[column].type != GTK_CMCELL_PIXTEXT)
2468     return 0;
2469
2470   if (text)
2471     *text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text;
2472   if (spacing)
2473     *spacing = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing;
2474   if (pixbuf)
2475     *pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf;
2476
2477   return 1;
2478 }
2479
2480 void
2481 gtk_cmclist_set_shift (GtkCMCList *clist,
2482                      gint      row,
2483                      gint      column,
2484                      gint      vertical,
2485                      gint      horizontal)
2486 {
2487   GtkRequisition requisition = { 0 };
2488   GtkCMCListRow *clist_row;
2489
2490   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2491
2492   if (row < 0 || row >= clist->rows)
2493     return;
2494   if (column < 0 || column >= clist->columns)
2495     return;
2496
2497   clist_row = ROW_ELEMENT (clist, row)->data;
2498
2499   if (clist->column[column].auto_resize &&
2500       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2501     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2502                                                    column, &requisition);
2503
2504   clist_row->cell[column].vertical = vertical;
2505   clist_row->cell[column].horizontal = horizontal;
2506
2507   column_auto_resize (clist, clist_row, column, requisition.width);
2508
2509   if (CLIST_UNFROZEN (clist) && gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2510     GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
2511 }
2512
2513 /* PRIVATE CELL FUNCTIONS
2514  *   set_cell_contents
2515  *   cell_size_request
2516  */
2517 static void
2518 set_cell_contents (GtkCMCList    *clist,
2519                    GtkCMCListRow *clist_row,
2520                    gint         column,
2521                    GtkCMCellType  type,
2522                    const gchar *text,
2523                    guint8       spacing,
2524                    GdkPixbuf   *pixbuf)
2525 {
2526   GtkRequisition requisition;
2527   gchar *old_text = NULL;
2528   GdkPixbuf *old_pixbuf = NULL;
2529   
2530   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2531   cm_return_if_fail (clist_row != NULL);
2532
2533   if (clist->column[column].auto_resize &&
2534       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2535     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2536                                                    column, &requisition);
2537
2538   switch (clist_row->cell[column].type)
2539     {
2540     case GTK_CMCELL_EMPTY:
2541       break;
2542     case GTK_CMCELL_TEXT:
2543       old_text = GTK_CMCELL_TEXT (clist_row->cell[column])->text;
2544       break;
2545     case GTK_CMCELL_PIXBUF:
2546       old_pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf;
2547       break;
2548     case GTK_CMCELL_PIXTEXT:
2549       old_text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text;
2550       old_pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf;
2551       break;
2552     case GTK_CMCELL_WIDGET:
2553       /* unimplemented */
2554       break;
2555     default:
2556       break;
2557     }
2558
2559   clist_row->cell[column].type = GTK_CMCELL_EMPTY;
2560
2561   /* Note that pixbuf and mask were already ref'ed by the caller
2562    */
2563   switch (type)
2564     {
2565     case GTK_CMCELL_TEXT:
2566       if (text)
2567         {
2568           clist_row->cell[column].type = GTK_CMCELL_TEXT;
2569           GTK_CMCELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
2570         }
2571       break;
2572     case GTK_CMCELL_PIXBUF:
2573       if (pixbuf)
2574         {
2575           clist_row->cell[column].type = GTK_CMCELL_PIXBUF;
2576           GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf = pixbuf;
2577         }
2578       break;
2579     case GTK_CMCELL_PIXTEXT:
2580       if (text && pixbuf)
2581         {
2582           clist_row->cell[column].type = GTK_CMCELL_PIXTEXT;
2583           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
2584           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
2585           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf;
2586         }
2587       break;
2588     default:
2589       break;
2590     }
2591
2592   if (clist->column[column].auto_resize &&
2593       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2594     column_auto_resize (clist, clist_row, column, requisition.width);
2595
2596   g_free (old_text);
2597   if (old_pixbuf)
2598     g_object_unref (old_pixbuf);
2599 }
2600
2601 PangoLayout *
2602 _gtk_cmclist_create_cell_layout (GtkCMCList       *clist,
2603                                GtkCMCListRow    *clist_row,
2604                                gint            column)
2605 {
2606   PangoLayout *layout;
2607   GtkStyle *style;
2608   GtkCMCell *cell;
2609   gchar *text;
2610   
2611   get_cell_style (clist, clist_row, GTK_STATE_NORMAL, column, &style);
2612
2613
2614   cell = &clist_row->cell[column];
2615   switch (cell->type)
2616     {
2617     case GTK_CMCELL_TEXT:
2618     case GTK_CMCELL_PIXTEXT:
2619       text = ((cell->type == GTK_CMCELL_PIXTEXT) ?
2620               GTK_CMCELL_PIXTEXT (*cell)->text :
2621               GTK_CMCELL_TEXT (*cell)->text);
2622
2623       if (!text)
2624         return NULL;
2625       
2626       layout = gtk_widget_create_pango_layout (GTK_WIDGET (clist),
2627                                                ((cell->type == GTK_CMCELL_PIXTEXT) ?
2628                                                 GTK_CMCELL_PIXTEXT (*cell)->text :
2629                                                 GTK_CMCELL_TEXT (*cell)->text));
2630       pango_layout_set_font_description (layout, style->font_desc);
2631       
2632       return layout;
2633       
2634     default:
2635       return NULL;
2636     }
2637 }
2638
2639 static void
2640 cell_size_request (GtkCMCList       *clist,
2641                    GtkCMCListRow    *clist_row,
2642                    gint            column,
2643                    GtkRequisition *requisition)
2644 {
2645   gint width;
2646   gint height;
2647   PangoLayout *layout;
2648   PangoRectangle logical_rect;
2649
2650   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2651   cm_return_if_fail (requisition != NULL);
2652
2653   layout = _gtk_cmclist_create_cell_layout (clist, clist_row, column);
2654   if (layout)
2655     {
2656       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2657       
2658       requisition->width = logical_rect.width;
2659       requisition->height = logical_rect.height;
2660       
2661       g_object_unref (G_OBJECT (layout));
2662     }
2663   else
2664     {
2665       requisition->width  = 0;
2666       requisition->height = 0;
2667     }
2668
2669   if (layout && clist_row->cell[column].type == GTK_CMCELL_PIXTEXT)
2670     requisition->width += GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing;
2671
2672   switch (clist_row->cell[column].type)
2673     {
2674     case GTK_CMCELL_PIXTEXT:
2675       width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2676       height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2677       requisition->width += width;
2678       requisition->height = MAX (requisition->height, height);      
2679       break;
2680     case GTK_CMCELL_PIXBUF:
2681       width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2682       height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2683       requisition->width += width;
2684       requisition->height = MAX (requisition->height, height);
2685       break;
2686       
2687     default:
2688       break;
2689     }
2690
2691   requisition->width  += clist_row->cell[column].horizontal;
2692   requisition->height += clist_row->cell[column].vertical;
2693 }
2694
2695 /* PUBLIC INSERT/REMOVE ROW FUNCTIONS
2696  *   gtk_cmclist_prepend
2697  *   gtk_cmclist_append
2698  *   gtk_cmclist_insert
2699  *   gtk_cmclist_remove
2700  *   gtk_cmclist_clear
2701  */
2702 gint
2703 gtk_cmclist_prepend (GtkCMCList    *clist,
2704                    gchar       *text[])
2705 {
2706   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2707   cm_return_val_if_fail (text != NULL, -1);
2708
2709   return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, 0, text);
2710 }
2711
2712 gint
2713 gtk_cmclist_append (GtkCMCList    *clist,
2714                   gchar       *text[])
2715 {
2716   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2717   cm_return_val_if_fail (text != NULL, -1);
2718
2719   return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, clist->rows, text);
2720 }
2721
2722 gint
2723 gtk_cmclist_insert (GtkCMCList    *clist,
2724                   gint         row,
2725                   gchar       *text[])
2726 {
2727   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2728   cm_return_val_if_fail (text != NULL, -1);
2729
2730   if (row < 0 || row > clist->rows)
2731     row = clist->rows;
2732
2733   return GTK_CMCLIST_GET_CLASS (clist)->insert_row (clist, row, text);
2734 }
2735
2736 void
2737 gtk_cmclist_remove (GtkCMCList *clist,
2738                   gint      row)
2739 {
2740   GTK_CMCLIST_GET_CLASS (clist)->remove_row (clist, row);
2741 }
2742
2743 void
2744 gtk_cmclist_clear (GtkCMCList *clist)
2745 {
2746   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2747   
2748   GTK_CMCLIST_GET_CLASS (clist)->clear (clist);
2749 }
2750
2751 /* PRIVATE INSERT/REMOVE ROW FUNCTIONS
2752  *   real_insert_row
2753  *   real_remove_row
2754  *   real_clear
2755  *   real_row_move
2756  */
2757 static gint
2758 real_insert_row (GtkCMCList *clist,
2759                  gint      row,
2760                  gchar    *text[])
2761 {
2762   gint i;
2763   GtkCMCListRow *clist_row;
2764
2765   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
2766   cm_return_val_if_fail (text != NULL, -1);
2767
2768   /* return if out of bounds */
2769   if (row < 0 || row > clist->rows)
2770     return -1;
2771
2772   /* create the row */
2773   clist_row = row_new (clist);
2774
2775   /* set the text in the row's columns */
2776   for (i = 0; i < clist->columns; i++)
2777     if (text[i])
2778       GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2779         (clist, clist_row, i, GTK_CMCELL_TEXT, text[i], 0, NULL);
2780
2781   if (!clist->rows)
2782     {
2783       clist->row_list = g_list_append (clist->row_list, clist_row);
2784       clist->row_list_end = clist->row_list;
2785     }
2786   else
2787     {
2788       if (GTK_CMCLIST_AUTO_SORT(clist))   /* override insertion pos */
2789         {
2790           GList *work;
2791           
2792           row = 0;
2793           work = clist->row_list;
2794           
2795           if (clist->sort_type == GTK_SORT_ASCENDING)
2796             {
2797               while (row < clist->rows &&
2798                      clist->compare (clist, clist_row,
2799                                      GTK_CMCLIST_ROW (work)) > 0)
2800                 {
2801                   row++;
2802                   work = work->next;
2803                 }
2804             }
2805           else
2806             {
2807               while (row < clist->rows &&
2808                      clist->compare (clist, clist_row,
2809                                      GTK_CMCLIST_ROW (work)) < 0)
2810                 {
2811                   row++;
2812                   work = work->next;
2813                 }
2814             }
2815         }
2816       
2817       /* reset the row end pointer if we're inserting at the end of the list */
2818       if (row == clist->rows)
2819         clist->row_list_end = (g_list_append (clist->row_list_end,
2820                                               clist_row))->next;
2821       else
2822         clist->row_list = g_list_insert (clist->row_list, clist_row, row);
2823
2824     }
2825   clist->rows++;
2826
2827   if (row < ROW_FROM_YPIXEL (clist, 0))
2828     clist->voffset -= (clist->row_height + CELL_SPACING);
2829
2830   /* syncronize the selection list */
2831   sync_selection (clist, row, SYNC_INSERT);
2832
2833   if (clist->rows == 1)
2834     {
2835       clist->focus_row = 0;
2836       if (clist->selection_mode == GTK_SELECTION_BROWSE)
2837         gtk_cmclist_select_row (clist, 0, -1);
2838     }
2839
2840   /* redraw the list if it isn't frozen */
2841   if (CLIST_UNFROZEN (clist))
2842     {
2843       adjust_adjustments (clist, FALSE);
2844
2845       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
2846         draw_rows (clist, NULL);
2847     }
2848
2849   return row;
2850 }
2851
2852 static void
2853 real_remove_row (GtkCMCList *clist,
2854                  gint      row)
2855 {
2856   gint was_visible;
2857   GList *list;
2858   GtkCMCListRow *clist_row;
2859
2860   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2861
2862   /* return if out of bounds */
2863   if (row < 0 || row > (clist->rows - 1))
2864     return;
2865
2866   was_visible = (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE);
2867
2868   /* get the row we're going to delete */
2869   list = ROW_ELEMENT (clist, row);
2870   g_assert (list != NULL);
2871   clist_row = list->data;
2872
2873   /* if we're removing a selected row, we have to make sure
2874    * it's properly unselected, and then sync up the clist->selected
2875    * list to reflect the deincrimented indexies of rows after the
2876    * removal */
2877   if (clist_row->state == GTK_STATE_SELECTED)
2878     g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
2879                      row, -1, NULL);
2880
2881   sync_selection (clist, row, SYNC_REMOVE);
2882
2883   /* reset the row end pointer if we're removing at the end of the list */
2884   clist->rows--;
2885   if (clist->row_list == list)
2886     clist->row_list = g_list_next (list);
2887   if (clist->row_list_end == list)
2888     clist->row_list_end = g_list_previous (list);
2889   list = g_list_remove (list, clist_row);
2890
2891   if (row < ROW_FROM_YPIXEL (clist, 0))
2892     clist->voffset += clist->row_height + CELL_SPACING;
2893
2894   if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection &&
2895       clist->focus_row >= 0)
2896     g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
2897                      clist->focus_row, -1, NULL);
2898
2899   /* toast the row */
2900   row_delete (clist, clist_row);
2901
2902   /* redraw the row if it isn't frozen */
2903   if (CLIST_UNFROZEN (clist))
2904     {
2905       adjust_adjustments (clist, FALSE);
2906
2907       if (was_visible)
2908         draw_rows (clist, NULL);
2909     }
2910 }
2911
2912 static void
2913 real_clear (GtkCMCList *clist)
2914 {
2915   GList *list;
2916   GList *free_list;
2917   GtkRequisition requisition;
2918   gint i;
2919
2920   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2921
2922   /* free up the selection list */
2923   g_list_free (clist->selection);
2924   g_list_free (clist->undo_selection);
2925   g_list_free (clist->undo_unselection);
2926
2927   clist->selection = NULL;
2928   clist->selection_end = NULL;
2929   clist->undo_selection = NULL;
2930   clist->undo_unselection = NULL;
2931   clist->voffset = 0;
2932   clist->focus_row = -1;
2933   clist->anchor = -1;
2934   clist->undo_anchor = -1;
2935   clist->anchor_state = GTK_STATE_SELECTED;
2936   clist->drag_pos = -1;
2937
2938   /* remove all the rows */
2939   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
2940   free_list = clist->row_list;
2941   clist->row_list = NULL;
2942   clist->row_list_end = NULL;
2943   clist->rows = 0;
2944   for (list = free_list; list; list = list->next)
2945     row_delete (clist, GTK_CMCLIST_ROW (list));
2946   g_list_free (free_list);
2947   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
2948   for (i = 0; i < clist->columns; i++)
2949     if (clist->column[i].auto_resize)
2950       {
2951         if (GTK_CMCLIST_SHOW_TITLES(clist) && clist->column[i].button)
2952     {
2953         gtk_widget_get_requisition (clist->column[i].button, &requisition);
2954           gtk_cmclist_set_column_width
2955             (clist, i, (requisition.width -
2956                         (CELL_SPACING + (2 * COLUMN_INSET))));
2957     }
2958         else
2959           gtk_cmclist_set_column_width (clist, i, 0);
2960       }
2961   /* zero-out the scrollbars */
2962   if (clist->vadjustment)
2963     {
2964       gtk_adjustment_set_value (clist->vadjustment, 0.0);
2965       CLIST_REFRESH (clist);
2966     }
2967   else
2968     gtk_widget_queue_resize (GTK_WIDGET (clist));
2969 }
2970
2971 static void
2972 real_row_move (GtkCMCList *clist,
2973                gint      source_row,
2974                gint      dest_row)
2975 {
2976   GtkCMCListRow *clist_row;
2977   GList *list;
2978   gint first, last;
2979   gint d;
2980
2981   cm_return_if_fail (GTK_IS_CMCLIST (clist));
2982
2983   if (GTK_CMCLIST_AUTO_SORT(clist))
2984     return;
2985
2986   if (source_row < 0 || source_row >= clist->rows ||
2987       dest_row   < 0 || dest_row   >= clist->rows ||
2988       source_row == dest_row)
2989     return;
2990
2991   gtk_cmclist_freeze (clist);
2992
2993   /* unlink source row */
2994   clist_row = ROW_ELEMENT (clist, source_row)->data;
2995   if (source_row == clist->rows - 1)
2996     clist->row_list_end = clist->row_list_end->prev;
2997   clist->row_list = g_list_remove (clist->row_list, clist_row);
2998   clist->rows--;
2999
3000   /* relink source row */
3001   clist->row_list = g_list_insert (clist->row_list, clist_row, dest_row);
3002   if (dest_row == clist->rows)
3003     clist->row_list_end = clist->row_list_end->next;
3004   clist->rows++;
3005
3006   /* sync selection */
3007   if (source_row > dest_row)
3008     {
3009       first = dest_row;
3010       last  = source_row;
3011       d = 1;
3012     }
3013   else
3014     {
3015       first = source_row;
3016       last  = dest_row;
3017       d = -1;
3018     }
3019
3020   for (list = clist->selection; list; list = list->next)
3021     {
3022       if (list->data == GINT_TO_POINTER (source_row))
3023         list->data = GINT_TO_POINTER (dest_row);
3024       else if (first <= GPOINTER_TO_INT (list->data) &&
3025                last >= GPOINTER_TO_INT (list->data))
3026         list->data = GINT_TO_POINTER (GPOINTER_TO_INT (list->data) + d);
3027     }
3028   
3029   if (clist->focus_row == source_row)
3030     clist->focus_row = dest_row;
3031   else if (clist->focus_row > first)
3032     clist->focus_row += d;
3033
3034   gtk_cmclist_thaw (clist);
3035 }
3036
3037 /* PUBLIC ROW FUNCTIONS
3038  *   gtk_cmclist_moveto
3039  *   gtk_cmclist_set_row_height
3040  *   gtk_cmclist_set_row_data
3041  *   gtk_cmclist_set_row_data_full
3042  *   gtk_cmclist_get_row_data
3043  *   gtk_cmclist_find_row_from_data
3044  *   gtk_cmclist_swap_rows
3045  *   gtk_cmclist_row_move
3046  *   gtk_cmclist_row_is_visible
3047  *   gtk_cmclist_set_foreground
3048  *   gtk_cmclist_set_background
3049  */
3050 void
3051 gtk_cmclist_moveto (GtkCMCList *clist,
3052                   gint      row,
3053                   gint      column,
3054                   gfloat    row_align,
3055                   gfloat    col_align)
3056 {
3057   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3058
3059   if (row < -1 || row >= clist->rows)
3060     return;
3061   if (column < -1 || column >= clist->columns)
3062     return;
3063
3064   row_align = CLAMP (row_align, 0, 1);
3065   col_align = CLAMP (col_align, 0, 1);
3066
3067   /* adjust horizontal scrollbar */
3068   if (clist->hadjustment && column >= 0)
3069     {
3070       gint x;
3071
3072       x = (COLUMN_LEFT (clist, column) - CELL_SPACING - COLUMN_INSET -
3073            (col_align * (clist->clist_window_width - 2 * COLUMN_INSET -
3074                          CELL_SPACING - clist->column[column].area.width)));
3075       if (x < 0)
3076         gtk_adjustment_set_value (clist->hadjustment, 0.0);
3077       else if (x > LIST_WIDTH (clist) - clist->clist_window_width)
3078         gtk_adjustment_set_value 
3079           (clist->hadjustment, LIST_WIDTH (clist) - clist->clist_window_width);
3080       else
3081         gtk_adjustment_set_value (clist->hadjustment, x);
3082     }
3083
3084   /* adjust vertical scrollbar */
3085   if (clist->vadjustment && row >= 0)
3086     move_vertical (clist, row, row_align);
3087 }
3088
3089 void
3090 gtk_cmclist_set_row_height (GtkCMCList *clist,
3091                           guint     height)
3092 {
3093   GtkStyle *style;
3094   GtkWidget *widget;
3095
3096   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3097
3098   widget = GTK_WIDGET (clist);
3099
3100   style = gtk_widget_get_style (widget);
3101
3102   if (height > 0)
3103     {
3104       clist->row_height = height;
3105       GTK_CMCLIST_SET_FLAG (clist, CMCLIST_ROW_HEIGHT_SET);
3106     }
3107   else
3108     {
3109       GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_ROW_HEIGHT_SET);
3110       clist->row_height = 0;
3111     }
3112
3113   if (style->font_desc)
3114     {
3115       PangoContext *context = gtk_widget_get_pango_context (widget);
3116       PangoFontMetrics *metrics;
3117
3118       metrics = pango_context_get_metrics (context,
3119                                            style->font_desc,
3120                                            pango_context_get_language (context));
3121
3122       if (!GTK_CMCLIST_ROW_HEIGHT_SET(clist))
3123         {
3124           clist->row_height = (pango_font_metrics_get_ascent (metrics) +
3125                                pango_font_metrics_get_descent (metrics));
3126           clist->row_height = PANGO_PIXELS (clist->row_height) + 1;
3127         }
3128
3129       pango_font_metrics_unref (metrics);
3130     }
3131
3132   CLIST_REFRESH (clist);
3133 }
3134
3135 void
3136 gtk_cmclist_set_row_data (GtkCMCList *clist,
3137                         gint      row,
3138                         gpointer  data)
3139 {
3140   gtk_cmclist_set_row_data_full (clist, row, data, NULL);
3141 }
3142
3143 void
3144 gtk_cmclist_set_row_data_full (GtkCMCList         *clist,
3145                              gint              row,
3146                              gpointer          data,
3147                              GDestroyNotify  destroy)
3148 {
3149   GtkCMCListRow *clist_row;
3150
3151   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3152
3153   if (row < 0 || row > (clist->rows - 1))
3154     return;
3155
3156   clist_row = ROW_ELEMENT (clist, row)->data;
3157
3158   if (clist_row->destroy)
3159     clist_row->destroy (clist_row->data);
3160   
3161   clist_row->data = data;
3162   clist_row->destroy = destroy;
3163 }
3164
3165 gpointer
3166 gtk_cmclist_get_row_data (GtkCMCList *clist,
3167                         gint      row)
3168 {
3169   GtkCMCListRow *clist_row;
3170
3171   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
3172
3173   if (row < 0 || row > (clist->rows - 1))
3174     return NULL;
3175
3176   clist_row = ROW_ELEMENT (clist, row)->data;
3177   return clist_row->data;
3178 }
3179
3180 gint
3181 gtk_cmclist_find_row_from_data (GtkCMCList *clist,
3182                               gpointer  data)
3183 {
3184   GList *list;
3185   gint n;
3186
3187   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), -1);
3188
3189   for (n = 0, list = clist->row_list; list; n++, list = list->next)
3190     if (GTK_CMCLIST_ROW (list)->data == data)
3191       return n;
3192
3193   return -1;
3194 }
3195
3196 void 
3197 gtk_cmclist_swap_rows (GtkCMCList *clist,
3198                      gint      row1, 
3199                      gint      row2)
3200 {
3201   gint first, last;
3202
3203   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3204   cm_return_if_fail (row1 != row2);
3205
3206   if (GTK_CMCLIST_AUTO_SORT(clist))
3207     return;
3208
3209   gtk_cmclist_freeze (clist);
3210
3211   first = MIN (row1, row2);
3212   last  = MAX (row1, row2);
3213
3214   gtk_cmclist_row_move (clist, last, first);
3215   gtk_cmclist_row_move (clist, first + 1, last);
3216   
3217   gtk_cmclist_thaw (clist);
3218 }
3219
3220 void
3221 gtk_cmclist_row_move (GtkCMCList *clist,
3222                     gint      source_row,
3223                     gint      dest_row)
3224 {
3225   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3226
3227   if (GTK_CMCLIST_AUTO_SORT(clist))
3228     return;
3229
3230   if (source_row < 0 || source_row >= clist->rows ||
3231       dest_row   < 0 || dest_row   >= clist->rows ||
3232       source_row == dest_row)
3233     return;
3234
3235   g_signal_emit (G_OBJECT (clist), clist_signals[ROW_MOVE], 0,
3236                    source_row, dest_row);
3237 }
3238
3239 GtkVisibility
3240 gtk_cmclist_row_is_visible (GtkCMCList *clist,
3241                           gint      row)
3242 {
3243   gint top;
3244
3245   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
3246
3247   if (row < 0 || row >= clist->rows)
3248     return GTK_VISIBILITY_NONE;
3249
3250   if (clist->row_height == 0)
3251     return GTK_VISIBILITY_NONE;
3252
3253   if (row < ROW_FROM_YPIXEL (clist, 0))
3254     return GTK_VISIBILITY_NONE;
3255
3256   if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height))
3257     return GTK_VISIBILITY_NONE;
3258
3259   top = ROW_TOP_YPIXEL (clist, row);
3260
3261   if ((top < 0)
3262       || ((top + clist->row_height) >= clist->clist_window_height))
3263     return GTK_VISIBILITY_PARTIAL;
3264
3265   return GTK_VISIBILITY_FULL;
3266 }
3267
3268 void
3269 gtk_cmclist_set_foreground (GtkCMCList       *clist,
3270                           gint            row,
3271                           const GdkColor *color)
3272 {
3273   GtkCMCListRow *clist_row;
3274
3275   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3276
3277   if (row < 0 || row >= clist->rows)
3278     return;
3279
3280   clist_row = ROW_ELEMENT (clist, row)->data;
3281
3282   if (color)
3283     {
3284       clist_row->foreground = *color;
3285       clist_row->fg_set = TRUE;
3286       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3287         gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (clist)),
3288                          &clist_row->foreground, TRUE, TRUE);
3289     }
3290   else
3291     clist_row->fg_set = FALSE;
3292
3293   if (CLIST_UNFROZEN (clist) && gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3294     GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3295 }
3296
3297 void
3298 gtk_cmclist_set_background (GtkCMCList       *clist,
3299                           gint            row,
3300                           const GdkColor *color)
3301 {
3302   GtkCMCListRow *clist_row;
3303
3304   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3305
3306   if (row < 0 || row >= clist->rows)
3307     return;
3308
3309   clist_row = ROW_ELEMENT (clist, row)->data;
3310
3311   if (color)
3312     {
3313       clist_row->background = *color;
3314       clist_row->bg_set = TRUE;
3315       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3316         gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (clist)),
3317                          &clist_row->background, TRUE, TRUE);
3318     }
3319   else
3320     clist_row->bg_set = FALSE;
3321
3322   if (CLIST_UNFROZEN (clist)
3323       && (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3324     GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3325 }
3326
3327 /* PUBLIC ROW/CELL STYLE FUNCTIONS
3328  *   gtk_cmclist_set_cell_style
3329  *   gtk_cmclist_get_cell_style
3330  *   gtk_cmclist_set_row_style
3331  *   gtk_cmclist_get_row_style
3332  */
3333 void
3334 gtk_cmclist_set_cell_style (GtkCMCList *clist,
3335                           gint      row,
3336                           gint      column,
3337                           GtkStyle *style)
3338 {
3339   GtkRequisition requisition = { 0 };
3340   GtkCMCListRow *clist_row;
3341
3342   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3343
3344   if (row < 0 || row >= clist->rows)
3345     return;
3346   if (column < 0 || column >= clist->columns)
3347     return;
3348
3349   clist_row = ROW_ELEMENT (clist, row)->data;
3350
3351   if (clist_row->cell[column].style == style)
3352     return;
3353
3354   if (clist->column[column].auto_resize &&
3355       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3356     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3357                                                    column, &requisition);
3358
3359   if (clist_row->cell[column].style)
3360     {
3361       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3362         gtk_style_detach (clist_row->cell[column].style);
3363       g_object_unref (clist_row->cell[column].style);
3364     }
3365
3366   clist_row->cell[column].style = style;
3367
3368   if (clist_row->cell[column].style)
3369     {
3370       g_object_ref (clist_row->cell[column].style);
3371       
3372       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3373         clist_row->cell[column].style =
3374           gtk_style_attach (clist_row->cell[column].style,
3375                             clist->clist_window);
3376     }
3377
3378   column_auto_resize (clist, clist_row, column, requisition.width);
3379
3380   /* redraw the list if it's not frozen */
3381   if (CLIST_UNFROZEN (clist))
3382     {
3383       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3384         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3385     }
3386 }
3387
3388 GtkStyle *
3389 gtk_cmclist_get_cell_style (GtkCMCList *clist,
3390                           gint      row,
3391                           gint      column)
3392 {
3393   GtkCMCListRow *clist_row;
3394
3395   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
3396
3397   if (row < 0 || row >= clist->rows || column < 0 || column >= clist->columns)
3398     return NULL;
3399
3400   clist_row = ROW_ELEMENT (clist, row)->data;
3401
3402   return clist_row->cell[column].style;
3403 }
3404
3405 void
3406 gtk_cmclist_set_row_style (GtkCMCList *clist,
3407                          gint      row,
3408                          GtkStyle *style)
3409 {
3410   GtkRequisition requisition;
3411   GtkCMCListRow *clist_row;
3412   gint *old_width;
3413   gint i;
3414
3415   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3416
3417   if (row < 0 || row >= clist->rows)
3418     return;
3419
3420   clist_row = ROW_ELEMENT (clist, row)->data;
3421
3422   if (clist_row->style == style)
3423     return;
3424
3425   old_width = g_new (gint, clist->columns);
3426
3427   if (!GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3428     {
3429       for (i = 0; i < clist->columns; i++)
3430         if (clist->column[i].auto_resize)
3431           {
3432             GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3433                                                            i, &requisition);
3434             old_width[i] = requisition.width;
3435           }
3436     }
3437
3438   if (clist_row->style)
3439     {
3440       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3441         gtk_style_detach (clist_row->style);
3442       g_object_unref (clist_row->style);
3443     }
3444
3445   clist_row->style = style;
3446
3447   if (clist_row->style)
3448     {
3449       g_object_ref (clist_row->style);
3450       
3451       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
3452         clist_row->style = gtk_style_attach (clist_row->style,
3453                                              clist->clist_window);
3454     }
3455
3456   if (GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3457     for (i = 0; i < clist->columns; i++)
3458       column_auto_resize (clist, clist_row, i, old_width[i]);
3459
3460   g_free (old_width);
3461
3462   /* redraw the list if it's not frozen */
3463   if (CLIST_UNFROZEN (clist))
3464     {
3465       if (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3466         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3467     }
3468 }
3469
3470 GtkStyle *
3471 gtk_cmclist_get_row_style (GtkCMCList *clist,
3472                          gint      row)
3473 {
3474   GtkCMCListRow *clist_row;
3475
3476   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), NULL);
3477
3478   if (row < 0 || row >= clist->rows)
3479     return NULL;
3480
3481   clist_row = ROW_ELEMENT (clist, row)->data;
3482
3483   return clist_row->style;
3484 }
3485
3486 /* PUBLIC SELECTION FUNCTIONS
3487  *   gtk_cmclist_set_selectable
3488  *   gtk_cmclist_get_selectable
3489  *   gtk_cmclist_select_row
3490  *   gtk_cmclist_unselect_row
3491  *   gtk_cmclist_select_all
3492  *   gtk_cmclist_unselect_all
3493  *   gtk_cmclist_undo_selection
3494  */
3495 void
3496 gtk_cmclist_set_selectable (GtkCMCList *clist,
3497                           gint      row,
3498                           gboolean  selectable)
3499 {
3500   GtkCMCListRow *clist_row;
3501
3502   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3503
3504   if (row < 0 || row >= clist->rows)
3505     return;
3506
3507   clist_row = ROW_ELEMENT (clist, row)->data;
3508
3509   if (selectable == clist_row->selectable)
3510     return;
3511
3512   clist_row->selectable = selectable;
3513
3514   if (!selectable && clist_row->state == GTK_STATE_SELECTED)
3515     {
3516       if (clist->anchor >= 0 &&
3517           clist->selection_mode == GTK_SELECTION_MULTIPLE)
3518         {
3519           clist->drag_button = 0;
3520           remove_grab (clist);
3521           GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3522         }
3523       g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3524                        row, -1, NULL);
3525     }      
3526 }
3527
3528 gboolean
3529 gtk_cmclist_get_selectable (GtkCMCList *clist,
3530                           gint      row)
3531 {
3532   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), FALSE);
3533
3534   if (row < 0 || row >= clist->rows)
3535     return FALSE;
3536
3537   return GTK_CMCLIST_ROW (ROW_ELEMENT (clist, row))->selectable;
3538 }
3539
3540 void
3541 gtk_cmclist_select_row (GtkCMCList *clist,
3542                       gint      row,
3543                       gint      column)
3544 {
3545   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3546
3547   if (row < 0 || row >= clist->rows)
3548     return;
3549   if (column < -1 || column >= clist->columns)
3550     return;
3551
3552   g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
3553                    row, column, NULL);
3554 }
3555
3556 void
3557 gtk_cmclist_unselect_row (GtkCMCList *clist,
3558                         gint      row,
3559                         gint      column)
3560 {
3561   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3562
3563   if (row < 0 || row >= clist->rows)
3564     return;
3565   if (column < -1 || column >= clist->columns)
3566     return;
3567
3568   g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3569                    row, column, NULL);
3570 }
3571
3572 void
3573 gtk_cmclist_select_all (GtkCMCList *clist)
3574 {
3575   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3576
3577   GTK_CMCLIST_GET_CLASS (clist)->select_all (clist);
3578 }
3579
3580 void
3581 gtk_cmclist_unselect_all (GtkCMCList *clist)
3582 {
3583   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3584
3585   GTK_CMCLIST_GET_CLASS (clist)->unselect_all (clist);
3586 }
3587
3588 void
3589 gtk_cmclist_undo_selection (GtkCMCList *clist)
3590 {
3591   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3592
3593   if (clist->selection_mode == GTK_SELECTION_MULTIPLE &&
3594       (clist->undo_selection || clist->undo_unselection))
3595     g_signal_emit (G_OBJECT (clist), clist_signals[UNDO_SELECTION], 0);
3596 }
3597
3598 /* PRIVATE SELECTION FUNCTIONS
3599  *   selection_find
3600  *   toggle_row
3601  *   fake_toggle_row
3602  *   toggle_focus_row
3603  *   toggle_add_mode
3604  *   real_select_row
3605  *   real_unselect_row
3606  *   real_select_all
3607  *   real_unselect_all
3608  *   fake_unselect_all
3609  *   real_undo_selection
3610  *   set_anchor
3611  *   resync_selection
3612  *   update_extended_selection
3613  *   start_selection
3614  *   end_selection
3615  *   extend_selection
3616  *   sync_selection
3617  */
3618 static GList *
3619 selection_find (GtkCMCList *clist,
3620                 gint      row_number,
3621                 GList    *row_list_element)
3622 {
3623   return g_list_find (clist->selection, GINT_TO_POINTER (row_number));
3624 }
3625
3626 static void
3627 toggle_row (GtkCMCList *clist,
3628             gint      row,
3629             gint      column,
3630             GdkEvent *event)
3631 {
3632   GtkCMCListRow *clist_row;
3633
3634   switch (clist->selection_mode)
3635     {
3636     case GTK_SELECTION_MULTIPLE:
3637     case GTK_SELECTION_SINGLE:
3638       clist_row = ROW_ELEMENT (clist, row)->data;
3639
3640       if (!clist_row)
3641         return;
3642
3643       if (clist_row->state == GTK_STATE_SELECTED)
3644         {
3645           g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3646                            row, column, event);
3647           return;
3648         }
3649     case GTK_SELECTION_BROWSE:
3650       g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
3651                        row, column, event);
3652       break;
3653     default:
3654       g_assert_not_reached ();
3655     }
3656 }
3657
3658 static void
3659 fake_toggle_row (GtkCMCList *clist,
3660                  gint      row)
3661 {
3662   GList *work;
3663
3664   work = ROW_ELEMENT (clist, row);
3665
3666   if (!work || !GTK_CMCLIST_ROW (work)->selectable)
3667     return;
3668   
3669   if (GTK_CMCLIST_ROW (work)->state == GTK_STATE_NORMAL)
3670     clist->anchor_state = GTK_CMCLIST_ROW (work)->state = GTK_STATE_SELECTED;
3671   else
3672     clist->anchor_state = GTK_CMCLIST_ROW (work)->state = GTK_STATE_NORMAL;
3673   
3674   if (CLIST_UNFROZEN (clist) &&
3675       gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3676     GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
3677                                           GTK_CMCLIST_ROW (work));
3678 }
3679
3680 static gboolean
3681 clist_has_grab (GtkCMCList *clist)
3682 {
3683   return (gtk_widget_has_grab (GTK_WIDGET(clist)) &&
3684           gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (clist))));
3685 }
3686
3687 static void
3688 toggle_focus_row (GtkCMCList *clist)
3689 {
3690   cm_return_if_fail (clist != 0);
3691   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3692
3693   if (clist_has_grab (clist) ||
3694       clist->focus_row < 0 || clist->focus_row >= clist->rows)
3695     return;
3696
3697   switch (clist->selection_mode)
3698     {
3699     case  GTK_SELECTION_SINGLE:
3700       toggle_row (clist, clist->focus_row, 0, NULL);
3701       break;
3702     case GTK_SELECTION_MULTIPLE:
3703       g_list_free (clist->undo_selection);
3704       g_list_free (clist->undo_unselection);
3705       clist->undo_selection = NULL;
3706       clist->undo_unselection = NULL;
3707
3708       clist->anchor = clist->focus_row;
3709       clist->drag_pos = clist->focus_row;
3710       clist->undo_anchor = clist->focus_row;
3711       
3712       if (GTK_CMCLIST_ADD_MODE(clist))
3713         fake_toggle_row (clist, clist->focus_row);
3714       else
3715         GTK_CMCLIST_GET_CLASS (clist)->fake_unselect_all (clist,clist->focus_row);
3716
3717       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3718       break;
3719     default:
3720       break;
3721     }
3722 }
3723
3724 static void
3725 toggle_add_mode (GtkCMCList *clist)
3726 {
3727   cm_return_if_fail (clist != 0);
3728   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3729   
3730   if (clist_has_grab (clist) ||
3731       clist->selection_mode != GTK_SELECTION_MULTIPLE)
3732     return;
3733
3734   gtk_cmclist_undraw_focus (GTK_WIDGET (clist));
3735   if (!GTK_CMCLIST_ADD_MODE(clist))
3736     {
3737       GTK_CMCLIST_SET_FLAG (clist, CMCLIST_ADD_MODE);
3738     }
3739   else
3740     {
3741       GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_ADD_MODE);
3742       clist->anchor_state = GTK_STATE_SELECTED;
3743     }
3744   gtk_cmclist_draw_focus (GTK_WIDGET (clist));
3745 }
3746
3747 static void
3748 real_select_row (GtkCMCList *clist,
3749                  gint      row,
3750                  gint      column,
3751                  GdkEvent *event)
3752 {
3753   GtkCMCListRow *clist_row;
3754   GList *list;
3755   gint sel_row;
3756   gboolean row_selected;
3757
3758   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3759
3760   if (row < 0 || row > (clist->rows - 1))
3761     return;
3762
3763   switch (clist->selection_mode)
3764     {
3765     case GTK_SELECTION_SINGLE:
3766     case GTK_SELECTION_BROWSE:
3767
3768       row_selected = FALSE;
3769       list = clist->selection;
3770
3771       while (list)
3772         {
3773           sel_row = GPOINTER_TO_INT (list->data);
3774           list = list->next;
3775
3776           if (row == sel_row)
3777             row_selected = TRUE;
3778           else
3779             g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3780                              sel_row, column, event);
3781         }
3782
3783       if (row_selected)
3784         return;
3785       
3786     default:
3787       break;
3788     }
3789
3790   clist_row = ROW_ELEMENT (clist, row)->data;
3791
3792   if (clist_row->state != GTK_STATE_NORMAL || !clist_row->selectable)
3793     return;
3794
3795   clist_row->state = GTK_STATE_SELECTED;
3796   if (!clist->selection)
3797     {
3798       clist->selection = g_list_append (clist->selection,
3799                                         GINT_TO_POINTER (row));
3800       clist->selection_end = clist->selection;
3801     }
3802   else
3803     clist->selection_end = 
3804       g_list_append (clist->selection_end, GINT_TO_POINTER (row))->next;
3805   
3806   if (CLIST_UNFROZEN (clist)
3807       && (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3808     GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3809 }
3810
3811 static void
3812 real_unselect_row (GtkCMCList *clist,
3813                    gint      row,
3814                    gint      column,
3815                    GdkEvent *event)
3816 {
3817   GtkCMCListRow *clist_row;
3818
3819   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3820
3821   if (row < 0 || row > (clist->rows - 1))
3822     return;
3823
3824   clist_row = ROW_ELEMENT (clist, row)->data;
3825
3826   if (clist_row->state == GTK_STATE_SELECTED)
3827     {
3828       clist_row->state = GTK_STATE_NORMAL;
3829
3830       if (clist->selection_end && 
3831           clist->selection_end->data == GINT_TO_POINTER (row))
3832         clist->selection_end = clist->selection_end->prev;
3833
3834       clist->selection = g_list_remove (clist->selection,
3835                                         GINT_TO_POINTER (row));
3836       
3837       if (CLIST_UNFROZEN (clist)
3838           && (gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3839         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
3840     }
3841 }
3842
3843 static void
3844 real_select_all (GtkCMCList *clist)
3845 {
3846   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3847
3848   if (clist_has_grab (clist))
3849     return;
3850
3851   switch (clist->selection_mode)
3852     {
3853     case GTK_SELECTION_SINGLE:
3854     case GTK_SELECTION_BROWSE:
3855       return;
3856
3857     case GTK_SELECTION_MULTIPLE:
3858       g_list_free (clist->undo_selection);
3859       g_list_free (clist->undo_unselection);
3860       clist->undo_selection = NULL;
3861       clist->undo_unselection = NULL;
3862           
3863       if (clist->rows &&
3864           ((GtkCMCListRow *) (clist->row_list->data))->state !=
3865           GTK_STATE_SELECTED)
3866         fake_toggle_row (clist, 0);
3867
3868       clist->anchor_state =  GTK_STATE_SELECTED;
3869       clist->anchor = 0;
3870       clist->drag_pos = 0;
3871       clist->undo_anchor = clist->focus_row;
3872       update_extended_selection (clist, clist->rows);
3873       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3874       return;
3875     default:
3876       g_assert_not_reached ();
3877     }
3878 }
3879
3880 static void
3881 real_unselect_all (GtkCMCList *clist)
3882 {
3883   GList *list;
3884   gint i;
3885  
3886   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3887
3888   if (clist_has_grab (clist))
3889     return;
3890
3891   switch (clist->selection_mode)
3892     {
3893     case GTK_SELECTION_BROWSE:
3894       if (clist->focus_row >= 0)
3895         {
3896           g_signal_emit (G_OBJECT (clist),
3897                            clist_signals[SELECT_ROW], 0,
3898                            clist->focus_row, -1, NULL);
3899           return;
3900         }
3901       break;
3902     case GTK_SELECTION_MULTIPLE:
3903       g_list_free (clist->undo_selection);
3904       g_list_free (clist->undo_unselection);
3905       clist->undo_selection = NULL;
3906       clist->undo_unselection = NULL;
3907
3908       clist->anchor = -1;
3909       clist->drag_pos = -1;
3910       clist->undo_anchor = clist->focus_row;
3911       break;
3912     default:
3913       break;
3914     }
3915
3916   list = clist->selection;
3917   while (list)
3918     {
3919       i = GPOINTER_TO_INT (list->data);
3920       list = list->next;
3921       g_signal_emit (G_OBJECT (clist),
3922                        clist_signals[UNSELECT_ROW], 0, i, -1, NULL);
3923     }
3924 }
3925
3926 static void
3927 fake_unselect_all (GtkCMCList *clist,
3928                    gint      row)
3929 {
3930   GList *list;
3931   GList *work;
3932   gint i;
3933
3934   if (row >= 0 && (work = ROW_ELEMENT (clist, row)))
3935     {
3936       if (GTK_CMCLIST_ROW (work)->state == GTK_STATE_NORMAL &&
3937           GTK_CMCLIST_ROW (work)->selectable)
3938         {
3939           GTK_CMCLIST_ROW (work)->state = GTK_STATE_SELECTED;
3940           
3941           if (CLIST_UNFROZEN (clist) &&
3942               gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
3943             GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
3944                                                   GTK_CMCLIST_ROW (work));
3945         }  
3946     }
3947
3948   clist->undo_selection = clist->selection;
3949   clist->selection = NULL;
3950   clist->selection_end = NULL;
3951
3952   for (list = clist->undo_selection; list; list = list->next)
3953     {
3954       if ((i = GPOINTER_TO_INT (list->data)) == row ||
3955           !(work = g_list_nth (clist->row_list, i)))
3956         continue;
3957
3958       GTK_CMCLIST_ROW (work)->state = GTK_STATE_NORMAL;
3959       if (CLIST_UNFROZEN (clist) &&
3960           gtk_cmclist_row_is_visible (clist, i) != GTK_VISIBILITY_NONE)
3961         GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, i,
3962                                               GTK_CMCLIST_ROW (work));
3963     }
3964 }
3965
3966 static void
3967 real_undo_selection (GtkCMCList *clist)
3968 {
3969   GList *work;
3970
3971   cm_return_if_fail (GTK_IS_CMCLIST (clist));
3972
3973   if (clist_has_grab (clist) ||
3974       clist->selection_mode != GTK_SELECTION_MULTIPLE)
3975     return;
3976
3977   GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3978
3979   if (!(clist->undo_selection || clist->undo_unselection))
3980     {
3981       gtk_cmclist_unselect_all (clist);
3982       return;
3983     }
3984
3985   for (work = clist->undo_selection; work; work = work->next)
3986     g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
3987                      GPOINTER_TO_INT (work->data), -1, NULL);
3988
3989   for (work = clist->undo_unselection; work; work = work->next)
3990     {
3991       /* g_print ("unselect %d\n",GPOINTER_TO_INT (work->data)); */
3992       g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
3993                        GPOINTER_TO_INT (work->data), -1, NULL);
3994     }
3995
3996   if (gtk_widget_has_focus(GTK_WIDGET(clist)) && clist->focus_row != clist->undo_anchor)
3997     {
3998       gtk_cmclist_undraw_focus (GTK_WIDGET (clist));
3999       clist->focus_row = clist->undo_anchor;
4000       gtk_cmclist_draw_focus (GTK_WIDGET (clist));
4001     }
4002   else
4003     clist->focus_row = clist->undo_anchor;
4004   
4005   clist->undo_anchor = -1;
4006  
4007   g_list_free (clist->undo_selection);
4008   g_list_free (clist->undo_unselection);
4009   clist->undo_selection = NULL;
4010   clist->undo_unselection = NULL;
4011
4012   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
4013       clist->clist_window_height)
4014     gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
4015   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
4016     gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4017 }
4018
4019 static void
4020 set_anchor (GtkCMCList *clist,
4021             gboolean  add_mode,
4022             gint      anchor,
4023             gint      undo_anchor)
4024 {
4025   cm_return_if_fail (GTK_IS_CMCLIST (clist));
4026   
4027   if (clist->selection_mode != GTK_SELECTION_MULTIPLE || clist->anchor >= 0)
4028     return;
4029
4030   g_list_free (clist->undo_selection);
4031   g_list_free (clist->undo_unselection);
4032   clist->undo_selection = NULL;
4033   clist->undo_unselection = NULL;
4034
4035   if (add_mode)
4036     fake_toggle_row (clist, anchor);
4037   else
4038     {
4039       GTK_CMCLIST_GET_CLASS (clist)->fake_unselect_all (clist, anchor);
4040       clist->anchor_state = GTK_STATE_SELECTED;
4041     }
4042
4043   clist->anchor = anchor;
4044   clist->drag_pos = anchor;
4045   clist->undo_anchor = undo_anchor;
4046 }
4047
4048 static void
4049 resync_selection (GtkCMCList *clist,
4050                   GdkEvent *event)
4051 {
4052   gint i;
4053   gint e;
4054   gint row;
4055   GList *list;
4056   GtkCMCListRow *clist_row;
4057
4058   if (clist->selection_mode != GTK_SELECTION_MULTIPLE)
4059     return;
4060
4061   if (clist->anchor < 0 || clist->drag_pos < 0)
4062     return;
4063
4064   gtk_cmclist_freeze (clist);
4065
4066   i = MIN (clist->anchor, clist->drag_pos);
4067   e = MAX (clist->anchor, clist->drag_pos);
4068
4069   if (clist->undo_selection)
4070     {
4071       list = clist->selection;
4072       clist->selection = clist->undo_selection;
4073       clist->selection_end = g_list_last (clist->selection);
4074       clist->undo_selection = list;
4075       list = clist->selection;
4076       while (list)
4077         {
4078           row = GPOINTER_TO_INT (list->data);
4079           list = list->next;
4080           if (row < i || row > e)
4081             {
4082               clist_row = g_list_nth (clist->row_list, row)->data;
4083               if (clist_row->selectable)
4084                 {
4085                   clist_row->state = GTK_STATE_SELECTED;
4086                   g_signal_emit (G_OBJECT (clist),
4087                                    clist_signals[UNSELECT_ROW], 0,
4088                                    row, -1, event);
4089                   clist->undo_selection = g_list_prepend
4090                     (clist->undo_selection, GINT_TO_POINTER (row));
4091                 }
4092             }
4093         }
4094     }    
4095
4096   if (clist->anchor < clist->drag_pos)
4097     {
4098       for (list = g_list_nth (clist->row_list, i); i <= e;
4099            i++, list = list->next)
4100         if (GTK_CMCLIST_ROW (list)->selectable)
4101           {
4102             if (g_list_find (clist->selection, GINT_TO_POINTER(i)))
4103               {
4104                 if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_NORMAL)
4105                   {
4106                     GTK_CMCLIST_ROW (list)->state = GTK_STATE_SELECTED;
4107                     g_signal_emit (G_OBJECT (clist),
4108                                      clist_signals[UNSELECT_ROW], 0,
4109                                      i, -1, event);
4110                     clist->undo_selection =
4111                       g_list_prepend (clist->undo_selection,
4112                                       GINT_TO_POINTER (i));
4113                   }
4114               }
4115             else if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_SELECTED)
4116               {
4117                 GTK_CMCLIST_ROW (list)->state = GTK_STATE_NORMAL;
4118                 clist->undo_unselection =
4119                   g_list_prepend (clist->undo_unselection,
4120                                   GINT_TO_POINTER (i));
4121               }
4122           }
4123     }
4124   else
4125     {
4126       for (list = g_list_nth (clist->row_list, e); i <= e;
4127            e--, list = list->prev)
4128         if (GTK_CMCLIST_ROW (list)->selectable)
4129           {
4130             if (g_list_find (clist->selection, GINT_TO_POINTER(e)))
4131               {
4132                 if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_NORMAL)
4133                   {
4134                     GTK_CMCLIST_ROW (list)->state = GTK_STATE_SELECTED;
4135                     g_signal_emit (G_OBJECT (clist),
4136                                      clist_signals[UNSELECT_ROW], 0,
4137                                      e, -1, event);
4138                     clist->undo_selection =
4139                       g_list_prepend (clist->undo_selection,
4140                                       GINT_TO_POINTER (e));
4141                   }
4142               }
4143             else if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_SELECTED)
4144               {
4145                 GTK_CMCLIST_ROW (list)->state = GTK_STATE_NORMAL;
4146                 clist->undo_unselection =
4147                   g_list_prepend (clist->undo_unselection,
4148                                   GINT_TO_POINTER (e));
4149               }
4150           }
4151     }
4152   
4153   clist->undo_unselection = g_list_reverse (clist->undo_unselection);
4154   for (list = clist->undo_unselection; list; list = list->next)
4155     g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
4156                      GPOINTER_TO_INT (list->data), -1, event);
4157
4158   clist->anchor = -1;
4159   clist->drag_pos = -1;
4160
4161   gtk_cmclist_thaw (clist);
4162 }
4163
4164 static void
4165 update_extended_selection (GtkCMCList *clist,
4166                            gint      row)
4167 {
4168   gint i;
4169   GList *list;
4170   GdkRectangle area;
4171   gint s1 = -1;
4172   gint s2 = -1;
4173   gint e1 = -1;
4174   gint e2 = -1;
4175   gint y1 = clist->clist_window_height;
4176   gint y2 = clist->clist_window_height;
4177   gint h1 = 0;
4178   gint h2 = 0;
4179   gint top;
4180
4181   if (clist->selection_mode != GTK_SELECTION_MULTIPLE || clist->anchor == -1)
4182     return;
4183
4184   if (row < 0)
4185     row = 0;
4186   if (row >= clist->rows)
4187     row = clist->rows - 1;
4188
4189   /* extending downwards */
4190   if (row > clist->drag_pos && clist->anchor <= clist->drag_pos)
4191     {
4192       s2 = clist->drag_pos + 1;
4193       e2 = row;
4194     }
4195   /* extending upwards */
4196   else if (row < clist->drag_pos && clist->anchor >= clist->drag_pos)
4197     {
4198       s2 = row;
4199       e2 = clist->drag_pos - 1;
4200     }
4201   else if (row < clist->drag_pos && clist->anchor < clist->drag_pos)
4202     {
4203       e1 = clist->drag_pos;
4204       /* row and drag_pos on different sides of anchor :
4205          take back the selection between anchor and drag_pos,
4206          select between anchor and row */
4207       if (row < clist->anchor)
4208         {
4209           s1 = clist->anchor + 1;
4210           s2 = row;
4211           e2 = clist->anchor - 1;
4212         }
4213       /* take back the selection between anchor and drag_pos */
4214       else
4215         s1 = row + 1;
4216     }
4217   else if (row > clist->drag_pos && clist->anchor > clist->drag_pos)
4218     {
4219       s1 = clist->drag_pos;
4220       /* row and drag_pos on different sides of anchor :
4221          take back the selection between anchor and drag_pos,
4222          select between anchor and row */
4223       if (row > clist->anchor)
4224         {
4225           e1 = clist->anchor - 1;
4226           s2 = clist->anchor + 1;
4227           e2 = row;
4228         }
4229       /* take back the selection between anchor and drag_pos */
4230       else
4231         e1 = row - 1;
4232     }
4233
4234   clist->drag_pos = row;
4235
4236   area.x = 0;
4237   area.width = clist->clist_window_width;
4238
4239   /* restore the elements between s1 and e1 */
4240   if (s1 >= 0)
4241     {
4242       for (i = s1, list = g_list_nth (clist->row_list, i); i <= e1;
4243            i++, list = list->next)
4244         if (GTK_CMCLIST_ROW (list)->selectable)
4245           {
4246             if (GTK_CMCLIST_GET_CLASS (clist)->selection_find (clist, i, list))
4247               GTK_CMCLIST_ROW (list)->state = GTK_STATE_SELECTED;
4248             else
4249               GTK_CMCLIST_ROW (list)->state = GTK_STATE_NORMAL;
4250           }
4251
4252       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4253
4254       if (top + clist->row_height <= 0)
4255         {
4256           area.y = 0;
4257           area.height = ROW_TOP_YPIXEL (clist, e1) + clist->row_height;
4258           draw_rows (clist, &area);
4259           gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4260         }
4261       else if (top >= clist->clist_window_height)
4262         {
4263           area.y = ROW_TOP_YPIXEL (clist, s1) - 1;
4264           area.height = clist->clist_window_height - area.y;
4265           draw_rows (clist, &area);
4266           gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
4267         }
4268       else if (top < 0)
4269         gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4270       else if (top + clist->row_height > clist->clist_window_height)
4271         gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
4272
4273       y1 = ROW_TOP_YPIXEL (clist, s1) - 1;
4274       h1 = (e1 - s1 + 1) * (clist->row_height + CELL_SPACING);
4275     }
4276
4277   /* extend the selection between s2 and e2 */
4278   if (s2 >= 0)
4279     {
4280       for (i = s2, list = g_list_nth (clist->row_list, i); i <= e2;
4281            i++, list = list->next)
4282         if (GTK_CMCLIST_ROW (list)->selectable &&
4283             GTK_CMCLIST_ROW (list)->state != clist->anchor_state)
4284           GTK_CMCLIST_ROW (list)->state = clist->anchor_state;
4285
4286       top = ROW_TOP_YPIXEL (clist, clist->focus_row);
4287
4288       if (top + clist->row_height <= 0)
4289         {
4290           area.y = 0;
4291           area.height = ROW_TOP_YPIXEL (clist, e2) + clist->row_height;
4292           draw_rows (clist, &area);
4293           gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4294         }
4295       else if (top >= clist->clist_window_height)
4296         {
4297           area.y = ROW_TOP_YPIXEL (clist, s2) - 1;
4298           area.height = clist->clist_window_height - area.y;
4299           draw_rows (clist, &area);
4300           gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
4301         }
4302       else if (top < 0)
4303         gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4304       else if (top + clist->row_height > clist->clist_window_height)
4305         gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
4306
4307       y2 = ROW_TOP_YPIXEL (clist, s2) - 1;
4308       h2 = (e2 - s2 + 1) * (clist->row_height + CELL_SPACING);
4309     }
4310
4311   area.y = MAX (0, MIN (y1, y2));
4312   if (area.y > clist->clist_window_height)
4313     area.y = 0;
4314   area.height = MIN (clist->clist_window_height, h1 + h2);
4315   if (s1 >= 0 && s2 >= 0)
4316     area.height += (clist->row_height + CELL_SPACING);
4317   draw_rows (clist, &area);
4318 }
4319
4320 static void
4321 start_selection (GtkCMCList *clist)
4322 {
4323   cm_return_if_fail (GTK_IS_CMCLIST (clist));
4324
4325   if (clist_has_grab (clist))
4326     return;
4327
4328   set_anchor (clist, GTK_CMCLIST_ADD_MODE(clist), clist->focus_row,
4329               clist->focus_row);
4330 }
4331
4332 static void
4333 end_selection (GtkCMCList *clist)
4334 {
4335   cm_return_if_fail (GTK_IS_CMCLIST (clist));
4336
4337   if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (clist))) &&
4338       gtk_widget_has_focus (GTK_WIDGET(clist)))
4339     return;
4340
4341   GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4342 }
4343
4344 static void
4345 extend_selection (GtkCMCList      *clist,
4346                   GtkScrollType  scroll_type,
4347                   gfloat         position,
4348                   gboolean       auto_start_selection)
4349 {
4350   cm_return_if_fail (GTK_IS_CMCLIST (clist));
4351
4352   if (clist_has_grab (clist) ||
4353       clist->selection_mode != GTK_SELECTION_MULTIPLE)
4354     return;
4355
4356   if (auto_start_selection)
4357     set_anchor (clist, GTK_CMCLIST_ADD_MODE(clist), clist->focus_row,
4358                 clist->focus_row);
4359   else if (clist->anchor == -1)
4360     return;
4361
4362   move_focus_row (clist, scroll_type, position);
4363
4364   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
4365       clist->clist_window_height)
4366     gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
4367   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
4368     gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
4369
4370   update_extended_selection (clist, clist->focus_row);
4371 }
4372
4373 static void
4374 sync_selection (GtkCMCList *clist,
4375                 gint      row,
4376                 gint      mode)
4377 {
4378   GList *list;
4379   gint d;
4380
4381   if (mode == SYNC_INSERT)
4382     d = 1;
4383   else
4384     d = -1;
4385       
4386   if (clist->focus_row >= row)
4387     {
4388       if (d > 0 || clist->focus_row > row)
4389         clist->focus_row += d;
4390       if (clist->focus_row == -1 && clist->rows >= 1)
4391         clist->focus_row = 0;
4392       else if (d < 0 && clist->focus_row >= clist->rows - 1)
4393         clist->focus_row = clist->rows - 2;
4394       else if (clist->focus_row >= clist->rows) /* Paranoia */
4395         clist->focus_row = clist->rows - 1;
4396     }
4397
4398   GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4399
4400   g_list_free (clist->undo_selection);
4401   g_list_free (clist->undo_unselection);
4402   clist->undo_selection = NULL;
4403   clist->undo_unselection = NULL;
4404
4405   clist->anchor = -1;
4406   clist->drag_pos = -1;
4407   clist->undo_anchor = clist->focus_row;
4408
4409   list = clist->selection;
4410
4411   while (list)
4412     {
4413       if (GPOINTER_TO_INT (list->data) >= row)
4414         list->data = ((gchar*) list->data) + d;
4415       list = list->next;
4416     }
4417 }
4418
4419 /* GTKOBJECT
4420  *   gtk_cmclist_destroy
4421  *   gtk_cmclist_finalize
4422  */
4423 static void
4424 gtk_cmclist_destroy (GtkObject *object)
4425 {
4426   gint i;
4427   GtkCMCList *clist;
4428
4429   cm_return_if_fail (GTK_IS_CMCLIST (object));
4430
4431   clist = GTK_CMCLIST (object);
4432
4433   /* freeze the list */
4434   clist->freeze_count++;
4435
4436   /* get rid of all the rows */
4437   gtk_cmclist_clear (clist);
4438
4439   /* Since we don't have a _remove method, unparent the children
4440    * instead of destroying them so the focus will be unset properly.
4441    * (For other containers, the _remove method takes care of the
4442    * unparent) The destroy will happen when the refcount drops
4443    * to zero.
4444    */
4445
4446   /* unref adjustments */
4447   if (clist->hadjustment)
4448     {
4449       g_signal_handlers_disconnect_matched(G_OBJECT (clist->hadjustment), G_SIGNAL_MATCH_DATA,
4450                         0, 0, 0, 0, clist);
4451       g_object_unref (G_OBJECT (clist->hadjustment));
4452       clist->hadjustment = NULL;
4453     }
4454   if (clist->vadjustment)
4455     {
4456       g_signal_handlers_disconnect_matched(G_OBJECT (clist->vadjustment), G_SIGNAL_MATCH_DATA,
4457                         0, 0, 0, 0, clist);
4458       g_object_unref (G_OBJECT (clist->vadjustment));
4459       clist->vadjustment = NULL;
4460     }
4461
4462   remove_grab (clist);
4463
4464   /* destroy the column buttons */
4465   for (i = 0; i < clist->columns; i++)
4466     if (clist->column[i].button)
4467       {
4468         gtk_widget_unparent (clist->column[i].button);
4469         clist->column[i].button = NULL;
4470       }
4471
4472   if (GTK_OBJECT_CLASS (parent_class)->destroy)
4473     (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
4474 }
4475
4476 static void
4477 gtk_cmclist_finalize (GObject *object)
4478 {
4479   GtkCMCList *clist;
4480
4481   cm_return_if_fail (GTK_IS_CMCLIST (object));
4482
4483   clist = GTK_CMCLIST (object);
4484
4485   columns_delete (clist);
4486
4487 #if !GLIB_CHECK_VERSION(2,10,0)
4488   g_mem_chunk_destroy (clist->cell_mem_chunk);
4489   g_mem_chunk_destroy (clist->row_mem_chunk);
4490 #endif
4491   G_OBJECT_CLASS (parent_class)->finalize (object);
4492 }
4493
4494 /* GTKWIDGET
4495  *   gtk_cmclist_realize
4496  *   gtk_cmclist_unrealize
4497  *   gtk_cmclist_map
4498  *   gtk_cmclist_unmap
4499  *   gtk_cmclist_expose
4500  *   gtk_cmclist_style_set
4501  *   gtk_cmclist_button_press
4502  *   gtk_cmclist_button_release
4503  *   gtk_cmclist_motion
4504  *   gtk_cmclist_size_request
4505  *   gtk_cmclist_size_allocate
4506  */
4507 static void
4508 gtk_cmclist_realize (GtkWidget *widget)
4509 {
4510   GtkAllocation allocation;
4511   GtkCMCList *clist;
4512   GtkStyle *style;
4513   GdkWindow *window;
4514   GdkWindowAttr attributes;
4515   GdkGCValues values;
4516   GtkCMCListRow *clist_row;
4517   GList *list;
4518   gint attributes_mask;
4519   gint border_width;
4520   gint i;
4521   gint j;
4522
4523   cm_return_if_fail (GTK_IS_CMCLIST (widget));
4524
4525   clist = GTK_CMCLIST (widget);
4526
4527   gtkut_widget_set_realized (widget, TRUE);
4528
4529   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
4530
4531   gtk_widget_get_allocation (widget, &allocation);
4532   
4533   attributes.window_type = GDK_WINDOW_CHILD;
4534   attributes.x = allocation.x + border_width;
4535   attributes.y = allocation.y + border_width;
4536   attributes.width = allocation.width - border_width * 2;
4537   attributes.height = allocation.height - border_width * 2;
4538   attributes.wclass = GDK_INPUT_OUTPUT;
4539   attributes.visual = gtk_widget_get_visual (widget);
4540   attributes.colormap = gtk_widget_get_colormap (widget);
4541   attributes.event_mask = gtk_widget_get_events (widget);
4542   attributes.event_mask |= (GDK_EXPOSURE_MASK |
4543                             GDK_BUTTON_PRESS_MASK |
4544                             GDK_BUTTON_RELEASE_MASK |
4545                             GDK_KEY_RELEASE_MASK);
4546   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
4547
4548   /* main window */
4549   window = gtk_widget_get_window (widget);
4550   window = gdk_window_new (gtk_widget_get_parent_window (widget),
4551                                    &attributes, attributes_mask);
4552   gdk_window_set_user_data (window, clist);
4553   gtk_widget_set_window (widget, window);
4554
4555   style = gtk_widget_get_style (widget);
4556   style = gtk_style_attach (style, window);
4557   gtk_widget_set_style (widget, style);
4558
4559   gtk_style_set_background (style, window, GTK_STATE_NORMAL);
4560
4561   /* column-title window */
4562
4563   attributes.x = clist->column_title_area.x;
4564   attributes.y = clist->column_title_area.y;
4565   attributes.width = clist->column_title_area.width;
4566   attributes.height = clist->column_title_area.height;
4567   
4568   clist->title_window = gdk_window_new (window, &attributes,
4569                                         attributes_mask);
4570   gdk_window_set_user_data (clist->title_window, clist);
4571
4572   gtk_style_set_background (style, clist->title_window,
4573                             GTK_STATE_NORMAL);
4574   gdk_window_show (clist->title_window);
4575
4576   /* set things up so column buttons are drawn in title window */
4577   for (i = 0; i < clist->columns; i++)
4578     if (clist->column[i].button)
4579       gtk_widget_set_parent_window (clist->column[i].button,
4580                                     clist->title_window);
4581
4582   /* clist-window */
4583   attributes.x = (clist->internal_allocation.x +
4584                   style->xthickness);
4585   attributes.y = (clist->internal_allocation.y +
4586                   style->ythickness +
4587                   clist->column_title_area.height);
4588   attributes.width = clist->clist_window_width;
4589   attributes.height = clist->clist_window_height;
4590   
4591   clist->clist_window = gdk_window_new (window, &attributes,
4592                                         attributes_mask);
4593   gdk_window_set_user_data (clist->clist_window, clist);
4594
4595   gdk_window_set_background (clist->clist_window,
4596                              &style->base[GTK_STATE_NORMAL]);
4597   gdk_window_show (clist->clist_window);
4598   gdk_drawable_get_size (clist->clist_window, &clist->clist_window_width,
4599                        &clist->clist_window_height);
4600
4601   /* create resize windows */
4602   attributes.wclass = GDK_INPUT_ONLY;
4603   attributes.event_mask = (GDK_BUTTON_PRESS_MASK |
4604                            GDK_BUTTON_RELEASE_MASK |
4605                            GDK_POINTER_MOTION_MASK |
4606                            GDK_POINTER_MOTION_HINT_MASK);
4607   attributes_mask = GDK_WA_CURSOR;
4608   attributes.cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
4609                                                   GDK_SB_H_DOUBLE_ARROW);
4610   clist->cursor_drag = attributes.cursor;
4611
4612   attributes.x =  LIST_WIDTH (clist) + 1;
4613   attributes.y = 0;
4614   attributes.width = 0;
4615   attributes.height = 0;
4616
4617   for (i = 0; i < clist->columns; i++)
4618     {
4619       clist->column[i].window = gdk_window_new (clist->title_window,
4620                                                 &attributes, attributes_mask);
4621       gdk_window_set_user_data (clist->column[i].window, clist);
4622     }
4623
4624   /* This is slightly less efficient than creating them with the
4625    * right size to begin with, but easier
4626    */
4627   size_allocate_title_buttons (clist);
4628
4629   /* attach optional row/cell styles, allocate foreground/background colors */
4630   list = clist->row_list;
4631   for (i = 0; i < clist->rows; i++)
4632     {
4633       clist_row = list->data;
4634       list = list->next;
4635
4636       if (clist_row->style)
4637         clist_row->style = gtk_style_attach (clist_row->style,
4638                                              clist->clist_window);
4639
4640       if (clist_row->fg_set || clist_row->bg_set)
4641         {
4642           GdkColormap *colormap;
4643
4644           colormap = gtk_widget_get_colormap (widget);
4645           if (clist_row->fg_set)
4646             gdk_colormap_alloc_color (colormap, &clist_row->foreground, TRUE, TRUE);
4647           if (clist_row->bg_set)
4648             gdk_colormap_alloc_color (colormap, &clist_row->background, TRUE, TRUE);
4649         }
4650       
4651       for (j = 0; j < clist->columns; j++)
4652         if  (clist_row->cell[j].style)
4653           clist_row->cell[j].style =
4654             gtk_style_attach (clist_row->cell[j].style, clist->clist_window);
4655     }
4656 }
4657
4658 static void
4659 gtk_cmclist_unrealize (GtkWidget *widget)
4660 {
4661   gint i;
4662   GtkCMCList *clist;
4663
4664   cm_return_if_fail (GTK_IS_CMCLIST (widget));
4665
4666   clist = GTK_CMCLIST (widget);
4667
4668   /* freeze the list */
4669   clist->freeze_count++;
4670
4671   if (gtk_widget_get_mapped (widget))
4672     gtk_cmclist_unmap (widget);
4673
4674   gtkut_widget_set_mapped (widget, FALSE);
4675
4676   /* detach optional row/cell styles */
4677   if (gtk_widget_get_realized (widget))
4678     {
4679       GtkCMCListRow *clist_row;
4680       GList *list;
4681       gint j;
4682
4683       list = clist->row_list;
4684       for (i = 0; i < clist->rows; i++)
4685         {
4686           clist_row = list->data;
4687           list = list->next;
4688
4689           if (clist_row->style)
4690             gtk_style_detach (clist_row->style);
4691           for (j = 0; j < clist->columns; j++)
4692             if  (clist_row->cell[j].style)
4693               gtk_style_detach (clist_row->cell[j].style);
4694         }
4695     }
4696
4697   gdk_cursor_unref (clist->cursor_drag);
4698
4699   for (i = 0; i < clist->columns; i++)
4700     {
4701       if (clist->column[i].button)
4702         gtk_widget_unrealize (clist->column[i].button);
4703       if (clist->column[i].window)
4704         {
4705           gdk_window_set_user_data (clist->column[i].window, NULL);
4706           gdk_window_destroy (clist->column[i].window);
4707           clist->column[i].window = NULL;
4708         }
4709     }
4710
4711   gdk_window_set_user_data (clist->clist_window, NULL);
4712   gdk_window_destroy (clist->clist_window);
4713   clist->clist_window = NULL;
4714
4715   gdk_window_set_user_data (clist->title_window, NULL);
4716   gdk_window_destroy (clist->title_window);
4717   clist->title_window = NULL;
4718
4719   clist->cursor_drag = NULL;
4720
4721   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
4722     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
4723 }
4724
4725 static void
4726 gtk_cmclist_map (GtkWidget *widget)
4727 {
4728   gint i;
4729   GtkCMCList *clist;
4730
4731   cm_return_if_fail (GTK_IS_CMCLIST (widget));
4732
4733   clist = GTK_CMCLIST (widget);
4734
4735   if (!gtk_widget_get_mapped (widget))
4736     {
4737       gtkut_widget_set_mapped (widget, TRUE);
4738
4739       /* map column buttons */
4740       for (i = 0; i < clist->columns; i++)
4741         {
4742           if (clist->column[i].button &&
4743               gtk_widget_get_visible (clist->column[i].button) &&
4744               !gtk_widget_get_mapped (clist->column[i].button))
4745             gtk_widget_map (clist->column[i].button);
4746         }
4747       
4748       for (i = 0; i < clist->columns; i++)
4749         if (clist->column[i].window && clist->column[i].button)
4750           {
4751             gdk_window_raise (clist->column[i].window);
4752             gdk_window_show (clist->column[i].window);
4753           }
4754
4755       gdk_window_show (clist->title_window);
4756       gdk_window_show (clist->clist_window);
4757       gdk_window_show (gtk_widget_get_window (widget));
4758
4759       /* unfreeze the list */
4760       clist->freeze_count = 0;
4761     }
4762 }
4763
4764 static void
4765 gtk_cmclist_unmap (GtkWidget *widget)
4766 {
4767   gint i;
4768   GtkCMCList *clist;
4769
4770   cm_return_if_fail (GTK_IS_CMCLIST (widget));
4771
4772   clist = GTK_CMCLIST (widget);
4773
4774   if (gtk_widget_get_mapped (widget))
4775     {
4776       gtkut_widget_set_mapped (widget, FALSE);
4777
4778       if (clist_has_grab (clist))
4779         {
4780           remove_grab (clist);
4781
4782           GTK_CMCLIST_GET_CLASS (widget)->resync_selection (clist, NULL);
4783
4784           clist->click_cell.row = -1;
4785           clist->click_cell.column = -1;
4786           clist->drag_button = 0;
4787
4788           if (GTK_CMCLIST_IN_DRAG(clist))
4789             {
4790               gpointer drag_data;
4791
4792               GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_IN_DRAG);
4793               drag_data = g_object_get_data (G_OBJECT (clist),
4794                                                "gtk-site-data");
4795               if (drag_data)
4796                 g_signal_handlers_unblock_matched(G_OBJECT(clist), G_SIGNAL_MATCH_DATA,
4797                                         0, 0, 0, 0, drag_data);
4798             }
4799         }
4800
4801       for (i = 0; i < clist->columns; i++)
4802         if (clist->column[i].window)
4803           gdk_window_hide (clist->column[i].window);
4804
4805       gdk_window_hide (clist->clist_window);
4806       gdk_window_hide (clist->title_window);
4807       gdk_window_hide (gtk_widget_get_window (widget));
4808
4809       /* unmap column buttons */
4810       for (i = 0; i < clist->columns; i++)
4811         if (clist->column[i].button &&
4812             gtk_widget_get_mapped (clist->column[i].button))
4813           gtk_widget_unmap (clist->column[i].button);
4814
4815       /* freeze the list */
4816       clist->freeze_count++;
4817     }
4818 }
4819
4820 static gint
4821 gtk_cmclist_expose (GtkWidget      *widget,
4822                   GdkEventExpose *event)
4823 {
4824   GdkWindow *window;
4825   GtkCMCList *clist;
4826   GtkStyle *style;
4827
4828   cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
4829   cm_return_val_if_fail (event != NULL, FALSE);
4830
4831   window = gtk_widget_get_window (widget);
4832   style = gtk_widget_get_style (widget);
4833
4834   if (gtk_widget_is_drawable (widget))
4835     {
4836       clist = GTK_CMCLIST (widget);
4837
4838       /* draw border */
4839       if (event->window == window)
4840         gtk_paint_shadow (style, window,
4841                          GTK_STATE_NORMAL, clist->shadow_type,
4842                          NULL, NULL, NULL,
4843                          0, 0,
4844                          clist->clist_window_width +
4845                          (2 * style->xthickness),
4846                          clist->clist_window_height +
4847                          (2 * style->ythickness) +
4848                          clist->column_title_area.height);
4849
4850       /* exposure events on the list */
4851       if (event->window == clist->clist_window)
4852         draw_rows (clist, &event->area);
4853
4854       if (event->window == clist->title_window)
4855         {
4856           gint i;
4857           
4858           for (i = 0; i < clist->columns; i++)
4859             {
4860               if (clist->column[i].button) {
4861                 gtk_container_propagate_expose (GTK_CONTAINER (clist),
4862                                                 clist->column[i].button,
4863                                                 event);
4864               }
4865             }
4866         }
4867     }
4868
4869   return FALSE;
4870 }
4871
4872 static void
4873 gtk_cmclist_style_set (GtkWidget *widget,
4874                      GtkStyle  *previous_style)
4875 {
4876   GtkCMCList *clist;
4877   GtkStyle *style;
4878
4879   cm_return_if_fail (GTK_IS_CMCLIST (widget));
4880
4881   if (GTK_WIDGET_CLASS (parent_class)->style_set)
4882     (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
4883
4884   clist = GTK_CMCLIST (widget);
4885
4886   if (gtk_widget_get_realized (widget))
4887     {
4888       style = gtk_widget_get_style (widget);
4889       gtk_style_set_background (style, gtk_widget_get_window (widget),
4890                                 gtk_widget_get_state(widget));
4891       gtk_style_set_background (style, clist->title_window, GTK_STATE_NORMAL);
4892       gdk_window_set_background (clist->clist_window, &style->base[GTK_STATE_NORMAL]);
4893     }
4894
4895   /* Fill in data after widget has correct style */
4896
4897   /* text properties */
4898   if (!GTK_CMCLIST_ROW_HEIGHT_SET(clist))
4899     /* Reset clist->row_height */
4900     gtk_cmclist_set_row_height (clist, 0);
4901
4902   /* Column widths */
4903   if (!GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4904     {
4905       gint width;
4906       gint i;
4907
4908       for (i = 0; i < clist->columns; i++)
4909         if (clist->column[i].auto_resize)
4910           {
4911             width = gtk_cmclist_optimal_column_width (clist, i);
4912             if (width != clist->column[i].width)
4913               gtk_cmclist_set_column_width (clist, i, width);
4914           }
4915     }
4916 }
4917
4918 static gint
4919 gtk_cmclist_button_press (GtkWidget      *widget,
4920                         GdkEventButton *event)
4921 {
4922   gint i;
4923   GtkCMCList *clist;
4924   gint x;
4925   gint y;
4926   gint row;
4927   gint column;
4928   gint button_actions;
4929
4930   cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
4931   cm_return_val_if_fail (event != NULL, FALSE);
4932
4933   clist = GTK_CMCLIST (widget);
4934
4935   button_actions = clist->button_actions[event->button - 1];
4936
4937   if (button_actions == GTK_CMBUTTON_IGNORED)
4938     return FALSE;
4939
4940   /* selections on the list */
4941   if (event->window == clist->clist_window)
4942     {
4943       x = event->x;
4944       y = event->y;
4945
4946       if (get_selection_info (clist, x, y, &row, &column))
4947         {
4948           gint old_row = clist->focus_row;
4949
4950           if (clist->focus_row == -1)
4951             old_row = row;
4952
4953           if (event->type == GDK_BUTTON_PRESS)
4954             {
4955               GdkEventMask mask = ((1 << (4 + event->button)) |
4956                                    GDK_POINTER_MOTION_HINT_MASK |
4957                                    GDK_BUTTON_RELEASE_MASK);
4958
4959               if (gdk_pointer_grab (clist->clist_window, FALSE, mask,
4960                                     NULL, NULL, event->time))
4961                 return FALSE;
4962               gtk_grab_add (widget);
4963
4964               clist->click_cell.row = row;
4965               clist->click_cell.column = column;
4966               clist->drag_button = event->button;
4967             }
4968           else
4969             {
4970               clist->click_cell.row = -1;
4971               clist->click_cell.column = -1;
4972
4973               clist->drag_button = 0;
4974               remove_grab (clist);
4975             }
4976
4977           if (button_actions & GTK_CMBUTTON_SELECTS)
4978             {
4979               if (GTK_CMCLIST_ADD_MODE(clist))
4980                 {
4981                   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_ADD_MODE);
4982                   if (gtk_widget_has_focus(widget))
4983                     {
4984                       gtk_cmclist_undraw_focus (widget);
4985                       clist->focus_row = row;
4986                       gtk_cmclist_draw_focus (widget);
4987                     }
4988                   else
4989                     {
4990                       clist->focus_row = row;
4991                     }
4992                 }
4993               else if (row != clist->focus_row)
4994                 {
4995                   if (gtk_widget_has_focus(widget))
4996                     {
4997                       gtk_cmclist_undraw_focus (widget);
4998                       clist->focus_row = row;
4999                       gtk_cmclist_draw_focus (widget);
5000                     }
5001                   else
5002                     clist->focus_row = row;
5003                 }
5004             }
5005
5006           if (!gtk_widget_has_focus(widget))
5007             gtk_widget_grab_focus (widget);
5008
5009           if (button_actions & GTK_CMBUTTON_SELECTS)
5010             {
5011               switch (clist->selection_mode)
5012                 {
5013                 case GTK_SELECTION_SINGLE:
5014                   if (event->type != GDK_BUTTON_PRESS)
5015                     {
5016                       g_signal_emit (G_OBJECT (clist),
5017                                        clist_signals[SELECT_ROW], 0,
5018                                        row, column, event);
5019                       clist->anchor = -1;
5020                     }
5021                   else
5022                     clist->anchor = row;
5023                   break;
5024                 case GTK_SELECTION_BROWSE:
5025                   g_signal_emit (G_OBJECT (clist),
5026                                    clist_signals[SELECT_ROW], 0,
5027                                    row, column, event);
5028                   break;
5029                 case GTK_SELECTION_MULTIPLE:
5030                   if (event->type != GDK_BUTTON_PRESS)
5031                     {
5032                       if (clist->anchor != -1)
5033                         {
5034                           update_extended_selection (clist, clist->focus_row);
5035                           GTK_CMCLIST_GET_CLASS (clist)->resync_selection
5036                             (clist, (GdkEvent *) event);
5037                         }
5038                       g_signal_emit (G_OBJECT (clist),
5039                                        clist_signals[SELECT_ROW], 0,
5040                                        row, column, event);
5041                       break;
5042                     }
5043               
5044                   if (event->state & GDK_CONTROL_MASK)
5045                     {
5046                       if (event->state & GDK_SHIFT_MASK)
5047                         {
5048                           if (clist->anchor < 0)
5049                             {
5050                               g_list_free (clist->undo_selection);
5051                               g_list_free (clist->undo_unselection);
5052                               clist->undo_selection = NULL;
5053                               clist->undo_unselection = NULL;
5054                               clist->anchor = old_row;
5055                               clist->drag_pos = old_row;
5056                               clist->undo_anchor = old_row;
5057                             }
5058                           update_extended_selection (clist, clist->focus_row);
5059                         }
5060                       else
5061                         {
5062                           if (clist->anchor == -1)
5063                             set_anchor (clist, TRUE, row, old_row);
5064                           else
5065                             update_extended_selection (clist,
5066                                                        clist->focus_row);
5067                         }
5068                       break;
5069                     }
5070
5071                   if (event->state & GDK_SHIFT_MASK)
5072                     {
5073                       set_anchor (clist, FALSE, old_row, old_row);
5074                       update_extended_selection (clist, clist->focus_row);
5075                       break;
5076                     }
5077
5078                   if (clist->anchor == -1)
5079                     set_anchor (clist, FALSE, row, old_row);
5080                   else
5081                     update_extended_selection (clist, clist->focus_row);
5082                   break;
5083                 default:
5084                   break;
5085                 }
5086             }
5087         }
5088       return TRUE;
5089     }
5090
5091   /* press on resize windows */
5092   for (i = 0; i < clist->columns; i++)
5093     if (clist->column[i].resizeable && clist->column[i].window &&
5094         event->window == clist->column[i].window)
5095       {
5096         gpointer drag_data;
5097
5098         if (gdk_pointer_grab (clist->column[i].window, FALSE,
5099                               GDK_POINTER_MOTION_HINT_MASK |
5100                               GDK_BUTTON1_MOTION_MASK |
5101                               GDK_BUTTON_RELEASE_MASK,
5102                               NULL, NULL, event->time))
5103           return FALSE;
5104
5105         gtk_grab_add (widget);
5106         GTK_CMCLIST_SET_FLAG (clist, CMCLIST_IN_DRAG);
5107
5108         /* block attached dnd signal handler */
5109         drag_data = g_object_get_data (G_OBJECT (clist), "gtk-site-data");
5110         if (drag_data)
5111                 g_signal_handlers_block_matched(G_OBJECT(clist), G_SIGNAL_MATCH_DATA,
5112                                         0, 0, 0, 0, drag_data);
5113
5114         if (!gtk_widget_has_focus(widget))
5115           gtk_widget_grab_focus (widget);
5116
5117         clist->drag_pos = i;
5118         clist->x_drag = (COLUMN_LEFT_XPIXEL(clist, i) + COLUMN_INSET +
5119                          clist->column[i].area.width + CELL_SPACING);
5120
5121         return TRUE;
5122       }
5123
5124   return FALSE;
5125 }
5126
5127 static gint
5128 gtk_cmclist_button_release (GtkWidget      *widget,
5129                           GdkEventButton *event)
5130 {
5131   GtkCMCList *clist;
5132   gint button_actions;
5133
5134   cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
5135   cm_return_val_if_fail (event != NULL, FALSE);
5136
5137   clist = GTK_CMCLIST (widget);
5138
5139   button_actions = clist->button_actions[event->button - 1];
5140   if (button_actions == GTK_CMBUTTON_IGNORED)
5141     return FALSE;
5142
5143   /* release on resize windows */
5144   if (GTK_CMCLIST_IN_DRAG(clist))
5145     {
5146       gpointer drag_data;
5147       gint width;
5148       gint x;
5149       gint i;
5150
5151       i = clist->drag_pos;
5152       clist->drag_pos = -1;
5153
5154       /* unblock attached dnd signal handler */
5155       drag_data = g_object_get_data (G_OBJECT (clist), "gtk-site-data");
5156       if (drag_data)
5157                 g_signal_handlers_unblock_matched(G_OBJECT(clist), G_SIGNAL_MATCH_DATA,
5158                                         0, 0, 0, 0, drag_data);
5159
5160       GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_IN_DRAG);
5161       gtk_widget_get_pointer (widget, &x, NULL);
5162       gtk_grab_remove (widget);
5163       gdk_display_pointer_ungrab (gtk_widget_get_display (widget), event->time);
5164
5165       if (clist->x_drag >= 0)
5166         clist_refresh(clist);
5167
5168       width = new_column_width (clist, i, &x);
5169       gtk_cmclist_set_column_width (clist, i, width);
5170
5171       return TRUE;
5172     }
5173
5174   if (clist->drag_button == event->button)
5175     {
5176       gint row;
5177       gint column;
5178
5179       clist->drag_button = 0;
5180       clist->click_cell.row = -1;
5181       clist->click_cell.column = -1;
5182
5183       remove_grab (clist);
5184
5185       if (button_actions & GTK_CMBUTTON_SELECTS)
5186         {
5187           switch (clist->selection_mode)
5188             {
5189             case GTK_SELECTION_MULTIPLE:
5190               if (!(event->state & GDK_SHIFT_MASK) ||
5191                   !gtk_widget_get_can_focus (widget) ||
5192                   event->x < 0 || event->x >= clist->clist_window_width ||
5193                   event->y < 0 || event->y >= clist->clist_window_height)
5194                 GTK_CMCLIST_GET_CLASS (clist)->resync_selection
5195                   (clist, (GdkEvent *) event);
5196               break;
5197             case GTK_SELECTION_SINGLE:
5198               if (get_selection_info (clist, event->x, event->y,
5199                                       &row, &column))
5200                 {
5201                   if (row >= 0 && row < clist->rows && clist->anchor == row)
5202                     toggle_row (clist, row, column, (GdkEvent *) event);
5203                 }
5204               clist->anchor = -1;
5205               break;
5206             default:
5207               break;
5208             }
5209         }
5210
5211       return TRUE;
5212     }
5213   
5214   return FALSE;
5215 }
5216
5217 static gint
5218 gtk_cmclist_motion (GtkWidget      *widget,
5219                   GdkEventMotion *event)
5220 {
5221   GtkCMCList *clist;
5222   gint x;
5223   gint y;
5224   gint row;
5225   gint new_width;
5226   gint button_actions = 0;
5227   guint value;
5228
5229   cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
5230
5231   clist = GTK_CMCLIST (widget);
5232   if (!clist_has_grab (clist))
5233     return FALSE;
5234
5235   if (clist->drag_button > 0)
5236     button_actions = clist->button_actions[clist->drag_button - 1];
5237
5238   if (GTK_CMCLIST_IN_DRAG(clist))
5239     {
5240       if (event->is_hint || event->window != gtk_widget_get_window (widget))
5241         gtk_widget_get_pointer (widget, &x, NULL);
5242       else
5243         x = event->x;
5244       
5245       new_width = new_column_width (clist, clist->drag_pos, &x);
5246       if (x != clist->x_drag)
5247         {
5248           /* x_drag < 0 indicates that the xor line is already invisible */
5249           if (clist->x_drag >= 0)
5250             clist_refresh(clist);
5251
5252           clist->x_drag = x;
5253
5254           if (clist->x_drag >= 0)
5255             draw_xor_line (clist);
5256         }
5257
5258       if (new_width <= MAX (COLUMN_MIN_WIDTH + 1,
5259                             clist->column[clist->drag_pos].min_width + 1))
5260         {
5261           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) < 0 && x < 0)
5262             gtk_cmclist_moveto (clist, -1, clist->drag_pos, 0, 0);
5263           return FALSE;
5264         }
5265       if (clist->column[clist->drag_pos].max_width >= COLUMN_MIN_WIDTH &&
5266           new_width >= clist->column[clist->drag_pos].max_width)
5267         {
5268           if (COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) + new_width >
5269               clist->clist_window_width && x < 0)
5270             move_horizontal (clist,
5271                              COLUMN_LEFT_XPIXEL (clist, clist->drag_pos) +
5272                              new_width - clist->clist_window_width +
5273                              COLUMN_INSET + CELL_SPACING);
5274           return FALSE;
5275         }
5276     }
5277
5278   if (event->is_hint || event->window != clist->clist_window)
5279     gdk_window_get_pointer (clist->clist_window, &x, &y, NULL);
5280   else
5281     {
5282       x = event->x;
5283       y = event->y;
5284     }
5285
5286   if (GTK_CMCLIST_REORDERABLE(clist) && button_actions & GTK_CMBUTTON_DRAGS)
5287     {
5288       /* delayed drag start */
5289       if (event->window == clist->clist_window &&
5290           clist->click_cell.row >= 0 && clist->click_cell.column >= 0 &&
5291           (y < 0 || y >= clist->clist_window_height ||
5292            x < 0 || x >= clist->clist_window_width  ||
5293            y < ROW_TOP_YPIXEL (clist, clist->click_cell.row) ||
5294            y >= (ROW_TOP_YPIXEL (clist, clist->click_cell.row) +
5295                  clist->row_height) ||
5296            x < COLUMN_LEFT_XPIXEL (clist, clist->click_cell.column) ||
5297            x >= (COLUMN_LEFT_XPIXEL(clist, clist->click_cell.column) + 
5298                  clist->column[clist->click_cell.column].area.width)))
5299         {
5300           GtkTargetList  *target_list;
5301
5302           target_list = gtk_target_list_new (&clist_target_table, 1);
5303           gtk_drag_begin (widget, target_list, GDK_ACTION_MOVE,
5304                           clist->drag_button, (GdkEvent *)event);
5305
5306         }
5307       return TRUE;
5308     }
5309
5310   /* horizontal autoscrolling */
5311   if (clist->hadjustment && LIST_WIDTH (clist) > clist->clist_window_width &&
5312       (x < 0 || x >= clist->clist_window_width))
5313     {
5314       if (clist->htimer)
5315         return FALSE;
5316
5317       clist->htimer = gdk_threads_add_timeout
5318         (SCROLL_TIME, (GSourceFunc) horizontal_timeout, clist);
5319       value = gtk_adjustment_get_value (clist->hadjustment);
5320       if (!((x < 0 && value == 0) ||
5321             (x >= clist->clist_window_width &&
5322              value ==
5323              LIST_WIDTH (clist) - clist->clist_window_width)))
5324         {
5325           if (x < 0)
5326             move_horizontal (clist, -1 + (x/2));
5327           else
5328             move_horizontal (clist, 1 + (x - clist->clist_window_width) / 2);
5329         }
5330     }
5331
5332   if (GTK_CMCLIST_IN_DRAG(clist))
5333     return FALSE;
5334
5335   /* vertical autoscrolling */
5336   row = ROW_FROM_YPIXEL (clist, y);
5337
5338   /* don't scroll on last pixel row if it's a cell spacing */
5339   if (y == clist->clist_window_height - 1 &&
5340       y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height)
5341     return FALSE;
5342
5343   if (LIST_HEIGHT (clist) > clist->clist_window_height &&
5344       (y < 0 || y >= clist->clist_window_height))
5345     {
5346       if (clist->vtimer)
5347         return FALSE;
5348       clist->vtimer = gdk_threads_add_timeout (SCROLL_TIME,
5349                                      (GSourceFunc) vertical_timeout, clist);
5350       if (clist->drag_button &&
5351           ((y < 0 && clist->focus_row == 0) ||
5352            (y >= clist->clist_window_height &&
5353             clist->focus_row == clist->rows - 1)))
5354         return FALSE;
5355     }
5356
5357   row = CLAMP (row, 0, clist->rows - 1);
5358
5359   if (button_actions & GTK_CMBUTTON_SELECTS &
5360       !g_object_get_data (G_OBJECT (widget), "gtk-site-data"))
5361     {
5362       if (row == clist->focus_row)
5363         return FALSE;
5364
5365       gtk_cmclist_undraw_focus (widget);
5366       clist->focus_row = row;
5367       gtk_cmclist_draw_focus (widget);
5368
5369       switch (clist->selection_mode)
5370         {
5371         case GTK_SELECTION_BROWSE:
5372           g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
5373                            clist->focus_row, -1, event);
5374           break;
5375         case GTK_SELECTION_MULTIPLE:
5376           update_extended_selection (clist, clist->focus_row);
5377           break;
5378         default:
5379           break;
5380         }
5381     }
5382   
5383   if (ROW_TOP_YPIXEL(clist, row) < 0)
5384     move_vertical (clist, row, 0);
5385   else if (ROW_TOP_YPIXEL(clist, row) + clist->row_height >
5386            clist->clist_window_height)
5387     move_vertical (clist, row, 1);
5388
5389   return FALSE;
5390 }
5391
5392 static void
5393 gtk_cmclist_size_request (GtkWidget      *widget,
5394                         GtkRequisition *requisition)
5395 {
5396   GtkCMCList *clist;
5397   GtkStyle *style;
5398   gint i;
5399   gint font_height = 0;
5400   guint border_width;
5401   cm_return_if_fail (GTK_IS_CMCLIST (widget));
5402   cm_return_if_fail (requisition != NULL);
5403
5404   clist = GTK_CMCLIST (widget);
5405   style = gtk_widget_get_style (widget);
5406
5407   requisition->width = 0;
5408   requisition->height = 0;
5409
5410   /* compute the size of the column title (title) area */
5411   clist->column_title_area.height = 0;
5412   if (GTK_CMCLIST_SHOW_TITLES(clist)) {
5413     font_height = (pango_font_description_get_size(style->font_desc)/PANGO_SCALE)*2+4;
5414     for (i = 0; i < clist->columns; i++)
5415       if (clist->column[i].button)
5416         {
5417           GtkRequisition child_requisition;
5418           
5419           gtk_widget_size_request (clist->column[i].button,
5420                                    &child_requisition);
5421           clist->column_title_area.height =
5422             MAX (clist->column_title_area.height,
5423                  child_requisition.height);
5424         }
5425     //clist->column_title_area.height = font_height;
5426   }
5427   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
5428   requisition->width += (style->xthickness +
5429                          border_width) * 2;
5430   requisition->height += (clist->column_title_area.height +
5431                           (style->ythickness +
5432                            border_width) * 2);
5433
5434   /* if (!clist->hadjustment) */
5435   requisition->width += list_requisition_width (clist);
5436   /* if (!clist->vadjustment) */
5437   requisition->height += LIST_HEIGHT (clist);
5438 }
5439
5440 static void
5441 gtk_cmclist_size_allocate (GtkWidget     *widget,
5442                          GtkAllocation *allocation)
5443 {
5444   GtkStyle *style;
5445   GtkCMCList *clist;
5446   GtkAllocation clist_allocation;
5447   gint border_width;
5448
5449   cm_return_if_fail (GTK_IS_CMCLIST (widget));
5450   cm_return_if_fail (allocation != NULL);
5451
5452   style = gtk_widget_get_style (widget);
5453   clist = GTK_CMCLIST (widget);
5454   gtk_widget_set_allocation (widget, allocation);
5455   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
5456
5457   if (gtk_widget_get_realized (widget))
5458     {
5459       gdk_window_move_resize (gtk_widget_get_window (widget),
5460                               allocation->x + border_width,
5461                               allocation->y + border_width,
5462                               allocation->width - border_width * 2,
5463                               allocation->height - border_width * 2);
5464     }
5465
5466   /* use internal allocation structure for all the math
5467    * because it's easier than always subtracting the container
5468    * border width */
5469   clist->internal_allocation.x = 0;
5470   clist->internal_allocation.y = 0;
5471   clist->internal_allocation.width = MAX (1, (gint)allocation->width -
5472                                           border_width * 2);
5473   clist->internal_allocation.height = MAX (1, (gint)allocation->height -
5474                                            border_width * 2);
5475         
5476   /* allocate clist window assuming no scrollbars */
5477   clist_allocation.x = (clist->internal_allocation.x +
5478                         style->xthickness);
5479   clist_allocation.y = (clist->internal_allocation.y +
5480                         style->ythickness +
5481                         clist->column_title_area.height);
5482   clist_allocation.width = MAX (1, (gint)clist->internal_allocation.width - 
5483                                 (2 * (gint)style->xthickness));
5484   clist_allocation.height = MAX (1, (gint)clist->internal_allocation.height -
5485                                  (2 * (gint)style->ythickness) -
5486                                  (gint)clist->column_title_area.height);
5487   
5488   clist->clist_window_width = clist_allocation.width;
5489   clist->clist_window_height = clist_allocation.height;
5490   
5491   if (gtk_widget_get_realized (widget))
5492     {
5493       gdk_window_move_resize (clist->clist_window,
5494                               clist_allocation.x,
5495                               clist_allocation.y,
5496                               clist_allocation.width,
5497                               clist_allocation.height);
5498     }
5499   
5500   /* position the window which holds the column title buttons */
5501   clist->column_title_area.x = style->xthickness;
5502   clist->column_title_area.y = style->ythickness;
5503   clist->column_title_area.width = clist_allocation.width;
5504   
5505   if (gtk_widget_get_realized (widget))
5506     {
5507       gdk_window_move_resize (clist->title_window,
5508                               clist->column_title_area.x,
5509                               clist->column_title_area.y,
5510                               clist->column_title_area.width,
5511                               clist->column_title_area.height);
5512     }
5513   
5514   /* column button allocation */
5515   size_allocate_columns (clist, FALSE);
5516   size_allocate_title_buttons (clist);
5517
5518   adjust_adjustments (clist, TRUE);
5519 }
5520
5521 /* GTKCONTAINER
5522  *   gtk_cmclist_forall
5523  */
5524 static void
5525 gtk_cmclist_forall (GtkContainer *container,
5526                   gboolean      include_internals,
5527                   GtkCallback   callback,
5528                   gpointer      callback_data)
5529 {
5530   GtkCMCList *clist;
5531   guint i;
5532
5533   cm_return_if_fail (GTK_IS_CMCLIST (container));
5534   cm_return_if_fail (callback != NULL);
5535
5536   if (!include_internals)
5537     return;
5538
5539   clist = GTK_CMCLIST (container);
5540       
5541   /* callback for the column buttons */
5542   for (i = 0; i < clist->columns; i++)
5543     if (clist->column[i].button)
5544       (*callback) (clist->column[i].button, callback_data);
5545 }
5546
5547 /* PRIVATE DRAWING FUNCTIONS
5548  *   get_cell_style
5549  *   draw_cell_pixbuf
5550  *   draw_row
5551  *   draw_rows
5552  *   draw_xor_line
5553  *   clist_refresh
5554  */
5555 static void
5556 get_cell_style (GtkCMCList     *clist,
5557                 GtkCMCListRow  *clist_row,
5558                 gint          state,
5559                 gint          column,
5560                 GtkStyle    **style)
5561 {
5562   GtkStyle *gtkstyle;
5563
5564   if (clist_row->cell[column].style)
5565     {
5566       if (style)
5567         *style = clist_row->cell[column].style;
5568     }
5569   else if (clist_row->style)
5570     {
5571       if (style)
5572         *style = clist_row->style;
5573     }
5574   else
5575     {
5576       gtkstyle = gtk_widget_get_style (GTK_WIDGET (clist));
5577       if (style)
5578         *style = gtkstyle;
5579     }
5580 }
5581
5582 static gint
5583 draw_cell_pixbuf (GdkWindow    *window,
5584                   GdkRectangle *clip_rectangle,
5585                   cairo_t      *cr,
5586                   GdkPixbuf    *pixbuf,
5587                   gint          x,
5588                   gint          y,
5589                   gint          width,
5590                   gint          height)
5591 {
5592   gint xsrc = 0;
5593   gint ysrc = 0;
5594
5595   if (!pixbuf || (width == 0 && height == 0))
5596         return x;
5597
5598   if (x < clip_rectangle->x)
5599     {
5600       xsrc = clip_rectangle->x - x;
5601       width -= xsrc;
5602       x = clip_rectangle->x;
5603     }
5604   if (x + width > clip_rectangle->x + clip_rectangle->width)
5605     width = clip_rectangle->x + clip_rectangle->width - x;
5606
5607   if (y < clip_rectangle->y)
5608     {
5609       ysrc = clip_rectangle->y - y;
5610       height -= ysrc;
5611       y = clip_rectangle->y;
5612     }
5613
5614   if (y + height > clip_rectangle->y + clip_rectangle->height)
5615     height = clip_rectangle->y + clip_rectangle->height - y;
5616
5617   gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
5618   cairo_paint(cr);
5619
5620   return x + MAX (width, 0);
5621 }
5622
5623 static void cairo_dash_from_add_mode(GtkCMCList *clist, cairo_t *cr)
5624 {
5625         const double dashes[] = { 4.0, 4.0 };
5626         if (GTK_CMCLIST_ADD_MODE(clist)) 
5627                 cairo_set_dash(cr, dashes, 2, 0);
5628         else
5629                 cairo_set_dash(cr, NULL, 0, 0);
5630 }
5631
5632 static void
5633 draw_row (GtkCMCList     *clist,
5634           GdkRectangle *area,
5635           gint          row,
5636           GtkCMCListRow  *clist_row)
5637 {
5638   GtkStyle *style;
5639   GtkWidget *widget;
5640   GdkRectangle *rect;
5641   GdkRectangle row_rectangle;
5642   GdkRectangle cell_rectangle;
5643   GdkRectangle clip_rectangle;
5644   GdkRectangle intersect_rectangle;
5645   gint last_column;
5646   gint state;
5647   gint i;
5648   cairo_t *cr;
5649   const double dashes[] = {4.0, 4.0};
5650   cm_return_if_fail (clist != NULL);
5651
5652   /* bail now if we arn't drawable yet */
5653   if (!gtk_widget_is_drawable (GTK_WIDGET(clist)) || row < 0 || row >= clist->rows)
5654     return;
5655
5656   widget = GTK_WIDGET (clist);
5657   style = gtk_widget_get_style (widget);
5658
5659   /* if the function is passed the pointer to the row instead of null,
5660    * it avoids this expensive lookup */
5661   if (!clist_row)
5662     clist_row = ROW_ELEMENT (clist, row)->data;
5663
5664   /* rectangle of the entire row */
5665   row_rectangle.x = 0;
5666   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
5667   row_rectangle.width = clist->clist_window_width;
5668   row_rectangle.height = clist->row_height;
5669
5670   /* rectangle of the cell spacing above the row */
5671   cell_rectangle.x = 0;
5672   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
5673   cell_rectangle.width = row_rectangle.width;
5674   cell_rectangle.height = CELL_SPACING;
5675
5676   /* rectangle used to clip drawing operations, its y and height
5677    * positions only need to be set once, so we set them once here. 
5678    * the x and width are set withing the drawing loop below once per
5679    * column */
5680   clip_rectangle.y = row_rectangle.y;
5681   clip_rectangle.height = row_rectangle.height;
5682
5683   state = clist_row->state;
5684   cr = gdk_cairo_create(clist->clist_window);
5685
5686   /* draw the cell borders and background */
5687   if (area)
5688     {
5689       rect = &intersect_rectangle;
5690       if (gdk_rectangle_intersect (area, &cell_rectangle,
5691                                    &intersect_rectangle)) {
5692                 gdk_cairo_rectangle(cr, &intersect_rectangle);
5693                 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
5694                 cairo_fill(cr);
5695       }
5696
5697       /* the last row has to clear its bottom cell spacing too */
5698       if (clist_row == clist->row_list_end->data)
5699         {
5700           cell_rectangle.y += clist->row_height + CELL_SPACING;
5701
5702           if (gdk_rectangle_intersect (area, &cell_rectangle,
5703                                        &intersect_rectangle)) {
5704              gdk_cairo_rectangle(cr, &intersect_rectangle);
5705              gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
5706              cairo_fill(cr);
5707             }
5708         }
5709
5710       if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
5711         return;
5712
5713     }
5714   else
5715     {
5716       rect = &clip_rectangle;
5717       gdk_cairo_rectangle(cr, &cell_rectangle);
5718       gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
5719       cairo_fill(cr);
5720
5721       /* the last row has to clear its bottom cell spacing too */
5722       if (clist_row == clist->row_list_end->data)
5723         {
5724           cell_rectangle.y += clist->row_height + CELL_SPACING;
5725           gdk_cairo_rectangle(cr, &cell_rectangle);
5726           gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
5727           cairo_fill(cr);
5728         }         
5729     }
5730   
5731   for (last_column = clist->columns - 1;
5732        last_column >= 0 && !clist->column[last_column].visible; last_column--)
5733     ;
5734
5735   /* iterate and draw all the columns (row cells) and draw their contents */
5736   for (i = 0; i < clist->columns; i++)
5737     {
5738       GtkStyle *style;
5739       PangoLayout *layout;
5740       PangoRectangle logical_rect;
5741
5742       gint width;
5743       gint height;
5744       gint pixbuf_width;
5745       gint offset = 0;
5746
5747       if (!clist->column[i].visible)
5748         continue;
5749
5750       get_cell_style (clist, clist_row, state, i, &style);
5751
5752       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
5753       clip_rectangle.width = clist->column[i].area.width;
5754
5755       /* calculate clipping region clipping region */
5756       clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
5757       clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
5758                                (i == last_column) * CELL_SPACING);
5759       
5760       if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
5761                                             &intersect_rectangle))
5762         continue;
5763
5764       gdk_cairo_rectangle(cr, rect);
5765       gdk_cairo_set_source_color(cr, &style->base[state]);
5766       cairo_fill(cr);
5767
5768       clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
5769       clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
5770                                (i == last_column) * CELL_SPACING);
5771
5772
5773       /* calculate real width for column justification */
5774       
5775       layout = _gtk_cmclist_create_cell_layout (clist, clist_row, i);
5776       if (layout)
5777         {
5778           pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
5779           width = logical_rect.width;
5780         }
5781       else
5782         width = 0;
5783
5784       pixbuf_width = 0;
5785       height = 0;
5786       offset = 0;
5787       switch (clist_row->cell[i].type)
5788         {
5789         case GTK_CMCELL_PIXBUF:
5790           pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
5791           height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
5792           width += pixbuf_width;
5793           break;
5794         case GTK_CMCELL_PIXTEXT:
5795           pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
5796           height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
5797           width += pixbuf_width + GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
5798           break;
5799         default:
5800           break;
5801         }
5802
5803       switch (clist->column[i].justification)
5804         {
5805         case GTK_JUSTIFY_LEFT:
5806           offset = clip_rectangle.x + clist_row->cell[i].horizontal;
5807           break;
5808         case GTK_JUSTIFY_RIGHT:
5809           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5810                     clip_rectangle.width - width);
5811           break;
5812         case GTK_JUSTIFY_CENTER:
5813         case GTK_JUSTIFY_FILL:
5814           offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
5815                     (clip_rectangle.width / 2) - (width / 2));
5816           break;
5817         };
5818
5819       /* Draw Text and/or Pixbuf */
5820       switch (clist_row->cell[i].type)
5821         {
5822         case GTK_CMCELL_PIXBUF:
5823           draw_cell_pixbuf (clist->clist_window, &clip_rectangle, cr,
5824                             GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf,
5825                             offset,
5826                             clip_rectangle.y + clist_row->cell[i].vertical +
5827                             (clip_rectangle.height - height) / 2,
5828                             pixbuf_width, height);
5829           break;
5830         case GTK_CMCELL_PIXTEXT:
5831           offset =
5832             draw_cell_pixbuf (clist->clist_window, &clip_rectangle, cr,
5833                               GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
5834                               offset,
5835                               clip_rectangle.y + clist_row->cell[i].vertical+
5836                               (clip_rectangle.height - height) / 2,
5837                               pixbuf_width, height);
5838           offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
5839
5840           /* Fall through */
5841         case GTK_CMCELL_TEXT:
5842           if (layout)
5843             {
5844               gint row_center_offset = (clist->row_height - logical_rect.height - 1) / 2;
5845               gdk_cairo_set_source_color(cr, &gtk_widget_get_style(GTK_WIDGET(clist))->fg[state]);
5846               cairo_move_to(cr, offset, row_rectangle.y + row_center_offset + clist_row->cell[i].vertical);
5847               pango_cairo_show_layout(cr, layout);
5848               g_object_unref (G_OBJECT (layout));
5849             }
5850           break;
5851         default:
5852           break;
5853         }
5854     }
5855
5856   /* draw focus rectangle */
5857   cairo_dash_from_add_mode(clist, cr);
5858   cairo_set_line_width(cr, 1.0);
5859   cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
5860   if (clist->focus_row == row &&
5861       gtk_widget_get_can_focus (widget) && gtk_widget_has_focus(widget))
5862     {
5863       if (!area) {
5864         cairo_rectangle(cr, row_rectangle.x + 1, row_rectangle.y,
5865                             row_rectangle.width - 1, row_rectangle.height);
5866         gdk_cairo_set_source_color(cr, &style->fg[GTK_STATE_NORMAL]);
5867         cairo_stroke(cr);
5868       }
5869       else if (gdk_rectangle_intersect (area, &row_rectangle,
5870                                         &intersect_rectangle))
5871         {
5872           cairo_rectangle(cr, row_rectangle.x + 1, row_rectangle.y,
5873                             row_rectangle.width - 1, row_rectangle.height);
5874           gdk_cairo_set_source_color(cr, &style->fg[GTK_STATE_NORMAL]);
5875           cairo_stroke(cr);
5876         }
5877     }
5878     cairo_destroy(cr);
5879
5880 }
5881
5882 static void
5883 draw_rows (GtkCMCList     *clist,
5884            GdkRectangle *area)
5885 {
5886   GList *list;
5887   GtkCMCListRow *clist_row;
5888   gint i;
5889   gint first_row;
5890   gint last_row;
5891
5892   cm_return_if_fail (GTK_IS_CMCLIST (clist));
5893
5894   if (clist->row_height == 0 ||
5895       !gtk_widget_is_drawable (GTK_WIDGET(clist)))
5896     return;
5897
5898   if (area)
5899     {
5900       first_row = ROW_FROM_YPIXEL (clist, area->y);
5901       last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
5902     }
5903   else
5904     {
5905       first_row = ROW_FROM_YPIXEL (clist, 0);
5906       last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
5907     }
5908
5909   /* this is a small special case which exposes the bottom cell line
5910    * on the last row -- it might go away if I change the wall the cell
5911    * spacings are drawn
5912    */
5913   if (clist->rows == first_row)
5914     first_row--;
5915
5916   list = ROW_ELEMENT (clist, first_row);
5917   i = first_row;
5918   while (list)
5919     {
5920       clist_row = list->data;
5921       list = list->next;
5922
5923       if (i > last_row)
5924         return;
5925
5926       GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, area, i, clist_row);
5927       i++;
5928     }
5929
5930   if (!area) {
5931     int w, h, y;
5932     gdk_drawable_get_size (GDK_DRAWABLE (clist->clist_window), &w, &h);
5933     y = ROW_TOP_YPIXEL (clist, i);
5934     gdk_window_clear_area (clist->clist_window,
5935                            0, y,
5936                            w, h - y);
5937   }
5938 }
5939
5940 static void                          
5941 draw_xor_line (GtkCMCList *clist)
5942 {
5943   cairo_t *cr;
5944   cr = gdk_cairo_create(clist->clist_window);
5945   cairo_set_line_width(cr, 1.0);
5946   cairo_move_to(cr, clist->x_drag,
5947                  gtk_widget_get_style (GTK_WIDGET(clist))->ythickness);
5948   cairo_line_to(cr, clist->x_drag,
5949                  clist->column_title_area.height +
5950                  clist->clist_window_height + 1);
5951   cairo_stroke(cr);
5952   cairo_destroy(cr);
5953 }
5954
5955 static void
5956 clist_refresh (GtkCMCList *clist)
5957 {
5958   cm_return_if_fail (GTK_IS_CMCLIST (clist));
5959   
5960   if (CLIST_UNFROZEN (clist))
5961     { 
5962       adjust_adjustments (clist, FALSE);
5963       draw_rows (clist, NULL);
5964     }
5965 }
5966
5967 /* get cell from coordinates
5968  *   get_selection_info
5969  *   gtk_cmclist_get_selection_info
5970  */
5971 static gint
5972 get_selection_info (GtkCMCList *clist,
5973                     gint      x,
5974                     gint      y,
5975                     gint     *row,
5976                     gint     *column)
5977 {
5978   gint trow, tcol;
5979
5980   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
5981
5982   /* bounds checking, return false if the user clicked 
5983    * on a blank area */
5984   trow = ROW_FROM_YPIXEL (clist, y);
5985   if (trow >= clist->rows)
5986     return 0;
5987
5988   if (row)
5989     *row = trow;
5990
5991   tcol = COLUMN_FROM_XPIXEL (clist, x);
5992   if (tcol >= clist->columns)
5993     return 0;
5994
5995   if (column)
5996     *column = tcol;
5997
5998   return 1;
5999 }
6000
6001 gint
6002 gtk_cmclist_get_selection_info (GtkCMCList *clist, 
6003                               gint      x, 
6004                               gint      y, 
6005                               gint     *row, 
6006                               gint     *column)
6007 {
6008   cm_return_val_if_fail (GTK_IS_CMCLIST (clist), 0);
6009   return get_selection_info (clist, x, y, row, column);
6010 }
6011
6012 /* PRIVATE ADJUSTMENT FUNCTIONS
6013  *   adjust_adjustments
6014  *   vadjustment_changed
6015  *   hadjustment_changed
6016  *   vadjustment_value_changed
6017  *   hadjustment_value_changed 
6018  *   check_exposures
6019  */
6020 static void
6021 adjust_adjustments (GtkCMCList *clist,
6022                     gboolean  block_resize)
6023 {
6024   if (clist->vadjustment)
6025     {
6026       gtk_adjustment_set_page_size (clist->vadjustment, clist->clist_window_height);
6027       gtk_adjustment_set_step_increment (clist->vadjustment, clist->row_height);
6028       gtk_adjustment_set_page_increment (clist->vadjustment,
6029         MAX (clist->clist_window_height - clist->row_height,
6030              clist->clist_window_height / 2));
6031       gtk_adjustment_set_lower (clist->vadjustment, 0);
6032       gtk_adjustment_set_upper (clist->vadjustment, LIST_HEIGHT (clist));
6033
6034       if ((clist->clist_window_height - clist->voffset) > LIST_HEIGHT (clist) ||
6035           (clist->voffset + (gint)gtk_adjustment_get_value (clist->vadjustment)) != 0)
6036         {
6037           gtk_adjustment_set_value (clist->vadjustment,
6038            MAX (0, (LIST_HEIGHT (clist) - clist->clist_window_height)));
6039           g_signal_emit_by_name (G_OBJECT (clist->vadjustment),
6040                                    "value_changed");
6041         }
6042       g_signal_emit_by_name (G_OBJECT (clist->vadjustment), "changed");
6043     }
6044
6045   if (clist->hadjustment)
6046     {
6047       gtk_adjustment_set_page_size (clist->hadjustment, clist->clist_window_width);
6048       gtk_adjustment_set_step_increment (clist->hadjustment, 10);
6049       gtk_adjustment_set_page_increment (clist->hadjustment,
6050         MAX (clist->clist_window_width -
6051          gtk_adjustment_get_step_increment (clist->hadjustment),
6052              clist->clist_window_width / 2));
6053       gtk_adjustment_set_lower (clist->hadjustment, 0);
6054       gtk_adjustment_set_upper (clist->hadjustment, LIST_WIDTH (clist));
6055
6056       if ((clist->clist_window_width - clist->hoffset) > LIST_WIDTH (clist) ||
6057           (clist->hoffset + (gint)gtk_adjustment_get_value (clist->hadjustment)) != 0)
6058         {
6059           gtk_adjustment_set_value (clist->hadjustment, MAX (0, (LIST_WIDTH (clist) -
6060                                                clist->clist_window_width)));
6061           g_signal_emit_by_name (G_OBJECT (clist->hadjustment),
6062                                    "value_changed");
6063         }
6064       g_signal_emit_by_name (G_OBJECT (clist->hadjustment), "changed");
6065     }
6066
6067   if (!block_resize && (!clist->vadjustment || !clist->hadjustment))
6068     {
6069       GtkWidget *widget;
6070       GtkRequisition requisition;
6071       GtkAllocation allocation;
6072
6073       widget = GTK_WIDGET (clist);
6074       gtk_widget_size_request (widget, &requisition);
6075       gtk_widget_get_allocation (widget, &allocation);
6076
6077       if ((!clist->hadjustment &&
6078            requisition.width != allocation.width) ||
6079           (!clist->vadjustment &&
6080            requisition.height != allocation.height))
6081         gtk_widget_queue_resize (widget);
6082     }
6083 }
6084
6085 static void
6086 vadjustment_changed (GtkAdjustment *adjustment,
6087                      gpointer       data)
6088 {
6089   GtkCMCList *clist;
6090
6091   cm_return_if_fail (adjustment != NULL);
6092   cm_return_if_fail (data != NULL);
6093
6094   clist = GTK_CMCLIST (data);
6095 }
6096
6097 static void
6098 hadjustment_changed (GtkAdjustment *adjustment,
6099                      gpointer       data)
6100 {
6101   GtkCMCList *clist;
6102
6103   cm_return_if_fail (adjustment != NULL);
6104   cm_return_if_fail (data != NULL);
6105
6106   clist = GTK_CMCLIST (data);
6107 }
6108
6109 static void
6110 vadjustment_value_changed (GtkAdjustment *adjustment,
6111                            gpointer       data)
6112 {
6113   GtkCMCList *clist;
6114   gint dy, value;
6115
6116   cm_return_if_fail (adjustment != NULL);
6117   cm_return_if_fail (GTK_IS_CMCLIST (data));
6118
6119   clist = GTK_CMCLIST (data);
6120
6121   if (adjustment != clist->vadjustment)
6122     return;
6123
6124   value = -gtk_adjustment_get_value (adjustment);
6125   dy = value - clist->voffset;
6126   clist->voffset = value;
6127
6128   if (gtk_widget_is_drawable (GTK_WIDGET(clist)))
6129     {
6130       gdk_window_scroll (clist->clist_window, 0, dy);
6131       gdk_window_process_updates (clist->clist_window, FALSE);
6132     }
6133   
6134   return;
6135 }
6136
6137 typedef struct
6138 {
6139   GdkWindow *window;
6140   gint dx;
6141 } ScrollData;
6142
6143 /* The window to which widget->window is relative */
6144 #define ALLOCATION_WINDOW(widget)               \
6145    (!gtk_widget_get_has_window (widget) ?               \
6146         gtk_widget_get_window (widget) :         \
6147         gdk_window_get_parent (gtk_widget_get_window(widget)))
6148
6149 static void
6150 adjust_allocation_recurse (GtkWidget *widget,
6151                            gpointer   data)
6152 {
6153   GtkAllocation allocation;
6154   ScrollData *scroll_data = data;
6155
6156   gtk_widget_get_allocation (widget, &allocation);
6157   
6158   if (!gtk_widget_get_realized (widget))
6159     {
6160       if (gtk_widget_get_visible (widget))
6161         {
6162           GdkRectangle tmp_rectangle = allocation;
6163           tmp_rectangle.x += scroll_data->dx;
6164       
6165           gtk_widget_size_allocate (widget, &tmp_rectangle);
6166         }
6167     }
6168   else
6169     {
6170       if (ALLOCATION_WINDOW (widget) == scroll_data->window)
6171         {
6172           allocation.x += scroll_data->dx;
6173           gtk_widget_set_allocation (widget, &allocation);
6174
6175           if (GTK_IS_CONTAINER (widget))
6176             gtk_container_forall (GTK_CONTAINER (widget),
6177                                   adjust_allocation_recurse,
6178                                   data);
6179         }
6180     }
6181 }
6182
6183 static void
6184 adjust_allocation (GtkWidget *widget,
6185                    gint       dx)
6186 {
6187   ScrollData scroll_data;
6188
6189   if (gtk_widget_get_realized (widget))
6190     scroll_data.window = ALLOCATION_WINDOW (widget);
6191   else
6192     scroll_data.window = NULL;
6193     
6194   scroll_data.dx = dx;
6195   
6196   adjust_allocation_recurse (widget, &scroll_data);
6197 }
6198
6199 static void
6200 hadjustment_value_changed (GtkAdjustment *adjustment,
6201                            gpointer       data)
6202 {
6203   GtkCMCList *clist;
6204   GtkContainer *container;
6205   GdkRectangle area;
6206   gint i;
6207   gint y = 0;
6208   gint value;
6209   gint dx;
6210   cairo_t *cr;
6211
6212   cm_return_if_fail (adjustment != NULL);
6213   cm_return_if_fail (GTK_IS_CMCLIST (data));
6214
6215   clist = GTK_CMCLIST (data);
6216   container = GTK_CONTAINER (data);
6217
6218   if (adjustment != clist->hadjustment)
6219     return;
6220
6221   value = gtk_adjustment_get_value (adjustment);
6222
6223   dx = -value - clist->hoffset;
6224
6225   if (gtk_widget_get_realized (GTK_WIDGET(clist)))
6226     gdk_window_scroll (clist->title_window, dx, 0);
6227
6228   /* adjust the column button's allocations */
6229   for (i = 0; i < clist->columns; i++)
6230     if (clist->column[i].button)
6231       adjust_allocation (clist->column[i].button, dx);
6232
6233   clist->hoffset = -value;
6234
6235   cr = gdk_cairo_create(clist->clist_window);
6236   cairo_dash_from_add_mode(clist, cr);
6237   cairo_set_line_width(cr, 1.0);
6238   cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
6239   if (gtk_widget_is_drawable (GTK_WIDGET(clist)))
6240     {
6241       GtkWidget *focus_child = gtk_container_get_focus_child (container);
6242       if (gtk_widget_get_can_focus(GTK_WIDGET(clist)) && 
6243           gtk_widget_has_focus(GTK_WIDGET(clist)) &&
6244           !focus_child && GTK_CMCLIST_ADD_MODE(clist))
6245         {
6246           y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6247           cairo_rectangle(cr, 0, y, clist->clist_window_width - 1,
6248                               clist->row_height - 1);
6249           cairo_stroke(cr);
6250         }
6251  
6252       gdk_window_scroll (clist->clist_window, dx, 0);
6253       gdk_window_process_updates (clist->clist_window, FALSE);
6254
6255       if (gtk_widget_get_can_focus(GTK_WIDGET(clist)) && 
6256           gtk_widget_has_focus(GTK_WIDGET(clist)) &&
6257           !focus_child)
6258         {
6259           if (GTK_CMCLIST_ADD_MODE(clist))
6260             {
6261               gint focus_row;
6262           
6263               focus_row = clist->focus_row;
6264               clist->focus_row = -1;
6265               draw_rows (clist, &area);
6266               clist->focus_row = focus_row;
6267           
6268               cairo_rectangle(cr, 0, y, clist->clist_window_width - 1,
6269                               clist->row_height - 1);
6270               cairo_stroke(cr);
6271               return;
6272             }
6273           else if (ABS(dx) < clist->clist_window_width - 1)
6274             {
6275               gint x0;
6276               gint x1;
6277           
6278               if (dx > 0)
6279                 {
6280                   x0 = clist->clist_window_width - 1;
6281                   x1 = dx;
6282                 }
6283               else
6284                 {
6285                   x0 = 0;
6286                   x1 = clist->clist_window_width - 1 + dx;
6287                 }
6288
6289               y = ROW_TOP_YPIXEL (clist, clist->focus_row);
6290               cairo_move_to(cr, x0, y + 1);
6291               cairo_line_to(cr, x0, y + clist->row_height - 2);
6292               cairo_stroke(cr);
6293               cairo_move_to(cr, x1, y + 1);
6294               cairo_line_to(cr, x1, y + clist->row_height - 2);
6295               cairo_stroke(cr);
6296             }
6297         }
6298     }
6299     cairo_destroy(cr);
6300 }
6301
6302 /* PRIVATE 
6303  * Memory Allocation/Distruction Routines for GtkCMCList stuctures
6304  *
6305  * functions:
6306  *   columns_new
6307  *   column_title_new
6308  *   columns_delete
6309  *   row_new
6310  *   row_delete
6311  */
6312 static GtkCMCListColumn *
6313 columns_new (GtkCMCList *clist)
6314 {
6315   GtkCMCListColumn *column;
6316   gint i;
6317
6318   column = g_new (GtkCMCListColumn, clist->columns);
6319
6320   for (i = 0; i < clist->columns; i++)
6321     {
6322       column[i].area.x = 0;
6323       column[i].area.y = 0;
6324       column[i].area.width = 0;
6325       column[i].area.height = 0;
6326       column[i].title = NULL;
6327       column[i].button = NULL;
6328       column[i].window = NULL;
6329       column[i].width = 0;
6330       column[i].min_width = -1;
6331       column[i].max_width = -1;
6332       column[i].visible = TRUE;
6333       column[i].width_set = FALSE;
6334       column[i].resizeable = TRUE;
6335       column[i].auto_resize = FALSE;
6336       column[i].button_passive = FALSE;
6337       column[i].justification = GTK_JUSTIFY_LEFT;
6338     }
6339
6340   return column;
6341 }
6342
6343 static void
6344 column_title_new (GtkCMCList    *clist,
6345                   gint         column,
6346                   const gchar *title)
6347 {
6348   g_free (clist->column[column].title);
6349
6350   clist->column[column].title = g_strdup (title);
6351 }
6352
6353 static void
6354 columns_delete (GtkCMCList *clist)
6355 {
6356   gint i;
6357
6358   for (i = 0; i < clist->columns; i++)
6359     g_free (clist->column[i].title);
6360       
6361   g_free (clist->column);
6362 }
6363
6364 static GtkCMCListRow *
6365 row_new (GtkCMCList *clist)
6366 {
6367   int i;
6368   GtkCMCListRow *clist_row;
6369
6370 #if GLIB_CHECK_VERSION(2,10,0)
6371   clist_row = g_slice_new (GtkCMCListRow);
6372   clist_row->cell = g_slice_alloc (sizeof (GtkCMCell) * clist->columns);
6373 #else
6374   clist_row = g_chunk_new (GtkCMCListRow, (GMemChunk *)clist->row_mem_chunk);
6375   clist_row->cell = g_chunk_new (GtkCMCell, (GMemChunk *)clist->cell_mem_chunk);
6376 #endif
6377
6378   for (i = 0; i < clist->columns; i++)
6379     {
6380       clist_row->cell[i].type = GTK_CMCELL_EMPTY;
6381       clist_row->cell[i].vertical = 0;
6382       clist_row->cell[i].horizontal = 0;
6383       clist_row->cell[i].style = NULL;
6384     }
6385
6386   clist_row->fg_set = FALSE;
6387   clist_row->bg_set = FALSE;
6388   clist_row->style = NULL;
6389   clist_row->selectable = TRUE;
6390   clist_row->state = GTK_STATE_NORMAL;
6391   clist_row->data = NULL;
6392   clist_row->destroy = NULL;
6393
6394   return clist_row;
6395 }
6396
6397 static void
6398 row_delete (GtkCMCList    *clist,
6399             GtkCMCListRow *clist_row)
6400 {
6401   gint i;
6402
6403   for (i = 0; i < clist->columns; i++)
6404     {
6405       GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
6406         (clist, clist_row, i, GTK_CMCELL_EMPTY, NULL, 0, NULL);
6407       if (clist_row->cell[i].style)
6408         {
6409           if (gtk_widget_get_realized (GTK_WIDGET(clist)))
6410             gtk_style_detach (clist_row->cell[i].style);
6411           g_object_unref (clist_row->cell[i].style);
6412         }
6413     }
6414
6415   if (clist_row->style)
6416     {
6417       if (gtk_widget_get_realized (GTK_WIDGET(clist)))
6418         gtk_style_detach (clist_row->style);
6419       g_object_unref (clist_row->style);
6420     }
6421
6422   if (clist_row->destroy)
6423     clist_row->destroy (clist_row->data);
6424
6425 #if GLIB_CHECK_VERSION(2,10,0)  
6426   g_slice_free1 (sizeof (GtkCMCell) * clist->columns, clist_row->cell);
6427   g_slice_free (GtkCMCListRow, clist_row);
6428 #else
6429   g_mem_chunk_free ((GMemChunk *)clist->cell_mem_chunk, clist_row->cell);
6430   g_mem_chunk_free ((GMemChunk *)clist->row_mem_chunk, clist_row);
6431 #endif
6432 }
6433
6434 /* FOCUS FUNCTIONS
6435  *   gtk_cmclist_focus_content_area
6436  *   gtk_cmclist_focus
6437  *   gtk_cmclist_draw_focus
6438  *   gtk_cmclist_focus_in
6439  *   gtk_cmclist_focus_out
6440  *   title_focus
6441  */
6442 static void
6443 gtk_cmclist_focus_content_area (GtkCMCList *clist)
6444 {
6445   if (clist->focus_row < 0)
6446     {
6447       clist->focus_row = 0;
6448       
6449       if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
6450            clist->selection_mode == GTK_SELECTION_MULTIPLE) &&
6451           !clist->selection)
6452         g_signal_emit (G_OBJECT (clist),
6453                          clist_signals[SELECT_ROW], 0,
6454                          clist->focus_row, -1, NULL);
6455     }
6456   gtk_widget_grab_focus (GTK_WIDGET (clist));
6457 }
6458
6459 static gboolean
6460 gtk_cmclist_focus (GtkWidget        *widget,
6461                  GtkDirectionType  direction)
6462 {
6463   GtkCMCList *clist = GTK_CMCLIST (widget);
6464   GtkWidget *focus_child;
6465   gboolean is_current_focus;
6466
6467   if (!gtk_widget_is_sensitive (widget))
6468     return FALSE;
6469
6470   focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget));
6471   
6472   is_current_focus = gtk_widget_is_focus (GTK_WIDGET (clist));
6473                           
6474   if (focus_child &&
6475       gtk_widget_child_focus (focus_child, direction))
6476     return TRUE;
6477       
6478   switch (direction)
6479     {
6480     case GTK_DIR_LEFT:
6481     case GTK_DIR_RIGHT:
6482       if (focus_child)
6483         {
6484           if (title_focus_move (clist, direction))
6485             return TRUE;
6486         }
6487       else if (!is_current_focus)
6488         {
6489           gtk_cmclist_focus_content_area (clist);
6490           return TRUE;
6491         }
6492       break;
6493     case GTK_DIR_DOWN:
6494     case GTK_DIR_TAB_FORWARD:
6495       if (!focus_child && !is_current_focus)
6496         {
6497           if (title_focus_in (clist, direction))
6498             return TRUE;
6499         }
6500       
6501       if (!is_current_focus && clist->rows)
6502         {
6503           gtk_cmclist_focus_content_area (clist);
6504           return TRUE;
6505         }
6506       break;
6507     case GTK_DIR_UP:
6508     case GTK_DIR_TAB_BACKWARD:
6509       if (!focus_child && is_current_focus)
6510         {
6511           if (title_focus_in (clist, direction))
6512             return TRUE;
6513         }
6514       
6515       if (!is_current_focus && !focus_child && clist->rows)
6516         {
6517           gtk_cmclist_focus_content_area (clist);
6518           return TRUE;
6519         }
6520       break;
6521     default:
6522       break;
6523     }
6524
6525   return FALSE;
6526 }
6527
6528 static void
6529 gtk_cmclist_set_focus_child (GtkContainer *container,
6530                            GtkWidget    *child)
6531 {
6532   GtkCMCList *clist = GTK_CMCLIST (container);
6533   gint i;
6534
6535   for (i = 0; i < clist->columns; i++)
6536     if (clist->column[i].button == child)
6537       clist->focus_header_column = i;
6538   
6539   parent_class->set_focus_child (container, child);
6540 }
6541
6542 static void
6543 gtk_cmclist_draw_focus (GtkWidget *widget)
6544 {
6545   GtkCMCList *clist;
6546   cairo_t *cr;
6547
6548   cm_return_if_fail (GTK_IS_CMCLIST (widget));
6549
6550   if (!gtk_widget_is_drawable (widget) || !gtk_widget_get_can_focus (widget))
6551     return;
6552
6553   clist = GTK_CMCLIST (widget);
6554   if (clist->focus_row >= 0) {
6555     cr = gdk_cairo_create(clist->clist_window);
6556     cairo_dash_from_add_mode(clist, cr);
6557     cairo_set_line_width(cr, 1.0);
6558     cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
6559     cairo_rectangle(cr, 1, ROW_TOP_YPIXEL(clist, clist->focus_row),
6560                         clist->clist_window_width - 1,
6561                         clist->row_height);
6562     cairo_stroke(cr);
6563     cairo_destroy(cr);
6564   }
6565 }
6566
6567 static void
6568 gtk_cmclist_undraw_focus (GtkWidget *widget)
6569 {
6570   GtkCMCList *clist;
6571
6572   cm_return_if_fail (GTK_IS_CMCLIST (widget));
6573
6574   if (!gtk_widget_is_drawable (widget) || !gtk_widget_get_can_focus (widget))
6575     return;
6576
6577   clist = GTK_CMCLIST (widget);
6578   if (clist->focus_row >= 0) {
6579     GTK_CMCLIST_GET_CLASS(clist)->draw_row(clist, NULL, clist->focus_row, 
6580         ROW_ELEMENT (clist, clist->focus_row)->data);
6581   }
6582 }
6583
6584 static gint
6585 gtk_cmclist_focus_in (GtkWidget     *widget,
6586                     GdkEventFocus *event)
6587 {
6588   GtkCMCList *clist = GTK_CMCLIST (widget);
6589
6590   if (clist->selection_mode == GTK_SELECTION_BROWSE &&
6591       clist->selection == NULL && clist->focus_row > -1)
6592     {
6593       GList *list;
6594
6595       list = g_list_nth (clist->row_list, clist->focus_row);
6596       if (list && GTK_CMCLIST_ROW (list)->selectable)
6597         g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
6598                          clist->focus_row, -1, event);
6599       else
6600         gtk_cmclist_draw_focus (widget);
6601     }
6602   else
6603     gtk_cmclist_undraw_focus (widget);
6604
6605   return FALSE;
6606 }
6607
6608 static gint
6609 gtk_cmclist_focus_out (GtkWidget     *widget,
6610                      GdkEventFocus *event)
6611 {
6612   GtkCMCList *clist = GTK_CMCLIST (widget);
6613
6614   gtk_cmclist_undraw_focus (widget);
6615   
6616   GTK_CMCLIST_GET_CLASS (widget)->resync_selection (clist, (GdkEvent *) event);
6617
6618   return FALSE;
6619 }
6620
6621 static gboolean
6622 focus_column (GtkCMCList *clist, gint column, gint dir)
6623 {
6624   GtkWidget *child = clist->column[column].button;
6625   
6626   if (gtk_widget_child_focus (child, dir))
6627     {
6628       return TRUE;
6629     }
6630   else if (gtk_widget_get_can_focus (child))
6631     {
6632       gtk_widget_grab_focus (child);
6633       return TRUE;
6634     }
6635
6636   return FALSE;
6637 }
6638
6639 /* Focus moved onto the headers. Focus first focusable and visible child.
6640  * (FIXME: focus the last focused child if visible)
6641  */
6642 static gboolean
6643 title_focus_in (GtkCMCList *clist, gint dir)
6644 {
6645   gint i;
6646   gint left, right;
6647
6648   if (!GTK_CMCLIST_SHOW_TITLES (clist))
6649     return FALSE;
6650
6651   /* Check last focused column */
6652   if (clist->focus_header_column != -1)
6653     {
6654       i = clist->focus_header_column;
6655       
6656       left = COLUMN_LEFT_XPIXEL (clist, i);
6657       right = left + clist->column[i].area.width;
6658       
6659       if (left >= 0 && right <= clist->clist_window_width)
6660         {
6661           if (focus_column (clist, i, dir))
6662             return TRUE;
6663         }
6664     }
6665
6666   /* Check fully visible columns */
6667   for (i = 0 ; i < clist->columns ; i++)
6668     {
6669       left = COLUMN_LEFT_XPIXEL (clist, i);
6670       right = left + clist->column[i].area.width;
6671       
6672       if (left >= 0 && right <= clist->clist_window_width)
6673         {
6674           if (focus_column (clist, i, dir))
6675             return TRUE;
6676         }
6677     }
6678
6679   /* Check partially visible columns */
6680   for (i = 0 ; i < clist->columns ; i++)
6681     {
6682       left = COLUMN_LEFT_XPIXEL (clist, i);
6683       right = left + clist->column[i].area.width;
6684
6685       if ((left < 0 && right > 0) ||
6686           (left < clist->clist_window_width && right > clist->clist_window_width))
6687         {
6688           if (focus_column (clist, i, dir))
6689             return TRUE;
6690         }
6691     }
6692       
6693   return FALSE;
6694 }
6695
6696 /* Move the focus right or left within the title buttons, scrolling
6697  * as necessary to keep the focused child visible.
6698  */
6699 static gboolean
6700 title_focus_move (GtkCMCList *clist,
6701                   gint      dir)
6702 {
6703   GtkWidget *focus_child;
6704   gboolean return_val = FALSE;
6705   gint d = 0;
6706   gint i = -1;
6707   gint j;
6708
6709   if (!GTK_CMCLIST_SHOW_TITLES(clist))
6710     return FALSE;
6711
6712   focus_child = gtk_container_get_focus_child (GTK_CONTAINER (clist));
6713   g_assert (focus_child);
6714
6715   /* Movement direction within headers
6716    */
6717   switch (dir)
6718     {
6719     case GTK_DIR_RIGHT:
6720       d = 1;
6721       break;
6722     case GTK_DIR_LEFT:
6723       d = -1;
6724       break;
6725     }
6726   
6727   for (i = 0; i < clist->columns; i++)
6728     if (clist->column[i].button == focus_child)
6729       break;
6730   
6731   g_assert (i != -1);           /* Have a starting column */
6732   
6733   j = i + d;
6734   while (!return_val && j >= 0 && j < clist->columns)
6735     {
6736       if (clist->column[j].button &&
6737           gtk_widget_get_visible (clist->column[j].button))
6738         {
6739           if (focus_column (clist, j, dir))
6740             {
6741               return_val = TRUE;
6742               break;
6743             }
6744         }
6745       j += d;
6746     }
6747
6748   /* If we didn't find it, wrap around and keep looking
6749    */
6750   if (!return_val)
6751     {
6752       j = d > 0 ? 0 : clist->columns - 1;
6753
6754       while (!return_val && j != i)
6755         {
6756           if (clist->column[j].button &&
6757               gtk_widget_get_visible (clist->column[j].button))
6758             {
6759               if (focus_column (clist, j, dir))
6760                 {
6761                   return_val = TRUE;
6762                   break;
6763                 }
6764             }
6765           j += d;
6766         }
6767     }
6768
6769   /* Scroll horizontally so focused column is visible
6770    */
6771   if (return_val)
6772     {
6773       if (COLUMN_LEFT_XPIXEL (clist, j) < CELL_SPACING + COLUMN_INSET)
6774         gtk_cmclist_moveto (clist, -1, j, 0, 0);
6775       else if (COLUMN_LEFT_XPIXEL(clist, j) + clist->column[j].area.width >
6776                clist->clist_window_width)
6777         {
6778           gint last_column;
6779           
6780           for (last_column = clist->columns - 1;
6781                last_column >= 0 && !clist->column[last_column].visible; last_column--);
6782
6783           if (j == last_column)
6784             gtk_cmclist_moveto (clist, -1, j, 0, 0);
6785           else
6786             gtk_cmclist_moveto (clist, -1, j, 0, 1);
6787         }
6788     }
6789   return TRUE;                  /* Even if we didn't find a new one, we can keep the
6790                                  * focus in the same place.
6791                                  */
6792 }
6793
6794 /* PRIVATE SCROLLING FUNCTIONS
6795  *   move_focus_row
6796  *   scroll_horizontal
6797  *   scroll_vertical
6798  *   move_horizontal
6799  *   move_vertical
6800  *   horizontal_timeout
6801  *   vertical_timeout
6802  *   remove_grab
6803  */
6804 static void
6805 move_focus_row (GtkCMCList      *clist,
6806                 GtkScrollType  scroll_type,
6807                 gfloat         position)
6808 {
6809   GtkWidget *widget;
6810
6811   cm_return_if_fail (clist != 0);
6812   cm_return_if_fail (GTK_IS_CMCLIST (clist));
6813
6814   widget = GTK_WIDGET (clist);
6815
6816   switch (scroll_type)
6817     {
6818     case GTK_SCROLL_STEP_UP:
6819     case GTK_SCROLL_STEP_BACKWARD:
6820       if (clist->focus_row <= 0)
6821         return;
6822       gtk_cmclist_undraw_focus (widget);
6823       clist->focus_row--;
6824       gtk_cmclist_draw_focus (widget);
6825       break;
6826
6827     case GTK_SCROLL_STEP_DOWN:
6828     case GTK_SCROLL_STEP_FORWARD:
6829       if (clist->focus_row >= clist->rows - 1)
6830         return;
6831       gtk_cmclist_undraw_focus (widget);
6832       clist->focus_row++;
6833       gtk_cmclist_draw_focus (widget);
6834       break;
6835     case GTK_SCROLL_PAGE_UP:
6836     case GTK_SCROLL_PAGE_BACKWARD:
6837       if (clist->focus_row <= 0)
6838         return;
6839       gtk_cmclist_undraw_focus (widget);
6840       clist->focus_row = MAX (0, clist->focus_row -
6841                               (2 * clist->clist_window_height -
6842                                clist->row_height - CELL_SPACING) / 
6843                               (2 * (clist->row_height + CELL_SPACING)));
6844       gtk_cmclist_draw_focus (widget);
6845       break;
6846     case GTK_SCROLL_PAGE_DOWN:
6847     case GTK_SCROLL_PAGE_FORWARD:
6848       if (clist->focus_row >= clist->rows - 1)
6849         return;
6850       gtk_cmclist_undraw_focus (widget);
6851       clist->focus_row = MIN (clist->rows - 1, clist->focus_row + 
6852                               (2 * clist->clist_window_height -
6853                                clist->row_height - CELL_SPACING) / 
6854                               (2 * (clist->row_height + CELL_SPACING)));
6855       gtk_cmclist_draw_focus (widget);
6856       break;
6857     case GTK_SCROLL_JUMP:
6858       if (position >= 0 && position <= 1)
6859         {
6860           gtk_cmclist_undraw_focus (widget);
6861           clist->focus_row = position * (clist->rows - 1);
6862           gtk_cmclist_draw_focus (widget);
6863         }
6864       break;
6865     default:
6866       break;
6867     }
6868 }
6869
6870 static void
6871 scroll_horizontal (GtkCMCList      *clist,
6872                    GtkScrollType  scroll_type,
6873                    gfloat         position)
6874 {
6875   gint column = 0;
6876   gint last_column;
6877
6878   cm_return_if_fail (clist != 0);
6879   cm_return_if_fail (GTK_IS_CMCLIST (clist));
6880
6881   if (clist_has_grab (clist))
6882     return;
6883
6884   for (last_column = clist->columns - 1;
6885        last_column >= 0 && !clist->column[last_column].visible; last_column--)
6886     ;
6887
6888   switch (scroll_type)
6889     {
6890     case GTK_SCROLL_STEP_BACKWARD:
6891       column = COLUMN_FROM_XPIXEL (clist, 0);
6892       if (COLUMN_LEFT_XPIXEL (clist, column) - CELL_SPACING - COLUMN_INSET >= 0
6893           && column > 0)
6894         column--;
6895       break;
6896     case GTK_SCROLL_STEP_FORWARD:
6897       column = COLUMN_FROM_XPIXEL (clist, clist->clist_window_width);
6898       if (column < 0)
6899         return;
6900       if (COLUMN_LEFT_XPIXEL (clist, column) +
6901           clist->column[column].area.width +
6902           CELL_SPACING + COLUMN_INSET - 1 <= clist->clist_window_width &&
6903           column < last_column)
6904         column++;
6905       break;
6906     case GTK_SCROLL_PAGE_BACKWARD:
6907     case GTK_SCROLL_PAGE_FORWARD:
6908       return;
6909     case GTK_SCROLL_JUMP:
6910       if (position >= 0 && position <= 1)
6911         {
6912           gint vis_columns = 0;
6913           gint i;
6914
6915           for (i = 0; i <= last_column; i++)
6916             if (clist->column[i].visible)
6917               vis_columns++;
6918
6919           column = position * vis_columns;
6920
6921           for (i = 0; i <= last_column && column > 0; i++)
6922             if (clist->column[i].visible)
6923               column--;
6924
6925           column = i;
6926         }
6927       else
6928         return;
6929       break;
6930     default:
6931       break;
6932     }
6933
6934   if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET)
6935     gtk_cmclist_moveto (clist, -1, column, 0, 0);
6936   else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1
6937            + clist->column[column].area.width > clist->clist_window_width)
6938     {
6939       if (column == last_column)
6940         gtk_cmclist_moveto (clist, -1, column, 0, 0);
6941       else
6942         gtk_cmclist_moveto (clist, -1, column, 0, 1);
6943     }
6944 }
6945
6946 static void
6947 scroll_vertical (GtkCMCList      *clist,
6948                  GtkScrollType  scroll_type,
6949                  gfloat         position)
6950 {
6951   gint old_focus_row;
6952
6953   cm_return_if_fail (GTK_IS_CMCLIST (clist));
6954
6955   if (clist_has_grab (clist))
6956     return;
6957
6958   switch (clist->selection_mode)
6959     {
6960     case GTK_SELECTION_MULTIPLE:
6961       if (clist->anchor >= 0)
6962         return;
6963     case GTK_SELECTION_BROWSE:
6964
6965       old_focus_row = clist->focus_row;
6966       move_focus_row (clist, scroll_type, position);
6967
6968       if (old_focus_row != clist->focus_row)
6969         {
6970           if (clist->selection_mode == GTK_SELECTION_BROWSE)
6971             g_signal_emit (G_OBJECT (clist), clist_signals[UNSELECT_ROW], 0,
6972                              old_focus_row, -1, NULL);
6973           else if (!GTK_CMCLIST_ADD_MODE(clist))
6974             {
6975               gtk_cmclist_unselect_all (clist);
6976               clist->undo_anchor = old_focus_row;
6977             }
6978         }
6979
6980       switch (gtk_cmclist_row_is_visible (clist, clist->focus_row))
6981         {
6982         case GTK_VISIBILITY_NONE:
6983           if (old_focus_row != clist->focus_row &&
6984               !(clist->selection_mode == GTK_SELECTION_MULTIPLE &&
6985                 GTK_CMCLIST_ADD_MODE(clist)))
6986             g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
6987                              clist->focus_row, -1, NULL);
6988           switch (scroll_type)
6989             {
6990             case GTK_SCROLL_PAGE_UP:
6991             case GTK_SCROLL_STEP_UP:
6992             case GTK_SCROLL_STEP_BACKWARD:
6993             case GTK_SCROLL_PAGE_BACKWARD:
6994               gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
6995               break;
6996             case GTK_SCROLL_PAGE_DOWN:
6997             case GTK_SCROLL_STEP_DOWN:
6998             case GTK_SCROLL_STEP_FORWARD:
6999             case GTK_SCROLL_PAGE_FORWARD:
7000               gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
7001               break;
7002             case GTK_SCROLL_JUMP:
7003               gtk_cmclist_moveto (clist, clist->focus_row, -1, 0.5, 0);
7004               break;
7005             default:
7006               break;
7007             }
7008           break;
7009         case GTK_VISIBILITY_PARTIAL:
7010           switch (scroll_type)
7011             {
7012             case GTK_SCROLL_STEP_BACKWARD:
7013             case GTK_SCROLL_PAGE_BACKWARD:
7014               gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
7015               break;
7016             case GTK_SCROLL_STEP_FORWARD:
7017             case GTK_SCROLL_PAGE_FORWARD:
7018               gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
7019               break;
7020             case GTK_SCROLL_JUMP:
7021               gtk_cmclist_moveto (clist, clist->focus_row, -1, 0.5, 0);
7022               break;
7023             default:
7024               break;
7025             }
7026         default:
7027           if (old_focus_row != clist->focus_row &&
7028               !(clist->selection_mode == GTK_SELECTION_MULTIPLE &&
7029                 GTK_CMCLIST_ADD_MODE(clist)))
7030             g_signal_emit (G_OBJECT (clist), clist_signals[SELECT_ROW], 0,
7031                              clist->focus_row, -1, NULL);
7032           break;
7033         }
7034       break;
7035     default:
7036       move_focus_row (clist, scroll_type, position);
7037
7038       if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
7039           clist->clist_window_height)
7040         gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
7041       else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
7042         gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
7043       break;
7044     }
7045 }
7046
7047 static void
7048 move_horizontal (GtkCMCList *clist,
7049                  gint      diff)
7050 {
7051   gdouble value;
7052
7053   if (!clist->hadjustment)
7054     return;
7055
7056   value = CLAMP (gtk_adjustment_get_value (clist->hadjustment) + diff, 0.0,
7057                  gtk_adjustment_get_upper (clist->hadjustment) -
7058                  gtk_adjustment_get_page_size (clist->hadjustment));
7059   gtk_adjustment_set_value (clist->hadjustment, value);
7060 }
7061
7062 static void
7063 move_vertical (GtkCMCList *clist,
7064                gint      row,
7065                gfloat    align)
7066 {
7067   gdouble value;
7068   gdouble upper;
7069   gdouble page_size;
7070
7071   if (!clist->vadjustment)
7072     return;
7073
7074   value = (ROW_TOP_YPIXEL (clist, row) - clist->voffset -
7075            align * (clist->clist_window_height - clist->row_height) +
7076            (2 * align - 1) * CELL_SPACING);
7077
7078   upper = gtk_adjustment_get_upper (clist->vadjustment);
7079   page_size = gtk_adjustment_get_page_size (clist->vadjustment);
7080   if ((value + page_size) > upper)
7081         value = upper - page_size;
7082
7083   gtk_adjustment_set_value (clist->vadjustment, value);
7084 }
7085
7086 static void
7087 do_fake_motion (GtkWidget *widget)
7088 {
7089   GdkEvent *event = gdk_event_new (GDK_MOTION_NOTIFY);
7090
7091   event->motion.send_event = TRUE;
7092
7093   gtk_cmclist_motion (widget, (GdkEventMotion *)event);
7094   gdk_event_free (event);
7095 }
7096
7097 static gint
7098 horizontal_timeout (GtkCMCList *clist)
7099 {
7100   clist->htimer = 0;
7101   do_fake_motion (GTK_WIDGET (clist));
7102
7103   return FALSE;
7104 }
7105
7106 static gint
7107 vertical_timeout (GtkCMCList *clist)
7108 {
7109   clist->vtimer = 0;
7110   do_fake_motion (GTK_WIDGET (clist));
7111
7112   return FALSE;
7113 }
7114
7115 static void
7116 remove_grab (GtkCMCList *clist)
7117 {
7118   GtkWidget *widget = GTK_WIDGET (clist);
7119   
7120   if (gtk_widget_has_grab (widget))
7121     {
7122       GdkDisplay *display = gtk_widget_get_display (widget);
7123       
7124       gtk_grab_remove (widget);
7125       if (gdk_display_pointer_is_grabbed (display))
7126         gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7127     }
7128
7129   if (clist->htimer)
7130     {
7131       g_source_remove (clist->htimer);
7132       clist->htimer = 0;
7133     }
7134
7135   if (clist->vtimer)
7136     {
7137       g_source_remove (clist->vtimer);
7138       clist->vtimer = 0;
7139     }
7140 }
7141
7142 /* PUBLIC SORTING FUNCTIONS
7143  * gtk_cmclist_sort
7144  * gtk_cmclist_set_compare_func
7145  * gtk_cmclist_set_auto_sort
7146  * gtk_cmclist_set_sort_type
7147  * gtk_cmclist_set_sort_column
7148  */
7149 void
7150 gtk_cmclist_sort (GtkCMCList *clist)
7151 {
7152   cm_return_if_fail (GTK_IS_CMCLIST (clist));
7153
7154   GTK_CMCLIST_GET_CLASS (clist)->sort_list (clist);
7155 }
7156
7157 void
7158 gtk_cmclist_set_compare_func (GtkCMCList            *clist,
7159                             GtkCMCListCompareFunc  cmp_func)
7160 {
7161   cm_return_if_fail (GTK_IS_CMCLIST (clist));
7162
7163   clist->compare = (cmp_func) ? cmp_func : default_compare;
7164 }
7165
7166 void       
7167 gtk_cmclist_set_auto_sort (GtkCMCList *clist,
7168                          gboolean  auto_sort)
7169 {
7170   cm_return_if_fail (GTK_IS_CMCLIST (clist));
7171   
7172   if (GTK_CMCLIST_AUTO_SORT(clist) && !auto_sort)
7173     GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_SORT);
7174   else if (!GTK_CMCLIST_AUTO_SORT(clist) && auto_sort)
7175     {
7176       GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_SORT);
7177       gtk_cmclist_sort (clist);
7178     }
7179 }
7180
7181 void       
7182 gtk_cmclist_set_sort_type (GtkCMCList    *clist,
7183                          GtkSortType  sort_type)
7184 {
7185   cm_return_if_fail (GTK_IS_CMCLIST (clist));
7186   
7187   clist->sort_type = sort_type;
7188 }
7189
7190 void
7191 gtk_cmclist_set_sort_column (GtkCMCList *clist,
7192                            gint      column)
7193 {
7194   cm_return_if_fail (GTK_IS_CMCLIST (clist));
7195
7196   if (column < 0 || column >= clist->columns)
7197     return;
7198
7199   clist->sort_column = column;
7200 }
7201
7202 /* PRIVATE SORTING FUNCTIONS
7203  *   default_compare
7204  *   real_sort_list
7205  *   gtk_cmclist_merge
7206  *   gtk_cmclist_mergesort
7207  */
7208 static gint
7209 default_compare (GtkCMCList      *clist,
7210                  gconstpointer  ptr1,
7211                  gconstpointer  ptr2)
7212 {
7213   char *text1 = NULL;
7214   char *text2 = NULL;
7215
7216   GtkCMCListRow *row1 = (GtkCMCListRow *) ptr1;
7217   GtkCMCListRow *row2 = (GtkCMCListRow *) ptr2;
7218
7219   switch (row1->cell[clist->sort_column].type)
7220     {
7221     case GTK_CMCELL_TEXT:
7222       text1 = GTK_CMCELL_TEXT (row1->cell[clist->sort_column])->text;
7223       break;
7224     case GTK_CMCELL_PIXTEXT:
7225       text1 = GTK_CMCELL_PIXTEXT (row1->cell[clist->sort_column])->text;
7226       break;
7227     default:
7228       break;
7229     }
7230  
7231   switch (row2->cell[clist->sort_column].type)
7232     {
7233     case GTK_CMCELL_TEXT:
7234       text2 = GTK_CMCELL_TEXT (row2->cell[clist->sort_column])->text;
7235       break;
7236     case GTK_CMCELL_PIXTEXT:
7237       text2 = GTK_CMCELL_PIXTEXT (row2->cell[clist->sort_column])->text;
7238       break;
7239     default:
7240       break;
7241     }
7242
7243   if (!text2)
7244     return (text1 != NULL);
7245
7246   if (!text1)
7247     return -1;
7248
7249   return strcmp (text1, text2);
7250 }
7251
7252 static void
7253 real_sort_list (GtkCMCList *clist)
7254 {
7255   GList *list;
7256   GList *work;
7257   gint i;
7258
7259   cm_return_if_fail (GTK_IS_CMCLIST (clist));
7260
7261   if (clist->rows <= 1)
7262     return;
7263
7264   if (clist_has_grab (clist))
7265     return;
7266
7267   gtk_cmclist_freeze (clist);
7268
7269   if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_MULTIPLE)
7270     {
7271       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7272       g_list_free (clist->undo_selection);
7273       g_list_free (clist->undo_unselection);
7274       clist->undo_selection = NULL;
7275       clist->undo_unselection = NULL;
7276     }
7277    
7278   clist->row_list = gtk_cmclist_mergesort (clist, clist->row_list, clist->rows);
7279
7280   work = clist->selection;
7281
7282   for (i = 0, list = clist->row_list; i < clist->rows; i++, list = list->next)
7283     {
7284       if (GTK_CMCLIST_ROW (list)->state == GTK_STATE_SELECTED)
7285         {
7286           work->data = GINT_TO_POINTER (i);
7287           work = work->next;
7288         }
7289       
7290       if (i == clist->rows - 1)
7291         clist->row_list_end = list;
7292     }
7293
7294   gtk_cmclist_thaw (clist);
7295 }
7296
7297 static GList *
7298 gtk_cmclist_merge (GtkCMCList *clist,
7299                  GList    *a,         /* first list to merge */
7300                  GList    *b)         /* second list to merge */
7301 {
7302   GList z = { 0 };                    /* auxiliary node */
7303   GList *c;
7304   gint cmp;
7305
7306   c = &z;
7307
7308   while (a || b)
7309     {
7310       if (a && !b)
7311         {
7312           c->next = a;
7313           a->prev = c;
7314           c = a;
7315           a = a->next;
7316           break;
7317         }
7318       else if (!a && b)
7319         {
7320           c->next = b;
7321           b->prev = c;
7322           c = b;
7323           b = b->next;
7324           break;
7325         }
7326       else /* a && b */
7327         {
7328           cmp = clist->compare (clist, GTK_CMCLIST_ROW (a), GTK_CMCLIST_ROW (b));
7329           if ((cmp >= 0 && clist->sort_type == GTK_SORT_DESCENDING) ||
7330               (cmp <= 0 && clist->sort_type == GTK_SORT_ASCENDING) ||
7331               (a && !b))
7332             {
7333               c->next = a;
7334               a->prev = c;
7335               c = a;
7336               a = a->next;
7337             }
7338           else
7339             {
7340               c->next = b;
7341               b->prev = c;
7342               c = b;
7343               b = b->next;
7344             }
7345         }
7346     }
7347
7348   z.next->prev = NULL;
7349   return z.next;
7350 }
7351
7352 static GList *
7353 gtk_cmclist_mergesort (GtkCMCList *clist,
7354                      GList    *list,         /* the list to sort */
7355                      gint      num)          /* the list's length */
7356 {
7357   GList *half;
7358   gint i;
7359
7360   if (num <= 1)
7361     {
7362       return list;
7363     }
7364   else
7365     {
7366       /* move "half" to the middle */
7367       half = list;
7368       for (i = 0; i < num / 2; i++)
7369         half = half->next;
7370
7371       /* cut the list in two */
7372       half->prev->next = NULL;
7373       half->prev = NULL;
7374
7375       /* recursively sort both lists */
7376       return gtk_cmclist_merge (clist,
7377                        gtk_cmclist_mergesort (clist, list, num / 2),
7378                        gtk_cmclist_mergesort (clist, half, num - num / 2));
7379     }
7380 }
7381
7382 /************************/
7383
7384 static void
7385 drag_source_info_destroy (gpointer data)
7386 {
7387   GtkCMCListCellInfo *info = data;
7388
7389   g_free (info);
7390 }
7391
7392 static void
7393 drag_dest_info_destroy (gpointer data)
7394 {
7395   GtkCMCListDestInfo *info = data;
7396
7397   g_free (info);
7398 }
7399
7400 static void
7401 drag_dest_cell (GtkCMCList         *clist,
7402                 gint              x,
7403                 gint              y,
7404                 GtkCMCListDestInfo *dest_info)
7405 {
7406   GtkWidget *widget;
7407   GtkStyle *style;
7408   guint border_width;
7409
7410   widget = GTK_WIDGET (clist);
7411   style = gtk_widget_get_style (widget);
7412
7413   dest_info->insert_pos = GTK_CMCLIST_DRAG_NONE;
7414
7415   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
7416   y -= (border_width +
7417         style->ythickness +
7418         clist->column_title_area.height);
7419
7420   dest_info->cell.row = ROW_FROM_YPIXEL (clist, y);
7421   if (dest_info->cell.row >= clist->rows)
7422     {
7423       dest_info->cell.row = clist->rows - 1;
7424       y = ROW_TOP_YPIXEL (clist, dest_info->cell.row) + clist->row_height;
7425     }
7426   if (dest_info->cell.row < -1)
7427     dest_info->cell.row = -1;
7428   
7429   x -= border_width + style->xthickness;
7430
7431   dest_info->cell.column = COLUMN_FROM_XPIXEL (clist, x);
7432
7433   if (dest_info->cell.row >= 0)
7434     {
7435       gint y_delta;
7436       gint h = 0;
7437
7438       y_delta = y - ROW_TOP_YPIXEL (clist, dest_info->cell.row);
7439       
7440       if (GTK_CMCLIST_DRAW_DRAG_RECT(clist))
7441         {
7442           dest_info->insert_pos = GTK_CMCLIST_DRAG_INTO;
7443           h = clist->row_height / 4;
7444         }
7445       else if (GTK_CMCLIST_DRAW_DRAG_LINE(clist))
7446         {
7447           dest_info->insert_pos = GTK_CMCLIST_DRAG_BEFORE;
7448           h = clist->row_height / 2;
7449         }
7450
7451       if (GTK_CMCLIST_DRAW_DRAG_LINE(clist))
7452         {
7453           if (y_delta < h)
7454             dest_info->insert_pos = GTK_CMCLIST_DRAG_BEFORE;
7455           else if (clist->row_height - y_delta < h)
7456             dest_info->insert_pos = GTK_CMCLIST_DRAG_AFTER;
7457         }
7458     }
7459 }
7460
7461 static void
7462 gtk_cmclist_drag_begin (GtkWidget            *widget,
7463                       GdkDragContext *context)
7464 {
7465   GtkCMCList *clist;
7466   GtkCMCListCellInfo *info;
7467
7468   cm_return_if_fail (GTK_IS_CMCLIST (widget));
7469   cm_return_if_fail (context != NULL);
7470
7471   clist = GTK_CMCLIST (widget);
7472
7473   clist->drag_button = 0;
7474   remove_grab (clist);
7475
7476   switch (clist->selection_mode)
7477     {
7478     case GTK_SELECTION_MULTIPLE:
7479       update_extended_selection (clist, clist->focus_row);
7480       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7481       break;
7482     case GTK_SELECTION_SINGLE:
7483       clist->anchor = -1;
7484     case GTK_SELECTION_BROWSE:
7485       break;
7486     default:
7487       g_assert_not_reached ();
7488     }
7489
7490   info = g_dataset_get_data (context, "gtk-clist-drag-source");
7491
7492   if (!info)
7493     {
7494       info = g_new (GtkCMCListCellInfo, 1);
7495
7496       if (clist->click_cell.row < 0)
7497         clist->click_cell.row = 0;
7498       else if (clist->click_cell.row >= clist->rows)
7499         clist->click_cell.row = clist->rows - 1;
7500       info->row = clist->click_cell.row;
7501       info->column = clist->click_cell.column;
7502
7503       g_dataset_set_data_full (context, "gtk-clist-drag-source", info,
7504                                drag_source_info_destroy);
7505     }
7506
7507   if (GTK_CMCLIST_USE_DRAG_ICONS (clist))
7508     gtk_drag_set_icon_default (context);
7509 }
7510
7511 static void
7512 gtk_cmclist_drag_end (GtkWidget    *widget,
7513                     GdkDragContext *context)
7514 {
7515   GtkCMCList *clist;
7516
7517   cm_return_if_fail (GTK_IS_CMCLIST (widget));
7518   cm_return_if_fail (context != NULL);
7519
7520   clist = GTK_CMCLIST (widget);
7521
7522   clist->click_cell.row = -1;
7523   clist->click_cell.column = -1;
7524 }
7525
7526 static void
7527 gtk_cmclist_drag_leave (GtkWidget      *widget,
7528                       GdkDragContext *context,
7529                       guint           time)
7530 {
7531   GtkCMCList *clist;
7532   GtkCMCListDestInfo *dest_info;
7533
7534   cm_return_if_fail (GTK_IS_CMCLIST (widget));
7535   cm_return_if_fail (context != NULL);
7536
7537   clist = GTK_CMCLIST (widget);
7538
7539   dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7540   
7541   if (dest_info)
7542     {
7543       if (dest_info->cell.row >= 0 &&
7544           GTK_CMCLIST_REORDERABLE(clist) &&
7545           gtk_drag_get_source_widget (context) == widget)
7546         {
7547           GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder");
7548           GdkAtom found = gtk_drag_dest_find_target(widget, context, NULL);
7549
7550               if (atom == found)
7551                 {
7552                   clist->drag_highlight_row = -1;
7553                 }
7554         }
7555       g_dataset_remove_data (context, "gtk-clist-drag-dest");
7556     }
7557 }
7558
7559 static gint
7560 gtk_cmclist_drag_motion (GtkWidget      *widget,
7561                        GdkDragContext *context,
7562                        gint            x,
7563                        gint            y,
7564                        guint           time)
7565 {
7566   GtkCMCList *clist;
7567   GtkCMCListDestInfo new_info;
7568   GtkCMCListDestInfo *dest_info;
7569
7570   cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
7571
7572   clist = GTK_CMCLIST (widget);
7573
7574   dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
7575
7576   if (!dest_info)
7577     {
7578       dest_info = g_new (GtkCMCListDestInfo, 1);
7579
7580       dest_info->insert_pos  = GTK_CMCLIST_DRAG_NONE;
7581       dest_info->cell.row    = -1;
7582       dest_info->cell.column = -1;
7583
7584       g_dataset_set_data_full (context, "gtk-clist-drag-dest", dest_info,
7585                                drag_dest_info_destroy);
7586     }
7587
7588   drag_dest_cell (clist, x, y, &new_info);
7589
7590   if (GTK_CMCLIST_REORDERABLE (clist))
7591     {
7592       GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder");
7593       GdkAtom found = gtk_drag_dest_find_target(widget, context, NULL);
7594
7595       if (atom == found)
7596         {
7597           if (gtk_drag_get_source_widget (context) != widget ||
7598               new_info.insert_pos == GTK_CMCLIST_DRAG_NONE ||
7599               new_info.cell.row == clist->click_cell.row ||
7600               (new_info.cell.row == clist->click_cell.row - 1 &&
7601                new_info.insert_pos == GTK_CMCLIST_DRAG_AFTER) ||
7602               (new_info.cell.row == clist->click_cell.row + 1 &&
7603                new_info.insert_pos == GTK_CMCLIST_DRAG_BEFORE))
7604             {
7605               if (dest_info->cell.row < 0)
7606                 {
7607                   gdk_drag_status (context, GDK_ACTION_DEFAULT, time);
7608                   return FALSE;
7609                 }
7610               return TRUE;
7611             }
7612                 
7613           if (new_info.cell.row != dest_info->cell.row ||
7614               (new_info.cell.row == dest_info->cell.row &&
7615                dest_info->insert_pos != new_info.insert_pos))
7616             {
7617
7618               dest_info->insert_pos  = new_info.insert_pos;
7619               dest_info->cell.row    = new_info.cell.row;
7620               dest_info->cell.column = new_info.cell.column;
7621               
7622               clist->drag_highlight_row = dest_info->cell.row;
7623               clist->drag_highlight_pos = dest_info->insert_pos;
7624
7625               gdk_drag_status (context,
7626                 gdk_drag_context_get_suggested_action(context), time);
7627             }
7628           return TRUE;
7629         }
7630     }
7631
7632   dest_info->insert_pos  = new_info.insert_pos;
7633   dest_info->cell.row    = new_info.cell.row;
7634   dest_info->cell.column = new_info.cell.column;
7635   return TRUE;
7636 }
7637
7638 static gboolean
7639 gtk_cmclist_drag_drop (GtkWidget      *widget,
7640                      GdkDragContext *context,
7641                      gint            x,
7642                      gint            y,
7643                      guint           time)
7644 {
7645   cm_return_val_if_fail (GTK_IS_CMCLIST (widget), FALSE);
7646   cm_return_val_if_fail (context != NULL, FALSE);
7647
7648   if (GTK_CMCLIST_REORDERABLE (widget) &&
7649       gtk_drag_get_source_widget (context) == widget)
7650     {
7651       GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder");
7652       GdkAtom found = gtk_drag_dest_find_target(widget, context, NULL);
7653
7654           if (atom == found)
7655             return TRUE;
7656     }
7657   return FALSE;
7658 }
7659
7660 static void
7661 gtk_cmclist_drag_data_received (GtkWidget        *widget,
7662                               GdkDragContext   *context,
7663                               gint              x,
7664                               gint              y,
7665                               GtkSelectionData *selection_data,
7666                               guint             info,
7667                               guint             time)
7668 {
7669   GtkCMCList *clist;
7670
7671   cm_return_if_fail (GTK_IS_CMCLIST (widget));
7672   cm_return_if_fail (context != NULL);
7673   cm_return_if_fail (selection_data != NULL);
7674
7675   clist = GTK_CMCLIST (widget);
7676
7677   if (GTK_CMCLIST_REORDERABLE (clist) &&
7678       gtk_drag_get_source_widget (context) == widget &&
7679       gtk_selection_data_get_target (selection_data) ==
7680       gdk_atom_intern_static_string ("gtk-clist-drag-reorder") &&
7681       gtk_selection_data_get_format (selection_data) == 8 &&
7682       gtk_selection_data_get_length (selection_data) == sizeof (GtkCMCListCellInfo))
7683     {
7684       GtkCMCListCellInfo *source_info;
7685
7686       source_info = (GtkCMCListCellInfo *)(gtk_selection_data_get_data (selection_data));
7687       if (source_info)
7688         {
7689           GtkCMCListDestInfo dest_info;
7690
7691           drag_dest_cell (clist, x, y, &dest_info);
7692
7693           if (dest_info.insert_pos == GTK_CMCLIST_DRAG_AFTER)
7694             dest_info.cell.row++;
7695           if (source_info->row < dest_info.cell.row)
7696             dest_info.cell.row--;
7697           if (dest_info.cell.row != source_info->row)
7698             gtk_cmclist_row_move (clist, source_info->row, dest_info.cell.row);
7699
7700           g_dataset_remove_data (context, "gtk-clist-drag-dest");
7701         }
7702     }
7703 }
7704
7705 static void  
7706 gtk_cmclist_drag_data_get (GtkWidget        *widget,
7707                          GdkDragContext   *context,
7708                          GtkSelectionData *selection_data,
7709                          guint             info,
7710                          guint             time)
7711 {
7712   GdkAtom target;
7713   cm_return_if_fail (GTK_IS_CMCLIST (widget));
7714   cm_return_if_fail (context != NULL);
7715   cm_return_if_fail (selection_data != NULL);
7716
7717   target = gtk_selection_data_get_target (selection_data);
7718   if (target == gdk_atom_intern_static_string ("gtk-clist-drag-reorder"))
7719     {
7720       GtkCMCListCellInfo *info;
7721
7722       info = g_dataset_get_data (context, "gtk-clist-drag-source");
7723
7724       if (info)
7725         {
7726           GtkCMCListCellInfo ret_info;
7727
7728           ret_info.row = info->row;
7729           ret_info.column = info->column;
7730
7731           gtk_selection_data_set (selection_data, target,
7732                                   8, (guchar *) &ret_info,
7733                                   sizeof (GtkCMCListCellInfo));
7734         }
7735     }
7736 }
7737
7738 void
7739 gtk_cmclist_set_reorderable (GtkCMCList *clist, 
7740                            gboolean  reorderable)
7741 {
7742   GtkWidget *widget;
7743
7744   cm_return_if_fail (GTK_IS_CMCLIST (clist));
7745
7746   if ((GTK_CMCLIST_REORDERABLE(clist) != 0) == reorderable)
7747     return;
7748
7749   widget = GTK_WIDGET (clist);
7750
7751   if (reorderable)
7752     {
7753       GTK_CMCLIST_SET_FLAG (clist, CMCLIST_REORDERABLE);
7754       gtk_drag_dest_set (widget,
7755                          GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
7756                          &clist_target_table, 1, GDK_ACTION_MOVE);
7757     }
7758   else
7759     {
7760       GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_REORDERABLE);
7761       gtk_drag_dest_unset (GTK_WIDGET (clist));
7762     }
7763 }
7764
7765 void
7766 gtk_cmclist_set_use_drag_icons (GtkCMCList *clist,
7767                               gboolean  use_icons)
7768 {
7769   cm_return_if_fail (GTK_IS_CMCLIST (clist));
7770
7771   if (use_icons != 0)
7772     GTK_CMCLIST_SET_FLAG (clist, CMCLIST_USE_DRAG_ICONS);
7773   else
7774     GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_USE_DRAG_ICONS);
7775 }
7776
7777 void
7778 gtk_cmclist_set_button_actions (GtkCMCList *clist,
7779                               guint     button,
7780                               guint8    button_actions)
7781 {
7782   cm_return_if_fail (GTK_IS_CMCLIST (clist));
7783   
7784   if (button < MAX_BUTTON)
7785     {
7786       if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (clist))) || 
7787           gtk_widget_has_grab (GTK_WIDGET(clist)))
7788         {
7789           remove_grab (clist);
7790           clist->drag_button = 0;
7791         }
7792
7793       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
7794
7795       clist->button_actions[button] = button_actions;
7796     }
7797 }