]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailclist.c
8639ca1e038ce8628a885f107d97223135ad89a5
[~andy/gtk] / modules / other / gail / gailclist.c
1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 2001 Sun Microsystems Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <gtk/gtk.h>
21 #include <stdio.h>
22 #include "gailclist.h"
23 #include "gailclistcell.h"
24 #include "gailcellparent.h"
25
26 /* Copied from gtkclist.c */
27 /* this defigns the base grid spacing */
28 #define CELL_SPACING 1
29
30 /* added the horizontal space at the beginning and end of a row*/
31 #define COLUMN_INSET 3
32
33
34 /* gives the top pixel of the given row in context of
35  * the clist's voffset */
36 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
37                                     (((row) + 1) * CELL_SPACING) + \
38                                     (clist)->voffset)
39
40 /* returns the row index from a y pixel location in the
41  * context of the clist's voffset */
42 #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
43                                     ((clist)->row_height + CELL_SPACING))
44 /* gives the left pixel of the given column in context of
45  * the clist's hoffset */
46 #define COLUMN_LEFT_XPIXEL(clist, colnum)  ((clist)->column[(colnum)].area.x + \
47                                             (clist)->hoffset)
48
49 /* returns the column index from a x pixel location in the
50  * context of the clist's hoffset */
51 static inline gint
52 COLUMN_FROM_XPIXEL (GtkCList * clist,
53                     gint x)
54 {
55   gint i, cx;
56
57   for (i = 0; i < clist->columns; i++)
58     if (clist->column[i].visible)
59       {
60         cx = clist->column[i].area.x + clist->hoffset;
61
62         if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
63             x <= (cx + clist->column[i].area.width + COLUMN_INSET))
64           return i;
65       }
66
67   /* no match */
68   return -1;
69 }
70
71 /* returns the top pixel of the given row in the context of
72  * the list height */
73 #define ROW_TOP(clist, row)        (((clist)->row_height + CELL_SPACING) * (row))
74
75 /* returns the left pixel of the given column in the context of
76  * the list width */
77 #define COLUMN_LEFT(clist, colnum) ((clist)->column[(colnum)].area.x)
78
79 /* returns the total height of the list */
80 #define LIST_HEIGHT(clist)         (((clist)->row_height * ((clist)->rows)) + \
81                                     (CELL_SPACING * ((clist)->rows + 1)))
82
83 static inline gint
84 LIST_WIDTH (GtkCList * clist)
85 {
86   gint last_column;
87
88   for (last_column = clist->columns - 1;
89        last_column >= 0 && !clist->column[last_column].visible; last_column--);
90
91   if (last_column >= 0)
92     return (clist->column[last_column].area.x +
93             clist->column[last_column].area.width +
94             COLUMN_INSET + CELL_SPACING);
95   return 0;
96 }
97
98 /* returns the GList item for the nth row */
99 #define ROW_ELEMENT(clist, row) (((row) == (clist)->rows - 1) ? \
100                                  (clist)->row_list_end : \
101                                  g_list_nth ((clist)->row_list, (row)))
102
103 typedef struct _GailCListRow        GailCListRow;
104 typedef struct _GailCListCellData   GailCListCellData;
105
106
107 static void       gail_clist_class_init            (GailCListClass    *klass);
108 static void       gail_clist_real_initialize       (AtkObject         *obj,
109                                                     gpointer          data);
110 static void       gail_clist_finalize              (GObject           *object);
111
112 static gint       gail_clist_get_n_children        (AtkObject         *obj);
113 static AtkObject* gail_clist_ref_child             (AtkObject         *obj,
114                                                     gint              i);
115 static AtkStateSet* gail_clist_ref_state_set       (AtkObject         *obj);
116
117
118 static void       atk_selection_interface_init     (AtkSelectionIface *iface);
119 static gboolean   gail_clist_clear_selection       (AtkSelection   *selection);
120
121 static AtkObject* gail_clist_ref_selection         (AtkSelection   *selection,
122                                                     gint           i);
123 static gint       gail_clist_get_selection_count   (AtkSelection   *selection);
124 static gboolean   gail_clist_is_child_selected     (AtkSelection   *selection,
125                                                     gint           i);
126 static gboolean   gail_clist_select_all_selection  (AtkSelection   *selection);
127
128 static void       atk_table_interface_init         (AtkTableIface     *iface);
129 static gint       gail_clist_get_index_at          (AtkTable      *table,
130                                                     gint          row,
131                                                     gint          column);
132 static gint       gail_clist_get_column_at_index   (AtkTable      *table,
133                                                     gint          index);
134 static gint       gail_clist_get_row_at_index      (AtkTable      *table,
135                                                     gint          index);
136 static AtkObject* gail_clist_ref_at                (AtkTable      *table,
137                                                     gint          row,
138                                                     gint          column);
139 static AtkObject* gail_clist_ref_at_actual         (AtkTable      *table,
140                                                     gint          row,
141                                                     gint          column);
142 static AtkObject*
143                   gail_clist_get_caption           (AtkTable      *table);
144
145 static gint       gail_clist_get_n_columns         (AtkTable      *table);
146 static gint       gail_clist_get_n_actual_columns  (GtkCList      *clist);
147
148 static G_CONST_RETURN gchar*
149                   gail_clist_get_column_description(AtkTable      *table,
150                                                     gint          column);
151 static AtkObject*  gail_clist_get_column_header     (AtkTable      *table,
152                                                     gint          column);
153 static gint       gail_clist_get_n_rows            (AtkTable      *table);
154 static G_CONST_RETURN gchar*
155                   gail_clist_get_row_description   (AtkTable      *table,
156                                                     gint          row);
157 static AtkObject*  gail_clist_get_row_header        (AtkTable      *table,
158                                                     gint          row);
159 static AtkObject* gail_clist_get_summary           (AtkTable      *table);
160 static gboolean   gail_clist_add_row_selection     (AtkTable      *table,
161                                                     gint          row);
162 static gboolean   gail_clist_remove_row_selection  (AtkTable      *table,
163                                                     gint          row);
164 static gint       gail_clist_get_selected_rows     (AtkTable      *table,
165                                                     gint          **rows_selected);
166 static gboolean   gail_clist_is_row_selected       (AtkTable      *table,
167                                                     gint          row);
168 static gboolean   gail_clist_is_selected           (AtkTable      *table,
169                                                     gint          row,
170                                                     gint          column);
171 static void       gail_clist_set_caption           (AtkTable      *table,
172                                                     AtkObject     *caption);
173 static void       gail_clist_set_column_description(AtkTable      *table,
174                                                     gint          column,
175                                                     const gchar   *description);
176 static void       gail_clist_set_column_header     (AtkTable      *table,
177                                                     gint          column,
178                                                     AtkObject     *header);
179 static void       gail_clist_set_row_description   (AtkTable      *table,
180                                                     gint          row,
181                                                     const gchar   *description);
182 static void       gail_clist_set_row_header        (AtkTable      *table,
183                                                     gint          row,
184                                                     AtkObject     *header);
185 static void       gail_clist_set_summary           (AtkTable      *table,
186                                                     AtkObject     *accessible);
187
188 /* gailcellparent.h */
189
190 static void       gail_cell_parent_interface_init  (GailCellParentIface *iface);
191 static void       gail_clist_get_cell_extents      (GailCellParent      *parent,
192                                                     GailCell            *cell,
193                                                     gint                *x,
194                                                     gint                *y,
195                                                     gint                *width,
196                                                     gint                *height,
197                                                     AtkCoordType        coord_type);
198
199 static void       gail_clist_get_cell_area         (GailCellParent      *parent,
200                                                     GailCell            *cell,
201                                                     GdkRectangle        *cell_rect);
202
203 static void       gail_clist_select_row_gtk        (GtkCList      *clist,
204                                                     int           row,
205                                                     int           column,
206                                                     GdkEvent      *event,
207                                                     gpointer      data);
208 static void       gail_clist_unselect_row_gtk      (GtkCList      *clist,
209                                                     int           row,
210                                                     int           column,
211                                                     GdkEvent      *event,
212                                                     gpointer      data);
213 static gint       gail_clist_get_visible_column    (AtkTable      *table,
214                                                     int           column);
215 static gint       gail_clist_get_actual_column     (AtkTable      *table,
216                                                     int           visible_column);
217 static void       gail_clist_set_row_data          (AtkTable      *table,
218                                                     gint          row,
219                                                     const gchar   *description,
220                                                     AtkObject     *header,
221                                                     gboolean      is_header);
222 static GailCListRow*
223                   gail_clist_get_row_data          (AtkTable      *table,
224                                                     gint          row);
225 static void       gail_clist_get_visible_rect      (GtkCList      *clist,
226                                                     GdkRectangle  *clist_rect);
227 static gboolean   gail_clist_is_cell_visible       (GdkRectangle  *cell_rect,
228                                                     GdkRectangle  *visible_rect);
229 static void       gail_clist_cell_data_new         (GailCList     *clist,
230                                                     GailCell      *cell,
231                                                     gint          column,
232                                                     gint          row);
233 static void       gail_clist_cell_destroyed        (gpointer      data);
234 static void       gail_clist_cell_data_remove      (GailCList     *clist,
235                                                     GailCell      *cell);
236 static GailCell*  gail_clist_find_cell             (GailCList     *clist,
237                                                     gint          index);
238 static void       gail_clist_adjustment_changed    (GtkAdjustment *adjustment,
239                                                     GtkCList      *clist);
240
241 struct _GailCListColumn
242 {
243   gchar *description;
244   AtkObject *header;
245 };
246
247 struct _GailCListRow
248 {
249   GtkCListRow *row_data;
250   int row_number;
251   gchar *description;
252   AtkObject *header;
253 };
254
255 struct _GailCListCellData
256 {
257   GtkCell *gtk_cell;
258   GailCell *gail_cell;
259   int row_number;
260   int column_number;
261 };
262
263 static gpointer parent_class = NULL;
264
265 GType
266 gail_clist_get_type (void)
267 {
268   static GType type = 0;
269
270   if (!type)
271     {
272       static const GTypeInfo tinfo =
273       {
274         sizeof (GailCListClass),
275         (GBaseInitFunc) NULL, /* base init */
276         (GBaseFinalizeFunc) NULL, /* base finalize */
277         (GClassInitFunc) gail_clist_class_init, /* class init */
278         (GClassFinalizeFunc) NULL, /* class finalize */
279         NULL, /* class data */
280         sizeof (GailCList), /* instance size */
281         0, /* nb preallocs */
282         (GInstanceInitFunc) NULL, /* instance init */
283         NULL /* value table */
284       };
285
286       static const GInterfaceInfo atk_table_info =
287       {
288         (GInterfaceInitFunc) atk_table_interface_init,
289         (GInterfaceFinalizeFunc) NULL,
290         NULL
291       };
292
293       static const GInterfaceInfo atk_selection_info =
294       {
295         (GInterfaceInitFunc) atk_selection_interface_init,
296         (GInterfaceFinalizeFunc) NULL,
297         NULL
298       };
299
300       static const GInterfaceInfo gail_cell_parent_info =
301       {
302         (GInterfaceInitFunc) gail_cell_parent_interface_init,
303         (GInterfaceFinalizeFunc) NULL,
304         NULL
305       };
306
307       type = g_type_register_static (GAIL_TYPE_CONTAINER,
308                                      "GailCList", &tinfo, 0);
309       g_type_add_interface_static (type, ATK_TYPE_TABLE,
310                                    &atk_table_info);
311       g_type_add_interface_static (type, ATK_TYPE_SELECTION,
312                                    &atk_selection_info);
313       g_type_add_interface_static (type, GAIL_TYPE_CELL_PARENT,
314                                    &gail_cell_parent_info);
315     }
316   return type;
317 }
318
319 static void
320 gail_clist_class_init (GailCListClass *klass)
321 {
322   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
323   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
324
325   parent_class = g_type_class_peek_parent (klass);
326  
327   class->get_n_children = gail_clist_get_n_children;
328   class->ref_child = gail_clist_ref_child;
329   class->ref_state_set = gail_clist_ref_state_set;
330   class->initialize = gail_clist_real_initialize;
331
332   gobject_class->finalize = gail_clist_finalize;
333 }
334
335 AtkObject* 
336 gail_clist_new (GtkWidget *widget)
337 {
338   GObject *object;
339   AtkObject *accessible;
340
341   g_return_val_if_fail (GTK_IS_CLIST (widget), NULL);
342
343   object = g_object_new (GAIL_TYPE_CLIST, NULL);
344
345   accessible = ATK_OBJECT (object);
346   atk_object_initialize (accessible, widget);
347
348   accessible->role = ATK_ROLE_TABLE;
349
350   return accessible;
351 }
352
353 static void
354 gail_clist_real_initialize (AtkObject *obj,
355                             gpointer  data)
356 {
357   GailCList *clist;
358   GtkCList *gtk_clist;
359   gint i;
360
361   ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
362
363   clist = GAIL_CLIST (obj);
364
365   clist->caption = NULL;
366   clist->summary = NULL;
367   clist->row_data = NULL;
368   clist->cell_data = NULL;
369   clist->previous_selected_cell = NULL;
370
371   gtk_clist = GTK_CLIST (data);
372  
373   clist->n_cols = gtk_clist->columns;
374   clist->columns = g_new (GailCListColumn, gtk_clist->columns);
375   for (i = 0; i < gtk_clist->columns; i++)
376     {
377       clist->columns[i].description = NULL;
378       clist->columns[i].header = NULL;
379     }
380   /*
381    * Set up signal handlers for select-row and unselect-row
382    */
383   g_signal_connect (gtk_clist,
384                     "select-row",
385                     G_CALLBACK (gail_clist_select_row_gtk),
386                     obj);
387   g_signal_connect (gtk_clist,
388                     "unselect-row",
389                     G_CALLBACK (gail_clist_unselect_row_gtk),
390                     obj);
391   /*
392    * Adjustment callbacks
393    */
394   if (gtk_clist->hadjustment)
395     {
396       g_signal_connect (gtk_clist->hadjustment,
397                         "value_changed",
398                         G_CALLBACK (gail_clist_adjustment_changed),
399                         gtk_clist);
400     }
401   if (gtk_clist->vadjustment)
402     {
403       g_signal_connect (gtk_clist->vadjustment,
404                         "value_changed",
405                         G_CALLBACK (gail_clist_adjustment_changed),
406                         gtk_clist);
407     }
408 }
409
410 static void
411 gail_clist_finalize (GObject            *object)
412 {
413   GailCList *clist = GAIL_CLIST (object);
414   gint i;
415   GArray *array;
416
417   if (clist->caption)
418     g_object_unref (clist->caption);
419   if (clist->summary)
420     g_object_unref (clist->summary);
421
422   for (i = 0; i < clist->n_cols; i++)
423     {
424       g_free (clist->columns[i].description);
425       if (clist->columns[i].header)
426         g_object_unref (clist->columns[i].header);
427     }
428   g_free (clist->columns);
429
430   array = clist->row_data;
431
432   if (clist->previous_selected_cell)
433     g_object_unref (clist->previous_selected_cell);
434
435   if (array)
436     {
437       for (i = 0; i < array->len; i++)
438         {
439           GailCListRow *row_data;
440
441           row_data = g_array_index (array, GailCListRow*, i);
442
443           if (row_data->header)
444             g_object_unref (row_data->header);
445           g_free (row_data->description);
446         }
447     }
448
449   if (clist->cell_data)
450     {
451       GList *temp_list;
452
453       for (temp_list = clist->cell_data; temp_list; temp_list = temp_list->next)
454         {
455           g_list_free (temp_list->data);
456         }
457       g_list_free (clist->cell_data);
458     }
459
460   G_OBJECT_CLASS (parent_class)->finalize (object);
461 }
462
463 static gint
464 gail_clist_get_n_children (AtkObject *obj)
465 {
466   GtkWidget *widget;
467   gint row, col;
468
469   g_return_val_if_fail (GAIL_IS_CLIST (obj), 0);
470
471   widget = GTK_ACCESSIBLE (obj)->widget;
472   if (widget == NULL)
473     /*
474      * State is defunct
475      */
476     return 0;
477
478   row = gail_clist_get_n_rows (ATK_TABLE (obj));
479   col = gail_clist_get_n_actual_columns (GTK_CLIST (widget));
480   return (row * col);
481 }
482
483 static AtkObject*
484 gail_clist_ref_child (AtkObject *obj,
485                       gint      i)
486 {
487   GtkWidget *widget;
488   gint row, col;
489   gint n_columns;
490
491   g_return_val_if_fail (GAIL_IS_CLIST (obj), NULL);
492   g_return_val_if_fail (i >= 0, NULL);
493
494   widget = GTK_ACCESSIBLE (obj)->widget;
495   if (widget == NULL)
496     /*
497      * State is defunct
498      */
499     return NULL;
500
501   n_columns = gail_clist_get_n_actual_columns (GTK_CLIST (widget));
502   if (!n_columns)
503     return NULL;
504
505   row = i / n_columns;
506   col = i % n_columns;
507   return gail_clist_ref_at_actual (ATK_TABLE (obj), row, col);
508 }
509
510 static AtkStateSet*
511 gail_clist_ref_state_set (AtkObject *obj)
512 {
513   AtkStateSet *state_set;
514   GtkWidget *widget;
515
516   state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj);
517   widget = GTK_ACCESSIBLE (obj)->widget;
518
519   if (widget != NULL)
520     atk_state_set_add_state (state_set, ATK_STATE_MANAGES_DESCENDANTS);
521
522   return state_set;
523 }
524
525 static void
526 atk_selection_interface_init (AtkSelectionIface *iface)
527 {
528   g_return_if_fail (iface != NULL);
529   
530   iface->clear_selection = gail_clist_clear_selection;
531   iface->ref_selection = gail_clist_ref_selection;
532   iface->get_selection_count = gail_clist_get_selection_count;
533   iface->is_child_selected = gail_clist_is_child_selected;
534   iface->select_all_selection = gail_clist_select_all_selection;
535 }
536
537 static gboolean
538 gail_clist_clear_selection (AtkSelection   *selection)
539 {
540   GtkCList *clist;
541   GtkWidget *widget;
542   
543   widget = GTK_ACCESSIBLE (selection)->widget;
544   if (widget == NULL)
545     /* State is defunct */
546     return FALSE;
547   
548   clist = GTK_CLIST (widget);
549   gtk_clist_unselect_all(clist);
550   return TRUE;
551 }
552
553 static AtkObject*
554 gail_clist_ref_selection (AtkSelection   *selection,
555                           gint           i)
556 {
557   gint visible_columns;
558   gint selected_row;
559   gint selected_column;
560   gint *selected_rows;
561
562   if ( i < 0 && i >= gail_clist_get_selection_count (selection))
563     return NULL;
564
565   visible_columns = gail_clist_get_n_columns (ATK_TABLE (selection));
566   gail_clist_get_selected_rows (ATK_TABLE (selection), &selected_rows);
567   selected_row = selected_rows[i / visible_columns];
568   g_free (selected_rows);
569   selected_column = gail_clist_get_actual_column (ATK_TABLE (selection), 
570                                                   i % visible_columns);
571
572   return gail_clist_ref_at (ATK_TABLE (selection), selected_row, 
573                             selected_column);
574 }
575
576 static gint
577 gail_clist_get_selection_count (AtkSelection   *selection)
578 {
579   gint n_rows_selected;
580
581   n_rows_selected = gail_clist_get_selected_rows (ATK_TABLE (selection), NULL);
582
583   if (n_rows_selected > 0)
584     /*
585      * The number of cells selected is the number of columns
586      * times the number of selected rows
587      */
588     return gail_clist_get_n_columns (ATK_TABLE (selection)) * n_rows_selected;
589   return 0;
590 }
591
592 static gboolean
593 gail_clist_is_child_selected (AtkSelection   *selection,
594                               gint           i)
595 {
596   gint row;
597
598   row = atk_table_get_row_at_index (ATK_TABLE (selection), i);
599
600   if (row == 0 && i >= gail_clist_get_n_columns (ATK_TABLE (selection)))
601     return FALSE;
602   return gail_clist_is_row_selected (ATK_TABLE (selection), row);
603 }
604
605 static gboolean
606 gail_clist_select_all_selection (AtkSelection   *selection)
607 {
608   GtkCList *clist;
609   GtkWidget *widget;
610   /* GtkArg arg; */
611   
612   widget = GTK_ACCESSIBLE (selection)->widget;
613   if (widget == NULL)
614     /* State is defunct */
615     return FALSE;
616
617   clist = GTK_CLIST (widget);
618   gtk_clist_select_all(clist);
619
620   return TRUE;
621 }
622
623 static void
624 atk_table_interface_init (AtkTableIface *iface)
625 {
626   g_return_if_fail (iface != NULL);
627   iface->ref_at = gail_clist_ref_at;
628   iface->get_index_at = gail_clist_get_index_at;
629   iface->get_column_at_index = gail_clist_get_column_at_index;
630   iface->get_row_at_index = gail_clist_get_row_at_index;
631   iface->get_caption = gail_clist_get_caption;
632   iface->get_n_columns = gail_clist_get_n_columns;
633   iface->get_column_description = gail_clist_get_column_description;
634   iface->get_column_header = gail_clist_get_column_header;
635   iface->get_n_rows = gail_clist_get_n_rows;
636   iface->get_row_description = gail_clist_get_row_description;
637   iface->get_row_header = gail_clist_get_row_header;
638   iface->get_summary = gail_clist_get_summary;
639   iface->add_row_selection = gail_clist_add_row_selection;
640   iface->remove_row_selection = gail_clist_remove_row_selection;
641   iface->get_selected_rows = gail_clist_get_selected_rows;
642   iface->is_row_selected = gail_clist_is_row_selected;
643   iface->is_selected = gail_clist_is_selected;
644   iface->set_caption = gail_clist_set_caption;
645   iface->set_column_description = gail_clist_set_column_description;
646   iface->set_column_header = gail_clist_set_column_header;
647   iface->set_row_description = gail_clist_set_row_description;
648   iface->set_row_header = gail_clist_set_row_header;
649   iface->set_summary = gail_clist_set_summary;
650 }
651
652 static AtkObject*
653 gail_clist_ref_at (AtkTable *table,
654                    gint     row,
655                    gint     column)
656 {
657   GtkWidget *widget;
658   gint actual_column;
659
660   widget = GTK_ACCESSIBLE (table)->widget;
661   if (widget == NULL)
662     /* State is defunct */
663     return NULL;
664
665   actual_column = gail_clist_get_actual_column (table, column);
666   return gail_clist_ref_at_actual (table, row, actual_column);
667 }
668
669
670 static AtkObject*
671 gail_clist_ref_at_actual (AtkTable      *table,
672                           gint          row,
673                           gint          column)
674 {
675   /*
676    * The column number pased to this function is the actual column number
677    * whereas the column number passed to gail_clist_ref_at is the
678    * visible column number
679    */
680   GtkCList *clist;
681   GtkWidget *widget;
682   GtkCellType cellType;
683   AtkObject *return_object;
684   gint n_rows, n_columns;
685   gint index;
686   GailCell *cell;
687
688   g_return_val_if_fail (GTK_IS_ACCESSIBLE (table), NULL);
689   
690   widget = GTK_ACCESSIBLE (table)->widget;
691   if (widget == NULL)
692     /* State is defunct */
693     return NULL;
694
695   clist = GTK_CLIST (widget);
696   n_rows = gail_clist_get_n_rows (table); 
697   n_columns = gail_clist_get_n_actual_columns (clist); 
698
699   if (row < 0 || row >= n_rows)
700     return NULL;
701   if (column < 0 || column >= n_columns)
702     return NULL;
703
704   /*
705    * Check whether the child is cached
706    */
707   index =  column + row * n_columns;
708   cell = gail_clist_find_cell (GAIL_CLIST (table), index);
709   if (cell)
710     {
711       g_object_ref (cell);
712       return ATK_OBJECT (cell);
713     }
714   cellType = gtk_clist_get_cell_type(clist, row, column);
715   switch (cellType) 
716     {
717     case GTK_CELL_TEXT:
718     case GTK_CELL_PIXTEXT:
719       return_object = gail_clist_cell_new ();
720       break;
721     case GTK_CELL_PIXMAP:
722       return_object = NULL;
723       break;
724     default:
725       /* Don't handle GTK_CELL_EMPTY or GTK_CELL_WIDGET, return NULL */
726       return_object = NULL;
727       break;
728     }
729   if (return_object)
730     {
731       cell = GAIL_CELL (return_object);
732
733       g_return_val_if_fail (ATK_IS_OBJECT (table), NULL);
734
735       gail_cell_init (cell, widget, ATK_OBJECT (table),
736                       index);
737       /*
738        * Store the cell in a cache
739        */
740       gail_clist_cell_data_new (GAIL_CLIST (table), cell, column, row);
741       /*
742        * If the column is visible, sets the cell's state
743        */
744       if (clist->column[column].visible)
745         {
746           GdkRectangle cell_rect, visible_rect;
747   
748           gail_clist_get_cell_area (GAIL_CELL_PARENT (table), cell, &cell_rect);
749           gail_clist_get_visible_rect (clist, &visible_rect);
750           gail_cell_add_state (cell, ATK_STATE_VISIBLE, FALSE);
751           if (gail_clist_is_cell_visible (&cell_rect, &visible_rect))
752             gail_cell_add_state (cell, ATK_STATE_SHOWING, FALSE);
753         }
754       /*
755        * If a row is selected, all cells in the row are selected
756        */
757       if (gail_clist_is_row_selected (table, row))
758         {
759           gail_cell_add_state (cell, ATK_STATE_SELECTED, FALSE);
760           if (clist->columns == 1)
761             gail_cell_add_state (cell, ATK_STATE_FOCUSED, FALSE);
762         }
763     }
764
765   return return_object; 
766 }
767
768 static gint
769 gail_clist_get_index_at (AtkTable *table,
770                          gint     row,
771                          gint     column)
772 {
773   gint n_cols, n_rows;
774
775   n_cols = atk_table_get_n_columns (table);
776   n_rows = atk_table_get_n_rows (table);
777
778   g_return_val_if_fail (row < n_rows, 0);
779   g_return_val_if_fail (column < n_cols, 0);
780
781   return row * n_cols + column;
782 }
783
784 static gint
785 gail_clist_get_column_at_index (AtkTable *table,
786                                 gint     index)
787 {
788   gint n_cols;
789
790   n_cols = atk_table_get_n_columns (table);
791
792   if (n_cols == 0)
793     return 0;
794   else
795     return (gint) (index % n_cols);
796 }
797
798 static gint
799 gail_clist_get_row_at_index (AtkTable *table,
800                              gint     index)
801 {
802   gint n_cols;
803
804   n_cols = atk_table_get_n_columns (table);
805
806   if (n_cols == 0)
807     return 0;
808   else
809     return (gint) (index / n_cols);
810 }
811
812 static AtkObject*
813 gail_clist_get_caption (AtkTable      *table)
814 {
815   GailCList* obj = GAIL_CLIST (table);
816
817   return obj->caption;
818 }
819
820 static gint
821 gail_clist_get_n_columns (AtkTable      *table)
822 {
823   GtkWidget *widget;
824   GtkCList *clist;
825
826   widget = GTK_ACCESSIBLE (table)->widget;
827   if (widget == NULL)
828     /* State is defunct */
829     return 0;
830
831   clist = GTK_CLIST (widget);
832
833   return gail_clist_get_visible_column (table, 
834                                   gail_clist_get_n_actual_columns (clist)); 
835 }
836
837 static gint
838 gail_clist_get_n_actual_columns (GtkCList *clist)
839 {
840   return clist->columns;
841 }
842
843 static G_CONST_RETURN gchar*
844 gail_clist_get_column_description (AtkTable      *table,
845                                    gint          column)
846 {
847   GailCList *clist = GAIL_CLIST (table);
848   GtkWidget *widget;
849   gint actual_column;
850
851   if (column < 0 || column >= gail_clist_get_n_columns (table))
852     return NULL;
853
854   actual_column = gail_clist_get_actual_column (table, column);
855   if (clist->columns[actual_column].description)
856     return (clist->columns[actual_column].description);
857
858   widget = GTK_ACCESSIBLE (clist)->widget;
859   if (widget == NULL)
860     return NULL;
861
862   return gtk_clist_get_column_title (GTK_CLIST (widget), actual_column);
863 }
864
865 static AtkObject*
866 gail_clist_get_column_header (AtkTable      *table,
867                               gint          column)
868 {
869   GailCList *clist = GAIL_CLIST (table);
870   GtkWidget *widget;
871   GtkWidget *return_widget;
872   gint actual_column;
873
874   if (column < 0 || column >= gail_clist_get_n_columns (table))
875     return NULL;
876
877   actual_column = gail_clist_get_actual_column (table, column);
878
879   if (clist->columns[actual_column].header)
880     return (clist->columns[actual_column].header);
881
882   widget = GTK_ACCESSIBLE (clist)->widget;
883   if (widget == NULL)
884     return NULL;
885
886   return_widget = gtk_clist_get_column_widget (GTK_CLIST (widget), 
887                                                actual_column);
888   if (return_widget == NULL)
889     return NULL;
890
891   g_return_val_if_fail (GTK_IS_BIN (return_widget), NULL);
892   return_widget = gtk_bin_get_child (GTK_BIN(return_widget));
893
894   return gtk_widget_get_accessible (return_widget);
895 }
896
897 static gint
898 gail_clist_get_n_rows (AtkTable      *table)
899 {
900   GtkWidget *widget;
901   GtkCList *clist;
902
903   widget = GTK_ACCESSIBLE (table)->widget;
904   if (widget == NULL)
905     /* State is defunct */
906     return 0;
907
908   clist = GTK_CLIST (widget);
909   return clist->rows;
910 }
911
912 static G_CONST_RETURN gchar*
913 gail_clist_get_row_description (AtkTable      *table,
914                                 gint          row)
915 {
916   GailCListRow* row_data;
917
918   row_data = gail_clist_get_row_data (table, row);
919   if (row_data == NULL)
920     return NULL;
921   return row_data->description;
922 }
923
924 static AtkObject*
925 gail_clist_get_row_header (AtkTable      *table,
926                            gint          row)
927 {
928   GailCListRow* row_data;
929
930   row_data = gail_clist_get_row_data (table, row);
931   if (row_data == NULL)
932     return NULL;
933   return row_data->header;
934 }
935
936 static AtkObject*
937 gail_clist_get_summary (AtkTable      *table)
938 {
939   GailCList* obj = GAIL_CLIST (table);
940
941   return obj->summary;
942 }
943
944 static gboolean
945 gail_clist_add_row_selection (AtkTable      *table,
946                               gint          row)
947 {
948   GtkWidget *widget;
949   GtkCList *clist;
950
951   widget = GTK_ACCESSIBLE (table)->widget;
952   if (widget == NULL)
953     /* State is defunct */
954     return 0;
955
956   clist = GTK_CLIST (widget);
957   gtk_clist_select_row (clist, row, -1);
958   if (gail_clist_is_row_selected (table, row))
959     return TRUE;
960   
961   return FALSE;
962 }
963
964 static gboolean
965 gail_clist_remove_row_selection (AtkTable      *table,
966                                  gint          row)
967 {
968   GtkWidget *widget;
969   GtkCList *clist;
970
971   widget = GTK_ACCESSIBLE (table)->widget;
972   if (widget == NULL)
973     /* State is defunct */
974     return 0;
975
976   clist = GTK_CLIST (widget);
977   if (gail_clist_is_row_selected (table, row))
978   {
979     gtk_clist_select_row (clist, row, -1);
980     return TRUE;
981   }
982   return FALSE;
983 }
984
985 static gint
986 gail_clist_get_selected_rows (AtkTable *table,
987                               gint     **rows_selected)
988 {
989   GtkWidget *widget;
990   GtkCList *clist;
991   GList *list;
992   gint n_selected;
993   gint i;
994
995   widget = GTK_ACCESSIBLE (table)->widget;
996   if (widget == NULL)
997     /* State is defunct */
998     return 0;
999
1000   clist = GTK_CLIST (widget);
1001  
1002   n_selected = g_list_length (clist->selection);
1003
1004   if (n_selected == 0) 
1005     return 0;
1006
1007   if (rows_selected)
1008     {
1009       gint *selected_rows;
1010
1011       selected_rows = (gint*) g_malloc (sizeof (gint) * n_selected);
1012       list = clist->selection;
1013
1014       i = 0;
1015       while (list)
1016         {
1017           selected_rows[i++] = GPOINTER_TO_INT (list->data);
1018           list = list->next;
1019         }
1020       *rows_selected = selected_rows;
1021     }
1022   return n_selected;
1023 }
1024
1025 static gboolean
1026 gail_clist_is_row_selected (AtkTable      *table,
1027                             gint          row)
1028 {
1029   GList *elem;
1030   GtkWidget *widget;
1031   GtkCList *clist;
1032   GtkCListRow *clist_row;
1033
1034   widget = GTK_ACCESSIBLE (table)->widget;
1035   if (widget == NULL)
1036     /* State is defunct */
1037         return FALSE;
1038
1039   clist = GTK_CLIST (widget);
1040       
1041   if (row < 0 || row >= clist->rows)
1042     return FALSE;
1043
1044   elem = ROW_ELEMENT (clist, row);
1045   if (!elem)
1046     return FALSE;
1047   clist_row = elem->data;
1048
1049   return (clist_row->state == GTK_STATE_SELECTED);
1050 }
1051
1052 static gboolean
1053 gail_clist_is_selected (AtkTable      *table,
1054                         gint          row,
1055                         gint          column)
1056 {
1057   return gail_clist_is_row_selected (table, row);
1058 }
1059
1060 static void
1061 gail_clist_set_caption (AtkTable      *table,
1062                         AtkObject     *caption)
1063 {
1064   GailCList* obj = GAIL_CLIST (table);
1065   AtkPropertyValues values = { NULL };
1066   AtkObject *old_caption;
1067
1068   old_caption = obj->caption;
1069   obj->caption = caption;
1070   if (obj->caption)
1071     g_object_ref (obj->caption);
1072
1073   g_value_init (&values.old_value, G_TYPE_POINTER);
1074   g_value_set_pointer (&values.old_value, old_caption);
1075   g_value_init (&values.new_value, G_TYPE_POINTER);
1076   g_value_set_pointer (&values.new_value, obj->caption);
1077
1078   values.property_name = "accessible-table-caption";
1079   g_signal_emit_by_name (table, 
1080                          "property_change::accessible-table-caption", 
1081                          &values, NULL);
1082   if (old_caption)
1083     g_object_unref (old_caption);
1084 }
1085
1086 static void
1087 gail_clist_set_column_description (AtkTable      *table,
1088                                    gint          column,
1089                                    const gchar   *description)
1090 {
1091   GailCList *clist = GAIL_CLIST (table);
1092   AtkPropertyValues values = { NULL };
1093   gint actual_column;
1094
1095   if (column < 0 || column >= gail_clist_get_n_columns (table))
1096     return;
1097
1098   if (description == NULL)
1099     return;
1100
1101   actual_column = gail_clist_get_actual_column (table, column);
1102   g_free (clist->columns[actual_column].description);
1103   clist->columns[actual_column].description = g_strdup (description);
1104
1105   g_value_init (&values.new_value, G_TYPE_INT);
1106   g_value_set_int (&values.new_value, column);
1107
1108   values.property_name = "accessible-table-column-description";
1109   g_signal_emit_by_name (table, 
1110                          "property_change::accessible-table-column-description",
1111                           &values, NULL);
1112
1113 }
1114
1115 static void
1116 gail_clist_set_column_header (AtkTable      *table,
1117                               gint          column,
1118                               AtkObject     *header)
1119 {
1120   GailCList *clist = GAIL_CLIST (table);
1121   AtkPropertyValues values = { NULL };
1122   gint actual_column;
1123
1124   if (column < 0 || column >= gail_clist_get_n_columns (table))
1125     return;
1126
1127   actual_column = gail_clist_get_actual_column (table, column);
1128   if (clist->columns[actual_column].header)
1129     g_object_unref (clist->columns[actual_column].header);
1130   if (header)
1131     g_object_ref (header);
1132   clist->columns[actual_column].header = header;
1133
1134   g_value_init (&values.new_value, G_TYPE_INT);
1135   g_value_set_int (&values.new_value, column);
1136
1137   values.property_name = "accessible-table-column-header";
1138   g_signal_emit_by_name (table, 
1139                          "property_change::accessible-table-column-header",
1140                          &values, NULL);
1141 }
1142
1143 static void
1144 gail_clist_set_row_description (AtkTable      *table,
1145                                 gint          row,
1146                                 const gchar   *description)
1147 {
1148   gail_clist_set_row_data (table, row, description, NULL, FALSE);
1149 }
1150
1151 static void
1152 gail_clist_set_row_header (AtkTable      *table,
1153                            gint          row,
1154                            AtkObject     *header)
1155 {
1156   gail_clist_set_row_data (table, row, NULL, header, TRUE);
1157 }
1158
1159 static void
1160 gail_clist_set_summary (AtkTable      *table,
1161                         AtkObject     *accessible)
1162 {
1163   GailCList* obj = GAIL_CLIST (table);
1164   AtkPropertyValues values = { 0, };
1165   AtkObject *old_summary;
1166
1167   old_summary = obj->summary;
1168   obj->summary = accessible;
1169   if (obj->summary)
1170     g_object_ref (obj->summary);
1171
1172   g_value_init (&values.old_value, G_TYPE_POINTER);
1173   g_value_set_pointer (&values.old_value, old_summary);
1174   g_value_init (&values.new_value, G_TYPE_POINTER);
1175   g_value_set_pointer (&values.new_value, obj->summary);
1176
1177   values.property_name = "accessible-table-summary";
1178   g_signal_emit_by_name (table, 
1179                          "property_change::accessible-table-summary", 
1180                          &values, NULL);
1181   if (old_summary)
1182     g_object_unref (old_summary);
1183 }
1184
1185
1186 static void gail_cell_parent_interface_init (GailCellParentIface *iface)
1187 {
1188   g_return_if_fail (iface);
1189
1190   iface->get_cell_extents = gail_clist_get_cell_extents;
1191   iface->get_cell_area = gail_clist_get_cell_area;
1192 }
1193
1194 static void
1195 gail_clist_get_cell_extents (GailCellParent *parent,
1196                              GailCell       *cell,
1197                              gint           *x,
1198                              gint           *y,
1199                              gint           *width,
1200                              gint           *height,
1201                              AtkCoordType   coord_type)
1202 {
1203   GtkWidget* widget;
1204   GtkCList *clist;
1205   gint widget_x, widget_y, widget_width, widget_height;
1206   GdkRectangle cell_rect;
1207   GdkRectangle visible_rect;
1208
1209   widget = GTK_ACCESSIBLE (parent)->widget;
1210   if (widget == NULL)
1211     return;
1212   clist = GTK_CLIST (widget);
1213
1214   atk_component_get_extents (ATK_COMPONENT (parent), &widget_x, &widget_y,
1215                              &widget_width, &widget_height,
1216                              coord_type);
1217
1218   gail_clist_get_cell_area (parent, cell, &cell_rect);
1219   *width = cell_rect.width;
1220   *height = cell_rect.height;
1221   gail_clist_get_visible_rect (clist, &visible_rect);
1222   if (gail_clist_is_cell_visible (&cell_rect, &visible_rect))
1223     {
1224       *x = cell_rect.x + widget_x;
1225       *y = cell_rect.y + widget_y;
1226     }
1227   else
1228     {
1229       *x = G_MININT;
1230       *y = G_MININT;
1231     }
1232 }
1233
1234 static void
1235 gail_clist_get_cell_area (GailCellParent *parent,
1236                           GailCell       *cell,
1237                           GdkRectangle   *cell_rect)
1238 {
1239   GtkWidget* widget;
1240   GtkCList *clist;
1241   gint column, row, n_columns;
1242
1243   widget = GTK_ACCESSIBLE (parent)->widget;
1244   if (widget == NULL)
1245     return;
1246   clist = GTK_CLIST (widget);
1247
1248   n_columns = gail_clist_get_n_actual_columns (clist);
1249   g_return_if_fail (n_columns > 0);
1250   column = cell->index % n_columns;
1251   row = cell->index / n_columns; 
1252   cell_rect->x = COLUMN_LEFT (clist, column);
1253   cell_rect->y = ROW_TOP (clist, row);
1254   cell_rect->width = clist->column[column].area.width;
1255   cell_rect->height = clist->row_height;
1256 }
1257
1258 static void
1259 gail_clist_select_row_gtk (GtkCList *clist,
1260                            gint      row,
1261                            gint      column,
1262                            GdkEvent *event,
1263                            gpointer data)
1264 {
1265   GailCList *gail_clist;
1266   GList *temp_list;
1267   AtkObject *selected_cell;
1268
1269   gail_clist = GAIL_CLIST (data);
1270
1271   for (temp_list = gail_clist->cell_data; temp_list; temp_list = temp_list->next)
1272     {
1273       GailCListCellData *cell_data;
1274
1275       cell_data = (GailCListCellData *) (temp_list->data);
1276
1277       if (row == cell_data->row_number)
1278         {
1279           /*
1280            * Row is selected
1281            */
1282           gail_cell_add_state (cell_data->gail_cell, ATK_STATE_SELECTED, TRUE);
1283         }
1284     }
1285   if (clist->columns == 1)
1286     {
1287       selected_cell = gail_clist_ref_at (ATK_TABLE (data), row, 1);
1288       if (selected_cell)
1289         {
1290           if (gail_clist->previous_selected_cell)
1291             g_object_unref (gail_clist->previous_selected_cell);
1292           gail_clist->previous_selected_cell = selected_cell;
1293           gail_cell_add_state (GAIL_CELL (selected_cell), ATK_STATE_FOCUSED, FALSE);
1294           g_signal_emit_by_name (gail_clist,
1295                                  "active-descendant-changed",
1296                                   selected_cell);
1297        }
1298     }
1299
1300   g_signal_emit_by_name (gail_clist, "selection_changed");
1301 }
1302
1303 static void
1304 gail_clist_unselect_row_gtk (GtkCList *clist,
1305                              gint      row,
1306                              gint      column,
1307                              GdkEvent *event,
1308                              gpointer data)
1309 {
1310   GailCList *gail_clist;
1311   GList *temp_list;
1312
1313   gail_clist = GAIL_CLIST (data);
1314
1315   for (temp_list = gail_clist->cell_data; temp_list; temp_list = temp_list->next)
1316     {
1317       GailCListCellData *cell_data;
1318
1319       cell_data = (GailCListCellData *) (temp_list->data);
1320
1321       if (row == cell_data->row_number)
1322         {
1323           /*
1324            * Row is unselected
1325            */
1326           gail_cell_add_state (cell_data->gail_cell, ATK_STATE_FOCUSED, FALSE);
1327           gail_cell_remove_state (cell_data->gail_cell, ATK_STATE_SELECTED, TRUE);
1328        }
1329     }
1330
1331   g_signal_emit_by_name (gail_clist, "selection_changed");
1332 }
1333
1334 /*
1335  * This function determines the number of visible columns
1336  * up to and including the specified column
1337  */
1338 static gint
1339 gail_clist_get_visible_column (AtkTable *table,
1340                                int      column)
1341 {
1342   GtkWidget *widget;
1343   GtkCList *clist;
1344   gint i;
1345   gint vis_columns;
1346
1347   widget = GTK_ACCESSIBLE (table)->widget;
1348   if (widget == NULL)
1349     /* State is defunct */
1350     return 0;
1351
1352   clist = GTK_CLIST (widget);
1353   for (i = 0, vis_columns = 0; i < column; i++)
1354     if (clist->column[i].visible)
1355       vis_columns++;
1356
1357   return vis_columns;  
1358 }
1359
1360 static gint
1361 gail_clist_get_actual_column (AtkTable *table,
1362                               int      visible_column)
1363 {
1364   GtkWidget *widget;
1365   GtkCList *clist;
1366   gint i;
1367   gint vis_columns;
1368
1369   widget = GTK_ACCESSIBLE (table)->widget;
1370   if (widget == NULL)
1371     /* State is defunct */
1372     return 0;
1373
1374   clist = GTK_CLIST (widget);
1375   for (i = 0, vis_columns = 0; i < clist->columns; i++)
1376     {
1377       if (clist->column[i].visible)
1378         {
1379           if (visible_column == vis_columns)
1380             return i;
1381           vis_columns++;
1382         }
1383     }
1384   return 0;  
1385 }
1386
1387 static void
1388 gail_clist_set_row_data (AtkTable      *table,
1389                          gint          row,
1390                          const gchar   *description,
1391                          AtkObject     *header,
1392                          gboolean      is_header)
1393 {
1394   GtkWidget *widget;
1395   GtkCList *gtk_clist;
1396   GailCList *gail_clist;
1397   GArray *array;
1398   GailCListRow* row_data;
1399   gint i;
1400   gboolean found = FALSE;
1401   AtkPropertyValues values = { NULL };
1402   gchar *signal_name;
1403
1404   widget = GTK_ACCESSIBLE (table)->widget;
1405   if (widget == NULL)
1406     /* State is defunct */
1407     return;
1408
1409   gtk_clist = GTK_CLIST (widget);
1410   if (row < 0 || row >= gtk_clist->rows)
1411     return;
1412
1413   gail_clist = GAIL_CLIST (table);
1414
1415   if (gail_clist->row_data == NULL)
1416     gail_clist->row_data = g_array_sized_new (FALSE, TRUE, 
1417                                               sizeof (GailCListRow *), 0);
1418
1419   array = gail_clist->row_data;
1420
1421   for (i = 0; i < array->len; i++)
1422     {
1423       row_data = g_array_index (array, GailCListRow*, i);
1424
1425       if (row == row_data->row_number)
1426         {
1427           found = TRUE;
1428           if (is_header)
1429             {
1430               if (row_data->header)
1431                 g_object_unref (row_data->header);
1432               row_data->header = header;
1433               if (row_data->header)
1434                 g_object_ref (row_data->header);
1435             }
1436           else
1437             {
1438               g_free (row_data->description);
1439               row_data->description = g_strdup (row_data->description);
1440             }
1441           break;
1442         }
1443     } 
1444   if (!found)
1445     {
1446       GList *elem;
1447
1448       elem = ROW_ELEMENT (gtk_clist, row);
1449       g_return_if_fail (elem != NULL);
1450
1451       row_data = g_new (GailCListRow, 1);
1452       row_data->row_number = row;
1453       row_data->row_data = elem->data;
1454       if (is_header)
1455         {
1456           row_data->header = header;
1457           if (row_data->header)
1458             g_object_ref (row_data->header);
1459           row_data->description = NULL;
1460         }
1461       else
1462         {
1463           row_data->description = g_strdup (row_data->description);
1464           row_data->header = NULL;
1465         }
1466       g_array_append_val (array, row_data);
1467     }
1468
1469   g_value_init (&values.new_value, G_TYPE_INT);
1470   g_value_set_int (&values.new_value, row);
1471
1472   if (is_header)
1473     {
1474       values.property_name = "accessible-table-row-header";
1475       signal_name = "property_change::accessible-table-row-header";
1476     }
1477   else
1478     {
1479       values.property_name = "accessible-table-row-description";
1480       signal_name = "property_change::accessible-table-row-description";
1481     }
1482   g_signal_emit_by_name (table, 
1483                          signal_name,
1484                          &values, NULL);
1485
1486 }
1487
1488 static GailCListRow*
1489 gail_clist_get_row_data (AtkTable      *table,
1490                          gint          row)
1491 {
1492   GtkWidget *widget;
1493   GtkCList *clist;
1494   GailCList *obj;
1495   GArray *array;
1496   GailCListRow* row_data;
1497   gint i;
1498
1499   widget = GTK_ACCESSIBLE (table)->widget;
1500   if (widget == NULL)
1501     /* State is defunct */
1502     return NULL;
1503
1504   clist = GTK_CLIST (widget);
1505   if (row < 0 || row >= clist->rows)
1506     return NULL;
1507
1508   obj = GAIL_CLIST (table);
1509
1510   if (obj->row_data == NULL)
1511     return NULL;
1512
1513   array = obj->row_data;
1514
1515   for (i = 0; i < array->len; i++)
1516     {
1517       row_data = g_array_index (array, GailCListRow*, i);
1518
1519       if (row == row_data->row_number)
1520         return row_data;
1521     }
1522  
1523   return NULL;
1524 }
1525
1526 static void
1527 gail_clist_get_visible_rect (GtkCList      *clist,
1528                              GdkRectangle  *clist_rect)
1529 {
1530   clist_rect->x = - clist->hoffset;
1531   clist_rect->y = - clist->voffset;
1532   clist_rect->width = clist->clist_window_width;
1533   clist_rect->height = clist->clist_window_height;
1534 }
1535
1536 static gboolean
1537 gail_clist_is_cell_visible (GdkRectangle  *cell_rect,
1538                             GdkRectangle  *visible_rect)
1539 {
1540   /*
1541    * A cell is reported as visible if any part of the cell is visible
1542    */
1543   if (((cell_rect->x + cell_rect->width) < visible_rect->x) ||
1544      ((cell_rect->y + cell_rect->height) < visible_rect->y) ||
1545      (cell_rect->x > (visible_rect->x + visible_rect->width)) ||
1546      (cell_rect->y > (visible_rect->y + visible_rect->height)))
1547     return FALSE;
1548   else
1549     return TRUE;
1550 }
1551
1552 static void
1553 gail_clist_cell_data_new (GailCList     *clist,
1554                           GailCell      *cell,
1555                           gint          column,
1556                           gint          row)
1557 {
1558   GList *elem;
1559   GailCListCellData *cell_data;
1560   GtkCList *gtk_clist;
1561   GtkCListRow *clist_row;
1562
1563   gtk_clist = GTK_CLIST (GTK_ACCESSIBLE (clist)->widget);
1564   elem = g_list_nth (gtk_clist->row_list, row);
1565   g_return_if_fail (elem != NULL);
1566   clist_row = (GtkCListRow *) elem->data;
1567   cell_data = g_new (GailCListCellData, 1);
1568   cell_data->gail_cell = cell;
1569   cell_data->gtk_cell = &(clist_row->cell[column]);
1570   cell_data->column_number = column;
1571   cell_data->row_number = row;
1572   clist->cell_data = g_list_append (clist->cell_data, cell_data);
1573
1574   g_object_weak_ref (G_OBJECT (cell),
1575                      (GWeakNotify) gail_clist_cell_destroyed,
1576                      cell);
1577 }
1578
1579 static void
1580 gail_clist_cell_destroyed (gpointer      data)
1581 {
1582   GailCell *cell = GAIL_CELL (data);
1583   AtkObject* parent;
1584
1585   parent = atk_object_get_parent (ATK_OBJECT (cell));
1586
1587   gail_clist_cell_data_remove (GAIL_CLIST (parent), cell);
1588 }
1589
1590 static void
1591 gail_clist_cell_data_remove (GailCList *clist,
1592                              GailCell  *cell)
1593 {
1594   GList *temp_list;
1595
1596   for (temp_list = clist->cell_data; temp_list; temp_list = temp_list->next)
1597     {
1598       GailCListCellData *cell_data;
1599
1600       cell_data = (GailCListCellData *) temp_list->data;
1601       if (cell_data->gail_cell == cell)
1602         {
1603           clist->cell_data = g_list_remove_link (clist->cell_data, temp_list);
1604           g_free (cell_data);
1605           return;
1606         }
1607     }
1608   g_warning ("No cell removed in gail_clist_cell_data_remove\n");
1609 }
1610
1611 static GailCell*
1612 gail_clist_find_cell (GailCList     *clist,
1613                       gint          index)
1614 {
1615   GList *temp_list;
1616   gint n_cols;
1617
1618   n_cols = clist->n_cols;
1619
1620   for (temp_list = clist->cell_data; temp_list; temp_list = temp_list->next)
1621     {
1622       GailCListCellData *cell_data;
1623       gint real_index;
1624
1625       cell_data = (GailCListCellData *) (temp_list->data);
1626
1627       real_index = cell_data->column_number + n_cols * cell_data->row_number;
1628       if (real_index == index)
1629         return cell_data->gail_cell;
1630     }
1631   return NULL;
1632 }
1633
1634 static void
1635 gail_clist_adjustment_changed (GtkAdjustment *adjustment,
1636                                GtkCList      *clist)
1637 {
1638   AtkObject *atk_obj;
1639   GdkRectangle visible_rect;
1640   GdkRectangle cell_rect;
1641   GailCList* obj;
1642   GList *temp_list;
1643
1644   /*
1645    * The scrollbars have changed
1646    */
1647   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (clist));
1648   obj = GAIL_CLIST (atk_obj);
1649
1650   /* Get the currently visible area */
1651   gail_clist_get_visible_rect (clist, &visible_rect);
1652
1653   /* loop over the cells and report if they are visible or not. */
1654   /* Must loop through them all */
1655   for (temp_list = obj->cell_data; temp_list; temp_list = temp_list->next)
1656     {
1657       GailCell *cell;
1658       GailCListCellData *cell_data;
1659
1660       cell_data = (GailCListCellData *) (temp_list->data);
1661       cell = cell_data->gail_cell;
1662
1663       gail_clist_get_cell_area (GAIL_CELL_PARENT (atk_obj), 
1664                                 cell, &cell_rect);
1665       if (gail_clist_is_cell_visible (&cell_rect, &visible_rect))
1666         gail_cell_add_state (cell, ATK_STATE_SHOWING, TRUE);
1667       else
1668         gail_cell_remove_state (cell, ATK_STATE_SHOWING, TRUE);
1669     }
1670   g_signal_emit_by_name (atk_obj, "visible_data_changed");
1671 }
1672