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