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