2009-01-09 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/upstream/at-spi2-core.git] / atk-adaptor / collection.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2007 IBM Corp.
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 /* collection.c: implements the Collection interface */
24
25 #include <string.h>
26
27 #include <atk/atk.h>
28 #include <droute/droute.h>
29
30 #include "atk-dbus.h"
31
32 #include "spi-common/bitarray.h"
33 #include "spi-common/spi-dbus.h"
34
35 typedef struct _MatchRulePrivate MatchRulePrivate;
36 struct _MatchRulePrivate
37 {
38   gint *states;
39      Accessibility_Collection_MatchType statematchtype;
40   AtkAttributeSet *attributes;
41      Accessibility_Collection_MatchType attributematchtype;
42   gint *roles;
43      Accessibility_Collection_MatchType rolematchtype;
44      gchar **ifaces;
45      Accessibility_Collection_MatchType interfacematchtype;
46      gboolean invert;
47 };
48
49 static gboolean
50 child_interface_p (AtkObject *child, 
51                    gchar *repo_id)
52 {
53   if (!strcasecmp(repo_id, "action")) return atk_is_action(child);
54   if (!strcasecmp(repo_id, "component")) return atk_is_component(child);
55   if (!strcasecmp(repo_id, "editabletext")) return atk_is_editable_text(child);
56   if (!strcasecmp(repo_id, "text")) return atk_is_text(child);
57   if (!strcasecmp(repo_id, "hypertext")) return atk_is_hypertext(child);
58   if (!strcasecmp(repo_id, "image")) return atk_is_image(child);
59   if (!strcasecmp(repo_id, "selection")) return atk_is_selection(child);
60   if (!strcasecmp(repo_id, "table")) return atk_is_table(child);
61   if (!strcasecmp(repo_id, "value")) return atk_is_value(child);
62   if (!strcasecmp(repo_id, "streamablecontent")) return atk_is_streamable_content(child);
63   if (!strcasecmp(repo_id, "document")) return atk_is_document(child);
64   return FALSE;
65 }
66
67 #define child_collection_p(ch) (TRUE)
68
69 static gboolean
70 match_states_all_p (AtkObject *child, 
71                     gint *set)
72 {
73      AtkStateSet *chs;
74      gint i;
75      gboolean ret = TRUE;
76
77      if (set == NULL)
78           return TRUE;
79
80      chs = atk_object_ref_state_set (child);
81
82      // TODO: use atk-state_set_contains_states; would be more efficient
83      for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
84      {
85           if (!atk_state_set_contains_state(chs, set[i]))
86           {
87                ret = FALSE;
88                break;
89           }
90      }
91    
92      g_object_unref(chs);
93      return ret;
94 }
95
96 static gboolean
97 match_states_any_p  (AtkObject *child, 
98                      gint *set)
99 {
100      AtkStateSet *chs;
101      gint i;
102      gboolean ret = FALSE;
103
104      if (set == NULL)
105           return TRUE;
106
107      chs = atk_object_ref_state_set (child);
108
109      for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
110      {
111           if (!atk_state_set_contains_state(chs, set[i]))
112           {
113                ret = TRUE;
114                break;
115           }
116      }
117    
118      g_object_unref(chs);
119      return ret;
120 }
121
122 static gboolean
123 match_states_none_p  (AtkObject *child, 
124                      gint *set)
125 {
126      AtkStateSet *chs;
127      gint i;
128      gboolean ret = TRUE;
129
130      if (set == NULL)
131           return TRUE;
132
133      chs = atk_object_ref_state_set (child);
134
135      for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
136      {
137           if (atk_state_set_contains_state(chs, set[i]))
138           {
139                ret = FALSE;
140                break;
141           }
142      }
143    
144      g_object_unref(chs);
145      return ret;
146 }
147
148 // TODO: need to convert at-spi roles/states to atk roles/states */
149 static gboolean
150 match_states_lookup (AtkObject *child,  
151                      MatchRulePrivate *mrp)
152 {
153      switch (mrp->statematchtype){
154      case Accessibility_Collection_MATCH_ALL : 
155           if (match_states_all_p (child, mrp->states))
156                return TRUE;
157           break;
158           
159      case  Accessibility_Collection_MATCH_ANY :
160           if (match_states_any_p (child, mrp->states))
161                return TRUE;
162           break;
163           
164      case  Accessibility_Collection_MATCH_NONE :
165           if (match_states_none_p (child, mrp->states))
166                return TRUE;
167           break;
168
169       default : break;    
170      }
171
172      return FALSE;    
173 }
174
175 // TODO: Map at-spi -> atk roles at mrp creation instead of doing this;
176 // would be more efficient
177 #define spi_get_role(obj) spi_accessible_role_from_atk_role(atk_object_get_role(obj))
178
179 static gboolean
180 match_roles_all_p (AtkObject *child, 
181                    gint *roles)
182 {
183    Accessibility_Role role; 
184
185      if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM) return TRUE;
186      else if (roles[1] != BITARRAY_SEQ_TERM) return FALSE;
187
188      return (atk_object_get_role(child) == roles[0]);
189     
190 }
191
192 static gboolean
193 match_roles_any_p (AtkObject *child, 
194                    gint *roles)
195 {
196      AtkRole role;
197      int i;
198
199      if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM) return TRUE;
200
201      role = atk_object_get_role(child);
202
203      for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++)
204           if (role  == roles[i])
205                return TRUE;
206
207      return FALSE;
208 }
209
210 static gboolean
211 match_roles_none_p (AtkObject *child, 
212                     gint *roles)
213 {
214   AtkRole role;
215      int i;
216
217      if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM) return TRUE;
218
219      role = atk_object_get_role(child);
220
221      for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++)
222           if (role == roles[i])
223                return FALSE;
224
225      return TRUE;
226 }
227
228 static gboolean
229 match_roles_lookup (AtkObject *child,  
230                     MatchRulePrivate *mrp)
231 {
232       switch (mrp->rolematchtype){
233          case Accessibility_Collection_MATCH_ALL : 
234               if (match_roles_all_p (child, mrp->roles))
235                    return TRUE;
236               break;
237
238          case  Accessibility_Collection_MATCH_ANY :
239               if (match_roles_any_p (child, mrp->roles))
240                    return TRUE;
241               break;
242
243          case  Accessibility_Collection_MATCH_NONE :
244               if (match_roles_none_p (child, mrp->roles))
245                    return TRUE;
246               break;
247
248       default : break;
249  
250          }
251       return FALSE;
252 }
253
254 static gboolean
255 match_interfaces_all_p (AtkObject *obj, 
256                         gchar **ifaces)
257 {
258      gint i;
259
260      if (ifaces == NULL)
261        return TRUE;
262
263      for (i = 0; ifaces[i]; i++)
264        if (!child_interface_p (obj, ifaces [i])){
265                return FALSE;
266        }
267      return TRUE;
268 }
269
270 static gboolean
271 match_interfaces_any_p (AtkObject *obj, 
272                         gchar **ifaces)
273 {
274      gint i;
275
276      if (ifaces == NULL)
277        return TRUE;
278
279
280      for (i = 0; ifaces[i]; i++)
281        if (child_interface_p (obj, ifaces [i])){
282                 return TRUE;
283        }
284      return FALSE;
285 }
286
287 static gboolean
288 match_interfaces_none_p (AtkObject *obj, 
289                          gchar **ifaces)
290 {
291      gint i;
292
293      for (i = 0; ifaces[i]; i++)
294            if (child_interface_p (obj, ifaces [i]))
295                 return FALSE;
296      
297      return TRUE;
298 }
299
300 static gboolean
301 match_interfaces_lookup (AtkObject *child, 
302                          MatchRulePrivate *mrp)
303 {
304      switch (mrp->interfacematchtype){
305
306      case Accessibility_Collection_MATCH_ALL : 
307           if (match_interfaces_all_p (child, mrp->ifaces))
308                return TRUE;
309           break;
310
311      case  Accessibility_Collection_MATCH_ANY :
312           if (match_interfaces_any_p (child, mrp->ifaces))
313                return TRUE;
314           break;
315
316      case  Accessibility_Collection_MATCH_NONE :
317           if (match_interfaces_none_p (child, mrp->ifaces))
318                return TRUE;
319           break;
320
321       default : break;    
322      }
323
324      return FALSE;     
325 }
326
327 #define split_attributes(attributes) (g_strsplit (attributes, ";", 0))
328
329 static gboolean 
330 match_attributes_all_p (AtkObject *child, 
331                         AtkAttributeSet *attributes)
332 {
333      int i, k;
334      AtkAttributeSet *oa;
335      gint length, oa_length;
336      gboolean flag = FALSE;
337
338      if (attributes == NULL || g_slist_length (attributes) == 0)
339           return TRUE;
340      
341      oa =  atk_object_get_attributes(child);
342      length = g_slist_length(attributes);
343      oa_length = g_slist_length(oa);
344
345      for (i = 0; i < length; i++) {
346           AtkAttribute *attr = g_slist_nth_data(attributes, i);
347           for (k = 0; k < oa_length; k++) {
348                AtkAttribute *oa_attr = g_slist_nth_data(attributes, i);
349                if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
350                    !g_ascii_strcasecmp (oa_attr->value, attr->value)){
351                     flag = TRUE;
352                     break;
353                }
354                else
355                     flag = FALSE;
356                }
357           if (!flag) {
358                atk_attribute_set_free(oa);
359                return FALSE; 
360           }
361      }
362      atk_attribute_set_free(oa);
363      return TRUE;
364 }
365
366 static gboolean 
367 match_attributes_any_p (AtkObject *child, 
368                         AtkAttributeSet *attributes)
369 {
370      int i, k;
371
372      AtkAttributeSet *oa;
373      gint length, oa_length;
374
375      length = g_slist_length(attributes);
376      if (length == 0)
377           return TRUE;
378
379      oa = atk_object_get_attributes(child);
380      oa_length = g_slist_length(oa);
381
382      for (i = 0; i < length; i++){
383           AtkAttribute *attr = g_slist_nth_data(attributes, i);
384           for (k = 0; k < oa_length; k++){
385                AtkAttribute *oa_attr = g_slist_nth_data(attributes, i);
386                if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
387                    !g_ascii_strcasecmp (oa_attr->value, attr->value)){
388                     atk_attribute_set_free(oa);
389                     return TRUE;
390                     }
391                }
392           }
393      atk_attribute_set_free(oa);
394      return FALSE;
395 }
396
397 static gboolean 
398 match_attributes_none_p (AtkObject *child, 
399                          AtkAttributeSet *attributes)
400 {
401      int i, k;
402
403      AtkAttributeSet *oa;
404      gint length, oa_length;
405
406      length = g_slist_length(attributes);
407      if (length == 0)
408           return TRUE;
409
410      oa = atk_object_get_attributes(child);
411      oa_length = g_slist_length(oa);
412
413      for (i = 0; i < length; i++){
414           AtkAttribute *attr = g_slist_nth_data(attributes, i);
415           for (k = 0; k < oa_length; k++){
416                AtkAttribute *oa_attr = g_slist_nth_data(attributes, i);
417                if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
418                    !g_ascii_strcasecmp (oa_attr->value, attr->value)){
419                     atk_attribute_set_free(oa);
420                     return FALSE;
421                     }
422                }
423           }
424      atk_attribute_set_free(oa);
425      return TRUE;
426 }
427
428 static gboolean
429 match_attributes_lookup (AtkObject *child, MatchRulePrivate *mrp)
430 {
431      switch (mrp->attributematchtype){
432
433           case Accessibility_Collection_MATCH_ALL : 
434           if (match_attributes_all_p (child, mrp->attributes))
435                return TRUE;
436           break;
437           
438      case  Accessibility_Collection_MATCH_ANY :
439           if (match_attributes_any_p (child, mrp->attributes))
440                return TRUE;
441           break;
442           
443      case  Accessibility_Collection_MATCH_NONE :
444           if (match_attributes_none_p (child, mrp->attributes))
445                return TRUE;
446           break;
447
448       default : break;    
449      }
450      return FALSE;   
451 }
452
453 static gboolean
454 traverse_p (AtkObject *child, 
455             const gboolean traverse)
456 {
457   if (traverse)
458     return TRUE;
459   else return !child_collection_p (child);
460 }
461
462 static int 
463 sort_order_canonical (MatchRulePrivate *mrp, GList *ls,                       
464                       gint kount, gint max,
465                       AtkObject *obj, glong index, gboolean flag,
466                       AtkObject *pobj, gboolean recurse, 
467                       gboolean traverse)
468 {
469      gint i = index;
470      glong acount  = atk_object_get_n_accessible_children (obj);
471      gboolean prev = pobj? TRUE : FALSE;
472      
473      for (; i < acount && (max == 0 || kount < max); i++){
474           AtkObject *child = 
475                         atk_object_ref_accessible_child(obj, i);
476
477           g_object_unref(child);
478           if (prev && child == pobj){
479             return kount;           
480           }
481          
482           if (flag  && match_interfaces_lookup (child, mrp)
483                     && match_states_lookup (child, mrp)
484                     && match_roles_lookup (child, mrp)
485                     && match_attributes_lookup (child, mrp)
486                     ){
487            
488             ls = g_list_append (ls, child);
489             kount++;
490           }
491
492           if (!flag)
493                flag = TRUE;
494
495           if (recurse && traverse_p (child, traverse))
496             kount = sort_order_canonical (mrp, ls,  kount, 
497                                           max, child, 0, TRUE, 
498                                           pobj, recurse, traverse);
499      }
500      return kount;
501
502
503 static int 
504 sort_order_rev_canonical (MatchRulePrivate *mrp, GList *ls,                   
505                       gint kount, gint max,
506                       AtkObject *obj, gboolean flag, 
507                       AtkObject *pobj)
508 {
509     AtkObject *nextobj;
510     AtkObject *parent;
511     glong indexinparent;
512
513     /* This breaks us out of the recursion. */
514     if (!obj || obj == pobj)
515     {
516         return kount;           
517     } 
518          
519     /* Add to the list if it matches */
520     if (flag && match_interfaces_lookup (obj, mrp) 
521                && match_states_lookup (obj, mrp)
522                && match_roles_lookup (obj, mrp)
523                && match_attributes_lookup (obj, mrp))
524     {
525          ls = g_list_append (ls, obj);
526          kount++;
527     }
528
529     if(!flag) flag = TRUE;
530
531     /* Get the current nodes index in it's parent and the parent object. */
532     indexinparent = atk_object_get_index_in_parent (obj);
533     parent = atk_object_get_parent(obj);
534
535     if(indexinparent > 0)
536     {
537          /* there are still some siblings to visit so get the previous sibling
538             and get it's last descendant.
539             First, get the previous sibling */
540          nextobj = atk_object_ref_accessible_child (parent, 
541                                                              indexinparent-1);
542          g_object_unref(nextobj);
543
544          /* Now, drill down the right side to the last descendant */
545          while(atk_object_get_n_accessible_children (nextobj) > 0)
546          {
547               nextobj = atk_object_ref_accessible_child(nextobj,
548                  atk_object_get_n_accessible_children (nextobj)-1);
549               g_object_unref (nextobj);
550          } 
551          /* recurse with the last descendant */
552          kount = sort_order_rev_canonical (mrp, ls,  kount, max, 
553                                        nextobj, TRUE, pobj);
554     } 
555     else
556     {
557          /* no more siblings so next node must be the parent */
558          kount = sort_order_rev_canonical (mrp, ls,  kount, max, 
559                                        parent, TRUE, pobj);
560
561     }
562     return kount;
563
564
565 static int
566 query_exec (MatchRulePrivate *mrp,  Accessibility_Collection_SortOrder sortby, 
567             GList *ls, gint kount, gint max, 
568             AtkObject *obj, glong index, 
569             gboolean flag, 
570             AtkObject *pobj,
571             gboolean recurse, gboolean traverse)
572 {
573      switch (sortby) {
574      case Accessibility_Collection_SORT_ORDER_CANONICAL :  
575        kount = sort_order_canonical(mrp, ls, 0, max, obj, index, flag, 
576                                     pobj, recurse, traverse); 
577        break;
578      case Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL :
579        kount = sort_order_canonical(mrp, ls, 0, max, obj, index, flag, 
580                                     pobj, recurse, traverse);    
581        break;
582      default: 
583        kount = 0; 
584        g_warning ("Sort method not implemented yet"); 
585        break; 
586      }
587      
588      return kount;
589 }
590
591 static dbus_bool_t
592 read_mr(DBusMessageIter *iter, MatchRulePrivate *mrp)
593 {
594   DBusMessageIter mrc, arrayc;
595   dbus_uint32_t *array;
596   dbus_int32_t matchType;
597   int array_count;
598   char *str;
599   AtkAttribute *attr;
600   int i;
601   char *interfaces = NULL;
602
603   // TODO: error checking
604   dbus_message_iter_recurse(iter, &mrc);
605   dbus_message_iter_recurse(&mrc, &arrayc);
606   dbus_message_iter_get_fixed_array(&arrayc, &array, &array_count);
607   bitarray_to_seq(array, array_count, &mrp->states);
608   for (i = 0; mrp->states[i] != BITARRAY_SEQ_TERM; i++)
609   {
610     mrp->states[i] = spi_atk_state_from_spi_state (mrp->states[i]);
611   }
612   dbus_message_iter_next(&mrc);
613   dbus_message_iter_read_basic(&mrc, &matchType);
614   dbus_message_iter_next(&mrc);
615   mrp->statematchtype = matchType;;
616   /* attributes */
617   dbus_message_iter_recurse(&mrc, &arrayc);
618   mrp->attributes = NULL;
619   while (dbus_message_iter_get_arg_type(&arrayc) != DBUS_TYPE_INVALID)
620   {
621     dbus_message_iter_get_basic(&arrayc, &str);
622     // TODO: remove this print
623     g_print("Got attribute: %s\n", str);
624     attr = g_new (AtkAttribute, 1);
625     if (attr)
626     {
627       int len = strcspn(str, ":");
628       attr->name = g_strndup(str, len);
629       if (str[len] == ':')
630       {
631         len++;
632         if (str[len] == ' ') len++;
633         attr->value = g_strdup(str + len);
634       }
635       else attr->value = NULL;
636       mrp->attributes = g_slist_prepend(mrp->attributes, attr);
637     }
638     dbus_message_iter_next(&arrayc);
639   }
640   dbus_message_iter_next(&mrc);
641   dbus_message_iter_read_basic(&mrc, &matchType);
642   mrp->attributematchtype = matchType;;
643   dbus_message_iter_next(&mrc);
644   /* Get roles and role match */
645   dbus_message_iter_recurse(&mrc, &arrayc);
646   dbus_message_iter_get_fixed_array(&arrayc, &array, &array_count);
647   bitarray_to_seq(array, array_count, &mrp->roles);
648   dbus_message_iter_next(&mrc);
649   dbus_message_iter_read_basic(&mrc, &matchType);
650   mrp->rolematchtype = matchType;;
651   dbus_message_iter_next(&mrc);
652   /* Get interfaces and interface match */
653   dbus_message_iter_read_basic(&mrc, &interfaces);
654   dbus_message_iter_next(&mrc);
655   mrp->ifaces = g_strsplit(interfaces, ";", 0);
656   dbus_message_iter_read_basic(&mrc, &matchType);
657   mrp->interfacematchtype = matchType;;
658   dbus_message_iter_next(&mrc);
659   /* get invert */
660   dbus_message_iter_read_basic(&mrc, &mrp->invert);
661   dbus_message_iter_next(iter);
662   return TRUE;
663 }
664
665 static DBusMessage *
666 return_and_free_list(DBusMessage *message, GList *ls)
667 {
668   DBusMessage *reply;
669   DBusMessageIter iter, iter_array;
670   GList *item;
671
672   reply = dbus_message_new_method_return(message);
673   if (!reply) return NULL;
674   dbus_message_iter_init_append(reply, &iter);
675   if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "o", &iter_array)) goto oom;
676   for (item = ls; item; item = g_list_next(item))
677   {
678       char *path = (char *) spi_dbus_object_to_path ((AtkObject *)item->data);
679       dbus_message_iter_append_basic(&iter_array, DBUS_TYPE_OBJECT_PATH, &path);
680       g_free(path);
681   }
682   if (!dbus_message_iter_close_container(&iter, &iter_array)) goto oom;
683   g_list_free (ls);
684   return reply;
685 oom:
686   // TODO: Handle out of memory
687   g_list_free (ls);
688   return reply;
689 }
690
691 static void free_mrp_data(MatchRulePrivate *mrp)
692 {
693   g_free(mrp->states);
694   atk_attribute_set_free(mrp->attributes);
695   g_free(mrp->roles);
696   g_strfreev(mrp->ifaces);
697 }
698
699 static DBusMessage *
700 getMatchesFrom (DBusMessage *message,
701                      AtkObject *current_object,
702                      MatchRulePrivate *mrp,
703                      const Accessibility_Collection_SortOrder sortby,
704                      const dbus_bool_t isrestrict,
705                      dbus_int32_t  count,
706                      const dbus_bool_t traverse)
707 {
708      GList *ls = NULL;
709      AtkObject *parent;
710      glong index = 
711            atk_object_get_index_in_parent (current_object);
712      gint kount = 0;
713
714      ls = g_list_append (ls, current_object);
715           
716      if (!isrestrict)
717      {
718           parent = atk_object_get_parent (current_object);
719           kount = query_exec (mrp,  sortby, ls, 0, count, parent, index, 
720                               FALSE, NULL, TRUE, traverse);
721      }
722      else 
723           kount = query_exec (mrp,  sortby, ls, 0, count, 
724                               current_object, 0, FALSE, NULL, 
725                               TRUE, traverse);
726
727      ls = g_list_remove (ls, ls->data);
728
729      if (sortby == Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL)
730        ls = g_list_reverse (ls);
731  
732      free_mrp_data (mrp);
733      return return_and_free_list (message, ls);
734 }
735
736 /*
737   inorder traversal from a given object in the hierarchy
738 */
739
740 static int
741 inorder (AtkObject *collection, MatchRulePrivate *mrp, 
742         GList *ls, gint kount, gint max,
743         AtkObject *obj,
744         gboolean flag,
745         AtkObject *pobj,
746         dbus_bool_t traverse)
747 {
748   int i = 0;
749   
750   /* First, look through the children recursively. */
751   kount = sort_order_canonical (mrp, ls, kount, max, obj, 0, TRUE,
752                                 NULL, TRUE, TRUE); 
753   
754   /* Next, we look through the right subtree */
755   while ((max == 0 || kount < max) 
756           && obj != collection)
757   {
758     AtkObject *parent =
759                                 atk_object_get_parent (obj);
760     i = atk_object_get_index_in_parent (obj);
761     kount  = sort_order_canonical (mrp, ls, kount, max, parent, 
762                                    i+1, TRUE, FALSE, TRUE, TRUE);
763     obj = parent;
764   }
765
766   if (kount < max)
767   {
768      kount = sort_order_canonical (mrp, ls, kount, max, 
769                                    obj, i + 1, TRUE, FALSE, 
770                                    TRUE, TRUE);
771   }
772
773   return kount;
774 }
775
776 /*
777   GetMatchesInOrder: get matches from a given object in an inorder traversal.
778 */
779
780 static DBusMessage *
781 getMatchesInOrder (DBusMessage *message,
782                    AtkObject *current_object,
783                    MatchRulePrivate *mrp,
784                    const Accessibility_Collection_SortOrder sortby,
785                    const dbus_bool_t recurse,
786                    dbus_int32_t count,
787                    const dbus_bool_t traverse)
788 {
789   GList *ls = NULL;
790   AtkObject *obj;
791   gint kount = 0;
792
793   ls = g_list_append (ls, current_object);
794
795   obj = atk_dbus_path_to_object (dbus_message_get_path (message));
796   
797   kount = inorder (obj, mrp, ls, 0, count, 
798                    current_object, TRUE, NULL, traverse);
799
800   ls = g_list_remove (ls, ls->data);
801
802   if (sortby == Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL)
803     ls = g_list_reverse (ls);
804
805   free_mrp_data (mrp);
806   return return_and_free_list (message, ls);
807 }
808
809 /*
810   GetMatchesInBackOrder: get matches from a given object in a
811   reverse order traversal.
812 */
813
814 static DBusMessage *
815 getMatchesInBackOrder (DBusMessage *message,
816                    AtkObject *current_object,
817                    MatchRulePrivate *mrp,
818                    const Accessibility_Collection_SortOrder sortby,
819                    dbus_int32_t count)
820 {
821   GList *ls = NULL;
822   AtkObject *collection;
823   gint kount = 0;
824
825   ls = g_list_append (ls, current_object);
826
827   collection = atk_dbus_path_to_object (dbus_message_get_path (message));
828
829   kount = sort_order_rev_canonical (mrp, ls, 0, count, current_object, 
830                                    FALSE, collection);
831
832   ls = g_list_remove (ls, ls->data);
833
834   if (sortby == Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL)
835     ls = g_list_reverse (ls);
836
837   free_mrp_data (mrp);
838   return return_and_free_list (message, ls);
839 }
840
841 static DBusMessage *
842 getMatchesTo (DBusMessage *message,
843               AtkObject *current_object,
844               MatchRulePrivate *mrp,
845               const Accessibility_Collection_SortOrder sortby,
846               const dbus_bool_t recurse, 
847               const dbus_bool_t isrestrict,
848               dbus_int32_t  count,
849               const dbus_bool_t traverse)
850 {
851   GList *ls = NULL;
852   AtkObject *obj;
853   gint kount = 0;
854
855   ls = g_list_append (ls, current_object); 
856     
857   if (recurse){
858     obj = atk_object_get_parent (current_object);
859     kount =  query_exec (mrp,  sortby, ls, 0, count, 
860                          obj, 0, TRUE, current_object, TRUE, traverse);
861   }
862   else{ 
863     obj = atk_dbus_path_to_object (dbus_message_get_path (message));
864     kount = query_exec (mrp,  sortby, ls, 0, count,
865                         obj, 0, TRUE, current_object, TRUE, traverse); 
866
867   }
868
869   ls = g_list_remove (ls, ls->data);
870    
871   if (sortby != Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL)
872     ls = g_list_reverse (ls);
873
874   free_mrp_data (mrp);
875   return return_and_free_list (message, ls);
876 }
877
878 static DBusMessage *
879 impl_getMatchesFrom (DBusConnection *bus, DBusMessage *message, void *user_data)
880 {
881   char *current_object_path = NULL;
882   AtkObject *current_object;
883   DBusMessageIter iter;
884   MatchRulePrivate rule;
885   dbus_uint16_t sortby;
886   dbus_uint16_t tree;
887   dbus_int32_t count;
888   dbus_bool_t traverse;
889   GList *ls = NULL;
890
891   dbus_message_iter_init(message, &iter);
892   dbus_message_iter_get_basic (&iter, current_object_path);
893   current_object = atk_dbus_path_to_object (current_object_path);
894   if (!current_object)
895   {
896     // TODO: object-not-found error
897     return spi_dbus_general_error (message);
898   }
899   dbus_message_iter_next (&iter);
900   if (!read_mr(&iter, &rule))
901   {
902     return spi_dbus_general_error (message);
903   }
904   dbus_message_iter_get_basic(&iter, &sortby);
905   dbus_message_iter_next(&iter);
906   dbus_message_iter_get_basic(&iter, &tree);
907   dbus_message_iter_next(&iter);
908   dbus_message_iter_get_basic(&iter, &count);
909   dbus_message_iter_next(&iter);
910   dbus_message_iter_get_basic(&iter, &traverse);
911   dbus_message_iter_next(&iter);
912
913   switch (tree){
914   case Accessibility_Collection_TREE_RESTRICT_CHILDREN : 
915     return getMatchesFrom (message, current_object, 
916                            &rule, sortby, TRUE, count, traverse);
917     break;
918   case Accessibility_Collection_TREE_RESTRICT_SIBLING :
919     return getMatchesFrom (message, current_object, 
920                            &rule, sortby, FALSE, count, traverse); 
921     break;
922   case Accessibility_Collection_TREE_INORDER :
923     return getMatchesInOrder (message, current_object, 
924                               &rule, sortby, TRUE, count, traverse); 
925     break;
926   default : return NULL;
927   }
928 }
929
930 static DBusMessage *
931 impl_getMatchesTo (DBusConnection *bus, DBusMessage *message, void *user_data)
932 {
933   char *current_object_path = NULL;
934   AtkObject *current_object;
935   DBusMessageIter iter;
936   MatchRulePrivate rule;
937   dbus_uint16_t sortby;
938   dbus_uint16_t tree;
939   dbus_bool_t recurse;
940   dbus_int32_t count;
941   dbus_bool_t traverse;
942   GList *ls = NULL;
943
944   dbus_message_iter_init(message, &iter);
945   dbus_message_iter_get_basic (&iter, current_object_path);
946   current_object = atk_dbus_path_to_object (current_object_path);
947   if (!current_object)
948   {
949     // TODO: object-not-found error
950     return spi_dbus_general_error (message);
951   }
952   dbus_message_iter_next (&iter);
953   if (!read_mr(&iter, &rule))
954   {
955     return spi_dbus_general_error (message);
956   }
957   dbus_message_iter_get_basic(&iter, &sortby);
958   dbus_message_iter_next(&iter);
959   dbus_message_iter_get_basic(&iter, &tree);
960   dbus_message_iter_next(&iter);
961   dbus_message_iter_get_basic(&iter, &recurse);
962   dbus_message_iter_next(&iter);
963   dbus_message_iter_get_basic(&iter, &count);
964   dbus_message_iter_next(&iter);
965   dbus_message_iter_get_basic(&iter, &traverse);
966   dbus_message_iter_next(&iter);
967
968   switch (tree){
969   case Accessibility_Collection_TREE_RESTRICT_CHILDREN : 
970     return getMatchesTo (message, current_object, 
971                          &rule, sortby, recurse, TRUE, count, traverse); 
972     break;
973   case Accessibility_Collection_TREE_RESTRICT_SIBLING :
974     return getMatchesTo (message, current_object, 
975                          &rule, sortby, recurse, FALSE, count, traverse); 
976     break;
977   case Accessibility_Collection_TREE_INORDER :
978     return getMatchesInBackOrder (message, current_object, 
979                                   &rule, sortby, count); 
980     break;
981   default : return NULL;
982   }
983 }
984
985 static DBusMessage *
986 impl_getMatches(DBusConnection *bus, DBusMessage *message, void *user_data)
987 {
988   AtkObject *obj = path_to_object (message);
989   DBusMessageIter iter;
990   MatchRulePrivate rule;
991   dbus_uint16_t sortby;
992   dbus_int32_t count;
993   dbus_bool_t traverse;
994   GList *ls = NULL;
995
996   dbus_message_iter_init(message, &iter);
997   if (!read_mr(&iter, &rule))
998   {
999     return spi_dbus_general_error (message);
1000   }
1001   dbus_message_iter_get_basic(&iter, &sortby);
1002   dbus_message_iter_next(&iter);
1003   dbus_message_iter_get_basic(&iter, &count);
1004   dbus_message_iter_next(&iter);
1005   dbus_message_iter_get_basic(&iter, &traverse);
1006   dbus_message_iter_next(&iter);
1007      ls = g_list_prepend (ls, obj);
1008   count = query_exec (&rule, sortby, ls, 0, count,
1009                          obj, 0, TRUE, NULL, TRUE, traverse);
1010      ls = g_list_remove (ls, ls->data);
1011       
1012      if (sortby == Accessibility_Collection_SORT_ORDER_REVERSE_CANONICAL)
1013        ls = g_list_reverse (ls);
1014      free_mrp_data (&rule);
1015      return return_and_free_list (message, ls);
1016 }
1017
1018 static DRouteMethod methods[] = {
1019   { impl_getMatchesFrom, "getMatchesFrom" },
1020   { impl_getMatchesTo, "getMatchesTo" },
1021   { impl_getMatches, "getMatches" },
1022   {NULL, NULL}
1023 };
1024
1025 void
1026 spi_initialize_collection (DRoutePath *path)
1027 {
1028   droute_path_add_interface (path,
1029                              SPI_DBUS_INTERFACE_COLLECTION,
1030                              methods,
1031                              NULL);
1032 };