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