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