talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

gtkcmclist.c (210224B)


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