]> Pileus Git - ~andy/gtk/blob - modules/other/gail/tests/ferret.c
b8882e8122ecf838cbe1598dde56b9b7a6e809bc
[~andy/gtk] / modules / other / gail / tests / ferret.c
1 #define MAX_BUFFER 256
2 #define MAX_GROUPS 20
3 #define MAX_NAME_VALUE 20
4
5 #include "config.h"
6
7 #include <sys/types.h>
8 #include <netinet/in.h>
9 #include <sys/socket.h>
10 #include <sys/un.h>
11 #include <errno.h>
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <strings.h>
15
16 #include "testlib.h"
17
18 typedef enum
19 {
20   OBJECT,
21   ACTION,
22   COMPONENT,
23   IMAGE,
24   SELECTION,
25   TABLE,
26   TEXT,
27   VALUE,
28   END_TABS
29 } TabNumber;
30
31 typedef enum
32 {
33   OBJECT_INTERFACE,
34   RELATION_INTERFACE,
35   STATE_INTERFACE,
36   ACTION_INTERFACE,
37   COMPONENT_INTERFACE,
38   IMAGE_INTERFACE,
39   SELECTION_INTERFACE,
40   TABLE_INTERFACE,
41   TEXT_INTERFACE,
42   TEXT_ATTRIBUTES,
43   VALUE_INTERFACE
44 } GroupId;
45
46 typedef enum
47 {
48   VALUE_STRING,
49   VALUE_BOOLEAN,
50   VALUE_TEXT,
51   VALUE_BUTTON
52 } ValueType;
53
54 /* GUI Information for the group */
55
56 typedef struct
57 {
58   GroupId       group_id;
59   GtkFrame      *scroll_outer_frame;
60   GtkWidget     *frame;
61   GtkVBox       *group_vbox;
62   GtkAdjustment *adj;
63   GList         *name_value;
64   gchar         *name;
65   gboolean      is_scrolled;
66   gint          default_height;
67 } GroupInfo;
68
69 typedef struct
70 {
71   GList     *groups;
72   GtkWidget *page;
73   GtkWidget *main_box;
74   gchar     *name;
75 } TabInfo;
76
77 typedef struct
78 {
79   ValueType type;
80   gboolean  active;
81
82   GtkHBox *column1, *column2, *hbox;
83   GtkLabel *label;
84
85   GtkButton *button;
86   GValue    button_gval;
87   gulong    signal_id;
88   AtkObject *atkobj;
89   gint      action_num;
90
91   GtkWidget *string;
92   GtkWidget *boolean;
93   GtkWidget *text;
94 } NameValue;
95
96 typedef enum {
97    FERRET_SIGNAL_OBJECT,
98    FERRET_SIGNAL_TEXT,
99    FERRET_SIGNAL_TABLE
100 } FerretSignalType;
101
102 /* Function prototypes */
103
104 /* GUI functions */
105
106 static void _init_data(void);
107 static void _create_window(void);
108 static void _add_menu(GtkWidget ** menu, GtkWidget ** menuitem,
109   gchar * name, gboolean init_value, GCallback func);
110 static void _clear_tab(TabNumber tab_n);
111 static void _greyout_tab (GtkWidget *widget, gboolean is_sensitive);
112 static void _finished_group(TabNumber tab_n, gint group_num);
113 static gboolean _object_is_ours (AtkObject *aobject);
114 static void _create_event_watcher (void);
115
116 /* Mouse Watcher/Magnifier/Festival functions */
117
118 static gboolean _mouse_watcher (GSignalInvocationHint *ihint,
119         guint                  n_param_values,
120         const GValue          *param_values,
121         gpointer               data);
122 static gboolean _button_watcher (GSignalInvocationHint *ihint,
123         guint                  n_param_values,
124         const GValue          *param_values,
125         gpointer               data);
126 static void _send_to_magnifier (gint x, gint y, gint w, gint h);
127 static void _send_to_festival (const gchar * name,
128   const gchar * role_name, const gchar * accel);
129 static void _speak_caret_event (AtkObject * aobject);
130 static void _festival_say (const gchar * text);
131 static void _festival_write (const gchar * text, int fd);
132 static gint _festival_init (void);
133
134 /* Update functions */
135
136 static void _update_current_page(GtkNotebook *notebook, gpointer p,
137   guint current_page);
138 static void _update(TabNumber top_tab, AtkObject *aobject);
139
140 /* Print functions */
141
142 static void _print_accessible (AtkObject *aobject);
143
144 static gint _print_object (AtkObject *aobject);
145 static gint _print_relation (AtkObject *aobject);
146 static gint _print_state (AtkObject *aobject);
147
148 static gint _print_action (AtkAction *aobject);
149 static gint _print_component (AtkComponent *aobject);
150 static gint _print_image (AtkImage *aobject);
151 static gint _print_selection (AtkSelection *aobject);
152 static gint _print_table (AtkTable *aobject);
153 static gint _print_text (AtkText *aobject);
154 static gint _print_text_attributes (AtkText *aobject);
155 static gint _print_value (AtkValue *aobject);
156 static void _print_value_type(gint group_num, gchar *type, GValue *value);
157 static gint _print_groupname(TabNumber tab_n, GroupId group_id,
158   const char *groupname);
159 static NameValue* _print_key_value(TabNumber tab_n, gint group_number,
160   const char *label, gpointer value, ValueType type);
161 static void _print_signal(AtkObject *aobject, FerretSignalType type,
162   const char *name, const char *info);
163
164 /* Data Access functions */
165
166 static GroupInfo* _get_group(TabInfo *tab, GroupId group_id,
167   const gchar *groupname);
168 void _get_group_scrolled(GroupInfo *group);
169 static NameValue* _get_name_value(GroupInfo *group, const gchar *label,
170   gpointer value, ValueType type);
171
172 /* Signal handlers */
173
174 static void _update_handlers(AtkObject *obj);
175 static void _notify_text_insert_handler (GObject *obj,
176   int position, int offset);
177 static void _notify_text_delete_handler (GObject *obj,
178   int position, int offset);
179 static void _notify_caret_handler (GObject *obj, int position);
180 static void _notify_table_row_inserted (GObject *obj,
181   gint start_offset, gint length);
182 static void _notify_table_column_inserted (GObject *obj,
183   gint start_offset, gint length);
184 static void _notify_table_row_deleted (GObject *obj,
185   gint start_offset, gint length);
186 static void _notify_table_column_deleted (GObject *obj,
187   gint start_offset, gint length);
188 static void _notify_table_row_reordered (GObject *obj);
189 static void _notify_table_column_reordered (GObject *obj);
190 static void _notify_object_child_added (GObject *obj,
191   gint index, AtkObject *child);
192 static void _notify_object_child_removed (GObject *obj,
193   gint index, AtkObject *child);
194 static void _notify_object_state_change (GObject *obj,
195   gchar *name, gboolean set);
196
197 /* Property handlers */
198
199 static void _property_change_handler (AtkObject *obj,
200   AtkPropertyValues *values);
201
202 /* Ferret GUI callbacks */
203
204 void _action_cb(GtkWidget *widget, gpointer  *userdata);
205 void _toggle_terminal(GtkCheckMenuItem *checkmenuitem,
206   gpointer user_data);
207 void _toggle_no_signals(GtkCheckMenuItem *checkmenuitem,
208   gpointer user_data);
209 void _toggle_magnifier(GtkCheckMenuItem *checkmenuitem,
210   gpointer user_data);
211 void _toggle_festival(GtkCheckMenuItem *checkmenuitem,
212   gpointer user_data);
213 void _toggle_festival_terse(GtkCheckMenuItem *checkmenuitem,
214   gpointer user_data);
215 void _toggle_trackmouse(GtkCheckMenuItem *checkmenuitem,
216   gpointer user_data);
217 void _toggle_trackfocus(GtkCheckMenuItem *checkmenuitem,
218   gpointer user_data);
219
220 /* Global variables */
221 static GtkNotebook *notebook;
222 static TabInfo  *nbook_tabs[END_TABS];
223 static gint mouse_watcher_focus_id = -1;
224 static gint mouse_watcher_button_id = -1;
225 static gint focus_tracker_id = -1;
226 static gboolean use_magnifier = FALSE;
227 static gboolean use_festival = FALSE;
228 static gboolean track_mouse = FALSE;
229 static gboolean track_focus = TRUE;
230 static gboolean say_role = TRUE;
231 static gboolean say_accel = TRUE;
232 static gboolean display_ascii = FALSE;
233 static gboolean no_signals = FALSE;
234 static gint last_caret_offset = 0;
235
236 static AtkObject *last_object = NULL;
237 static GtkWidget *mainWindow = NULL;
238 static GtkWidget *vbox1 = NULL;
239 static GtkWidget *menu = NULL;
240 static GtkWidget *menutop = NULL;
241 static GtkWidget *menubar = NULL;
242 static GtkWidget *menuitem_terminal = NULL;
243 static GtkWidget *menuitem_no_signals = NULL;
244 static GtkWidget *menuitem_magnifier = NULL;
245 static GtkWidget *menuitem_festival = NULL;
246 static GtkWidget *menuitem_festival_terse = NULL;
247 static GtkWidget *menuitem_trackmouse = NULL;
248 static GtkWidget *menuitem_trackfocus = NULL;
249
250 #ifdef HAVE_SOCKADDR_UN_SUN_LEN
251 static struct sockaddr_un mag_server = { 0, AF_UNIX , "/tmp/magnifier_socket" };
252 static struct sockaddr_un client = { 0 , AF_UNIX, "/tmp/mag_client"};
253 #else
254 static struct sockaddr_un mag_server = { AF_UNIX , "/tmp/magnifier_socket" };
255 static struct sockaddr_un client = { AF_UNIX, "/tmp/mag_client"};
256 #endif
257
258 /* GUI Information for the output window */
259 typedef struct
260 {
261   GtkWindow     *outputWindow;
262   GtkWidget     *hbox;
263   GtkWidget     *vbox;
264   GtkWidget     *label;
265   GtkWidget     *textInsert;
266   gchar         *testTitle;
267 } MainDialog;
268
269 static void
270 _send_to_magnifier(gint x, gint y, gint w, gint h)
271 {
272   int desc;
273   int length_msg G_GNUC_UNUSED;
274   gchar buff[100];
275
276   sprintf (buff, "~5:%d,%d", x+w/2, y+h/2);
277
278 #ifdef MAG_DEBUG
279   g_print ("sending magnifier: %s\n", buff);
280 #endif
281
282 #ifdef HAVE_SOCKADDR_UN_SUN_LEN
283   mag_server.sun_len = SUN_LEN(&mag_server);
284   client.sun_len = SUN_LEN(&client);
285 #endif
286   
287   if((desc=socket(AF_UNIX,SOCK_STREAM,0)) == -1){
288     perror("socket");
289     return;
290   }
291   unlink("/tmp/mag_client");
292
293   if (bind(desc, (struct sockaddr*)&client, sizeof(client)) == -1)
294     {
295       perror("bind");
296       return;
297     }
298
299   if (connect(desc,(struct sockaddr*)&mag_server,sizeof(mag_server)) == -1)
300     {
301       perror("connect");
302       return;
303     }
304
305   length_msg = write(desc,buff,strlen(buff));
306   unlink("/tmp/mag_client");
307   return;
308 }
309
310 static int _festival_init (void)
311 {
312   int fd;
313   struct sockaddr_in name;
314   int tries = 2;
315
316   name.sin_family = AF_INET;
317   name.sin_port = htons (1314);
318   name.sin_addr.s_addr = htonl(INADDR_ANY);
319   fd = socket (PF_INET, SOCK_STREAM, 0);
320
321   while (connect(fd, (struct sockaddr *) &name, sizeof (name)) < 0) {
322     if (!tries--) {
323       perror ("connect");
324       return -1;
325     }
326   }
327
328   _festival_write ("(audio_mode'async)", fd);
329   return fd;
330 }
331
332 static void _festival_say (const gchar *text)
333 {
334   static int fd = 0;
335   gchar *quoted;
336   gchar *p;
337   gchar prefix [100];
338   const gchar *stretch;
339
340   fprintf (stderr, "saying %s\n", text);
341
342   if (!fd)
343     {
344       fd = _festival_init ();
345     }
346
347   quoted = g_malloc(100+strlen(text)*2);
348
349   stretch = g_strdup (g_getenv ("FESTIVAL_STRETCH"));
350   if (!stretch) stretch = "0.75";
351   sprintf (prefix, "(audio_mode'shutup)\n (Parameter.set 'Duration_Stretch %s)\n (SayText \"", stretch);
352   
353   strcpy(quoted, prefix);
354   p = quoted + strlen (prefix);
355   while (*text) {
356     if ( *text == '\\' || *text == '"' )
357       *p = '\\';
358     *p++ = *text++;
359   }
360   *p++ = '"';
361   *p++ = ')';
362   *p = 0;
363
364   _festival_write (quoted, fd);
365
366   g_free(quoted);
367 }
368
369
370 static void _send_to_festival (const gchar *role_name,
371   const gchar *name, const gchar *accel)
372 {
373   gchar *string;
374   int len = (strlen (role_name)+1 + strlen (name)+2 + 4 + strlen (accel)+2);
375   int i, j;
376   gchar ch;
377   gchar *accel_name;
378   
379   string = (gchar *) g_malloc (len * sizeof (gchar));
380
381   i = 0;
382   if (say_role)
383     {
384       j = 0;
385       while (role_name[j])
386         {
387           ch = role_name[j++];
388           if (ch == '_') ch = ' ';
389           string[i++] = ch;
390         };
391       string[i++] = ' ';
392     }
393   j = 0;
394   while (name[j])
395     {
396       ch = name[j++];
397       if (ch == '_') ch = ' ';
398       string[i++] = ch;
399     };
400   if ((say_accel) && (strlen (accel) > 0))
401     {
402       accel_name = (gchar *)accel;
403       if (strncmp (accel, "<C", 2) == 0)
404         {
405           accel_name = strncpy (accel_name, " control ", 9);
406         }
407       else if (strncmp (accel, " control", 5))
408         {
409           string[i++] = ' ';
410           string[i++] = 'a';
411           string[i++] = 'l';
412           string[i++] = 't';
413           string[i++] = ' ';
414         }
415       j = 0;
416       while (accel_name[j])
417         {
418           ch = accel_name[j++];
419           if (ch == '_') ch = ' ';
420           string[i++] = ch;
421         };
422     }
423   string[i] = '\0';
424
425   _festival_say (string);
426   g_free (string);
427 }
428
429 static void _festival_write (const gchar *command_string, int fd)
430 {
431   gssize n_bytes;
432
433   if (fd < 0) {
434     perror("socket");
435     return;
436   }
437   n_bytes = write(fd, command_string, strlen(command_string));
438   g_assert (n_bytes == strlen(command_string));
439 }
440
441 static void _speak_caret_event (AtkObject *aobject)
442 {
443   gint dummy1, dummy2;
444   gint caret_offset = atk_text_get_caret_offset (ATK_TEXT (aobject));
445   gchar * text;
446
447   if (abs(caret_offset - last_caret_offset) > 1)
448     {
449       text = atk_text_get_text_at_offset (ATK_TEXT (aobject),
450                                               caret_offset,
451                                               ATK_TEXT_BOUNDARY_LINE_START,
452                                               &dummy1,
453                                               &dummy2);
454     }
455   else
456     {
457       text = atk_text_get_text_before_offset (ATK_TEXT (aobject),
458                                               caret_offset,
459                                               ATK_TEXT_BOUNDARY_CHAR,
460                                               &dummy1,
461                                               &dummy2);
462     }
463   _festival_say (text);
464   g_free (text);
465   last_caret_offset = caret_offset;
466 }
467
468 static void
469 _greyout_tab (GtkWidget *page_child, gboolean is_sensitive)
470 {
471   GtkWidget *tab;
472
473   tab = gtk_notebook_get_tab_label (notebook, page_child);
474   if (tab)
475       gtk_widget_set_sensitive (GTK_WIDGET (tab), is_sensitive);
476 }
477
478 static void
479 _refresh_notebook (AtkObject *aobject)
480 {
481   if (ATK_IS_OBJECT (aobject))
482   {
483     _greyout_tab (nbook_tabs[ACTION]->page, ATK_IS_ACTION(aobject));
484     _greyout_tab (nbook_tabs[COMPONENT]->page, ATK_IS_COMPONENT(aobject));
485     _greyout_tab (nbook_tabs[IMAGE]->page, ATK_IS_IMAGE(aobject));
486     _greyout_tab (nbook_tabs[SELECTION]->page, ATK_IS_SELECTION(aobject));
487     _greyout_tab (nbook_tabs[TABLE]->page, ATK_IS_TABLE(aobject));
488     _greyout_tab (nbook_tabs[TEXT]->page, ATK_IS_TEXT(aobject));
489     _greyout_tab (nbook_tabs[VALUE]->page, ATK_IS_VALUE(aobject));
490   }
491 }
492
493 static void _print_accessible (AtkObject *aobject)
494 {
495   TabNumber top_tab;
496
497   if (_object_is_ours(aobject))
498     {
499       if (display_ascii)
500         g_print("\nFocus entered the ferret output window!\n");
501       return;
502     }
503
504   _refresh_notebook(aobject);
505
506   if (display_ascii)
507     g_print("\nFocus change\n");
508
509   /* Do not attach signal handlers if the user has asked not to */
510   if (!no_signals)
511     _update_handlers(aobject);
512   else
513     last_object = aobject; /* _update_handler normally does this */
514
515   top_tab = gtk_notebook_get_current_page (notebook) + OBJECT;
516   _update(top_tab, aobject);
517
518   if (use_magnifier)
519     {
520       gint x, y;
521       gint w=0, h=0;
522       
523       if (ATK_IS_TEXT (aobject))
524         {
525           gint x0, y0, w0, h0;
526           gint xN, yN, wN, hN;
527           gint len;
528           len = atk_text_get_character_count (ATK_TEXT (aobject));
529           atk_text_get_character_extents (ATK_TEXT (aobject), 0,
530                                           &x0, &y0, &w0, &h0,
531                                           ATK_XY_SCREEN);
532           if (len > 0)
533             {
534               atk_text_get_character_extents (ATK_TEXT (aobject), len-1,
535                                               &xN, &yN, &wN, &hN,
536                                               ATK_XY_SCREEN);
537               x = MIN (x0, xN);
538               y = MIN (y0, yN);
539               w = MAX (x0+w0, xN+wN) - x;
540               h = MAX (y0+h0, yN+hN) - y;
541             }
542           else
543             {
544               x = x0;
545               y = y0;
546             }
547         } 
548       else if (ATK_IS_COMPONENT (aobject))
549         {
550           atk_component_get_extents (ATK_COMPONENT(aobject),
551                                      &x, &y, &w, &h,
552                                      ATK_XY_SCREEN);
553         }
554       if (w > -1) _send_to_magnifier (x, y, w, h);
555     }
556 }
557
558 static gboolean
559 _object_is_ours (AtkObject *aobject)
560 {
561   /* determine whether this object is parented by our own accessible... */
562
563    AtkObject *toplevel = aobject;
564
565    while (atk_object_get_role(aobject) != ATK_ROLE_FRAME)
566      {
567        aobject = atk_object_get_parent (aobject);
568        if (aobject == NULL) break;
569        toplevel = aobject;
570      };
571
572   /*
573    * Some widgets do not have an ATK_ROLE_FRAME at the top,
574    * so ignore those.
575    */
576    if (aobject != NULL)
577      {
578        GtkWidget *widget;
579
580        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (toplevel));
581        if (widget == mainWindow)
582            return TRUE;
583      }
584
585   return FALSE;
586 }
587
588 static gchar *
589 ferret_get_name_from_container (AtkObject *aobject)
590 {
591   gchar *s = NULL;
592   gint n = atk_object_get_n_accessible_children (aobject);
593   gint i = 0;
594   
595   while (!s && (i < n))
596     {
597       AtkObject *child;     
598       child = atk_object_ref_accessible_child (aobject, i);
599       if (ATK_IS_TEXT (child))
600         {
601                 gint count = atk_text_get_character_count (ATK_TEXT (child));
602                 s = atk_text_get_text (ATK_TEXT (child),
603                                        0,
604                                        count);
605         }
606       g_object_unref (child);
607       ++i;          
608     }
609   
610   if (!s)
611     {
612       s = g_strdup ("");
613     }
614   return s;     
615 }
616
617 static gint
618 _print_object (AtkObject *aobject)
619 {
620     GtkWidget *widget;
621     G_CONST_RETURN gchar * parent_name = NULL;
622     G_CONST_RETURN gchar * name = NULL;
623     G_CONST_RETURN gchar * description = NULL;
624     G_CONST_RETURN gchar * typename = NULL;
625     G_CONST_RETURN gchar * parent_typename = NULL;
626     G_CONST_RETURN gchar * role_name = NULL;
627     G_CONST_RETURN gchar * accel_name = NULL;
628     G_CONST_RETURN gchar * text = NULL;
629     AtkRole role;
630     AtkObject *parent = NULL;
631     static AtkObject *prev_aobject = NULL;
632     gint n_children = 0;
633     gint index_in_parent = -1;
634     gchar *output_str;
635     gint group_num;
636     TabNumber tab_n = OBJECT;
637
638     group_num = _print_groupname(tab_n, OBJECT_INTERFACE, "Object Interface");
639
640     name = atk_object_get_name (aobject);
641     typename = g_type_name (G_OBJECT_TYPE (aobject));
642     description = atk_object_get_description (aobject);
643     parent = atk_object_get_parent(aobject);
644     if (parent)
645       index_in_parent = atk_object_get_index_in_parent(aobject);
646     n_children = atk_object_get_n_accessible_children(aobject);
647     role = atk_object_get_role(aobject);
648     role_name = atk_role_get_name(role);
649
650     if (ATK_IS_ACTION (aobject))
651       {
652         accel_name = atk_action_get_keybinding (ATK_ACTION(aobject), 0);
653         if (!accel_name) accel_name = "";
654       }
655     else
656       {
657         accel_name = "";
658       }
659
660     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (aobject));
661     if (widget)
662       {
663         _print_key_value(tab_n, group_num, "Widget name",
664           (gpointer)gtk_widget_get_name (widget),
665           VALUE_STRING);
666       }
667     else
668       {
669         _print_key_value(tab_n, group_num, "Widget name", "No Widget",
670           VALUE_STRING);
671       }
672
673     if (typename)
674       {
675         _print_key_value(tab_n, group_num, "Accessible Type",
676           (gpointer)typename, VALUE_STRING);
677       }
678     else
679       {
680         _print_key_value(tab_n, group_num, "Accessible Type", "NULL",
681           VALUE_STRING);
682       }
683
684     if (name)
685       {
686         _print_key_value(tab_n, group_num, "Accessible Name",
687           (gpointer)name, VALUE_STRING);
688       }
689     else
690       {
691         _print_key_value(tab_n, group_num, "Accessible Name", "(unknown)",
692           VALUE_STRING);
693       }
694     if (use_festival)
695       {
696         if (aobject != prev_aobject)
697           {
698             if (ATK_IS_TEXT (aobject) && !name)
699               {
700                 text = 
701                   atk_text_get_text_at_offset (ATK_TEXT (aobject),
702                                                (gint) 0,
703                                                ATK_TEXT_BOUNDARY_SENTENCE_END,
704                                                (gint *) NULL,
705                                                (gint *) NULL);
706                 fprintf (stderr, "first sentence: %s\n", text);
707                 _send_to_festival (role_name, 
708                                    text, "");
709                 if (!name) name = "no name";
710               }
711             else 
712               { 
713                 text = "";
714                 if (!name)
715                   {
716                     if (atk_object_get_role (aobject) == ATK_ROLE_TABLE_CELL)
717                       {
718                         gchar *cname = ferret_get_name_from_container (aobject);
719                         if (cname) name = g_strdup (cname);
720                       }
721                     else if (atk_object_get_role (aobject) == ATK_ROLE_CHECK_BOX)
722                       {
723                         name = g_strdup ("check box");
724                       }
725                     else
726                       {
727                         name = "no name";
728                       }
729                   }
730                 _send_to_festival (role_name, name, accel_name);
731               }
732           }
733       }
734
735     if (parent)
736       {
737         parent_name = atk_object_get_name(parent);
738
739         parent_typename = g_type_name (G_OBJECT_TYPE (parent));
740
741         if (parent_typename)
742           {
743             _print_key_value(tab_n, group_num, "Parent Accessible Type",
744               (gpointer)parent_typename, VALUE_STRING);
745           }
746         else
747           {
748             _print_key_value(tab_n, group_num,
749               "Parent Accessible Type", "NULL", VALUE_STRING);
750           }
751
752         if (parent_name)
753           {
754             _print_key_value(tab_n, group_num, "Parent Accessible Name",
755               (gpointer)parent_name, VALUE_STRING);
756           }
757         else
758           {
759             _print_key_value(tab_n, group_num,
760               "Parent Accessible Name", "NULL", VALUE_STRING);
761           }
762
763         output_str = g_strdup_printf("%d", index_in_parent);
764         _print_key_value(tab_n, group_num, "Index in Parent",
765           (gpointer)output_str, VALUE_STRING);
766         g_free(output_str);
767       }
768     else
769       {
770         _print_key_value(tab_n, group_num, "Parent", "NULL", VALUE_STRING);
771       }
772
773     if (description)
774       {
775         _print_key_value(tab_n, group_num, "Accessible Description",
776           (gpointer)description, VALUE_STRING);
777       }
778     else
779       {
780         _print_key_value(tab_n, group_num,
781           "Accessible Description", "NULL", VALUE_STRING);
782       }
783
784     if (role_name)
785       {
786       _print_key_value(tab_n, group_num, "Accessible Role", (gpointer)role_name,
787         VALUE_STRING);
788       }
789     else
790       {
791       _print_key_value(tab_n, group_num, "Accessible Role", "NULL",
792         VALUE_STRING);
793       }
794
795     output_str = g_strdup_printf("%d", n_children);
796     _print_key_value(tab_n, group_num, "Number Children", (gpointer)output_str,
797        VALUE_STRING);
798     g_free(output_str);
799     prev_aobject = aobject;
800
801     return(group_num);
802 }
803
804 static gint
805 _print_relation (AtkObject *aobject)
806 {
807     AtkRelationSet* relation_set = atk_object_ref_relation_set (aobject);
808     gint n_relations =  atk_relation_set_get_n_relations (relation_set);
809     gint group_num;
810     TabNumber tab_n = OBJECT;
811
812     group_num = _print_groupname(tab_n, RELATION_INTERFACE,
813       "Relation Interface");
814
815     if (relation_set)
816       {
817         AtkRelation * relation;
818         G_CONST_RETURN gchar * relation_name = NULL;
819         G_CONST_RETURN gchar * relation_obj_name = NULL;
820         AtkRelationType relation_type;
821         AtkObject *relation_obj;
822         GPtrArray * relation_arry;
823         gchar *label_str;
824         gchar *output_str;
825         gint i, j;
826
827         output_str = g_strdup_printf("%d", n_relations);
828         _print_key_value(tab_n, group_num,
829           "Number of Relations", (gpointer)output_str, VALUE_STRING);
830         g_free(output_str);
831
832         for (i = 0; i < n_relations; i++)
833           {
834             relation = atk_relation_set_get_relation (relation_set, i);
835
836             relation_type = atk_relation_get_relation_type (relation);
837             relation_name = atk_relation_type_get_name (relation_type);
838
839             relation_arry = atk_relation_get_target(relation);
840
841             if (relation_name)
842               {
843                 label_str = g_strdup_printf("Relation %d Name", i + 1);
844                 _print_key_value(tab_n, group_num, label_str,
845                   (gpointer)relation_name, VALUE_STRING);
846                 g_free(label_str);
847               }
848             else
849               {
850                 label_str = g_strdup_printf("Relation %d Type", i + 1);
851                 output_str = g_strdup_printf("%d", relation_type);
852                 _print_key_value(tab_n, group_num, label_str,
853                   (gpointer)output_str, VALUE_STRING);
854                 g_free(label_str);
855                 g_free(output_str);
856               }
857
858             label_str = g_strdup_printf("Relation %d with", i + 1);
859             output_str = g_strdup_printf("%d AtkObjects", relation_arry->len);
860             _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
861               VALUE_STRING);
862             g_free(label_str);
863             g_free(output_str);
864
865             for (j=0; j < relation_arry->len; j++)
866               {
867                 label_str = g_strdup_printf(
868                   "Relation %d,%d with AtkObject Name", i + 1, j + 1);
869                 relation_obj = (AtkObject *)
870                    g_ptr_array_index(relation_arry, j);
871                 relation_obj_name = atk_object_get_name(relation_obj);
872
873                 _print_key_value(tab_n, group_num, label_str,
874                   (gpointer)relation_obj_name, VALUE_STRING);
875                 g_free(label_str);
876               }
877           }
878
879         g_object_unref (relation_set);
880       }
881     return(group_num);
882 }
883
884 static gint
885 _print_state (AtkObject *aobject)
886 {
887     AtkStateSet *state_set = atk_object_ref_state_set(aobject);
888     gint group_num;
889     TabNumber tab_n = OBJECT;
890     static AtkStateType states_to_track[] =
891       {
892         ATK_STATE_ACTIVE,
893         ATK_STATE_CHECKED,
894         ATK_STATE_EXPANDED,
895         ATK_STATE_EXPANDABLE,
896         ATK_STATE_SELECTED,
897         ATK_STATE_SHOWING,
898         ATK_STATE_VISIBLE
899       };
900
901     group_num = _print_groupname(tab_n, STATE_INTERFACE,
902       "State Interface");
903
904     if (state_set)
905       {
906         gboolean boolean_value;
907         AtkStateType one_state;
908         G_CONST_RETURN gchar *name;
909         gint i;
910
911         for (i=0; i < sizeof(states_to_track)/sizeof(AtkStateType); i++)
912           {
913             one_state = (AtkStateType) states_to_track[i];
914             name = atk_state_type_get_name (one_state);
915
916             if (name)
917               {
918                 boolean_value =
919                   atk_state_set_contains_state (state_set, one_state);
920                 _print_key_value(tab_n, group_num, name,
921                   (gpointer)(&boolean_value), VALUE_BOOLEAN);
922               }
923           }
924       }
925
926     g_object_unref (state_set);
927     return(group_num);
928 }
929
930 static gint
931 _print_action (AtkAction *aobject)
932 {
933     G_CONST_RETURN gchar *action_name;
934     G_CONST_RETURN gchar *action_description;
935     G_CONST_RETURN gchar *action_keybinding;
936     gchar *label_str, *output_str;
937     gint group_num;
938     gint num_actions, j;
939     TabNumber tab_n = ACTION;
940     NameValue *nv;
941
942     group_num = _print_groupname(tab_n, ACTION_INTERFACE,
943       "Action Interface");
944
945     num_actions = atk_action_get_n_actions (aobject);
946     output_str = g_strdup_printf("%d", num_actions);
947     _print_key_value(tab_n, group_num, "Number of Actions",
948       (gpointer) output_str, VALUE_STRING);
949     g_free(output_str);
950
951     for (j = 0; j < num_actions; j++)
952       {
953         label_str = g_strdup_printf("Action %d Name", j + 1);
954         action_name = atk_action_get_name (aobject, j);
955         if (action_name)
956           {
957             nv = _print_key_value(tab_n, group_num, label_str,
958              (gpointer) action_name, VALUE_BUTTON);
959           }
960         else
961           {
962             nv = _print_key_value(tab_n, group_num, label_str, "NULL",
963               VALUE_BUTTON);
964           }
965
966         nv->atkobj = ATK_OBJECT(aobject);
967         nv->action_num = j;
968         nv->signal_id = g_signal_connect (nv->button,
969           "clicked", G_CALLBACK (_action_cb), nv);
970
971         g_free(label_str);
972
973         label_str = g_strdup_printf("Action %d Description", j + 1);
974         action_description = atk_action_get_description (aobject, j);
975         if (action_description)
976           {
977             _print_key_value(tab_n, group_num, label_str,
978               (gpointer)action_description, VALUE_STRING);
979           }
980         else
981           {
982             _print_key_value(tab_n, group_num, label_str, "NULL",
983               VALUE_STRING);
984           }
985         g_free(label_str);
986
987         label_str = g_strdup_printf("Action %d Keybinding", j + 1);
988         action_keybinding = atk_action_get_keybinding (aobject, j);
989         if (action_keybinding)
990           {
991             _print_key_value(tab_n, group_num, label_str,
992               (gpointer)action_keybinding, VALUE_STRING);
993           }
994         else
995           {
996             _print_key_value(tab_n, group_num, label_str, "NULL",
997               VALUE_STRING);
998           }
999         g_free(label_str);
1000       }
1001     return(group_num);
1002 }
1003
1004 static gint
1005 _print_component (AtkComponent *aobject)
1006 {
1007     gchar *output_str;
1008     gint x = 0;
1009     gint y = 0;
1010     gint width = 0;
1011     gint height = 0;
1012     gint group_num;
1013     TabNumber tab_n = COMPONENT;
1014
1015     group_num = _print_groupname(tab_n, COMPONENT_INTERFACE,
1016       "Component Interface");
1017
1018     atk_component_get_extents (aobject,
1019        &x, &y, &width, &height, ATK_XY_SCREEN);
1020
1021     output_str = g_strdup_printf("x: %d y: %d width: %d height %d",
1022       x, y, width, height);
1023     _print_key_value(tab_n, group_num, "Geometry", (gpointer)output_str,
1024       VALUE_STRING);
1025     g_free(output_str);
1026     return(group_num);
1027 }
1028
1029 static gint
1030 _print_image (AtkImage *aobject)
1031 {
1032     G_CONST_RETURN gchar *image_desc;
1033     gchar *output_str;
1034     gint x = 0;
1035     gint y = 0;
1036     gint height = 0;
1037     gint width = 0;
1038     gint group_num;
1039     TabNumber tab_n = IMAGE;
1040
1041     group_num = _print_groupname(tab_n, IMAGE_INTERFACE,
1042       "Image Interface");
1043
1044     image_desc = atk_image_get_image_description(aobject);
1045     if (image_desc)
1046       {
1047         _print_key_value(tab_n, group_num, "Description", (gpointer)image_desc,
1048           VALUE_STRING);
1049       }
1050     else
1051       {
1052         _print_key_value(tab_n, group_num, "Description", "NULL",
1053           VALUE_STRING);
1054       }
1055
1056     atk_image_get_image_position(aobject, &x, &y, ATK_XY_SCREEN);
1057     atk_image_get_image_size(aobject, &height, &width);
1058
1059     output_str = g_strdup_printf("x: %d y: %d width: %d height %d",
1060        x, y, width, height);
1061     _print_key_value(tab_n, group_num, "Geometry", (gpointer)output_str,
1062       VALUE_STRING);
1063     g_free(output_str);
1064     return(group_num);
1065 }
1066
1067 static gint
1068 _print_selection (AtkSelection *aobject)
1069 {
1070     AtkObject *object;
1071     AtkRole role;
1072     gchar *label_str, *output_str;
1073     gint group_num;
1074     gint n_selected, j, n_selectable;
1075     TabNumber tab_n = SELECTION;
1076
1077     group_num = _print_groupname(tab_n, SELECTION_INTERFACE,
1078       "Selection Interface");
1079
1080     n_selected = atk_selection_get_selection_count (aobject);
1081     output_str = g_strdup_printf ("%d", n_selected);
1082     _print_key_value (tab_n, group_num, "Number of Selected Children",
1083                       (gpointer) output_str, VALUE_STRING);
1084     g_free (output_str);
1085     /*
1086      * The number of selected items is the number of children except for
1087      * a ComboBox where it is the number of items in the list.
1088      */
1089     object = ATK_OBJECT (aobject);
1090     role = atk_object_get_role (object);
1091     if (role == ATK_ROLE_COMBO_BOX)
1092     {
1093       object = atk_object_ref_accessible_child (object, 0);
1094       g_return_val_if_fail (atk_object_get_role (object) == ATK_ROLE_LIST,
1095                             group_num);
1096       n_selectable = atk_object_get_n_accessible_children (object);
1097       g_object_unref (G_OBJECT (object)); 
1098     }
1099     else
1100     {
1101       n_selectable = atk_object_get_n_accessible_children (object);
1102     }
1103     output_str = g_strdup_printf ("%d", n_selectable);
1104     _print_key_value (tab_n, group_num, "Number of Selectable Children",
1105                       (gpointer) output_str, VALUE_STRING);
1106     g_free (output_str);
1107
1108     for (j = 0; j < n_selected; j++)
1109     {
1110       G_CONST_RETURN gchar *selected_name;
1111       AtkObject *selected_object;
1112
1113       selected_object = atk_selection_ref_selection (aobject, j);
1114       selected_name = atk_object_get_name (selected_object);
1115       if (selected_name == NULL)
1116       {
1117         selected_name = "No name";
1118       }
1119       label_str = g_strdup_printf ("Selected item: %d Name", j+1);
1120       _print_key_value (tab_n, group_num, label_str,
1121                         (gpointer) selected_name, VALUE_STRING);
1122       g_free (label_str);
1123       g_object_unref (G_OBJECT (selected_object));
1124     }
1125     return group_num;
1126 }
1127
1128 static gint
1129 _print_table (AtkTable *aobject)
1130 {
1131     gchar *label_str, *output_str;
1132     G_CONST_RETURN gchar *col_desc;
1133     AtkObject *caption;
1134     gint n_cols, n_rows;
1135     gint i;
1136     gint group_num;
1137     TabNumber tab_n = TABLE;
1138
1139     group_num = _print_groupname(tab_n, TABLE_INTERFACE,
1140       "Table Interface");
1141
1142     n_cols = atk_table_get_n_columns(aobject);
1143     output_str = g_strdup_printf("%d", n_cols);
1144     _print_key_value(tab_n, group_num, "Number Columns", (gpointer)output_str,
1145       VALUE_STRING);
1146     g_free(output_str);
1147
1148     n_rows = atk_table_get_n_rows(aobject);
1149     output_str = g_strdup_printf("%d", n_rows);
1150     _print_key_value(tab_n, group_num, "Number Rows", (gpointer)output_str,
1151       VALUE_STRING);
1152     g_free(output_str);
1153
1154     caption = atk_table_get_caption(aobject);
1155     if (caption)
1156       {
1157         G_CONST_RETURN gchar* caption_name;
1158
1159         caption_name = atk_object_get_name (caption);
1160         if (caption_name)
1161           {
1162             _print_key_value(tab_n, group_num, "Caption Name", 
1163                              (gpointer)caption_name, VALUE_STRING);
1164           }
1165       }
1166
1167     for (i=0; i < n_cols; i++)
1168       {
1169         label_str = g_strdup_printf("Column %d Description", i + 1);
1170
1171         col_desc = atk_table_get_column_description(aobject, i);
1172         if (col_desc)
1173           {
1174             _print_key_value(tab_n, group_num, label_str, (gpointer)col_desc,
1175               VALUE_STRING);
1176           }
1177         else
1178           {
1179             _print_key_value(tab_n, group_num, label_str, "NULL",
1180               VALUE_STRING);
1181           }
1182
1183         g_free(label_str);
1184       }
1185
1186     return(group_num);
1187 }
1188
1189 static gint
1190 _print_text (AtkText *aobject)
1191 {
1192     gchar *output_str, *text_val, *text_val_escaped;
1193     gint n_chars, caret_offset;
1194     gint start_offset, end_offset;
1195     gint group_num;
1196     gint x, y, w, h;
1197     TabNumber tab_n = TEXT;
1198
1199     group_num = _print_groupname(tab_n, TEXT_INTERFACE,
1200       "Text Content");
1201
1202     n_chars = atk_text_get_character_count(aobject);
1203
1204     output_str = g_strdup_printf("%d", n_chars);
1205     _print_key_value(tab_n, group_num, "Total Character Count",
1206       (gpointer)output_str, VALUE_STRING);
1207     g_free(output_str);
1208
1209    /*
1210     * Pass through g_strescape so that non-ASCII chars are made
1211     * print-able.
1212     */
1213     text_val = atk_text_get_text (aobject, 0, n_chars);
1214     if (text_val)
1215       {
1216         text_val_escaped = g_strescape(text_val, NULL);
1217         _print_key_value (tab_n, group_num, "Text", (gpointer)text_val_escaped,
1218           VALUE_TEXT);
1219         g_free (text_val);
1220         g_free (text_val_escaped);
1221       }
1222     else
1223       {
1224         _print_key_value (tab_n, group_num, "Text", "NULL", VALUE_TEXT);
1225       }
1226
1227     caret_offset = atk_text_get_caret_offset(aobject);
1228     output_str = g_strdup_printf("%d", caret_offset);
1229     _print_key_value(tab_n, group_num, "Caret Offset", (gpointer)output_str,
1230       VALUE_STRING);
1231     g_free(output_str);
1232
1233     if (caret_offset < 0)
1234       return(group_num);
1235
1236     text_val = atk_text_get_text_at_offset (aobject, caret_offset,
1237                                             ATK_TEXT_BOUNDARY_CHAR,
1238                                             &start_offset, &end_offset);
1239     if (text_val)
1240       {
1241         text_val_escaped = g_strescape(text_val, NULL);
1242         _print_key_value(tab_n, group_num, "Current Character",
1243           (gpointer)text_val_escaped, VALUE_STRING);
1244         g_free (text_val);
1245         g_free (text_val_escaped);
1246       }
1247     else
1248       {
1249         _print_key_value(tab_n, group_num, "Current Character", "none",
1250           VALUE_STRING);
1251       }
1252
1253     atk_text_get_character_extents (aobject, caret_offset,
1254                                     &x, &y, &w, &h, ATK_XY_SCREEN);
1255     output_str = g_strdup_printf ("(%d, %d) (%d, %d)", x, y, w, h);
1256     if (output_str)
1257       {
1258         _print_key_value(tab_n, group_num, "Character Bounds (screen)",
1259           (gpointer)output_str, VALUE_STRING);
1260         g_free(output_str);
1261       }
1262
1263     atk_text_get_character_extents (aobject, caret_offset,
1264                                     &x, &y, &w, &h, ATK_XY_WINDOW);
1265     output_str = g_strdup_printf ("(%d, %d) (%d, %d)", x, y, w, h);
1266     if (output_str)
1267       {
1268         _print_key_value(tab_n, group_num, "Character Bounds (window)",
1269           (gpointer)output_str, VALUE_STRING);
1270         g_free(output_str);
1271       }
1272
1273     text_val = atk_text_get_text_at_offset (aobject, caret_offset,
1274                                             ATK_TEXT_BOUNDARY_WORD_START,
1275                                             &start_offset, &end_offset);
1276     if (text_val)
1277       {
1278         text_val_escaped = g_strescape(text_val, NULL);
1279         _print_key_value(tab_n, group_num, "Current Word",
1280           (gpointer)text_val_escaped, VALUE_STRING);
1281         g_free (text_val);
1282         g_free (text_val_escaped);
1283       }
1284     else
1285       {
1286         _print_key_value(tab_n, group_num, "Current Word", "none",
1287           VALUE_STRING);
1288       }
1289
1290     text_val = atk_text_get_text_at_offset (aobject, caret_offset,
1291                                             ATK_TEXT_BOUNDARY_LINE_START,
1292                                             &start_offset, &end_offset);
1293     if (text_val)
1294       {
1295         text_val_escaped = g_strescape(text_val, NULL);
1296         _print_key_value(tab_n, group_num, "Current Line",
1297           (gpointer)text_val_escaped, VALUE_STRING);
1298         g_free (text_val);
1299         g_free (text_val_escaped);
1300       }
1301     else
1302       {
1303         _print_key_value(tab_n, group_num, "Current Line", "none",
1304           VALUE_STRING);
1305       }
1306
1307     text_val = atk_text_get_text_at_offset (aobject, caret_offset,
1308                                               ATK_TEXT_BOUNDARY_SENTENCE_START,
1309                                               &start_offset, &end_offset);
1310     if (text_val)
1311       {
1312         text_val_escaped = g_strescape(text_val, NULL);
1313         _print_key_value(tab_n, group_num, "Current Sentence",
1314           (gpointer)text_val_escaped, VALUE_STRING);
1315         g_free (text_val);
1316         g_free (text_val_escaped);
1317       }
1318     else
1319       {
1320         _print_key_value(tab_n, group_num, "Current Line", "none",
1321           VALUE_STRING);
1322       }
1323     return(group_num);
1324 }
1325
1326 static gint
1327 _print_text_attributes (AtkText *aobject)
1328 {
1329     AtkAttributeSet *attribute_set;
1330     AtkAttribute *attribute;
1331     gchar *output_str, *label_str;
1332     gint start_offset, end_offset, caret_offset;
1333     gint attribute_set_len, attribute_offset, i;
1334     gint group_num;
1335     TabNumber tab_n = TEXT;
1336
1337     group_num = _print_groupname (tab_n, TEXT_ATTRIBUTES,
1338       "Text Attributes at Caret");
1339
1340     caret_offset = atk_text_get_caret_offset(aobject);
1341     attribute_offset = caret_offset;
1342
1343     start_offset = 0;
1344     end_offset = 0;
1345
1346     attribute_set = atk_text_get_run_attributes(aobject, attribute_offset,
1347           &start_offset, &end_offset);
1348
1349     label_str = g_strdup_printf("Attribute run start");
1350
1351     output_str = g_strdup_printf("%d", start_offset);
1352     _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
1353                      VALUE_STRING);
1354     g_free(label_str);
1355     g_free(output_str);
1356
1357     label_str = g_strdup_printf("Attribute run end");
1358     output_str = g_strdup_printf("%d", end_offset);
1359     _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
1360                      VALUE_STRING);
1361     g_free(label_str);
1362     g_free(output_str);
1363
1364     if (attribute_set == NULL)
1365       attribute_set_len = 0;
1366     else
1367       attribute_set_len = g_slist_length(attribute_set);
1368
1369     label_str = g_strdup_printf("Number of Attributes");
1370     output_str = g_strdup_printf("%d", attribute_set_len);
1371     _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
1372                      VALUE_STRING);
1373     g_free(label_str);
1374     g_free(output_str);
1375
1376     for (i=0; i < attribute_set_len; i++)
1377       {
1378         attribute = ((AtkAttribute *) g_slist_nth(attribute_set, i)->data);
1379
1380         _print_key_value(tab_n, group_num, attribute->name,
1381                          (gpointer)attribute->value, VALUE_STRING);
1382       }
1383     if (attribute_set != NULL)
1384       atk_attribute_set_free(attribute_set);
1385
1386
1387     return(group_num);
1388 }
1389
1390 static gint
1391 _print_value (AtkValue *aobject)
1392 {
1393     GValue *value_back, val;
1394     gint group_num;
1395     TabNumber tab_n = VALUE;
1396
1397     value_back = &val;
1398
1399     group_num = _print_groupname(tab_n, VALUE_INTERFACE,
1400       "Value Interface");
1401
1402     atk_value_get_current_value(aobject, value_back);
1403     _print_value_type(group_num, "Value", value_back);
1404     atk_value_get_minimum_value(aobject, value_back);
1405     _print_value_type(group_num, "Minimum Value", value_back);
1406     atk_value_get_maximum_value(aobject, value_back);
1407     _print_value_type(group_num, "Maximum Value", value_back);
1408     return(group_num);
1409 }
1410
1411 static void
1412 _print_value_type(gint group_num, gchar *type, GValue *value)
1413 {
1414     gchar *label_str = NULL;
1415     gchar *output_str = NULL;
1416     TabNumber tab_n = VALUE;
1417
1418     if (G_VALUE_HOLDS_DOUBLE (value))
1419       {
1420         label_str = g_strdup_printf("%s - Double", type);
1421         output_str = g_strdup_printf("%f",
1422           g_value_get_double (value));
1423         _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
1424           VALUE_STRING);
1425       }
1426     else if (G_VALUE_HOLDS_INT (value))
1427       {
1428         label_str = g_strdup_printf("%s - Integer", type);
1429         output_str = g_strdup_printf("%d",
1430           g_value_get_int (value));
1431         _print_key_value(tab_n, group_num, label_str, (gpointer)output_str,
1432           VALUE_STRING);
1433       }
1434     else
1435       {
1436         _print_key_value(tab_n, group_num, "Value", "Unknown Type",
1437           VALUE_STRING);
1438       }
1439
1440     if (label_str)
1441         g_free(label_str);
1442     if (output_str)
1443         g_free(output_str);
1444 }
1445
1446 static void
1447 _create_event_watcher (void)
1448 {
1449     focus_tracker_id = atk_add_focus_tracker (_print_accessible);
1450
1451     if (track_mouse)
1452       {
1453         mouse_watcher_focus_id =
1454           atk_add_global_event_listener(_mouse_watcher,
1455           "Gtk:GtkWidget:enter_notify_event");
1456         mouse_watcher_button_id =
1457           atk_add_global_event_listener(_button_watcher,
1458           "Gtk:GtkWidget:button_press_event");
1459       }
1460 }
1461
1462 static gboolean
1463 _mouse_watcher (GSignalInvocationHint *ihint,
1464                guint                  n_param_values,
1465                const GValue          *param_values,
1466                gpointer               data)
1467 {
1468     GObject *object;
1469     GtkWidget *widget;
1470
1471     object = g_value_get_object (param_values + 0);
1472
1473     if (GTK_IS_MENU(object)) return TRUE;
1474
1475     g_assert (GTK_IS_WIDGET(object));
1476
1477     widget = GTK_WIDGET (object);
1478     if (GTK_IS_WINDOW (widget))
1479     {
1480         GtkWidget *focus_widget = gtk_window_get_focus (GTK_WINDOW (widget));
1481         if (focus_widget != NULL)
1482             widget = focus_widget;
1483     }
1484
1485     _print_accessible (gtk_widget_get_accessible (widget));
1486     return TRUE;
1487 }
1488
1489 static gboolean
1490 _button_watcher (GSignalInvocationHint *ihint,
1491                  guint                  n_param_values,
1492                  const GValue          *param_values,
1493                  gpointer               data)
1494 {
1495     GObject *object;
1496     GtkWidget *widget;
1497
1498     object = g_value_get_object (param_values + 0);
1499
1500     widget = GTK_WIDGET (object);
1501     if (GTK_IS_CONTAINER (widget))
1502     {
1503       if (G_VALUE_HOLDS_BOXED (param_values + 1))
1504         {
1505           GdkEventButton *event;
1506           gpointer gp;
1507           AtkObject *aobject;
1508           AtkObject *child;
1509           gint  aobject_x, aobject_y;
1510           gint x, y;
1511
1512           gp = g_value_get_boxed (param_values + 1);
1513           event = (GdkEventButton *) gp;
1514           aobject = gtk_widget_get_accessible (widget);
1515           aobject_x = aobject_y = 0;
1516           atk_component_get_position (ATK_COMPONENT (aobject), 
1517                                       &aobject_x, &aobject_y, 
1518                                       ATK_XY_WINDOW);
1519           x = aobject_x + (gint) event->x; 
1520           y = aobject_y + (gint) event->y; 
1521           child = atk_component_ref_accessible_at_point (ATK_COMPONENT (aobject),
1522                                                          x,
1523                                                          y,
1524                                                          ATK_XY_WINDOW);
1525           if (child)
1526             {
1527               _print_accessible (child);
1528               g_object_unref (child);
1529             }
1530           else
1531             {
1532               if (!GTK_IS_MENU_ITEM (widget))
1533                 {
1534                   g_print ("No child at position %d %d for %s\n", 
1535                            x,
1536                            y,
1537                            g_type_name (G_OBJECT_TYPE (widget)));
1538                 }
1539             }
1540         }
1541     }
1542
1543     return TRUE;
1544 }
1545
1546 static void _add_notebook_page (GtkNotebook *nbook,
1547                                 GtkWidget *content_widget,
1548                                 GtkWidget **new_page,
1549                                 const gchar *label_text)
1550 {
1551   GtkWidget *label;
1552
1553   if (content_widget != NULL)
1554     {
1555       *new_page = content_widget;
1556     }
1557   else
1558     {
1559       *new_page = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
1560     }
1561
1562   label = gtk_label_new ("");
1563   gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), label_text);
1564   gtk_notebook_append_page (notebook, *new_page, label);
1565   gtk_widget_show(*new_page);
1566 }
1567
1568 static void
1569 _create_notebook (void)
1570 {
1571   TabInfo *tab;
1572   notebook = GTK_NOTEBOOK (gtk_notebook_new());
1573
1574   tab = nbook_tabs[OBJECT];
1575   _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Object</b>");
1576
1577   tab = nbook_tabs[ACTION];
1578   _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Action</b>");
1579
1580   tab = nbook_tabs[COMPONENT];
1581   _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Component</b>");
1582
1583   tab = nbook_tabs[IMAGE];
1584   _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Image</b>");
1585
1586   tab = nbook_tabs[SELECTION];
1587   _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Selection</b>");
1588
1589   tab = nbook_tabs[TABLE];
1590   _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Table</b>");
1591
1592   tab = nbook_tabs[TEXT];
1593   _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>Te_xt</b>");
1594
1595   tab = nbook_tabs[VALUE];
1596   _add_notebook_page (notebook, tab->main_box, &tab->page, "<b>_Value</b>");
1597
1598   g_signal_connect (notebook,
1599                     "switch-page",
1600                     G_CALLBACK (_update_current_page),
1601                     NULL);
1602 }
1603
1604 static void
1605 _init_data(void)
1606 {
1607   TabInfo *the_tab;
1608
1609   the_tab = g_new0(TabInfo, 1);
1610   the_tab->page = NULL;
1611   the_tab->main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 20);
1612   the_tab->name = "Object";
1613   nbook_tabs[OBJECT] = the_tab;
1614
1615   the_tab = g_new0(TabInfo, 1);
1616   the_tab->page = NULL;
1617   the_tab->main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 20);
1618   the_tab->name = "Action";
1619   nbook_tabs[ACTION] = the_tab;
1620
1621   the_tab = g_new0(TabInfo, 1);
1622   the_tab->page = NULL;
1623   the_tab->main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 20);
1624   the_tab->name = "Component";
1625   nbook_tabs[COMPONENT] = the_tab;
1626
1627   the_tab = g_new0(TabInfo, 1);
1628   the_tab->page = NULL;
1629   the_tab->main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 20);
1630   the_tab->name = "Image";
1631   nbook_tabs[IMAGE] = the_tab;
1632
1633   the_tab = g_new0(TabInfo, 1);
1634   the_tab->page = NULL;
1635   the_tab->main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 20);
1636   the_tab->name = "Selection";
1637   nbook_tabs[SELECTION] = the_tab;
1638
1639   the_tab = g_new0(TabInfo, 1);
1640   the_tab->page = NULL;
1641   the_tab->main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 20);
1642   the_tab->name = "Table";
1643   nbook_tabs[TABLE] = the_tab;
1644
1645   the_tab = g_new0(TabInfo, 1);
1646   the_tab->page = NULL;
1647   the_tab->main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 20);
1648   the_tab->name = "Text";
1649   nbook_tabs[TEXT] = the_tab;
1650
1651   the_tab = g_new0(TabInfo, 1);
1652   the_tab->page = NULL;
1653   the_tab->main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 20);
1654   the_tab->name = "Value";
1655   nbook_tabs[VALUE] = the_tab;
1656 }
1657
1658 static void
1659 _create_window (void)
1660 {
1661     static GtkWidget *window = NULL;
1662
1663     if (!window)
1664     {
1665         window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1666         gtk_widget_set_name (window, "Ferret Window");
1667
1668         g_signal_connect (window, "destroy",
1669                           G_CALLBACK (gtk_widget_destroyed),
1670                           &window);
1671
1672         gtk_window_set_title (GTK_WINDOW (window), "GTK+ Ferret Output");
1673         gtk_window_set_default_size (GTK_WINDOW (window), 333, 550);
1674         gtk_container_set_border_width (GTK_CONTAINER (window), 0);
1675
1676         vbox1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1677         gtk_container_add (GTK_CONTAINER (window), vbox1);
1678         gtk_widget_show (vbox1);
1679
1680         menubar = gtk_menu_bar_new ();
1681         gtk_box_pack_start (GTK_BOX (vbox1), menubar, FALSE, TRUE, 0);
1682         gtk_widget_show (menubar);
1683         menutop = gtk_menu_item_new_with_label("Menu");
1684         gtk_menu_shell_append (GTK_MENU_SHELL (menubar), menutop);
1685         gtk_widget_show (menutop);
1686         menu = gtk_menu_new();
1687         gtk_menu_item_set_submenu (GTK_MENU_ITEM (menutop), menu);
1688         gtk_widget_show (menu);
1689
1690         _add_menu(&menu, &menuitem_trackmouse, "Track Mouse", track_mouse,
1691            G_CALLBACK(_toggle_trackmouse));
1692         _add_menu(&menu, &menuitem_trackfocus, "Track Focus", track_focus,
1693            G_CALLBACK(_toggle_trackfocus));
1694         _add_menu(&menu, &menuitem_magnifier, "Magnifier", use_magnifier,
1695            G_CALLBACK(_toggle_magnifier));
1696         _add_menu(&menu, &menuitem_festival, "Festival", use_festival,
1697            G_CALLBACK(_toggle_festival));
1698         _add_menu(&menu, &menuitem_festival_terse, "Festival Terse",
1699           (!say_role && !say_accel),
1700           G_CALLBACK(_toggle_festival_terse));
1701         _add_menu(&menu, &menuitem_terminal, "Terminal Output", display_ascii,
1702            G_CALLBACK(_toggle_terminal));
1703         _add_menu(&menu, &menuitem_no_signals, "No ATK Signals", no_signals,
1704            G_CALLBACK(_toggle_no_signals));
1705
1706         _create_notebook ();
1707         gtk_container_add (GTK_CONTAINER (vbox1), GTK_WIDGET (notebook));
1708         gtk_widget_show (GTK_WIDGET (notebook));
1709     }
1710     if (!gtk_widget_get_visible (window))
1711         gtk_widget_show (window);
1712
1713     mainWindow = GTK_WIDGET (window);
1714 }
1715
1716 static void
1717 _add_menu(GtkWidget ** menu, GtkWidget ** menuitem, gchar * name,
1718   gboolean init_value, GCallback func)
1719 {
1720     *menuitem = gtk_check_menu_item_new_with_label(name);
1721     gtk_check_menu_item_set_active(
1722       GTK_CHECK_MENU_ITEM(*menuitem), init_value);
1723     gtk_menu_shell_append (GTK_MENU_SHELL (*menu), *menuitem);
1724     gtk_widget_show(*menuitem);
1725     g_signal_connect(*menuitem, "toggled", func, NULL);
1726 }
1727
1728 int
1729 gtk_module_init(gint argc, char* argv[])
1730 {
1731     if (g_getenv ("FERRET_ASCII"))
1732        display_ascii = TRUE;
1733
1734     if (g_getenv ("FERRET_NOSIGNALS"))
1735        no_signals = TRUE;
1736
1737     if (display_ascii)
1738        g_print("GTK ferret Module loaded\n");
1739
1740     if (g_getenv("FERRET_MAGNIFIER"))
1741         use_magnifier = TRUE;
1742
1743     if (g_getenv ("FERRET_FESTIVAL"))
1744         use_festival = TRUE;
1745
1746     if (g_getenv ("FERRET_MOUSETRACK"))
1747         track_mouse = TRUE;
1748
1749     if (g_getenv ("FERRET_TERSE"))
1750       {
1751         say_role = FALSE;
1752         say_accel = FALSE;
1753       }
1754
1755     _init_data();
1756
1757     _create_window();
1758
1759     _create_event_watcher();
1760
1761     return 0;
1762 }
1763
1764 static void
1765 _clear_tab(TabNumber tab_n)
1766 {
1767     GList *group_list, *nv_list;
1768     TabInfo *tab;
1769     GroupInfo *group;
1770     NameValue *nv;
1771
1772     tab = nbook_tabs[tab_n];
1773
1774     for (group_list = tab->groups; group_list; group_list = group_list->next)
1775       {
1776         group = (GroupInfo *) group_list->data;
1777
1778         if (group->is_scrolled)
1779           gtk_widget_hide(GTK_WIDGET(group->scroll_outer_frame));
1780
1781         gtk_widget_hide(GTK_WIDGET(group->frame));
1782         gtk_widget_hide(GTK_WIDGET(group->group_vbox));
1783
1784         for (nv_list = group->name_value; nv_list; nv_list = nv_list->next)
1785           {
1786             nv = (NameValue *) nv_list->data;
1787             nv->active = FALSE;
1788             gtk_widget_hide(GTK_WIDGET(nv->column1));
1789             gtk_widget_hide(GTK_WIDGET(nv->column2));
1790             gtk_widget_hide(GTK_WIDGET(nv->label));
1791
1792             switch (nv->type)
1793               {
1794               case VALUE_STRING:
1795                 gtk_widget_hide(GTK_WIDGET(nv->string));
1796                 break;
1797               case VALUE_BOOLEAN:
1798                 gtk_widget_hide(GTK_WIDGET(nv->boolean));
1799                 break;
1800               case VALUE_TEXT:
1801                 gtk_widget_hide(GTK_WIDGET(nv->text));
1802                 break;
1803               case VALUE_BUTTON:
1804                 gtk_widget_hide(GTK_WIDGET(nv->button));
1805                 break;
1806               }
1807             gtk_widget_hide(GTK_WIDGET(nv->hbox));
1808
1809             /* Disconnect signal handler if any */
1810             if (nv->signal_id != -1)
1811               g_signal_handler_disconnect(nv->button, nv->signal_id);
1812
1813             nv->signal_id = -1;
1814           }
1815       }
1816 }
1817
1818 static gint
1819 _print_groupname(TabNumber tab_n, GroupId group_id,
1820   const char *groupname)
1821 {
1822   TabInfo *tab;
1823   GroupInfo *the_group;
1824   gint rc = -1;
1825
1826   if (display_ascii)
1827       g_print("\n<%s>\n", groupname);
1828
1829   tab = nbook_tabs[tab_n];
1830   the_group = _get_group(tab, group_id, groupname);
1831   rc = g_list_index(tab->groups, the_group);
1832   return rc;
1833 }
1834
1835 static GroupInfo*
1836 _get_group(TabInfo *tab, GroupId group_id, const gchar *groupname)
1837 {
1838     GroupInfo *group = NULL;
1839     gboolean found = FALSE;
1840     GList *group_list;
1841
1842     for (group_list = tab->groups; group_list; group_list = group_list->next)
1843       {
1844         group = (GroupInfo *) group_list->data;
1845         if (group_id == group->group_id)
1846           {
1847             found = TRUE;
1848             break;
1849           }
1850       }
1851
1852    if (!found)
1853      {
1854        /* build a new one */
1855        group = (GroupInfo *)g_new0(GroupInfo, 1);
1856        group->group_id = group_id;
1857        _get_group_scrolled(group);
1858
1859        if (group->is_scrolled)
1860          {
1861            group->frame = gtk_scrolled_window_new (NULL, NULL);
1862            gtk_widget_set_size_request (GTK_WIDGET (group->frame), -2,
1863              group->default_height);
1864            group->scroll_outer_frame = GTK_FRAME(gtk_frame_new(groupname));
1865            gtk_container_add(GTK_CONTAINER(group->scroll_outer_frame),
1866              group->frame);
1867          }
1868        else
1869          {
1870            group->frame = gtk_frame_new(groupname);
1871          }
1872
1873        gtk_container_set_border_width(GTK_CONTAINER(group->frame), 10);
1874
1875        group->name = g_strdup(groupname);
1876        group->group_vbox = GTK_VBOX(gtk_box_new (GTK_ORIENTATION_VERTICAL, 10));
1877
1878        if (group->is_scrolled)
1879          {
1880            gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (group->frame),
1881               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1882            gtk_scrolled_window_add_with_viewport(
1883              GTK_SCROLLED_WINDOW(group->frame),
1884              GTK_WIDGET(group->group_vbox));
1885          }
1886        else
1887          {
1888            gtk_container_add(GTK_CONTAINER(group->frame),
1889              GTK_WIDGET(group->group_vbox));
1890          }
1891
1892        tab->groups = g_list_append (tab->groups, group);
1893
1894        if (group->is_scrolled)
1895          {
1896            gtk_box_pack_start (GTK_BOX (tab->main_box),
1897                                GTK_WIDGET (group->scroll_outer_frame),
1898                                TRUE, TRUE, 0);
1899          }
1900        else
1901          {
1902            gtk_box_pack_start (GTK_BOX (tab->main_box),
1903                                GTK_WIDGET (group->frame),
1904                                TRUE, TRUE, 0);
1905          }
1906      }
1907
1908    return group;
1909 }
1910
1911 void
1912 _get_group_scrolled(GroupInfo *group)
1913 {
1914    if (group->group_id == OBJECT_INTERFACE)
1915      {
1916        group->is_scrolled = FALSE;
1917      }
1918    else if (group->group_id == RELATION_INTERFACE)
1919      {
1920        group->is_scrolled = TRUE;
1921        group->default_height = 50;
1922      }
1923    else if (group->group_id == STATE_INTERFACE)
1924      {
1925        group->is_scrolled = TRUE;
1926        group->default_height = 100;
1927      }
1928    else if (group->group_id == ACTION_INTERFACE)
1929      {
1930        group->is_scrolled = TRUE;
1931        group->default_height = 200;
1932      }
1933    else if (group->group_id == TEXT_ATTRIBUTES)
1934      {
1935        group->is_scrolled = TRUE;
1936        group->default_height = 70;
1937      }
1938    else
1939      {
1940        group->is_scrolled = FALSE;
1941      }
1942 }
1943
1944 NameValue *
1945 _get_name_value(GroupInfo *group, const gchar *label,
1946   gpointer value_ptr, ValueType type)
1947 {
1948     NameValue *name_value = NULL;
1949     GList *nv_list;
1950     GValue *value;
1951     gboolean found = FALSE;
1952     static char *empty_string = "";
1953
1954     if (!label)
1955       {
1956         label = empty_string;
1957       }
1958
1959     for (nv_list = group->name_value; nv_list; nv_list = nv_list->next)
1960       {
1961         name_value = (NameValue *) nv_list->data;
1962         if (!name_value->active)
1963           {
1964             found = TRUE;
1965             break;
1966           }
1967       }
1968
1969     if (!found)
1970       {
1971         name_value = (NameValue *)g_new0(NameValue, 1);
1972         name_value->column1 = GTK_HBOX(gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10));
1973         name_value->column2 = GTK_HBOX(gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10));
1974         name_value->hbox = GTK_HBOX(gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5));
1975         name_value->label = GTK_LABEL(gtk_label_new(label));
1976         name_value->string = gtk_label_new (NULL);
1977         name_value->boolean = gtk_check_button_new ();
1978         gtk_entry_buffer_set_max_length (gtk_entry_get_buffer (GTK_ENTRY (name_value->text)), 1000);
1979         name_value->button = GTK_BUTTON(gtk_button_new ());
1980
1981         gtk_box_pack_end(GTK_BOX(name_value->column1),
1982           GTK_WIDGET(name_value->label), FALSE, FALSE, 10);
1983
1984         switch (type)
1985           {
1986           case VALUE_STRING:
1987             gtk_label_set_text(GTK_LABEL(name_value->string),
1988               (gchar *) value_ptr);
1989             gtk_box_pack_start(GTK_BOX(name_value->column2),
1990               GTK_WIDGET(name_value->string), FALSE, FALSE, 10);
1991             break;
1992           case VALUE_BOOLEAN:
1993             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(name_value->boolean),
1994                *((gboolean *)value_ptr));
1995             gtk_widget_set_sensitive(name_value->boolean, FALSE);
1996             gtk_box_pack_start(GTK_BOX(name_value->column2),
1997               GTK_WIDGET(name_value->boolean), FALSE, FALSE, 10);
1998             break;
1999           case VALUE_TEXT:
2000             gtk_entry_set_text (GTK_ENTRY (name_value->text),
2001               (gchar *)value_ptr);
2002             gtk_box_pack_start(GTK_BOX(name_value->column2),
2003               GTK_WIDGET(name_value->text), FALSE, FALSE, 10);
2004           case VALUE_BUTTON:
2005             value = &(name_value->button_gval);
2006             memset (value, 0, sizeof (GValue));
2007             g_value_init (value, G_TYPE_STRING);
2008             g_value_set_string (value, (gchar *)value_ptr);
2009             g_object_set_property(G_OBJECT(name_value->button),
2010               "label", value);
2011             gtk_box_pack_start(GTK_BOX(name_value->column2),
2012               GTK_WIDGET(name_value->button), FALSE, FALSE, 10);
2013             break;
2014           }
2015
2016         gtk_box_pack_start (GTK_BOX (name_value->hbox),
2017                             GTK_WIDGET (name_value->column1),
2018                             TRUE, TRUE, 0);
2019         gtk_box_pack_start (GTK_BOX (name_value->hbox),
2020                             GTK_WIDGET (name_value->column2),
2021                             TRUE, TRUE, 0);
2022         gtk_container_add(GTK_CONTAINER(group->group_vbox),
2023           GTK_WIDGET(name_value->hbox));
2024         group->name_value = g_list_append (group->name_value, name_value);
2025       }
2026     else
2027       {
2028         gtk_label_set_text(GTK_LABEL(name_value->label), label);
2029         switch (type)
2030           {
2031           case VALUE_STRING:
2032             gtk_label_set_text(GTK_LABEL(name_value->string),
2033               (gchar *) value_ptr);
2034             break;
2035           case VALUE_BOOLEAN:
2036             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(name_value->boolean),
2037                *((gboolean *)value_ptr));
2038             gtk_widget_set_sensitive(name_value->boolean, FALSE);
2039             break;
2040           case VALUE_TEXT:
2041             gtk_entry_set_text (GTK_ENTRY (name_value->text),
2042               (gchar *) value_ptr);
2043             break;
2044           case VALUE_BUTTON:
2045             value = &(name_value->button_gval);
2046             memset (value, 0, sizeof (GValue));
2047             g_value_init (value, G_TYPE_STRING);
2048             g_value_set_string (value, (gchar *)value_ptr);
2049             g_object_set_property(G_OBJECT(name_value->button),
2050               "label", value);
2051             break;
2052           }
2053       }
2054
2055     name_value->active = TRUE;
2056     name_value->type = type;
2057     name_value->signal_id = -1;
2058
2059     gtk_widget_show(GTK_WIDGET(name_value->label));
2060
2061     switch (type)
2062       {
2063       case VALUE_STRING:
2064         gtk_widget_show(GTK_WIDGET(name_value->string));
2065         break;
2066       case VALUE_BOOLEAN:
2067         gtk_widget_show(GTK_WIDGET(name_value->boolean));
2068         break;
2069       case VALUE_TEXT:
2070         gtk_widget_show(GTK_WIDGET(name_value->text));
2071         break;
2072       case VALUE_BUTTON:
2073         gtk_widget_show(GTK_WIDGET(name_value->button));
2074         break;
2075       }
2076
2077     gtk_widget_show(GTK_WIDGET(name_value->column1));
2078     gtk_widget_show(GTK_WIDGET(name_value->column2));
2079     gtk_widget_show(GTK_WIDGET(name_value->hbox));
2080     gtk_widget_show(GTK_WIDGET(group->group_vbox));
2081
2082     return name_value;
2083 }
2084
2085 static NameValue *
2086 _print_key_value(TabNumber tab_n, gint group_number,
2087    const char *label, gpointer value, ValueType type)
2088 {
2089   TabInfo *tab;
2090   GroupInfo *the_group;
2091
2092   if (display_ascii)
2093     {
2094       if (type == VALUE_BOOLEAN)
2095         {
2096           if (*((gboolean *)value))
2097               g_print("\t%-30s\tTRUE\n", label);
2098           else
2099               g_print("\t%-30s\tFALSE\n", label);
2100         }
2101       else
2102         {
2103           g_print("\t%-30s\t%s\n", label, 
2104                   value ? (gchar *)value : "NULL");
2105         }
2106     }
2107
2108   tab = nbook_tabs[tab_n];
2109   the_group = (GroupInfo *)g_list_nth_data(tab->groups, group_number);
2110   return _get_name_value(the_group, label, (gpointer)value, type);
2111 }
2112
2113 static void
2114 _finished_group(TabNumber tab_no, gint group_number)
2115 {
2116     TabInfo *tab;
2117     GroupInfo *the_group;
2118
2119     tab = nbook_tabs[tab_no];
2120
2121     the_group = (GroupInfo *)g_list_nth_data(tab->groups, group_number);
2122
2123     if (the_group->is_scrolled)
2124       gtk_widget_show(GTK_WIDGET(the_group->scroll_outer_frame));
2125
2126     gtk_widget_show(GTK_WIDGET(the_group->frame));
2127     gtk_widget_show(GTK_WIDGET(the_group->group_vbox));
2128     gtk_widget_show(GTK_WIDGET(tab->main_box));
2129 }
2130
2131 /* Signal handlers */
2132
2133 static gulong child_added_id = 0;
2134 static gulong child_removed_id = 0;
2135 static gulong state_change_id = 0;
2136
2137 static gulong text_caret_handler_id = 0;
2138 static gulong text_inserted_id = 0;
2139 static gulong text_deleted_id = 0;
2140
2141 static gulong table_row_inserted_id = 0;
2142 static gulong table_column_inserted_id = 0;
2143 static gulong table_row_deleted_id = 0;
2144 static gulong table_column_deleted_id = 0;
2145 static gulong table_row_reordered_id = 0;
2146 static gulong table_column_reordered_id = 0;
2147
2148 static gulong property_id = 0;
2149
2150 static void
2151 _update_handlers(AtkObject *obj)
2152 {
2153     /* Remove signal handlers from object that had focus before */
2154
2155     if (last_object != NULL && G_TYPE_CHECK_INSTANCE(last_object))
2156       {
2157         if (child_added_id != 0)
2158            g_signal_handler_disconnect(last_object, child_added_id);
2159         if (child_removed_id != 0)
2160            g_signal_handler_disconnect(last_object, child_removed_id);
2161         if (state_change_id != 0)
2162            g_signal_handler_disconnect(last_object, state_change_id);
2163
2164         if (text_caret_handler_id != 0)
2165            g_signal_handler_disconnect(last_object, text_caret_handler_id);
2166         if (text_inserted_id != 0)
2167            g_signal_handler_disconnect(last_object, text_inserted_id);
2168         if (text_deleted_id != 0)
2169            g_signal_handler_disconnect(last_object, text_deleted_id);
2170
2171         if (table_row_inserted_id != 0)
2172            g_signal_handler_disconnect(last_object, table_row_inserted_id);
2173         if (table_column_inserted_id != 0)
2174            g_signal_handler_disconnect(last_object, table_column_inserted_id);
2175         if (table_row_deleted_id != 0)
2176            g_signal_handler_disconnect(last_object, table_row_deleted_id);
2177         if (table_column_deleted_id != 0)
2178            g_signal_handler_disconnect(last_object, table_column_deleted_id);
2179         if (table_row_reordered_id != 0)
2180            g_signal_handler_disconnect(last_object, table_row_reordered_id);
2181         if (table_column_reordered_id != 0)
2182            g_signal_handler_disconnect(last_object, table_column_reordered_id);
2183         if (property_id != 0)
2184            g_signal_handler_disconnect(last_object, property_id);
2185
2186         g_object_unref(last_object);
2187       }
2188
2189     last_object = NULL;
2190
2191     child_added_id = 0;
2192     child_removed_id = 0;
2193     text_caret_handler_id = 0;
2194     text_inserted_id = 0;
2195     text_deleted_id = 0;
2196     table_row_inserted_id = 0;
2197     table_column_inserted_id = 0;
2198     table_row_deleted_id = 0;
2199     table_column_deleted_id = 0;
2200     table_row_reordered_id = 0;
2201     table_column_reordered_id = 0;
2202     property_id = 0;
2203
2204     if (!G_TYPE_CHECK_INSTANCE(obj))
2205         return;
2206
2207     g_object_ref(obj);
2208     last_object = obj;
2209
2210     /* Add signal handlers to object that now has focus. */
2211
2212     if (ATK_IS_OBJECT(obj))
2213       {
2214          child_added_id = g_signal_connect_closure (obj,
2215                 "children_changed::add",
2216                 g_cclosure_new (G_CALLBACK (_notify_object_child_added),
2217                 NULL, NULL), FALSE);
2218
2219          child_removed_id = g_signal_connect_closure (obj,
2220                 "children_changed::remove",
2221                 g_cclosure_new (G_CALLBACK (_notify_object_child_removed),
2222                 NULL, NULL), FALSE);
2223
2224          state_change_id = g_signal_connect_closure (obj,
2225                 "state_change",
2226                 g_cclosure_new (G_CALLBACK (_notify_object_state_change),
2227                 NULL, NULL), FALSE);
2228       }
2229
2230     if (ATK_IS_TEXT(obj))
2231       {
2232         text_caret_handler_id = g_signal_connect_closure_by_id (obj,
2233                 g_signal_lookup ("text_caret_moved", G_OBJECT_TYPE (obj)),
2234                 0, g_cclosure_new (G_CALLBACK (_notify_caret_handler),
2235                 NULL, NULL), FALSE);
2236         text_inserted_id = g_signal_connect_closure (obj,
2237                 "text_changed::insert",
2238                 g_cclosure_new (G_CALLBACK (_notify_text_insert_handler),
2239                 NULL, NULL), FALSE);
2240         text_deleted_id = g_signal_connect_closure (obj,
2241                 "text_changed::delete",
2242                 g_cclosure_new (G_CALLBACK (_notify_text_delete_handler),
2243                 NULL, NULL), FALSE);
2244       }
2245
2246     if (ATK_IS_TABLE(obj))
2247       {
2248         table_row_inserted_id = g_signal_connect_closure_by_id (obj,
2249                 g_signal_lookup ("row_inserted", G_OBJECT_TYPE (obj)),
2250                 0, g_cclosure_new (G_CALLBACK (_notify_table_row_inserted),
2251                 NULL, NULL), FALSE);
2252         table_column_inserted_id = g_signal_connect_closure_by_id (obj,
2253                 g_signal_lookup ("column_inserted", G_OBJECT_TYPE (obj)),
2254                 0, g_cclosure_new (G_CALLBACK (_notify_table_column_inserted),
2255                 NULL, NULL), FALSE);
2256         table_row_deleted_id = g_signal_connect_closure_by_id (obj,
2257                 g_signal_lookup ("row_deleted", G_OBJECT_TYPE (obj)),
2258                 0, g_cclosure_new (G_CALLBACK (_notify_table_row_deleted),
2259                 NULL, NULL), FALSE);
2260         table_column_deleted_id = g_signal_connect_closure_by_id (obj,
2261                 g_signal_lookup ("column_deleted", G_OBJECT_TYPE (obj)),
2262                 0, g_cclosure_new (G_CALLBACK (_notify_table_column_deleted),
2263                 NULL, NULL), FALSE);
2264         table_row_reordered_id = g_signal_connect_closure_by_id (obj,
2265                 g_signal_lookup ("row_reordered", G_OBJECT_TYPE (obj)),
2266                 0, g_cclosure_new (G_CALLBACK (_notify_table_row_reordered),
2267                 NULL, NULL), FALSE);
2268         table_column_reordered_id = g_signal_connect_closure_by_id (obj,
2269                 g_signal_lookup ("column_reordered", G_OBJECT_TYPE (obj)),
2270                 0, g_cclosure_new (G_CALLBACK (_notify_table_column_reordered),
2271                 NULL, NULL), FALSE);
2272       }
2273
2274     property_id = g_signal_connect_closure_by_id (obj,
2275       g_signal_lookup ("property_change", G_OBJECT_TYPE (obj)),
2276       0, g_cclosure_new (G_CALLBACK (_property_change_handler),
2277       NULL, NULL),
2278           FALSE);
2279 }
2280
2281 /* Text signals */
2282
2283 static void
2284 _notify_text_insert_handler (GObject *obj, int position, int offset)
2285 {
2286     gchar *text = atk_text_get_text (ATK_TEXT (obj), position, position + offset);
2287     gchar *output_str = g_strdup_printf("position %d, length %d text: %s",
2288       position, offset,  text ? text: "<NULL>");
2289     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TEXT,
2290       "Text Insert", output_str);
2291     g_free(output_str);
2292 }
2293
2294 static void
2295 _notify_text_delete_handler (GObject *obj, int position, int offset)
2296 {
2297     gchar *text = atk_text_get_text (ATK_TEXT (obj), position, position + offset);
2298     gchar *output_str = g_strdup_printf("position %d, length %d text: %s",
2299       position, offset,  text ? text: "<NULL>");
2300     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TEXT,
2301       "Text Delete", output_str);
2302     g_free(output_str);
2303 }
2304
2305 static void
2306 _notify_caret_handler (GObject *obj, int position)
2307 {
2308     gchar *output_str = g_strdup_printf("position %d", position);
2309     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TEXT,
2310       "Text Caret Moved", output_str);
2311     g_free(output_str);
2312 }
2313
2314 /* Table signals */
2315
2316 static void
2317 _notify_table_row_inserted (GObject *obj, gint start_offset,
2318   gint length)
2319 {
2320     gchar *output_str =
2321       g_strdup_printf("position %d, num of rows inserted %d!\n",
2322       start_offset, length);
2323     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2324       "Table Row Inserted", output_str);
2325     g_free(output_str);
2326 }
2327
2328 static void
2329 _notify_table_column_inserted (GObject *obj, gint start_offset,
2330   gint length)
2331 {
2332     gchar *output_str =
2333       g_strdup_printf("position %d, num of rows inserted %d!\n",
2334       start_offset, length);
2335     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2336       "Table Column Inserted", output_str);
2337     g_free(output_str);
2338 }
2339
2340 static void
2341 _notify_table_row_deleted (GObject *obj, gint start_offset,
2342   gint length)
2343 {
2344     gchar *output_str = g_strdup_printf("position %d, num of rows inserted %d!\n",
2345       start_offset, length);
2346     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2347       "Table Row Deleted", output_str);
2348     g_free(output_str);
2349 }
2350
2351 static void
2352 _notify_table_column_deleted (GObject *obj, gint start_offset,
2353   gint length)
2354 {
2355     gchar *output_str = g_strdup_printf("position %d, num of rows inserted %d!\n",
2356       start_offset, length);
2357     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2358       "Table Column Deleted", output_str);
2359     g_free(output_str);
2360 }
2361
2362 static void
2363 _notify_table_row_reordered (GObject *obj)
2364 {
2365     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2366       "Table Row Reordered", NULL);
2367 }
2368
2369 static void
2370 _notify_table_column_reordered (GObject *obj)
2371 {
2372     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2373       "Table Column Reordered", NULL);
2374 }
2375
2376 /* Object signals */
2377
2378 static void
2379 _notify_object_child_added (GObject *obj, gint index,
2380   AtkObject *child)
2381 {
2382     gchar *output_str = g_strdup_printf("index %d", index);
2383     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_OBJECT,
2384       "Child Added", output_str);
2385     g_free(output_str);
2386 }
2387
2388 static void
2389 _notify_object_child_removed (GObject *obj, gint index,
2390   AtkObject *child)
2391 {
2392     gchar *output_str = g_strdup_printf("index %d", index);
2393     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_OBJECT,
2394       "Child Removed", output_str);
2395     g_free(output_str);
2396 }
2397
2398 static void 
2399 _notify_object_state_change (GObject *obj, gchar *name, gboolean set)
2400 {
2401     gchar *output_str = g_strdup_printf("name %s %s set", 
2402                         name, set ? "is" : "was");
2403     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_OBJECT,
2404       "State Change", output_str);
2405     g_free(output_str);
2406 }
2407
2408
2409 /* Function to print signals */
2410
2411 static void
2412 _print_signal(AtkObject *aobject, FerretSignalType type,
2413   const char *name, const char *info)
2414 {
2415     TabNumber top_tab = gtk_notebook_get_current_page (notebook) + OBJECT;
2416
2417     if (no_signals)
2418       return;
2419
2420     if (display_ascii)
2421       {
2422         if (info != NULL)
2423             g_print("SIGNAL:\t%-34s\t%s\n", name, info);
2424         else
2425             g_print("SIGNAL:\t%-34s\n", name);
2426       }
2427
2428     if (use_festival)
2429       {
2430         if ((type == FERRET_SIGNAL_TEXT) && (!strncmp(name, "Text Caret", 10)))
2431           {
2432             _speak_caret_event (aobject);
2433          }
2434         else if (type == FERRET_SIGNAL_TEXT)
2435           {
2436             last_caret_offset = atk_text_get_caret_offset (ATK_TEXT (aobject));
2437           }
2438       }
2439
2440     if (use_magnifier && ATK_IS_TEXT (aobject) &&
2441         (type == FERRET_SIGNAL_TEXT) && (!strncmp(name, "Text Caret", 10)))
2442       {
2443         gint x, y, w, h;
2444         gint caret_offset = atk_text_get_caret_offset (ATK_TEXT (aobject));
2445         atk_text_get_character_extents ( ATK_TEXT (aobject), caret_offset, &x, &y, &w, &h, ATK_XY_SCREEN);
2446         _send_to_magnifier (x, y, w, h);
2447       }
2448
2449     if ((type == FERRET_SIGNAL_TEXT && top_tab == TEXT) ||
2450         (type == FERRET_SIGNAL_TABLE && top_tab == TABLE) ||
2451         (type == FERRET_SIGNAL_OBJECT && top_tab == OBJECT))
2452       {
2453         if (display_ascii)
2454             g_print("Updating tab\n");
2455
2456         _update(top_tab, aobject);
2457       }
2458 }
2459
2460 /* Update functions */
2461
2462 static void
2463 _update (TabNumber top_tab, AtkObject *aobject)
2464 {
2465     gint group_num;
2466
2467     if (top_tab >= OBJECT && top_tab < END_TABS)
2468     {
2469        _clear_tab(top_tab);
2470     }
2471
2472     if (top_tab == OBJECT && ATK_IS_OBJECT(aobject))
2473       {
2474         group_num = _print_object(aobject);
2475         _finished_group(OBJECT, group_num);
2476         group_num = _print_relation(aobject);
2477         _finished_group(OBJECT, group_num);
2478         group_num = _print_state(aobject);
2479         _finished_group(OBJECT, group_num);
2480       }
2481     if (top_tab == TEXT && ATK_IS_TEXT(aobject))
2482       {
2483         group_num = _print_text(ATK_TEXT (aobject));
2484         _finished_group(TEXT, group_num);
2485         group_num = _print_text_attributes(ATK_TEXT (aobject));
2486         _finished_group(TEXT, group_num);
2487       }
2488     if (top_tab == SELECTION && ATK_IS_SELECTION(aobject))
2489       {
2490         group_num = _print_selection(ATK_SELECTION (aobject));
2491         _finished_group(SELECTION, group_num);
2492       }
2493     if (top_tab == TABLE && ATK_IS_TABLE(aobject))
2494       {
2495         group_num = _print_table(ATK_TABLE (aobject));
2496         _finished_group(TABLE, group_num);
2497       }
2498     if (top_tab == ACTION && ATK_IS_ACTION(aobject))
2499       {
2500         group_num = _print_action(ATK_ACTION (aobject));
2501         _finished_group(ACTION, group_num);
2502       }
2503     if (top_tab == COMPONENT && ATK_IS_COMPONENT(aobject))
2504       {
2505         group_num = _print_component(ATK_COMPONENT(aobject));
2506         _finished_group(COMPONENT, group_num);
2507       }
2508     if (top_tab == IMAGE && ATK_IS_IMAGE(aobject))
2509       {
2510         group_num = _print_image(ATK_IMAGE (aobject));
2511         _finished_group(IMAGE, group_num);
2512       }
2513     if (top_tab == VALUE && ATK_IS_VALUE(aobject))
2514       {
2515         group_num = _print_value(ATK_VALUE(aobject));
2516         _finished_group(VALUE, group_num);
2517       }
2518 }
2519
2520 static void
2521 _update_current_page(GtkNotebook *notebook, gpointer p, guint current_page)
2522 {
2523   _update(current_page+OBJECT, last_object);
2524 }
2525
2526 /* Property listeners */
2527
2528 static void _property_change_handler (AtkObject *obj,
2529   AtkPropertyValues *values)
2530 {
2531     TabNumber top_tab = gtk_notebook_get_current_page (notebook) + OBJECT;
2532
2533     if (no_signals)
2534       return;
2535
2536    /*
2537     * Only process if the property change corrisponds to the current
2538     * object
2539     */
2540     if (obj != last_object)
2541       {
2542         if (display_ascii)
2543           {
2544             g_print("\nProperty change event <%s> for object not in focus\n",
2545                 values->property_name);
2546           }
2547
2548         return;
2549       }
2550
2551     if (display_ascii)
2552       {
2553         g_print("\nProperty change event <%s> occurred.\n",
2554           values->property_name);
2555       }
2556
2557    /*
2558     * Update the top tab if a property changes.
2559     *
2560     * We may be able to ignore some property changes if they do not
2561     * change anything in ferret.
2562     */
2563
2564     if (top_tab == OBJECT &&
2565        ((strcmp (values->property_name, "accessible-name") == 0) ||
2566         (strcmp (values->property_name, "accessible-description") == 0) ||
2567         (strcmp (values->property_name, "accessible-parent") == 0) ||
2568         (strcmp (values->property_name, "accessible-value") == 0) ||
2569         (strcmp (values->property_name, "accessible-role") == 0) ||
2570         (strcmp (values->property_name, "accessible-component-layout") == 0) ||
2571         (strcmp (values->property_name, "accessible-component-mdi-zorder") == 0) ||
2572         (strcmp (values->property_name, "accessible-table-caption") == 0) ||
2573         (strcmp (values->property_name,
2574                  "accessible-table-column-description") == 0) ||
2575         (strcmp (values->property_name,
2576                  "accessible-table-column-header") == 0) ||
2577         (strcmp (values->property_name,
2578                  "accessible-table-row-description") == 0) ||
2579         (strcmp (values->property_name,
2580                  "accessible-table-row-header") == 0) ||
2581         (strcmp (values->property_name, "accessible-table-summary") == 0)))
2582       {
2583         if (display_ascii)
2584             g_print("Updating tab\n");
2585
2586         _update(top_tab, last_object);
2587       }
2588     else if (top_tab == VALUE &&
2589         (strcmp (values->property_name, "accessible-value") == 0))
2590       {
2591         if (display_ascii)
2592             g_print("Updating tab\n");
2593
2594         _update(top_tab, last_object);
2595       }
2596 }
2597
2598 /* Action button callback function */
2599
2600 void _action_cb(GtkWidget *widget, gpointer  *userdata)
2601 {
2602    NameValue *nv = (NameValue *)userdata;
2603    atk_action_do_action(ATK_ACTION(nv->atkobj), nv->action_num);
2604 }
2605
2606 /* Menu-bar callback functions */
2607
2608 void _toggle_terminal(GtkCheckMenuItem *checkmenuitem,
2609   gpointer user_data)
2610 {
2611    if (gtk_check_menu_item_get_active (checkmenuitem))
2612        display_ascii = TRUE;
2613    else
2614        display_ascii = FALSE;
2615 }
2616
2617 void _toggle_no_signals(GtkCheckMenuItem *checkmenuitem,
2618   gpointer user_data)
2619 {
2620    if (gtk_check_menu_item_get_active (checkmenuitem))
2621        no_signals = TRUE;
2622    else
2623        no_signals = FALSE;
2624 }
2625
2626 void _toggle_magnifier(GtkCheckMenuItem *checkmenuitem,
2627   gpointer user_data)
2628 {
2629    if (gtk_check_menu_item_get_active (checkmenuitem))
2630        use_magnifier = TRUE;
2631    else
2632        use_magnifier = FALSE;
2633 }
2634
2635 void _toggle_festival(GtkCheckMenuItem *checkmenuitem,
2636   gpointer user_data)
2637 {
2638    if (gtk_check_menu_item_get_active (checkmenuitem))
2639        use_festival = TRUE;
2640    else
2641        use_festival = FALSE;
2642 }
2643
2644 void _toggle_festival_terse(GtkCheckMenuItem *checkmenuitem,
2645   gpointer user_data)
2646 {
2647    if (gtk_check_menu_item_get_active (checkmenuitem))
2648      {
2649         say_role = FALSE;
2650         say_accel = FALSE;
2651      }
2652    else
2653      {
2654         say_role = TRUE;
2655         say_accel = TRUE;
2656      }
2657 }
2658
2659 void _toggle_trackmouse(GtkCheckMenuItem *checkmenuitem,
2660   gpointer user_data)
2661 {
2662    if (gtk_check_menu_item_get_active (checkmenuitem))
2663      {
2664         mouse_watcher_focus_id =
2665           atk_add_global_event_listener(_mouse_watcher,
2666           "Gtk:GtkWidget:enter_notify_event");
2667         mouse_watcher_button_id =
2668           atk_add_global_event_listener(_button_watcher,
2669           "Gtk:GtkWidget:button_press_event");
2670        track_mouse = TRUE;
2671      }
2672    else
2673      {
2674        if (mouse_watcher_focus_id != -1)
2675          {
2676            atk_remove_global_event_listener(mouse_watcher_focus_id);
2677            atk_remove_global_event_listener(mouse_watcher_button_id);
2678            track_mouse = FALSE;
2679          }
2680      }
2681 }
2682
2683 void _toggle_trackfocus(GtkCheckMenuItem *checkmenuitem,
2684   gpointer user_data)
2685 {
2686    if (gtk_check_menu_item_get_active (checkmenuitem))
2687      {
2688        track_focus = TRUE;
2689        focus_tracker_id = atk_add_focus_tracker (_print_accessible);
2690      }
2691    else
2692      { 
2693        g_print ("No longer tracking focus.\n");
2694        track_focus = FALSE;
2695        atk_remove_focus_tracker (focus_tracker_id);
2696      }
2697 }