]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailitem.c
Include <config.h>. Bug #504720.
[~andy/gtk] / modules / other / gail / gailitem.c
1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 2001, 2002, 2003 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 <config.h>
21
22 #include <string.h>
23 #include <gtk/gtk.h>
24 #include "gailitem.h"
25 #include <libgail-util/gailmisc.h>
26
27 static void                  gail_item_class_init      (GailItemClass *klass);
28 static void                  gail_item_init            (GailItem      *item);
29 static G_CONST_RETURN gchar* gail_item_get_name        (AtkObject     *obj);
30 static gint                  gail_item_get_n_children  (AtkObject     *obj);
31 static AtkObject*            gail_item_ref_child       (AtkObject     *obj,
32                                                         gint          i);
33 static void                  gail_item_real_initialize (AtkObject     *obj,
34                                                         gpointer      data);
35 static void                  gail_item_label_map_gtk   (GtkWidget     *widget,
36                                                         gpointer      data);
37 static void                  gail_item_finalize        (GObject       *object);
38 static void                  gail_item_init_textutil   (GailItem      *item,
39                                                         GtkWidget     *label);
40 static void                  gail_item_notify_label_gtk(GObject       *obj,
41                                                         GParamSpec    *pspec,
42                                                         gpointer      data);
43
44 /* atktext.h */ 
45 static void       atk_text_interface_init          (AtkTextIface        *iface);
46
47 static gchar*     gail_item_get_text               (AtkText           *text,
48                                                     gint              start_pos,
49                                                     gint              end_pos);
50 static gunichar   gail_item_get_character_at_offset(AtkText           *text,
51                                                     gint              offset);
52 static gchar*     gail_item_get_text_before_offset (AtkText           *text,
53                                                     gint              offset,
54                                                     AtkTextBoundary   boundary_type,
55                                                     gint              *start_offset,
56                                                     gint              *end_offset);
57 static gchar*     gail_item_get_text_at_offset     (AtkText           *text,
58                                                     gint              offset,
59                                                     AtkTextBoundary   boundary_type,
60                                                     gint              *start_offset,
61                                                     gint              *end_offset);
62 static gchar*     gail_item_get_text_after_offset  (AtkText           *text,
63                                                     gint              offset,
64                                                     AtkTextBoundary   boundary_type,
65                                                     gint              *start_offset,
66                                                     gint              *end_offset);
67 static gint       gail_item_get_character_count    (AtkText           *text);
68 static void       gail_item_get_character_extents  (AtkText           *text,
69                                                     gint              offset,
70                                                     gint              *x,
71                                                     gint              *y,
72                                                     gint              *width,
73                                                     gint              *height,
74                                                     AtkCoordType      coords);
75 static gint      gail_item_get_offset_at_point     (AtkText           *text,
76                                                     gint              x,
77                                                     gint              y,
78                                                     AtkCoordType      coords);
79 static AtkAttributeSet* gail_item_get_run_attributes 
80                                                    (AtkText           *text,
81                                                     gint              offset,
82                                                     gint              *start_offset,
83                                                     gint              *end_offset);
84 static AtkAttributeSet* gail_item_get_default_attributes
85                                                    (AtkText           *text);
86 static GtkWidget*            get_label_from_container   (GtkWidget    *container);
87
88 G_DEFINE_TYPE_WITH_CODE (GailItem, gail_item, GAIL_TYPE_CONTAINER,
89                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
90
91 static void
92 gail_item_class_init (GailItemClass *klass)
93 {
94   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
95   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
96   GailContainerClass *container_class;
97
98   container_class = (GailContainerClass *)klass;
99
100   gobject_class->finalize = gail_item_finalize;
101
102   class->get_name = gail_item_get_name;
103   class->get_n_children = gail_item_get_n_children;
104   class->ref_child = gail_item_ref_child;
105   class->initialize = gail_item_real_initialize;
106   /*
107    * As we report the item as having no children we are not interested
108    * in add and remove signals
109    */
110   container_class->add_gtk = NULL;
111   container_class->remove_gtk = NULL;
112 }
113
114 static void
115 gail_item_init (GailItem      *item)
116 {
117 }
118
119 AtkObject*
120 gail_item_new (GtkWidget *widget)
121 {
122   GObject *object;
123   AtkObject *accessible;
124
125   g_return_val_if_fail (GTK_IS_ITEM (widget), NULL);
126
127   object = g_object_new (GAIL_TYPE_ITEM, NULL);
128
129   accessible = ATK_OBJECT (object);
130   atk_object_initialize (accessible, widget);
131
132   return accessible;
133 }
134
135 static void
136 gail_item_real_initialize (AtkObject *obj,
137                            gpointer     data)
138 {
139   GailItem *item = GAIL_ITEM (obj);
140   GtkWidget  *label;
141
142   ATK_OBJECT_CLASS (gail_item_parent_class)->initialize (obj, data);
143
144   item->textutil = NULL;
145   item->text = NULL;
146
147   label = get_label_from_container (GTK_WIDGET (data));
148   if (GTK_IS_LABEL (label))
149     {
150       if (GTK_WIDGET_MAPPED (label))
151         gail_item_init_textutil (item, label);
152       else
153         g_signal_connect (label,
154                           "map",
155                           G_CALLBACK (gail_item_label_map_gtk),
156                           item);
157     }
158
159   obj->role = ATK_ROLE_LIST_ITEM;
160 }
161
162 static void
163 gail_item_label_map_gtk (GtkWidget *widget,
164                          gpointer data)
165 {
166   GailItem *item;
167
168   item = GAIL_ITEM (data);
169   gail_item_init_textutil (item, widget);
170 }
171
172 static void
173 gail_item_init_textutil (GailItem  *item,
174                          GtkWidget *label)
175 {
176   const gchar *label_text;
177
178   if (item->textutil == NULL)
179     {
180       item->textutil = gail_text_util_new ();
181       g_signal_connect (label,
182                         "notify",
183                         (GCallback) gail_item_notify_label_gtk,
184                         item);     
185     }
186   label_text = gtk_label_get_text (GTK_LABEL (label));
187   gail_text_util_text_setup (item->textutil, label_text);
188 }
189
190 static void
191 gail_item_finalize (GObject *object)
192 {
193   GailItem *item = GAIL_ITEM (object);
194
195   if (item->textutil)
196     {
197       g_object_unref (item->textutil);
198     }
199   if (item->text)
200     {
201       g_free (item->text);
202       item->text = NULL;
203     }
204   G_OBJECT_CLASS (gail_item_parent_class)->finalize (object);
205 }
206
207 static G_CONST_RETURN gchar*
208 gail_item_get_name (AtkObject *obj)
209 {
210   G_CONST_RETURN gchar* name;
211
212   g_return_val_if_fail (GAIL_IS_ITEM (obj), NULL);
213
214   name = ATK_OBJECT_CLASS (gail_item_parent_class)->get_name (obj);
215   if (name == NULL)
216     {
217       /*
218        * Get the label child
219        */
220       GtkWidget *widget;
221       GtkWidget *label;
222
223       widget = GTK_ACCESSIBLE (obj)->widget;
224       if (widget == NULL)
225         /*
226          * State is defunct
227          */
228         return NULL;
229
230       label = get_label_from_container (widget);
231       if (GTK_IS_LABEL (label))
232         return gtk_label_get_text (GTK_LABEL(label));
233       /*
234        * If we have a menu item in a menu attached to a GtkOptionMenu
235        * the label of the selected item is detached from the menu item
236        */
237       else if (GTK_IS_MENU_ITEM (widget))
238         {
239           GtkWidget *parent;
240           GtkWidget *attach;
241           GList *list;
242           AtkObject *parent_obj;
243           gint index;
244
245           parent = gtk_widget_get_parent (widget);
246           if (GTK_IS_MENU (parent))
247             {
248               attach = gtk_menu_get_attach_widget (GTK_MENU (parent)); 
249
250               if (GTK_IS_OPTION_MENU (attach))
251                 {
252                   label = get_label_from_container (attach);
253                   if (GTK_IS_LABEL (label))
254                     return gtk_label_get_text (GTK_LABEL(label));
255                 }
256               list = gtk_container_get_children (GTK_CONTAINER (parent));
257               index = g_list_index (list, widget);
258
259               if (index < 0 || index > g_list_length (list))
260                 {
261                   g_list_free (list);
262                   return NULL;
263                 }
264               g_list_free (list);
265
266               parent_obj = atk_object_get_parent (gtk_widget_get_accessible (parent));
267               if (GTK_IS_ACCESSIBLE (parent_obj))
268                 {
269                   parent = GTK_ACCESSIBLE (parent_obj)->widget;
270                   if (GTK_IS_COMBO_BOX (parent))
271                     {
272                       GtkTreeModel *model;
273                       GtkTreeIter iter;
274                       GailItem *item;
275                       gint n_columns, i;
276
277                       model = gtk_combo_box_get_model (GTK_COMBO_BOX (parent));                       
278                       item = GAIL_ITEM (obj);
279                       if (gtk_tree_model_iter_nth_child (model, &iter, NULL, index))
280                         {
281                           n_columns = gtk_tree_model_get_n_columns (model);
282                           for (i = 0; i < n_columns; i++)
283                             {
284                               GValue value = { 0, };
285
286                                gtk_tree_model_get_value (model, &iter, i, &value);
287                                if (G_VALUE_HOLDS_STRING (&value))
288                                  {
289                                    g_free (item->text);
290                                    item->text =  (gchar *) g_value_dup_string (&value);
291                                    g_value_unset (&value);
292                                    break;
293                                  }
294                             }
295                         }
296                       name = item->text;
297                     }
298                 }
299             }
300         }
301     }
302   return name;
303 }
304
305 /*
306  * We report that this object has no children
307  */
308
309 static gint
310 gail_item_get_n_children (AtkObject* obj)
311 {
312   return 0;
313 }
314
315 static AtkObject*
316 gail_item_ref_child (AtkObject *obj,
317                      gint      i)
318 {
319   return NULL;
320 }
321
322 static void
323 gail_item_notify_label_gtk (GObject           *obj,
324                             GParamSpec        *pspec,
325                             gpointer           data)
326 {
327   AtkObject* atk_obj = ATK_OBJECT (data);
328   GtkLabel *label;
329   GailItem *gail_item;
330
331   if (strcmp (pspec->name, "label") == 0)
332     {
333       const gchar* label_text;
334
335       label = GTK_LABEL (obj);
336
337       label_text = gtk_label_get_text (label);
338
339       gail_item = GAIL_ITEM (atk_obj);
340       gail_text_util_text_setup (gail_item->textutil, label_text);
341
342       if (atk_obj->name == NULL)
343       {
344         /*
345          * The label has changed so notify a change in accessible-name
346          */
347         g_object_notify (G_OBJECT (atk_obj), "accessible-name");
348       }
349       /*
350        * The label is the only property which can be changed
351        */
352       g_signal_emit_by_name (atk_obj, "visible_data_changed");
353     }
354 }
355
356 /* atktext.h */
357
358 static void
359 atk_text_interface_init (AtkTextIface *iface)
360 {
361   iface->get_text = gail_item_get_text;
362   iface->get_character_at_offset = gail_item_get_character_at_offset;
363   iface->get_text_before_offset = gail_item_get_text_before_offset;
364   iface->get_text_at_offset = gail_item_get_text_at_offset;
365   iface->get_text_after_offset = gail_item_get_text_after_offset;
366   iface->get_character_count = gail_item_get_character_count;
367   iface->get_character_extents = gail_item_get_character_extents;
368   iface->get_offset_at_point = gail_item_get_offset_at_point;
369   iface->get_run_attributes = gail_item_get_run_attributes;
370   iface->get_default_attributes = gail_item_get_default_attributes;
371 }
372
373 static gchar*
374 gail_item_get_text (AtkText *text,
375                     gint    start_pos,
376                     gint    end_pos)
377 {
378   GtkWidget *widget;
379   GtkWidget *label;
380   GailItem *item;
381   const gchar *label_text;
382
383   widget = GTK_ACCESSIBLE (text)->widget;
384   if (widget == NULL)
385     /* State is defunct */
386     return NULL;
387
388   label = get_label_from_container (widget);
389
390   if (!GTK_IS_LABEL (label))
391     return NULL;
392
393   item = GAIL_ITEM (text);
394   if (!item->textutil) 
395     gail_item_init_textutil (item, label);
396
397   label_text = gtk_label_get_text (GTK_LABEL (label));
398
399   if (label_text == NULL)
400     return NULL;
401   else
402   {
403     return gail_text_util_get_substring (item->textutil, 
404                                          start_pos, end_pos);
405   }
406 }
407
408 static gchar*
409 gail_item_get_text_before_offset (AtkText         *text,
410                                   gint            offset,
411                                   AtkTextBoundary boundary_type,
412                                   gint            *start_offset,
413                                   gint            *end_offset)
414 {
415   GtkWidget *widget;
416   GtkWidget *label;
417   GailItem *item;
418   
419   widget = GTK_ACCESSIBLE (text)->widget;
420   
421   if (widget == NULL)
422     /* State is defunct */
423     return NULL;
424   
425   /* Get label */
426   label = get_label_from_container (widget);
427
428   if (!GTK_IS_LABEL(label))
429     return NULL;
430
431   item = GAIL_ITEM (text);
432   if (!item->textutil)
433     gail_item_init_textutil (item, label);
434
435   return gail_text_util_get_text (item->textutil,
436                            gtk_label_get_layout (GTK_LABEL (label)), GAIL_BEFORE_OFFSET, 
437                            boundary_type, offset, start_offset, end_offset); 
438 }
439
440 static gchar*
441 gail_item_get_text_at_offset (AtkText         *text,
442                               gint            offset,
443                               AtkTextBoundary boundary_type,
444                               gint            *start_offset,
445                               gint            *end_offset)
446 {
447   GtkWidget *widget;
448   GtkWidget *label;
449   GailItem *item;
450  
451   widget = GTK_ACCESSIBLE (text)->widget;
452   
453   if (widget == NULL)
454     /* State is defunct */
455     return NULL;
456   
457   /* Get label */
458   label = get_label_from_container (widget);
459
460   if (!GTK_IS_LABEL(label))
461     return NULL;
462
463   item = GAIL_ITEM (text);
464   if (!item->textutil)
465     gail_item_init_textutil (item, label);
466
467   return gail_text_util_get_text (item->textutil,
468                               gtk_label_get_layout (GTK_LABEL (label)), GAIL_AT_OFFSET, 
469                               boundary_type, offset, start_offset, end_offset);
470 }
471
472 static gchar*
473 gail_item_get_text_after_offset (AtkText         *text,
474                                  gint            offset,
475                                  AtkTextBoundary boundary_type,
476                                  gint            *start_offset,
477                                  gint            *end_offset)
478 {
479   GtkWidget *widget;
480   GtkWidget *label;
481   GailItem *item;
482
483   widget = GTK_ACCESSIBLE (text)->widget;
484   
485   if (widget == NULL)
486   {
487     /* State is defunct */
488     return NULL;
489   }
490   
491   /* Get label */
492   label = get_label_from_container (widget);
493
494   if (!GTK_IS_LABEL(label))
495     return NULL;
496
497   item = GAIL_ITEM (text);
498   if (!item->textutil)
499     gail_item_init_textutil (item, label);
500
501   return gail_text_util_get_text (item->textutil,
502                            gtk_label_get_layout (GTK_LABEL (label)), GAIL_AFTER_OFFSET, 
503                            boundary_type, offset, start_offset, end_offset);
504 }
505
506 static gint
507 gail_item_get_character_count (AtkText *text)
508 {
509   GtkWidget *widget;
510   GtkWidget *label;
511
512   widget = GTK_ACCESSIBLE (text)->widget;
513   if (widget == NULL)
514     /* State is defunct */
515     return 0;
516
517   label = get_label_from_container (widget);
518
519   if (!GTK_IS_LABEL(label))
520     return 0;
521
522   return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1);
523 }
524
525 static void
526 gail_item_get_character_extents (AtkText      *text,
527                                  gint         offset,
528                                  gint         *x,
529                                  gint         *y,
530                                  gint         *width,
531                                  gint         *height,
532                                  AtkCoordType coords)
533 {
534   GtkWidget *widget;
535   GtkWidget *label;
536   PangoRectangle char_rect;
537   gint index, x_layout, y_layout;
538   const gchar *label_text;
539  
540   widget = GTK_ACCESSIBLE (text)->widget;
541
542   if (widget == NULL)
543     /* State is defunct */
544     return;
545
546   label = get_label_from_container (widget);
547
548   if (!GTK_IS_LABEL(label))
549     return;
550   
551   gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
552   label_text = gtk_label_get_text (GTK_LABEL (label));
553   index = g_utf8_offset_to_pointer (label_text, offset) - label_text;
554   pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect);
555   
556   gail_misc_get_extents_from_pango_rectangle (label, &char_rect, 
557                     x_layout, y_layout, x, y, width, height, coords);
558
559
560 static gint 
561 gail_item_get_offset_at_point (AtkText      *text,
562                                gint         x,
563                                gint         y,
564                                AtkCoordType coords)
565
566   GtkWidget *widget;
567   GtkWidget *label;
568   gint index, x_layout, y_layout;
569   const gchar *label_text;
570
571   widget = GTK_ACCESSIBLE (text)->widget;
572   if (widget == NULL)
573     /* State is defunct */
574     return -1;
575
576   label = get_label_from_container (widget);
577
578   if (!GTK_IS_LABEL(label))
579     return -1;
580   
581   gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout);
582   
583   index = gail_misc_get_index_at_point_in_layout (label, 
584                                               gtk_label_get_layout (GTK_LABEL (label)), 
585                                               x_layout, y_layout, x, y, coords);
586   label_text = gtk_label_get_text (GTK_LABEL (label));
587   if (index == -1)
588     {
589       if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
590         return g_utf8_strlen (label_text, -1);
591
592       return index;  
593     }
594   else
595     return g_utf8_pointer_to_offset (label_text, label_text + index);  
596 }
597
598 static AtkAttributeSet*
599 gail_item_get_run_attributes (AtkText *text,
600                               gint    offset,
601                               gint    *start_offset,
602                               gint    *end_offset)
603 {
604   GtkWidget *widget;
605   GtkWidget *label;
606   AtkAttributeSet *at_set = NULL;
607   GtkJustification justify;
608   GtkTextDirection dir;
609
610   widget = GTK_ACCESSIBLE (text)->widget;
611   if (widget == NULL)
612     /* State is defunct */
613     return NULL;
614
615   label = get_label_from_container (widget);
616
617   if (!GTK_IS_LABEL(label))
618     return NULL;
619   
620   /* Get values set for entire label, if any */
621   justify = gtk_label_get_justify (GTK_LABEL (label));
622   if (justify != GTK_JUSTIFY_CENTER)
623     {
624       at_set = gail_misc_add_attribute (at_set, 
625                                         ATK_TEXT_ATTR_JUSTIFICATION,
626      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify)));
627     }
628   dir = gtk_widget_get_direction (label);
629   if (dir == GTK_TEXT_DIR_RTL)
630     {
631       at_set = gail_misc_add_attribute (at_set, 
632                                         ATK_TEXT_ATTR_DIRECTION,
633      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
634     }
635
636   at_set = gail_misc_layout_get_run_attributes (at_set,
637                                                 gtk_label_get_layout (GTK_LABEL (label)),
638                                                 (gchar *) gtk_label_get_text (GTK_LABEL (label)),
639                                                 offset,
640                                                 start_offset,
641                                                 end_offset);
642   return at_set;
643 }
644
645 static AtkAttributeSet*
646 gail_item_get_default_attributes (AtkText *text)
647 {
648   GtkWidget *widget;
649   GtkWidget *label;
650   AtkAttributeSet *at_set = NULL;
651
652   widget = GTK_ACCESSIBLE (text)->widget;
653   if (widget == NULL)
654     /* State is defunct */
655     return NULL;
656
657   label = get_label_from_container (widget);
658
659   if (!GTK_IS_LABEL(label))
660     return NULL;
661
662   at_set = gail_misc_get_default_attributes (at_set,
663                                              gtk_label_get_layout (GTK_LABEL (label)),
664                                              widget);
665   return at_set;
666 }
667
668 static gunichar 
669 gail_item_get_character_at_offset (AtkText *text,
670                                    gint    offset)
671 {
672   GtkWidget *widget;
673   GtkWidget *label;
674   const gchar *string;
675   gchar *index;
676
677   widget = GTK_ACCESSIBLE (text)->widget;
678   if (widget == NULL)
679     /* State is defunct */
680     return '\0';
681
682   label = get_label_from_container (widget);
683
684   if (!GTK_IS_LABEL(label))
685     return '\0';
686   string = gtk_label_get_text (GTK_LABEL (label));
687   if (offset >= g_utf8_strlen (string, -1))
688     return '\0';
689   index = g_utf8_offset_to_pointer (string, offset);
690
691   return g_utf8_get_char (index);
692 }
693
694 static GtkWidget*
695 get_label_from_container (GtkWidget *container)
696 {
697   GtkWidget *label;
698   GList *children, *tmp_list;
699
700   if (!GTK_IS_CONTAINER (container))
701     return NULL;
702  
703   children = gtk_container_get_children (GTK_CONTAINER (container));
704   label = NULL;
705
706   for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next) 
707     {
708       if (GTK_IS_LABEL (tmp_list->data))
709         {
710            label = tmp_list->data;
711            break;
712         }
713       /*
714        * Get label from menu item in desktop background preferences
715        * option menu. See bug #144084.
716        */
717       else if (GTK_IS_BOX (tmp_list->data))
718         {
719            label = get_label_from_container (GTK_WIDGET (tmp_list->data));
720            if (label)
721              break;
722         }
723     }
724   g_list_free (children);
725
726   return label;
727 }