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