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