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