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