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