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