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 Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 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 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, 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);
497 g_object_unref (child);
498 if (prev && child == pobj)
503 if (flag && match_interfaces_lookup (child, mrp)
504 && match_states_lookup (child, mrp)
505 && match_roles_lookup (child, mrp)
506 && match_attributes_lookup (child, mrp))
509 ls = g_list_append (ls, child);
516 if (recurse && traverse_p (child, traverse))
517 kount = sort_order_canonical (mrp, ls, kount,
519 pobj, recurse, traverse);
525 sort_order_rev_canonical (MatchRulePrivate * mrp, GList * ls,
526 gint kount, gint max,
527 AtkObject * obj, gboolean flag, AtkObject * pobj)
533 /* This breaks us out of the recursion. */
534 if (!obj || obj == pobj)
539 /* Add to the list if it matches */
540 if (flag && match_interfaces_lookup (obj, mrp)
541 && match_states_lookup (obj, mrp)
542 && match_roles_lookup (obj, mrp) && match_attributes_lookup (obj, mrp)
543 && (max == 0 || kount < max))
545 ls = g_list_append (ls, obj);
552 /* Get the current nodes index in it's parent and the parent object. */
553 indexinparent = atk_object_get_index_in_parent (obj);
554 parent = atk_object_get_parent (obj);
556 if (indexinparent > 0 && (max == 0 || kount < max))
558 /* there are still some siblings to visit so get the previous sibling
559 and get it's last descendant.
560 First, get the previous sibling */
561 nextobj = atk_object_ref_accessible_child (parent, indexinparent - 1);
562 g_object_unref (nextobj);
564 /* Now, drill down the right side to the last descendant */
565 while (atk_object_get_n_accessible_children (nextobj) > 0)
567 nextobj = atk_object_ref_accessible_child (nextobj,
568 atk_object_get_n_accessible_children
570 g_object_unref (nextobj);
572 /* recurse with the last descendant */
573 kount = sort_order_rev_canonical (mrp, ls, kount, max,
574 nextobj, TRUE, pobj);
576 else if (max == 0 || kount < max)
578 /* no more siblings so next node must be the parent */
579 kount = sort_order_rev_canonical (mrp, ls, kount, max,
587 query_exec (MatchRulePrivate * mrp, AtspiCollectionSortOrder sortby,
588 GList * ls, gint kount, gint max,
589 AtkObject * obj, glong index,
591 AtkObject * pobj, gboolean recurse, gboolean traverse)
595 case ATSPI_Collection_SORT_ORDER_CANONICAL:
596 kount = sort_order_canonical (mrp, ls, 0, max, obj, index, flag,
597 pobj, recurse, traverse);
599 case ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL:
600 kount = sort_order_canonical (mrp, ls, 0, max, obj, index, flag,
601 pobj, recurse, traverse);
605 g_warning ("Sort method not implemented yet");
613 bitarray_to_seq (dbus_uint32_t *array, int array_count, int **ret)
618 int *out = (int *) g_malloc (out_size * sizeof (int));
622 for (i = 0; i < array_count; i++)
624 for (j = 0; j < 32; j++)
626 if (array[i] & (1 << j))
628 if (out_count == out_size - 2)
631 out = (int *) g_realloc (out, out_size * sizeof (int));
635 out[out_count++] = i * 32 + j;
639 out[out_count] = BITARRAY_SEQ_TERM;
646 read_mr (DBusMessageIter * iter, MatchRulePrivate * mrp)
648 DBusMessageIter iter_struct, iter_array, iter_dict, iter_dict_entry;
649 dbus_uint32_t *array;
650 dbus_int32_t matchType;
655 dbus_message_iter_recurse (iter, &iter_struct);
658 dbus_message_iter_recurse (&iter_struct, &iter_array);
659 dbus_message_iter_get_fixed_array (&iter_array, &array, &array_count);
660 bitarray_to_seq (array, array_count, &mrp->states);
661 for (i = 0; mrp->states[i] != BITARRAY_SEQ_TERM; i++)
663 mrp->states[i] = spi_atk_state_from_spi_state (mrp->states[i]);
665 dbus_message_iter_next (&iter_struct);
666 dbus_message_iter_get_basic (&iter_struct, &matchType);
667 dbus_message_iter_next (&iter_struct);
668 mrp->statematchtype = matchType;;
671 mrp->attributes = NULL;
672 dbus_message_iter_recurse (&iter_struct, &iter_dict);
673 while (dbus_message_iter_get_arg_type (&iter_dict) != DBUS_TYPE_INVALID)
675 const char *key, *val;
677 dbus_message_iter_recurse (&iter_dict, &iter_dict_entry);
678 dbus_message_iter_get_basic (&iter_dict_entry, &key);
679 dbus_message_iter_next (&iter_dict_entry);
680 dbus_message_iter_get_basic (&iter_dict_entry, &val);
684 if (*q == '\0' || (*q == ':' && (q == val || q[-1] != '\\')))
687 attr = g_new (AtkAttribute, 1);
688 attr->name = g_strdup (key);
689 attr->value = g_strdup (p);
690 attr->value[q - p] = '\0';
695 memmove (tmp, tmp + 1, strlen (tmp));
699 mrp->attributes = g_slist_prepend (mrp->attributes, attr);
708 dbus_message_iter_next (&iter_dict);
710 dbus_message_iter_next (&iter_struct);
711 dbus_message_iter_get_basic (&iter_struct, &matchType);
712 mrp->attributematchtype = matchType;;
713 dbus_message_iter_next (&iter_struct);
715 /* Get roles and role match */
716 dbus_message_iter_recurse (&iter_struct, &iter_array);
717 dbus_message_iter_get_fixed_array (&iter_array, &array, &array_count);
718 bitarray_to_seq (array, array_count, &mrp->roles);
719 dbus_message_iter_next (&iter_struct);
720 dbus_message_iter_get_basic (&iter_struct, &matchType);
721 mrp->rolematchtype = matchType;;
722 dbus_message_iter_next (&iter_struct);
724 /* Get interfaces and interface match */
725 dbus_message_iter_recurse (&iter_struct, &iter_array);
726 mrp->ifaces = g_new0 (gchar *, 16);
728 while (i < 15 && dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
731 dbus_message_iter_get_basic (&iter_array, &iface);
732 mrp->ifaces [i] = g_strdup (iface);
734 dbus_message_iter_next (&iter_array);
736 dbus_message_iter_next (&iter_struct);
737 dbus_message_iter_get_basic (&iter_struct, &matchType);
738 mrp->interfacematchtype = matchType;;
739 dbus_message_iter_next (&iter_struct);
741 dbus_message_iter_get_basic (&iter_struct, &mrp->invert);
742 dbus_message_iter_next (iter);
747 return_and_free_list (DBusMessage * message, GList * ls)
750 DBusMessageIter iter, iter_array;
753 reply = dbus_message_new_method_return (message);
756 dbus_message_iter_init_append (reply, &iter);
757 if (!dbus_message_iter_open_container
758 (&iter, DBUS_TYPE_ARRAY, "(so)", &iter_array))
760 for (item = ls; item; item = g_list_next (item))
762 spi_object_append_reference (&iter_array, ATK_OBJECT (item->data));
764 if (!dbus_message_iter_close_container (&iter, &iter_array))
769 // TODO: Handle out of memory
775 free_mrp_data (MatchRulePrivate * mrp)
777 g_free (mrp->states);
778 atk_attribute_set_free (mrp->attributes);
780 g_strfreev (mrp->ifaces);
784 GetMatchesFrom (DBusMessage * message,
785 AtkObject * current_object,
786 MatchRulePrivate * mrp,
787 const AtspiCollectionSortOrder sortby,
788 const dbus_bool_t isrestrict,
789 dbus_int32_t count, const dbus_bool_t traverse)
793 glong index = atk_object_get_index_in_parent (current_object);
795 ls = g_list_append (ls, current_object);
799 parent = atk_object_get_parent (current_object);
800 query_exec (mrp, sortby, ls, 0, count, parent, index,
801 FALSE, NULL, TRUE, traverse);
804 query_exec (mrp, sortby, ls, 0, count,
805 current_object, 0, FALSE, NULL, TRUE, traverse);
807 ls = g_list_remove (ls, ls->data);
809 if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
810 ls = g_list_reverse (ls);
813 return return_and_free_list (message, ls);
817 inorder traversal from a given object in the hierarchy
821 inorder (AtkObject * collection, MatchRulePrivate * mrp,
822 GList * ls, gint kount, gint max,
824 gboolean flag, AtkObject * pobj, dbus_bool_t traverse)
828 /* First, look through the children recursively. */
829 kount = sort_order_canonical (mrp, ls, kount, max, obj, 0, TRUE,
832 /* Next, we look through the right subtree */
833 while ((max == 0 || kount < max) && obj != collection)
835 AtkObject *parent = atk_object_get_parent (obj);
836 i = atk_object_get_index_in_parent (obj);
837 kount = sort_order_canonical (mrp, ls, kount, max, parent,
838 i + 1, TRUE, FALSE, TRUE, TRUE);
842 if (max == 0 || kount < max)
844 kount = sort_order_canonical (mrp, ls, kount, max,
845 obj, i + 1, TRUE, FALSE, TRUE, TRUE);
852 GetMatchesInOrder: get matches from a given object in an inorder traversal.
856 GetMatchesInOrder (DBusMessage * message,
857 AtkObject * current_object,
858 MatchRulePrivate * mrp,
859 const AtspiCollectionSortOrder sortby,
860 const dbus_bool_t recurse,
861 dbus_int32_t count, const dbus_bool_t traverse)
866 ls = g_list_append (ls, current_object);
868 obj = ATK_OBJECT(spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
870 inorder (obj, mrp, ls, 0, count,
871 current_object, TRUE, NULL, traverse);
873 ls = g_list_remove (ls, ls->data);
875 if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
876 ls = g_list_reverse (ls);
879 return return_and_free_list (message, ls);
883 GetMatchesInBackOrder: get matches from a given object in a
884 reverse order traversal.
888 GetMatchesInBackOrder (DBusMessage * message,
889 AtkObject * current_object,
890 MatchRulePrivate * mrp,
891 const AtspiCollectionSortOrder sortby,
895 AtkObject *collection;
897 ls = g_list_append (ls, current_object);
899 collection = ATK_OBJECT(spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
901 sort_order_rev_canonical (mrp, ls, 0, count, current_object,
904 ls = g_list_remove (ls, ls->data);
906 if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
907 ls = g_list_reverse (ls);
910 return return_and_free_list (message, ls);
914 GetMatchesTo (DBusMessage * message,
915 AtkObject * current_object,
916 MatchRulePrivate * mrp,
917 const AtspiCollectionSortOrder sortby,
918 const dbus_bool_t recurse,
919 const dbus_bool_t isrestrict,
920 dbus_int32_t count, const dbus_bool_t traverse)
924 ls = g_list_append (ls, current_object);
928 obj = ATK_OBJECT (atk_object_get_parent (current_object));
929 query_exec (mrp, sortby, ls, 0, count,
930 obj, 0, TRUE, current_object, TRUE, traverse);
934 obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
935 query_exec (mrp, sortby, ls, 0, count,
936 obj, 0, TRUE, current_object, TRUE, traverse);
940 ls = g_list_remove (ls, ls->data);
942 if (sortby != ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
943 ls = g_list_reverse (ls);
946 return return_and_free_list (message, ls);
950 impl_GetMatchesFrom (DBusConnection * bus, DBusMessage * message,
953 char *current_object_path = NULL;
954 AtkObject *current_object;
955 DBusMessageIter iter;
956 MatchRulePrivate rule;
957 dbus_uint32_t sortby;
960 dbus_bool_t traverse;
961 const char *signature;
963 signature = dbus_message_get_signature (message);
964 if (strcmp (signature, "o(aiia{ss}iaiiasib)uuib") != 0)
966 return droute_invalid_arguments_error (message);
969 dbus_message_iter_init (message, &iter);
970 dbus_message_iter_get_basic (&iter, ¤t_object_path);
971 current_object = ATK_OBJECT (spi_register_path_to_object (spi_global_register, current_object_path));
974 // TODO: object-not-found error
975 return spi_dbus_general_error (message);
977 dbus_message_iter_next (&iter);
978 if (!read_mr (&iter, &rule))
980 return spi_dbus_general_error (message);
982 dbus_message_iter_get_basic (&iter, &sortby);
983 dbus_message_iter_next (&iter);
984 dbus_message_iter_get_basic (&iter, &tree);
985 dbus_message_iter_next (&iter);
986 dbus_message_iter_get_basic (&iter, &count);
987 dbus_message_iter_next (&iter);
988 dbus_message_iter_get_basic (&iter, &traverse);
989 dbus_message_iter_next (&iter);
993 case ATSPI_Collection_TREE_RESTRICT_CHILDREN:
994 return GetMatchesFrom (message, current_object,
995 &rule, sortby, TRUE, count, traverse);
997 case ATSPI_Collection_TREE_RESTRICT_SIBLING:
998 return GetMatchesFrom (message, current_object,
999 &rule, sortby, FALSE, count, traverse);
1001 case ATSPI_Collection_TREE_INORDER:
1002 return GetMatchesInOrder (message, current_object,
1003 &rule, sortby, TRUE, count, traverse);
1010 static DBusMessage *
1011 impl_GetMatchesTo (DBusConnection * bus, DBusMessage * message,
1014 char *current_object_path = NULL;
1015 AtkObject *current_object;
1016 DBusMessageIter iter;
1017 MatchRulePrivate rule;
1018 dbus_uint32_t sortby;
1020 dbus_bool_t recurse;
1022 dbus_bool_t traverse;
1023 const char *signature;
1025 signature = dbus_message_get_signature (message);
1026 if (strcmp (signature, "o(aiia{ss}iaiiasib)uubib") != 0)
1028 return droute_invalid_arguments_error (message);
1031 dbus_message_iter_init (message, &iter);
1032 dbus_message_iter_get_basic (&iter, ¤t_object_path);
1033 current_object = ATK_OBJECT (spi_register_path_to_object (spi_global_register, current_object_path));
1034 if (!current_object)
1036 // TODO: object-not-found error
1037 return spi_dbus_general_error (message);
1039 dbus_message_iter_next (&iter);
1040 if (!read_mr (&iter, &rule))
1042 return spi_dbus_general_error (message);
1044 dbus_message_iter_get_basic (&iter, &sortby);
1045 dbus_message_iter_next (&iter);
1046 dbus_message_iter_get_basic (&iter, &tree);
1047 dbus_message_iter_next (&iter);
1048 dbus_message_iter_get_basic (&iter, &recurse);
1049 dbus_message_iter_next (&iter);
1050 dbus_message_iter_get_basic (&iter, &count);
1051 dbus_message_iter_next (&iter);
1052 dbus_message_iter_get_basic (&iter, &traverse);
1053 dbus_message_iter_next (&iter);
1057 case ATSPI_Collection_TREE_RESTRICT_CHILDREN:
1058 return GetMatchesTo (message, current_object,
1059 &rule, sortby, recurse, TRUE, count, traverse);
1061 case ATSPI_Collection_TREE_RESTRICT_SIBLING:
1062 return GetMatchesTo (message, current_object,
1063 &rule, sortby, recurse, FALSE, count, traverse);
1065 case ATSPI_Collection_TREE_INORDER:
1066 return GetMatchesInBackOrder (message, current_object,
1067 &rule, sortby, count);
1075 append_accessible_properties (DBusMessageIter *iter, AtkObject *obj,
1078 DBusMessageIter iter_struct, iter_dict, iter_dict_entry;
1083 dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &iter_struct);
1084 spi_object_append_reference (&iter_struct, obj);
1085 dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_dict);
1086 if (properties && properties->len)
1089 for (i = 0; i < properties->len; i++)
1091 gchar *prop = g_array_index (properties, char *, i);
1092 DRoutePropertyFunction func;
1094 func = _atk_bridge_find_property_func (prop, &type);
1095 if (func && G_TYPE_CHECK_INSTANCE_TYPE (obj, type))
1097 dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY,
1098 NULL, &iter_dict_entry);
1099 dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop);
1100 func (&iter_dict_entry, obj);
1101 dbus_message_iter_close_container (&iter_dict, &iter_dict_entry);
1108 gpointer key, value;
1109 g_hash_table_iter_init (&hi, spi_global_app_data->property_hash);
1110 while (g_hash_table_iter_next (&hi, &key, &value))
1112 const DRouteProperty *prop = value;
1113 GType type = _atk_bridge_type_from_iface (key);
1114 if (!G_TYPE_CHECK_INSTANCE_TYPE (obj, type))
1116 for (;prop->name; prop++)
1118 const char *p = key + strlen (key);
1119 gchar *property_name;
1120 while (p[-1] != '.')
1122 if (!strcmp (p, "Accessible"))
1123 property_name = g_strdup (prop->name);
1125 property_name = g_strconcat (p, ".", prop->name, NULL);
1126 dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY,
1127 NULL, &iter_dict_entry);
1128 dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &property_name);
1129 g_free (property_name);
1130 prop->get (&iter_dict_entry, obj);
1131 dbus_message_iter_close_container (&iter_dict, &iter_dict_entry);
1135 dbus_message_iter_close_container (&iter_struct, &iter_dict);
1136 dbus_message_iter_close_container (iter, &iter_struct);
1138 set = atk_object_ref_state_set (obj);
1141 gboolean md = atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS);
1142 g_object_unref (set);
1146 count = atk_object_get_n_accessible_children (obj);
1147 for (i = 0; i < count; i++)
1149 AtkObject *child = atk_object_ref_accessible_child (obj, i);
1152 append_accessible_properties (iter, child, properties);
1153 g_object_unref (child);
1159 skip (const char **p)
1161 const char *sig = *p;
1162 gint nest = (*sig != 'a');
1167 if (*sig == '(' || *sig == '{')
1169 else if (*sig == ')' || *sig == '}')
1177 types_match (DBusMessageIter *iter, char c)
1179 char t = dbus_message_iter_get_arg_type (iter);
1181 if (t == 'r' && c == '(')
1188 walk (DBusMessageIter *iter, const char *sig, gboolean array)
1190 while (*sig && *sig != ')' && *sig != '}')
1192 if (array && dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_INVALID)
1194 if (!types_match (iter, *sig))
1196 g_error ("Expected %s, got %c", sig, dbus_message_iter_get_arg_type (iter));
1204 dbus_error_init (&error);
1205 dbus_message_iter_get_basic (iter, &str);
1206 g_print ("%s\n", str);
1207 if (!dbus_validate_utf8 (str, &error))
1208 g_error ("Bad UTF-8 string");
1213 DBusMessageIter iter_array;
1214 dbus_message_iter_recurse (iter, &iter_array);
1215 walk (&iter_array, sig + 1, TRUE);
1219 case DBUS_TYPE_STRUCT:
1220 case DBUS_TYPE_DICT_ENTRY:
1222 DBusMessageIter iter_struct;
1223 dbus_message_iter_recurse (iter, &iter_struct);
1224 walk (&iter_struct, sig + 1, FALSE);
1228 dbus_message_iter_next (iter);
1232 if (dbus_message_iter_get_arg_type (iter) != DBUS_TYPE_INVALID)
1233 g_error ("Unexpected data '%c'", dbus_message_iter_get_arg_type (iter));
1237 walkm (DBusMessage *message)
1239 DBusMessageIter iter;
1240 const char *sig = dbus_message_get_signature (message);
1242 g_print ("sig: %s\n", sig);
1243 dbus_message_iter_init (message, &iter);
1244 walk (&iter, sig, FALSE);
1247 static DBusMessage *
1248 impl_GetTree (DBusConnection * bus,
1249 DBusMessage * message, void *user_data)
1251 AtkObject *object = (AtkObject *) user_data;
1253 DBusMessageIter iter, iter_array;
1254 MatchRulePrivate rule;
1257 g_return_val_if_fail (ATK_IS_OBJECT (user_data),
1258 droute_not_yet_handled_error (message));
1260 if (strcmp (dbus_message_get_signature (message), "(aiia{ss}iaiiasib)as") != 0)
1261 return droute_invalid_arguments_error (message);
1263 properties = g_array_new (TRUE, TRUE, sizeof (char *));
1264 dbus_message_iter_init (message, &iter);
1265 if (!read_mr (&iter, &rule))
1267 return spi_dbus_general_error (message);
1270 dbus_message_iter_recurse (&iter, &iter_array);
1271 while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
1274 dbus_message_iter_get_basic (&iter_array, &prop);
1275 g_array_append_val (properties, prop);
1276 dbus_message_iter_next (&iter_array);
1279 reply = dbus_message_new_method_return (message);
1282 dbus_message_iter_init_append (reply, &iter);
1283 dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "((so)a{sv})",
1285 append_accessible_properties (&iter_array, object, properties);
1286 dbus_message_iter_close_container (&iter, &iter_array);
1292 static DBusMessage *
1293 impl_GetMatches (DBusConnection * bus, DBusMessage * message, void *user_data)
1295 AtkObject *obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
1296 DBusMessageIter iter;
1297 MatchRulePrivate rule;
1298 dbus_uint32_t sortby;
1300 dbus_bool_t traverse;
1302 const char *signature;
1304 signature = dbus_message_get_signature (message);
1305 if (strcmp (signature, "(aiia{ss}iaiiasib)uib") != 0)
1307 return droute_invalid_arguments_error (message);
1310 dbus_message_iter_init (message, &iter);
1311 if (!read_mr (&iter, &rule))
1313 return spi_dbus_general_error (message);
1315 dbus_message_iter_get_basic (&iter, &sortby);
1316 dbus_message_iter_next (&iter);
1317 dbus_message_iter_get_basic (&iter, &count);
1318 dbus_message_iter_next (&iter);
1319 dbus_message_iter_get_basic (&iter, &traverse);
1320 dbus_message_iter_next (&iter);
1321 ls = g_list_prepend (ls, obj);
1322 count = query_exec (&rule, sortby, ls, 0, count,
1323 obj, 0, TRUE, NULL, TRUE, traverse);
1324 ls = g_list_remove (ls, ls->data);
1326 if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
1327 ls = g_list_reverse (ls);
1328 free_mrp_data (&rule);
1329 return return_and_free_list (message, ls);
1332 static DRouteMethod methods[] = {
1333 {impl_GetMatchesFrom, "GetMatchesFrom"},
1334 {impl_GetMatchesTo, "GetMatchesTo"},
1335 {impl_GetTree, "GetTree"},
1336 {impl_GetMatches, "GetMatches"},
1341 spi_initialize_collection (DRoutePath * path)
1343 spi_atk_add_interface (path,
1344 ATSPI_DBUS_INTERFACE_COLLECTION, spi_org_a11y_atspi_Collection, methods, NULL);