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