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