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