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