]> Pileus Git - ~andy/gtk/blob - gtk/gtkclist.c
call the base class init fucntions from all parent types upon class
[~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 "../config.h"
22 #include "gtkclist.h"
23
24 /* the number rows memchunk expands at a time */
25 #define CLIST_OPTIMUM_SIZE 512
26
27 /* the width of the column resize windows */
28 #define DRAG_WIDTH  6
29
30 /* minimum allowed width of a column */
31 #define COLUMN_MIN_WIDTH 5
32
33 /* this defigns the base grid spacing */
34 #define CELL_SPACING 1
35
36 /* added the horizontal space at the beginning and end of a row*/
37 #define COLUMN_INSET 3
38
39 /* scrollbar spacing class macro */
40 #define SCROLLBAR_SPACING(w) (GTK_CLIST_CLASS (GTK_OBJECT (w)->klass)->scrollbar_spacing)
41
42 /* gives the top pixel of the given row in context of
43  * the clist's voffset */
44 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
45                                     (((row) + 1) * CELL_SPACING) + \
46                                     (clist)->voffset)
47
48 /* returns the row index from a y pixel location in the 
49  * context of the clist's voffset */
50 #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
51                                     ((clist)->row_height + CELL_SPACING))
52
53 /* gives the left pixel of the given column in context of
54  * the clist's hoffset */
55 #define COLUMN_LEFT_XPIXEL(clist, column)  ((clist)->column[(column)].area.x + \
56                                             (clist)->hoffset)
57
58 /* returns the column index from a x pixel location in the 
59  * context of the clist's hoffset */
60 static inline gint
61 COLUMN_FROM_XPIXEL (GtkCList * clist,
62                     gint x)
63 {
64   gint i, cx;
65
66   for (i = 0; i < clist->columns; i++)
67     {
68       cx = clist->column[i].area.x + clist->hoffset;
69
70       if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
71           x <= (cx + clist->column[i].area.width + COLUMN_INSET))
72         return i;
73     }
74
75   /* no match */
76   return -1;
77 }
78
79 /* returns the top pixel of the given row in the context of
80  * the list height */
81 #define ROW_TOP(clist, row)        (((clist)->row_height + CELL_SPACING) * (row))
82
83 /* returns the left pixel of the given column in the context of
84  * the list width */
85 #define COLUMN_LEFT(clist, column) ((clist)->column[(column)].area.x)
86
87 /* returns the total height of the list */
88 #define LIST_HEIGHT(clist)         (((clist)->row_height * ((clist)->rows)) + \
89                                     (CELL_SPACING * ((clist)->rows + 1)))
90
91 /* returns the total width of the list */
92 #define LIST_WIDTH(clist)          ((clist)->column[(clist)->columns - 1].area.x + \
93                                     (clist)->column[(clist)->columns - 1].area.width + \
94                                     COLUMN_INSET + CELL_SPACING)
95
96
97 /* Signals */
98 enum
99 {
100   SELECT_ROW,
101   UNSELECT_ROW,
102   CLICK_COLUMN,
103   LAST_SIGNAL
104 };
105
106 enum
107 {
108   SYNC_REMOVE,
109   SYNC_INSERT
110 };
111
112
113 typedef void (*GtkCListSignal1) (GtkObject * object,
114                                  gint arg1,
115                                  gint arg2,
116                                  GdkEventButton * arg3,
117                                  gpointer data);
118
119 typedef void (*GtkCListSignal2) (GtkObject * object,
120                                  gint arg1,
121                                  gpointer data);
122
123
124 static void sync_selection (GtkCList * clist,
125                             gint row,
126                             gint mode);
127
128 /* GtkCList Methods */
129 static void gtk_clist_class_init (GtkCListClass * klass);
130 static void gtk_clist_init (GtkCList * clist);
131
132 /* GtkObject Methods */
133 static void gtk_clist_destroy (GtkObject * object);
134 static void gtk_clist_finalize (GtkObject * object);
135
136
137 /* GtkWidget Methods */
138 static void gtk_clist_realize (GtkWidget * widget);
139 static void gtk_clist_unrealize (GtkWidget * widget);
140 static void gtk_clist_map (GtkWidget * widget);
141 static void gtk_clist_unmap (GtkWidget * widget);
142 static void gtk_clist_draw (GtkWidget * widget,
143                             GdkRectangle * area);
144 static gint gtk_clist_expose (GtkWidget * widget,
145                               GdkEventExpose * event);
146 static gint gtk_clist_button_press (GtkWidget * widget,
147                                     GdkEventButton * event);
148 static gint gtk_clist_button_release (GtkWidget * widget,
149                                       GdkEventButton * event);
150 static gint gtk_clist_motion (GtkWidget * widget, 
151                               GdkEventMotion * event);
152
153 static void gtk_clist_size_request (GtkWidget * widget,
154                                     GtkRequisition * requisition);
155 static void gtk_clist_size_allocate (GtkWidget * widget,
156                                      GtkAllocation * allocation);
157 static gint get_selection_info (GtkCList * clist,
158                                 gint x,
159                                 gint y,
160                                 gint * row,
161                                 gint * column);
162
163 /* GtkContainer Methods */
164 static void gtk_clist_foreach (GtkContainer * container,
165                                GtkCallback callback,
166                                gpointer callback_data);
167
168 /* Drawing */
169 static void draw_row (GtkCList * clist,
170                       GdkRectangle * area,
171                       gint row,
172                       GtkCListRow * clist_row);
173 static void draw_rows (GtkCList * clist,
174                        GdkRectangle * area);
175
176 /* Size Allocation */
177 static void size_allocate_title_buttons (GtkCList * clist);
178 static void size_allocate_columns (GtkCList * clist);
179
180 /* Selection */
181 static void toggle_row (GtkCList * clist,
182                         gint row,
183                         gint column,
184                         GdkEventButton * event);
185 static void select_row (GtkCList * clist,
186                         gint row,
187                         gint column,
188                         GdkEventButton * event);
189 static void unselect_row (GtkCList * clist,
190                           gint row,
191                           gint column,
192                           GdkEventButton * event);
193
194 static void real_select_row (GtkCList * clist,
195                              gint row,
196                              gint column,
197                              GdkEventButton * event);
198 static void real_unselect_row (GtkCList * clist,
199                                gint row,
200                                gint column,
201                                GdkEventButton * event);
202
203 /* Resize Columns */
204 static void draw_xor_line (GtkCList * clist);
205 static gint new_column_width (GtkCList * clist,
206                               gint column,
207                               gint * x,
208                               gint * visible);
209 static void resize_column (GtkCList * clist,
210                            gint column,
211                            gint width);
212
213 /* Buttons */
214 static void column_button_create (GtkCList * clist,
215                                   gint column);
216 static void column_button_clicked (GtkWidget * widget,
217                                    gpointer data);
218
219 /* Scrollbars */
220 static void create_scrollbars (GtkCList * clist);
221 static void adjust_scrollbars (GtkCList * clist);
222 static void check_exposures   (GtkCList * clist);
223 static void vadjustment_changed (GtkAdjustment * adjustment,
224                                  gpointer data);
225 static void vadjustment_value_changed (GtkAdjustment * adjustment,
226                                        gpointer data);
227 static void hadjustment_changed (GtkAdjustment * adjustment,
228                                  gpointer data);
229 static void hadjustment_value_changed (GtkAdjustment * adjustment,
230                                        gpointer data);
231
232
233 /* Memory Allocation/Distruction Routines */
234 static GtkCListColumn *columns_new (GtkCList * clist);
235
236 static void column_title_new (GtkCList * clist,
237                               gint column,
238                               gchar * title);
239 static void columns_delete (GtkCList * clist);
240
241 static GtkCListRow *row_new (GtkCList * clist);
242
243 static void row_delete (GtkCList * clist,
244                         GtkCListRow * clist_row);
245 static void cell_empty (GtkCList * clist,
246                         GtkCListRow * clist_row,
247                         gint column);
248 static void cell_set_text (GtkCList * clist,
249                            GtkCListRow * clist_row,
250                            gint column,
251                            gchar * text);
252 static void cell_set_pixmap (GtkCList * clist,
253                              GtkCListRow * clist_row,
254                              gint column,
255                              GdkPixmap * pixmap,
256                              GdkBitmap * mask);
257 static void cell_set_pixtext (GtkCList * clist,
258                               GtkCListRow * clist_row,
259                               gint column,
260                               gchar * text,
261                               guint8 spacing,
262                               GdkPixmap * pixmap,
263                               GdkBitmap * mask);
264
265 /* Signals */
266 static void gtk_clist_marshal_signal_1 (GtkObject * object,
267                                         GtkSignalFunc func,
268                                         gpointer func_data,
269                                         GtkArg * args);
270 static void gtk_clist_marshal_signal_2 (GtkObject * object,
271                                         GtkSignalFunc func,
272                                         gpointer func_data,
273                                         GtkArg * args);
274
275 /* Fill in data after widget is realized and has style */
276
277 static void add_style_data (GtkCList * clist);
278
279 static GtkContainerClass *parent_class = NULL;
280 static guint clist_signals[LAST_SIGNAL] = {0};
281
282
283 GtkType
284 gtk_clist_get_type (void)
285 {
286   static GtkType clist_type = 0;
287
288   if (!clist_type)
289     {
290       GtkTypeInfo clist_info =
291       {
292         "GtkCList",
293         sizeof (GtkCList),
294         sizeof (GtkCListClass),
295         (GtkClassInitFunc) gtk_clist_class_init,
296         (GtkObjectInitFunc) gtk_clist_init,
297         /* reversed_1 */ NULL,
298         /* reversed_2 */ NULL,
299         (GtkClassInitFunc) NULL,
300       };
301
302       clist_type = gtk_type_unique (gtk_container_get_type (), &clist_info);
303     }
304
305   return clist_type;
306 }
307
308 static void
309 gtk_clist_class_init (GtkCListClass * klass)
310 {
311   GtkObjectClass *object_class;
312   GtkWidgetClass *widget_class;
313   GtkContainerClass *container_class;
314
315   object_class = (GtkObjectClass *) klass;
316   widget_class = (GtkWidgetClass *) klass;
317   container_class = (GtkContainerClass *) klass;
318
319   parent_class = gtk_type_class (gtk_container_get_type ());
320
321   clist_signals[SELECT_ROW] =
322     gtk_signal_new ("select_row",
323                     GTK_RUN_FIRST,
324                     object_class->type,
325                     GTK_SIGNAL_OFFSET (GtkCListClass, select_row),
326                     gtk_clist_marshal_signal_1,
327             GTK_TYPE_NONE, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
328   clist_signals[UNSELECT_ROW] =
329     gtk_signal_new ("unselect_row",
330                     GTK_RUN_FIRST,
331                     object_class->type,
332                     GTK_SIGNAL_OFFSET (GtkCListClass, unselect_row),
333                     gtk_clist_marshal_signal_1,
334             GTK_TYPE_NONE, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_GDK_EVENT);
335   clist_signals[CLICK_COLUMN] =
336     gtk_signal_new ("click_column",
337                     GTK_RUN_FIRST,
338                     object_class->type,
339                     GTK_SIGNAL_OFFSET (GtkCListClass, click_column),
340                     gtk_clist_marshal_signal_2,
341                     GTK_TYPE_NONE, 1, GTK_TYPE_INT);
342
343   gtk_object_class_add_signals (object_class, clist_signals, LAST_SIGNAL);
344
345   object_class->destroy = gtk_clist_destroy;
346   object_class->finalize = gtk_clist_finalize;
347
348   widget_class->realize = gtk_clist_realize;
349   widget_class->unrealize = gtk_clist_unrealize;
350   widget_class->map = gtk_clist_map;
351   widget_class->unmap = gtk_clist_unmap;
352   widget_class->draw = gtk_clist_draw;
353   widget_class->button_press_event = gtk_clist_button_press;
354   widget_class->button_release_event = gtk_clist_button_release;
355   widget_class->motion_notify_event = gtk_clist_motion;
356   widget_class->expose_event = gtk_clist_expose;
357   widget_class->size_request = gtk_clist_size_request;
358   widget_class->size_allocate = gtk_clist_size_allocate;
359
360   /* container_class->add = NULL; use the default GtkContainerClass warning */
361   /* container_class->remove = NULL; use the default GtkContainerClass warning */
362   container_class->foreach = gtk_clist_foreach;
363
364   klass->select_row = real_select_row;
365   klass->unselect_row = real_unselect_row;
366   klass->click_column = NULL;
367
368   klass->draw_row = draw_row;
369
370   klass->scrollbar_spacing = 5;
371 }
372
373 static void
374 gtk_clist_marshal_signal_1 (GtkObject * object,
375                             GtkSignalFunc func,
376                             gpointer func_data,
377                             GtkArg * args)
378 {
379   GtkCListSignal1 rfunc;
380
381   rfunc = (GtkCListSignal1) func;
382
383   (*rfunc) (object, GTK_VALUE_INT (args[0]),
384             GTK_VALUE_INT (args[1]),
385             GTK_VALUE_POINTER (args[2]),
386             func_data);
387 }
388
389 static void
390 gtk_clist_marshal_signal_2 (GtkObject * object,
391                             GtkSignalFunc func,
392                             gpointer func_data,
393                             GtkArg * args)
394 {
395   GtkCListSignal2 rfunc;
396
397   rfunc = (GtkCListSignal2) func;
398
399   (*rfunc) (object, GTK_VALUE_INT (args[0]),
400             func_data);
401 }
402
403 static void
404 gtk_clist_init (GtkCList * clist)
405 {
406   clist->flags = 0;
407
408   GTK_WIDGET_UNSET_FLAGS (clist, GTK_NO_WINDOW);
409   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
410
411   clist->row_mem_chunk = NULL;
412   clist->cell_mem_chunk = NULL;
413
414   clist->rows = 0;
415   clist->row_center_offset = 0;
416   clist->row_height = 0;
417   clist->row_list = NULL;
418   clist->row_list_end = NULL;
419
420   clist->columns = 0;
421
422   clist->title_window = NULL;
423   clist->column_title_area.x = 0;
424   clist->column_title_area.y = 0;
425   clist->column_title_area.width = 1;
426   clist->column_title_area.height = 1;
427
428   clist->clist_window = NULL;
429   clist->clist_window_width = 1;
430   clist->clist_window_height = 1;
431
432   clist->hoffset = 0;
433   clist->voffset = 0;
434
435   clist->shadow_type = GTK_SHADOW_IN;
436   clist->hscrollbar_policy = GTK_POLICY_ALWAYS;
437   clist->vscrollbar_policy = GTK_POLICY_ALWAYS;
438
439   clist->cursor_drag = NULL;
440   clist->xor_gc = NULL;
441   clist->fg_gc = NULL;
442   clist->bg_gc = NULL;
443   clist->x_drag = 0;
444
445   clist->selection_mode = GTK_SELECTION_SINGLE;
446   clist->selection = NULL;
447 }
448
449 /* Constructors */
450 void
451 gtk_clist_construct (GtkCList * clist,
452                      gint columns,
453                      gchar *titles[])
454 {
455   int i;
456
457   g_return_if_fail (clist != NULL);
458   g_return_if_fail (GTK_IS_CLIST (clist));
459   g_return_if_fail (GTK_CLIST_CONSTRUCTED (clist) == FALSE);
460
461   GTK_CLIST_SET_FLAG (clist, CLIST_CONSTRUCTED);
462
463   /* initalize memory chunks, if this has not been done by any
464    * possibly derived widget
465    */
466   if (!clist->row_mem_chunk)
467     clist->row_mem_chunk = g_mem_chunk_new ("clist row mem chunk",
468                                             sizeof (GtkCListRow),
469                                             sizeof (GtkCListRow) * CLIST_OPTIMUM_SIZE, 
470                                             G_ALLOC_AND_FREE);
471
472   if (!clist->cell_mem_chunk)
473     clist->cell_mem_chunk = g_mem_chunk_new ("clist cell mem chunk",
474                                              sizeof (GtkCell) * columns,
475                                              sizeof (GtkCell) * columns * CLIST_OPTIMUM_SIZE, 
476                                              G_ALLOC_AND_FREE);
477
478   /* set number of columns, allocate memory */
479   clist->columns = columns;
480   clist->column = columns_new (clist);
481
482   /* there needs to be at least one column button 
483    * because there is alot of code that will break if it
484    * isn't there*/
485   column_button_create (clist, 0);
486
487   /* create scrollbars */
488   create_scrollbars (clist);
489
490   if (titles)
491     {
492       GTK_CLIST_SET_FLAG (clist, CLIST_SHOW_TITLES);
493       for (i = 0; i < columns; i++)
494         gtk_clist_set_column_title (clist, i, titles[i]);
495     }
496   else
497     {
498       GTK_CLIST_UNSET_FLAG (clist, CLIST_SHOW_TITLES);
499     }
500 }
501
502 /*
503  * GTKCLIST PUBLIC INTERFACE
504  *   gtk_clist_new_with_titles
505  *   gtk_clist_new
506  */
507 GtkWidget *
508 gtk_clist_new_with_titles (gint columns,
509                            gchar * titles[])
510 {
511   GtkWidget *widget;
512
513   g_return_val_if_fail (titles != NULL, NULL);
514   
515   widget = gtk_type_new (gtk_clist_get_type ());
516   
517   gtk_clist_construct (GTK_CLIST (widget), columns, titles);
518
519   return widget;
520 }
521
522 GtkWidget *
523 gtk_clist_new (gint columns)
524 {
525   GtkCList *clist;
526
527   if (columns < 1)
528     return NULL;
529
530   clist = gtk_type_new (gtk_clist_get_type ());
531   gtk_clist_construct (clist, columns, NULL);
532   return GTK_WIDGET (clist);
533 }
534
535 void
536 gtk_clist_set_border (GtkCList * clist,
537                       GtkShadowType border)
538 {
539   g_return_if_fail (clist != NULL);
540
541   clist->shadow_type = border;
542
543   if (GTK_WIDGET_VISIBLE (clist))
544     gtk_widget_queue_resize (GTK_WIDGET (clist));
545 }
546
547 void
548 gtk_clist_set_selection_mode (GtkCList * clist,
549                               GtkSelectionMode mode)
550 {
551   g_return_if_fail (clist != NULL);
552
553   clist->selection_mode = mode;
554 }
555
556 void
557 gtk_clist_freeze (GtkCList * clist)
558 {
559   g_return_if_fail (clist != NULL);
560
561   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
562 }
563
564 void
565 gtk_clist_thaw (GtkCList * clist)
566 {
567   g_return_if_fail (clist != NULL);
568
569   GTK_CLIST_UNSET_FLAG (clist, CLIST_FROZEN);
570
571   adjust_scrollbars (clist);
572   draw_rows (clist, NULL);
573 }
574
575 void
576 gtk_clist_column_titles_show (GtkCList * clist)
577 {
578   g_return_if_fail (clist != NULL);
579
580   if (!GTK_CLIST_SHOW_TITLES (clist))
581     {
582       GTK_CLIST_SET_FLAG (clist, CLIST_SHOW_TITLES);
583       if (clist->title_window)
584               gdk_window_show (clist->title_window);
585       gtk_widget_queue_resize (GTK_WIDGET (clist));
586     }
587 }
588
589 void 
590 gtk_clist_column_titles_hide (GtkCList * clist)
591 {
592   g_return_if_fail (clist != NULL);
593
594   if (GTK_CLIST_SHOW_TITLES (clist))
595     {
596       GTK_CLIST_UNSET_FLAG (clist, CLIST_SHOW_TITLES);
597       if (clist->title_window)
598               gdk_window_hide (clist->title_window);
599       gtk_widget_queue_resize (GTK_WIDGET (clist));
600     }
601 }
602
603 void
604 gtk_clist_column_title_active (GtkCList * clist,
605                                gint column)
606 {
607   g_return_if_fail (clist != NULL);
608
609   if (column < 0 || column >= clist->columns)
610     return;
611
612   if (!GTK_WIDGET_SENSITIVE (clist->column[column].button) ||
613       !GTK_WIDGET_CAN_FOCUS (clist->column[column].button))
614     {
615       GTK_WIDGET_SET_FLAGS (clist->column[column].button, GTK_SENSITIVE | GTK_CAN_FOCUS);
616       if (GTK_WIDGET_VISIBLE (clist))
617         gtk_widget_queue_draw (clist->column[column].button);
618     }
619 }
620
621 void
622 gtk_clist_column_title_passive (GtkCList * clist,
623                                 gint column)
624 {
625   g_return_if_fail (clist != NULL);
626
627   if (column < 0 || column >= clist->columns)
628     return;
629
630   if (GTK_WIDGET_SENSITIVE (clist->column[column].button) ||
631       GTK_WIDGET_CAN_FOCUS (clist->column[column].button))
632     {
633       GTK_WIDGET_UNSET_FLAGS (clist->column[column].button, GTK_SENSITIVE | GTK_CAN_FOCUS);
634       if (GTK_WIDGET_VISIBLE (clist))
635         gtk_widget_queue_draw (clist->column[column].button);
636     }
637 }
638
639 void
640 gtk_clist_column_titles_active (GtkCList * clist)
641 {
642   gint i;
643
644   g_return_if_fail (clist != NULL);
645
646   for (i = 0; i < clist->columns; i++)
647     if (clist->column[i].button)
648       gtk_clist_column_title_active (clist, i);
649 }
650
651 void
652 gtk_clist_column_titles_passive (GtkCList * clist)
653 {
654   gint i;
655
656   g_return_if_fail (clist != NULL);
657
658   for (i = 0; i < clist->columns; i++)
659     if (clist->column[i].button)
660       gtk_clist_column_title_passive (clist, i);
661 }
662
663 void
664 gtk_clist_set_column_title (GtkCList * clist,
665                             gint column,
666                             gchar * title)
667 {
668   gint new_button = 0;
669   GtkWidget *old_widget;
670   GtkWidget *alignment = NULL;
671   GtkWidget *label;
672
673   g_return_if_fail (clist != NULL);
674
675   if (column < 0 || column >= clist->columns)
676     return;
677
678   /* if the column button doesn't currently exist,
679    * it has to be created first */
680   if (!clist->column[column].button)
681     {
682       column_button_create (clist, column);
683       new_button = 1;
684     }
685
686   column_title_new (clist, column, title);
687
688   /* remove and destroy the old widget */
689   old_widget = GTK_BUTTON (clist->column[column].button)->child;
690   if (old_widget)
691     gtk_container_remove (GTK_CONTAINER (clist->column[column].button), old_widget);
692
693   /* create new alignment based no column justification */
694   switch (clist->column[column].justification)
695     {
696     case GTK_JUSTIFY_LEFT:
697       alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
698       break;
699
700     case GTK_JUSTIFY_RIGHT:
701       alignment = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
702       break;
703
704     case GTK_JUSTIFY_CENTER:
705       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
706       break;
707
708     case GTK_JUSTIFY_FILL:
709       alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
710       break;
711     }
712
713   label = gtk_label_new (clist->column[column].title);
714   gtk_container_add (GTK_CONTAINER (alignment), label);
715   gtk_container_add (GTK_CONTAINER (clist->column[column].button), alignment);
716   gtk_widget_show (label);
717   gtk_widget_show (alignment);
718
719   /* if this button didn't previously exist, then the
720    * column button positions have to be re-computed */
721   if (GTK_WIDGET_VISIBLE (clist) && new_button)
722     size_allocate_title_buttons (clist);
723 }
724
725 void
726 gtk_clist_set_column_widget (GtkCList * clist,
727                              gint column,
728                              GtkWidget * widget)
729 {
730   gint new_button = 0;
731   GtkWidget *old_widget;
732
733   g_return_if_fail (clist != NULL);
734
735   if (column < 0 || column >= clist->columns)
736     return;
737
738   /* if the column button doesn't currently exist,
739    * it has to be created first */
740   if (!clist->column[column].button)
741     {
742       column_button_create (clist, column);
743       new_button = 1;
744     }
745
746   column_title_new (clist, column, NULL);
747
748   /* remove and destroy the old widget */
749   old_widget = GTK_BUTTON (clist->column[column].button)->child;
750   if (old_widget)
751     gtk_container_remove (GTK_CONTAINER (clist->column[column].button), old_widget);
752
753   /* add and show the widget */
754   if (widget)
755     {
756       gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget);
757       gtk_widget_show (widget);
758     }
759
760   /* if this button didn't previously exist, then the
761    * column button positions have to be re-computed */
762   if (GTK_WIDGET_VISIBLE (clist) && new_button)
763     size_allocate_title_buttons (clist);
764 }
765
766 void
767 gtk_clist_set_column_justification (GtkCList * clist,
768                                     gint column,
769                                     GtkJustification justification)
770 {
771   GtkWidget *alignment;
772
773   g_return_if_fail (clist != NULL);
774
775   if (column < 0 || column >= clist->columns)
776     return;
777
778   clist->column[column].justification = justification;
779
780   /* change the alinment of the button title if it's not a
781    * custom widget */
782   if (clist->column[column].title)
783     {
784       alignment = GTK_BUTTON (clist->column[column].button)->child;
785
786       switch (clist->column[column].justification)
787         {
788         case GTK_JUSTIFY_LEFT:
789           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.0, 0.5, 0.0, 0.0);
790           break;
791
792         case GTK_JUSTIFY_RIGHT:
793           gtk_alignment_set (GTK_ALIGNMENT (alignment), 1.0, 0.5, 0.0, 0.0);
794           break;
795
796         case GTK_JUSTIFY_CENTER:
797           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
798           break;
799
800         case GTK_JUSTIFY_FILL:
801           gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
802           break;
803
804         default:
805           break;
806         }
807     }
808
809   if (!GTK_CLIST_FROZEN (clist))
810     draw_rows (clist, NULL);
811 }
812
813 void
814 gtk_clist_set_column_width (GtkCList * clist,
815                             gint column,
816                             gint width)
817 {
818   g_return_if_fail (clist != NULL);
819
820   if (column < 0 || column >= clist->columns)
821     return;
822
823   clist->column[column].width = width;
824   clist->column[column].width_set = TRUE;
825
826   /* FIXME: this is quite expensive to do if the widget hasn't
827    *        been size_allocated yet, and pointless. Should
828    *        a flag be kept
829    */
830   size_allocate_columns (clist);
831   size_allocate_title_buttons (clist);
832
833   if (!GTK_CLIST_FROZEN (clist))
834     {
835       adjust_scrollbars (clist);
836       draw_rows (clist, NULL);
837     }
838 }
839
840 void
841 gtk_clist_set_row_height (GtkCList * clist,
842                           gint height)
843 {
844   gint text_height;
845
846   g_return_if_fail (clist != NULL);
847
848   if (height > 0)
849     clist->row_height = height;
850   else
851     return;
852
853   GTK_CLIST_SET_FLAG (clist, CLIST_ROW_HEIGHT_SET);
854   
855   if (GTK_WIDGET_REALIZED (clist))
856     {
857       text_height = height - (GTK_WIDGET (clist)->style->font->ascent +
858                               GTK_WIDGET (clist) ->style->font->descent + 1);
859       clist->row_center_offset = (text_height / 2) + GTK_WIDGET (clist)->style->font->ascent + 1.5;
860     }
861       
862   if (!GTK_CLIST_FROZEN (clist))
863     {
864       adjust_scrollbars (clist);
865       draw_rows (clist, NULL);
866     }
867 }
868
869 void
870 gtk_clist_moveto (GtkCList * clist,
871                   gint row,
872                   gint column,
873                   gfloat row_align,
874                   gfloat col_align)
875 {
876   gint x, y;
877
878   g_return_if_fail (clist != NULL);
879
880   if (row < -1 || row >= clist->rows)
881     return;
882   if (column < -1 || column >= clist->columns)
883     return;
884
885   /* adjust vertical scrollbar */
886   if (row >= 0)
887     {
888       x = ROW_TOP (clist, row) - (row_align * (clist->clist_window_height - 
889                                                (clist->row_height + 2 * CELL_SPACING)));
890       
891       if (x < 0)
892         GTK_RANGE (clist->vscrollbar)->adjustment->value = 0.0;
893       else if (x > LIST_HEIGHT (clist) - clist->clist_window_height)
894         GTK_RANGE (clist->vscrollbar)->adjustment->value = LIST_HEIGHT (clist) - 
895           clist->clist_window_height;
896       else
897         GTK_RANGE (clist->vscrollbar)->adjustment->value = x;
898       
899       gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), 
900                                "value_changed");
901     } 
902      
903   /* adjust horizontal scrollbar */
904   if (column >= 0)
905     {
906       y = COLUMN_LEFT (clist, column) - (col_align * (clist->clist_window_width - 
907                                                       clist->column[column].area.width + 
908                                                       2 * (CELL_SPACING + COLUMN_INSET)));
909       
910       if (y < 0)
911         GTK_RANGE (clist->hscrollbar)->adjustment->value = 0.0;
912       else if (y > LIST_WIDTH (clist) - clist->clist_window_width)
913         GTK_RANGE (clist->hscrollbar)->adjustment->value = LIST_WIDTH (clist) - 
914           clist->clist_window_width;
915       else
916         GTK_RANGE (clist->hscrollbar)->adjustment->value = y;
917       
918       gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->hscrollbar)->adjustment), 
919                                "value_changed");
920     }
921 }
922
923 GtkCellType 
924 gtk_clist_get_cell_type (GtkCList * clist,
925                          gint row,
926                          gint column)
927 {
928   GtkCListRow *clist_row;
929
930   g_return_val_if_fail (clist != NULL, -1);
931
932   if (row < 0 || row >= clist->rows)
933     return -1;
934   if (column < 0 || column >= clist->columns)
935     return -1;
936
937   clist_row = (g_list_nth (clist->row_list, row))->data;
938
939   return clist_row->cell[column].type;
940 }
941
942 void
943 gtk_clist_set_text (GtkCList * clist,
944                     gint row,
945                     gint column,
946                     gchar * text)
947 {
948   GtkCListRow *clist_row;
949
950   g_return_if_fail (clist != NULL);
951
952   if (row < 0 || row >= clist->rows)
953     return;
954   if (column < 0 || column >= clist->columns)
955     return;
956
957   clist_row = (g_list_nth (clist->row_list, row))->data;
958
959   /* if text is null, then the cell is empty */
960   if (text)
961     cell_set_text (clist, clist_row, column, text);
962   else
963     cell_empty (clist, clist_row, column);
964
965   /* redraw the list if it's not frozen */
966   if (!GTK_CLIST_FROZEN (clist))
967     {
968       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
969         (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
970           (clist, NULL, row, clist_row);
971     }
972 }
973
974 gint
975 gtk_clist_get_text (GtkCList * clist,
976                     gint row,
977                     gint column,
978                     gchar ** text)
979 {
980   GtkCListRow *clist_row;
981
982   g_return_val_if_fail (clist != NULL, 0);
983
984   if (row < 0 || row >= clist->rows)
985     return 0;
986   if (column < 0 || column >= clist->columns)
987     return 0;
988
989   clist_row = (g_list_nth (clist->row_list, row))->data;
990
991   if (clist_row->cell[column].type != GTK_CELL_TEXT)
992     return 0;
993
994   if (text)
995     *text = GTK_CELL_TEXT (clist_row->cell[column])->text;
996
997   return 1;
998 }
999
1000 void
1001 gtk_clist_set_pixmap (GtkCList * clist,
1002                       gint row,
1003                       gint column,
1004                       GdkPixmap * pixmap,
1005                       GdkBitmap * mask)
1006 {
1007   GtkCListRow *clist_row;
1008
1009   g_return_if_fail (clist != NULL);
1010
1011   if (row < 0 || row >= clist->rows)
1012     return;
1013   if (column < 0 || column >= clist->columns)
1014     return;
1015
1016   clist_row = (g_list_nth (clist->row_list, row))->data;
1017   
1018   gdk_pixmap_ref (pixmap);
1019   
1020   if (mask) gdk_pixmap_ref (mask);
1021   
1022   cell_set_pixmap (clist, clist_row, column, pixmap, mask);
1023
1024   /* redraw the list if it's not frozen */
1025   if (!GTK_CLIST_FROZEN (clist))
1026     {
1027       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
1028         (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
1029           (clist, NULL, row, clist_row);
1030     }
1031 }
1032
1033 gint
1034 gtk_clist_get_pixmap (GtkCList * clist,
1035                       gint row,
1036                       gint column,
1037                       GdkPixmap ** pixmap,
1038                       GdkBitmap ** mask)
1039 {
1040   GtkCListRow *clist_row;
1041
1042   g_return_val_if_fail (clist != NULL, 0);
1043
1044   if (row < 0 || row >= clist->rows)
1045     return 0;
1046   if (column < 0 || column >= clist->columns)
1047     return 0;
1048
1049   clist_row = (g_list_nth (clist->row_list, row))->data;
1050
1051   if (clist_row->cell[column].type != GTK_CELL_PIXMAP)
1052     return 0;
1053
1054   if (pixmap)
1055   {
1056     *pixmap = GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap;
1057     /* mask can be NULL */
1058     *mask = GTK_CELL_PIXMAP (clist_row->cell[column])->mask;
1059   }
1060
1061   return 1;
1062 }
1063
1064 void
1065 gtk_clist_set_pixtext (GtkCList * clist,
1066                        gint row,
1067                        gint column,
1068                        gchar * text,
1069                        guint8 spacing,
1070                        GdkPixmap * pixmap,
1071                        GdkBitmap * mask)
1072 {
1073   GtkCListRow *clist_row;
1074
1075   g_return_if_fail (clist != NULL);
1076
1077   if (row < 0 || row >= clist->rows)
1078     return;
1079   if (column < 0 || column >= clist->columns)
1080     return;
1081
1082   clist_row = (g_list_nth (clist->row_list, row))->data;
1083   
1084   gdk_pixmap_ref (pixmap);
1085   if (mask) gdk_pixmap_ref (mask);
1086   cell_set_pixtext (clist, clist_row, column, text, spacing, pixmap, mask);
1087
1088   /* redraw the list if it's not frozen */
1089   if (!GTK_CLIST_FROZEN (clist))
1090     {
1091       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
1092         (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row) 
1093           (clist, NULL, row, clist_row);
1094     }
1095 }
1096
1097 gint
1098 gtk_clist_get_pixtext (GtkCList * clist,
1099                        gint row,
1100                        gint column,
1101                        gchar ** text,
1102                        guint8 * spacing,
1103                        GdkPixmap ** pixmap,
1104                        GdkBitmap ** mask)
1105 {
1106   GtkCListRow *clist_row;
1107
1108   g_return_val_if_fail (clist != NULL, 0);
1109
1110   if (row < 0 || row >= clist->rows)
1111     return 0;
1112   if (column < 0 || column >= clist->columns)
1113     return 0;
1114
1115   clist_row = (g_list_nth (clist->row_list, row))->data;
1116
1117   if (clist_row->cell[column].type != GTK_CELL_PIXTEXT)
1118     return 0;
1119
1120   if (text)
1121     *text = GTK_CELL_PIXTEXT (clist_row->cell[column])->text;
1122   if (spacing)
1123     *spacing = GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing;
1124   if (pixmap)
1125     *pixmap = GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap;
1126
1127   /* mask can be NULL */
1128   *mask = GTK_CELL_PIXTEXT (clist_row->cell[column])->mask;
1129
1130   return 1;
1131 }
1132
1133 void
1134 gtk_clist_set_foreground (GtkCList * clist,
1135                           gint row,
1136                           GdkColor * color)
1137 {
1138   GtkCListRow *clist_row;
1139
1140   g_return_if_fail (clist != NULL);
1141
1142   if (row < 0 || row >= clist->rows)
1143     return;
1144
1145   clist_row = (g_list_nth (clist->row_list, row))->data;
1146
1147   if (color)
1148     {
1149       clist_row->foreground = *color;
1150       clist_row->fg_set = TRUE;
1151     }
1152   else
1153     clist_row->fg_set = FALSE;
1154
1155   if (!GTK_CLIST_FROZEN (clist)
1156       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
1157     (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
1158       (clist, NULL, row, clist_row);
1159 }
1160
1161 void
1162 gtk_clist_set_background (GtkCList * clist,
1163                           gint row,
1164                           GdkColor * color)
1165 {
1166   GtkCListRow *clist_row;
1167
1168   g_return_if_fail (clist != NULL);
1169
1170   if (row < 0 || row >= clist->rows)
1171     return;
1172
1173   clist_row = (g_list_nth (clist->row_list, row))->data;
1174
1175   if (color)
1176     {
1177       clist_row->background = *color;
1178       clist_row->bg_set = TRUE;
1179     }
1180   else
1181     clist_row->bg_set = FALSE;
1182
1183   if (!GTK_CLIST_FROZEN (clist)
1184       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
1185     (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
1186       (clist, NULL, row, clist_row);
1187 }
1188
1189 void
1190 gtk_clist_set_shift (GtkCList * clist,
1191                      gint row,
1192                      gint column,
1193                      gint vertical,
1194                      gint horizontal)
1195 {
1196   GtkCListRow *clist_row;
1197
1198   g_return_if_fail (clist != NULL);
1199
1200   if (row < 0 || row >= clist->rows)
1201     return;
1202   if (column < 0 || column >= clist->columns)
1203     return;
1204
1205   clist_row = (g_list_nth (clist->row_list, row))->data;
1206
1207   clist_row->cell[column].vertical = vertical;
1208   clist_row->cell[column].horizontal = horizontal;
1209
1210   if (!GTK_CLIST_FROZEN (clist)
1211       && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
1212     (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row) 
1213       (clist, NULL, row, clist_row);
1214 }
1215
1216 gint
1217 gtk_clist_append (GtkCList * clist,
1218                   gchar * text[])
1219 {
1220   gint i;
1221   GtkCListRow *clist_row;
1222
1223   g_return_val_if_fail (clist != NULL, -1);
1224
1225   clist_row = row_new (clist);
1226   clist->rows++;
1227
1228   /* set the text in the row's columns */
1229   if (text)
1230     for (i = 0; i < clist->columns; i++)
1231       if (text[i])
1232         cell_set_text (clist, clist_row, i, text[i]);
1233
1234   /* keeps track of the end of the list so the list 
1235    * doesn't have to be traversed every time a item is added */
1236   if (!clist->row_list)
1237     {
1238       clist->row_list = g_list_append (clist->row_list, clist_row);
1239       clist->row_list_end = clist->row_list;
1240
1241       /* check the selection mode to see if we should select
1242        * the first row automaticly */
1243       switch (clist->selection_mode)
1244         {
1245         case GTK_SELECTION_BROWSE:
1246           gtk_clist_select_row (clist, 0, -1);
1247           break;
1248
1249         default:
1250           break;
1251         }
1252     }
1253   else
1254     clist->row_list_end = (g_list_append (clist->row_list_end, clist_row))->next;
1255   
1256   /* redraw the list if it's not frozen */
1257   if (!GTK_CLIST_FROZEN (clist))
1258     {
1259       adjust_scrollbars (clist);
1260
1261       if (gtk_clist_row_is_visible (clist, clist->rows - 1) != GTK_VISIBILITY_NONE)
1262         draw_rows (clist, NULL);
1263     }
1264
1265   /* return index of the row */
1266   return clist->rows - 1;
1267 }
1268
1269 void
1270 gtk_clist_insert (GtkCList * clist,
1271                   gint row,
1272                   gchar * text[])
1273 {
1274   gint i;
1275   GtkCListRow *clist_row;
1276
1277   g_return_if_fail (clist != NULL);
1278   g_return_if_fail (text != NULL);
1279
1280   /* return if out of bounds */
1281   if (row < 0 || row > clist->rows)
1282     return;
1283
1284   if (clist->rows == 0)
1285     gtk_clist_append (clist, text);
1286   else
1287     {
1288       /* create the row */
1289       clist_row = row_new (clist);
1290
1291       /* set the text in the row's columns */
1292       if (text)
1293         for (i = 0; i < clist->columns; i++)
1294           if (text[i])
1295             cell_set_text (clist, clist_row, i, text[i]);
1296       
1297       /* reset the row end pointer if we're inserting at the
1298        * end of the list */
1299       if (row == clist->rows)
1300         clist->row_list_end = (g_list_append (clist->row_list_end, clist_row))->next;
1301       else
1302         clist->row_list = g_list_insert (clist->row_list, clist_row, row);
1303
1304       clist->rows++;
1305
1306       /* syncronize the selection list */
1307       sync_selection (clist, row, SYNC_INSERT);
1308     }
1309
1310   /* redraw the list if it isn't frozen */
1311   if (!GTK_CLIST_FROZEN (clist))
1312     {
1313       adjust_scrollbars (clist);
1314
1315       if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
1316         draw_rows (clist, NULL);
1317     }
1318 }
1319
1320 void
1321 gtk_clist_remove (GtkCList * clist,
1322                   gint row)
1323 {
1324   gint was_visible, was_selected;
1325   GList *list;
1326   GtkCListRow *clist_row;
1327
1328   g_return_if_fail (clist != NULL);
1329
1330   /* return if out of bounds */
1331   if (row < 0 || row > (clist->rows - 1))
1332     return;
1333
1334   was_visible = (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE);
1335   was_selected = 0;
1336
1337   /* get the row we're going to delete */
1338   list = g_list_nth (clist->row_list, row);
1339   clist_row = list->data;
1340
1341   /* if we're removing a selected row, we have to make sure
1342    * it's properly unselected, and then sync up the clist->selected
1343    * list to reflect the deincrimented indexies of rows after the
1344    * removal */
1345   if (clist_row->state == GTK_STATE_SELECTED)
1346     {
1347       was_selected = 1;
1348
1349       switch (clist->selection_mode)
1350         {
1351         case GTK_SELECTION_SINGLE:
1352         case GTK_SELECTION_BROWSE:
1353         case GTK_SELECTION_MULTIPLE:
1354           gtk_clist_unselect_row (clist, row, -1);
1355           break;
1356
1357         default:
1358           break;
1359         }
1360     }
1361
1362   /* reset the row end pointer if we're removing at the
1363    * end of the list */
1364   if (row == clist->rows - 1)
1365     clist->row_list_end = list->prev;
1366
1367   clist->row_list = g_list_remove (clist->row_list, clist_row);
1368   clist->rows--;
1369   sync_selection (clist, row, SYNC_REMOVE);
1370
1371   /* preform any selections required by the selection mode */
1372   if (was_selected)
1373     {
1374       switch (clist->selection_mode)
1375         {
1376         case GTK_SELECTION_BROWSE:
1377           if (row == clist->rows)
1378             gtk_clist_select_row (clist, row - 1, -1);
1379           else
1380             gtk_clist_select_row (clist, row, -1);
1381           break;
1382
1383         default:
1384           break;
1385         }
1386     }
1387
1388   /* toast the row */
1389   row_delete (clist, clist_row);
1390
1391   /* redraw the row if it isn't frozen */
1392   if (!GTK_CLIST_FROZEN (clist))
1393     {
1394       adjust_scrollbars (clist);
1395
1396       if (was_visible)
1397         draw_rows (clist, NULL);
1398     }
1399 }
1400
1401 static void
1402 sync_selection (GtkCList * clist,
1403                 gint row,
1404                 gint mode)
1405 {
1406   GList *list;
1407   
1408   list = clist->selection;
1409   while (list)
1410     {
1411       if (GPOINTER_TO_INT (list->data) >= row)
1412         switch (mode)
1413           {
1414           case SYNC_INSERT:
1415             list->data = ((gchar*) list->data) + 1;
1416             break;
1417
1418           case SYNC_REMOVE:
1419             list->data = ((gchar*) list->data) - 1;
1420             break;
1421
1422           default:
1423             break;
1424           }
1425
1426       list = list->next;
1427     }
1428 }
1429
1430 void
1431 gtk_clist_clear (GtkCList * clist)
1432 {
1433   GList *list;
1434
1435   g_return_if_fail (clist != NULL);
1436
1437   /* remove all the rows */
1438   for (list = clist->row_list; list; list = list->next)
1439     {
1440       GtkCListRow *clist_row;
1441
1442       clist_row = list->data;
1443       row_delete (clist, clist_row);
1444     }
1445   g_list_free (clist->row_list);
1446
1447   /* free up the selection list */
1448   g_list_free (clist->selection);
1449
1450   clist->row_list = NULL;
1451   clist->row_list_end = NULL;
1452   clist->selection = NULL;
1453   clist->voffset = 0;
1454   clist->rows = 0;
1455
1456   /* zero-out the scrollbars */
1457   if (clist->vscrollbar)
1458     {
1459       GTK_RANGE (clist->vscrollbar)->adjustment->value = 0.0;
1460       gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), "changed");
1461       
1462       if (!GTK_CLIST_FROZEN (clist))
1463         {
1464           adjust_scrollbars (clist);
1465           draw_rows (clist, NULL);
1466         }
1467     }
1468 }
1469
1470 void 
1471 gtk_clist_swap_rows (GtkCList * clist,
1472                      gint row1, 
1473                      gint row2)
1474 {
1475   gint first, last;
1476   GList *list, *link1, *link2;
1477   gpointer swap;
1478   
1479   g_return_if_fail (clist != NULL);
1480
1481   if (row1 < 0 || row1 > (clist->rows - 1))
1482     return;
1483
1484   if (row2 < 0 || row2 > (clist->rows - 1))
1485     return;
1486
1487   first = MIN (row1, row2);
1488   last = MAX (row1, row2);
1489
1490   link1 = g_list_nth (clist->row_list, first);
1491   link2 = g_list_nth (link1, row2 - row1);
1492
1493   swap = link1->data;
1494   link1->data = link2->data;
1495   link2->data = swap;
1496
1497   list = clist->selection;
1498   while (list)
1499     {
1500       if (GPOINTER_TO_INT (list->data) == row1)
1501         GPOINTER_TO_INT (list->data) = row2;
1502
1503        if (GPOINTER_TO_INT (list->data) == row2)
1504         GPOINTER_TO_INT (list->data) = row1;
1505
1506       list = list->next;
1507     }
1508
1509   if (!GTK_CLIST_FROZEN (clist))
1510     {
1511       if (gtk_clist_row_is_visible (clist, row1) != GTK_VISIBILITY_NONE)
1512         (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
1513           (clist, NULL, row1, (GtkCListRow *) link2->data);
1514
1515       if (gtk_clist_row_is_visible (clist, row2) != GTK_VISIBILITY_NONE)
1516         (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
1517           (clist, NULL, row2, (GtkCListRow *) link1->data);
1518     }
1519 }
1520
1521 void
1522 gtk_clist_set_row_data (GtkCList * clist,
1523                         gint row,
1524                         gpointer data)
1525 {
1526   gtk_clist_set_row_data_full (clist, row, data, NULL);
1527 }
1528
1529 void
1530 gtk_clist_set_row_data_full (GtkCList * clist,
1531                              gint row,
1532                              gpointer data,
1533                              GtkDestroyNotify destroy)
1534 {
1535   GtkCListRow *clist_row;
1536
1537   g_return_if_fail (clist != NULL);
1538
1539   if (row < 0 || row > (clist->rows - 1))
1540     return;
1541
1542   clist_row = (g_list_nth (clist->row_list, row))->data;
1543   clist_row->data = data;
1544   clist_row->destroy = destroy;
1545
1546   /* re-send the selected signal if data is changed/added
1547    * so the application can respond to the new data -- 
1548    * this could be questionable behavior */
1549   if (clist_row->state == GTK_STATE_SELECTED)
1550     gtk_clist_select_row (clist, row, -1);
1551 }
1552
1553 gpointer
1554 gtk_clist_get_row_data (GtkCList * clist,
1555                         gint row)
1556 {
1557   GtkCListRow *clist_row;
1558
1559   g_return_val_if_fail (clist != NULL, NULL);
1560
1561   if (row < 0 || row > (clist->rows - 1))
1562     return NULL;
1563
1564   clist_row = (g_list_nth (clist->row_list, row))->data;
1565   return clist_row->data;
1566 }
1567
1568 gint
1569 gtk_clist_find_row_from_data (GtkCList * clist,
1570                               gpointer data)
1571 {
1572   GList *list;
1573   gint n;
1574
1575   g_return_val_if_fail (clist != NULL, -1);
1576   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
1577
1578   if (clist->rows < 1)
1579     return -1; /* is this an optimization or just worthless? */
1580
1581   n = 0;
1582   list = clist->row_list;
1583   while (list)
1584     {
1585       GtkCListRow *clist_row;
1586
1587       clist_row = list->data;
1588       if (clist_row->data == data)
1589         break;
1590       n++;
1591       list = list->next;
1592     }
1593
1594   if (list)
1595     return n;
1596
1597   return -1;
1598 }
1599
1600 void
1601 gtk_clist_select_row (GtkCList * clist,
1602                       gint row,
1603                       gint column)
1604 {
1605   g_return_if_fail (clist != NULL);
1606
1607   if (row < 0 || row >= clist->rows)
1608     return;
1609
1610   if (column < -1 || column >= clist->columns)
1611     return;
1612
1613   select_row (clist, row, column, NULL);
1614 }
1615
1616 void
1617 gtk_clist_unselect_row (GtkCList * clist,
1618                         gint row,
1619                         gint column)
1620 {
1621   g_return_if_fail (clist != NULL);
1622
1623   if (row < 0 || row >= clist->rows)
1624     return;
1625
1626   if (column < -1 || column >= clist->columns)
1627     return;
1628
1629   unselect_row (clist, row, column, NULL);
1630 }
1631
1632 GtkVisibility
1633 gtk_clist_row_is_visible (GtkCList * clist,
1634                           gint row)
1635 {
1636   gint top;
1637
1638   g_return_val_if_fail (clist != NULL, 0);
1639
1640   if (row < 0 || row >= clist->rows)
1641     return GTK_VISIBILITY_NONE;
1642
1643   if (clist->row_height == 0)
1644     return GTK_VISIBILITY_NONE;
1645
1646   if (row < ROW_FROM_YPIXEL (clist, 0))
1647     return GTK_VISIBILITY_NONE;
1648
1649   if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height))
1650     return GTK_VISIBILITY_NONE;
1651
1652   top = ROW_TOP_YPIXEL (clist, row);
1653
1654   if ((top < 0)
1655       || ((top + clist->row_height) >= clist->clist_window_height))
1656     return GTK_VISIBILITY_PARTIAL;
1657
1658   return GTK_VISIBILITY_FULL;
1659 }
1660
1661 GtkAdjustment*
1662 gtk_clist_get_vadjustment (GtkCList * clist)
1663 {
1664   g_return_val_if_fail (clist != NULL, NULL);
1665   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1666
1667   return gtk_range_get_adjustment (GTK_RANGE (clist->vscrollbar));
1668 }
1669
1670 GtkAdjustment*
1671 gtk_clist_get_hadjustment (GtkCList * clist)
1672 {
1673   g_return_val_if_fail (clist != NULL, NULL);
1674   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1675
1676   return gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar));
1677 }
1678
1679 void
1680 gtk_clist_set_policy (GtkCList * clist,
1681                       GtkPolicyType vscrollbar_policy,
1682                       GtkPolicyType hscrollbar_policy)
1683 {
1684   g_return_if_fail (clist != NULL);
1685   g_return_if_fail (GTK_IS_CLIST (clist));
1686
1687   if (clist->vscrollbar_policy != vscrollbar_policy)
1688     {
1689       clist->vscrollbar_policy = vscrollbar_policy;
1690
1691       if (GTK_WIDGET (clist)->parent)
1692         gtk_widget_queue_resize (GTK_WIDGET (clist));
1693     }
1694
1695   if (clist->hscrollbar_policy != hscrollbar_policy)
1696     {
1697       clist->hscrollbar_policy = hscrollbar_policy;
1698
1699       if (GTK_WIDGET (clist)->parent)
1700         gtk_widget_queue_resize (GTK_WIDGET (clist));
1701     }
1702 }
1703
1704 /*
1705  * GTKOBJECT
1706  *   gtk_clist_destroy
1707  *   gtk_clist_finalize
1708  */
1709 static void
1710 gtk_clist_destroy (GtkObject * object)
1711 {
1712   gint i;
1713   GtkCList *clist;
1714
1715   g_return_if_fail (object != NULL);
1716   g_return_if_fail (GTK_IS_CLIST (object));
1717
1718   clist = GTK_CLIST (object);
1719
1720   /* freeze the list */
1721   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
1722
1723   /* get rid of all the rows */
1724   gtk_clist_clear (clist);
1725
1726   /* Since we don't have a _remove method, unparent the children
1727    * instead of destroying them so the focus will be unset properly.
1728    * (For other containers, the _remove method takes care of the
1729    * unparent) The destroy will happen when the refcount drops
1730    * to zero.
1731    */
1732
1733   /* destroy the scrollbars */
1734   if (clist->vscrollbar)
1735     {
1736       gtk_widget_unparent (clist->vscrollbar);
1737       clist->vscrollbar = NULL;
1738     }
1739   if (clist->hscrollbar)
1740     {
1741       gtk_widget_unparent (clist->hscrollbar);
1742       clist->hscrollbar = NULL;
1743     }
1744
1745   /* destroy the column buttons */
1746   for (i = 0; i < clist->columns; i++)
1747     if (clist->column[i].button)
1748       {
1749         gtk_widget_unparent (clist->column[i].button);
1750         clist->column[i].button = NULL;
1751       }
1752
1753   if (GTK_OBJECT_CLASS (parent_class)->destroy)
1754     (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
1755 }
1756
1757 static void
1758 gtk_clist_finalize (GtkObject * object)
1759 {
1760   GtkCList *clist;
1761
1762   g_return_if_fail (object != NULL);
1763   g_return_if_fail (GTK_IS_CLIST (object));
1764
1765   clist = GTK_CLIST (object);
1766
1767   columns_delete (clist);
1768
1769   g_mem_chunk_destroy (clist->cell_mem_chunk);
1770   g_mem_chunk_destroy (clist->row_mem_chunk);
1771
1772   if (GTK_OBJECT_CLASS (parent_class)->finalize)
1773     (*GTK_OBJECT_CLASS (parent_class)->finalize) (object);
1774 }
1775
1776 /*
1777  * GTKWIDGET
1778  *   gtk_clist_realize
1779  *   gtk_clist_unrealize
1780  *   gtk_clist_map
1781  *   gtk_clist_unmap
1782  *   gtk_clist_draw
1783  *   gtk_clist_expose
1784  *   gtk_clist_button_press
1785  *   gtk_clist_button_release
1786  *   gtk_clist_button_motion
1787  *   gtk_clist_size_request
1788  *   gtk_clist_size_allocate
1789  */
1790 static void
1791 gtk_clist_realize (GtkWidget * widget)
1792 {
1793   gint i;
1794   GtkCList *clist;
1795   GdkWindowAttr attributes;
1796   gint attributes_mask;
1797   GdkGCValues values;
1798   gint border_width;
1799
1800   g_return_if_fail (widget != NULL);
1801   g_return_if_fail (GTK_IS_CLIST (widget));
1802
1803   clist = GTK_CLIST (widget);
1804
1805   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1806
1807   add_style_data (clist);
1808
1809   border_width = GTK_CONTAINER (widget)->border_width;
1810   
1811   attributes.window_type = GDK_WINDOW_CHILD;
1812   attributes.x = widget->allocation.x + border_width;
1813   attributes.y = widget->allocation.y + border_width;
1814   attributes.width = widget->allocation.width - border_width * 2;
1815   attributes.height = widget->allocation.height - border_width * 2;
1816   attributes.wclass = GDK_INPUT_OUTPUT;
1817   attributes.visual = gtk_widget_get_visual (widget);
1818   attributes.colormap = gtk_widget_get_colormap (widget);
1819   attributes.event_mask = gtk_widget_get_events (widget);
1820   attributes.event_mask |= (GDK_EXPOSURE_MASK |
1821                             GDK_BUTTON_PRESS_MASK |
1822                             GDK_BUTTON_RELEASE_MASK |
1823                             GDK_KEY_PRESS_MASK);
1824   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1825
1826
1827   /* main window */
1828   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1829   gdk_window_set_user_data (widget->window, clist);
1830
1831   widget->style = gtk_style_attach (widget->style, widget->window);
1832
1833   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1834
1835   /* column-title window */
1836
1837   attributes.x = clist->column_title_area.x;
1838   attributes.y = clist->column_title_area.y;
1839   attributes.width = clist->column_title_area.width;
1840   attributes.height = clist->column_title_area.height;
1841   
1842   clist->title_window = gdk_window_new (widget->window, &attributes, attributes_mask);
1843   gdk_window_set_user_data (clist->title_window, clist);
1844
1845   gtk_style_set_background (widget->style, clist->title_window, GTK_STATE_SELECTED);
1846   gdk_window_show (clist->title_window);
1847
1848   /* set things up so column buttons are drawn in title window */
1849   for (i = 0; i < clist->columns; i++)
1850     if (clist->column[i].button)
1851       gtk_widget_set_parent_window (clist->column[i].button, clist->title_window);
1852
1853   /* clist-window */
1854   attributes.x = clist->internal_allocation.x + widget->style->klass->xthickness;
1855   attributes.y = clist->internal_allocation.y + widget->style->klass->ythickness +
1856     clist->column_title_area.height;
1857   attributes.width = clist->clist_window_width;
1858   attributes.height = clist->clist_window_height;
1859   
1860   clist->clist_window = gdk_window_new (widget->window, &attributes, attributes_mask);
1861   gdk_window_set_user_data (clist->clist_window, clist);
1862
1863   gdk_window_set_background (clist->clist_window, &widget->style->bg[GTK_STATE_PRELIGHT]);
1864   gdk_window_show (clist->clist_window);
1865   gdk_window_get_size (clist->clist_window, &clist->clist_window_width,
1866                        &clist->clist_window_height);
1867
1868   /* create resize windows */
1869   attributes.wclass = GDK_INPUT_ONLY;
1870   attributes.event_mask = (GDK_BUTTON_PRESS_MASK |
1871                            GDK_BUTTON_RELEASE_MASK |
1872                            GDK_POINTER_MOTION_MASK |
1873                            GDK_POINTER_MOTION_HINT_MASK);
1874   attributes.cursor = clist->cursor_drag = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
1875   attributes_mask = GDK_WA_CURSOR;
1876   
1877   for (i = 0; i < clist->columns; i++)
1878     {
1879       clist->column[i].window = gdk_window_new (clist->title_window, &attributes, attributes_mask);
1880       gdk_window_set_user_data (clist->column[i].window, clist);
1881     }
1882
1883   /* This is slightly less efficient than creating them with the
1884    * right size to begin with, but easier
1885    */
1886   size_allocate_title_buttons (clist);
1887
1888   /* GCs */
1889   clist->fg_gc = gdk_gc_new (widget->window);
1890   clist->bg_gc = gdk_gc_new (widget->window);
1891   
1892   /* We'll use this gc to do scrolling as well */
1893   gdk_gc_set_exposures (clist->fg_gc, TRUE);
1894
1895   values.foreground = widget->style->white;
1896   values.function = GDK_XOR;
1897   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1898   clist->xor_gc = gdk_gc_new_with_values (widget->window,
1899                                           &values,
1900                                           GDK_GC_FOREGROUND |
1901                                           GDK_GC_FUNCTION |
1902                                           GDK_GC_SUBWINDOW);
1903 }
1904
1905 static void
1906 gtk_clist_unrealize (GtkWidget * widget)
1907 {
1908   gint i;
1909   GtkCList *clist;
1910
1911   g_return_if_fail (widget != NULL);
1912   g_return_if_fail (GTK_IS_CLIST (widget));
1913
1914   clist = GTK_CLIST (widget);
1915
1916   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
1917
1918   gdk_cursor_destroy (clist->cursor_drag);
1919   gdk_gc_destroy (clist->xor_gc);
1920   gdk_gc_destroy (clist->fg_gc);
1921   gdk_gc_destroy (clist->bg_gc);
1922
1923   for (i = 0; i < clist->columns; i++)
1924     if (clist->column[i].window)
1925       {
1926         gdk_window_set_user_data (clist->column[i].window, NULL);
1927         gdk_window_destroy (clist->column[i].window);
1928         clist->column[i].window = NULL;
1929       }
1930
1931   gdk_window_set_user_data (clist->clist_window, NULL);
1932   gdk_window_destroy (clist->clist_window);
1933   clist->clist_window = NULL;
1934
1935   gdk_window_set_user_data (clist->title_window, NULL);
1936   gdk_window_destroy (clist->title_window);
1937   clist->title_window = NULL;
1938
1939   clist->cursor_drag = NULL;
1940   clist->xor_gc = NULL;
1941   clist->fg_gc = NULL;
1942   clist->bg_gc = NULL;
1943
1944   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1945     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1946 }
1947
1948 static void
1949 gtk_clist_map (GtkWidget * widget)
1950 {
1951   gint i;
1952   GtkCList *clist;
1953
1954   g_return_if_fail (widget != NULL);
1955   g_return_if_fail (GTK_IS_CLIST (widget));
1956
1957   clist = GTK_CLIST (widget);
1958
1959   if (!GTK_WIDGET_MAPPED (widget))
1960     {
1961       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
1962
1963       gdk_window_show (widget->window);
1964       gdk_window_show (clist->title_window);
1965       gdk_window_show (clist->clist_window);
1966
1967       /* map column buttons */
1968       for (i = 0; i < clist->columns; i++)
1969         if (clist->column[i].button &&
1970             GTK_WIDGET_VISIBLE (clist->column[i].button) &&
1971             !GTK_WIDGET_MAPPED (clist->column[i].button))
1972           gtk_widget_map (clist->column[i].button);
1973       
1974       /* map resize windows AFTER column buttons (above) */
1975       for (i = 0; i < clist->columns; i++)
1976         if (clist->column[i].window && clist->column[i].button)
1977           gdk_window_show (clist->column[i].window);
1978        
1979       /* map vscrollbars */
1980       if (GTK_WIDGET_VISIBLE (clist->vscrollbar) &&
1981           !GTK_WIDGET_MAPPED (clist->vscrollbar))
1982         gtk_widget_map (clist->vscrollbar);
1983
1984       if (GTK_WIDGET_VISIBLE (clist->hscrollbar) &&
1985           !GTK_WIDGET_MAPPED (clist->hscrollbar))
1986         gtk_widget_map (clist->hscrollbar);
1987
1988       /* unfreeze the list */
1989       GTK_CLIST_UNSET_FLAG (clist, CLIST_FROZEN);
1990     }
1991 }
1992
1993 static void
1994 gtk_clist_unmap (GtkWidget * widget)
1995 {
1996   gint i;
1997   GtkCList *clist;
1998
1999   g_return_if_fail (widget != NULL);
2000   g_return_if_fail (GTK_IS_CLIST (widget));
2001
2002   clist = GTK_CLIST (widget);
2003
2004   if (GTK_WIDGET_MAPPED (widget))
2005     {
2006       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2007
2008       for (i = 0; i < clist->columns; i++)
2009         if (clist->column[i].window)
2010           gdk_window_hide (clist->column[i].window);
2011
2012       gdk_window_hide (clist->clist_window);
2013       gdk_window_hide (clist->title_window);
2014       gdk_window_hide (widget->window);
2015
2016       /* unmap scrollbars */
2017       if (GTK_WIDGET_MAPPED (clist->vscrollbar))
2018         gtk_widget_unmap (clist->vscrollbar);
2019
2020       if (GTK_WIDGET_MAPPED (clist->hscrollbar))
2021         gtk_widget_unmap (clist->hscrollbar);
2022
2023       /* unmap column buttons */
2024       for (i = 0; i < clist->columns; i++)
2025         if (clist->column[i].button &&
2026             GTK_WIDGET_MAPPED (clist->column[i].button))
2027           gtk_widget_unmap (clist->column[i].button);
2028
2029       /* freeze the list */
2030       GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
2031     }
2032 }
2033
2034 static void
2035 gtk_clist_draw (GtkWidget * widget,
2036                 GdkRectangle * area)
2037 {
2038   GtkCList *clist;
2039   gint border_width;
2040
2041   g_return_if_fail (widget != NULL);
2042   g_return_if_fail (GTK_IS_CLIST (widget));
2043   g_return_if_fail (area != NULL);
2044
2045   if (GTK_WIDGET_DRAWABLE (widget))
2046     {
2047       clist = GTK_CLIST (widget);
2048       border_width = GTK_CONTAINER (widget)->border_width;
2049
2050       gdk_window_clear_area (widget->window,
2051                              area->x - border_width, 
2052                              area->y - border_width,
2053                              area->width, area->height);
2054
2055       /* draw list shadow/border */
2056       gtk_draw_shadow (widget->style, widget->window,
2057                        GTK_STATE_NORMAL, clist->shadow_type,
2058                        0, 0, 
2059                        clist->clist_window_width + (2 * widget->style->klass->xthickness),
2060                        clist->clist_window_height + (2 * widget->style->klass->ythickness) +
2061                        clist->column_title_area.height);
2062
2063       gdk_window_clear_area (clist->clist_window,
2064                              0, 0, -1, -1);
2065
2066       draw_rows (clist, NULL);
2067     }
2068 }
2069
2070 static gint
2071 gtk_clist_expose (GtkWidget * widget,
2072                   GdkEventExpose * event)
2073 {
2074   GtkCList *clist;
2075
2076   g_return_val_if_fail (widget != NULL, FALSE);
2077   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2078   g_return_val_if_fail (event != NULL, FALSE);
2079
2080   if (GTK_WIDGET_DRAWABLE (widget))
2081     {
2082       clist = GTK_CLIST (widget);
2083
2084       /* draw border */
2085       if (event->window == widget->window)
2086         gtk_draw_shadow (widget->style, widget->window,
2087                          GTK_STATE_NORMAL, clist->shadow_type,
2088                          0, 0,
2089                          clist->clist_window_width + (2 * widget->style->klass->xthickness),
2090                          clist->clist_window_height + (2 * widget->style->klass->ythickness) +
2091                          clist->column_title_area.height);
2092
2093       /* exposure events on the list */
2094       if (event->window == clist->clist_window)
2095         draw_rows (clist, &event->area);
2096     }
2097
2098   return FALSE;
2099 }
2100
2101 static gint
2102 gtk_clist_button_press (GtkWidget * widget,
2103                         GdkEventButton * event)
2104 {
2105   gint i;
2106   GtkCList *clist;
2107   gint x, y, row, column;
2108
2109   g_return_val_if_fail (widget != NULL, FALSE);
2110   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2111   g_return_val_if_fail (event != NULL, FALSE);
2112
2113   clist = GTK_CLIST (widget);
2114
2115   /* selections on the list */
2116   if (event->window == clist->clist_window)
2117     {
2118       x = event->x;
2119       y = event->y;
2120
2121       if (get_selection_info (clist, x, y, &row, &column))
2122         toggle_row (clist, row, column, event);
2123
2124       return FALSE;
2125     }
2126
2127   /* press on resize windows */
2128   for (i = 0; i < clist->columns; i++)
2129     if (clist->column[i].window && event->window == clist->column[i].window)
2130       {
2131         GTK_CLIST_SET_FLAG (clist, CLIST_IN_DRAG);
2132         gtk_widget_get_pointer (widget, &clist->x_drag, NULL);
2133
2134         gdk_pointer_grab (clist->column[i].window, FALSE,
2135                           GDK_POINTER_MOTION_HINT_MASK |
2136                           GDK_BUTTON1_MOTION_MASK |
2137                           GDK_BUTTON_RELEASE_MASK,
2138                           NULL, NULL, event->time);
2139
2140         draw_xor_line (clist);
2141         return FALSE;
2142       }
2143
2144   return FALSE;
2145 }
2146
2147 static gint
2148 gtk_clist_button_release (GtkWidget * widget,
2149                           GdkEventButton * event)
2150 {
2151   gint i, x, width, visible;
2152   GtkCList *clist;
2153
2154   g_return_val_if_fail (widget != NULL, FALSE);
2155   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2156   g_return_val_if_fail (event != NULL, FALSE);
2157
2158   clist = GTK_CLIST (widget);
2159
2160   /* release on resize windows */
2161   if (GTK_CLIST_IN_DRAG (clist))
2162     for (i = 0; i < clist->columns; i++)
2163       if (clist->column[i].window && event->window == clist->column[i].window)
2164         {
2165           GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
2166           gtk_widget_get_pointer (widget, &x, NULL);
2167           width = new_column_width (clist, i, &x, &visible);
2168           gdk_pointer_ungrab (event->time);
2169           
2170           if (visible)
2171             draw_xor_line (clist);
2172
2173           resize_column (clist, i, width);
2174           return FALSE;
2175         }
2176
2177   return FALSE;
2178 }
2179
2180 static gint
2181 gtk_clist_motion (GtkWidget * widget,
2182                   GdkEventMotion * event)
2183 {
2184   gint i, x, visible;
2185   GtkCList *clist;
2186
2187   g_return_val_if_fail (widget != NULL, FALSE);
2188   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2189
2190   clist = GTK_CLIST (widget);
2191
2192   if (GTK_CLIST_IN_DRAG (clist))
2193     for (i = 0; i < clist->columns; i++)
2194       if (clist->column[i].window && event->window == clist->column[i].window)
2195         {
2196           if (event->is_hint || event->window != widget->window)
2197             gtk_widget_get_pointer (widget, &x, NULL);
2198           else
2199             x = event->x;
2200
2201           new_column_width (clist, i, &x, &visible);
2202           /* Welcome to my hack!  I'm going to use a value of x_drage = -99999 to
2203            * indicate the the xor line is already no visible */
2204           if (!visible && clist->x_drag != -99999)
2205             {
2206               draw_xor_line (clist);
2207               clist->x_drag = -99999;
2208             }
2209
2210           if (x != clist->x_drag && visible)
2211             {
2212               if (clist->x_drag != -99999)
2213                 draw_xor_line (clist);
2214
2215               clist->x_drag = x;
2216               draw_xor_line (clist);
2217             }
2218         }
2219
2220   return TRUE;
2221 }
2222
2223 static void
2224 gtk_clist_size_request (GtkWidget * widget,
2225                         GtkRequisition * requisition)
2226 {
2227   gint i;
2228   GtkCList *clist;
2229
2230   g_return_if_fail (widget != NULL);
2231   g_return_if_fail (GTK_IS_CLIST (widget));
2232   g_return_if_fail (requisition != NULL);
2233
2234   clist = GTK_CLIST (widget);
2235
2236   add_style_data (clist);
2237
2238   requisition->width = 0;
2239   requisition->height = 0;
2240
2241   /* compute the size of the column title (title) area */
2242   clist->column_title_area.height = 0;
2243   if (GTK_CLIST_SHOW_TITLES (clist))
2244     for (i = 0; i < clist->columns; i++)
2245       if (clist->column[i].button)
2246         {
2247           gtk_widget_size_request (clist->column[i].button, &clist->column[i].button->requisition);
2248           clist->column_title_area.height = MAX (clist->column_title_area.height,
2249                                                  clist->column[i].button->requisition.height);
2250         }
2251   requisition->height += clist->column_title_area.height;
2252
2253   /* add the vscrollbar space */
2254   if ((clist->vscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
2255       GTK_WIDGET_VISIBLE (clist->vscrollbar))
2256     {
2257       gtk_widget_size_request (clist->vscrollbar, &clist->vscrollbar->requisition);
2258
2259       requisition->width += clist->vscrollbar->requisition.width + SCROLLBAR_SPACING (clist);
2260       requisition->height = MAX (requisition->height,
2261                                  clist->vscrollbar->requisition.height);
2262     }
2263
2264   /* add the hscrollbar space */
2265   if ((clist->hscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
2266       GTK_WIDGET_VISIBLE (clist->hscrollbar))
2267     {
2268       gtk_widget_size_request (clist->hscrollbar, &clist->hscrollbar->requisition);
2269
2270       requisition->height += clist->hscrollbar->requisition.height + SCROLLBAR_SPACING (clist);
2271       requisition->width = MAX (clist->hscrollbar->requisition.width, 
2272                                 requisition->width - 
2273                                 clist->vscrollbar->requisition.width);
2274
2275     }
2276
2277   requisition->width += widget->style->klass->xthickness * 2 +
2278     GTK_CONTAINER (widget)->border_width * 2;
2279   requisition->height += widget->style->klass->ythickness * 2 +
2280     GTK_CONTAINER (widget)->border_width * 2;
2281 }
2282
2283 static void
2284 gtk_clist_size_allocate (GtkWidget * widget,
2285                          GtkAllocation * allocation)
2286 {
2287   GtkCList *clist;
2288   GtkAllocation clist_allocation;
2289   GtkAllocation child_allocation;
2290   gint i, vscrollbar_vis, hscrollbar_vis;
2291
2292   g_return_if_fail (widget != NULL);
2293   g_return_if_fail (GTK_IS_CLIST (widget));
2294   g_return_if_fail (allocation != NULL);
2295
2296   clist = GTK_CLIST (widget);
2297   widget->allocation = *allocation;
2298
2299   if (GTK_WIDGET_REALIZED (widget))
2300     {
2301       gdk_window_move_resize (widget->window,
2302                               allocation->x + GTK_CONTAINER (widget)->border_width,
2303                               allocation->y + GTK_CONTAINER (widget)->border_width,
2304                               allocation->width - GTK_CONTAINER (widget)->border_width * 2,
2305                               allocation->height - GTK_CONTAINER (widget)->border_width * 2);
2306     }
2307
2308   /* use internal allocation structure for all the math
2309    * because it's easier than always subtracting the container
2310    * border width */
2311   clist->internal_allocation.x = 0;
2312   clist->internal_allocation.y = 0;
2313   clist->internal_allocation.width = MAX (1, allocation->width -
2314     GTK_CONTAINER (widget)->border_width * 2);
2315   clist->internal_allocation.height = MAX (1, allocation->height -
2316     GTK_CONTAINER (widget)->border_width * 2);
2317         
2318   /* allocate clist window assuming no scrollbars */
2319   clist_allocation.x = clist->internal_allocation.x + widget->style->klass->xthickness;
2320   clist_allocation.y = clist->internal_allocation.y + widget->style->klass->ythickness +
2321     clist->column_title_area.height;
2322   clist_allocation.width = MAX (1, clist->internal_allocation.width - 
2323     (2 * widget->style->klass->xthickness));
2324   clist_allocation.height = MAX (1, clist->internal_allocation.height -
2325     (2 * widget->style->klass->ythickness) -
2326     clist->column_title_area.height);
2327   
2328   /* 
2329    * here's where we decide to show/not show the scrollbars
2330    */
2331   vscrollbar_vis = 0;
2332   hscrollbar_vis = 0;
2333   
2334   for (i = 0; i <= 1; i++)
2335     {
2336       if (LIST_HEIGHT (clist) <= clist_allocation.height &&
2337           clist->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
2338         {
2339           vscrollbar_vis = 0;
2340         }
2341       else
2342         {
2343           if (!vscrollbar_vis)
2344             {
2345               vscrollbar_vis = 1;
2346               clist_allocation.width = MAX (1, clist_allocation.width - 
2347                 (clist->vscrollbar->requisition.width +
2348                  SCROLLBAR_SPACING (clist)));
2349             }  
2350         }
2351       
2352       if (LIST_WIDTH (clist) <= clist_allocation.width &&
2353           clist->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2354         {
2355           hscrollbar_vis = 0;
2356         }
2357       else
2358         {
2359           if (!hscrollbar_vis)
2360             {
2361               hscrollbar_vis = 1;
2362               clist_allocation.height = MAX (1, clist_allocation.height - 
2363                 (clist->hscrollbar->requisition.height +
2364                  SCROLLBAR_SPACING (clist)));
2365             }  
2366         }
2367     }
2368   
2369   clist->clist_window_width = clist_allocation.width;
2370   clist->clist_window_height = clist_allocation.height;
2371   
2372   if (GTK_WIDGET_REALIZED (widget))
2373     {
2374       gdk_window_move_resize (clist->clist_window,
2375                               clist_allocation.x,
2376                               clist_allocation.y,
2377                               clist_allocation.width,
2378                               clist_allocation.height);
2379     }
2380   
2381   /* position the window which holds the column title buttons */
2382   clist->column_title_area.x = widget->style->klass->xthickness;
2383   clist->column_title_area.y = widget->style->klass->ythickness;
2384   clist->column_title_area.width = clist_allocation.width;
2385   
2386   if (GTK_WIDGET_REALIZED (widget))
2387     {
2388       gdk_window_move_resize (clist->title_window,
2389                               clist->column_title_area.x,
2390                               clist->column_title_area.y,
2391                               clist->column_title_area.width,
2392                               clist->column_title_area.height);
2393     }
2394   
2395   /* column button allocation */
2396   size_allocate_columns (clist);
2397
2398   if (GTK_WIDGET_REALIZED (widget))
2399     size_allocate_title_buttons (clist);
2400
2401   adjust_scrollbars (clist);
2402   
2403   /* allocate the vscrollbar */
2404   if (vscrollbar_vis)
2405     {
2406       if (!GTK_WIDGET_VISIBLE (clist->vscrollbar))
2407         gtk_widget_show (clist->vscrollbar);
2408       
2409       child_allocation.x = clist->internal_allocation.x + 
2410         clist->internal_allocation.width -
2411         clist->vscrollbar->requisition.width;
2412       child_allocation.y = clist->internal_allocation.y;
2413       child_allocation.width = clist->vscrollbar->requisition.width;
2414       child_allocation.height = MAX (1, clist->internal_allocation.height -
2415         (hscrollbar_vis ? (clist->hscrollbar->requisition.height + SCROLLBAR_SPACING (clist)) : 0));
2416       
2417       gtk_widget_size_allocate (clist->vscrollbar, &child_allocation);
2418     }
2419   else
2420     {
2421       if (GTK_WIDGET_VISIBLE (clist->vscrollbar))
2422         gtk_widget_hide (clist->vscrollbar);
2423     }
2424   
2425   if (hscrollbar_vis)
2426     {
2427       if (!GTK_WIDGET_VISIBLE (clist->hscrollbar))
2428         gtk_widget_show (clist->hscrollbar);
2429       
2430       child_allocation.x = clist->internal_allocation.x;
2431       child_allocation.y = clist->internal_allocation.y +
2432         clist->internal_allocation.height -
2433         clist->hscrollbar->requisition.height;
2434       child_allocation.width = MAX (1, clist->internal_allocation.width -
2435         (vscrollbar_vis ? (clist->vscrollbar->requisition.width + SCROLLBAR_SPACING (clist)) : 0));
2436       child_allocation.height = clist->hscrollbar->requisition.height;
2437       
2438       gtk_widget_size_allocate (clist->hscrollbar, &child_allocation);
2439     }
2440   else
2441     {
2442       if (GTK_WIDGET_VISIBLE (clist->hscrollbar))
2443         gtk_widget_hide (clist->hscrollbar);
2444     }
2445
2446   /* set the vscrollbar adjustments */
2447   adjust_scrollbars (clist);
2448 }
2449
2450 /* 
2451  * GTKCONTAINER
2452  *   gtk_clist_foreach
2453  */
2454 static void
2455 gtk_clist_foreach (GtkContainer * container,
2456                    GtkCallback callback,
2457                    gpointer callback_data)
2458 {
2459   gint i;
2460   GtkCList *clist;
2461
2462   g_return_if_fail (container != NULL);
2463   g_return_if_fail (GTK_IS_CLIST (container));
2464   g_return_if_fail (callback != NULL);
2465
2466   clist = GTK_CLIST (container);
2467
2468   /* callback for the column buttons */
2469   for (i = 0; i < clist->columns; i++)
2470     if (clist->column[i].button)
2471       (*callback) (clist->column[i].button, callback_data);
2472
2473   /* callbacks for the scrollbars */
2474   if (clist->vscrollbar)
2475     (*callback) (clist->vscrollbar, callback_data);
2476   if (clist->hscrollbar)
2477     (*callback) (clist->hscrollbar, callback_data);
2478 }
2479
2480 /*
2481  * DRAWING
2482  *   draw_row
2483  *   draw_rows
2484  */
2485 static void
2486 draw_row (GtkCList * clist,
2487           GdkRectangle * area,
2488           gint row,
2489           GtkCListRow * clist_row)
2490 {
2491   GtkWidget *widget;
2492   GdkGC *fg_gc, *bg_gc;
2493   GdkRectangle row_rectangle, cell_rectangle, clip_rectangle, intersect_rectangle,
2494    *rect;
2495   gint i, offset = 0, width, height, pixmap_width = 0;
2496   gint xsrc, ysrc, xdest, ydest;
2497
2498   g_return_if_fail (clist != NULL);
2499
2500   /* bail now if we arn't drawable yet */
2501   if (!GTK_WIDGET_DRAWABLE (clist))
2502     return;
2503
2504   if (row < 0 || row >= clist->rows)
2505     return;
2506
2507   widget = GTK_WIDGET (clist);
2508
2509   /* if the function is passed the pointer to the row instead of null,
2510    * it avoids this expensive lookup */
2511   if (!clist_row)
2512     clist_row = (g_list_nth (clist->row_list, row))->data;
2513
2514   /* rectangle of the entire row */
2515   row_rectangle.x = 0;
2516   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
2517   row_rectangle.width = clist->clist_window_width;
2518   row_rectangle.height = clist->row_height;
2519
2520   /* rectangle of the cell spacing above the row */
2521   cell_rectangle.x = 0;
2522   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
2523   cell_rectangle.width = row_rectangle.width;
2524   cell_rectangle.height = CELL_SPACING;
2525
2526   /* rectangle used to clip drawing operations, it's y and height
2527    * positions only need to be set once, so we set them once here. 
2528    * the x and width are set withing the drawing loop below once per
2529    * column */
2530   clip_rectangle.y = row_rectangle.y;
2531   clip_rectangle.height = row_rectangle.height;
2532
2533   /* select GC for background rectangle */
2534   if (clist_row->state == GTK_STATE_SELECTED)
2535     {
2536       fg_gc = widget->style->fg_gc[GTK_STATE_SELECTED];
2537       bg_gc = widget->style->bg_gc[GTK_STATE_SELECTED];
2538     }
2539   else
2540     {
2541       if (clist_row->fg_set)
2542         {
2543           gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
2544           fg_gc = clist->fg_gc;
2545         }
2546       else
2547         fg_gc = widget->style->fg_gc[GTK_STATE_NORMAL];
2548         
2549       if (clist_row->bg_set)
2550         {
2551           gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
2552           bg_gc = clist->bg_gc;
2553         }
2554       else
2555         bg_gc = widget->style->bg_gc[GTK_STATE_PRELIGHT];
2556     }
2557
2558   /* draw the cell borders and background */
2559   if (area)
2560     {
2561       if (gdk_rectangle_intersect (area, &cell_rectangle, &intersect_rectangle))
2562         gdk_draw_rectangle (clist->clist_window,
2563                             widget->style->base_gc[GTK_STATE_NORMAL],
2564                             TRUE,
2565                             intersect_rectangle.x,
2566                             intersect_rectangle.y,
2567                             intersect_rectangle.width,
2568                             intersect_rectangle.height);
2569
2570       /* the last row has to clear it's bottom cell spacing too */
2571       if (clist_row == clist->row_list_end->data)
2572         {
2573           cell_rectangle.y += clist->row_height + CELL_SPACING;
2574
2575           if (gdk_rectangle_intersect (area, &cell_rectangle, &intersect_rectangle))
2576             gdk_draw_rectangle (clist->clist_window,
2577                                 widget->style->base_gc[GTK_STATE_NORMAL],
2578                                 TRUE,
2579                                 intersect_rectangle.x,
2580                                 intersect_rectangle.y,
2581                                 intersect_rectangle.width,
2582                                 intersect_rectangle.height);
2583         }
2584
2585       if (!gdk_rectangle_intersect (area, &row_rectangle, &intersect_rectangle))
2586         return;
2587
2588       if (clist_row->state == GTK_STATE_SELECTED || clist_row->bg_set)
2589         gdk_draw_rectangle (clist->clist_window,
2590                             bg_gc,
2591                             TRUE,
2592                             intersect_rectangle.x,
2593                             intersect_rectangle.y,
2594                             intersect_rectangle.width,
2595                             intersect_rectangle.height);
2596       else
2597         gdk_window_clear_area (clist->clist_window,
2598                                intersect_rectangle.x,
2599                                intersect_rectangle.y,
2600                                intersect_rectangle.width,
2601                                intersect_rectangle.height);
2602     }
2603   else
2604     {
2605       gdk_draw_rectangle (clist->clist_window,
2606                           widget->style->base_gc[GTK_STATE_NORMAL],
2607                           TRUE,
2608                           cell_rectangle.x,
2609                           cell_rectangle.y,
2610                           cell_rectangle.width,
2611                           cell_rectangle.height);
2612
2613       /* the last row has to clear it's bottom cell spacing too */
2614       if (clist_row == clist->row_list_end->data)
2615         {
2616           cell_rectangle.y += clist->row_height + CELL_SPACING;
2617
2618           gdk_draw_rectangle (clist->clist_window,
2619                               widget->style->base_gc[GTK_STATE_NORMAL],
2620                               TRUE,
2621                               cell_rectangle.x,
2622                               cell_rectangle.y,
2623                               cell_rectangle.width,
2624                               cell_rectangle.height);     
2625         }         
2626
2627       if (clist_row->state == GTK_STATE_SELECTED || clist_row->bg_set)
2628         gdk_draw_rectangle (clist->clist_window,
2629                             bg_gc,
2630                             TRUE,
2631                             row_rectangle.x,
2632                             row_rectangle.y,
2633                             row_rectangle.width,
2634                             row_rectangle.height);
2635       else
2636         gdk_window_clear_area (clist->clist_window,
2637                                row_rectangle.x,
2638                                row_rectangle.y,
2639                                row_rectangle.width,
2640                                row_rectangle.height);
2641     }
2642
2643   /* iterate and draw all the columns (row cells) and draw their contents */
2644   for (i = 0; i < clist->columns; i++)
2645     {
2646       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
2647       clip_rectangle.width = clist->column[i].area.width;
2648
2649       /* calculate clipping region clipping region */
2650       if (!area)
2651         {
2652           rect = &clip_rectangle;
2653         }
2654       else
2655         {
2656           if (!gdk_rectangle_intersect (area, &clip_rectangle, 
2657                                         &intersect_rectangle))
2658             continue;
2659           rect = &intersect_rectangle;
2660         }
2661
2662       /* calculate real width for column justification */
2663       switch (clist_row->cell[i].type)
2664         {
2665         case GTK_CELL_EMPTY:
2666           continue;
2667           break;
2668
2669         case GTK_CELL_TEXT:
2670           width = gdk_string_width (GTK_WIDGET (clist)->style->font,
2671                                     GTK_CELL_TEXT (clist_row->cell[i])->text);
2672           break;
2673
2674         case GTK_CELL_PIXMAP:
2675           gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap, &width, &height);
2676           pixmap_width = width;
2677           break;
2678
2679         case GTK_CELL_PIXTEXT:
2680           gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap, &width, &height);
2681           pixmap_width = width;
2682           width += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
2683           width = gdk_string_width (GTK_WIDGET (clist)->style->font,
2684                                     GTK_CELL_PIXTEXT (clist_row->cell[i])->text);
2685           break;
2686
2687         case GTK_CELL_WIDGET:
2688           /* unimplimented */
2689           continue;
2690           break;
2691
2692         default:
2693           continue;
2694           break;
2695         }
2696
2697       switch (clist->column[i].justification)
2698         {
2699         case GTK_JUSTIFY_LEFT:
2700           offset = clip_rectangle.x;
2701           break;
2702
2703         case GTK_JUSTIFY_RIGHT:
2704           offset = (clip_rectangle.x + clip_rectangle.width) - width;
2705           break;
2706
2707         case GTK_JUSTIFY_CENTER:
2708           offset = (clip_rectangle.x + (clip_rectangle.width / 2)) - (width / 2);
2709           break;
2710
2711         case GTK_JUSTIFY_FILL:
2712           offset = (clip_rectangle.x + (clip_rectangle.width / 2)) - (width / 2);
2713           break;
2714
2715         default:
2716           offset = 0;
2717           break;
2718         };
2719
2720       /* Draw Text or Pixmap */
2721       switch (clist_row->cell[i].type)
2722         {
2723         case GTK_CELL_EMPTY:
2724           continue;
2725           break;
2726
2727         case GTK_CELL_TEXT:
2728           gdk_gc_set_clip_rectangle (fg_gc, rect);
2729
2730           gdk_draw_string (clist->clist_window, 
2731                            widget->style->font,
2732                            fg_gc,
2733                            offset + clist_row->cell[i].horizontal,
2734                            row_rectangle.y + clist->row_center_offset + 
2735                            clist_row->cell[i].vertical,
2736                            GTK_CELL_TEXT (clist_row->cell[i])->text);
2737
2738           gdk_gc_set_clip_rectangle (fg_gc, NULL);
2739           break;
2740
2741         case GTK_CELL_PIXMAP:
2742           xsrc = 0;
2743           ysrc = 0;
2744           xdest = offset + clist_row->cell[i].horizontal;
2745           ydest = (clip_rectangle.y + (clip_rectangle.height / 2)) - height / 2 +
2746             clist_row->cell[i].vertical;
2747
2748           if (xdest < clip_rectangle.x)
2749             {
2750               xsrc = clip_rectangle.x - xdest;
2751               pixmap_width -= xsrc;
2752               xdest = clip_rectangle.x;
2753             }
2754
2755           if (xdest + pixmap_width > clip_rectangle.x + clip_rectangle.width)
2756             pixmap_width = (clip_rectangle.x + clip_rectangle.width) - xdest;
2757
2758           if (ydest < clip_rectangle.y)
2759             {
2760               ysrc = clip_rectangle.y - ydest;
2761               height -= ysrc;
2762               ydest = clip_rectangle.y;
2763             }
2764
2765           if (ydest + height > clip_rectangle.y + clip_rectangle.height)
2766             height = (clip_rectangle.y + clip_rectangle.height) - ydest;
2767
2768           if (GTK_CELL_PIXMAP (clist_row->cell[i])->mask)
2769           {
2770               gdk_gc_set_clip_mask (fg_gc, GTK_CELL_PIXMAP (clist_row->cell[i])->mask);
2771               gdk_gc_set_clip_origin (fg_gc, xdest, ydest);
2772           }
2773           gdk_draw_pixmap (clist->clist_window,
2774                            fg_gc,
2775                            GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
2776                            xsrc, ysrc,
2777                            xdest, ydest,
2778                            pixmap_width, height);
2779
2780           if (GTK_CELL_PIXMAP (clist_row->cell[i])->mask)
2781           {
2782               gdk_gc_set_clip_origin (fg_gc, 0, 0);
2783               gdk_gc_set_clip_mask (fg_gc, NULL);
2784           }
2785           break;
2786
2787         case GTK_CELL_PIXTEXT:
2788           /* draw the pixmap */
2789           xsrc = 0;
2790           ysrc = 0;
2791           xdest = offset + clist_row->cell[i].horizontal;
2792           ydest = (clip_rectangle.y + (clip_rectangle.height / 2)) - height / 2 +
2793             clist_row->cell[i].vertical;
2794
2795           if (xdest < clip_rectangle.x)
2796             {
2797               xsrc = clip_rectangle.x - xdest;
2798               pixmap_width -= xsrc;
2799               xdest = clip_rectangle.x;
2800             }
2801
2802           if (xdest + pixmap_width > clip_rectangle.x + clip_rectangle.width)
2803             pixmap_width = (clip_rectangle.x + clip_rectangle.width) - xdest;
2804
2805           if (ydest < clip_rectangle.y)
2806             {
2807               ysrc = clip_rectangle.y - ydest;
2808               height -= ysrc;
2809               ydest = clip_rectangle.y;
2810             }
2811
2812           if (ydest + height > clip_rectangle.y + clip_rectangle.height)
2813             height = (clip_rectangle.y + clip_rectangle.height) - ydest;
2814
2815           if (GTK_CELL_PIXTEXT (clist_row->cell[i])->mask)
2816           {
2817               gdk_gc_set_clip_mask (fg_gc, GTK_CELL_PIXTEXT (clist_row->cell[i])->mask);
2818               gdk_gc_set_clip_origin (fg_gc, xdest, ydest);
2819           }
2820               
2821           gdk_draw_pixmap (clist->clist_window,
2822                            fg_gc,
2823                            GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
2824                            xsrc, ysrc,
2825                            xdest,
2826                            ydest,
2827                            pixmap_width, height);
2828
2829           gdk_gc_set_clip_origin (fg_gc, 0, 0);
2830
2831           offset += pixmap_width + GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
2832           
2833           /* draw the string */
2834           gdk_gc_set_clip_rectangle (fg_gc, rect);
2835
2836           gdk_draw_string (clist->clist_window, 
2837                            widget->style->font,
2838                            fg_gc,
2839                            offset + clist_row->cell[i].horizontal,
2840                            row_rectangle.y + clist->row_center_offset + 
2841                            clist_row->cell[i].vertical,
2842                            GTK_CELL_PIXTEXT (clist_row->cell[i])->text);
2843
2844           gdk_gc_set_clip_rectangle (fg_gc, NULL);
2845           break;
2846
2847         case GTK_CELL_WIDGET:
2848           /* unimplimented */
2849           continue;
2850           break;
2851
2852         default:
2853           continue;
2854           break;
2855         }
2856     }
2857 }
2858
2859 static void
2860 draw_rows (GtkCList * clist,
2861            GdkRectangle * area)
2862 {
2863   GList *list;
2864   GtkCListRow *clist_row;
2865   int i, first_row, last_row;
2866
2867   g_return_if_fail (clist != NULL);
2868   g_return_if_fail (GTK_IS_CLIST (clist));
2869
2870   if (clist->row_height == 0 ||
2871       !GTK_WIDGET_DRAWABLE (clist))
2872     return;
2873
2874   if (area)
2875     {
2876       first_row = ROW_FROM_YPIXEL (clist, area->y);
2877       last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
2878     }
2879   else
2880     {
2881       first_row = ROW_FROM_YPIXEL (clist, 0);
2882       last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
2883     }
2884
2885   /* this is a small special case which exposes the bottom cell line
2886    * on the last row -- it might go away if I change the wall the cell spacings
2887    * are drawn */
2888   if (clist->rows == first_row)
2889     first_row--;
2890
2891   list = g_list_nth (clist->row_list, first_row);
2892   i = first_row;
2893   while (list)
2894     {
2895       clist_row = list->data;
2896       list = list->next;
2897
2898       if (i > last_row)
2899         return;
2900
2901       (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
2902         (clist, area, i, clist_row);
2903       i++;
2904     }
2905
2906   if (!area)
2907     gdk_window_clear_area (clist->clist_window, 0, ROW_TOP_YPIXEL (clist, i), -1, -1);
2908 }
2909
2910 /*
2911  * SIZE ALLOCATION
2912  *   size_allocate_title_buttons
2913  *   size_allocate_columns
2914  */
2915 static void
2916 size_allocate_title_buttons (GtkCList * clist)
2917 {
2918   gint i, last_button = 0;
2919   GtkAllocation button_allocation;
2920
2921   if (!GTK_WIDGET_REALIZED (clist))
2922     return;
2923
2924   button_allocation.x = clist->hoffset;
2925   button_allocation.y = 0;
2926   button_allocation.width = 0;
2927   button_allocation.height = clist->column_title_area.height;
2928
2929   for (i = 0; i < clist->columns; i++)
2930     {
2931       button_allocation.width += clist->column[i].area.width;
2932
2933       if (i == clist->columns - 1)
2934         button_allocation.width += 2 * (CELL_SPACING + COLUMN_INSET);
2935       else
2936         button_allocation.width += CELL_SPACING + (2 * COLUMN_INSET);
2937
2938       if (i == (clist->columns - 1) || clist->column[i + 1].button)
2939         {
2940           gtk_widget_size_allocate (clist->column[last_button].button, &button_allocation);
2941           button_allocation.x += button_allocation.width;
2942           button_allocation.width = 0;
2943
2944           gdk_window_show (clist->column[last_button].window);
2945           gdk_window_move_resize (clist->column[last_button].window,
2946                                   button_allocation.x - (DRAG_WIDTH / 2), 
2947                                   0, DRAG_WIDTH, clist->column_title_area.height);
2948           
2949           last_button = i + 1;
2950         }
2951       else
2952         {
2953           gdk_window_hide (clist->column[i].window);
2954         }
2955     }
2956 }
2957
2958 static void
2959 size_allocate_columns (GtkCList * clist)
2960 {
2961   gint i, xoffset = 0;
2962
2963   for (i = 0; i < clist->columns; i++)
2964     {
2965       clist->column[i].area.x = xoffset + CELL_SPACING + COLUMN_INSET;
2966
2967       if (i == clist->columns - 1)
2968         {
2969           gint width;
2970
2971           if (clist->column[i].width_set)
2972             {
2973               width = clist->column[i].width;
2974             }
2975           else
2976             {
2977               if (clist->column[i].title)
2978                 width = gdk_string_width (GTK_WIDGET (clist)->style->font, 
2979                                           clist->column[i].title);
2980               else
2981                 width = 0;
2982             }
2983
2984           clist->column[i].area.width = MAX (width,
2985                                              clist->clist_window_width -
2986                                              xoffset - (2 * (CELL_SPACING + COLUMN_INSET)));
2987                                             
2988         }
2989       else
2990         {
2991           clist->column[i].area.width = clist->column[i].width;
2992         }
2993
2994       xoffset += clist->column[i].area.width + CELL_SPACING + (2 * COLUMN_INSET);
2995     }
2996 }
2997
2998 /*
2999  * SELECTION
3000  *   select_row
3001  *   real_select_row
3002  *   real_unselect_row
3003  *   get_selection_info
3004  */
3005 static void
3006 toggle_row (GtkCList * clist,
3007             gint row,
3008             gint column,
3009             GdkEventButton * event)
3010 {
3011   gint i;
3012   GList *list;
3013   GtkCListRow *clist_row, *selected_row;
3014
3015   i = 0;
3016   list = clist->row_list;
3017   selected_row = NULL;
3018
3019   switch (clist->selection_mode)
3020     {
3021     case GTK_SELECTION_SINGLE:
3022       while (list)
3023         {
3024           clist_row = list->data;
3025           list = list->next;
3026
3027           if (row == i)
3028             selected_row = clist_row;
3029           else if (clist_row->state == GTK_STATE_SELECTED)
3030             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3031                              i, column, event);
3032
3033           i++;
3034         }
3035
3036       if (selected_row && selected_row->state == GTK_STATE_SELECTED)
3037         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3038                          row, column, event);
3039       else
3040         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
3041                          row, column, event);
3042       break;
3043
3044
3045     case GTK_SELECTION_BROWSE:
3046       while (list)
3047         {
3048           clist_row = list->data;
3049           list = list->next;
3050
3051           if (i != row && clist_row->state == GTK_STATE_SELECTED)
3052             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3053                              i, column, event);
3054           i++;
3055         }
3056
3057       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
3058                        row, column, event);
3059       break;
3060
3061
3062     case GTK_SELECTION_MULTIPLE:
3063       clist_row = (g_list_nth (clist->row_list, row))->data;
3064
3065       if (clist_row->state == GTK_STATE_SELECTED)
3066         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3067                          row, column, event);
3068       else
3069         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
3070                          row, column, event);
3071       break;
3072
3073
3074     case GTK_SELECTION_EXTENDED:
3075       break;
3076
3077     default:
3078       break;
3079     }
3080 }
3081
3082 static void
3083 select_row (GtkCList * clist,
3084             gint row,
3085             gint column,
3086             GdkEventButton * event)
3087 {
3088   gint i;
3089   GList *list;
3090   GtkCListRow *clist_row;
3091
3092   switch (clist->selection_mode)
3093     {
3094     case GTK_SELECTION_SINGLE:
3095     case GTK_SELECTION_BROWSE:
3096       i = 0;
3097       list = clist->row_list;
3098       while (list)
3099         {
3100           clist_row = list->data;
3101           list = list->next;
3102
3103           if (row != i && clist_row->state == GTK_STATE_SELECTED)
3104             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3105                              i, column, event);
3106
3107           i++;
3108         }
3109
3110       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
3111                        row, column, event);
3112       break;
3113
3114     case GTK_SELECTION_MULTIPLE:
3115       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
3116                        row, column, event);
3117
3118       break;
3119
3120     case GTK_SELECTION_EXTENDED:
3121       break;
3122
3123     default:
3124       break;
3125     }
3126 }
3127
3128 static void
3129 unselect_row (GtkCList * clist,
3130               gint row,
3131               gint column,
3132               GdkEventButton * event)
3133 {
3134   switch (clist->selection_mode)
3135     {
3136     case GTK_SELECTION_SINGLE:
3137     case GTK_SELECTION_BROWSE:
3138     case GTK_SELECTION_MULTIPLE:
3139       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3140                        row, column, event);
3141       break;
3142
3143     case GTK_SELECTION_EXTENDED:
3144       break;
3145
3146     default:
3147       break;
3148     }
3149 }
3150
3151 static void
3152 real_select_row (GtkCList * clist,
3153                  gint row,
3154                  gint column,
3155                  GdkEventButton * event)
3156 {
3157   GtkCListRow *clist_row;
3158
3159   g_return_if_fail (clist != NULL);
3160
3161   if (row < 0 || row > (clist->rows - 1))
3162     return;
3163
3164   clist_row = (g_list_nth (clist->row_list, row))->data;
3165
3166   if (clist_row->state == GTK_STATE_NORMAL)
3167     {
3168       clist_row->state = GTK_STATE_SELECTED;
3169       clist->selection = g_list_append (clist->selection, GINT_TO_POINTER (row));
3170
3171       if (!GTK_CLIST_FROZEN (clist)
3172           && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3173         (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
3174           (clist, NULL, row, clist_row);
3175     }
3176 }
3177
3178 static void
3179 real_unselect_row (GtkCList * clist,
3180                    gint row,
3181                    gint column,
3182                    GdkEventButton * event)
3183 {
3184   GtkCListRow *clist_row;
3185
3186   g_return_if_fail (clist != NULL);
3187
3188   if (row < 0 || row > (clist->rows - 1))
3189     return;
3190
3191   clist_row = (g_list_nth (clist->row_list, row))->data;
3192
3193   if (clist_row->state == GTK_STATE_SELECTED)
3194     {
3195       clist_row->state = GTK_STATE_NORMAL;
3196       clist->selection = g_list_remove (clist->selection, GINT_TO_POINTER (row));
3197
3198       if (!GTK_CLIST_FROZEN (clist)
3199           && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3200         (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
3201           (clist, NULL, row, clist_row);
3202     }
3203 }
3204
3205 static gint
3206 get_selection_info (GtkCList * clist,
3207                     gint x,
3208                     gint y,
3209                     gint * row,
3210                     gint * column)
3211 {
3212   gint trow, tcol;
3213
3214   g_return_val_if_fail (clist != NULL, 0);
3215
3216   /* bounds checking, return false if the user clicked 
3217    * on a blank area */
3218   trow = ROW_FROM_YPIXEL (clist, y);
3219   if (trow >= clist->rows)
3220     return 0;
3221
3222   if (row)
3223     *row = trow;
3224
3225   tcol = COLUMN_FROM_XPIXEL (clist, x);
3226   if (tcol >= clist->columns)
3227     return 0;
3228
3229   if (column)
3230     *column = tcol;
3231
3232   return 1;
3233 }
3234
3235 gint
3236 gtk_clist_get_selection_info (GtkCList *clist, 
3237                               gint      x, 
3238                               gint      y, 
3239                               gint *    row, 
3240                               gint *    column)
3241 {
3242   g_return_val_if_fail (clist != NULL, 0);
3243   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
3244   return get_selection_info (clist, x, y, row, column);
3245 }
3246
3247 /* 
3248  * RESIZE COLUMNS
3249  *   draw_xor_line
3250  *   new_column_width
3251  *   resize_column
3252  */
3253 static void                          
3254 draw_xor_line (GtkCList * clist)
3255 {
3256   GtkWidget *widget;
3257   
3258   g_return_if_fail (clist != NULL);
3259   
3260   widget = GTK_WIDGET (clist);
3261
3262   gdk_draw_line (widget->window, clist->xor_gc,  
3263                  clist->x_drag,                                       
3264                  widget->style->klass->ythickness,                               
3265                  clist->x_drag,                                             
3266                  clist->column_title_area.height + clist->clist_window_height + 1);
3267 }
3268
3269 /* this function returns the new width of the column being resized given
3270  * the column and x position of the cursor; the x cursor position is passed
3271  * in as a pointer and automagicly corrected if it's beyond min/max limits */
3272 static gint
3273 new_column_width (GtkCList * clist,
3274                   gint column,
3275                   gint * x,
3276                   gint * visible)
3277 {
3278   gint cx, rx, width;
3279
3280   cx = *x;
3281
3282   /* first translate the x position from widget->window
3283    * to clist->clist_window */
3284   cx -= GTK_WIDGET (clist)->style->klass->xthickness;
3285
3286   /* rx is x from the list beginning */
3287   rx = cx - clist->hoffset;
3288
3289   /* you can't shrink a column to less than its minimum width */
3290   if (cx < (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET + COLUMN_MIN_WIDTH))
3291     {
3292       *x = cx = COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET + COLUMN_MIN_WIDTH +
3293         GTK_WIDGET (clist)->style->klass->xthickness;
3294       cx -= GTK_WIDGET (clist)->style->klass->xthickness;
3295       rx = cx - clist->hoffset;
3296     }
3297
3298   if (cx > clist->clist_window_width)
3299     *visible = 0;
3300   else
3301     *visible = 1;
3302
3303   /* calculate new column width making sure it doesn't end up
3304    * less than the minimum width */
3305   width = (rx - COLUMN_LEFT (clist, column)) - COLUMN_INSET -
3306     ((clist->columns == (column - 1)) ? CELL_SPACING : 0);
3307   if (width < COLUMN_MIN_WIDTH)
3308     width = COLUMN_MIN_WIDTH;
3309
3310   return width;
3311 }
3312
3313 /* this will do more later */
3314 static void
3315 resize_column (GtkCList * clist,
3316                gint column,
3317                gint width)
3318 {
3319   gtk_clist_set_column_width (clist, column, width);
3320 }
3321
3322 /* BUTTONS */
3323 static void
3324 column_button_create (GtkCList * clist,
3325                       gint column)
3326 {
3327   GtkWidget *button;
3328
3329   button = clist->column[column].button = gtk_button_new ();
3330   gtk_widget_set_parent (button, GTK_WIDGET (clist));
3331   if (GTK_WIDGET_REALIZED (clist) && clist->title_window)
3332     gtk_widget_set_parent_window (clist->column[column].button, clist->title_window);
3333   
3334   gtk_signal_connect (GTK_OBJECT (button),
3335                       "clicked",
3336                       (GtkSignalFunc) column_button_clicked,
3337                       (gpointer) clist);
3338
3339   gtk_widget_show (button);
3340 }
3341
3342 static void
3343 column_button_clicked (GtkWidget * widget,
3344                        gpointer data)
3345 {
3346   gint i;
3347   GtkCList *clist;
3348
3349   g_return_if_fail (widget != NULL);
3350   g_return_if_fail (GTK_IS_CLIST (data));
3351
3352   clist = GTK_CLIST (data);
3353
3354   /* find the column who's button was pressed */
3355   for (i = 0; i < clist->columns; i++)
3356     if (clist->column[i].button == widget)
3357       break;
3358
3359   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[CLICK_COLUMN], i);
3360 }
3361
3362 /* 
3363  * SCROLLBARS
3364  *
3365  * functions:
3366  *   create_scrollbars
3367  *   adjust_scrollbars
3368  *   vadjustment_changed
3369  *   hadjustment_changed
3370  *   vadjustment_value_changed
3371  *   hadjustment_value_changed 
3372  */
3373 static void
3374 create_scrollbars (GtkCList * clist)
3375 {
3376   GtkAdjustment *adjustment;
3377
3378   clist->vscrollbar = gtk_vscrollbar_new (NULL);
3379   adjustment = gtk_range_get_adjustment (GTK_RANGE (clist->vscrollbar));
3380
3381   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
3382                       (GtkSignalFunc) vadjustment_changed,
3383                       (gpointer) clist);
3384
3385   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
3386                       (GtkSignalFunc) vadjustment_value_changed,
3387                       (gpointer) clist);
3388
3389   gtk_widget_set_parent (clist->vscrollbar, GTK_WIDGET (clist));
3390   gtk_widget_show (clist->vscrollbar);
3391
3392   clist->hscrollbar = gtk_hscrollbar_new (NULL);
3393   adjustment = gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar));
3394
3395   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
3396                       (GtkSignalFunc) hadjustment_changed,
3397                       (gpointer) clist);
3398
3399   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
3400                       (GtkSignalFunc) hadjustment_value_changed,
3401                       (gpointer) clist);
3402
3403   gtk_widget_set_parent (clist->hscrollbar, GTK_WIDGET (clist));
3404   gtk_widget_show (clist->hscrollbar);
3405 }
3406
3407 static void
3408 adjust_scrollbars (GtkCList * clist)
3409 {
3410   GTK_RANGE (clist->vscrollbar)->adjustment->page_size = clist->clist_window_height;
3411   GTK_RANGE (clist->vscrollbar)->adjustment->page_increment = clist->clist_window_height / 2;
3412   GTK_RANGE (clist->vscrollbar)->adjustment->step_increment = 10;
3413   GTK_RANGE (clist->vscrollbar)->adjustment->lower = 0;
3414   GTK_RANGE (clist->vscrollbar)->adjustment->upper = LIST_HEIGHT (clist);
3415
3416   if (clist->clist_window_height - clist->voffset > LIST_HEIGHT (clist))
3417     {
3418       GTK_RANGE (clist->vscrollbar)->adjustment->value = MAX (0, LIST_HEIGHT (clist) - 
3419         clist->clist_window_height);
3420       gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), 
3421                                "value_changed");
3422     }
3423
3424   GTK_RANGE (clist->hscrollbar)->adjustment->page_size = clist->clist_window_width;
3425   GTK_RANGE (clist->hscrollbar)->adjustment->page_increment = clist->clist_window_width / 2;
3426   GTK_RANGE (clist->hscrollbar)->adjustment->step_increment = 10;
3427   GTK_RANGE (clist->hscrollbar)->adjustment->lower = 0;
3428   GTK_RANGE (clist->hscrollbar)->adjustment->upper = LIST_WIDTH (clist);
3429
3430   if (clist->clist_window_width - clist->hoffset > LIST_WIDTH (clist))
3431     {
3432       GTK_RANGE (clist->hscrollbar)->adjustment->value = MAX (0, LIST_WIDTH (clist) - 
3433         clist->clist_window_width);
3434       gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->hscrollbar)->adjustment), 
3435                                "value_changed");
3436     }
3437
3438   if (LIST_HEIGHT (clist) <= clist->clist_window_height &&
3439       clist->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
3440     {
3441       if (GTK_WIDGET_VISIBLE (clist->vscrollbar))
3442         {
3443           gtk_widget_hide (clist->vscrollbar);
3444           gtk_widget_queue_resize (GTK_WIDGET (clist));
3445         }
3446     }
3447   else
3448     {
3449       if (!GTK_WIDGET_VISIBLE (clist->vscrollbar))
3450         {
3451           gtk_widget_show (clist->vscrollbar);
3452           gtk_widget_queue_resize (GTK_WIDGET (clist));
3453         }
3454     }
3455
3456   if (LIST_WIDTH (clist) <= clist->clist_window_width &&
3457       clist->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
3458     {
3459       if (GTK_WIDGET_VISIBLE (clist->hscrollbar))
3460         {
3461           gtk_widget_hide (clist->hscrollbar);
3462           gtk_widget_queue_resize (GTK_WIDGET (clist));
3463         }
3464     }
3465   else
3466     {
3467       if (!GTK_WIDGET_VISIBLE (clist->hscrollbar))
3468         {
3469           gtk_widget_show (clist->hscrollbar);
3470           gtk_widget_queue_resize (GTK_WIDGET (clist));
3471         }
3472     }
3473
3474   gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), "changed");
3475   gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->hscrollbar)->adjustment), "changed");
3476 }
3477
3478 static void
3479 vadjustment_changed (GtkAdjustment * adjustment,
3480                                gpointer data)
3481 {
3482   GtkCList *clist;
3483
3484   g_return_if_fail (adjustment != NULL);
3485   g_return_if_fail (data != NULL);
3486
3487   clist = GTK_CLIST (data);
3488 }
3489
3490 static void
3491 hadjustment_changed (GtkAdjustment * adjustment,
3492                                gpointer data)
3493 {
3494   GtkCList *clist;
3495
3496   g_return_if_fail (adjustment != NULL);
3497   g_return_if_fail (data != NULL);
3498
3499   clist = GTK_CLIST (data);
3500 }
3501
3502 static void
3503 check_exposures (GtkCList *clist)
3504 {
3505   GdkEvent *event;
3506
3507   if (!GTK_WIDGET_REALIZED (clist))
3508     return;
3509
3510   /* Make sure graphics expose events are processed before scrolling
3511    * again */
3512   while ((event = gdk_event_get_graphics_expose (clist->clist_window)) != NULL)
3513     {
3514       gtk_widget_event (GTK_WIDGET (clist), event);
3515       if (event->expose.count == 0)
3516         {
3517           gdk_event_free (event);
3518           break;
3519         }
3520       gdk_event_free (event);
3521     }
3522 }
3523
3524 static void
3525 vadjustment_value_changed (GtkAdjustment * adjustment,
3526                                      gpointer data)
3527 {
3528   GtkCList *clist;
3529   GdkRectangle area;
3530   gint diff, value;
3531
3532   g_return_if_fail (adjustment != NULL);
3533   g_return_if_fail (data != NULL);
3534   g_return_if_fail (GTK_IS_CLIST (data));
3535
3536   clist = GTK_CLIST (data);
3537
3538   if (!GTK_WIDGET_DRAWABLE (clist))
3539     return;
3540
3541   value = adjustment->value;
3542
3543   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (clist->vscrollbar)))
3544     {
3545       if (value > -clist->voffset)
3546         {
3547           /* scroll down */
3548           diff = value + clist->voffset;
3549
3550           /* we have to re-draw the whole screen here... */
3551           if (diff >= clist->clist_window_height)
3552             {
3553               clist->voffset = -value;
3554               draw_rows (clist, NULL);
3555               return;
3556             }
3557
3558           if ((diff != 0) && (diff != clist->clist_window_height))
3559             gdk_window_copy_area (clist->clist_window,
3560                                   clist->fg_gc,
3561                                   0, 0,
3562                                   clist->clist_window,
3563                                   0,
3564                                   diff,
3565                                   clist->clist_window_width,
3566                                   clist->clist_window_height - diff);
3567
3568           area.x = 0;
3569           area.y = clist->clist_window_height - diff;
3570           area.width = clist->clist_window_width;
3571           area.height = diff;
3572         }
3573       else
3574         {
3575           /* scroll up */
3576           diff = -clist->voffset - value;
3577
3578           /* we have to re-draw the whole screen here... */
3579           if (diff >= clist->clist_window_height)
3580             {
3581               clist->voffset = -value;
3582               draw_rows (clist, NULL);
3583               return;
3584             }
3585
3586           if ((diff != 0) && (diff != clist->clist_window_height))
3587             gdk_window_copy_area (clist->clist_window,
3588                                   clist->fg_gc,
3589                                   0, diff,
3590                                   clist->clist_window,
3591                                   0,
3592                                   0,
3593                                   clist->clist_window_width,
3594                                   clist->clist_window_height - diff);
3595
3596           area.x = 0;
3597           area.y = 0;
3598           area.width = clist->clist_window_width;
3599           area.height = diff;
3600
3601         }
3602
3603       clist->voffset = -value;
3604       if ((diff != 0) && (diff != clist->clist_window_height))
3605         check_exposures (clist);
3606     }
3607
3608   draw_rows (clist, &area);
3609 }
3610
3611 static void
3612 hadjustment_value_changed (GtkAdjustment * adjustment,
3613                                      gpointer data)
3614 {
3615   GtkCList *clist;
3616   GdkRectangle area;
3617   gint i, diff, value;
3618
3619   g_return_if_fail (adjustment != NULL);
3620   g_return_if_fail (data != NULL);
3621   g_return_if_fail (GTK_IS_CLIST (data));
3622
3623   clist = GTK_CLIST (data);
3624
3625   if (!GTK_WIDGET_DRAWABLE (clist))
3626     return;
3627
3628   value = adjustment->value;
3629
3630   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar)))
3631     {
3632       /* move the column buttons and resize windows */
3633       for (i = 0; i < clist->columns; i++)
3634         {
3635           if (clist->column[i].button)
3636             {
3637               clist->column[i].button->allocation.x -= value + clist->hoffset;
3638
3639               if (clist->column[i].button->window)
3640                 {
3641                   gdk_window_move (clist->column[i].button->window,
3642                                    clist->column[i].button->allocation.x,
3643                                    clist->column[i].button->allocation.y);
3644
3645                   if (clist->column[i].window)
3646                     gdk_window_move (clist->column[i].window,
3647                                      clist->column[i].button->allocation.x +
3648                                      clist->column[i].button->allocation.width - 
3649                                      (DRAG_WIDTH / 2), 0); 
3650                 }
3651             }
3652         }
3653
3654       if (value > -clist->hoffset)
3655         {
3656           /* scroll right */
3657           diff = value + clist->hoffset;
3658
3659           /* we have to re-draw the whole screen here... */
3660           if (diff >= clist->clist_window_width)
3661             {
3662               clist->hoffset = -value;
3663               draw_rows (clist, NULL);
3664               return;
3665             }
3666
3667           if ((diff != 0) && (diff != clist->clist_window_width))
3668             gdk_window_copy_area (clist->clist_window,
3669                                   clist->fg_gc,
3670                                   0, 0,
3671                                   clist->clist_window,
3672                                   diff,
3673                                   0,
3674                                   clist->clist_window_width - diff,
3675                                   clist->clist_window_height);
3676
3677           area.x = clist->clist_window_width - diff;
3678           area.y = 0;
3679           area.width = diff;
3680           area.height = clist->clist_window_height;
3681         }
3682       else
3683         {
3684           /* scroll left */
3685           diff = -clist->hoffset - value;
3686
3687           /* we have to re-draw the whole screen here... */
3688           if (diff >= clist->clist_window_width)
3689             {
3690               clist->hoffset = -value;
3691               draw_rows (clist, NULL);
3692               return;
3693             }
3694
3695           if ((diff != 0) && (diff != clist->clist_window_width))
3696             gdk_window_copy_area (clist->clist_window,
3697                                   clist->fg_gc,
3698                                   diff, 0,
3699                                   clist->clist_window,
3700                                   0,
3701                                   0,
3702                                   clist->clist_window_width - diff,
3703                                   clist->clist_window_height);
3704
3705           area.x = 0;
3706           area.y = 0;
3707           area.width = diff;
3708           area.height = clist->clist_window_height;
3709         }
3710
3711       clist->hoffset = -value;
3712       if ((diff != 0) && (diff != clist->clist_window_width))
3713         check_exposures (clist);
3714     }
3715
3716   draw_rows (clist, &area);
3717 }
3718
3719 /* 
3720  * Memory Allocation/Distruction Routines for GtkCList stuctures
3721  *
3722  * functions:
3723  *   columns_new
3724  *   column_title_new
3725  *   columns_delete
3726  *   row_new
3727  *   row_delete
3728  *   cell_empty
3729  *   cell_set_text
3730  *   cell_set_pixmap 
3731  */
3732 static GtkCListColumn *
3733 columns_new (GtkCList * clist)
3734 {
3735   gint i;
3736   GtkCListColumn *column;
3737
3738   column = g_new (GtkCListColumn, clist->columns);
3739
3740   for (i = 0; i < clist->columns; i++)
3741     {
3742       column[i].area.x = 0;
3743       column[i].area.y = 0;
3744       column[i].area.width = 0;
3745       column[i].area.height = 0;
3746       column[i].title = NULL;
3747       column[i].button = NULL;
3748       column[i].window = NULL;
3749       column[i].width = 0;
3750       column[i].width_set = FALSE;
3751       column[i].justification = GTK_JUSTIFY_LEFT;
3752     }
3753
3754   return column;
3755 }
3756
3757 static void
3758 column_title_new (GtkCList * clist,
3759                   gint column,
3760                   gchar * title)
3761 {
3762   if (clist->column[column].title)
3763     g_free (clist->column[column].title);
3764
3765   clist->column[column].title = g_strdup (title);
3766 }
3767
3768 static void
3769 columns_delete (GtkCList * clist)
3770 {
3771   gint i;
3772
3773   for (i = 0; i < clist->columns; i++)
3774     if (clist->column[i].title)
3775       g_free (clist->column[i].title);
3776       
3777   g_free (clist->column);
3778 }
3779
3780 static GtkCListRow *
3781 row_new (GtkCList * clist)
3782 {
3783   int i;
3784   GtkCListRow *clist_row;
3785
3786   clist_row = g_chunk_new (GtkCListRow, clist->row_mem_chunk);
3787   clist_row->cell = g_chunk_new (GtkCell, clist->cell_mem_chunk);
3788
3789   for (i = 0; i < clist->columns; i++)
3790     {
3791       clist_row->cell[i].type = GTK_CELL_EMPTY;
3792       clist_row->cell[i].vertical = 0;
3793       clist_row->cell[i].horizontal = 0;
3794     }
3795
3796   clist_row->fg_set = FALSE;
3797   clist_row->bg_set = FALSE;
3798   clist_row->state = GTK_STATE_NORMAL;
3799   clist_row->data = NULL;
3800   clist_row->destroy = NULL;
3801
3802   return clist_row;
3803 }
3804
3805 static void
3806 row_delete (GtkCList * clist,
3807             GtkCListRow * clist_row)
3808 {
3809   gint i;
3810
3811   for (i = 0; i < clist->columns; i++)
3812     cell_empty (clist, clist_row, i);
3813
3814   if (clist_row->destroy)
3815     clist_row->destroy (clist_row->data);
3816
3817   g_mem_chunk_free (clist->cell_mem_chunk, clist_row->cell);
3818   g_mem_chunk_free (clist->row_mem_chunk, clist_row);
3819 }
3820
3821 static void
3822 cell_empty (GtkCList * clist,
3823             GtkCListRow * clist_row,
3824             gint column)
3825 {
3826   switch (clist_row->cell[column].type)
3827     {
3828     case GTK_CELL_EMPTY:
3829       break;
3830       
3831     case GTK_CELL_TEXT:
3832       g_free (GTK_CELL_TEXT (clist_row->cell[column])->text);
3833       break;
3834       
3835     case GTK_CELL_PIXMAP:
3836       gdk_pixmap_unref (GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap);
3837       if (GTK_CELL_PIXMAP (clist_row->cell[column])->mask)
3838           gdk_bitmap_unref (GTK_CELL_PIXMAP (clist_row->cell[column])->mask);
3839       break;
3840       
3841     case GTK_CELL_PIXTEXT:
3842       g_free (GTK_CELL_PIXTEXT (clist_row->cell[column])->text);
3843       gdk_pixmap_unref (GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap);
3844       if (GTK_CELL_PIXTEXT (clist_row->cell[column])->mask)
3845           gdk_bitmap_unref (GTK_CELL_PIXTEXT (clist_row->cell[column])->mask);
3846       break;
3847
3848     case GTK_CELL_WIDGET:
3849       /* unimplimented */
3850       break;
3851       
3852     default:
3853       break;
3854     }
3855
3856   clist_row->cell[column].type = GTK_CELL_EMPTY;
3857 }
3858
3859 static void
3860 cell_set_text (GtkCList * clist,
3861                GtkCListRow * clist_row,
3862                gint column,
3863                gchar * text)
3864 {
3865   cell_empty (clist, clist_row, column);
3866
3867   if (text)
3868     {
3869       clist_row->cell[column].type = GTK_CELL_TEXT;
3870       GTK_CELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
3871     }
3872 }
3873
3874 static void
3875 cell_set_pixmap (GtkCList * clist,
3876                  GtkCListRow * clist_row,
3877                  gint column,
3878                  GdkPixmap * pixmap,
3879                  GdkBitmap * mask)
3880 {
3881   cell_empty (clist, clist_row, column);
3882
3883   if (pixmap)
3884     {
3885       clist_row->cell[column].type = GTK_CELL_PIXMAP;
3886       GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap = pixmap;
3887       /* We set the mask even if it is NULL */
3888       GTK_CELL_PIXMAP (clist_row->cell[column])->mask = mask;
3889     }
3890 }
3891
3892 static void
3893 cell_set_pixtext (GtkCList * clist,
3894                   GtkCListRow * clist_row,
3895                   gint column,
3896                   gchar * text,
3897                   guint8 spacing,
3898                   GdkPixmap * pixmap,
3899                   GdkBitmap * mask)
3900 {
3901   cell_empty (clist, clist_row, column);
3902
3903   if (text && pixmap)
3904     {
3905       clist_row->cell[column].type = GTK_CELL_PIXTEXT;
3906       GTK_CELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
3907       GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
3908       GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap = pixmap;
3909       GTK_CELL_PIXTEXT (clist_row->cell[column])->mask = mask;
3910     }
3911 }
3912
3913 /* Fill in data after widget has correct style */
3914
3915 static void 
3916 add_style_data (GtkCList * clist)
3917 {
3918   GtkWidget *widget;
3919
3920   widget = GTK_WIDGET(clist);
3921
3922   /* text properties */
3923   if (!GTK_CLIST_ROW_HEIGHT_SET (clist))
3924     {
3925       clist->row_height = widget->style->font->ascent + widget->style->font->descent + 1;
3926       clist->row_center_offset = widget->style->font->ascent + 1.5;
3927     }
3928   else
3929     {
3930       gint text_height;
3931       text_height = clist->row_height - (GTK_WIDGET (clist)->style->font->ascent +
3932                           GTK_WIDGET (clist) ->style->font->descent + 1);
3933       clist->row_center_offset = (text_height / 2) + GTK_WIDGET (clist)->style->font->ascent + 1.5;
3934     }
3935
3936   /* Column widths */
3937 }