Always emit children-changed, property-change, and state-changed events
[platform/core/uifw/at-spi2-atk.git] / cspi / spi-event.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
6  * Copyright 2001, 2002, 2003 Ximian, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include <cspi/spi-private.h>
25
26 static GSList *_cspi_event_queue = NULL;
27
28 /**
29  * SPI_freeAccessibleKeySet:
30  * @keyset: An AccessibleKeyset to free.
31  *
32  * Release the memory used by an AccessibleKeySet.
33  *
34  **/
35 void
36 SPI_freeAccessibleKeySet (AccessibleKeySet *keyset)
37 {
38   int i = 0;    
39   g_free (keyset->keysyms);
40   g_free (keyset->keycodes);
41   while (keyset->keystrings [i])
42     {
43       g_free (keyset->keystrings [i++]);
44     }
45   g_free (keyset->keystrings);
46   g_free (keyset);
47 }
48
49 /**
50  * SPI_createAccessibleKeySet:
51  * @len: the number of key values in the key set.
52  * @keysyms: a UTF-8 string containing symbolic key values to be matched, or NULL if
53  *           matching is performed against other key values instead.
54  * @keycodes: an array of unsigned short values which are the hardware keycodes
55  *           to be matched, or NULL if the keyset is specified solely by keysyms
56  *           and/or keystrings.
57  * @keystrings: an array of null-terminated character strings which specify key
58  *             name values to match, or NULL if the keyset is specified solely by
59  *             keycodes and/or keysyms.
60  *
61  * Create a new #AccessibleKeySet of a specified length.
62  * A KeySet is used typically to match key event values, and a matches are made
63  * using the following criteria: a match exists with a key event if all non-null
64  * i-th members of the keyset match the key event.
65  * If both keystring and keysym values are NULL, a keycode value match is
66  * forced, thus the match for keysym=0, keycode=0, keystring=NULL is
67  * keycode 0.
68  *
69  * Returns: a pointer to a newly-created #AccessibleKeySet.
70  *
71  **/
72 AccessibleKeySet *
73 SPI_createAccessibleKeySet (int len, const char *keysyms, short *keycodes,
74                             const char **keystrings)
75 {
76   AccessibleKeySet *keyset = g_new0 (AccessibleKeySet, 1);
77   int i, keysym_len = 0;
78   const char *keysym_ptr = keysyms;
79   keyset->len = len;
80   keyset->keysyms = g_new0 (unsigned long, len);
81   keyset->keycodes = g_new0 (unsigned short, len);
82   keyset->keystrings = g_new0 (char *, len);
83   if (keysyms)
84     {
85       keysym_len = g_utf8_strlen (keysyms, -1);
86     }
87   for (i = 0; i < len; ++i)
88     {
89       if (i < keysym_len)
90         {
91           keyset->keysyms [i] = (unsigned long) g_utf8_get_char (keysym_ptr);
92           keysym_ptr = g_utf8_find_next_char (keysym_ptr, NULL);
93         }
94       else
95         {
96           keyset->keysyms [i] = 0;
97         }
98       if (keycodes)
99         {
100           keyset->keycodes [i] = keycodes [i];
101         }
102       if (keystrings)
103         {
104           keyset->keystrings [i] = g_strdup (keystrings [i]);
105         }
106     }
107   return keyset;        
108 }
109
110 /**
111  * SPI_createAccessibleEventListener:
112  * @callback : an #AccessibleEventListenerCB callback function, or NULL.
113  * @user_data: a pointer to data which will be passed to the callback when invoked.
114  *
115  * Create a new #AccessibleEventListener with a specified (in-process) callback function.
116  *
117  * Returns: a pointer to a newly-created #AccessibleEventListener.
118  *
119  **/
120 AccessibleEventListener *
121 SPI_createAccessibleEventListener (AccessibleEventListenerCB callback,
122                                    void                     *user_data)
123 {
124   AccessibleEventListener *listener = cspi_event_listener_new ();
125   if (callback)
126     {
127       AccessibleEventListener_addCallback (listener, callback, user_data);
128     }
129   return listener;
130 }
131
132 /**
133  * AccessibleEventListener_addCallback:
134  * @listener: the #AccessibleEventListener instance to modify.
135  * @callback: an #AccessibleEventListenerCB function pointer.
136  * @user_data: a pointer to data which will be passed to the callback when invoked.
137  *
138  * Add an in-process callback function to an existing AccessibleEventListener.
139  * Note that the callback function must live in the same address
140  * space as the AccessibleEventListener implementation code, thus one should not
141  * use this function to attach callbacks to a 'remote' event listener
142  * (that is, one that was not created by a client call to
143  * createAccessibleEventListener ();
144  *
145  * Returns: #TRUE if successful, otherwise #FALSE.
146  *
147  **/
148 SPIBoolean
149 AccessibleEventListener_addCallback (AccessibleEventListener *listener,
150                                      AccessibleEventListenerCB callback,
151                                      void                     *user_data)
152 {
153   cspi_event_listener_add_cb (listener, callback, user_data);
154   return TRUE;
155 }
156
157 /**
158  * AccessibleEventListener_unref:
159  * @listener: a pointer to the #AccessibleEventListener being operated on.
160  *
161  * Decrements an #AccessibleEventListener's reference count.
162  **/
163 void
164 AccessibleEventListener_unref (AccessibleEventListener *listener)
165 {
166   cspi_event_listener_unref (listener);
167 }
168
169 /**
170  * AccessibleEventListener_removeCallback:
171  * @listener: the #AccessibleEventListener instance to modify.
172  * @callback: an #AccessibleEventListenerCB function pointer.
173  *
174  * Remove an in-process callback function from an existing AccessibleEventListener.
175  *
176  * Returns: #TRUE if successful, otherwise #FALSE.
177  *
178  **/
179 SPIBoolean
180 AccessibleEventListener_removeCallback (AccessibleEventListener  *listener,
181                                         AccessibleEventListenerCB callback)
182 {
183   cspi_event_listener_remove_cb (listener, callback);
184   return TRUE;
185 }
186
187 /**
188  * SPI_createAccessibleKeystrokeListener:
189  * @callback : an #AccessibleKeystrokeListenerCB callback function, or NULL.
190  * @user_data: a pointer to data which will be passed to the callback when invoked.
191  *
192  * Create a new #AccessibleKeystrokeListener with a specified callback function.
193  *
194  * Returns: a pointer to a newly-created #AccessibleKeystrokeListener.
195  *
196  **/
197 AccessibleKeystrokeListener *
198 SPI_createAccessibleKeystrokeListener (AccessibleKeystrokeListenerCB callback,
199                                        void                         *user_data)
200 {
201   AccessibleDeviceListener *listener = cspi_device_listener_new ();
202   if (callback)
203     {
204       AccessibleDeviceListener_addCallback (listener, callback, user_data);
205     }
206   return listener;
207 }
208
209 /**
210  * AccessibleKeystrokeListener_addCallback:
211  * @listener: the #AccessibleKeystrokeListener instance to modify.
212  * @callback: an #AccessibleKeystrokeListenerCB function pointer.
213  * @user_data: a pointer to data which will be passed to the callback when invoked.
214  *
215  * Add an in-process callback function to an existing #AccessibleKeystrokeListener.
216  *
217  * Returns: #TRUE if successful, otherwise #FALSE.
218  *
219  **/
220 SPIBoolean
221 AccessibleKeystrokeListener_addCallback (AccessibleKeystrokeListener *listener,
222                                          AccessibleKeystrokeListenerCB callback,
223                                          void                         *user_data)
224 {
225   cspi_device_listener_add_cb (listener, callback, user_data);
226   return TRUE;
227 }
228
229 /**
230  * AccessibleKeystrokeListener_removeCallback:
231  * @listener: the #AccessibleKeystrokeListener instance to modify.
232  * @callback: an #AccessibleKeystrokeListenerCB function pointer.
233  *
234  * Remove an in-process callback function from an existing #AccessibleKeystrokeListener.
235  *
236  * Returns: #TRUE if successful, otherwise #FALSE.
237  *
238  **/
239 SPIBoolean
240 AccessibleKeystrokeListener_removeCallback (AccessibleKeystrokeListener *listener,
241                                             AccessibleKeystrokeListenerCB callback)
242 {
243   cspi_device_listener_remove_cb (listener, callback);
244   return TRUE;
245 }
246
247 /**
248  * AccessibleKeystrokeListener_unref:
249  * @listener: a pointer to the #AccessibleKeystrokeListener being operated on.
250  *
251  * Decrements an #AccessibleKeystrokeListener's reference count.
252  **/
253 void
254 AccessibleKeystrokeListener_unref (AccessibleKeystrokeListener *listener)
255 {
256   cspi_device_listener_unref (listener);
257 }
258
259 /**
260  * SPI_createAccessibleDeviceListener:
261  * @callback : an #AccessibleDeviceListenerCB callback function, or NULL.
262  * @user_data: a pointer to data which will be passed to the callback when invoked.
263  *
264  * Create a new #AccessibleDeviceListener with a specified callback function.
265  *
266  * Returns: a pointer to a newly-created #AccessibleDeviceListener.
267  *
268  **/
269 AccessibleDeviceListener *
270 SPI_createAccessibleDeviceListener (AccessibleDeviceListenerCB callback,
271                                        void                         *user_data)
272 {
273   AccessibleDeviceListener *listener = cspi_device_listener_new ();
274   if (callback)
275     {
276       AccessibleDeviceListener_addCallback (listener, callback, user_data);
277     }
278   return listener;
279 }
280
281 /**
282  * AccessibleDeviceListener_addCallback:
283  * @listener: the #AccessibleDeviceListener instance to modify.
284  * @callback: an #AccessibleDeviceListenerCB function pointer.
285  * @user_data: a pointer to data which will be passed to the callback when invoked.
286  *
287  * Add an in-process callback function to an existing #AccessibleDeviceListener.
288  *
289  * Returns: #TRUE if successful, otherwise #FALSE.
290  *
291  **/
292 SPIBoolean
293 AccessibleDeviceListener_addCallback (AccessibleDeviceListener *listener,
294                                          AccessibleDeviceListenerCB callback,
295                                          void                         *user_data)
296 {
297   cspi_device_listener_add_cb (listener, callback, user_data);
298   return TRUE;
299 }
300
301 /**
302  * AccessibleDeviceListener_removeCallback:
303  * @listener: the #AccessibleDeviceListener instance to modify.
304  * @callback: an #AccessibleDeviceListenerCB function pointer.
305  *
306  * Remove an in-process callback function from an existing #AccessibleDeviceListener.
307  *
308  * Returns: #TRUE if successful, otherwise #FALSE.
309  *
310  **/
311 SPIBoolean
312 AccessibleDeviceListener_removeCallback (AccessibleDeviceListener *listener,
313                                             AccessibleDeviceListenerCB callback)
314 {
315   cspi_device_listener_remove_cb (listener, callback);
316   return TRUE;
317 }
318
319 /**
320  * AccessibleDeviceListener_unref:
321  * @listener: a pointer to the #AccessibleDeviceListener being operated on.
322  *
323  * Decrements an #AccessibleDeviceListener's reference count.
324  **/
325 void
326 AccessibleDeviceListener_unref (AccessibleDeviceListener *listener)
327 {
328   cspi_device_listener_unref (listener);
329 }
330
331 static char *
332 cspi_internal_event_get_text (const InternalEvent *e)
333 {
334   g_return_val_if_fail (e, NULL);
335   if (e->event.v_type == EVENT_DATA_STRING)
336     {
337       return g_strdup (e->event.v.text? e->event.v.text: "");
338     }
339   return NULL;
340 }
341
342 static Accessible *
343 cspi_internal_event_get_object (const InternalEvent *e)
344 {
345   g_return_val_if_fail (e, NULL);
346   if (e->event.v_type == EVENT_DATA_OBJECT)
347     {
348       cspi_object_ref (e->event.v.accessible);
349       return e->event.v.accessible;
350     }
351   return NULL;
352 }
353
354 static SPIRect *
355 cspi_internal_event_get_rect (const InternalEvent *e)
356 {
357   g_return_val_if_fail (e, NULL);
358   if (e->event.v_type == EVENT_DATA_RECT)
359   {
360       SPIRect *rect = g_new (SPIRect, 1);
361       if (rect) memcpy (rect, &e->event.v.rect, sizeof(*rect));
362       return rect;
363     }
364   return NULL;
365 }
366
367 /**
368  * AccessibleEvent_getSourceName:
369  * @e: an #AccessibleEvent to be queried. 
370  *
371  * Get the 'accessible-name' of the object emitting the event.
372  *
373  * Returns: The name of the event source, or NULL if the event source cannot be identified
374  *          or does not report a name.
375  */
376 char*        AccessibleEvent_getSourceName (const AccessibleEvent *e)
377 {
378   if (e && e->source)
379     {
380       return Accessible_getName (e->source);
381     }
382   return NULL;
383 }
384
385 /**
386  * AccessibleEvent_getSourceRole:
387  * @e: an #AccessibleEvent to be queried. 
388  *
389  * Get the #AccessibleRole of the object emitting the event.
390  *
391  * Returns: #AccessibleRole of the event source, or SPI_ROLE_UNKNOWN
392  *          if the event source's role is unknown or unspecified.
393  *          (Some kinds of events, such as 'mouse:' events or
394  *          toolkit events, don't have associated object roles.)
395  */
396 AccessibleRole AccessibleEvent_getSourceRole (const AccessibleEvent *e)
397 {
398   if (e && e->source)
399     {
400       return Accessible_getRole (e->source);
401     }
402         return SPI_ROLE_UNKNOWN;
403 }
404
405 /**
406  * AccessibleEvent_getSourceApplication:
407  * @e: an #AccessibleEvent to be queried. 
408  *
409  * Get the #Application hosting the object which emitted the event.
410  *
411  * Returns: A pointer to the host #Application contining the event source
412  *          component.
413  */
414 #if 0
415 AccessibleApplication* AccessibleEvent_getSourceApplication (const AccessibleEvent *e)
416 {
417 xyzzy
418     InternalEvent *ie = (InternalEvent *)e;
419     CORBA_any *any = ((ie && ie->data) ? (CORBA_any *)ie->data : NULL);
420     if (any &&
421         CORBA_TypeCode_equivalent (any->_type, 
422                                    TC_Accessibility_EventDetails, NULL))
423       {
424           Accessibility_EventDetails *details = (Accessibility_EventDetails *) any->_value;
425           return  cspi_object_take (details->host_application);
426       }
427     else
428         return NULL;
429 }
430 #endif
431
432 /**
433  * AccessibleEvent_getSourceDetails:
434  * @e: an #AccessibleEvent to be queried. 
435  * @name: a pointer to a character string which will point to the name of the event source 
436  * on successful completion of the call.
437  * @role: a pointer to an #AccessibleRole which will point to the role of the event source
438  * on successful completion of the call.
439  * @app: A pointer to an #AccessibleApplication which points to the host application for this event
440  * on successful completion of the call.
441  *
442  * Get the host #Application, "accessible name", and #AccessibleRole 
443  * of the object which emitted the event.
444  *
445  * Returns: TRUE if the source details were successfully retrieved, 
446  *          FALSE if they were not, either due to error, incomplete data,
447  *          or the fact that the event did not encapsulate the required data.
448  */
449 #if 0
450 SPIBoolean   AccessibleEvent_getSourceDetails (const AccessibleEvent *e, 
451                                                char **name, AccessibleRole *role, 
452                                                AccessibleApplication **app)
453 {
454     InternalEvent *ie = (InternalEvent *)e;
455     CORBA_any *any = ((ie && ie->data) ? (CORBA_any *)ie->data : NULL);
456     if (any &&
457         CORBA_TypeCode_equivalent (any->_type, 
458                                    TC_Accessibility_EventDetails, NULL))
459       {
460           Accessibility_EventDetails *details = (Accessibility_EventDetails *) any->_value;
461           *name = CORBA_string_dup (details->source_name);
462           *role = cspi_role_from_spi_role (details->source_role);
463           *app = cspi_object_take (details->host_application);
464           return TRUE;
465       }
466     else
467       {
468         *name = NULL;
469         *role = SPI_ROLE_UNKNOWN;
470         *app = NULL;
471         return FALSE;
472       }
473 }
474 #endif
475
476 /**
477  * AccessibleTextChangedEvent_getChangeString:
478  * @e: a pointer to the #AccessibleEvent being queried.
479  *
480  * Queries an #AccessibleEvent of type "object:text-changed", 
481  *         returning the text inserted or deleted.
482  *
483  * Returns: a UTF-8 text string indicating the text inserted,
484  *          deleted, or substituted by this event.
485  **/
486 char *
487 AccessibleTextChangedEvent_getChangeString (const AccessibleEvent *e)
488 {
489   const InternalEvent *foo = (InternalEvent *) e;
490   /* TODO: check the event type. */
491   return cspi_internal_event_get_text (foo);
492 }
493
494 /**
495  * AccessibleTextSelectionChangedEvent_getSelectionString:
496  * @e: a pointer to the #AccessibleEvent being queried.
497  *
498  * Queries an #AccessibleEvent of type "object:text-selection-changed", 
499  *         returning the newly added, removed, or modified selection string.
500  *
501  * Returns: a UTF-8 text string indicating the recently changed selection.
502  **/
503 char *
504 AccessibleTextSelectionChangedEvent_getSelectionString (const AccessibleEvent *e)
505 {
506   const InternalEvent *foo = (InternalEvent *) e;
507   /* TODO: check the event type. */
508   return cspi_internal_event_get_text (foo);
509 }
510
511 /**
512  * AccessibleWindowEvent_getTitleString:
513  * @e: a pointer to the #AccessibleEvent being queried.
514  *
515  * Queries an #AccessibleEvent of type "window:", 
516  *         returning the window title.
517  *
518  * Returns: a UTF-8 text string representing the title of the 
519  *         recently changed window.
520  **/
521 char *
522 AccessibleWindowEvent_getTitleString (const AccessibleEvent *e)
523 {
524   const InternalEvent *foo = (InternalEvent *) e;
525   /* TODO: check the event type. */
526   return cspi_internal_event_get_text (foo);
527 }
528
529 /**
530  * AccessibleChildChangedEvent_getChildAccessible:
531  * @e: a pointer to the #AccessibleEvent being queried.
532  *
533  * Queries an #AccessibleEvent of type "object:children_changed"
534  *         to get a reference to the changed #Accessible.
535  *         Note that context #Accessibles are not guaranteed to outlive
536  *         event delivery, in which case this call may return %NULL
537  *         even if the object existed at the time of dispatch.
538  *
539  * Returns: the context #Accessible for the event, or %NULL if
540  *          there is no longer a valid context #Accessible 
541  *          object for the event.
542  **/
543 Accessible *
544 AccessibleChildChangedEvent_getChildAccessible (const AccessibleEvent *e)
545 {
546   const InternalEvent *foo = (InternalEvent *) e;
547   return (Accessible *) cspi_internal_event_get_object (foo);
548 }
549
550 /**
551  * AccessibleParentChangedEvent_getParentAccessible:
552  * @e: a pointer to the #AccessibleEvent being queried.
553  *
554  * Queries an #AccessibleEvent of type "object:property-change:accessible-parent"
555  *         to get a reference to the changed #Accessible.
556  *         Note that context #Accessibles are not guaranteed to outlive
557  *         event delivery, in which case this call may return %NULL
558  *         even if the object existed at the time of dispatch.
559  *
560  * Returns: an #Accessible pointer representing the new parent object.
561  **/
562 Accessible *
563 AccessibleParentChangedEvent_getParentAccessible (const AccessibleEvent *e)
564 {
565   const InternalEvent *foo = (InternalEvent *) e;
566   return (Accessible *) cspi_internal_event_get_object (foo);
567 }
568
569 /**
570  * AccessibleActiveDescendantChangedEvent_getActiveDescendant:
571  * @e: a pointer to the #AccessibleEvent being queried.
572  *
573  * Queries an #AccessibleEvent of type "object:active-descendant-changed"
574  *         to get a reference to the changed #Accessible.
575  *         Note that context #Accessibles are not guaranteed to outlive
576  *         event delivery, in which case this call may return %NULL
577  *         even if the object existed at the time of dispatch.
578  *
579  * Returns: an #Accessible pointer representing the new active descendant.
580  **/
581 Accessible *
582 AccessibleActiveDescendantChangedEvent_getActiveDescendant (const AccessibleEvent *e) 
583 {
584   const InternalEvent *foo = (InternalEvent *) e;
585   return (Accessible *) cspi_internal_event_get_object (foo);
586 }
587
588 /**
589  * AccessibleTableSummaryChangedEvent_getSummaryAccessible:
590  * @e: a pointer to the #AccessibleEvent being queried.
591  *
592  * Queries an #AccessibleEvent of type "object:property-changed:accessible-table-summary"
593  *         to get a reference to the changed #Accessible.
594  *         Note that context #Accessibles are not guaranteed to outlive
595  *         event delivery, in which case this call may return %NULL
596  *         even if the object existed at the time of dispatch.
597  *
598  * Returns: an #Accessible pointer representing the new table summary.
599  **/
600 Accessible *
601 AccessibleTableSummaryChangedEvent_getSummaryAccessible (const AccessibleEvent *e) 
602 {
603   const InternalEvent *foo = (InternalEvent *) e;
604   return (Accessible *) cspi_internal_event_get_object (foo);
605 }
606
607 /**
608  * AccessibleTableHeaderChangedEvent_getHeaderAccessible:
609  * @e: a pointer to the #AccessibleEvent being queried.
610  *
611  * Queries an #AccessibleEvent of type 
612  *         "object:property-changed:accessible-table-row-header" or
613  *         "object:property-changed:accessible-table-column-header"
614  *         to get a reference to the changed #Accessible.
615  *         Note that context #Accessibles are not guaranteed to outlive
616  *         event delivery, in which case this call may return %NULL
617  *         even if the object existed at the time of dispatch.
618  *
619  * Returns: an #Accessible pointer representing the new table header.
620  **/
621 Accessible *
622 AccessibleTableHeaderChangedEvent_getHeaderAccessible (const AccessibleEvent *e)
623 {
624   const InternalEvent *foo = (InternalEvent *) e;
625   return (Accessible *) cspi_internal_event_get_object (foo);
626 }
627
628
629 /**
630  * AccessibleTableCaptionChangedEvent_getCaptionString:
631  * @e: a pointer to the #AccessibleEvent being queried.
632  *
633  * Queries an #AccessibleEvent of type 
634  *         "object:property-changed:accessible-table-caption-object" 
635  *         returning the text in the caption, if present.
636  *
637  * Returns: a UTF-8 text string indicating the text in the caption.
638  **/
639 char *
640 AccessibleTableCaptionChangedEvent_getCaptionString (const AccessibleEvent *e)
641 {
642   const InternalEvent *foo = (InternalEvent *) e;
643   /* TODO: check the event type. */
644   return cspi_internal_event_get_text (foo);
645 }
646
647 /**
648  * AccessibleTableRowDescriptionChangedEvent_getDescriptionString:
649  * @e: a pointer to the #AccessibleEvent being queried.
650  *
651  * Queries an #AccessibleEvent of type 
652  *         "object:property-changed:accessible-table-row-description" 
653  *         returning the new table row description.
654  *
655  * Returns: a UTF-8 text string representing the recently changed
656  *         table row description 
657  **/
658 char *
659 AccessibleTableRowDescriptionChangedEvent_getDescriptionString (const AccessibleEvent *e)
660 {
661   const InternalEvent *foo = (InternalEvent *) e;
662   /* TODO: check the event type. */
663   return cspi_internal_event_get_text (foo);
664 }
665
666 /**
667  * AccessibleTableColumnDescriptionChangedEvent_getDescriptionString:
668  * @e: a pointer to the #AccessibleEvent being queried.
669  *
670  * Queries an #AccessibleEvent of type 
671  *         "object:property-changed:accessible-table-column-description" 
672  *         returning the new table column description.
673  *
674  * Returns: a UTF-8 text string representing the recently changed
675  *         table column description 
676  **/
677 char *
678 AccessibleTableColumnDescriptionChangedEvent_getDescriptionString (const AccessibleEvent *e)
679 {
680   const InternalEvent *foo = (InternalEvent *) e;
681   /* TODO: check the event type. */
682   return cspi_internal_event_get_text (foo);
683 }
684
685 /**
686  * AccessibleDescriptionChangedEvent_getDescriptionString:
687  * @e: a pointer to the #AccessibleEvent being queried.
688  *
689  * Queries an #AccessibleEvent of type 
690  *         "object:property-changed:accessible-description" 
691  *         returning the new description.
692  *
693  * Returns: a UTF-8 text string representing the recently changed
694  *         description 
695  **/
696 char *
697 AccessibleDescriptionChangedEvent_getDescriptionString (const AccessibleEvent *e)
698 {
699   const InternalEvent *foo = (InternalEvent *) e;
700   /* TODO: check the event type. */
701   return cspi_internal_event_get_text (foo);
702 }
703
704 /**
705  * AccessibleBoundsChangedEvent_getNewBounds:
706  * @e: a pointer to the #AccessibleEvent being queried.
707  *
708  * Queries an #AccessibleEvent of type "object:bounds-changed", 
709  *         returning a pointer to an SPIRect structure containing the
710  *         new bounds, or NULL on error.
711  *         The returned structure should be freed with SPI_freeRect when 
712  *         the caller has finished referencing it.
713  *
714  * @Since: AT-SPI 1.6
715  *
716  * Returns: a pointer to an SPIRect defining the new object bounds.
717  **/
718 SPIRect *
719 AccessibleBoundsChangedEvent_getNewBounds (const AccessibleEvent *e)
720 {
721   const InternalEvent *foo = (InternalEvent *) e;
722   /* TODO: check the event type. */
723   return cspi_internal_event_get_rect (foo);
724 }
725
726 static gint
727 cspi_event_compare (gconstpointer p1, gconstpointer p2)
728 {
729   const InternalEvent *e1 = p1, *e2 = p2;
730   return (gint) ((long) e2->id  - (long) e1->id);
731 }
732
733 static InternalEvent *
734 cspi_internal_event_lookup (const InternalEvent *e)
735 {
736   InternalEvent *internal = NULL;
737   GSList *p =
738     g_slist_find_custom (_cspi_event_queue, e, cspi_event_compare);
739   if (p)
740     internal = p->data;
741   return internal;
742 }
743
744 static const InternalEvent *
745 cspi_internal_event_check (const AccessibleEvent *e)
746 {
747   InternalEvent *internal = (InternalEvent *) e;
748   if (internal->magic == SPI_INTERNAL_EVENT_MAGIC) 
749     return internal;
750   else
751     return NULL;
752 }
753
754 static InternalEvent *
755 cspi_internal_event_add (const InternalEvent *e)
756 {
757   _cspi_event_queue = g_slist_prepend (_cspi_event_queue, (gpointer) e);
758   return (InternalEvent *) e;
759 }
760
761 static void
762 cspi_internal_event_remove (const InternalEvent *e)
763 {
764   GSList *link = g_slist_find_custom (_cspi_event_queue, e, cspi_event_compare);
765   if (link)
766     _cspi_event_queue = g_slist_remove_link (_cspi_event_queue, link);
767 }
768
769 /**
770  * AccessibleNameChangedEvent_getNameString:
771  * @e: a pointer to the #AccessibleEvent being queried.
772  *
773  * Queries an #AccessibleEvent of type "object:property-change:accessible_name:", 
774  *         returning the name.
775  *
776  * Returns: a UTF-8 text string representing the name of the 
777  *         object which recently changed.
778  **/
779 char *
780 AccessibleNameChangedEvent_getNameString (const AccessibleEvent *e)
781 {
782   const InternalEvent *foo = (InternalEvent *) e;
783   return cspi_internal_event_get_text (foo);
784 }
785
786 /**
787  * AccessibleEvent_ref:
788  * @e: a pointer to the #AccessibleEvent being referenced.
789  *
790  * Increments by 1 the reference count of the event
791  *
792  * Returns: TRUE if the function succeeded; FALSE if the pointer is not a
793  *         valid event.
794  **/
795 SPIBoolean
796 AccessibleEvent_ref (const AccessibleEvent *e)
797 {
798   const InternalEvent *private = cspi_internal_event_check (e);
799   if (private)
800     {
801       InternalEvent *event = cspi_internal_event_lookup (private);
802       /* 
803        * put event in the cache if it's not there already, 
804        * and increment refcount 
805        */
806       if (!event)
807         {
808           event = cspi_internal_event_add (private);
809         }
810       event->ref_count++;
811       return TRUE;
812     }
813   else
814     return FALSE;
815 }
816
817 /**
818  * AccessibleEvent_unref:
819  * @e: a pointer to the #AccessibleEvent being referenced.
820  *
821  * Decrements by 1 the reference count of the event. The event is destroyed
822  * when the reference count recahes zero.
823  *
824  **/
825 void
826 AccessibleEvent_unref (const AccessibleEvent *e)
827 {
828   const InternalEvent *private = cspi_internal_event_check (e);
829   /* decrement refcount and remove if appropriate */
830   if (private)
831     {
832       InternalEvent *event = cspi_internal_event_lookup (private);
833       if (event) 
834         {
835           event->ref_count--;
836           if (event->ref_count < 1)
837             {
838               cspi_internal_event_remove (event);
839               g_free ((gpointer)e->type);
840               Accessible_unref (e->source);
841               if (event->event.v_type == EVENT_DATA_OBJECT)
842                 {
843                   Accessible_unref (event->event.v.accessible);
844                 }
845               g_free ((gpointer)e);
846             }
847         }
848     }
849 }
850
851 typedef struct
852 {
853   CSpiEventListener *listener;
854   char *category;
855   char *name;
856   char *detail;
857 } CSpiEventListenerEntry;
858
859 static GList *event_listeners = NULL;
860
861 static dbus_bool_t
862 demarshal_rect (DBusMessageIter *iter, SPIRect *rect)
863 {
864   dbus_int32_t x, y, width, height;
865   DBusMessageIter iter_struct;
866
867   dbus_message_iter_recurse (iter, &iter_struct);
868   if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
869   dbus_message_iter_get_basic (&iter_struct, &x);
870   dbus_message_iter_next (&iter_struct);
871   if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
872   dbus_message_iter_get_basic (&iter_struct, &y);
873   dbus_message_iter_next (&iter_struct);
874   if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
875   dbus_message_iter_get_basic (&iter_struct, &width);
876   dbus_message_iter_next (&iter_struct);
877   if (dbus_message_iter_get_arg_type (&iter_struct) != DBUS_TYPE_INT32) return FALSE;
878   dbus_message_iter_get_basic (&iter_struct, &height);
879   rect->x = x;
880   rect->y = y;
881   rect->width = width;
882   rect->height = height;
883   return TRUE;
884 }
885
886 static gboolean
887 parse_eventType (const char *eventType, char **categoryp, char **namep, char **detailp, char **matchrule)
888 {
889   char *tmp = g_strdup (eventType);
890   char *category = NULL, *name = NULL, *detail = NULL;
891   char *saveptr = NULL;
892   char *p;
893
894   if (tmp == NULL) return FALSE;
895     while ((p = strchr (tmp, '-'))) *p = '_';
896   category = strtok_r (tmp, ":", &saveptr);
897   if (category) category = g_strdup (category);
898   if (!category) goto oom;
899   name = strtok_r (NULL, ":", &saveptr);
900   if (name)
901   {
902     name = g_strdup (name);
903     if (!name) goto oom;
904     detail = strtok_r (NULL, ":", &saveptr);
905     if (detail) detail = g_strdup (detail);
906   }
907   else
908   {
909     name = g_strdup (category);
910     if (!name) goto oom;
911   }
912   if (matchrule)
913   {
914     *matchrule = g_strdup_printf ("type='signal',interface='org.a11y.atspi.event.%c%s',member='%s'", toupper(category[0]), category + 1, name);
915     if (!*matchrule) goto oom;
916   }
917   if (categoryp) *categoryp = category;
918   else g_free (category);
919   if (namep) *namep = name;
920   else if (name) g_free (name);
921   if (detailp) *detailp = detail;
922   else if (detail) g_free (detail);
923   g_free (tmp);
924   return TRUE;
925 oom:
926   if (tmp) g_free (tmp);
927   if (category) g_free (category);
928   if (name) g_free (name);
929   if (detail) g_free (detail);
930   return FALSE;
931 }
932
933 static void listener_data_free (CSpiEventListenerEntry *e)
934 {
935   g_free (e->category);
936   g_free (e->name);
937   if (e->detail) g_free (e->detail);
938   g_free (e);
939 }
940
941 /**
942  * SPI_registerGlobalEventListener:
943  * @listener: the #AccessibleEventListener to be registered against an
944  *            event type.
945  * @eventType: a character string indicating the type of events for which
946  *            notification is requested.  Format is
947  *            EventClass:major_type:minor_type:detail
948  *            where all subfields other than EventClass are optional.
949  *            EventClasses include "object", "window", "mouse",
950  *            and toolkit events (e.g. "Gtk", "AWT").
951  *            Examples: "focus:", "Gtk:GtkWidget:button_press_event".
952  *
953  * Legal object event types:
954  *
955  *    (property change events)
956  *
957  *            object:property-change
958  *            object:property-change:accessible-name
959  *            object:property-change:accessible-description
960  *            object:property-change:accessible-parent
961  *            object:property-change:accessible-value
962  *            object:property-change:accessible-role
963  *            object:property-change:accessible-table-caption
964  *            object:property-change:accessible-table-column-description
965  *            object:property-change:accessible-table-column-header
966  *            object:property-change:accessible-table-row-description
967  *            object:property-change:accessible-table-row-header
968  *            object:property-change:accessible-table-summary
969  *
970  *    (other object events)
971  *
972  *            object:state-changed 
973  *            object:children-changed
974  *            object:visible-data-changed
975  *            object:selection-changed
976  *            object:text-selection-changed
977  *            object:text-changed
978  *            object:text-caret-moved
979  *            object:row-inserted
980  *            object:row-reordered
981  *            object:row-deleted
982  *            object:column-inserted
983  *            object:column-reordered
984  *            object:column-deleted
985  *            object:model-changed
986  *            object:active-descendant-changed
987  *
988  *  (window events)
989  *
990  *            window:minimize
991  *            window:maximize
992  *            window:restore
993  *            window:close
994  *            window:create
995  *            window:reparent
996  *            window:desktop-create
997  *            window:desktop-destroy
998  *            window:activate
999  *            window:deactivate
1000  *            window:raise
1001  *            window:lower
1002  *            window:move
1003  *            window:resize
1004  *            window:shade
1005  *            window:unshade
1006  *            window:restyle
1007  *
1008  *  (other events)
1009  *
1010  *            focus:
1011  *            mouse:abs
1012  *            mouse:rel
1013  *            mouse:b1p
1014  *            mouse:b1r
1015  *            mouse:b2p
1016  *            mouse:b2r
1017  *            mouse:b3p
1018  *            mouse:b3r
1019  *
1020  * NOTE: this string may be UTF-8, but should not contain byte value 56
1021  *            (ascii ':'), except as a delimiter, since non-UTF-8 string
1022  *            delimiting functions are used internally.
1023  *            In general, listening to
1024  *            toolkit-specific events is not recommended.
1025  *
1026  * Add an in-process callback function to an existing AccessibleEventListener.
1027  *
1028  * Returns: #TRUE if successful, otherwise #FALSE.
1029  **/
1030 SPIBoolean
1031 SPI_registerGlobalEventListener (AccessibleEventListener *listener,
1032                                  const char              *eventType)
1033 {
1034   CSpiEventListenerEntry *e;
1035   char *matchrule;
1036   DBusError error;
1037   GList *new_list;
1038
1039   if (!listener)
1040     {
1041       return FALSE;
1042     }
1043
1044   e = g_new (CSpiEventListenerEntry, 1);
1045   if (!e) return FALSE;
1046   e->listener = listener;
1047   if (!parse_eventType (eventType, &e->category, &e->name, &e->detail, &matchrule))
1048   {
1049     g_free (e);
1050     return FALSE;
1051   }
1052   new_list = g_list_prepend (event_listeners, e);
1053   if (!new_list)
1054   {
1055     listener_data_free (e);
1056     return FALSE;
1057   }
1058   event_listeners = new_list;
1059   dbus_error_init (&error);
1060   dbus_bus_add_match (SPI_bus(), matchrule, &error);
1061   if (error.message)
1062   {
1063     g_warning ("Adding match: %s", error.message);
1064   }
1065   return TRUE;
1066 }
1067
1068 /**
1069  * SPI_deregisterGlobalEventListenerAll:
1070  * @listener: the #AccessibleEventListener to be registered against
1071  *            an event type.
1072  *
1073  * deregisters an AccessibleEventListener from the registry, for all
1074  *            event types it may be listening to. Use
1075  *            AccessibleEventListener_unref to release the
1076  *            listener reference.
1077  *
1078  * Returns: #TRUE if successful, otherwise #FALSE.
1079  **/
1080 SPIBoolean
1081 SPI_deregisterGlobalEventListenerAll (AccessibleEventListener *listener)
1082 {
1083   GList *l;
1084
1085   if (!listener)
1086     {
1087       return FALSE;
1088     }
1089
1090   for (l = event_listeners; l;)
1091   {
1092     CSpiEventListenerEntry *e = l->data;
1093     if (e->listener == listener)
1094     {
1095       listener_data_free (e);
1096       l = g_list_remove (l, e);
1097     }
1098     else l = g_list_next (l);
1099   }
1100   return TRUE;
1101 }
1102
1103 /**
1104  * SPI_deregisterGlobalEventListener:
1105  * @listener: the #AccessibleEventListener registered against an event type.
1106  * @eventType: a string specifying the event type for which this
1107  *             listener is to be deregistered.
1108  *
1109  * deregisters an AccessibleEventListener from the registry, for a specific
1110  *             event type.
1111  *
1112  * Returns: #TRUE if successful, otherwise #FALSE.
1113  **/
1114 SPIBoolean
1115 SPI_deregisterGlobalEventListener (AccessibleEventListener *listener,
1116                                    const char              *eventType)
1117 {
1118   char *category, *name, *detail, *matchrule;
1119   GList *l;
1120
1121   if (!parse_eventType (eventType, &category, &name, &detail, &matchrule))
1122   {
1123     return FALSE;
1124   }
1125   if (!listener)
1126     {
1127       return FALSE;
1128     }
1129
1130   for (l = event_listeners; l;)
1131   {
1132     CSpiEventListenerEntry *e = l->data;
1133     if (e->listener == listener && !strcmp (e->category, category) && !strcmp (e->name, name) && (e->detail == detail || !strcmp (e->detail, detail)))
1134     {
1135       DBusError error;
1136       listener_data_free (e);
1137       l = g_list_remove (l, e);
1138       dbus_error_init (&error);
1139       dbus_bus_remove_match (SPI_bus(), matchrule, &error);
1140     }
1141     else l = g_list_next (l);
1142   }
1143   g_free (category);
1144   g_free (name);
1145   if (detail) g_free (detail);
1146   g_free (matchrule);
1147   return TRUE;
1148 }
1149
1150 void
1151 cspi_dispatch_event (AccessibleEvent *e)
1152 {
1153   char *category, *name, *detail;
1154   GList *l;
1155
1156   if (!parse_eventType (e->type, &category, &name, &detail, NULL))
1157   {
1158     g_warning ("Couldn't parse event: %s\n", e->type);
1159     return;
1160   }
1161   for (l = event_listeners; l; l = g_list_next (l))
1162   {
1163     CSpiEventListenerEntry *entry = l->data;
1164     if (!strcmp (category, entry->category) &&
1165         (entry->name == NULL || !strcmp (name, entry->name)) &&
1166         (entry->detail == NULL || !strcmp (detail, entry->detail)))
1167     {
1168       CSpiEventListenerClass *klass = CSPI_EVENT_LISTENER_GET_CLASS (entry->listener);
1169       if (klass->event) (*klass->event)(entry->listener, e);
1170     }
1171   }
1172   if (detail) g_free (detail);
1173   g_free (name);
1174   g_free (category);
1175 }
1176
1177 DBusHandlerResult
1178 cspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data)
1179 {
1180   char *detail = NULL;
1181   const char *category = dbus_message_get_interface (message);
1182   const char *name = dbus_message_get_member (message);
1183   DBusMessageIter iter, iter_variant;
1184   dbus_message_iter_init (message, &iter);
1185   AccessibleEvent e;
1186   dbus_int32_t detail1, detail2;
1187   char *p;
1188
1189   if (category)
1190   {
1191     category = strrchr (category, '.');
1192     if (category == NULL)
1193     {
1194       // TODO: Error
1195       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1196     }
1197     category++;
1198   }
1199   g_return_val_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
1200   dbus_message_iter_get_basic (&iter, &detail);
1201   dbus_message_iter_next (&iter);
1202   /* TODO: Return error indicating invalid arguments  in next line */
1203   g_return_val_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_INT32, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
1204   dbus_message_iter_get_basic (&iter, &detail1);
1205   e.detail1 = detail1;
1206   dbus_message_iter_next (&iter);
1207   g_return_val_if_fail (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_INT32, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
1208   dbus_message_iter_get_basic (&iter, &detail2);
1209   e.detail2 = detail2;
1210   dbus_message_iter_next (&iter);
1211   e.type = g_strdup_printf ("%c%s:", tolower (category[0]), category + 1);
1212   if (strcasecmp  (category, name) != 0)
1213   {
1214     p = g_strconcat (e.type, ":", name, NULL);
1215     if (p)
1216     {
1217       g_free (e.type);
1218       e.type = p;
1219     }
1220   }
1221   if (detail[0] != '\0')
1222   {
1223     p = g_strconcat (e.type, ":", detail, NULL);
1224     if (p)
1225     {
1226       g_free (e.type);
1227       e.type = p;
1228     }
1229   }
1230     while ((p = strchr (e.type, '_'))) *p = '-';
1231   e.source = cspi_ref_accessible (dbus_message_get_sender(message), dbus_message_get_path(message));
1232   dbus_message_iter_recurse (&iter, &iter_variant);
1233   switch (dbus_message_iter_get_arg_type (&iter_variant))
1234   {
1235     case DBUS_TYPE_OBJECT_PATH:
1236     {
1237       dbus_message_iter_get_basic (&iter_variant, &p);
1238       e.v_type = EVENT_DATA_OBJECT;
1239       e.v.accessible = cspi_ref_accessible (dbus_message_get_sender(message), p);
1240       break;
1241     }
1242     case DBUS_TYPE_STRING:
1243     {
1244       dbus_message_iter_get_basic (&iter_variant, &p);
1245       e.v_type = EVENT_DATA_STRING;
1246       e.v.text = g_strdup (p);
1247       break;
1248     }
1249     case DBUS_TYPE_STRUCT:
1250     {
1251       if (demarshal_rect (&iter_variant, &e.v.rect))
1252       {
1253         e.v_type = EVENT_DATA_RECT;
1254       }
1255       break;
1256     }
1257   default:
1258     break;
1259   }
1260   cspi_dispatch_event (&e);
1261   return DBUS_HANDLER_RESULT_HANDLED;
1262 }