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