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