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