2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2007 IBM Corp.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
23 /* collection.c: implements the Collection interface */
28 #include <droute/droute.h>
33 #include "accessible-stateset.h"
35 #include "accessible-register.h"
37 #include "introspection.h"
39 typedef struct _MatchRulePrivate MatchRulePrivate;
40 struct _MatchRulePrivate
43 AtspiCollectionMatchType statematchtype;
44 AtkAttributeSet *attributes;
45 AtspiCollectionMatchType attributematchtype;
47 AtspiCollectionMatchType rolematchtype;
49 AtspiCollectionMatchType interfacematchtype;
54 child_interface_p (AtkObject * child, gchar * repo_id)
56 if (!strcasecmp (repo_id, "action"))
57 return ATK_IS_ACTION (child);
58 if (!strcasecmp (repo_id, "component"))
59 return ATK_IS_COMPONENT (child);
60 if (!strcasecmp (repo_id, "editabletext"))
61 return ATK_IS_EDITABLE_TEXT (child);
62 if (!strcasecmp (repo_id, "text"))
63 return ATK_IS_TEXT (child);
64 if (!strcasecmp (repo_id, "hypertext"))
65 return ATK_IS_HYPERTEXT (child);
66 if (!strcasecmp (repo_id, "image"))
67 return ATK_IS_IMAGE (child);
68 if (!strcasecmp (repo_id, "selection"))
69 return ATK_IS_SELECTION (child);
70 if (!strcasecmp (repo_id, "table"))
71 return ATK_IS_TABLE (child);
72 if (!strcasecmp (repo_id, "value"))
73 return ATK_IS_VALUE (child);
74 if (!strcasecmp (repo_id, "streamablecontent"))
75 return ATK_IS_STREAMABLE_CONTENT (child);
76 if (!strcasecmp (repo_id, "document"))
77 return ATK_IS_DOCUMENT (child);
81 #define child_collection_p(ch) (TRUE)
84 match_states_all_p (AtkObject * child, gint * set)
90 if (set == NULL || set[0] == BITARRAY_SEQ_TERM)
93 chs = atk_object_ref_state_set (child);
95 // TODO: use atk-state_set_contains_states; would be more efficient
96 for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
98 if (!atk_state_set_contains_state (chs, set[i]))
105 g_object_unref (chs);
110 match_states_any_p (AtkObject * child, gint * set)
114 gboolean ret = FALSE;
116 if (set == NULL || set[0] == BITARRAY_SEQ_TERM)
119 chs = atk_object_ref_state_set (child);
121 for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
123 if (atk_state_set_contains_state (chs, set[i]))
130 g_object_unref (chs);
135 match_states_none_p (AtkObject * child, gint * set)
141 if (set == NULL || set[0] == BITARRAY_SEQ_TERM)
144 chs = atk_object_ref_state_set (child);
146 for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
148 if (atk_state_set_contains_state (chs, set[i]))
155 g_object_unref (chs);
159 // TODO: need to convert at-spi roles/states to atk roles/states */
161 match_states_lookup (AtkObject * child, MatchRulePrivate * mrp)
163 switch (mrp->statematchtype)
165 case ATSPI_Collection_MATCH_ALL:
166 if (match_states_all_p (child, mrp->states))
170 case ATSPI_Collection_MATCH_ANY:
171 if (match_states_any_p (child, mrp->states))
175 case ATSPI_Collection_MATCH_NONE:
176 if (match_states_none_p (child, mrp->states))
187 // TODO: Map at-spi -> atk roles at mrp creation instead of doing this;
188 // would be more efficient
189 #define spi_get_role(obj) spi_accessible_role_from_atk_role(atk_object_get_role(obj))
192 match_roles_all_p (AtkObject * child, gint * roles)
194 if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM)
196 else if (roles[1] != BITARRAY_SEQ_TERM)
199 return (atk_object_get_role (child) == roles[0]);
204 match_roles_any_p (AtkObject * child, gint * roles)
209 if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM)
212 role = spi_accessible_role_from_atk_role (atk_object_get_role (child));
214 for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++)
215 if (role == roles[i])
222 match_roles_none_p (AtkObject * child, gint * roles)
227 if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM)
230 role = atk_object_get_role (child);
232 for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++)
233 if (role == roles[i])
240 match_roles_lookup (AtkObject * child, MatchRulePrivate * mrp)
242 switch (mrp->rolematchtype)
244 case ATSPI_Collection_MATCH_ALL:
245 if (match_roles_all_p (child, mrp->roles))
249 case ATSPI_Collection_MATCH_ANY:
250 if (match_roles_any_p (child, mrp->roles))
254 case ATSPI_Collection_MATCH_NONE:
255 if (match_roles_none_p (child, mrp->roles))
267 match_interfaces_all_p (AtkObject * obj, gchar ** ifaces)
274 for (i = 0; ifaces[i]; i++)
275 if (!child_interface_p (obj, ifaces[i]))
283 match_interfaces_any_p (AtkObject * obj, gchar ** ifaces)
291 for (i = 0; ifaces[i]; i++)
292 if (child_interface_p (obj, ifaces[i]))
300 match_interfaces_none_p (AtkObject * obj, gchar ** ifaces)
304 for (i = 0; ifaces[i]; i++)
305 if (child_interface_p (obj, ifaces[i]))
312 match_interfaces_lookup (AtkObject * child, MatchRulePrivate * mrp)
314 switch (mrp->interfacematchtype)
317 case ATSPI_Collection_MATCH_ALL:
318 if (match_interfaces_all_p (child, mrp->ifaces))
322 case ATSPI_Collection_MATCH_ANY:
323 if (match_interfaces_any_p (child, mrp->ifaces))
327 case ATSPI_Collection_MATCH_NONE:
328 if (match_interfaces_none_p (child, mrp->ifaces))
339 #define split_attributes(attributes) (g_strsplit (attributes, ";", 0))
342 match_attributes_all_p (AtkObject * child, AtkAttributeSet * attributes)
346 gint length, oa_length;
347 gboolean flag = FALSE;
349 if (attributes == NULL || g_slist_length (attributes) == 0)
352 oa = atk_object_get_attributes (child);
353 length = g_slist_length (attributes);
354 oa_length = g_slist_length (oa);
356 for (i = 0; i < length; i++)
358 AtkAttribute *attr = g_slist_nth_data (attributes, i);
359 for (k = 0; k < oa_length; k++)
361 AtkAttribute *oa_attr = g_slist_nth_data (attributes, i);
362 if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
363 !g_ascii_strcasecmp (oa_attr->value, attr->value))
373 atk_attribute_set_free (oa);
377 atk_attribute_set_free (oa);
382 match_attributes_any_p (AtkObject * child, AtkAttributeSet * attributes)
387 gint length, oa_length;
389 length = g_slist_length (attributes);
393 oa = atk_object_get_attributes (child);
394 oa_length = g_slist_length (oa);
396 for (i = 0; i < length; i++)
398 AtkAttribute *attr = g_slist_nth_data (attributes, i);
399 for (k = 0; k < oa_length; k++)
401 AtkAttribute *oa_attr = g_slist_nth_data (oa, k);
402 if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
403 !g_ascii_strcasecmp (oa_attr->value, attr->value))
405 atk_attribute_set_free (oa);
410 atk_attribute_set_free (oa);
415 match_attributes_none_p (AtkObject * child, AtkAttributeSet * attributes)
420 gint length, oa_length;
422 length = g_slist_length (attributes);
426 oa = atk_object_get_attributes (child);
427 oa_length = g_slist_length (oa);
429 for (i = 0; i < length; i++)
431 AtkAttribute *attr = g_slist_nth_data (attributes, i);
432 for (k = 0; k < oa_length; k++)
434 AtkAttribute *oa_attr = g_slist_nth_data (attributes, i);
435 if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
436 !g_ascii_strcasecmp (oa_attr->value, attr->value))
438 atk_attribute_set_free (oa);
443 atk_attribute_set_free (oa);
448 match_attributes_lookup (AtkObject * child, MatchRulePrivate * mrp)
450 switch (mrp->attributematchtype)
453 case ATSPI_Collection_MATCH_ALL:
454 if (match_attributes_all_p (child, mrp->attributes))
458 case ATSPI_Collection_MATCH_ANY:
459 if (match_attributes_any_p (child, mrp->attributes))
463 case ATSPI_Collection_MATCH_NONE:
464 if (match_attributes_none_p (child, mrp->attributes))
475 traverse_p (AtkObject * child, const gboolean traverse)
480 return !child_collection_p (child);
484 sort_order_canonical (MatchRulePrivate * mrp, GList * ls,
485 gint kount, gint max,
486 AtkObject * obj, glong index, gboolean flag,
487 AtkObject * pobj, gboolean recurse, gboolean traverse)
490 glong acount = atk_object_get_n_accessible_children (obj);
491 gboolean prev = pobj ? TRUE : FALSE;
493 for (; i < acount && (max == 0 || kount < max); i++)
495 AtkObject *child = atk_object_ref_accessible_child (obj, i);
500 if (prev && child == pobj)
502 g_object_unref (child);
506 if (flag && match_interfaces_lookup (child, mrp)
507 && match_states_lookup (child, mrp)
508 && match_roles_lookup (child, mrp)
509 && match_attributes_lookup (child, mrp))
512 ls = g_list_append (ls, child);
519 if (recurse && traverse_p (child, traverse))
520 kount = sort_order_canonical (mrp, ls, kount,
522 pobj, recurse, traverse);
523 g_object_unref (child);
529 sort_order_rev_canonical (MatchRulePrivate * mrp, GList * ls,
530 gint kount, gint max,
531 AtkObject * obj, gboolean flag, AtkObject * pobj)
537 /* This breaks us out of the recursion. */
538 if (!obj || obj == pobj)
543 /* Add to the list if it matches */
544 if (flag && match_interfaces_lookup (obj, mrp)
545 && match_states_lookup (obj, mrp)
546 && match_roles_lookup (obj, mrp) && match_attributes_lookup (obj, mrp)
547 && (max == 0 || kount < max))
549 ls = g_list_append (ls, obj);
556 /* Get the current nodes index in it's parent and the parent object. */
557 indexinparent = atk_object_get_index_in_parent (obj);
558 parent = atk_object_get_parent (obj);
560 if (indexinparent > 0 && (max == 0 || kount < max))
562 /* there are still some siblings to visit so get the previous sibling
563 and get it's last descendant.
564 First, get the previous sibling */
565 nextobj = atk_object_ref_accessible_child (parent, indexinparent - 1);
567 /* Now, drill down the right side to the last descendant */
568 while (nextobj && atk_object_get_n_accessible_children (nextobj) > 0)
572 follow = atk_object_ref_accessible_child (nextobj,
573 atk_object_get_n_accessible_children
575 g_object_unref (nextobj);
578 /* recurse with the last descendant */
579 kount = sort_order_rev_canonical (mrp, ls, kount, max,
580 nextobj, TRUE, pobj);
582 g_object_unref (nextobj);
584 else if (max == 0 || kount < max)
586 /* no more siblings so next node must be the parent */
587 kount = sort_order_rev_canonical (mrp, ls, kount, max,
595 query_exec (MatchRulePrivate * mrp, AtspiCollectionSortOrder sortby,
596 GList * ls, gint kount, gint max,
597 AtkObject * obj, glong index,
599 AtkObject * pobj, gboolean recurse, gboolean traverse)
603 case ATSPI_Collection_SORT_ORDER_CANONICAL:
604 kount = sort_order_canonical (mrp, ls, 0, max, obj, index, flag,
605 pobj, recurse, traverse);
607 case ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL:
608 kount = sort_order_canonical (mrp, ls, 0, max, obj, index, flag,
609 pobj, recurse, traverse);
613 g_warning ("Sort method not implemented yet");
621 bitarray_to_seq (dbus_uint32_t *array, int array_count, int **ret)
626 int *out = (int *) g_malloc (out_size * sizeof (int));
630 for (i = 0; i < array_count; i++)
632 for (j = 0; j < 32; j++)
634 if (array[i] & (1 << j))
636 if (out_count == out_size - 2)
639 out = (int *) g_realloc (out, out_size * sizeof (int));
643 out[out_count++] = i * 32 + j;
647 out[out_count] = BITARRAY_SEQ_TERM;
654 read_mr (DBusMessageIter * iter, MatchRulePrivate * mrp)
656 DBusMessageIter iter_struct, iter_array, iter_dict, iter_dict_entry;
657 dbus_uint32_t *array;
658 dbus_int32_t matchType;
663 dbus_message_iter_recurse (iter, &iter_struct);
666 dbus_message_iter_recurse (&iter_struct, &iter_array);
667 dbus_message_iter_get_fixed_array (&iter_array, &array, &array_count);
668 bitarray_to_seq (array, array_count, &mrp->states);
669 for (i = 0; mrp->states[i] != BITARRAY_SEQ_TERM; i++)
671 mrp->states[i] = spi_atk_state_from_spi_state (mrp->states[i]);
673 dbus_message_iter_next (&iter_struct);
674 dbus_message_iter_get_basic (&iter_struct, &matchType);
675 dbus_message_iter_next (&iter_struct);
676 mrp->statematchtype = matchType;;
679 mrp->attributes = NULL;
680 dbus_message_iter_recurse (&iter_struct, &iter_dict);
681 while (dbus_message_iter_get_arg_type (&iter_dict) != DBUS_TYPE_INVALID)
683 const char *key, *val;
685 dbus_message_iter_recurse (&iter_dict, &iter_dict_entry);
686 dbus_message_iter_get_basic (&iter_dict_entry, &key);
687 dbus_message_iter_next (&iter_dict_entry);
688 dbus_message_iter_get_basic (&iter_dict_entry, &val);
692 if (*q == '\0' || (*q == ':' && (q == val || q[-1] != '\\')))
695 attr = g_new (AtkAttribute, 1);
696 attr->name = g_strdup (key);
697 attr->value = g_strdup (p);
698 attr->value[q - p] = '\0';
703 memmove (tmp, tmp + 1, strlen (tmp));
707 mrp->attributes = g_slist_prepend (mrp->attributes, attr);
716 dbus_message_iter_next (&iter_dict);
718 dbus_message_iter_next (&iter_struct);
719 dbus_message_iter_get_basic (&iter_struct, &matchType);
720 mrp->attributematchtype = matchType;;
721 dbus_message_iter_next (&iter_struct);
723 /* Get roles and role match */
724 dbus_message_iter_recurse (&iter_struct, &iter_array);
725 dbus_message_iter_get_fixed_array (&iter_array, &array, &array_count);
726 bitarray_to_seq (array, array_count, &mrp->roles);
727 dbus_message_iter_next (&iter_struct);
728 dbus_message_iter_get_basic (&iter_struct, &matchType);
729 mrp->rolematchtype = matchType;;
730 dbus_message_iter_next (&iter_struct);
732 /* Get interfaces and interface match */
733 dbus_message_iter_recurse (&iter_struct, &iter_array);
734 mrp->ifaces = g_new0 (gchar *, 16);
736 while (i < 15 && dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
739 dbus_message_iter_get_basic (&iter_array, &iface);
740 mrp->ifaces [i] = g_strdup (iface);
742 dbus_message_iter_next (&iter_array);
744 dbus_message_iter_next (&iter_struct);
745 dbus_message_iter_get_basic (&iter_struct, &matchType);
746 mrp->interfacematchtype = matchType;;
747 dbus_message_iter_next (&iter_struct);
749 dbus_message_iter_get_basic (&iter_struct, &mrp->invert);
750 dbus_message_iter_next (iter);
755 return_and_free_list (DBusMessage * message, GList * ls)
758 DBusMessageIter iter, iter_array;
761 reply = dbus_message_new_method_return (message);
764 dbus_message_iter_init_append (reply, &iter);
765 if (!dbus_message_iter_open_container
766 (&iter, DBUS_TYPE_ARRAY, "(so)", &iter_array))
768 for (item = ls; item; item = g_list_next (item))
770 spi_object_append_reference (&iter_array, ATK_OBJECT (item->data));
772 if (!dbus_message_iter_close_container (&iter, &iter_array))
777 // TODO: Handle out of memory
783 free_mrp_data (MatchRulePrivate * mrp)
785 g_free (mrp->states);
786 atk_attribute_set_free (mrp->attributes);
788 g_strfreev (mrp->ifaces);
792 GetMatchesFrom (DBusMessage * message,
793 AtkObject * current_object,
794 MatchRulePrivate * mrp,
795 const AtspiCollectionSortOrder sortby,
796 const dbus_bool_t isrestrict,
797 dbus_int32_t count, const dbus_bool_t traverse)
801 glong index = atk_object_get_index_in_parent (current_object);
803 ls = g_list_append (ls, current_object);
807 parent = atk_object_get_parent (current_object);
808 query_exec (mrp, sortby, ls, 0, count, parent, index,
809 FALSE, NULL, TRUE, traverse);
812 query_exec (mrp, sortby, ls, 0, count,
813 current_object, 0, FALSE, NULL, TRUE, traverse);
815 ls = g_list_remove (ls, ls->data);
817 if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
818 ls = g_list_reverse (ls);
821 return return_and_free_list (message, ls);
825 inorder traversal from a given object in the hierarchy
829 inorder (AtkObject * collection, MatchRulePrivate * mrp,
830 GList * ls, gint kount, gint max,
832 gboolean flag, AtkObject * pobj, dbus_bool_t traverse)
836 /* First, look through the children recursively. */
837 kount = sort_order_canonical (mrp, ls, kount, max, obj, 0, TRUE,
840 /* Next, we look through the right subtree */
841 while ((max == 0 || kount < max) && obj && obj != collection)
843 AtkObject *parent = atk_object_get_parent (obj);
844 i = atk_object_get_index_in_parent (obj);
845 kount = sort_order_canonical (mrp, ls, kount, max, parent,
846 i + 1, TRUE, FALSE, TRUE, TRUE);
850 if (max == 0 || kount < max)
852 kount = sort_order_canonical (mrp, ls, kount, max,
853 obj, i + 1, TRUE, FALSE, TRUE, TRUE);
860 GetMatchesInOrder: get matches from a given object in an inorder traversal.
864 GetMatchesInOrder (DBusMessage * message,
865 AtkObject * current_object,
866 MatchRulePrivate * mrp,
867 const AtspiCollectionSortOrder sortby,
868 const dbus_bool_t recurse,
869 dbus_int32_t count, const dbus_bool_t traverse)
874 ls = g_list_append (ls, current_object);
876 obj = ATK_OBJECT(spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
878 inorder (obj, mrp, ls, 0, count,
879 current_object, TRUE, NULL, traverse);
881 ls = g_list_remove (ls, ls->data);
883 if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
884 ls = g_list_reverse (ls);
887 return return_and_free_list (message, ls);
891 GetMatchesInBackOrder: get matches from a given object in a
892 reverse order traversal.
896 GetMatchesInBackOrder (DBusMessage * message,
897 AtkObject * current_object,
898 MatchRulePrivate * mrp,
899 const AtspiCollectionSortOrder sortby,
903 AtkObject *collection;
905 ls = g_list_append (ls, current_object);
907 collection = ATK_OBJECT(spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
909 sort_order_rev_canonical (mrp, ls, 0, count, current_object,
912 ls = g_list_remove (ls, ls->data);
914 if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
915 ls = g_list_reverse (ls);
918 return return_and_free_list (message, ls);
922 GetMatchesTo (DBusMessage * message,
923 AtkObject * current_object,
924 MatchRulePrivate * mrp,
925 const AtspiCollectionSortOrder sortby,
926 const dbus_bool_t recurse,
927 const dbus_bool_t isrestrict,
928 dbus_int32_t count, const dbus_bool_t traverse)
932 ls = g_list_append (ls, current_object);
936 obj = ATK_OBJECT (atk_object_get_parent (current_object));
937 query_exec (mrp, sortby, ls, 0, count,
938 obj, 0, TRUE, current_object, TRUE, traverse);
942 obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
943 query_exec (mrp, sortby, ls, 0, count,
944 obj, 0, TRUE, current_object, TRUE, traverse);
948 ls = g_list_remove (ls, ls->data);
950 if (sortby != ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
951 ls = g_list_reverse (ls);
954 return return_and_free_list (message, ls);
958 impl_GetMatchesFrom (DBusConnection * bus, DBusMessage * message,
961 char *current_object_path = NULL;
962 AtkObject *current_object;
963 DBusMessageIter iter;
964 MatchRulePrivate rule;
965 dbus_uint32_t sortby;
968 dbus_bool_t traverse;
969 const char *signature;
971 signature = dbus_message_get_signature (message);
972 if (strcmp (signature, "o(aiia{ss}iaiiasib)uuib") != 0)
974 return droute_invalid_arguments_error (message);
977 dbus_message_iter_init (message, &iter);
978 dbus_message_iter_get_basic (&iter, ¤t_object_path);
979 current_object = ATK_OBJECT (spi_register_path_to_object (spi_global_register, current_object_path));
982 // TODO: object-not-found error
983 return spi_dbus_general_error (message);
985 dbus_message_iter_next (&iter);
986 if (!read_mr (&iter, &rule))
988 return spi_dbus_general_error (message);
990 dbus_message_iter_get_basic (&iter, &sortby);
991 dbus_message_iter_next (&iter);
992 dbus_message_iter_get_basic (&iter, &tree);
993 dbus_message_iter_next (&iter);
994 dbus_message_iter_get_basic (&iter, &count);
995 dbus_message_iter_next (&iter);
996 dbus_message_iter_get_basic (&iter, &traverse);
997 dbus_message_iter_next (&iter);
1001 case ATSPI_Collection_TREE_RESTRICT_CHILDREN:
1002 return GetMatchesFrom (message, current_object,
1003 &rule, sortby, TRUE, count, traverse);
1005 case ATSPI_Collection_TREE_RESTRICT_SIBLING:
1006 return GetMatchesFrom (message, current_object,
1007 &rule, sortby, FALSE, count, traverse);
1009 case ATSPI_Collection_TREE_INORDER:
1010 return GetMatchesInOrder (message, current_object,
1011 &rule, sortby, TRUE, count, traverse);
1018 static DBusMessage *
1019 impl_GetMatchesTo (DBusConnection * bus, DBusMessage * message,
1022 char *current_object_path = NULL;
1023 AtkObject *current_object;
1024 DBusMessageIter iter;
1025 MatchRulePrivate rule;
1026 dbus_uint32_t sortby;
1028 dbus_bool_t recurse;
1030 dbus_bool_t traverse;
1031 const char *signature;
1033 signature = dbus_message_get_signature (message);
1034 if (strcmp (signature, "o(aiia{ss}iaiiasib)uubib") != 0)
1036 return droute_invalid_arguments_error (message);
1039 dbus_message_iter_init (message, &iter);
1040 dbus_message_iter_get_basic (&iter, ¤t_object_path);
1041 current_object = ATK_OBJECT (spi_register_path_to_object (spi_global_register, current_object_path));
1042 if (!current_object)
1044 // TODO: object-not-found error
1045 return spi_dbus_general_error (message);
1047 dbus_message_iter_next (&iter);
1048 if (!read_mr (&iter, &rule))
1050 return spi_dbus_general_error (message);
1052 dbus_message_iter_get_basic (&iter, &sortby);
1053 dbus_message_iter_next (&iter);
1054 dbus_message_iter_get_basic (&iter, &tree);
1055 dbus_message_iter_next (&iter);
1056 dbus_message_iter_get_basic (&iter, &recurse);
1057 dbus_message_iter_next (&iter);
1058 dbus_message_iter_get_basic (&iter, &count);
1059 dbus_message_iter_next (&iter);
1060 dbus_message_iter_get_basic (&iter, &traverse);
1061 dbus_message_iter_next (&iter);
1065 case ATSPI_Collection_TREE_RESTRICT_CHILDREN:
1066 return GetMatchesTo (message, current_object,
1067 &rule, sortby, recurse, TRUE, count, traverse);
1069 case ATSPI_Collection_TREE_RESTRICT_SIBLING:
1070 return GetMatchesTo (message, current_object,
1071 &rule, sortby, recurse, FALSE, count, traverse);
1073 case ATSPI_Collection_TREE_INORDER:
1074 return GetMatchesInBackOrder (message, current_object,
1075 &rule, sortby, count);
1083 append_accessible_properties (DBusMessageIter *iter, AtkObject *obj,
1086 DBusMessageIter iter_struct, iter_dict, iter_dict_entry;
1091 dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &iter_struct);
1092 spi_object_append_reference (&iter_struct, obj);
1093 dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_dict);
1094 if (properties && properties->len)
1097 for (i = 0; i < properties->len; i++)
1099 gchar *prop = g_array_index (properties, char *, i);
1100 DRoutePropertyFunction func;
1102 func = _atk_bridge_find_property_func (prop, &type);
1103 if (func && G_TYPE_CHECK_INSTANCE_TYPE (obj, type))
1105 dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY,
1106 NULL, &iter_dict_entry);
1107 dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop);
1108 func (&iter_dict_entry, obj);
1109 dbus_message_iter_close_container (&iter_dict, &iter_dict_entry);
1116 gpointer key, value;
1117 g_hash_table_iter_init (&hi, spi_global_app_data->property_hash);
1118 while (g_hash_table_iter_next (&hi, &key, &value))
1120 const DRouteProperty *prop = value;
1121 GType type = _atk_bridge_type_from_iface (key);
1122 if (!G_TYPE_CHECK_INSTANCE_TYPE (obj, type))
1124 for (;prop->name; prop++)
1126 const char *p = key + strlen (key);
1127 gchar *property_name;
1128 while (p[-1] != '.')
1130 if (!strcmp (p, "Accessible"))
1131 property_name = g_strdup (prop->name);
1133 property_name = g_strconcat (p, ".", prop->name, NULL);
1134 dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY,
1135 NULL, &iter_dict_entry);
1136 dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &property_name);
1137 g_free (property_name);
1138 prop->get (&iter_dict_entry, obj);
1139 dbus_message_iter_close_container (&iter_dict, &iter_dict_entry);
1143 dbus_message_iter_close_container (&iter_struct, &iter_dict);
1144 dbus_message_iter_close_container (iter, &iter_struct);
1146 set = atk_object_ref_state_set (obj);
1149 gboolean md = atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS);
1150 g_object_unref (set);
1154 count = atk_object_get_n_accessible_children (obj);
1155 for (i = 0; i < count; i++)
1157 AtkObject *child = atk_object_ref_accessible_child (obj, i);
1160 append_accessible_properties (iter, child, properties);
1161 g_object_unref (child);
1168 skip (const char **p)
1170 const char *sig = *p;
1171 gint nest = (*sig != 'a');
1176 if (*sig == '(' || *sig == '{')
1178 else if (*sig == ')' || *sig == '}')
1186 types_match (DBusMessageIter *iter, char c)
1188 char t = dbus_message_iter_get_arg_type (iter);
1190 if (t == 'r' && c == '(')
1197 walk (DBusMessageIter *iter, const char *sig, gboolean array)
1199 while (*sig && *sig != ')' && *sig != '}')
1201 if (array && dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_INVALID)
1203 if (!types_match (iter, *sig))
1205 g_error ("Expected %s, got %c", sig, dbus_message_iter_get_arg_type (iter));
1213 dbus_error_init (&error);
1214 dbus_message_iter_get_basic (iter, &str);
1215 g_print ("%s\n", str);
1216 if (!dbus_validate_utf8 (str, &error))
1217 g_error ("Bad UTF-8 string");
1222 DBusMessageIter iter_array;
1223 dbus_message_iter_recurse (iter, &iter_array);
1224 walk (&iter_array, sig + 1, TRUE);
1228 case DBUS_TYPE_STRUCT:
1229 case DBUS_TYPE_DICT_ENTRY:
1231 DBusMessageIter iter_struct;
1232 dbus_message_iter_recurse (iter, &iter_struct);
1233 walk (&iter_struct, sig + 1, FALSE);
1237 dbus_message_iter_next (iter);
1241 if (dbus_message_iter_get_arg_type (iter) != DBUS_TYPE_INVALID)
1242 g_error ("Unexpected data '%c'", dbus_message_iter_get_arg_type (iter));
1246 walkm (DBusMessage *message)
1248 DBusMessageIter iter;
1249 const char *sig = dbus_message_get_signature (message);
1251 g_print ("sig: %s\n", sig);
1252 dbus_message_iter_init (message, &iter);
1253 walk (&iter, sig, FALSE);
1257 static DBusMessage *
1258 impl_GetTree (DBusConnection * bus,
1259 DBusMessage * message, void *user_data)
1261 AtkObject *object = (AtkObject *) user_data;
1263 DBusMessageIter iter, iter_array;
1264 MatchRulePrivate rule;
1267 g_return_val_if_fail (ATK_IS_OBJECT (user_data),
1268 droute_not_yet_handled_error (message));
1270 if (strcmp (dbus_message_get_signature (message), "(aiia{ss}iaiiasib)as") != 0)
1271 return droute_invalid_arguments_error (message);
1273 properties = g_array_new (TRUE, TRUE, sizeof (char *));
1274 dbus_message_iter_init (message, &iter);
1275 if (!read_mr (&iter, &rule))
1277 return spi_dbus_general_error (message);
1280 dbus_message_iter_recurse (&iter, &iter_array);
1281 while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
1284 dbus_message_iter_get_basic (&iter_array, &prop);
1285 g_array_append_val (properties, prop);
1286 dbus_message_iter_next (&iter_array);
1289 reply = dbus_message_new_method_return (message);
1292 dbus_message_iter_init_append (reply, &iter);
1293 dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "((so)a{sv})",
1295 append_accessible_properties (&iter_array, object, properties);
1296 dbus_message_iter_close_container (&iter, &iter_array);
1304 static DBusMessage *
1305 impl_GetMatches (DBusConnection * bus, DBusMessage * message, void *user_data)
1307 AtkObject *obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
1308 DBusMessageIter iter;
1309 MatchRulePrivate rule;
1310 dbus_uint32_t sortby;
1312 dbus_bool_t traverse;
1314 const char *signature;
1316 signature = dbus_message_get_signature (message);
1317 if (strcmp (signature, "(aiia{ss}iaiiasib)uib") != 0)
1319 return droute_invalid_arguments_error (message);
1322 dbus_message_iter_init (message, &iter);
1323 if (!read_mr (&iter, &rule))
1325 return spi_dbus_general_error (message);
1327 dbus_message_iter_get_basic (&iter, &sortby);
1328 dbus_message_iter_next (&iter);
1329 dbus_message_iter_get_basic (&iter, &count);
1330 dbus_message_iter_next (&iter);
1331 dbus_message_iter_get_basic (&iter, &traverse);
1332 dbus_message_iter_next (&iter);
1333 ls = g_list_prepend (ls, obj);
1334 count = query_exec (&rule, sortby, ls, 0, count,
1335 obj, 0, TRUE, NULL, TRUE, traverse);
1336 ls = g_list_remove (ls, ls->data);
1338 if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
1339 ls = g_list_reverse (ls);
1340 free_mrp_data (&rule);
1341 return return_and_free_list (message, ls);
1344 static DRouteMethod methods[] = {
1345 {impl_GetMatchesFrom, "GetMatchesFrom"},
1346 {impl_GetMatchesTo, "GetMatchesTo"},
1347 {impl_GetTree, "GetTree"},
1348 {impl_GetMatches, "GetMatches"},
1353 spi_initialize_collection (DRoutePath * path)
1355 spi_atk_add_interface (path,
1356 ATSPI_DBUS_INTERFACE_COLLECTION, spi_org_a11y_atspi_Collection, methods, NULL);