]> Pileus Git - ~andy/gtk/blob - modules/other/gail/tests/ferret.c
Use gtk_box_new() instead gtk_[v|h]box_new()
[~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, length_msg;
273   gchar buff[100];
274
275   sprintf (buff, "~5:%d,%d", x+w/2, y+h/2);
276
277 #ifdef MAG_DEBUG
278   g_print ("sending magnifier: %s\n", buff);
279 #endif
280
281 #ifdef HAVE_SOCKADDR_UN_SUN_LEN
282   mag_server.sun_len = SUN_LEN(&mag_server);
283   client.sun_len = SUN_LEN(&client);
284 #endif
285   
286   if((desc=socket(AF_UNIX,SOCK_STREAM,0)) == -1){
287     perror("socket");
288     return;
289   }
290   unlink("/tmp/mag_client");
291
292   if (bind(desc, (struct sockaddr*)&client, sizeof(client)) == -1)
293     {
294       perror("bind");
295       return;
296     }
297
298   if (connect(desc,(struct sockaddr*)&mag_server,sizeof(mag_server)) == -1)
299     {
300       perror("connect");
301       return;
302     }
303
304   length_msg = write(desc,buff,strlen(buff));
305   unlink("/tmp/mag_client");
306   return;
307 }
308
309 static int _festival_init (void)
310 {
311   int fd;
312   struct sockaddr_in name;
313   int tries = 2;
314
315   name.sin_family = AF_INET;
316   name.sin_port = htons (1314);
317   name.sin_addr.s_addr = htonl(INADDR_ANY);
318   fd = socket (PF_INET, SOCK_STREAM, 0);
319
320   while (connect(fd, (struct sockaddr *) &name, sizeof (name)) < 0) {
321     if (!tries--) {
322       perror ("connect");
323       return -1;
324     }
325   }
326
327   _festival_write ("(audio_mode'async)", fd);
328   return fd;
329 }
330
331 static void _festival_say (const gchar *text)
332 {
333   static int fd = 0;
334   gchar *quoted;
335   gchar *p;
336   gchar prefix [100];
337   const gchar *stretch;
338
339   fprintf (stderr, "saying %s\n", text);
340
341   if (!fd)
342     {
343       fd = _festival_init ();
344     }
345
346   quoted = g_malloc(100+strlen(text)*2);
347
348   stretch = g_strdup (g_getenv ("FESTIVAL_STRETCH"));
349   if (!stretch) stretch = "0.75";
350   sprintf (prefix, "(audio_mode'shutup)\n (Parameter.set 'Duration_Stretch %s)\n (SayText \"", stretch);
351   
352   strcpy(quoted, prefix);
353   p = quoted + strlen (prefix);
354   while (*text) {
355     if ( *text == '\\' || *text == '"' )
356       *p = '\\';
357     *p++ = *text++;
358   }
359   *p++ = '"';
360   *p++ = ')';
361   *p = 0;
362
363   _festival_write (quoted, fd);
364
365   g_free(quoted);
366 }
367
368
369 static void _send_to_festival (const gchar *role_name,
370   const gchar *name, const gchar *accel)
371 {
372   gchar *string;
373   int len = (strlen (role_name)+1 + strlen (name)+2 + 4 + strlen (accel)+2);
374   int i, j;
375   gchar ch;
376   gchar *accel_name;
377   
378   string = (gchar *) g_malloc (len * sizeof (gchar));
379
380   i = 0;
381   if (say_role)
382     {
383       j = 0;
384       while (role_name[j])
385         {
386           ch = role_name[j++];
387           if (ch == '_') ch = ' ';
388           string[i++] = ch;
389         };
390       string[i++] = ' ';
391     }
392   j = 0;
393   while (name[j])
394     {
395       ch = name[j++];
396       if (ch == '_') ch = ' ';
397       string[i++] = ch;
398     };
399   if ((say_accel) && (strlen (accel) > 0))
400     {
401       accel_name = (gchar *)accel;
402       if (strncmp (accel, "<C", 2) == 0)
403         {
404           accel_name = strncpy (accel_name, " control ", 9);
405         }
406       else if (strncmp (accel, " control", 5))
407         {
408           string[i++] = ' ';
409           string[i++] = 'a';
410           string[i++] = 'l';
411           string[i++] = 't';
412           string[i++] = ' ';
413         }
414       j = 0;
415       while (accel_name[j])
416         {
417           ch = accel_name[j++];
418           if (ch == '_') ch = ' ';
419           string[i++] = ch;
420         };
421     }
422   string[i] = '\0';
423
424   _festival_say (string);
425   g_free (string);
426 }
427
428 static void _festival_write (const gchar *command_string, int fd)
429 {
430   gssize n_bytes;
431
432   if (fd < 0) {
433     perror("socket");
434     return;
435   }
436   n_bytes = write(fd, command_string, strlen(command_string));
437   g_assert (n_bytes == strlen(command_string));
438 }
439
440 static void _speak_caret_event (AtkObject *aobject)
441 {
442   gint dummy1, dummy2;
443   gint caret_offset = atk_text_get_caret_offset (ATK_TEXT (aobject));
444   gchar * text;
445
446   if (abs(caret_offset - last_caret_offset) > 1)
447     {
448       text = atk_text_get_text_at_offset (ATK_TEXT (aobject),
449                                               caret_offset,
450                                               ATK_TEXT_BOUNDARY_LINE_START,
451                                               &dummy1,
452                                               &dummy2);
453     }
454   else
455     {
456       text = atk_text_get_text_before_offset (ATK_TEXT (aobject),
457                                               caret_offset,
458                                               ATK_TEXT_BOUNDARY_CHAR,
459                                               &dummy1,
460                                               &dummy2);
461     }
462   _festival_say (text);
463   g_free (text);
464   last_caret_offset = caret_offset;
465 }
466
467 static void
468 _greyout_tab (GtkWidget *page_child, gboolean is_sensitive)
469 {
470   GtkWidget *tab;
471
472   tab = gtk_notebook_get_tab_label (notebook, page_child);
473   if (tab)
474       gtk_widget_set_sensitive (GTK_WIDGET (tab), is_sensitive);
475 }
476
477 static void
478 _refresh_notebook (AtkObject *aobject)
479 {
480   if (ATK_IS_OBJECT (aobject))
481   {
482     _greyout_tab (nbook_tabs[ACTION]->page, ATK_IS_ACTION(aobject));
483     _greyout_tab (nbook_tabs[COMPONENT]->page, ATK_IS_COMPONENT(aobject));
484     _greyout_tab (nbook_tabs[IMAGE]->page, ATK_IS_IMAGE(aobject));
485     _greyout_tab (nbook_tabs[SELECTION]->page, ATK_IS_SELECTION(aobject));
486     _greyout_tab (nbook_tabs[TABLE]->page, ATK_IS_TABLE(aobject));
487     _greyout_tab (nbook_tabs[TEXT]->page, ATK_IS_TEXT(aobject));
488     _greyout_tab (nbook_tabs[VALUE]->page, ATK_IS_VALUE(aobject));
489   }
490 }
491
492 static void _print_accessible (AtkObject *aobject)
493 {
494   TabNumber top_tab;
495
496   if (_object_is_ours(aobject))
497     {
498       if (display_ascii)
499         g_print("\nFocus entered the ferret output window!\n");
500       return;
501     }
502
503   _refresh_notebook(aobject);
504
505   if (display_ascii)
506     g_print("\nFocus change\n");
507
508   /* Do not attach signal handlers if the user has asked not to */
509   if (!no_signals)
510     _update_handlers(aobject);
511   else
512     last_object = aobject; /* _update_handler normally does this */
513
514   top_tab = gtk_notebook_get_current_page (notebook) + OBJECT;
515   _update(top_tab, aobject);
516
517   if (use_magnifier)
518     {
519       gint x, y;
520       gint w=0, h=0;
521       
522       if (ATK_IS_TEXT (aobject))
523         {
524           gint x0, y0, w0, h0;
525           gint xN, yN, wN, hN;
526           gint len;
527           len = atk_text_get_character_count (ATK_TEXT (aobject));
528           atk_text_get_character_extents (ATK_TEXT (aobject), 0,
529                                           &x0, &y0, &w0, &h0,
530                                           ATK_XY_SCREEN);
531           if (len > 0)
532             {
533               atk_text_get_character_extents (ATK_TEXT (aobject), len-1,
534                                               &xN, &yN, &wN, &hN,
535                                               ATK_XY_SCREEN);
536               x = MIN (x0, xN);
537               y = MIN (y0, yN);
538               w = MAX (x0+w0, xN+wN) - x;
539               h = MAX (y0+h0, yN+hN) - y;
540             }
541           else
542             {
543               x = x0;
544               y = y0;
545             }
546         } 
547       else if (ATK_IS_COMPONENT (aobject))
548         {
549           atk_component_get_extents (ATK_COMPONENT(aobject),
550                                      &x, &y, &w, &h,
551                                      ATK_XY_SCREEN);
552         }
553       if (w > -1) _send_to_magnifier (x, y, w, h);
554     }
555 }
556
557 static gboolean
558 _object_is_ours (AtkObject *aobject)
559 {
560   /* determine whether this object is parented by our own accessible... */
561
562    AtkObject *toplevel = aobject;
563
564    while (atk_object_get_role(aobject) != ATK_ROLE_FRAME)
565      {
566        aobject = atk_object_get_parent (aobject);
567        if (aobject == NULL) break;
568        toplevel = aobject;
569      };
570
571   /*
572    * Some widgets do not have an ATK_ROLE_FRAME at the top,
573    * so ignore those.
574    */
575    if (aobject != NULL)
576      {
577        GtkWidget *widget;
578
579        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (toplevel));
580        if (widget == mainWindow)
581            return TRUE;
582      }
583
584   return FALSE;
585 }
586
587 static gchar *
588 ferret_get_name_from_container (AtkObject *aobject)
589 {
590   gchar *s = NULL;
591   gint n = atk_object_get_n_accessible_children (aobject);
592   gint i = 0;
593   
594   while (!s && (i < n))
595     {
596       AtkObject *child;     
597       child = atk_object_ref_accessible_child (aobject, i);
598       if (ATK_IS_TEXT (child))
599         {
600                 gint count = atk_text_get_character_count (ATK_TEXT (child));
601                 s = atk_text_get_text (ATK_TEXT (child),
602                                        0,
603                                        count);
604         }
605       g_object_unref (child);
606       ++i;          
607     }
608   
609   if (!s)
610     {
611       s = g_strdup ("");
612     }
613   return s;     
614 }
615
616 static gint
617 _print_object (AtkObject *aobject)
618 {
619     GtkWidget *widget;
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     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (aobject));
660     if (widget)
661       {
662         _print_key_value(tab_n, group_num, "Widget name",
663           (gpointer)gtk_widget_get_name (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 (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_get_focus (GTK_WINDOW (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_paned_new (GTK_ORIENTATION_VERTICAL);
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 (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_box_new (GTK_ORIENTATION_VERTICAL, 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_box_new (GTK_ORIENTATION_VERTICAL, 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_box_new (GTK_ORIENTATION_VERTICAL, 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_box_new (GTK_ORIENTATION_VERTICAL, 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_box_new (GTK_ORIENTATION_VERTICAL, 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_box_new (GTK_ORIENTATION_VERTICAL, 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_box_new (GTK_ORIENTATION_VERTICAL, 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_box_new (GTK_ORIENTATION_VERTICAL, 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
1670         g_signal_connect (window, "destroy",
1671                           G_CALLBACK (gtk_widget_destroyed),
1672                           &window);
1673
1674         gtk_window_set_title (GTK_WINDOW (window), "GTK+ Ferret Output");
1675         gtk_window_set_default_size (GTK_WINDOW (window), 333, 550);
1676         gtk_container_set_border_width (GTK_CONTAINER (window), 0);
1677
1678         vbox1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
1679         gtk_container_add (GTK_CONTAINER (window), vbox1);
1680         gtk_widget_show (vbox1);
1681
1682         menubar = gtk_menu_bar_new ();
1683         gtk_box_pack_start (GTK_BOX (vbox1), menubar, FALSE, TRUE, 0);
1684         gtk_widget_show (menubar);
1685         menutop = gtk_menu_item_new_with_label("Menu");
1686         gtk_menu_shell_append (GTK_MENU_SHELL (menubar), menutop);
1687         gtk_widget_show (menutop);
1688         menu = gtk_menu_new();
1689         gtk_menu_item_set_submenu (GTK_MENU_ITEM (menutop), menu);
1690         gtk_widget_show (menu);
1691
1692         _add_menu(&menu, &menuitem_trackmouse, "Track Mouse", track_mouse,
1693            G_CALLBACK(_toggle_trackmouse));
1694         _add_menu(&menu, &menuitem_trackfocus, "Track Focus", track_focus,
1695            G_CALLBACK(_toggle_trackfocus));
1696         _add_menu(&menu, &menuitem_magnifier, "Magnifier", use_magnifier,
1697            G_CALLBACK(_toggle_magnifier));
1698         _add_menu(&menu, &menuitem_festival, "Festival", use_festival,
1699            G_CALLBACK(_toggle_festival));
1700         _add_menu(&menu, &menuitem_festival_terse, "Festival Terse",
1701           (!say_role && !say_accel),
1702           G_CALLBACK(_toggle_festival_terse));
1703         _add_menu(&menu, &menuitem_terminal, "Terminal Output", display_ascii,
1704            G_CALLBACK(_toggle_terminal));
1705         _add_menu(&menu, &menuitem_no_signals, "No ATK Signals", no_signals,
1706            G_CALLBACK(_toggle_no_signals));
1707
1708         _create_notebook ();
1709         gtk_container_add (GTK_CONTAINER (vbox1), GTK_WIDGET (notebook));
1710         gtk_widget_show (GTK_WIDGET (notebook));
1711     }
1712     if (!gtk_widget_get_visible (window))
1713         gtk_widget_show (window);
1714
1715     mainWindow = GTK_WIDGET (window);
1716 }
1717
1718 static void
1719 _add_menu(GtkWidget ** menu, GtkWidget ** menuitem, gchar * name,
1720   gboolean init_value, GCallback func)
1721 {
1722     *menuitem = gtk_check_menu_item_new_with_label(name);
1723     gtk_check_menu_item_set_active(
1724       GTK_CHECK_MENU_ITEM(*menuitem), init_value);
1725     gtk_menu_shell_append (GTK_MENU_SHELL (*menu), *menuitem);
1726     gtk_widget_show(*menuitem);
1727     g_signal_connect(*menuitem, "toggled", func, NULL);
1728 }
1729
1730 int
1731 gtk_module_init(gint argc, char* argv[])
1732 {
1733     if (g_getenv ("FERRET_ASCII"))
1734        display_ascii = TRUE;
1735
1736     if (g_getenv ("FERRET_NOSIGNALS"))
1737        no_signals = TRUE;
1738
1739     if (display_ascii)
1740        g_print("GTK ferret Module loaded\n");
1741
1742     if (g_getenv("FERRET_MAGNIFIER"))
1743         use_magnifier = TRUE;
1744
1745     if (g_getenv ("FERRET_FESTIVAL"))
1746         use_festival = TRUE;
1747
1748     if (g_getenv ("FERRET_MOUSETRACK"))
1749         track_mouse = TRUE;
1750
1751     if (g_getenv ("FERRET_TERSE"))
1752       {
1753         say_role = FALSE;
1754         say_accel = FALSE;
1755       }
1756
1757     _init_data();
1758
1759     _create_window();
1760
1761     _create_event_watcher();
1762
1763     return 0;
1764 }
1765
1766 static void
1767 _clear_tab(TabNumber tab_n)
1768 {
1769     GList *group_list, *nv_list;
1770     TabInfo *tab;
1771     GroupInfo *group;
1772     NameValue *nv;
1773
1774     tab = nbook_tabs[tab_n];
1775
1776     for (group_list = tab->groups; group_list; group_list = group_list->next)
1777       {
1778         group = (GroupInfo *) group_list->data;
1779
1780         if (group->is_scrolled)
1781           gtk_widget_hide(GTK_WIDGET(group->scroll_outer_frame));
1782
1783         gtk_widget_hide(GTK_WIDGET(group->frame));
1784         gtk_widget_hide(GTK_WIDGET(group->group_vbox));
1785
1786         for (nv_list = group->name_value; nv_list; nv_list = nv_list->next)
1787           {
1788             nv = (NameValue *) nv_list->data;
1789             nv->active = FALSE;
1790             gtk_widget_hide(GTK_WIDGET(nv->column1));
1791             gtk_widget_hide(GTK_WIDGET(nv->column2));
1792             gtk_widget_hide(GTK_WIDGET(nv->label));
1793
1794             switch (nv->type)
1795               {
1796               case VALUE_STRING:
1797                 gtk_widget_hide(GTK_WIDGET(nv->string));
1798                 break;
1799               case VALUE_BOOLEAN:
1800                 gtk_widget_hide(GTK_WIDGET(nv->boolean));
1801                 break;
1802               case VALUE_TEXT:
1803                 gtk_widget_hide(GTK_WIDGET(nv->text));
1804                 break;
1805               case VALUE_BUTTON:
1806                 gtk_widget_hide(GTK_WIDGET(nv->button));
1807                 break;
1808               }
1809             gtk_widget_hide(GTK_WIDGET(nv->hbox));
1810
1811             /* Disconnect signal handler if any */
1812             if (nv->signal_id != -1)
1813               g_signal_handler_disconnect(nv->button, nv->signal_id);
1814
1815             nv->signal_id = -1;
1816           }
1817       }
1818 }
1819
1820 static gint
1821 _print_groupname(TabNumber tab_n, GroupId group_id,
1822   const char *groupname)
1823 {
1824   TabInfo *tab;
1825   GroupInfo *the_group;
1826   gint rc = -1;
1827
1828   if (display_ascii)
1829       g_print("\n<%s>\n", groupname);
1830
1831   tab = nbook_tabs[tab_n];
1832   the_group = _get_group(tab, group_id, groupname);
1833   rc = g_list_index(tab->groups, the_group);
1834   return rc;
1835 }
1836
1837 static GroupInfo*
1838 _get_group(TabInfo *tab, GroupId group_id, const gchar *groupname)
1839 {
1840     GroupInfo *group = NULL;
1841     gboolean found = FALSE;
1842     GList *group_list;
1843
1844     for (group_list = tab->groups; group_list; group_list = group_list->next)
1845       {
1846         group = (GroupInfo *) group_list->data;
1847         if (group_id == group->group_id)
1848           {
1849             found = TRUE;
1850             break;
1851           }
1852       }
1853
1854    if (!found)
1855      {
1856        /* build a new one */
1857        group = (GroupInfo *)g_new0(GroupInfo, 1);
1858        group->group_id = group_id;
1859        _get_group_scrolled(group);
1860
1861        if (group->is_scrolled)
1862          {
1863            group->frame = gtk_scrolled_window_new (NULL, NULL);
1864            gtk_widget_set_size_request (GTK_WIDGET (group->frame), -2,
1865              group->default_height);
1866            group->scroll_outer_frame = GTK_FRAME(gtk_frame_new(groupname));
1867            gtk_container_add(GTK_CONTAINER(group->scroll_outer_frame),
1868              group->frame);
1869          }
1870        else
1871          {
1872            group->frame = gtk_frame_new(groupname);
1873          }
1874
1875        gtk_container_set_border_width(GTK_CONTAINER(group->frame), 10);
1876
1877        group->name = g_strdup(groupname);
1878        group->group_vbox = GTK_VBOX(gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 10));
1879
1880        if (group->is_scrolled)
1881          {
1882            gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (group->frame),
1883               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1884            gtk_scrolled_window_add_with_viewport(
1885              GTK_SCROLLED_WINDOW(group->frame),
1886              GTK_WIDGET(group->group_vbox));
1887          }
1888        else
1889          {
1890            gtk_container_add(GTK_CONTAINER(group->frame),
1891              GTK_WIDGET(group->group_vbox));
1892          }
1893
1894        tab->groups = g_list_append (tab->groups, group);
1895
1896        if (group->is_scrolled)
1897          {
1898            gtk_box_pack_start (GTK_BOX (tab->main_box),
1899                                GTK_WIDGET (group->scroll_outer_frame),
1900                                TRUE, TRUE, 0);
1901          }
1902        else
1903          {
1904            gtk_box_pack_start (GTK_BOX (tab->main_box),
1905                                GTK_WIDGET (group->frame),
1906                                TRUE, TRUE, 0);
1907          }
1908      }
1909
1910    return group;
1911 }
1912
1913 void
1914 _get_group_scrolled(GroupInfo *group)
1915 {
1916    if (group->group_id == OBJECT_INTERFACE)
1917      {
1918        group->is_scrolled = FALSE;
1919      }
1920    else if (group->group_id == RELATION_INTERFACE)
1921      {
1922        group->is_scrolled = TRUE;
1923        group->default_height = 50;
1924      }
1925    else if (group->group_id == STATE_INTERFACE)
1926      {
1927        group->is_scrolled = TRUE;
1928        group->default_height = 100;
1929      }
1930    else if (group->group_id == ACTION_INTERFACE)
1931      {
1932        group->is_scrolled = TRUE;
1933        group->default_height = 200;
1934      }
1935    else if (group->group_id == TEXT_ATTRIBUTES)
1936      {
1937        group->is_scrolled = TRUE;
1938        group->default_height = 70;
1939      }
1940    else
1941      {
1942        group->is_scrolled = FALSE;
1943      }
1944 }
1945
1946 NameValue *
1947 _get_name_value(GroupInfo *group, const gchar *label,
1948   gpointer value_ptr, ValueType type)
1949 {
1950     NameValue *name_value = NULL;
1951     GList *nv_list;
1952     GValue *value;
1953     gboolean found = FALSE;
1954     static char *empty_string = "";
1955
1956     if (!label)
1957       {
1958         label = empty_string;
1959       }
1960
1961     for (nv_list = group->name_value; nv_list; nv_list = nv_list->next)
1962       {
1963         name_value = (NameValue *) nv_list->data;
1964         if (!name_value->active)
1965           {
1966             found = TRUE;
1967             break;
1968           }
1969       }
1970
1971     if (!found)
1972       {
1973         name_value = (NameValue *)g_new0(NameValue, 1);
1974         name_value->column1 = GTK_HBOX(gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 10));
1975         name_value->column2 = GTK_HBOX(gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 10));
1976         name_value->hbox = GTK_HBOX(gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 5));
1977         name_value->label = GTK_LABEL(gtk_label_new(label));
1978         name_value->string = gtk_label_new (NULL);
1979         name_value->boolean = gtk_check_button_new ();
1980         gtk_entry_buffer_set_max_length (gtk_entry_get_buffer (GTK_ENTRY (name_value->text)), 1000);
1981         name_value->button = GTK_BUTTON(gtk_button_new ());
1982
1983         gtk_box_pack_end(GTK_BOX(name_value->column1),
1984           GTK_WIDGET(name_value->label), FALSE, FALSE, 10);
1985
1986         switch (type)
1987           {
1988           case VALUE_STRING:
1989             gtk_label_set_text(GTK_LABEL(name_value->string),
1990               (gchar *) value_ptr);
1991             gtk_box_pack_start(GTK_BOX(name_value->column2),
1992               GTK_WIDGET(name_value->string), FALSE, FALSE, 10);
1993             break;
1994           case VALUE_BOOLEAN:
1995             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(name_value->boolean),
1996                *((gboolean *)value_ptr));
1997             gtk_widget_set_sensitive(name_value->boolean, FALSE);
1998             gtk_box_pack_start(GTK_BOX(name_value->column2),
1999               GTK_WIDGET(name_value->boolean), FALSE, FALSE, 10);
2000             break;
2001           case VALUE_TEXT:
2002             gtk_entry_set_text (GTK_ENTRY (name_value->text),
2003               (gchar *)value_ptr);
2004             gtk_box_pack_start(GTK_BOX(name_value->column2),
2005               GTK_WIDGET(name_value->text), FALSE, FALSE, 10);
2006           case VALUE_BUTTON:
2007             value = &(name_value->button_gval);
2008             memset (value, 0, sizeof (GValue));
2009             g_value_init (value, G_TYPE_STRING);
2010             g_value_set_string (value, (gchar *)value_ptr);
2011             g_object_set_property(G_OBJECT(name_value->button),
2012               "label", value);
2013             gtk_box_pack_start(GTK_BOX(name_value->column2),
2014               GTK_WIDGET(name_value->button), FALSE, FALSE, 10);
2015             break;
2016           }
2017
2018         gtk_box_pack_start (GTK_BOX (name_value->hbox),
2019                             GTK_WIDGET (name_value->column1),
2020                             TRUE, TRUE, 0);
2021         gtk_box_pack_start (GTK_BOX (name_value->hbox),
2022                             GTK_WIDGET (name_value->column2),
2023                             TRUE, TRUE, 0);
2024         gtk_container_add(GTK_CONTAINER(group->group_vbox),
2025           GTK_WIDGET(name_value->hbox));
2026         group->name_value = g_list_append (group->name_value, name_value);
2027       }
2028     else
2029       {
2030         gtk_label_set_text(GTK_LABEL(name_value->label), label);
2031         switch (type)
2032           {
2033           case VALUE_STRING:
2034             gtk_label_set_text(GTK_LABEL(name_value->string),
2035               (gchar *) value_ptr);
2036             break;
2037           case VALUE_BOOLEAN:
2038             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(name_value->boolean),
2039                *((gboolean *)value_ptr));
2040             gtk_widget_set_sensitive(name_value->boolean, FALSE);
2041             break;
2042           case VALUE_TEXT:
2043             gtk_entry_set_text (GTK_ENTRY (name_value->text),
2044               (gchar *) value_ptr);
2045             break;
2046           case VALUE_BUTTON:
2047             value = &(name_value->button_gval);
2048             memset (value, 0, sizeof (GValue));
2049             g_value_init (value, G_TYPE_STRING);
2050             g_value_set_string (value, (gchar *)value_ptr);
2051             g_object_set_property(G_OBJECT(name_value->button),
2052               "label", value);
2053             break;
2054           }
2055       }
2056
2057     name_value->active = TRUE;
2058     name_value->type = type;
2059     name_value->signal_id = -1;
2060
2061     gtk_widget_show(GTK_WIDGET(name_value->label));
2062
2063     switch (type)
2064       {
2065       case VALUE_STRING:
2066         gtk_widget_show(GTK_WIDGET(name_value->string));
2067         break;
2068       case VALUE_BOOLEAN:
2069         gtk_widget_show(GTK_WIDGET(name_value->boolean));
2070         break;
2071       case VALUE_TEXT:
2072         gtk_widget_show(GTK_WIDGET(name_value->text));
2073         break;
2074       case VALUE_BUTTON:
2075         gtk_widget_show(GTK_WIDGET(name_value->button));
2076         break;
2077       }
2078
2079     gtk_widget_show(GTK_WIDGET(name_value->column1));
2080     gtk_widget_show(GTK_WIDGET(name_value->column2));
2081     gtk_widget_show(GTK_WIDGET(name_value->hbox));
2082     gtk_widget_show(GTK_WIDGET(group->group_vbox));
2083
2084     return name_value;
2085 }
2086
2087 static NameValue *
2088 _print_key_value(TabNumber tab_n, gint group_number,
2089    const char *label, gpointer value, ValueType type)
2090 {
2091   TabInfo *tab;
2092   GroupInfo *the_group;
2093
2094   if (display_ascii)
2095     {
2096       if (type == VALUE_BOOLEAN)
2097         {
2098           if (*((gboolean *)value))
2099               g_print("\t%-30s\tTRUE\n", label);
2100           else
2101               g_print("\t%-30s\tFALSE\n", label);
2102         }
2103       else
2104         {
2105           g_print("\t%-30s\t%s\n", label, 
2106                   value ? (gchar *)value : "NULL");
2107         }
2108     }
2109
2110   tab = nbook_tabs[tab_n];
2111   the_group = (GroupInfo *)g_list_nth_data(tab->groups, group_number);
2112   return _get_name_value(the_group, label, (gpointer)value, type);
2113 }
2114
2115 static void
2116 _finished_group(TabNumber tab_no, gint group_number)
2117 {
2118     TabInfo *tab;
2119     GroupInfo *the_group;
2120
2121     tab = nbook_tabs[tab_no];
2122
2123     the_group = (GroupInfo *)g_list_nth_data(tab->groups, group_number);
2124
2125     if (the_group->is_scrolled)
2126       gtk_widget_show(GTK_WIDGET(the_group->scroll_outer_frame));
2127
2128     gtk_widget_show(GTK_WIDGET(the_group->frame));
2129     gtk_widget_show(GTK_WIDGET(the_group->group_vbox));
2130     gtk_widget_show(GTK_WIDGET(tab->main_box));
2131 }
2132
2133 /* Signal handlers */
2134
2135 static gulong child_added_id = 0;
2136 static gulong child_removed_id = 0;
2137 static gulong state_change_id = 0;
2138
2139 static gulong text_caret_handler_id = 0;
2140 static gulong text_inserted_id = 0;
2141 static gulong text_deleted_id = 0;
2142
2143 static gulong table_row_inserted_id = 0;
2144 static gulong table_column_inserted_id = 0;
2145 static gulong table_row_deleted_id = 0;
2146 static gulong table_column_deleted_id = 0;
2147 static gulong table_row_reordered_id = 0;
2148 static gulong table_column_reordered_id = 0;
2149
2150 static gulong property_id = 0;
2151
2152 static void
2153 _update_handlers(AtkObject *obj)
2154 {
2155     /* Remove signal handlers from object that had focus before */
2156
2157     if (last_object != NULL && G_TYPE_CHECK_INSTANCE(last_object))
2158       {
2159         if (child_added_id != 0)
2160            g_signal_handler_disconnect(last_object, child_added_id);
2161         if (child_removed_id != 0)
2162            g_signal_handler_disconnect(last_object, child_removed_id);
2163         if (state_change_id != 0)
2164            g_signal_handler_disconnect(last_object, state_change_id);
2165
2166         if (text_caret_handler_id != 0)
2167            g_signal_handler_disconnect(last_object, text_caret_handler_id);
2168         if (text_inserted_id != 0)
2169            g_signal_handler_disconnect(last_object, text_inserted_id);
2170         if (text_deleted_id != 0)
2171            g_signal_handler_disconnect(last_object, text_deleted_id);
2172
2173         if (table_row_inserted_id != 0)
2174            g_signal_handler_disconnect(last_object, table_row_inserted_id);
2175         if (table_column_inserted_id != 0)
2176            g_signal_handler_disconnect(last_object, table_column_inserted_id);
2177         if (table_row_deleted_id != 0)
2178            g_signal_handler_disconnect(last_object, table_row_deleted_id);
2179         if (table_column_deleted_id != 0)
2180            g_signal_handler_disconnect(last_object, table_column_deleted_id);
2181         if (table_row_reordered_id != 0)
2182            g_signal_handler_disconnect(last_object, table_row_reordered_id);
2183         if (table_column_reordered_id != 0)
2184            g_signal_handler_disconnect(last_object, table_column_reordered_id);
2185         if (property_id != 0)
2186            g_signal_handler_disconnect(last_object, property_id);
2187
2188         g_object_unref(last_object);
2189       }
2190
2191     last_object = NULL;
2192
2193     child_added_id = 0;
2194     child_removed_id = 0;
2195     text_caret_handler_id = 0;
2196     text_inserted_id = 0;
2197     text_deleted_id = 0;
2198     table_row_inserted_id = 0;
2199     table_column_inserted_id = 0;
2200     table_row_deleted_id = 0;
2201     table_column_deleted_id = 0;
2202     table_row_reordered_id = 0;
2203     table_column_reordered_id = 0;
2204     property_id = 0;
2205
2206     if (!G_TYPE_CHECK_INSTANCE(obj))
2207         return;
2208
2209     g_object_ref(obj);
2210     last_object = obj;
2211
2212     /* Add signal handlers to object that now has focus. */
2213
2214     if (ATK_IS_OBJECT(obj))
2215       {
2216          child_added_id = g_signal_connect_closure (obj,
2217                 "children_changed::add",
2218                 g_cclosure_new (G_CALLBACK (_notify_object_child_added),
2219                 NULL, NULL), FALSE);
2220
2221          child_removed_id = g_signal_connect_closure (obj,
2222                 "children_changed::remove",
2223                 g_cclosure_new (G_CALLBACK (_notify_object_child_removed),
2224                 NULL, NULL), FALSE);
2225
2226          state_change_id = g_signal_connect_closure (obj,
2227                 "state_change",
2228                 g_cclosure_new (G_CALLBACK (_notify_object_state_change),
2229                 NULL, NULL), FALSE);
2230       }
2231
2232     if (ATK_IS_TEXT(obj))
2233       {
2234         text_caret_handler_id = g_signal_connect_closure_by_id (obj,
2235                 g_signal_lookup ("text_caret_moved", G_OBJECT_TYPE (obj)),
2236                 0, g_cclosure_new (G_CALLBACK (_notify_caret_handler),
2237                 NULL, NULL), FALSE);
2238         text_inserted_id = g_signal_connect_closure (obj,
2239                 "text_changed::insert",
2240                 g_cclosure_new (G_CALLBACK (_notify_text_insert_handler),
2241                 NULL, NULL), FALSE);
2242         text_deleted_id = g_signal_connect_closure (obj,
2243                 "text_changed::delete",
2244                 g_cclosure_new (G_CALLBACK (_notify_text_delete_handler),
2245                 NULL, NULL), FALSE);
2246       }
2247
2248     if (ATK_IS_TABLE(obj))
2249       {
2250         table_row_inserted_id = g_signal_connect_closure_by_id (obj,
2251                 g_signal_lookup ("row_inserted", G_OBJECT_TYPE (obj)),
2252                 0, g_cclosure_new (G_CALLBACK (_notify_table_row_inserted),
2253                 NULL, NULL), FALSE);
2254         table_column_inserted_id = g_signal_connect_closure_by_id (obj,
2255                 g_signal_lookup ("column_inserted", G_OBJECT_TYPE (obj)),
2256                 0, g_cclosure_new (G_CALLBACK (_notify_table_column_inserted),
2257                 NULL, NULL), FALSE);
2258         table_row_deleted_id = g_signal_connect_closure_by_id (obj,
2259                 g_signal_lookup ("row_deleted", G_OBJECT_TYPE (obj)),
2260                 0, g_cclosure_new (G_CALLBACK (_notify_table_row_deleted),
2261                 NULL, NULL), FALSE);
2262         table_column_deleted_id = g_signal_connect_closure_by_id (obj,
2263                 g_signal_lookup ("column_deleted", G_OBJECT_TYPE (obj)),
2264                 0, g_cclosure_new (G_CALLBACK (_notify_table_column_deleted),
2265                 NULL, NULL), FALSE);
2266         table_row_reordered_id = g_signal_connect_closure_by_id (obj,
2267                 g_signal_lookup ("row_reordered", G_OBJECT_TYPE (obj)),
2268                 0, g_cclosure_new (G_CALLBACK (_notify_table_row_reordered),
2269                 NULL, NULL), FALSE);
2270         table_column_reordered_id = g_signal_connect_closure_by_id (obj,
2271                 g_signal_lookup ("column_reordered", G_OBJECT_TYPE (obj)),
2272                 0, g_cclosure_new (G_CALLBACK (_notify_table_column_reordered),
2273                 NULL, NULL), FALSE);
2274       }
2275
2276     property_id = g_signal_connect_closure_by_id (obj,
2277       g_signal_lookup ("property_change", G_OBJECT_TYPE (obj)),
2278       0, g_cclosure_new (G_CALLBACK (_property_change_handler),
2279       NULL, NULL),
2280           FALSE);
2281 }
2282
2283 /* Text signals */
2284
2285 static void
2286 _notify_text_insert_handler (GObject *obj, int position, int offset)
2287 {
2288     gchar *text = atk_text_get_text (ATK_TEXT (obj), position, position + offset);
2289     gchar *output_str = g_strdup_printf("position %d, length %d text: %s",
2290       position, offset,  text ? text: "<NULL>");
2291     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TEXT,
2292       "Text Insert", output_str);
2293     g_free(output_str);
2294 }
2295
2296 static void
2297 _notify_text_delete_handler (GObject *obj, int position, int offset)
2298 {
2299     gchar *text = atk_text_get_text (ATK_TEXT (obj), position, position + offset);
2300     gchar *output_str = g_strdup_printf("position %d, length %d text: %s",
2301       position, offset,  text ? text: "<NULL>");
2302     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TEXT,
2303       "Text Delete", output_str);
2304     g_free(output_str);
2305 }
2306
2307 static void
2308 _notify_caret_handler (GObject *obj, int position)
2309 {
2310     gchar *output_str = g_strdup_printf("position %d", position);
2311     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TEXT,
2312       "Text Caret Moved", output_str);
2313     g_free(output_str);
2314 }
2315
2316 /* Table signals */
2317
2318 static void
2319 _notify_table_row_inserted (GObject *obj, gint start_offset,
2320   gint length)
2321 {
2322     gchar *output_str =
2323       g_strdup_printf("position %d, num of rows inserted %d!\n",
2324       start_offset, length);
2325     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2326       "Table Row Inserted", output_str);
2327     g_free(output_str);
2328 }
2329
2330 static void
2331 _notify_table_column_inserted (GObject *obj, gint start_offset,
2332   gint length)
2333 {
2334     gchar *output_str =
2335       g_strdup_printf("position %d, num of rows inserted %d!\n",
2336       start_offset, length);
2337     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2338       "Table Column Inserted", output_str);
2339     g_free(output_str);
2340 }
2341
2342 static void
2343 _notify_table_row_deleted (GObject *obj, gint start_offset,
2344   gint length)
2345 {
2346     gchar *output_str = g_strdup_printf("position %d, num of rows inserted %d!\n",
2347       start_offset, length);
2348     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2349       "Table Row Deleted", output_str);
2350     g_free(output_str);
2351 }
2352
2353 static void
2354 _notify_table_column_deleted (GObject *obj, gint start_offset,
2355   gint length)
2356 {
2357     gchar *output_str = g_strdup_printf("position %d, num of rows inserted %d!\n",
2358       start_offset, length);
2359     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2360       "Table Column Deleted", output_str);
2361     g_free(output_str);
2362 }
2363
2364 static void
2365 _notify_table_row_reordered (GObject *obj)
2366 {
2367     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2368       "Table Row Reordered", NULL);
2369 }
2370
2371 static void
2372 _notify_table_column_reordered (GObject *obj)
2373 {
2374     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_TABLE,
2375       "Table Column Reordered", NULL);
2376 }
2377
2378 /* Object signals */
2379
2380 static void
2381 _notify_object_child_added (GObject *obj, gint index,
2382   AtkObject *child)
2383 {
2384     gchar *output_str = g_strdup_printf("index %d", index);
2385     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_OBJECT,
2386       "Child Added", output_str);
2387     g_free(output_str);
2388 }
2389
2390 static void
2391 _notify_object_child_removed (GObject *obj, gint index,
2392   AtkObject *child)
2393 {
2394     gchar *output_str = g_strdup_printf("index %d", index);
2395     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_OBJECT,
2396       "Child Removed", output_str);
2397     g_free(output_str);
2398 }
2399
2400 static void 
2401 _notify_object_state_change (GObject *obj, gchar *name, gboolean set)
2402 {
2403     gchar *output_str = g_strdup_printf("name %s %s set", 
2404                         name, set ? "is" : "was");
2405     _print_signal(ATK_OBJECT(obj), FERRET_SIGNAL_OBJECT,
2406       "State Change", output_str);
2407     g_free(output_str);
2408 }
2409
2410
2411 /* Function to print signals */
2412
2413 static void
2414 _print_signal(AtkObject *aobject, FerretSignalType type,
2415   const char *name, const char *info)
2416 {
2417     TabNumber top_tab = gtk_notebook_get_current_page (notebook) + OBJECT;
2418
2419     if (no_signals)
2420       return;
2421
2422     if (display_ascii)
2423       {
2424         if (info != NULL)
2425             g_print("SIGNAL:\t%-34s\t%s\n", name, info);
2426         else
2427             g_print("SIGNAL:\t%-34s\n", name);
2428       }
2429
2430     if (use_festival)
2431       {
2432         if ((type == FERRET_SIGNAL_TEXT) && (!strncmp(name, "Text Caret", 10)))
2433           {
2434             _speak_caret_event (aobject);
2435          }
2436         else if (type == FERRET_SIGNAL_TEXT)
2437           {
2438             last_caret_offset = atk_text_get_caret_offset (ATK_TEXT (aobject));
2439           }
2440       }
2441
2442     if (use_magnifier && ATK_IS_TEXT (aobject) &&
2443         (type == FERRET_SIGNAL_TEXT) && (!strncmp(name, "Text Caret", 10)))
2444       {
2445         gint x, y, w, h;
2446         gint caret_offset = atk_text_get_caret_offset (ATK_TEXT (aobject));
2447         atk_text_get_character_extents ( ATK_TEXT (aobject), caret_offset, &x, &y, &w, &h, ATK_XY_SCREEN);
2448         _send_to_magnifier (x, y, w, h);
2449       }
2450
2451     if ((type == FERRET_SIGNAL_TEXT && top_tab == TEXT) ||
2452         (type == FERRET_SIGNAL_TABLE && top_tab == TABLE) ||
2453         (type == FERRET_SIGNAL_OBJECT && top_tab == OBJECT))
2454       {
2455         if (display_ascii)
2456             g_print("Updating tab\n");
2457
2458         _update(top_tab, aobject);
2459       }
2460 }
2461
2462 /* Update functions */
2463
2464 static void
2465 _update (TabNumber top_tab, AtkObject *aobject)
2466 {
2467     gint group_num;
2468
2469     if (top_tab >= OBJECT && top_tab < END_TABS)
2470     {
2471        _clear_tab(top_tab);
2472     }
2473
2474     if (top_tab == OBJECT && ATK_IS_OBJECT(aobject))
2475       {
2476         group_num = _print_object(aobject);
2477         _finished_group(OBJECT, group_num);
2478         group_num = _print_relation(aobject);
2479         _finished_group(OBJECT, group_num);
2480         group_num = _print_state(aobject);
2481         _finished_group(OBJECT, group_num);
2482       }
2483     if (top_tab == TEXT && ATK_IS_TEXT(aobject))
2484       {
2485         group_num = _print_text(ATK_TEXT (aobject));
2486         _finished_group(TEXT, group_num);
2487         group_num = _print_text_attributes(ATK_TEXT (aobject));
2488         _finished_group(TEXT, group_num);
2489       }
2490     if (top_tab == SELECTION && ATK_IS_SELECTION(aobject))
2491       {
2492         group_num = _print_selection(ATK_SELECTION (aobject));
2493         _finished_group(SELECTION, group_num);
2494       }
2495     if (top_tab == TABLE && ATK_IS_TABLE(aobject))
2496       {
2497         group_num = _print_table(ATK_TABLE (aobject));
2498         _finished_group(TABLE, group_num);
2499       }
2500     if (top_tab == ACTION && ATK_IS_ACTION(aobject))
2501       {
2502         group_num = _print_action(ATK_ACTION (aobject));
2503         _finished_group(ACTION, group_num);
2504       }
2505     if (top_tab == COMPONENT && ATK_IS_COMPONENT(aobject))
2506       {
2507         group_num = _print_component(ATK_COMPONENT(aobject));
2508         _finished_group(COMPONENT, group_num);
2509       }
2510     if (top_tab == IMAGE && ATK_IS_IMAGE(aobject))
2511       {
2512         group_num = _print_image(ATK_IMAGE (aobject));
2513         _finished_group(IMAGE, group_num);
2514       }
2515     if (top_tab == VALUE && ATK_IS_VALUE(aobject))
2516       {
2517         group_num = _print_value(ATK_VALUE(aobject));
2518         _finished_group(VALUE, group_num);
2519       }
2520 }
2521
2522 static void
2523 _update_current_page(GtkNotebook *notebook, gpointer p, guint current_page)
2524 {
2525   _update(current_page+OBJECT, last_object);
2526 }
2527
2528 /* Property listeners */
2529
2530 static void _property_change_handler (AtkObject *obj,
2531   AtkPropertyValues *values)
2532 {
2533     TabNumber top_tab = gtk_notebook_get_current_page (notebook) + OBJECT;
2534
2535     if (no_signals)
2536       return;
2537
2538    /*
2539     * Only process if the property change corrisponds to the current
2540     * object
2541     */
2542     if (obj != last_object)
2543       {
2544         if (display_ascii)
2545           {
2546             g_print("\nProperty change event <%s> for object not in focus\n",
2547                 values->property_name);
2548           }
2549
2550         return;
2551       }
2552
2553     if (display_ascii)
2554       {
2555         g_print("\nProperty change event <%s> occurred.\n",
2556           values->property_name);
2557       }
2558
2559    /*
2560     * Update the top tab if a property changes.
2561     *
2562     * We may be able to ignore some property changes if they do not
2563     * change anything in ferret.
2564     */
2565
2566     if (top_tab == OBJECT &&
2567        ((strcmp (values->property_name, "accessible-name") == 0) ||
2568         (strcmp (values->property_name, "accessible-description") == 0) ||
2569         (strcmp (values->property_name, "accessible-parent") == 0) ||
2570         (strcmp (values->property_name, "accessible-value") == 0) ||
2571         (strcmp (values->property_name, "accessible-role") == 0) ||
2572         (strcmp (values->property_name, "accessible-component-layout") == 0) ||
2573         (strcmp (values->property_name, "accessible-component-mdi-zorder") == 0) ||
2574         (strcmp (values->property_name, "accessible-table-caption") == 0) ||
2575         (strcmp (values->property_name,
2576                  "accessible-table-column-description") == 0) ||
2577         (strcmp (values->property_name,
2578                  "accessible-table-column-header") == 0) ||
2579         (strcmp (values->property_name,
2580                  "accessible-table-row-description") == 0) ||
2581         (strcmp (values->property_name,
2582                  "accessible-table-row-header") == 0) ||
2583         (strcmp (values->property_name, "accessible-table-summary") == 0)))
2584       {
2585         if (display_ascii)
2586             g_print("Updating tab\n");
2587
2588         _update(top_tab, last_object);
2589       }
2590     else if (top_tab == VALUE &&
2591         (strcmp (values->property_name, "accessible-value") == 0))
2592       {
2593         if (display_ascii)
2594             g_print("Updating tab\n");
2595
2596         _update(top_tab, last_object);
2597       }
2598 }
2599
2600 /* Action button callback function */
2601
2602 void _action_cb(GtkWidget *widget, gpointer  *userdata)
2603 {
2604    NameValue *nv = (NameValue *)userdata;
2605    atk_action_do_action(ATK_ACTION(nv->atkobj), nv->action_num);
2606 }
2607
2608 /* Menu-bar callback functions */
2609
2610 void _toggle_terminal(GtkCheckMenuItem *checkmenuitem,
2611   gpointer user_data)
2612 {
2613    if (gtk_check_menu_item_get_active (checkmenuitem))
2614        display_ascii = TRUE;
2615    else
2616        display_ascii = FALSE;
2617 }
2618
2619 void _toggle_no_signals(GtkCheckMenuItem *checkmenuitem,
2620   gpointer user_data)
2621 {
2622    if (gtk_check_menu_item_get_active (checkmenuitem))
2623        no_signals = TRUE;
2624    else
2625        no_signals = FALSE;
2626 }
2627
2628 void _toggle_magnifier(GtkCheckMenuItem *checkmenuitem,
2629   gpointer user_data)
2630 {
2631    if (gtk_check_menu_item_get_active (checkmenuitem))
2632        use_magnifier = TRUE;
2633    else
2634        use_magnifier = FALSE;
2635 }
2636
2637 void _toggle_festival(GtkCheckMenuItem *checkmenuitem,
2638   gpointer user_data)
2639 {
2640    if (gtk_check_menu_item_get_active (checkmenuitem))
2641        use_festival = TRUE;
2642    else
2643        use_festival = FALSE;
2644 }
2645
2646 void _toggle_festival_terse(GtkCheckMenuItem *checkmenuitem,
2647   gpointer user_data)
2648 {
2649    if (gtk_check_menu_item_get_active (checkmenuitem))
2650      {
2651         say_role = FALSE;
2652         say_accel = FALSE;
2653      }
2654    else
2655      {
2656         say_role = TRUE;
2657         say_accel = TRUE;
2658      }
2659 }
2660
2661 void _toggle_trackmouse(GtkCheckMenuItem *checkmenuitem,
2662   gpointer user_data)
2663 {
2664    if (gtk_check_menu_item_get_active (checkmenuitem))
2665      {
2666         mouse_watcher_focus_id =
2667           atk_add_global_event_listener(_mouse_watcher,
2668           "Gtk:GtkWidget:enter_notify_event");
2669         mouse_watcher_button_id =
2670           atk_add_global_event_listener(_button_watcher,
2671           "Gtk:GtkWidget:button_press_event");
2672        track_mouse = TRUE;
2673      }
2674    else
2675      {
2676        if (mouse_watcher_focus_id != -1)
2677          {
2678            atk_remove_global_event_listener(mouse_watcher_focus_id);
2679            atk_remove_global_event_listener(mouse_watcher_button_id);
2680            track_mouse = FALSE;
2681          }
2682      }
2683 }
2684
2685 void _toggle_trackfocus(GtkCheckMenuItem *checkmenuitem,
2686   gpointer user_data)
2687 {
2688    if (gtk_check_menu_item_get_active (checkmenuitem))
2689      {
2690        track_focus = TRUE;
2691        focus_tracker_id = atk_add_focus_tracker (_print_accessible);
2692      }
2693    else
2694      { 
2695        g_print ("No longer tracking focus.\n");
2696        track_focus = FALSE;
2697        atk_remove_focus_tracker (focus_tracker_id);
2698      }
2699 }