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