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