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