source: trunk/third/gtk/gtk/gtkclist.c @ 15781

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