]> Pileus Git - ~andy/gtk/blob - gtk/gtkclist.c
prefixed all clist flags with GTK_ to avoid name clashes. redefined
[~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 ()
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_POINTER);
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_POINTER);
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 ((gint) 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_set_row_data (GtkCList * clist,
1471                         gint row,
1472                         gpointer data)
1473 {
1474   gtk_clist_set_row_data_full (clist, row, data, NULL);
1475 }
1476
1477 void
1478 gtk_clist_set_row_data_full (GtkCList * clist,
1479                              gint row,
1480                              gpointer data,
1481                              GtkDestroyNotify destroy)
1482 {
1483   GtkCListRow *clist_row;
1484
1485   g_return_if_fail (clist != NULL);
1486
1487   if (row < 0 || row > (clist->rows - 1))
1488     return;
1489
1490   clist_row = (g_list_nth (clist->row_list, row))->data;
1491   clist_row->data = data;
1492   clist_row->destroy = destroy;
1493
1494   /* re-send the selected signal if data is changed/added
1495    * so the application can respond to the new data -- 
1496    * this could be questionable behavior */
1497   if (clist_row->state == GTK_STATE_SELECTED)
1498     gtk_clist_select_row (clist, row, -1);
1499 }
1500
1501 gpointer
1502 gtk_clist_get_row_data (GtkCList * clist,
1503                         gint row)
1504 {
1505   GtkCListRow *clist_row;
1506
1507   g_return_val_if_fail (clist != NULL, NULL);
1508
1509   if (row < 0 || row > (clist->rows - 1))
1510     return NULL;
1511
1512   clist_row = (g_list_nth (clist->row_list, row))->data;
1513   return clist_row->data;
1514 }
1515
1516 gint
1517 gtk_clist_find_row_from_data (GtkCList * clist,
1518                               gpointer data)
1519 {
1520   GList *list;
1521   gint n;
1522
1523   g_return_val_if_fail (clist != NULL, -1);
1524   g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
1525
1526   if (clist->rows < 1)
1527     return -1; /* is this an optimization or just worthless? */
1528
1529   n = 0;
1530   list = clist->row_list;
1531   while (list)
1532     {
1533       GtkCListRow *clist_row;
1534
1535       clist_row = list->data;
1536       if (clist_row->data == data)
1537         break;
1538       n++;
1539       list = list->next;
1540     }
1541
1542   if (list)
1543     return n;
1544
1545   return -1;
1546 }
1547
1548 void
1549 gtk_clist_select_row (GtkCList * clist,
1550                       gint row,
1551                       gint column)
1552 {
1553   g_return_if_fail (clist != NULL);
1554
1555   if (row < 0 || row >= clist->rows)
1556     return;
1557
1558   if (column < -1 || column >= clist->columns)
1559     return;
1560
1561   select_row (clist, row, column, NULL);
1562 }
1563
1564 void
1565 gtk_clist_unselect_row (GtkCList * clist,
1566                         gint row,
1567                         gint column)
1568 {
1569   g_return_if_fail (clist != NULL);
1570
1571   if (row < 0 || row >= clist->rows)
1572     return;
1573
1574   if (column < -1 || column >= clist->columns)
1575     return;
1576
1577   unselect_row (clist, row, column, NULL);
1578 }
1579
1580 GtkVisibility
1581 gtk_clist_row_is_visible (GtkCList * clist,
1582                           gint row)
1583 {
1584   gint top;
1585
1586   g_return_val_if_fail (clist != NULL, 0);
1587
1588   if (row < 0 || row >= clist->rows)
1589     return GTK_VISIBILITY_NONE;
1590
1591   if (clist->row_height == 0)
1592     return GTK_VISIBILITY_NONE;
1593
1594   if (row < ROW_FROM_YPIXEL (clist, 0))
1595     return GTK_VISIBILITY_NONE;
1596
1597   if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height))
1598     return GTK_VISIBILITY_NONE;
1599
1600   top = ROW_TOP_YPIXEL (clist, row);
1601
1602   if ((top < 0)
1603       || ((top + clist->row_height) >= clist->clist_window_height))
1604     return GTK_VISIBILITY_PARTIAL;
1605
1606   return GTK_VISIBILITY_FULL;
1607 }
1608
1609 GtkAdjustment *
1610 gtk_clist_get_vadjustment (GtkCList * clist)
1611 {
1612   g_return_val_if_fail (clist != NULL, NULL);
1613   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1614
1615   return gtk_range_get_adjustment (GTK_RANGE (clist->vscrollbar));
1616 }
1617
1618 GtkAdjustment *
1619 gtk_clist_get_hadjustment (GtkCList * clist)
1620 {
1621   g_return_val_if_fail (clist != NULL, NULL);
1622   g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
1623
1624   return gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar));
1625 }
1626
1627 void
1628 gtk_clist_set_policy (GtkCList * clist,
1629                       GtkPolicyType vscrollbar_policy,
1630                       GtkPolicyType hscrollbar_policy)
1631 {
1632   g_return_if_fail (clist != NULL);
1633   g_return_if_fail (GTK_IS_CLIST (clist));
1634
1635   if (clist->vscrollbar_policy != vscrollbar_policy)
1636     {
1637       clist->vscrollbar_policy = vscrollbar_policy;
1638
1639       if (GTK_WIDGET (clist)->parent)
1640         gtk_widget_queue_resize (GTK_WIDGET (clist));
1641     }
1642
1643   if (clist->hscrollbar_policy != hscrollbar_policy)
1644     {
1645       clist->hscrollbar_policy = hscrollbar_policy;
1646
1647       if (GTK_WIDGET (clist)->parent)
1648         gtk_widget_queue_resize (GTK_WIDGET (clist));
1649     }
1650 }
1651
1652 /*
1653  * GTKOBJECT
1654  *   gtk_clist_destroy
1655  *   gtk_clist_finalize
1656  */
1657 static void
1658 gtk_clist_destroy (GtkObject * object)
1659 {
1660   gint i;
1661   GtkCList *clist;
1662
1663   g_return_if_fail (object != NULL);
1664   g_return_if_fail (GTK_IS_CLIST (object));
1665
1666   clist = GTK_CLIST (object);
1667
1668   /* freeze the list */
1669   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
1670
1671   /* get rid of all the rows */
1672   gtk_clist_clear (clist);
1673
1674   /* Since we don't have a _remove method, unparent the children
1675    * instead of destroying them so the focus will be unset properly.
1676    * (For other containers, the _remove method takes care of the
1677    * unparent) The destroy will happen when the refcount drops
1678    * to zero.
1679    */
1680
1681   /* destroy the scrollbars */
1682   if (clist->vscrollbar)
1683     {
1684       gtk_widget_unparent (clist->vscrollbar);
1685       clist->vscrollbar = NULL;
1686     }
1687   if (clist->hscrollbar)
1688     {
1689       gtk_widget_unparent (clist->hscrollbar);
1690       clist->hscrollbar = NULL;
1691     }
1692
1693   /* destroy the column buttons */
1694   for (i = 0; i < clist->columns; i++)
1695     if (clist->column[i].button)
1696       {
1697         gtk_widget_unparent (clist->column[i].button);
1698         clist->column[i].button = NULL;
1699       }
1700
1701   if (GTK_OBJECT_CLASS (parent_class)->destroy)
1702     (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
1703 }
1704
1705 static void
1706 gtk_clist_finalize (GtkObject * object)
1707 {
1708   GtkCList *clist;
1709
1710   g_return_if_fail (object != NULL);
1711   g_return_if_fail (GTK_IS_CLIST (object));
1712
1713   clist = GTK_CLIST (object);
1714
1715   columns_delete (clist);
1716
1717   g_mem_chunk_destroy (clist->cell_mem_chunk);
1718   g_mem_chunk_destroy (clist->row_mem_chunk);
1719
1720   if (GTK_OBJECT_CLASS (parent_class)->finalize)
1721     (*GTK_OBJECT_CLASS (parent_class)->finalize) (object);
1722 }
1723
1724 /*
1725  * GTKWIDGET
1726  *   gtk_clist_realize
1727  *   gtk_clist_unrealize
1728  *   gtk_clist_map
1729  *   gtk_clist_unmap
1730  *   gtk_clist_draw
1731  *   gtk_clist_expose
1732  *   gtk_clist_button_press
1733  *   gtk_clist_button_release
1734  *   gtk_clist_button_motion
1735  *   gtk_clist_size_request
1736  *   gtk_clist_size_allocate
1737  */
1738 static void
1739 gtk_clist_realize (GtkWidget * widget)
1740 {
1741   gint i;
1742   GtkCList *clist;
1743   GdkWindowAttr attributes;
1744   gint attributes_mask;
1745   GdkGCValues values;
1746   gint border_width;
1747
1748   g_return_if_fail (widget != NULL);
1749   g_return_if_fail (GTK_IS_CLIST (widget));
1750
1751   clist = GTK_CLIST (widget);
1752
1753   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1754
1755   add_style_data (clist);
1756
1757   border_width = GTK_CONTAINER (widget)->border_width;
1758   
1759   attributes.window_type = GDK_WINDOW_CHILD;
1760   attributes.x = widget->allocation.x + border_width;
1761   attributes.y = widget->allocation.y + border_width;
1762   attributes.width = widget->allocation.width - border_width * 2;
1763   attributes.height = widget->allocation.height - border_width * 2;
1764   attributes.wclass = GDK_INPUT_OUTPUT;
1765   attributes.visual = gtk_widget_get_visual (widget);
1766   attributes.colormap = gtk_widget_get_colormap (widget);
1767   attributes.event_mask = gtk_widget_get_events (widget);
1768   attributes.event_mask |= (GDK_EXPOSURE_MASK |
1769                             GDK_BUTTON_PRESS_MASK |
1770                             GDK_BUTTON_RELEASE_MASK |
1771                             GDK_KEY_PRESS_MASK);
1772   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1773
1774
1775   /* main window */
1776   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1777   gdk_window_set_user_data (widget->window, clist);
1778
1779   widget->style = gtk_style_attach (widget->style, widget->window);
1780
1781   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1782
1783   /* column-title window */
1784
1785   attributes.x = clist->column_title_area.x;
1786   attributes.y = clist->column_title_area.y;
1787   attributes.width = clist->column_title_area.width;
1788   attributes.height = clist->column_title_area.height;
1789   
1790   clist->title_window = gdk_window_new (widget->window, &attributes, attributes_mask);
1791   gdk_window_set_user_data (clist->title_window, clist);
1792
1793   gtk_style_set_background (widget->style, clist->title_window, GTK_STATE_SELECTED);
1794   gdk_window_show (clist->title_window);
1795
1796   /* set things up so column buttons are drawn in title window */
1797   for (i = 0; i < clist->columns; i++)
1798     if (clist->column[i].button)
1799       gtk_widget_set_parent_window (clist->column[i].button, clist->title_window);
1800
1801   /* clist-window */
1802   attributes.x = clist->internal_allocation.x + widget->style->klass->xthickness;
1803   attributes.y = clist->internal_allocation.y + widget->style->klass->ythickness +
1804     clist->column_title_area.height;
1805   attributes.width = clist->clist_window_width;
1806   attributes.height = clist->clist_window_height;
1807   
1808   clist->clist_window = gdk_window_new (widget->window, &attributes, attributes_mask);
1809   gdk_window_set_user_data (clist->clist_window, clist);
1810
1811   gdk_window_set_background (clist->clist_window, &widget->style->bg[GTK_STATE_PRELIGHT]);
1812   gdk_window_show (clist->clist_window);
1813   gdk_window_get_size (clist->clist_window, &clist->clist_window_width,
1814                        &clist->clist_window_height);
1815
1816   /* create resize windows */
1817   attributes.wclass = GDK_INPUT_ONLY;
1818   attributes.event_mask = (GDK_BUTTON_PRESS_MASK |
1819                            GDK_BUTTON_RELEASE_MASK |
1820                            GDK_POINTER_MOTION_MASK |
1821                            GDK_POINTER_MOTION_HINT_MASK);
1822   attributes.cursor = clist->cursor_drag = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
1823   attributes_mask = GDK_WA_CURSOR;
1824   
1825   for (i = 0; i < clist->columns; i++)
1826     {
1827       clist->column[i].window = gdk_window_new (clist->title_window, &attributes, attributes_mask);
1828       gdk_window_set_user_data (clist->column[i].window, clist);
1829     }
1830
1831   /* This is slightly less efficient than creating them with the
1832    * right size to begin with, but easier
1833    */
1834   size_allocate_title_buttons (clist);
1835
1836   /* GCs */
1837   clist->fg_gc = gdk_gc_new (widget->window);
1838   clist->bg_gc = gdk_gc_new (widget->window);
1839   
1840   /* We'll use this gc to do scrolling as well */
1841   gdk_gc_set_exposures (clist->fg_gc, TRUE);
1842
1843   values.foreground = widget->style->white;
1844   values.function = GDK_XOR;
1845   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1846   clist->xor_gc = gdk_gc_new_with_values (widget->window,
1847                                           &values,
1848                                           GDK_GC_FOREGROUND |
1849                                           GDK_GC_FUNCTION |
1850                                           GDK_GC_SUBWINDOW);
1851 }
1852
1853 static void
1854 gtk_clist_unrealize (GtkWidget * widget)
1855 {
1856   gint i;
1857   GtkCList *clist;
1858
1859   g_return_if_fail (widget != NULL);
1860   g_return_if_fail (GTK_IS_CLIST (widget));
1861
1862   clist = GTK_CLIST (widget);
1863
1864   GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
1865
1866   gdk_cursor_destroy (clist->cursor_drag);
1867   gdk_gc_destroy (clist->xor_gc);
1868   gdk_gc_destroy (clist->fg_gc);
1869   gdk_gc_destroy (clist->bg_gc);
1870
1871   for (i = 0; i < clist->columns; i++)
1872     if (clist->column[i].window)
1873       {
1874         gdk_window_set_user_data (clist->column[i].window, NULL);
1875         gdk_window_destroy (clist->column[i].window);
1876         clist->column[i].window = NULL;
1877       }
1878
1879   gdk_window_set_user_data (clist->clist_window, NULL);
1880   gdk_window_destroy (clist->clist_window);
1881   clist->clist_window = NULL;
1882
1883   gdk_window_set_user_data (clist->title_window, NULL);
1884   gdk_window_destroy (clist->title_window);
1885   clist->title_window = NULL;
1886
1887   clist->cursor_drag = NULL;
1888   clist->xor_gc = NULL;
1889   clist->fg_gc = NULL;
1890   clist->bg_gc = NULL;
1891
1892   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1893     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1894 }
1895
1896 static void
1897 gtk_clist_map (GtkWidget * widget)
1898 {
1899   gint i;
1900   GtkCList *clist;
1901
1902   g_return_if_fail (widget != NULL);
1903   g_return_if_fail (GTK_IS_CLIST (widget));
1904
1905   clist = GTK_CLIST (widget);
1906
1907   if (!GTK_WIDGET_MAPPED (widget))
1908     {
1909       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
1910
1911       gdk_window_show (widget->window);
1912       gdk_window_show (clist->title_window);
1913       gdk_window_show (clist->clist_window);
1914
1915       /* map column buttons */
1916       for (i = 0; i < clist->columns; i++)
1917         if (clist->column[i].button &&
1918             GTK_WIDGET_VISIBLE (clist->column[i].button) &&
1919             !GTK_WIDGET_MAPPED (clist->column[i].button))
1920           gtk_widget_map (clist->column[i].button);
1921       
1922       /* map resize windows AFTER column buttons (above) */
1923       for (i = 0; i < clist->columns; i++)
1924         if (clist->column[i].window && clist->column[i].button)
1925           gdk_window_show (clist->column[i].window);
1926        
1927       /* map vscrollbars */
1928       if (GTK_WIDGET_VISIBLE (clist->vscrollbar) &&
1929           !GTK_WIDGET_MAPPED (clist->vscrollbar))
1930         gtk_widget_map (clist->vscrollbar);
1931
1932       if (GTK_WIDGET_VISIBLE (clist->hscrollbar) &&
1933           !GTK_WIDGET_MAPPED (clist->hscrollbar))
1934         gtk_widget_map (clist->hscrollbar);
1935
1936       /* unfreeze the list */
1937       GTK_CLIST_UNSET_FLAG (clist, CLIST_FROZEN);
1938     }
1939 }
1940
1941 static void
1942 gtk_clist_unmap (GtkWidget * widget)
1943 {
1944   gint i;
1945   GtkCList *clist;
1946
1947   g_return_if_fail (widget != NULL);
1948   g_return_if_fail (GTK_IS_CLIST (widget));
1949
1950   clist = GTK_CLIST (widget);
1951
1952   if (GTK_WIDGET_MAPPED (widget))
1953     {
1954       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
1955
1956       for (i = 0; i < clist->columns; i++)
1957         if (clist->column[i].window)
1958           gdk_window_hide (clist->column[i].window);
1959
1960       gdk_window_hide (clist->clist_window);
1961       gdk_window_hide (clist->title_window);
1962       gdk_window_hide (widget->window);
1963
1964       /* unmap scrollbars */
1965       if (GTK_WIDGET_MAPPED (clist->vscrollbar))
1966         gtk_widget_unmap (clist->vscrollbar);
1967
1968       if (GTK_WIDGET_MAPPED (clist->hscrollbar))
1969         gtk_widget_unmap (clist->hscrollbar);
1970
1971       /* unmap column buttons */
1972       for (i = 0; i < clist->columns; i++)
1973         if (clist->column[i].button &&
1974             GTK_WIDGET_MAPPED (clist->column[i].button))
1975           gtk_widget_unmap (clist->column[i].button);
1976
1977       /* freeze the list */
1978       GTK_CLIST_SET_FLAG (clist, CLIST_FROZEN);
1979     }
1980 }
1981
1982 static void
1983 gtk_clist_draw (GtkWidget * widget,
1984                 GdkRectangle * area)
1985 {
1986   GtkCList *clist;
1987   gint border_width;
1988
1989   g_return_if_fail (widget != NULL);
1990   g_return_if_fail (GTK_IS_CLIST (widget));
1991   g_return_if_fail (area != NULL);
1992
1993   if (GTK_WIDGET_DRAWABLE (widget))
1994     {
1995       clist = GTK_CLIST (widget);
1996       border_width = GTK_CONTAINER (widget)->border_width;
1997
1998       gdk_window_clear_area (widget->window,
1999                              area->x - border_width, 
2000                              area->y - border_width,
2001                              area->width, area->height);
2002
2003       /* draw list shadow/border */
2004       gtk_draw_shadow (widget->style, widget->window,
2005                        GTK_STATE_NORMAL, clist->shadow_type,
2006                        0, 0, 
2007                        clist->clist_window_width + (2 * widget->style->klass->xthickness),
2008                        clist->clist_window_height + (2 * widget->style->klass->ythickness) +
2009                        clist->column_title_area.height);
2010
2011       gdk_window_clear_area (clist->clist_window,
2012                              0, 0, -1, -1);
2013
2014       draw_rows (clist, NULL);
2015     }
2016 }
2017
2018 static gint
2019 gtk_clist_expose (GtkWidget * widget,
2020                   GdkEventExpose * event)
2021 {
2022   GtkCList *clist;
2023
2024   g_return_val_if_fail (widget != NULL, FALSE);
2025   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2026   g_return_val_if_fail (event != NULL, FALSE);
2027
2028   if (GTK_WIDGET_DRAWABLE (widget))
2029     {
2030       clist = GTK_CLIST (widget);
2031
2032       /* draw border */
2033       if (event->window == widget->window)
2034         gtk_draw_shadow (widget->style, widget->window,
2035                          GTK_STATE_NORMAL, clist->shadow_type,
2036                          0, 0,
2037                          clist->clist_window_width + (2 * widget->style->klass->xthickness),
2038                          clist->clist_window_height + (2 * widget->style->klass->ythickness) +
2039                          clist->column_title_area.height);
2040
2041       /* exposure events on the list */
2042       if (event->window == clist->clist_window)
2043         draw_rows (clist, &event->area);
2044     }
2045
2046   return FALSE;
2047 }
2048
2049 static gint
2050 gtk_clist_button_press (GtkWidget * widget,
2051                         GdkEventButton * event)
2052 {
2053   gint i;
2054   GtkCList *clist;
2055   gint x, y, row, column;
2056
2057   g_return_val_if_fail (widget != NULL, FALSE);
2058   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2059   g_return_val_if_fail (event != NULL, FALSE);
2060
2061   clist = GTK_CLIST (widget);
2062
2063   /* selections on the list */
2064   if (event->window == clist->clist_window)
2065     {
2066       x = event->x;
2067       y = event->y;
2068
2069       if (get_selection_info (clist, x, y, &row, &column))
2070         toggle_row (clist, row, column, event);
2071
2072       return FALSE;
2073     }
2074
2075   /* press on resize windows */
2076   for (i = 0; i < clist->columns; i++)
2077     if (clist->column[i].window && event->window == clist->column[i].window)
2078       {
2079         GTK_CLIST_SET_FLAG (clist, CLIST_IN_DRAG);
2080         gtk_widget_get_pointer (widget, &clist->x_drag, NULL);
2081
2082         gdk_pointer_grab (clist->column[i].window, FALSE,
2083                           GDK_POINTER_MOTION_HINT_MASK |
2084                           GDK_BUTTON1_MOTION_MASK |
2085                           GDK_BUTTON_RELEASE_MASK,
2086                           NULL, NULL, event->time);
2087
2088         draw_xor_line (clist);
2089         return FALSE;
2090       }
2091
2092   return FALSE;
2093 }
2094
2095 static gint
2096 gtk_clist_button_release (GtkWidget * widget,
2097                           GdkEventButton * event)
2098 {
2099   gint i, x, width, visible;
2100   GtkCList *clist;
2101
2102   g_return_val_if_fail (widget != NULL, FALSE);
2103   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2104   g_return_val_if_fail (event != NULL, FALSE);
2105
2106   clist = GTK_CLIST (widget);
2107
2108   /* release on resize windows */
2109   if (GTK_CLIST_IN_DRAG (clist))
2110     for (i = 0; i < clist->columns; i++)
2111       if (clist->column[i].window && event->window == clist->column[i].window)
2112         {
2113           GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
2114           gtk_widget_get_pointer (widget, &x, NULL);
2115           width = new_column_width (clist, i, &x, &visible);
2116           gdk_pointer_ungrab (event->time);
2117           
2118           if (visible)
2119             draw_xor_line (clist);
2120
2121           resize_column (clist, i, width);
2122           return FALSE;
2123         }
2124
2125   return FALSE;
2126 }
2127
2128 static gint
2129 gtk_clist_motion (GtkWidget * widget,
2130                   GdkEventMotion * event)
2131 {
2132   gint i, x, visible;
2133   GtkCList *clist;
2134
2135   g_return_val_if_fail (widget != NULL, FALSE);
2136   g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
2137
2138   clist = GTK_CLIST (widget);
2139
2140   if (GTK_CLIST_IN_DRAG (clist))
2141     for (i = 0; i < clist->columns; i++)
2142       if (clist->column[i].window && event->window == clist->column[i].window)
2143         {
2144           if (event->is_hint || event->window != widget->window)
2145             gtk_widget_get_pointer (widget, &x, NULL);
2146           else
2147             x = event->x;
2148
2149           new_column_width (clist, i, &x, &visible);
2150           /* Welcome to my hack!  I'm going to use a value of x_drage = -99999 to
2151            * indicate the the xor line is already no visible */
2152           if (!visible && clist->x_drag != -99999)
2153             {
2154               draw_xor_line (clist);
2155               clist->x_drag = -99999;
2156             }
2157
2158           if (x != clist->x_drag && visible)
2159             {
2160               if (clist->x_drag != -99999)
2161                 draw_xor_line (clist);
2162
2163               clist->x_drag = x;
2164               draw_xor_line (clist);
2165             }
2166         }
2167
2168   return TRUE;
2169 }
2170
2171 static void
2172 gtk_clist_size_request (GtkWidget * widget,
2173                         GtkRequisition * requisition)
2174 {
2175   gint i;
2176   GtkCList *clist;
2177
2178   g_return_if_fail (widget != NULL);
2179   g_return_if_fail (GTK_IS_CLIST (widget));
2180   g_return_if_fail (requisition != NULL);
2181
2182   clist = GTK_CLIST (widget);
2183
2184   add_style_data (clist);
2185
2186   requisition->width = 0;
2187   requisition->height = 0;
2188
2189   /* compute the size of the column title (title) area */
2190   clist->column_title_area.height = 0;
2191   if (GTK_CLIST_SHOW_TITLES (clist))
2192     for (i = 0; i < clist->columns; i++)
2193       if (clist->column[i].button)
2194         {
2195           gtk_widget_size_request (clist->column[i].button, &clist->column[i].button->requisition);
2196           clist->column_title_area.height = MAX (clist->column_title_area.height,
2197                                                  clist->column[i].button->requisition.height);
2198         }
2199   requisition->height += clist->column_title_area.height;
2200
2201   /* add the vscrollbar space */
2202   if ((clist->vscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
2203       GTK_WIDGET_VISIBLE (clist->vscrollbar))
2204     {
2205       gtk_widget_size_request (clist->vscrollbar, &clist->vscrollbar->requisition);
2206
2207       requisition->width += clist->vscrollbar->requisition.width + SCROLLBAR_SPACING (clist);
2208       requisition->height = MAX (requisition->height,
2209                                  clist->vscrollbar->requisition.height);
2210     }
2211
2212   /* add the hscrollbar space */
2213   if ((clist->hscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
2214       GTK_WIDGET_VISIBLE (clist->hscrollbar))
2215     {
2216       gtk_widget_size_request (clist->hscrollbar, &clist->hscrollbar->requisition);
2217
2218       requisition->height += clist->hscrollbar->requisition.height + SCROLLBAR_SPACING (clist);
2219       requisition->width = MAX (clist->hscrollbar->requisition.width, 
2220                                 requisition->width - 
2221                                 clist->vscrollbar->requisition.width);
2222
2223     }
2224
2225   requisition->width += widget->style->klass->xthickness * 2 +
2226     GTK_CONTAINER (widget)->border_width * 2;
2227   requisition->height += widget->style->klass->ythickness * 2 +
2228     GTK_CONTAINER (widget)->border_width * 2;
2229 }
2230
2231 static void
2232 gtk_clist_size_allocate (GtkWidget * widget,
2233                          GtkAllocation * allocation)
2234 {
2235   GtkCList *clist;
2236   GtkAllocation clist_allocation;
2237   GtkAllocation child_allocation;
2238   gint i, vscrollbar_vis, hscrollbar_vis;
2239
2240   g_return_if_fail (widget != NULL);
2241   g_return_if_fail (GTK_IS_CLIST (widget));
2242   g_return_if_fail (allocation != NULL);
2243
2244   clist = GTK_CLIST (widget);
2245   widget->allocation = *allocation;
2246
2247   if (GTK_WIDGET_REALIZED (widget))
2248     {
2249       gdk_window_move_resize (widget->window,
2250                               allocation->x + GTK_CONTAINER (widget)->border_width,
2251                               allocation->y + GTK_CONTAINER (widget)->border_width,
2252                               allocation->width - GTK_CONTAINER (widget)->border_width * 2,
2253                               allocation->height - GTK_CONTAINER (widget)->border_width * 2);
2254     }
2255
2256   /* use internal allocation structure for all the math
2257    * because it's easier than always subtracting the container
2258    * border width */
2259   clist->internal_allocation.x = 0;
2260   clist->internal_allocation.y = 0;
2261   clist->internal_allocation.width = MAX (1, allocation->width -
2262     GTK_CONTAINER (widget)->border_width * 2);
2263   clist->internal_allocation.height = MAX (1, allocation->height -
2264     GTK_CONTAINER (widget)->border_width * 2);
2265         
2266   /* allocate clist window assuming no scrollbars */
2267   clist_allocation.x = clist->internal_allocation.x + widget->style->klass->xthickness;
2268   clist_allocation.y = clist->internal_allocation.y + widget->style->klass->ythickness +
2269     clist->column_title_area.height;
2270   clist_allocation.width = MAX (1, clist->internal_allocation.width - 
2271     (2 * widget->style->klass->xthickness));
2272   clist_allocation.height = MAX (1, clist->internal_allocation.height -
2273     (2 * widget->style->klass->ythickness) -
2274     clist->column_title_area.height);
2275   
2276   /* 
2277    * here's where we decide to show/not show the scrollbars
2278    */
2279   vscrollbar_vis = 0;
2280   hscrollbar_vis = 0;
2281   
2282   for (i = 0; i <= 1; i++)
2283     {
2284       if (LIST_HEIGHT (clist) <= clist_allocation.height &&
2285           clist->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
2286         {
2287           vscrollbar_vis = 0;
2288         }
2289       else
2290         {
2291           if (!vscrollbar_vis)
2292             {
2293               vscrollbar_vis = 1;
2294               clist_allocation.width = MAX (1, clist_allocation.width - 
2295                 (clist->vscrollbar->requisition.width +
2296                  SCROLLBAR_SPACING (clist)));
2297             }  
2298         }
2299       
2300       if (LIST_WIDTH (clist) <= clist_allocation.width &&
2301           clist->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
2302         {
2303           hscrollbar_vis = 0;
2304         }
2305       else
2306         {
2307           if (!hscrollbar_vis)
2308             {
2309               hscrollbar_vis = 1;
2310               clist_allocation.height = MAX (1, clist_allocation.height - 
2311                 (clist->hscrollbar->requisition.height +
2312                  SCROLLBAR_SPACING (clist)));
2313             }  
2314         }
2315     }
2316   
2317   clist->clist_window_width = clist_allocation.width;
2318   clist->clist_window_height = clist_allocation.height;
2319   
2320   if (GTK_WIDGET_REALIZED (widget))
2321     {
2322       gdk_window_move_resize (clist->clist_window,
2323                               clist_allocation.x,
2324                               clist_allocation.y,
2325                               clist_allocation.width,
2326                               clist_allocation.height);
2327     }
2328   
2329   /* position the window which holds the column title buttons */
2330   clist->column_title_area.x = widget->style->klass->xthickness;
2331   clist->column_title_area.y = widget->style->klass->ythickness;
2332   clist->column_title_area.width = clist_allocation.width;
2333   
2334   if (GTK_WIDGET_REALIZED (widget))
2335     {
2336       gdk_window_move_resize (clist->title_window,
2337                               clist->column_title_area.x,
2338                               clist->column_title_area.y,
2339                               clist->column_title_area.width,
2340                               clist->column_title_area.height);
2341     }
2342   
2343   /* column button allocation */
2344   size_allocate_columns (clist);
2345
2346   if (GTK_WIDGET_REALIZED (widget))
2347     size_allocate_title_buttons (clist);
2348
2349   adjust_scrollbars (clist);
2350   
2351   /* allocate the vscrollbar */
2352   if (vscrollbar_vis)
2353     {
2354       if (!GTK_WIDGET_VISIBLE (clist->vscrollbar))
2355         gtk_widget_show (clist->vscrollbar);
2356       
2357       child_allocation.x = clist->internal_allocation.x + 
2358         clist->internal_allocation.width -
2359         clist->vscrollbar->requisition.width;
2360       child_allocation.y = clist->internal_allocation.y;
2361       child_allocation.width = clist->vscrollbar->requisition.width;
2362       child_allocation.height = MAX (1, clist->internal_allocation.height -
2363         (hscrollbar_vis ? (clist->hscrollbar->requisition.height + SCROLLBAR_SPACING (clist)) : 0));
2364       
2365       gtk_widget_size_allocate (clist->vscrollbar, &child_allocation);
2366     }
2367   else
2368     {
2369       if (GTK_WIDGET_VISIBLE (clist->vscrollbar))
2370         gtk_widget_hide (clist->vscrollbar);
2371     }
2372   
2373   if (hscrollbar_vis)
2374     {
2375       if (!GTK_WIDGET_VISIBLE (clist->hscrollbar))
2376         gtk_widget_show (clist->hscrollbar);
2377       
2378       child_allocation.x = clist->internal_allocation.x;
2379       child_allocation.y = clist->internal_allocation.y +
2380         clist->internal_allocation.height -
2381         clist->hscrollbar->requisition.height;
2382       child_allocation.width = MAX (1, clist->internal_allocation.width -
2383         (vscrollbar_vis ? (clist->vscrollbar->requisition.width + SCROLLBAR_SPACING (clist)) : 0));
2384       child_allocation.height = clist->hscrollbar->requisition.height;
2385       
2386       gtk_widget_size_allocate (clist->hscrollbar, &child_allocation);
2387     }
2388   else
2389     {
2390       if (GTK_WIDGET_VISIBLE (clist->hscrollbar))
2391         gtk_widget_hide (clist->hscrollbar);
2392     }
2393
2394   /* set the vscrollbar adjustments */
2395   adjust_scrollbars (clist);
2396 }
2397
2398 /* 
2399  * GTKCONTAINER
2400  *   gtk_clist_foreach
2401  */
2402 static void
2403 gtk_clist_foreach (GtkContainer * container,
2404                    GtkCallback callback,
2405                    gpointer callback_data)
2406 {
2407   gint i;
2408   GtkCList *clist;
2409
2410   g_return_if_fail (container != NULL);
2411   g_return_if_fail (GTK_IS_CLIST (container));
2412   g_return_if_fail (callback != NULL);
2413
2414   clist = GTK_CLIST (container);
2415
2416   /* callback for the column buttons */
2417   for (i = 0; i < clist->columns; i++)
2418     if (clist->column[i].button)
2419       (*callback) (clist->column[i].button, callback_data);
2420
2421   /* callbacks for the scrollbars */
2422   if (clist->vscrollbar)
2423     (*callback) (clist->vscrollbar, callback_data);
2424   if (clist->hscrollbar)
2425     (*callback) (clist->hscrollbar, callback_data);
2426 }
2427
2428 /*
2429  * DRAWING
2430  *   draw_row
2431  *   draw_rows
2432  */
2433 static void
2434 draw_row (GtkCList * clist,
2435           GdkRectangle * area,
2436           gint row,
2437           GtkCListRow * clist_row)
2438 {
2439   GtkWidget *widget;
2440   GdkGC *fg_gc, *bg_gc;
2441   GdkRectangle row_rectangle, cell_rectangle, clip_rectangle, intersect_rectangle,
2442    *rect;
2443   gint i, offset = 0, width, height, pixmap_width = 0;
2444   gint xsrc, ysrc, xdest, ydest;
2445
2446   g_return_if_fail (clist != NULL);
2447
2448   /* bail now if we arn't drawable yet */
2449   if (!GTK_WIDGET_DRAWABLE (clist))
2450     return;
2451
2452   if (row < 0 || row >= clist->rows)
2453     return;
2454
2455   widget = GTK_WIDGET (clist);
2456
2457   /* if the function is passed the pointer to the row instead of null,
2458    * it avoids this expensive lookup */
2459   if (!clist_row)
2460     clist_row = (g_list_nth (clist->row_list, row))->data;
2461
2462   /* rectangle of the entire row */
2463   row_rectangle.x = 0;
2464   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
2465   row_rectangle.width = clist->clist_window_width;
2466   row_rectangle.height = clist->row_height;
2467
2468   /* rectangle of the cell spacing above the row */
2469   cell_rectangle.x = 0;
2470   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
2471   cell_rectangle.width = row_rectangle.width;
2472   cell_rectangle.height = CELL_SPACING;
2473
2474   /* rectangle used to clip drawing operations, it's y and height
2475    * positions only need to be set once, so we set them once here. 
2476    * the x and width are set withing the drawing loop below once per
2477    * column */
2478   clip_rectangle.y = row_rectangle.y;
2479   clip_rectangle.height = row_rectangle.height;
2480
2481   /* select GC for background rectangle */
2482   if (clist_row->state == GTK_STATE_SELECTED)
2483     {
2484       fg_gc = widget->style->fg_gc[GTK_STATE_SELECTED];
2485       bg_gc = widget->style->bg_gc[GTK_STATE_SELECTED];
2486     }
2487   else
2488     {
2489       if (clist_row->fg_set)
2490         {
2491           gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
2492           fg_gc = clist->fg_gc;
2493         }
2494       else
2495         fg_gc = widget->style->fg_gc[GTK_STATE_NORMAL];
2496         
2497       if (clist_row->bg_set)
2498         {
2499           gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
2500           bg_gc = clist->bg_gc;
2501         }
2502       else
2503         bg_gc = widget->style->bg_gc[GTK_STATE_PRELIGHT];
2504     }
2505
2506   /* draw the cell borders and background */
2507   if (area)
2508     {
2509       if (gdk_rectangle_intersect (area, &cell_rectangle, &intersect_rectangle))
2510         gdk_draw_rectangle (clist->clist_window,
2511                             widget->style->base_gc[GTK_STATE_NORMAL],
2512                             TRUE,
2513                             intersect_rectangle.x,
2514                             intersect_rectangle.y,
2515                             intersect_rectangle.width,
2516                             intersect_rectangle.height);
2517
2518       /* the last row has to clear it's bottom cell spacing too */
2519       if (clist_row == clist->row_list_end->data)
2520         {
2521           cell_rectangle.y += clist->row_height + CELL_SPACING;
2522
2523           if (gdk_rectangle_intersect (area, &cell_rectangle, &intersect_rectangle))
2524             gdk_draw_rectangle (clist->clist_window,
2525                                 widget->style->base_gc[GTK_STATE_NORMAL],
2526                                 TRUE,
2527                                 intersect_rectangle.x,
2528                                 intersect_rectangle.y,
2529                                 intersect_rectangle.width,
2530                                 intersect_rectangle.height);
2531         }
2532
2533       if (!gdk_rectangle_intersect (area, &row_rectangle, &intersect_rectangle))
2534         return;
2535
2536       if (clist_row->state == GTK_STATE_SELECTED || clist_row->fg_set)
2537         gdk_draw_rectangle (clist->clist_window,
2538                             bg_gc,
2539                             TRUE,
2540                             intersect_rectangle.x,
2541                             intersect_rectangle.y,
2542                             intersect_rectangle.width,
2543                             intersect_rectangle.height);
2544       else
2545         gdk_window_clear_area (clist->clist_window,
2546                                intersect_rectangle.x,
2547                                intersect_rectangle.y,
2548                                intersect_rectangle.width,
2549                                intersect_rectangle.height);
2550     }
2551   else
2552     {
2553       gdk_draw_rectangle (clist->clist_window,
2554                           widget->style->base_gc[GTK_STATE_NORMAL],
2555                           TRUE,
2556                           cell_rectangle.x,
2557                           cell_rectangle.y,
2558                           cell_rectangle.width,
2559                           cell_rectangle.height);
2560
2561       /* the last row has to clear it's bottom cell spacing too */
2562       if (clist_row == clist->row_list_end->data)
2563         {
2564           cell_rectangle.y += clist->row_height + CELL_SPACING;
2565
2566           gdk_draw_rectangle (clist->clist_window,
2567                               widget->style->base_gc[GTK_STATE_NORMAL],
2568                               TRUE,
2569                               cell_rectangle.x,
2570                               cell_rectangle.y,
2571                               cell_rectangle.width,
2572                               cell_rectangle.height);     
2573         }         
2574
2575       if (clist_row->state == GTK_STATE_SELECTED || clist_row->fg_set)
2576         gdk_draw_rectangle (clist->clist_window,
2577                             bg_gc,
2578                             TRUE,
2579                             row_rectangle.x,
2580                             row_rectangle.y,
2581                             row_rectangle.width,
2582                             row_rectangle.height);
2583       else
2584         gdk_window_clear_area (clist->clist_window,
2585                                row_rectangle.x,
2586                                row_rectangle.y,
2587                                row_rectangle.width,
2588                                row_rectangle.height);
2589     }
2590
2591   /* iterate and draw all the columns (row cells) and draw their contents */
2592   for (i = 0; i < clist->columns; i++)
2593     {
2594       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
2595       clip_rectangle.width = clist->column[i].area.width;
2596
2597       /* calculate clipping region clipping region */
2598       if (!area)
2599         {
2600           rect = &clip_rectangle;
2601         }
2602       else
2603         {
2604           if (!gdk_rectangle_intersect (area, &clip_rectangle, &intersect_rectangle))
2605             continue;
2606           rect = &intersect_rectangle;
2607         }
2608
2609       /* calculate real width for column justification */
2610       switch (clist_row->cell[i].type)
2611         {
2612         case GTK_CELL_EMPTY:
2613           continue;
2614           break;
2615
2616         case GTK_CELL_TEXT:
2617           width = gdk_string_width (GTK_WIDGET (clist)->style->font,
2618                                     GTK_CELL_TEXT (clist_row->cell[i])->text);
2619           break;
2620
2621         case GTK_CELL_PIXMAP:
2622           gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap, &width, &height);
2623           pixmap_width = width;
2624           break;
2625
2626         case GTK_CELL_PIXTEXT:
2627           gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap, &width, &height);
2628           pixmap_width = width;
2629           width += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
2630           width = gdk_string_width (GTK_WIDGET (clist)->style->font,
2631                                     GTK_CELL_PIXTEXT (clist_row->cell[i])->text);
2632           break;
2633
2634         case GTK_CELL_WIDGET:
2635           /* unimplimented */
2636           continue;
2637           break;
2638
2639         default:
2640           continue;
2641           break;
2642         }
2643
2644       switch (clist->column[i].justification)
2645         {
2646         case GTK_JUSTIFY_LEFT:
2647           offset = clip_rectangle.x;
2648           break;
2649
2650         case GTK_JUSTIFY_RIGHT:
2651           offset = (clip_rectangle.x + clip_rectangle.width) - width;
2652           break;
2653
2654         case GTK_JUSTIFY_CENTER:
2655           offset = (clip_rectangle.x + (clip_rectangle.width / 2)) - (width / 2);
2656           break;
2657
2658         case GTK_JUSTIFY_FILL:
2659           offset = (clip_rectangle.x + (clip_rectangle.width / 2)) - (width / 2);
2660           break;
2661
2662         default:
2663           offset = 0;
2664           break;
2665         };
2666
2667       /* Draw Text or Pixmap */
2668       switch (clist_row->cell[i].type)
2669         {
2670         case GTK_CELL_EMPTY:
2671           continue;
2672           break;
2673
2674         case GTK_CELL_TEXT:
2675           gdk_gc_set_clip_rectangle (fg_gc, rect);
2676
2677           gdk_draw_string (clist->clist_window, 
2678                            widget->style->font,
2679                            fg_gc,
2680                            offset + clist_row->cell[i].horizontal,
2681                            row_rectangle.y + clist->row_center_offset + 
2682                            clist_row->cell[i].vertical,
2683                            GTK_CELL_TEXT (clist_row->cell[i])->text);
2684
2685           gdk_gc_set_clip_rectangle (fg_gc, NULL);
2686           break;
2687
2688         case GTK_CELL_PIXMAP:
2689           xsrc = 0;
2690           ysrc = 0;
2691           xdest = offset + clist_row->cell[i].horizontal;
2692           ydest = (clip_rectangle.y + (clip_rectangle.height / 2)) - height / 2 +
2693             clist_row->cell[i].vertical;
2694
2695           if (GTK_CELL_PIXMAP (clist_row->cell[i])->mask)
2696           {
2697               gdk_gc_set_clip_mask (fg_gc, GTK_CELL_PIXMAP (clist_row->cell[i])->mask);
2698               gdk_gc_set_clip_origin (fg_gc, xdest, ydest);
2699           }
2700           gdk_draw_pixmap (clist->clist_window,
2701                            fg_gc,
2702                            GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
2703                            xsrc, ysrc,
2704                            xdest,
2705                            ydest,
2706                            pixmap_width, height);
2707
2708           if (GTK_CELL_PIXMAP (clist_row->cell[i])->mask)
2709           {
2710               gdk_gc_set_clip_origin (fg_gc, 0, 0);
2711               gdk_gc_set_clip_mask (fg_gc, NULL);
2712           }
2713           break;
2714
2715         case GTK_CELL_PIXTEXT:
2716           /* draw the pixmap */
2717           xsrc = 0;
2718           ysrc = 0;
2719           xdest = offset + clist_row->cell[i].horizontal;
2720           ydest = (clip_rectangle.y + (clip_rectangle.height / 2)) - height / 2 +
2721             clist_row->cell[i].vertical;
2722
2723           if (GTK_CELL_PIXTEXT (clist_row->cell[i])->mask)
2724           {
2725               gdk_gc_set_clip_mask (fg_gc, GTK_CELL_PIXTEXT (clist_row->cell[i])->mask);
2726               gdk_gc_set_clip_origin (fg_gc, xdest, ydest);
2727           }
2728               
2729           gdk_draw_pixmap (clist->clist_window,
2730                            fg_gc,
2731                            GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
2732                            xsrc, ysrc,
2733                            xdest,
2734                            ydest,
2735                            pixmap_width, height);
2736
2737           gdk_gc_set_clip_origin (fg_gc, 0, 0);
2738
2739           offset += pixmap_width + GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
2740           
2741           /* draw the string */
2742           gdk_gc_set_clip_rectangle (fg_gc, rect);
2743
2744           gdk_draw_string (clist->clist_window, 
2745                            widget->style->font,
2746                            fg_gc,
2747                            offset + clist_row->cell[i].horizontal,
2748                            row_rectangle.y + clist->row_center_offset + 
2749                            clist_row->cell[i].vertical,
2750                            GTK_CELL_PIXTEXT (clist_row->cell[i])->text);
2751
2752           gdk_gc_set_clip_rectangle (fg_gc, NULL);
2753
2754           break;
2755
2756         case GTK_CELL_WIDGET:
2757           /* unimplimented */
2758           continue;
2759           break;
2760
2761         default:
2762           continue;
2763           break;
2764         }
2765     }
2766 }
2767
2768 static void
2769 draw_rows (GtkCList * clist,
2770            GdkRectangle * area)
2771 {
2772   GList *list;
2773   GtkCListRow *clist_row;
2774   int i, first_row, last_row;
2775
2776   g_return_if_fail (clist != NULL);
2777   g_return_if_fail (GTK_IS_CLIST (clist));
2778
2779   if (clist->row_height == 0 ||
2780       !GTK_WIDGET_DRAWABLE (clist))
2781     return;
2782
2783   if (area)
2784     {
2785       first_row = ROW_FROM_YPIXEL (clist, area->y);
2786       last_row = ROW_FROM_YPIXEL (clist, area->y + area->height);
2787     }
2788   else
2789     {
2790       first_row = ROW_FROM_YPIXEL (clist, 0);
2791       last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
2792     }
2793
2794   /* this is a small special case which exposes the bottom cell line
2795    * on the last row -- it might go away if I change the wall the cell spacings
2796    * are drawn */
2797   if (clist->rows == first_row)
2798     first_row--;
2799
2800   list = g_list_nth (clist->row_list, first_row);
2801   i = first_row;
2802   while (list)
2803     {
2804       clist_row = list->data;
2805       list = list->next;
2806
2807       if (i > last_row)
2808         return;
2809
2810       (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
2811         (clist, area, i, clist_row);
2812       i++;
2813     }
2814
2815   if (!area)
2816     gdk_window_clear_area (clist->clist_window, 0, ROW_TOP_YPIXEL (clist, i), -1, -1);
2817 }
2818
2819 /*
2820  * SIZE ALLOCATION
2821  *   size_allocate_title_buttons
2822  *   size_allocate_columns
2823  */
2824 static void
2825 size_allocate_title_buttons (GtkCList * clist)
2826 {
2827   gint i, last_button = 0;
2828   GtkAllocation button_allocation;
2829
2830   if (!GTK_WIDGET_REALIZED (clist))
2831     return;
2832
2833   button_allocation.x = clist->hoffset;
2834   button_allocation.y = 0;
2835   button_allocation.width = 0;
2836   button_allocation.height = clist->column_title_area.height;
2837
2838   for (i = 0; i < clist->columns; i++)
2839     {
2840       button_allocation.width += clist->column[i].area.width;
2841
2842       if (i == clist->columns - 1)
2843         button_allocation.width += 2 * (CELL_SPACING + COLUMN_INSET);
2844       else
2845         button_allocation.width += CELL_SPACING + (2 * COLUMN_INSET);
2846
2847       if (i == (clist->columns - 1) || clist->column[i + 1].button)
2848         {
2849           gtk_widget_size_allocate (clist->column[last_button].button, &button_allocation);
2850           button_allocation.x += button_allocation.width;
2851           button_allocation.width = 0;
2852
2853           gdk_window_show (clist->column[last_button].window);
2854           gdk_window_move_resize (clist->column[last_button].window,
2855                                   button_allocation.x - (DRAG_WIDTH / 2), 
2856                                   0, DRAG_WIDTH, clist->column_title_area.height);
2857           
2858           last_button = i + 1;
2859         }
2860       else
2861         {
2862           gdk_window_hide (clist->column[i].window);
2863         }
2864     }
2865 }
2866
2867 static void
2868 size_allocate_columns (GtkCList * clist)
2869 {
2870   gint i, xoffset = 0;
2871
2872   for (i = 0; i < clist->columns; i++)
2873     {
2874       clist->column[i].area.x = xoffset + CELL_SPACING + COLUMN_INSET;
2875
2876       if (i == clist->columns - 1)
2877         {
2878           gint width;
2879
2880           if (clist->column[i].width_set)
2881             {
2882               width = clist->column[i].width;
2883             }
2884           else
2885             {
2886               if (clist->column[i].title)
2887                 width = gdk_string_width (GTK_WIDGET (clist)->style->font, 
2888                                           clist->column[i].title);
2889               else
2890                 width = 0;
2891             }
2892
2893           clist->column[i].area.width = MAX (width,
2894                                              clist->clist_window_width -
2895                                              xoffset - (2 * (CELL_SPACING + COLUMN_INSET)));
2896                                             
2897         }
2898       else
2899         {
2900           clist->column[i].area.width = clist->column[i].width;
2901         }
2902
2903       xoffset += clist->column[i].area.width + CELL_SPACING + (2 * COLUMN_INSET);
2904     }
2905 }
2906
2907 /*
2908  * SELECTION
2909  *   select_row
2910  *   real_select_row
2911  *   real_unselect_row
2912  *   get_selection_info
2913  */
2914 static void
2915 toggle_row (GtkCList * clist,
2916             gint row,
2917             gint column,
2918             GdkEventButton * event)
2919 {
2920   gint i;
2921   GList *list;
2922   GtkCListRow *clist_row, *selected_row;
2923
2924   i = 0;
2925   list = clist->row_list;
2926   selected_row = NULL;
2927
2928   switch (clist->selection_mode)
2929     {
2930     case GTK_SELECTION_SINGLE:
2931       while (list)
2932         {
2933           clist_row = list->data;
2934           list = list->next;
2935
2936           if (row == i)
2937             selected_row = clist_row;
2938           else if (clist_row->state == GTK_STATE_SELECTED)
2939             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
2940                              i, column, event);
2941
2942           i++;
2943         }
2944
2945       if (selected_row && selected_row->state == GTK_STATE_SELECTED)
2946         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
2947                          row, column, event);
2948       else
2949         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
2950                          row, column, event);
2951       break;
2952
2953
2954     case GTK_SELECTION_BROWSE:
2955       while (list)
2956         {
2957           clist_row = list->data;
2958           list = list->next;
2959
2960           if (i != row && clist_row->state == GTK_STATE_SELECTED)
2961             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
2962                              i, column, event);
2963           i++;
2964         }
2965
2966       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
2967                        row, column, event);
2968       break;
2969
2970
2971     case GTK_SELECTION_MULTIPLE:
2972       clist_row = (g_list_nth (clist->row_list, row))->data;
2973
2974       if (clist_row->state == GTK_STATE_SELECTED)
2975         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
2976                          row, column, event);
2977       else
2978         gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
2979                          row, column, event);
2980       break;
2981
2982
2983     case GTK_SELECTION_EXTENDED:
2984       break;
2985
2986     default:
2987       break;
2988     }
2989 }
2990
2991 static void
2992 select_row (GtkCList * clist,
2993             gint row,
2994             gint column,
2995             GdkEventButton * event)
2996 {
2997   gint i;
2998   GList *list;
2999   GtkCListRow *clist_row;
3000
3001   switch (clist->selection_mode)
3002     {
3003     case GTK_SELECTION_SINGLE:
3004     case GTK_SELECTION_BROWSE:
3005       i = 0;
3006       list = clist->row_list;
3007       while (list)
3008         {
3009           clist_row = list->data;
3010           list = list->next;
3011
3012           if (row != i && clist_row->state == GTK_STATE_SELECTED)
3013             gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3014                              i, column, event);
3015
3016           i++;
3017         }
3018
3019       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
3020                        row, column, event);
3021       break;
3022
3023     case GTK_SELECTION_MULTIPLE:
3024       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW], 
3025                        row, column, event);
3026
3027       break;
3028
3029     case GTK_SELECTION_EXTENDED:
3030       break;
3031
3032     default:
3033       break;
3034     }
3035 }
3036
3037 static void
3038 unselect_row (GtkCList * clist,
3039               gint row,
3040               gint column,
3041               GdkEventButton * event)
3042 {
3043   switch (clist->selection_mode)
3044     {
3045     case GTK_SELECTION_SINGLE:
3046     case GTK_SELECTION_BROWSE:
3047     case GTK_SELECTION_MULTIPLE:
3048       gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW], 
3049                        row, column, event);
3050       break;
3051
3052     case GTK_SELECTION_EXTENDED:
3053       break;
3054
3055     default:
3056       break;
3057     }
3058 }
3059
3060 static void
3061 real_select_row (GtkCList * clist,
3062                  gint row,
3063                  gint column,
3064                  GdkEventButton * event)
3065 {
3066   GtkCListRow *clist_row;
3067
3068   g_return_if_fail (clist != NULL);
3069
3070   if (row < 0 || row > (clist->rows - 1))
3071     return;
3072
3073   clist_row = (g_list_nth (clist->row_list, row))->data;
3074
3075   if (clist_row->state == GTK_STATE_NORMAL)
3076     {
3077       clist_row->state = GTK_STATE_SELECTED;
3078       clist->selection = g_list_append (clist->selection, (gpointer) row);
3079
3080       if (!GTK_CLIST_FROZEN (clist)
3081           && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3082         (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
3083           (clist, NULL, row, clist_row);
3084     }
3085 }
3086
3087 static void
3088 real_unselect_row (GtkCList * clist,
3089                    gint row,
3090                    gint column,
3091                    GdkEventButton * event)
3092 {
3093   GtkCListRow *clist_row;
3094
3095   g_return_if_fail (clist != NULL);
3096
3097   if (row < 0 || row > (clist->rows - 1))
3098     return;
3099
3100   clist_row = (g_list_nth (clist->row_list, row))->data;
3101
3102   if (clist_row->state == GTK_STATE_SELECTED)
3103     {
3104       clist_row->state = GTK_STATE_NORMAL;
3105       clist->selection = g_list_remove (clist->selection, (gpointer) row);
3106
3107       if (!GTK_CLIST_FROZEN (clist)
3108           && (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
3109         (GTK_CLIST_CLASS (GTK_OBJECT (clist)->klass)->draw_row)
3110           (clist, NULL, row, clist_row);
3111     }
3112 }
3113
3114 static gint
3115 get_selection_info (GtkCList * clist,
3116                     gint x,
3117                     gint y,
3118                     gint * row,
3119                     gint * column)
3120 {
3121   gint trow, tcol;
3122
3123   g_return_val_if_fail (clist != NULL, 0);
3124
3125   /* bounds checking, return false if the user clicked 
3126    * on a blank area */
3127   trow = ROW_FROM_YPIXEL (clist, y);
3128   if (trow >= clist->rows)
3129     return 0;
3130
3131   if (row)
3132     *row = trow;
3133
3134   tcol = COLUMN_FROM_XPIXEL (clist, x);
3135   if (tcol >= clist->columns)
3136     return 0;
3137
3138   if (column)
3139     *column = tcol;
3140
3141   return 1;
3142 }
3143
3144 gint
3145 gtk_clist_get_selection_info (GtkCList *clist, 
3146                               gint      x, 
3147                               gint      y, 
3148                               gint *    row, 
3149                               gint *    column)
3150 {
3151   g_return_val_if_fail (clist != NULL, 0);
3152   g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
3153   return get_selection_info (clist, x, y, row, column);
3154 }
3155
3156 /* 
3157  * RESIZE COLUMNS
3158  *   draw_xor_line
3159  *   new_column_width
3160  *   resize_column
3161  */
3162 static void                          
3163 draw_xor_line (GtkCList * clist)
3164 {
3165   GtkWidget *widget;
3166   
3167   g_return_if_fail (clist != NULL);
3168   
3169   widget = GTK_WIDGET (clist);
3170
3171   gdk_draw_line (widget->window, clist->xor_gc,  
3172                  clist->x_drag,                                       
3173                  widget->style->klass->ythickness,                               
3174                  clist->x_drag,                                             
3175                  clist->column_title_area.height + clist->clist_window_height + 1);
3176 }
3177
3178 /* this function returns the new width of the column being resized given
3179  * the column and x position of the cursor; the x cursor position is passed
3180  * in as a pointer and automagicly corrected if it's beyond min/max limits */
3181 static gint
3182 new_column_width (GtkCList * clist,
3183                   gint column,
3184                   gint * x,
3185                   gint * visible)
3186 {
3187   gint cx, rx, width;
3188
3189   cx = *x;
3190
3191   /* first translate the x position from widget->window
3192    * to clist->clist_window */
3193   cx -= GTK_WIDGET (clist)->style->klass->xthickness;
3194
3195   /* rx is x from the list beginning */
3196   rx = cx - clist->hoffset;
3197
3198   /* you can't shrink a column to less than its minimum width */
3199   if (cx < (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET + COLUMN_MIN_WIDTH))
3200     {
3201       *x = cx = COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET + COLUMN_MIN_WIDTH +
3202         GTK_WIDGET (clist)->style->klass->xthickness;
3203       cx -= GTK_WIDGET (clist)->style->klass->xthickness;
3204       rx = cx - clist->hoffset;
3205     }
3206
3207   if (cx > clist->clist_window_width)
3208     *visible = 0;
3209   else
3210     *visible = 1;
3211
3212   /* calculate new column width making sure it doesn't end up
3213    * less than the minimum width */
3214   width = (rx - COLUMN_LEFT (clist, column)) - COLUMN_INSET -
3215     ((clist->columns == (column - 1)) ? CELL_SPACING : 0);
3216   if (width < COLUMN_MIN_WIDTH)
3217     width = COLUMN_MIN_WIDTH;
3218
3219   return width;
3220 }
3221
3222 /* this will do more later */
3223 static void
3224 resize_column (GtkCList * clist,
3225                gint column,
3226                gint width)
3227 {
3228   gtk_clist_set_column_width (clist, column, width);
3229 }
3230
3231 /* BUTTONS */
3232 static void
3233 column_button_create (GtkCList * clist,
3234                       gint column)
3235 {
3236   GtkWidget *button;
3237
3238   button = clist->column[column].button = gtk_button_new ();
3239   gtk_widget_set_parent (button, GTK_WIDGET (clist));
3240   if (GTK_WIDGET_REALIZED (clist) && clist->title_window)
3241     gtk_widget_set_parent_window (clist->column[column].button, clist->title_window);
3242   
3243   gtk_signal_connect (GTK_OBJECT (button),
3244                       "clicked",
3245                       (GtkSignalFunc) column_button_clicked,
3246                       (gpointer) clist);
3247
3248   gtk_widget_show (button);
3249 }
3250
3251 static void
3252 column_button_clicked (GtkWidget * widget,
3253                        gpointer data)
3254 {
3255   gint i;
3256   GtkCList *clist;
3257
3258   g_return_if_fail (widget != NULL);
3259   g_return_if_fail (GTK_IS_CLIST (data));
3260
3261   clist = GTK_CLIST (data);
3262
3263   /* find the column who's button was pressed */
3264   for (i = 0; i < clist->columns; i++)
3265     if (clist->column[i].button == widget)
3266       break;
3267
3268   gtk_signal_emit (GTK_OBJECT (clist), clist_signals[CLICK_COLUMN], i);
3269 }
3270
3271 /* 
3272  * SCROLLBARS
3273  *
3274  * functions:
3275  *   create_scrollbars
3276  *   adjust_scrollbars
3277  *   vadjustment_changed
3278  *   hadjustment_changed
3279  *   vadjustment_value_changed
3280  *   hadjustment_value_changed 
3281  */
3282 static void
3283 create_scrollbars (GtkCList * clist)
3284 {
3285   GtkAdjustment *adjustment;
3286
3287   clist->vscrollbar = gtk_vscrollbar_new (NULL);
3288   adjustment = gtk_range_get_adjustment (GTK_RANGE (clist->vscrollbar));
3289
3290   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
3291                       (GtkSignalFunc) vadjustment_changed,
3292                       (gpointer) clist);
3293
3294   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
3295                       (GtkSignalFunc) vadjustment_value_changed,
3296                       (gpointer) clist);
3297
3298   gtk_widget_set_parent (clist->vscrollbar, GTK_WIDGET (clist));
3299   gtk_widget_show (clist->vscrollbar);
3300
3301   clist->hscrollbar = gtk_hscrollbar_new (NULL);
3302   adjustment = gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar));
3303
3304   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
3305                       (GtkSignalFunc) hadjustment_changed,
3306                       (gpointer) clist);
3307
3308   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
3309                       (GtkSignalFunc) hadjustment_value_changed,
3310                       (gpointer) clist);
3311
3312   gtk_widget_set_parent (clist->hscrollbar, GTK_WIDGET (clist));
3313   gtk_widget_show (clist->hscrollbar);
3314 }
3315
3316 static void
3317 adjust_scrollbars (GtkCList * clist)
3318 {
3319   GTK_RANGE (clist->vscrollbar)->adjustment->page_size = clist->clist_window_height;
3320   GTK_RANGE (clist->vscrollbar)->adjustment->page_increment = clist->clist_window_height / 2;
3321   GTK_RANGE (clist->vscrollbar)->adjustment->step_increment = 10;
3322   GTK_RANGE (clist->vscrollbar)->adjustment->lower = 0;
3323   GTK_RANGE (clist->vscrollbar)->adjustment->upper = LIST_HEIGHT (clist);
3324
3325   if (clist->clist_window_height - clist->voffset > LIST_HEIGHT (clist))
3326     {
3327       GTK_RANGE (clist->vscrollbar)->adjustment->value = MAX (0, LIST_HEIGHT (clist) - 
3328         clist->clist_window_height);
3329       gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), 
3330                                "value_changed");
3331     }
3332
3333   GTK_RANGE (clist->hscrollbar)->adjustment->page_size = clist->clist_window_width;
3334   GTK_RANGE (clist->hscrollbar)->adjustment->page_increment = clist->clist_window_width / 2;
3335   GTK_RANGE (clist->hscrollbar)->adjustment->step_increment = 10;
3336   GTK_RANGE (clist->hscrollbar)->adjustment->lower = 0;
3337   GTK_RANGE (clist->hscrollbar)->adjustment->upper = LIST_WIDTH (clist);
3338
3339   if (clist->clist_window_width - clist->hoffset > LIST_WIDTH (clist))
3340     {
3341       GTK_RANGE (clist->hscrollbar)->adjustment->value = MAX (0, LIST_WIDTH (clist) - 
3342         clist->clist_window_width);
3343       gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->hscrollbar)->adjustment), 
3344                                "value_changed");
3345     }
3346
3347   if (LIST_HEIGHT (clist) <= clist->clist_window_height &&
3348       clist->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
3349     {
3350       if (GTK_WIDGET_VISIBLE (clist->vscrollbar))
3351         {
3352           gtk_widget_hide (clist->vscrollbar);
3353           gtk_widget_queue_resize (GTK_WIDGET (clist));
3354         }
3355     }
3356   else
3357     {
3358       if (!GTK_WIDGET_VISIBLE (clist->vscrollbar))
3359         {
3360           gtk_widget_show (clist->vscrollbar);
3361           gtk_widget_queue_resize (GTK_WIDGET (clist));
3362         }
3363     }
3364
3365   if (LIST_WIDTH (clist) <= clist->clist_window_width &&
3366       clist->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
3367     {
3368       if (GTK_WIDGET_VISIBLE (clist->hscrollbar))
3369         {
3370           gtk_widget_hide (clist->hscrollbar);
3371           gtk_widget_queue_resize (GTK_WIDGET (clist));
3372         }
3373     }
3374   else
3375     {
3376       if (!GTK_WIDGET_VISIBLE (clist->hscrollbar))
3377         {
3378           gtk_widget_show (clist->hscrollbar);
3379           gtk_widget_queue_resize (GTK_WIDGET (clist));
3380         }
3381     }
3382
3383   gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->vscrollbar)->adjustment), "changed");
3384   gtk_signal_emit_by_name (GTK_OBJECT (GTK_RANGE (clist->hscrollbar)->adjustment), "changed");
3385 }
3386
3387 static void
3388 vadjustment_changed (GtkAdjustment * adjustment,
3389                                gpointer data)
3390 {
3391   GtkCList *clist;
3392
3393   g_return_if_fail (adjustment != NULL);
3394   g_return_if_fail (data != NULL);
3395
3396   clist = GTK_CLIST (data);
3397 }
3398
3399 static void
3400 hadjustment_changed (GtkAdjustment * adjustment,
3401                                gpointer data)
3402 {
3403   GtkCList *clist;
3404
3405   g_return_if_fail (adjustment != NULL);
3406   g_return_if_fail (data != NULL);
3407
3408   clist = GTK_CLIST (data);
3409 }
3410
3411 static void
3412 check_exposures (GtkCList *clist)
3413 {
3414   GdkEvent *event;
3415
3416   if (!GTK_WIDGET_REALIZED (clist))
3417     return;
3418
3419   /* Make sure graphics expose events are processed before scrolling
3420    * again */
3421   while ((event = gdk_event_get_graphics_expose (clist->clist_window)) != NULL)
3422     {
3423       gtk_widget_event (GTK_WIDGET (clist), event);
3424       if (event->expose.count == 0)
3425         {
3426           gdk_event_free (event);
3427           break;
3428         }
3429       gdk_event_free (event);
3430     }
3431 }
3432
3433 static void
3434 vadjustment_value_changed (GtkAdjustment * adjustment,
3435                                      gpointer data)
3436 {
3437   GtkCList *clist;
3438   GdkRectangle area;
3439   gint diff, value;
3440
3441   g_return_if_fail (adjustment != NULL);
3442   g_return_if_fail (data != NULL);
3443   g_return_if_fail (GTK_IS_CLIST (data));
3444
3445   clist = GTK_CLIST (data);
3446
3447   if (!GTK_WIDGET_DRAWABLE (clist))
3448     return;
3449
3450   value = adjustment->value;
3451
3452   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (clist->vscrollbar)))
3453     {
3454       if (value > -clist->voffset)
3455         {
3456           /* scroll down */
3457           diff = value + clist->voffset;
3458
3459           /* we have to re-draw the whole screen here... */
3460           if (diff >= clist->clist_window_height)
3461             {
3462               clist->voffset = -value;
3463               draw_rows (clist, NULL);
3464               return;
3465             }
3466
3467           if ((diff != 0) && (diff != clist->clist_window_height))
3468             gdk_window_copy_area (clist->clist_window,
3469                                   clist->fg_gc,
3470                                   0, 0,
3471                                   clist->clist_window,
3472                                   0,
3473                                   diff,
3474                                   clist->clist_window_width,
3475                                   clist->clist_window_height - diff);
3476
3477           area.x = 0;
3478           area.y = clist->clist_window_height - diff;
3479           area.width = clist->clist_window_width;
3480           area.height = diff;
3481         }
3482       else
3483         {
3484           /* scroll up */
3485           diff = -clist->voffset - value;
3486
3487           /* we have to re-draw the whole screen here... */
3488           if (diff >= clist->clist_window_height)
3489             {
3490               clist->voffset = -value;
3491               draw_rows (clist, NULL);
3492               return;
3493             }
3494
3495           if ((diff != 0) && (diff != clist->clist_window_height))
3496             gdk_window_copy_area (clist->clist_window,
3497                                   clist->fg_gc,
3498                                   0, diff,
3499                                   clist->clist_window,
3500                                   0,
3501                                   0,
3502                                   clist->clist_window_width,
3503                                   clist->clist_window_height - diff);
3504
3505           area.x = 0;
3506           area.y = 0;
3507           area.width = clist->clist_window_width;
3508           area.height = diff;
3509
3510         }
3511
3512       clist->voffset = -value;
3513       if ((diff != 0) && (diff != clist->clist_window_height))
3514         check_exposures (clist);
3515     }
3516
3517   draw_rows (clist, &area);
3518 }
3519
3520 static void
3521 hadjustment_value_changed (GtkAdjustment * adjustment,
3522                                      gpointer data)
3523 {
3524   GtkCList *clist;
3525   GdkRectangle area;
3526   gint i, diff, value;
3527
3528   g_return_if_fail (adjustment != NULL);
3529   g_return_if_fail (data != NULL);
3530   g_return_if_fail (GTK_IS_CLIST (data));
3531
3532   clist = GTK_CLIST (data);
3533
3534   if (!GTK_WIDGET_DRAWABLE (clist))
3535     return;
3536
3537   value = adjustment->value;
3538
3539   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (clist->hscrollbar)))
3540     {
3541       /* move the column buttons and resize windows */
3542       for (i = 0; i < clist->columns; i++)
3543         {
3544           if (clist->column[i].button)
3545             {
3546               clist->column[i].button->allocation.x -= value + clist->hoffset;
3547
3548               if (clist->column[i].button->window)
3549                 {
3550                   gdk_window_move (clist->column[i].button->window,
3551                                    clist->column[i].button->allocation.x,
3552                                    clist->column[i].button->allocation.y);
3553
3554                   if (clist->column[i].window)
3555                     gdk_window_move (clist->column[i].window,
3556                                      clist->column[i].button->allocation.x +
3557                                      clist->column[i].button->allocation.width - 
3558                                      (DRAG_WIDTH / 2), 0); 
3559                 }
3560             }
3561         }
3562
3563       if (value > -clist->hoffset)
3564         {
3565           /* scroll right */
3566           diff = value + clist->hoffset;
3567
3568           /* we have to re-draw the whole screen here... */
3569           if (diff >= clist->clist_window_width)
3570             {
3571               clist->hoffset = -value;
3572               draw_rows (clist, NULL);
3573               return;
3574             }
3575
3576           if ((diff != 0) && (diff != clist->clist_window_width))
3577             gdk_window_copy_area (clist->clist_window,
3578                                   clist->fg_gc,
3579                                   0, 0,
3580                                   clist->clist_window,
3581                                   diff,
3582                                   0,
3583                                   clist->clist_window_width - diff,
3584                                   clist->clist_window_height);
3585
3586           area.x = clist->clist_window_width - diff;
3587           area.y = 0;
3588           area.width = diff;
3589           area.height = clist->clist_window_height;
3590         }
3591       else
3592         {
3593           /* scroll left */
3594           diff = -clist->hoffset - value;
3595
3596           /* we have to re-draw the whole screen here... */
3597           if (diff >= clist->clist_window_width)
3598             {
3599               clist->hoffset = -value;
3600               draw_rows (clist, NULL);
3601               return;
3602             }
3603
3604           if ((diff != 0) && (diff != clist->clist_window_width))
3605             gdk_window_copy_area (clist->clist_window,
3606                                   clist->fg_gc,
3607                                   diff, 0,
3608                                   clist->clist_window,
3609                                   0,
3610                                   0,
3611                                   clist->clist_window_width - diff,
3612                                   clist->clist_window_height);
3613
3614           area.x = 0;
3615           area.y = 0;
3616           area.width = diff;
3617           area.height = clist->clist_window_height;
3618         }
3619
3620       clist->hoffset = -value;
3621       if ((diff != 0) && (diff != clist->clist_window_width))
3622         check_exposures (clist);
3623     }
3624
3625   draw_rows (clist, &area);
3626 }
3627
3628 /* 
3629  * Memory Allocation/Distruction Routines for GtkCList stuctures
3630  *
3631  * functions:
3632  *   columns_new
3633  *   column_title_new
3634  *   columns_delete
3635  *   row_new
3636  *   row_delete
3637  *   cell_empty
3638  *   cell_set_text
3639  *   cell_set_pixmap 
3640  */
3641 static GtkCListColumn *
3642 columns_new (GtkCList * clist)
3643 {
3644   gint i;
3645   GtkCListColumn *column;
3646
3647   column = g_new (GtkCListColumn, clist->columns);
3648
3649   for (i = 0; i < clist->columns; i++)
3650     {
3651       column[i].area.x = 0;
3652       column[i].area.y = 0;
3653       column[i].area.width = 0;
3654       column[i].area.height = 0;
3655       column[i].title = NULL;
3656       column[i].button = NULL;
3657       column[i].window = NULL;
3658       column[i].width = 0;
3659       column[i].width_set = FALSE;
3660       column[i].justification = GTK_JUSTIFY_LEFT;
3661     }
3662
3663   return column;
3664 }
3665
3666 static void
3667 column_title_new (GtkCList * clist,
3668                   gint column,
3669                   gchar * title)
3670 {
3671   if (clist->column[column].title)
3672     g_free (clist->column[column].title);
3673
3674   clist->column[column].title = g_strdup (title);
3675 }
3676
3677 static void
3678 columns_delete (GtkCList * clist)
3679 {
3680   gint i;
3681
3682   for (i = 0; i < clist->columns; i++)
3683     if (clist->column[i].title)
3684       g_free (clist->column[i].title);
3685       
3686   g_free (clist->column);
3687 }
3688
3689 static GtkCListRow *
3690 row_new (GtkCList * clist)
3691 {
3692   int i;
3693   GtkCListRow *clist_row;
3694
3695   clist_row = g_chunk_new (GtkCListRow, clist->row_mem_chunk);
3696   clist_row->cell = g_chunk_new (GtkCell, clist->cell_mem_chunk);
3697
3698   for (i = 0; i < clist->columns; i++)
3699     {
3700       clist_row->cell[i].type = GTK_CELL_EMPTY;
3701       clist_row->cell[i].vertical = 0;
3702       clist_row->cell[i].horizontal = 0;
3703     }
3704
3705   clist_row->fg_set = FALSE;
3706   clist_row->bg_set = FALSE;
3707   clist_row->state = GTK_STATE_NORMAL;
3708   clist_row->data = NULL;
3709   clist_row->destroy = NULL;
3710
3711   return clist_row;
3712 }
3713
3714 static void
3715 row_delete (GtkCList * clist,
3716             GtkCListRow * clist_row)
3717 {
3718   gint i;
3719
3720   for (i = 0; i < clist->columns; i++)
3721     cell_empty (clist, clist_row, i);
3722
3723   if (clist_row->destroy)
3724     clist_row->destroy (clist_row->data);
3725
3726   g_mem_chunk_free (clist->cell_mem_chunk, clist_row->cell);
3727   g_mem_chunk_free (clist->row_mem_chunk, clist_row);
3728 }
3729
3730 static void
3731 cell_empty (GtkCList * clist,
3732             GtkCListRow * clist_row,
3733             gint column)
3734 {
3735   switch (clist_row->cell[column].type)
3736     {
3737     case GTK_CELL_EMPTY:
3738       break;
3739       
3740     case GTK_CELL_TEXT:
3741       g_free (GTK_CELL_TEXT (clist_row->cell[column])->text);
3742       break;
3743       
3744     case GTK_CELL_PIXMAP:
3745       gdk_pixmap_unref (GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap);
3746       if (GTK_CELL_PIXMAP (clist_row->cell[column])->mask)
3747           gdk_bitmap_unref (GTK_CELL_PIXMAP (clist_row->cell[column])->mask);
3748       break;
3749       
3750     case GTK_CELL_PIXTEXT:
3751       g_free (GTK_CELL_PIXTEXT (clist_row->cell[column])->text);
3752       gdk_pixmap_unref (GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap);
3753       if (GTK_CELL_PIXTEXT (clist_row->cell[column])->mask)
3754           gdk_bitmap_unref (GTK_CELL_PIXTEXT (clist_row->cell[column])->mask);
3755       break;
3756
3757     case GTK_CELL_WIDGET:
3758       /* unimplimented */
3759       break;
3760       
3761     default:
3762       break;
3763     }
3764
3765   clist_row->cell[column].type = GTK_CELL_EMPTY;
3766 }
3767
3768 static void
3769 cell_set_text (GtkCList * clist,
3770                GtkCListRow * clist_row,
3771                gint column,
3772                gchar * text)
3773 {
3774   cell_empty (clist, clist_row, column);
3775
3776   if (text)
3777     {
3778       clist_row->cell[column].type = GTK_CELL_TEXT;
3779       GTK_CELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
3780     }
3781 }
3782
3783 static void
3784 cell_set_pixmap (GtkCList * clist,
3785                  GtkCListRow * clist_row,
3786                  gint column,
3787                  GdkPixmap * pixmap,
3788                  GdkBitmap * mask)
3789 {
3790   cell_empty (clist, clist_row, column);
3791
3792   if (pixmap)
3793     {
3794       clist_row->cell[column].type = GTK_CELL_PIXMAP;
3795       GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap = pixmap;
3796       /* We set the mask even if it is NULL */
3797       GTK_CELL_PIXMAP (clist_row->cell[column])->mask = mask;
3798     }
3799 }
3800
3801 static void
3802 cell_set_pixtext (GtkCList * clist,
3803                   GtkCListRow * clist_row,
3804                   gint column,
3805                   gchar * text,
3806                   guint8 spacing,
3807                   GdkPixmap * pixmap,
3808                   GdkBitmap * mask)
3809 {
3810   cell_empty (clist, clist_row, column);
3811
3812   if (text && pixmap)
3813     {
3814       clist_row->cell[column].type = GTK_CELL_PIXTEXT;
3815       GTK_CELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
3816       GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
3817       GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap = pixmap;
3818       GTK_CELL_PIXTEXT (clist_row->cell[column])->mask = mask;
3819     }
3820 }
3821
3822 /* Fill in data after widget has correct style */
3823
3824 static void 
3825 add_style_data (GtkCList * clist)
3826 {
3827   GtkWidget *widget;
3828
3829   widget = GTK_WIDGET(clist);
3830
3831   /* text properties */
3832   if (!GTK_CLIST_ROW_HEIGHT_SET (clist))
3833     {
3834       clist->row_height = widget->style->font->ascent + widget->style->font->descent + 1;
3835       clist->row_center_offset = widget->style->font->ascent + 1.5;
3836     }
3837   else
3838     {
3839       gint text_height;
3840       text_height = clist->row_height - (GTK_WIDGET (clist)->style->font->ascent +
3841                           GTK_WIDGET (clist) ->style->font->descent + 1);
3842       clist->row_center_offset = (text_height / 2) + GTK_WIDGET (clist)->style->font->ascent + 1.5;
3843     }
3844
3845   /* Column widths */
3846 }