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