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