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 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.
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
 * Lesser General Public License for more details.
16
 *
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.
21
 */
22

            
23
/* collection.c: implements the Collection interface */
24

            
25
#include <string.h>
26
#include <strings.h>
27

            
28
#include "bridge.h"
29
#include <atk/atk.h>
30
#include <droute/droute.h>
31

            
32
#include "accessible-stateset.h"
33
#include "bitarray.h"
34
#include "spi-dbus.h"
35

            
36
#include "accessible-register.h"
37
#include "introspection.h"
38
#include "object.h"
39

            
40
#define MAX_CHILDREN 65536
41

            
42
typedef struct _MatchRulePrivate MatchRulePrivate;
43
struct _MatchRulePrivate
44
{
45
  gint *states;
46
  AtspiCollectionMatchType statematchtype;
47
  AtkAttributeSet *attributes;
48
  AtspiCollectionMatchType attributematchtype;
49
  gint *roles;
50
  AtspiCollectionMatchType rolematchtype;
51
  gchar **ifaces;
52
  AtspiCollectionMatchType interfacematchtype;
53
  gboolean invert;
54
};
55

            
56
static gboolean
57
child_interface_p (AtkObject *child, gchar *repo_id)
58
{
59
  if (!strcasecmp (repo_id, "action"))
60
    return ATK_IS_ACTION (child);
61
  if (!strcasecmp (repo_id, "component"))
62
    return ATK_IS_COMPONENT (child);
63
  if (!strcasecmp (repo_id, "editabletext"))
64
    return ATK_IS_EDITABLE_TEXT (child);
65
  if (!strcasecmp (repo_id, "text"))
66
    return ATK_IS_TEXT (child);
67
  if (!strcasecmp (repo_id, "hypertext"))
68
    return ATK_IS_HYPERTEXT (child);
69
  if (!strcasecmp (repo_id, "image"))
70
    return ATK_IS_IMAGE (child);
71
  if (!strcasecmp (repo_id, "selection"))
72
    return ATK_IS_SELECTION (child);
73
  if (!strcasecmp (repo_id, "table"))
74
    return ATK_IS_TABLE (child);
75
  if (!strcasecmp (repo_id, "value"))
76
    return ATK_IS_VALUE (child);
77
  if (!strcasecmp (repo_id, "streamablecontent"))
78
    return ATK_IS_STREAMABLE_CONTENT (child);
79
  if (!strcasecmp (repo_id, "document"))
80
    return ATK_IS_DOCUMENT (child);
81
  return FALSE;
82
}
83

            
84
#define child_collection_p(ch) (TRUE)
85

            
86
static gboolean
87
25
match_states_all_p (AtkObject *child, gint *set)
88
{
89
  AtkStateSet *chs;
90
  gint i;
91
25
  gboolean ret = TRUE;
92

            
93
25
  if (set == NULL || set[0] == BITARRAY_SEQ_TERM)
94
14
    return TRUE;
95

            
96
11
  chs = atk_object_ref_state_set (child);
97

            
98
  // TODO: use atk-state_set_contains_states; would be more efficient
99
23
  for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
100
    {
101
17
      if (!atk_state_set_contains_state (chs, set[i]))
102
        {
103
5
          ret = FALSE;
104
5
          break;
105
        }
106
    }
107

            
108
11
  g_object_unref (chs);
109
11
  return ret;
110
}
111

            
112
static gboolean
113
match_states_any_p (AtkObject *child, gint *set)
114
{
115
  AtkStateSet *chs;
116
  gint i;
117
  gboolean ret = FALSE;
118

            
119
  if (set == NULL || set[0] == BITARRAY_SEQ_TERM)
120
    return TRUE;
121

            
122
  chs = atk_object_ref_state_set (child);
123

            
124
  for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
125
    {
126
      if (atk_state_set_contains_state (chs, set[i]))
127
        {
128
          ret = TRUE;
129
          break;
130
        }
131
    }
132

            
133
  g_object_unref (chs);
134
  return ret;
135
}
136

            
137
static gboolean
138
match_states_none_p (AtkObject *child, gint *set)
139
{
140
  AtkStateSet *chs;
141
  gint i;
142
  gboolean ret = TRUE;
143

            
144
  if (set == NULL || set[0] == BITARRAY_SEQ_TERM)
145
    return TRUE;
146

            
147
  chs = atk_object_ref_state_set (child);
148

            
149
  for (i = 0; set[i] != BITARRAY_SEQ_TERM; i++)
150
    {
151
      if (atk_state_set_contains_state (chs, set[i]))
152
        {
153
          ret = FALSE;
154
          break;
155
        }
156
    }
157

            
158
  g_object_unref (chs);
159
  return ret;
160
}
161

            
162
// TODO: need to convert at-spi roles/states to atk roles/states */
163
static gboolean
164
25
match_states_lookup (AtkObject *child, MatchRulePrivate *mrp)
165
{
166
25
  switch (mrp->statematchtype)
167
    {
168
25
    case ATSPI_Collection_MATCH_ALL:
169
25
      if (match_states_all_p (child, mrp->states))
170
20
        return TRUE;
171
5
      break;
172

            
173
    case ATSPI_Collection_MATCH_ANY:
174
      if (match_states_any_p (child, mrp->states))
175
        return TRUE;
176
      break;
177

            
178
    case ATSPI_Collection_MATCH_NONE:
179
      if (match_states_none_p (child, mrp->states))
180
        return TRUE;
181
      break;
182

            
183
    default:
184
      break;
185
    }
186

            
187
5
  return FALSE;
188
}
189

            
190
// TODO: Map at-spi -> atk roles at mrp creation instead of doing this;
191
// would be more efficient
192
#define spi_get_role(obj) spi_accessible_role_from_atk_role (atk_object_get_role (obj))
193

            
194
static gboolean
195
13
match_roles_all_p (AtkObject *child, gint *roles)
196
{
197
13
  if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM)
198
13
    return TRUE;
199
  else if (roles[1] != BITARRAY_SEQ_TERM)
200
    return FALSE;
201

            
202
  return (spi_get_role (child) == roles[0]);
203
}
204

            
205
static gboolean
206
match_roles_any_p (AtkObject *child, gint *roles)
207
{
208
  AtspiRole role;
209
  int i;
210

            
211
  if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM)
212
    return TRUE;
213

            
214
  role = spi_accessible_role_from_atk_role (atk_object_get_role (child));
215

            
216
  for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++)
217
    if (role == roles[i])
218
      return TRUE;
219

            
220
  return FALSE;
221
}
222

            
223
static gboolean
224
7
match_roles_none_p (AtkObject *child, gint *roles)
225
{
226
  AtspiRole role;
227
  int i;
228

            
229
7
  if (roles == NULL || roles[0] == BITARRAY_SEQ_TERM)
230
    return TRUE;
231

            
232
7
  role = spi_get_role (child);
233

            
234
13
  for (i = 0; roles[i] != BITARRAY_SEQ_TERM; i++)
235
7
    if (role == roles[i])
236
1
      return FALSE;
237

            
238
6
  return TRUE;
239
}
240

            
241
static gboolean
242
20
match_roles_lookup (AtkObject *child, MatchRulePrivate *mrp)
243
{
244
20
  switch (mrp->rolematchtype)
245
    {
246
13
    case ATSPI_Collection_MATCH_ALL:
247
13
      if (match_roles_all_p (child, mrp->roles))
248
13
        return TRUE;
249
      break;
250

            
251
    case ATSPI_Collection_MATCH_ANY:
252
      if (match_roles_any_p (child, mrp->roles))
253
        return TRUE;
254
      break;
255

            
256
7
    case ATSPI_Collection_MATCH_NONE:
257
7
      if (match_roles_none_p (child, mrp->roles))
258
6
        return TRUE;
259
1
      break;
260

            
261
    default:
262
      break;
263
    }
264
1
  return FALSE;
265
}
266

            
267
static gboolean
268
25
match_interfaces_all_p (AtkObject *obj, gchar **ifaces)
269
{
270
  gint i;
271

            
272
25
  if (ifaces == NULL)
273
    return TRUE;
274

            
275
25
  for (i = 0; ifaces[i]; i++)
276
    if (!child_interface_p (obj, ifaces[i]))
277
      {
278
        return FALSE;
279
      }
280
25
  return TRUE;
281
}
282

            
283
static gboolean
284
match_interfaces_any_p (AtkObject *obj, gchar **ifaces)
285
{
286
  gint i;
287

            
288
  if (ifaces == NULL)
289
    return TRUE;
290

            
291
  for (i = 0; ifaces[i]; i++)
292
    if (child_interface_p (obj, ifaces[i]))
293
      {
294
        return TRUE;
295
      }
296
  return FALSE;
297
}
298

            
299
static gboolean
300
match_interfaces_none_p (AtkObject *obj, gchar **ifaces)
301
{
302
  gint i;
303

            
304
  for (i = 0; ifaces[i]; i++)
305
    if (child_interface_p (obj, ifaces[i]))
306
      return FALSE;
307

            
308
  return TRUE;
309
}
310

            
311
static gboolean
312
25
match_interfaces_lookup (AtkObject *child, MatchRulePrivate *mrp)
313
{
314
25
  switch (mrp->interfacematchtype)
315
    {
316

            
317
25
    case ATSPI_Collection_MATCH_ALL:
318
25
      if (match_interfaces_all_p (child, mrp->ifaces))
319
25
        return TRUE;
320
      break;
321

            
322
    case ATSPI_Collection_MATCH_ANY:
323
      if (match_interfaces_any_p (child, mrp->ifaces))
324
        return TRUE;
325
      break;
326

            
327
    case ATSPI_Collection_MATCH_NONE:
328
      if (match_interfaces_none_p (child, mrp->ifaces))
329
        return TRUE;
330
      break;
331

            
332
    default:
333
      break;
334
    }
335

            
336
  return FALSE;
337
}
338

            
339
#define split_attributes(attributes) (g_strsplit (attributes, ";", 0))
340

            
341
static gboolean
342
12
match_attributes_all_p (AtkObject *child, AtkAttributeSet *attributes)
343
{
344
  int i, k;
345
  AtkAttributeSet *oa;
346
  gint length, oa_length;
347
12
  gboolean flag = FALSE;
348

            
349
12
  if (attributes == NULL || g_slist_length (attributes) == 0)
350
12
    return TRUE;
351

            
352
  oa = atk_object_get_attributes (child);
353
  length = g_slist_length (attributes);
354
  oa_length = g_slist_length (oa);
355

            
356
  for (i = 0; i < length; i++)
357
    {
358
      AtkAttribute *attr = g_slist_nth_data (attributes, i);
359
      for (k = 0; k < oa_length; k++)
360
        {
361
          AtkAttribute *oa_attr = g_slist_nth_data (oa, k);
362
          if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
363
              !g_ascii_strcasecmp (oa_attr->value, attr->value))
364
            {
365
              flag = TRUE;
366
              break;
367
            }
368
          else
369
            flag = FALSE;
370
        }
371
      if (!flag)
372
        {
373
          atk_attribute_set_free (oa);
374
          return FALSE;
375
        }
376
    }
377
  atk_attribute_set_free (oa);
378
  return TRUE;
379
}
380

            
381
static gboolean
382
match_attributes_any_p (AtkObject *child, AtkAttributeSet *attributes)
383
{
384
  int i, k;
385

            
386
  AtkAttributeSet *oa;
387
  gint length, oa_length;
388

            
389
  length = g_slist_length (attributes);
390
  if (length == 0)
391
    return TRUE;
392

            
393
  oa = atk_object_get_attributes (child);
394
  oa_length = g_slist_length (oa);
395

            
396
  for (i = 0; i < length; i++)
397
    {
398
      AtkAttribute *attr = g_slist_nth_data (attributes, i);
399
      for (k = 0; k < oa_length; k++)
400
        {
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))
404
            {
405
              atk_attribute_set_free (oa);
406
              return TRUE;
407
            }
408
        }
409
    }
410
  atk_attribute_set_free (oa);
411
  return FALSE;
412
}
413

            
414
static gboolean
415
7
match_attributes_none_p (AtkObject *child, AtkAttributeSet *attributes)
416
{
417
  int i, k;
418

            
419
  AtkAttributeSet *oa;
420
  gint length, oa_length;
421

            
422
7
  length = g_slist_length (attributes);
423
7
  if (length == 0)
424
    return TRUE;
425

            
426
7
  oa = atk_object_get_attributes (child);
427
7
  oa_length = g_slist_length (oa);
428

            
429
14
  for (i = 0; i < length; i++)
430
    {
431
7
      AtkAttribute *attr = g_slist_nth_data (attributes, i);
432
21
      for (k = 0; k < oa_length; k++)
433
        {
434
14
          AtkAttribute *oa_attr = g_slist_nth_data (oa, k);
435
14
          if (!g_ascii_strcasecmp (oa_attr->name, attr->name) &&
436
              !g_ascii_strcasecmp (oa_attr->value, attr->value))
437
            {
438
              atk_attribute_set_free (oa);
439
              return FALSE;
440
            }
441
        }
442
    }
443
7
  atk_attribute_set_free (oa);
444
7
  return TRUE;
445
}
446

            
447
static gboolean
448
19
match_attributes_lookup (AtkObject *child, MatchRulePrivate *mrp)
449
{
450
19
  switch (mrp->attributematchtype)
451
    {
452

            
453
12
    case ATSPI_Collection_MATCH_ALL:
454
12
      if (match_attributes_all_p (child, mrp->attributes))
455
12
        return TRUE;
456
      break;
457

            
458
    case ATSPI_Collection_MATCH_ANY:
459
      if (match_attributes_any_p (child, mrp->attributes))
460
        return TRUE;
461
      break;
462

            
463
7
    case ATSPI_Collection_MATCH_NONE:
464
7
      if (match_attributes_none_p (child, mrp->attributes))
465
7
        return TRUE;
466
      break;
467

            
468
    default:
469
      break;
470
    }
471
  return FALSE;
472
}
473

            
474
static gboolean
475
24
traverse_p (AtkObject *child, const gboolean traverse)
476
{
477
24
  if (traverse)
478
21
    return TRUE;
479
  else
480
3
    return !child_collection_p (child);
481
}
482

            
483
static int
484
31
sort_order_canonical (MatchRulePrivate *mrp, GList *ls, gint kount, gint max, AtkObject *obj, glong index, gboolean flag, AtkObject *pobj, gboolean recurse, gboolean traverse)
485
{
486
31
  gint i = index;
487
31
  glong acount = atk_object_get_n_accessible_children (obj);
488
31
  gboolean prev = pobj ? TRUE : FALSE;
489

            
490
31
  if (acount > MAX_CHILDREN)
491
    acount = MAX_CHILDREN;
492
55
  for (; i < acount && (max == 0 || kount < max); i++)
493
    {
494
24
      AtkObject *child = atk_object_ref_accessible_child (obj, i);
495

            
496
24
      if (!child)
497
        continue;
498

            
499
24
      if (prev && child == pobj)
500
        {
501
          g_object_unref (child);
502
          return kount;
503
        }
504

            
505
24
      if (flag && match_interfaces_lookup (child, mrp) && match_states_lookup (child, mrp) && match_roles_lookup (child, mrp) && match_attributes_lookup (child, mrp))
506
        {
507

            
508
18
          ls = g_list_append (ls, child);
509
18
          kount++;
510
        }
511

            
512
24
      if (!flag)
513
        flag = TRUE;
514

            
515
24
      if (recurse && traverse_p (child, traverse))
516
21
        kount = sort_order_canonical (mrp, ls, kount,
517
                                      max, child, 0, TRUE,
518
                                      pobj, recurse, traverse);
519
24
      g_object_unref (child);
520
    }
521
31
  return kount;
522
}
523

            
524
static int
525
3
sort_order_rev_canonical (MatchRulePrivate *mrp, GList *ls, gint kount, gint max, AtkObject *obj, gboolean flag, AtkObject *pobj)
526
{
527
  AtkObject *nextobj;
528
  AtkObject *parent;
529
  glong indexinparent;
530

            
531
  /* This breaks us out of the recursion. */
532
3
  if (!obj || obj == pobj)
533
    {
534
1
      return kount;
535
    }
536

            
537
  /* Add to the list if it matches */
538
2
  if (flag && match_interfaces_lookup (obj, mrp) && match_states_lookup (obj, mrp) && match_roles_lookup (obj, mrp) && match_attributes_lookup (obj, mrp) && (max == 0 || kount < max))
539
    {
540
1
      ls = g_list_append (ls, obj);
541
1
      kount++;
542
    }
543

            
544
  /* Get the current nodes index in it's parent and the parent object. */
545
2
  indexinparent = atk_object_get_index_in_parent (obj);
546
2
  parent = atk_object_get_parent (obj);
547

            
548
2
  if (indexinparent > 0 && (max == 0 || kount < max))
549
    {
550
      /* there are still some siblings to visit so get the previous sibling
551
         and get it's last descendant.
552
         First, get the previous sibling */
553
1
      nextobj = atk_object_ref_accessible_child (parent, indexinparent - 1);
554

            
555
      /* Now, drill down the right side to the last descendant */
556
1
      while (nextobj && atk_object_get_n_accessible_children (nextobj) > 0)
557
        {
558
          AtkObject *follow;
559
          gint count = atk_object_get_n_accessible_children (nextobj);
560
          if (count > MAX_CHILDREN)
561
            count = MAX_CHILDREN;
562

            
563
          follow = atk_object_ref_accessible_child (nextobj, count - 1);
564
          g_object_unref (nextobj);
565
          nextobj = follow;
566
        }
567
      /* recurse with the last descendant */
568
1
      kount = sort_order_rev_canonical (mrp, ls, kount, max,
569
                                        nextobj, TRUE, pobj);
570
1
      if (nextobj)
571
1
        g_object_unref (nextobj);
572
    }
573
1
  else if (max == 0 || kount < max)
574
    {
575
      /* no more siblings so next node must be the parent */
576
1
      kount = sort_order_rev_canonical (mrp, ls, kount, max,
577
                                        parent, TRUE, pobj);
578
    }
579
2
  return kount;
580
}
581

            
582
static int
583
1
query_exec (MatchRulePrivate *mrp, AtspiCollectionSortOrder sortby, GList *ls, gint kount, gint max, AtkObject *obj, glong index, gboolean flag, AtkObject *pobj, gboolean recurse, gboolean traverse)
584
{
585
1
  switch (sortby)
586
    {
587
1
    case ATSPI_Collection_SORT_ORDER_CANONICAL:
588
1
      kount = sort_order_canonical (mrp, ls, 0, max, obj, index, flag,
589
                                    pobj, recurse, traverse);
590
1
      break;
591
    case ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL:
592
      kount = sort_order_canonical (mrp, ls, 0, max, obj, index, flag,
593
                                    pobj, recurse, traverse);
594
      break;
595
    default:
596
      kount = 0;
597
      g_warning ("Sort method not implemented yet");
598
      break;
599
    }
600

            
601
1
  return kount;
602
}
603

            
604
static gboolean
605
10
bitarray_to_seq (dbus_uint32_t *array, int array_count, int **ret)
606
{
607
10
  int out_size = 4;
608
10
  int out_count = 0;
609
  int i, j;
610
10
  int *out = (int *) g_malloc (out_size * sizeof (int));
611

            
612
40
  for (i = 0; i < array_count; i++)
613
    {
614
990
      for (j = 0; j < 32; j++)
615
        {
616
960
          if (array[i] & (1 << j))
617
            {
618
7
              if (out_count == out_size - 2)
619
                {
620
                  out_size <<= 1;
621
                  out = (int *) g_realloc (out, out_size * sizeof (int));
622
                }
623
7
              out[out_count++] = i * 32 + j;
624
            }
625
        }
626
    }
627
10
  out[out_count] = BITARRAY_SEQ_TERM;
628
10
  *ret = out;
629
10
  return TRUE;
630
}
631

            
632
static dbus_bool_t
633
5
read_mr (DBusMessageIter *iter, MatchRulePrivate *mrp)
634
{
635
  DBusMessageIter iter_struct, iter_array, iter_dict, iter_dict_entry;
636
  dbus_uint32_t *array;
637
  dbus_int32_t matchType;
638
  int array_count;
639
  AtkAttribute *attr;
640
  int i;
641

            
642
5
  dbus_message_iter_recurse (iter, &iter_struct);
643

            
644
  /* states */
645
5
  dbus_message_iter_recurse (&iter_struct, &iter_array);
646
5
  dbus_message_iter_get_fixed_array (&iter_array, &array, &array_count);
647
5
  bitarray_to_seq (array, array_count, &mrp->states);
648
11
  for (i = 0; mrp->states[i] != BITARRAY_SEQ_TERM; i++)
649
    {
650
6
      mrp->states[i] = spi_atk_state_from_spi_state (mrp->states[i]);
651
    }
652
5
  dbus_message_iter_next (&iter_struct);
653
5
  dbus_message_iter_get_basic (&iter_struct, &matchType);
654
5
  dbus_message_iter_next (&iter_struct);
655
5
  mrp->statematchtype = matchType;
656
  ;
657

            
658
  /* attributes */
659
5
  mrp->attributes = NULL;
660
5
  dbus_message_iter_recurse (&iter_struct, &iter_dict);
661
6
  while (dbus_message_iter_get_arg_type (&iter_dict) != DBUS_TYPE_INVALID)
662
    {
663
      const char *key, *val;
664
      const char *p, *q;
665
1
      dbus_message_iter_recurse (&iter_dict, &iter_dict_entry);
666
1
      dbus_message_iter_get_basic (&iter_dict_entry, &key);
667
1
      dbus_message_iter_next (&iter_dict_entry);
668
1
      dbus_message_iter_get_basic (&iter_dict_entry, &val);
669
1
      p = q = val;
670
      for (;;)
671
        {
672
5
          if (*q == '\0' || (*q == ':' && (q == val || q[-1] != '\\')))
673
            {
674
              char *tmp;
675
1
              attr = g_new (AtkAttribute, 1);
676
2
              attr->name = g_strdup (key);
677
1
              attr->value = g_strdup (p);
678
1
              attr->value[q - p] = '\0';
679
1
              tmp = attr->value;
680
5
              while (*tmp != '\0')
681
                {
682
4
                  if (*tmp == '\\')
683
                    memmove (tmp, tmp + 1, strlen (tmp));
684
                  else
685
4
                    tmp++;
686
                }
687
1
              mrp->attributes = g_slist_prepend (mrp->attributes, attr);
688
1
              if (*q == '\0')
689
1
                break;
690
              else
691
                p = ++q;
692
            }
693
          else
694
4
            q++;
695
        }
696
1
      dbus_message_iter_next (&iter_dict);
697
    }
698
5
  dbus_message_iter_next (&iter_struct);
699
5
  dbus_message_iter_get_basic (&iter_struct, &matchType);
700
5
  mrp->attributematchtype = matchType;
701
  ;
702
5
  dbus_message_iter_next (&iter_struct);
703

            
704
  /* Get roles and role match */
705
5
  dbus_message_iter_recurse (&iter_struct, &iter_array);
706
5
  dbus_message_iter_get_fixed_array (&iter_array, &array, &array_count);
707
5
  bitarray_to_seq (array, array_count, &mrp->roles);
708
5
  dbus_message_iter_next (&iter_struct);
709
5
  dbus_message_iter_get_basic (&iter_struct, &matchType);
710
5
  mrp->rolematchtype = matchType;
711
  ;
712
5
  dbus_message_iter_next (&iter_struct);
713

            
714
  /* Get interfaces and interface match */
715
5
  dbus_message_iter_recurse (&iter_struct, &iter_array);
716
5
  mrp->ifaces = g_new0 (gchar *, 16);
717
5
  i = 0;
718
5
  while (i < 15 && dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
719
    {
720
      char *iface;
721
      dbus_message_iter_get_basic (&iter_array, &iface);
722
      mrp->ifaces[i] = g_strdup (iface);
723
      i++;
724
      dbus_message_iter_next (&iter_array);
725
    }
726
5
  dbus_message_iter_next (&iter_struct);
727
5
  dbus_message_iter_get_basic (&iter_struct, &matchType);
728
5
  mrp->interfacematchtype = matchType;
729
  ;
730
5
  dbus_message_iter_next (&iter_struct);
731
  /* get invert */
732
5
  dbus_message_iter_get_basic (&iter_struct, &mrp->invert);
733
5
  dbus_message_iter_next (iter);
734
5
  return TRUE;
735
}
736

            
737
static DBusMessage *
738
5
return_and_free_list (DBusMessage *message, GList *ls)
739
{
740
  DBusMessage *reply;
741
  DBusMessageIter iter, iter_array;
742
  GList *item;
743

            
744
5
  reply = dbus_message_new_method_return (message);
745
5
  if (!reply)
746
    return NULL;
747
5
  dbus_message_iter_init_append (reply, &iter);
748
5
  if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(so)", &iter_array))
749
    goto oom;
750
24
  for (item = ls; item; item = g_list_next (item))
751
    {
752
19
      spi_object_append_reference (&iter_array, ATK_OBJECT (item->data));
753
    }
754
5
  if (!dbus_message_iter_close_container (&iter, &iter_array))
755
    goto oom;
756
5
  g_list_free (ls);
757
5
  return reply;
758
oom:
759
  // TODO: Handle out of memory
760
  g_list_free (ls);
761
  return reply;
762
}
763

            
764
static void
765
5
free_mrp_data (MatchRulePrivate *mrp)
766
{
767
5
  g_free (mrp->states);
768
5
  atk_attribute_set_free (mrp->attributes);
769
5
  g_free (mrp->roles);
770
5
  g_strfreev (mrp->ifaces);
771
5
}
772

            
773
static DBusMessage *
774
GetMatchesFrom (DBusMessage *message,
775
                AtkObject *current_object,
776
                MatchRulePrivate *mrp,
777
                const AtspiCollectionSortOrder sortby,
778
                const dbus_bool_t isrestrict,
779
                dbus_int32_t count,
780
                const dbus_bool_t traverse)
781
{
782
  GList *ls = NULL;
783
  AtkObject *parent;
784
  glong index = atk_object_get_index_in_parent (current_object);
785

            
786
  ls = g_list_append (ls, current_object);
787

            
788
  if (!isrestrict)
789
    {
790
      parent = atk_object_get_parent (current_object);
791
      query_exec (mrp, sortby, ls, 0, count, parent, index,
792
                  FALSE, NULL, TRUE, traverse);
793
    }
794
  else
795
    query_exec (mrp, sortby, ls, 0, count,
796
                current_object, 0, FALSE, NULL, TRUE, traverse);
797

            
798
  ls = g_list_remove (ls, ls->data);
799

            
800
  if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
801
    ls = g_list_reverse (ls);
802

            
803
  free_mrp_data (mrp);
804
  return return_and_free_list (message, ls);
805
}
806

            
807
/*
808
  inorder traversal from a given object in the hierarchy
809
*/
810

            
811
static int
812
3
inorder (AtkObject *collection, MatchRulePrivate *mrp, GList *ls, gint kount, gint max, AtkObject *obj, gboolean flag, AtkObject *pobj, dbus_bool_t traverse)
813
{
814
3
  int i = 0;
815

            
816
  /* First, look through the children recursively. */
817
3
  kount = sort_order_canonical (mrp, ls, kount, max, obj, 0, TRUE,
818
                                NULL, TRUE, TRUE);
819

            
820
  /* Next, we look through the right subtree */
821
6
  while ((max == 0 || kount < max) && obj && obj != collection)
822
    {
823
3
      AtkObject *parent = atk_object_get_parent (obj);
824
3
      i = atk_object_get_index_in_parent (obj);
825
3
      kount = sort_order_canonical (mrp, ls, kount, max, parent,
826
3
                                    i + 1, TRUE, FALSE, TRUE, TRUE);
827
3
      obj = parent;
828
    }
829

            
830
3
  if (max == 0 || kount < max)
831
    {
832
3
      kount = sort_order_canonical (mrp, ls, kount, max,
833
3
                                    obj, i + 1, TRUE, FALSE, TRUE, TRUE);
834
    }
835

            
836
3
  return kount;
837
}
838

            
839
/*
840
  GetMatchesInOrder: get matches from a given object in an inorder traversal.
841
*/
842

            
843
static DBusMessage *
844
3
GetMatchesInOrder (DBusMessage *message,
845
                   AtkObject *current_object,
846
                   MatchRulePrivate *mrp,
847
                   const AtspiCollectionSortOrder sortby,
848
                   const dbus_bool_t recurse,
849
                   dbus_int32_t count,
850
                   const dbus_bool_t traverse)
851
{
852
3
  GList *ls = NULL;
853
  AtkObject *obj;
854

            
855
3
  ls = g_list_append (ls, current_object);
856

            
857
3
  obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
858

            
859
3
  inorder (obj, mrp, ls, 0, count,
860
           current_object, TRUE, NULL, traverse);
861

            
862
3
  ls = g_list_remove (ls, ls->data);
863

            
864
3
  if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
865
    ls = g_list_reverse (ls);
866

            
867
3
  free_mrp_data (mrp);
868
3
  return return_and_free_list (message, ls);
869
}
870

            
871
/*
872
  GetMatchesInBackOrder: get matches from a given object in a
873
  reverse order traversal.
874
*/
875

            
876
static DBusMessage *
877
1
GetMatchesInBackOrder (DBusMessage *message,
878
                       AtkObject *current_object,
879
                       MatchRulePrivate *mrp,
880
                       const AtspiCollectionSortOrder sortby,
881
                       dbus_int32_t count)
882
{
883
1
  GList *ls = NULL;
884
  AtkObject *collection;
885

            
886
1
  ls = g_list_append (ls, current_object);
887

            
888
1
  collection = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
889

            
890
1
  sort_order_rev_canonical (mrp, ls, 0, count, current_object,
891
                            FALSE, collection);
892

            
893
1
  ls = g_list_remove (ls, ls->data);
894

            
895
1
  if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
896
    ls = g_list_reverse (ls);
897

            
898
1
  free_mrp_data (mrp);
899
1
  return return_and_free_list (message, ls);
900
}
901

            
902
static DBusMessage *
903
GetMatchesTo (DBusMessage *message,
904
              AtkObject *current_object,
905
              MatchRulePrivate *mrp,
906
              const AtspiCollectionSortOrder sortby,
907
              const dbus_bool_t recurse,
908
              const dbus_bool_t isrestrict,
909
              dbus_int32_t count,
910
              const dbus_bool_t traverse)
911
{
912
  GList *ls = NULL;
913
  AtkObject *obj;
914
  ls = g_list_append (ls, current_object);
915

            
916
  if (recurse)
917
    {
918
      obj = ATK_OBJECT (atk_object_get_parent (current_object));
919
      query_exec (mrp, sortby, ls, 0, count,
920
                  obj, 0, TRUE, current_object, TRUE, traverse);
921
    }
922
  else
923
    {
924
      obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
925
      query_exec (mrp, sortby, ls, 0, count,
926
                  obj, 0, TRUE, current_object, TRUE, traverse);
927
    }
928

            
929
  ls = g_list_remove (ls, ls->data);
930

            
931
  if (sortby != ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
932
    ls = g_list_reverse (ls);
933

            
934
  free_mrp_data (mrp);
935
  return return_and_free_list (message, ls);
936
}
937

            
938
static DBusMessage *
939
3
impl_GetMatchesFrom (DBusConnection *bus, DBusMessage *message, void *user_data)
940
{
941
3
  char *current_object_path = NULL;
942
  AtkObject *current_object;
943
  DBusMessageIter iter;
944
  MatchRulePrivate rule;
945
  dbus_uint32_t sortby;
946
  dbus_uint32_t tree;
947
  dbus_int32_t count;
948
  dbus_bool_t traverse;
949
  const char *signature;
950

            
951
3
  signature = dbus_message_get_signature (message);
952
3
  if (strcmp (signature, "o(aiia{ss}iaiiasib)uuib") != 0)
953
    {
954
      return droute_invalid_arguments_error (message);
955
    }
956

            
957
3
  dbus_message_iter_init (message, &iter);
958
3
  dbus_message_iter_get_basic (&iter, &current_object_path);
959
3
  current_object = ATK_OBJECT (spi_register_path_to_object (spi_global_register, current_object_path));
960
3
  if (!current_object)
961
    {
962
      // TODO: object-not-found error
963
      return spi_dbus_general_error (message);
964
    }
965
3
  dbus_message_iter_next (&iter);
966
3
  if (!read_mr (&iter, &rule))
967
    {
968
      return spi_dbus_general_error (message);
969
    }
970
3
  dbus_message_iter_get_basic (&iter, &sortby);
971
3
  dbus_message_iter_next (&iter);
972
3
  dbus_message_iter_get_basic (&iter, &tree);
973
3
  dbus_message_iter_next (&iter);
974
3
  dbus_message_iter_get_basic (&iter, &count);
975
3
  dbus_message_iter_next (&iter);
976
3
  dbus_message_iter_get_basic (&iter, &traverse);
977
3
  dbus_message_iter_next (&iter);
978

            
979
3
  switch (tree)
980
    {
981
    case ATSPI_Collection_TREE_RESTRICT_CHILDREN:
982
      return GetMatchesFrom (message, current_object,
983
                             &rule, sortby, TRUE, count, traverse);
984
      break;
985
    case ATSPI_Collection_TREE_RESTRICT_SIBLING:
986
      return GetMatchesFrom (message, current_object,
987
                             &rule, sortby, FALSE, count, traverse);
988
      break;
989
3
    case ATSPI_Collection_TREE_INORDER:
990
3
      return GetMatchesInOrder (message, current_object,
991
                                &rule, sortby, TRUE, count, traverse);
992
      break;
993
    default:
994
      return NULL;
995
    }
996
}
997

            
998
static DBusMessage *
999
1
impl_GetMatchesTo (DBusConnection *bus, DBusMessage *message, void *user_data)
{
1
  char *current_object_path = NULL;
  AtkObject *current_object;
  DBusMessageIter iter;
  MatchRulePrivate rule;
  dbus_uint32_t sortby;
  dbus_uint32_t tree;
  dbus_bool_t recurse;
  dbus_int32_t count;
  dbus_bool_t traverse;
  const char *signature;
1
  signature = dbus_message_get_signature (message);
1
  if (strcmp (signature, "o(aiia{ss}iaiiasib)uubib") != 0)
    {
      return droute_invalid_arguments_error (message);
    }
1
  dbus_message_iter_init (message, &iter);
1
  dbus_message_iter_get_basic (&iter, &current_object_path);
1
  current_object = ATK_OBJECT (spi_register_path_to_object (spi_global_register, current_object_path));
1
  if (!current_object)
    {
      // TODO: object-not-found error
      return spi_dbus_general_error (message);
    }
1
  dbus_message_iter_next (&iter);
1
  if (!read_mr (&iter, &rule))
    {
      return spi_dbus_general_error (message);
    }
1
  dbus_message_iter_get_basic (&iter, &sortby);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &tree);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &recurse);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &count);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &traverse);
1
  dbus_message_iter_next (&iter);
1
  switch (tree)
    {
    case ATSPI_Collection_TREE_RESTRICT_CHILDREN:
      return GetMatchesTo (message, current_object,
                           &rule, sortby, recurse, TRUE, count, traverse);
      break;
    case ATSPI_Collection_TREE_RESTRICT_SIBLING:
      return GetMatchesTo (message, current_object,
                           &rule, sortby, recurse, FALSE, count, traverse);
      break;
1
    case ATSPI_Collection_TREE_INORDER:
1
      return GetMatchesInBackOrder (message, current_object,
                                    &rule, sortby, count);
      break;
    default:
      return NULL;
    }
}
static void
append_accessible_properties (DBusMessageIter *iter, AtkObject *obj, GArray *properties)
{
  DBusMessageIter iter_struct, iter_dict, iter_dict_entry;
  AtkStateSet *set;
  gint i;
  gint count;
  dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &iter_struct);
  spi_object_append_reference (&iter_struct, obj);
  dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_dict);
  if (properties && properties->len)
    {
      gint i;
      for (i = 0; i < properties->len; i++)
        {
          gchar *prop = g_array_index (properties, char *, i);
          DRoutePropertyFunction func;
          GType type;
          func = _atk_bridge_find_property_func (prop, &type);
          if (func && G_TYPE_CHECK_INSTANCE_TYPE (obj, type))
            {
              dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY,
                                                NULL, &iter_dict_entry);
              dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop);
              func (&iter_dict_entry, obj);
              dbus_message_iter_close_container (&iter_dict, &iter_dict_entry);
            }
        }
    }
  else
    {
      GHashTableIter hi;
      gpointer key, value;
      g_hash_table_iter_init (&hi, spi_global_app_data->property_hash);
      while (g_hash_table_iter_next (&hi, &key, &value))
        {
          const DRouteProperty *prop = value;
          GType type = _atk_bridge_type_from_iface (key);
          if (!G_TYPE_CHECK_INSTANCE_TYPE (obj, type))
            continue;
          for (; prop->name; prop++)
            {
              const char *p = key + strlen (key);
              gchar *property_name;
              while (p[-1] != '.')
                p--;
              if (!strcmp (p, "Accessible"))
                property_name = g_strdup (prop->name);
              else
                property_name = g_strconcat (p, ".", prop->name, NULL);
              dbus_message_iter_open_container (&iter_dict, DBUS_TYPE_DICT_ENTRY,
                                                NULL, &iter_dict_entry);
              dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &property_name);
              g_free (property_name);
              prop->get (&iter_dict_entry, obj);
              dbus_message_iter_close_container (&iter_dict, &iter_dict_entry);
            }
        }
    }
  dbus_message_iter_close_container (&iter_struct, &iter_dict);
  dbus_message_iter_close_container (iter, &iter_struct);
  set = atk_object_ref_state_set (obj);
  if (set)
    {
      gboolean md = atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS);
      g_object_unref (set);
      if (md)
        return;
    }
  count = atk_object_get_n_accessible_children (obj);
  if (count > MAX_CHILDREN)
    count = MAX_CHILDREN;
  for (i = 0; i < count; i++)
    {
      AtkObject *child = atk_object_ref_accessible_child (obj, i);
      if (child)
        {
          append_accessible_properties (iter, child, properties);
          g_object_unref (child);
        }
    }
}
static DBusMessage *
impl_GetTree (DBusConnection *bus,
              DBusMessage *message,
              void *user_data)
{
  AtkObject *object = (AtkObject *) user_data;
  DBusMessage *reply;
  DBusMessageIter iter, iter_array;
  MatchRulePrivate rule;
  GArray *properties;
  g_return_val_if_fail (ATK_IS_OBJECT (user_data),
                        droute_not_yet_handled_error (message));
  if (strcmp (dbus_message_get_signature (message), "(aiia{ss}iaiiasib)as") != 0)
    return droute_invalid_arguments_error (message);
  properties = g_array_new (TRUE, TRUE, sizeof (char *));
  dbus_message_iter_init (message, &iter);
  if (!read_mr (&iter, &rule))
    {
      return spi_dbus_general_error (message);
    }
  dbus_message_iter_recurse (&iter, &iter_array);
  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
    {
      const char *prop;
      dbus_message_iter_get_basic (&iter_array, &prop);
      g_array_append_val (properties, prop);
      dbus_message_iter_next (&iter_array);
    }
  reply = dbus_message_new_method_return (message);
  if (reply)
    {
      dbus_message_iter_init_append (reply, &iter);
      dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "((so)a{sv})",
                                        &iter_array);
      append_accessible_properties (&iter_array, object, properties);
      dbus_message_iter_close_container (&iter, &iter_array);
    }
  return reply;
}
static DBusMessage *
1
impl_GetMatches (DBusConnection *bus, DBusMessage *message, void *user_data)
{
1
  AtkObject *obj = ATK_OBJECT (spi_register_path_to_object (spi_global_register, dbus_message_get_path (message)));
  DBusMessageIter iter;
  MatchRulePrivate rule;
  dbus_uint32_t sortby;
  dbus_int32_t count;
  dbus_bool_t traverse;
1
  GList *ls = NULL;
  const char *signature;
1
  signature = dbus_message_get_signature (message);
1
  if (strcmp (signature, "(aiia{ss}iaiiasib)uib") != 0)
    {
      return droute_invalid_arguments_error (message);
    }
1
  dbus_message_iter_init (message, &iter);
1
  if (!read_mr (&iter, &rule))
    {
      return spi_dbus_general_error (message);
    }
1
  dbus_message_iter_get_basic (&iter, &sortby);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &count);
1
  dbus_message_iter_next (&iter);
1
  dbus_message_iter_get_basic (&iter, &traverse);
1
  dbus_message_iter_next (&iter);
1
  ls = g_list_prepend (ls, obj);
1
  count = query_exec (&rule, sortby, ls, 0, count,
                      obj, 0, TRUE, NULL, TRUE, traverse);
1
  ls = g_list_remove (ls, ls->data);
1
  if (sortby == ATSPI_Collection_SORT_ORDER_REVERSE_CANONICAL)
    ls = g_list_reverse (ls);
1
  free_mrp_data (&rule);
1
  return return_and_free_list (message, ls);
}
static DRouteMethod methods[] = {
  { impl_GetMatchesFrom, "GetMatchesFrom" },
  { impl_GetMatchesTo, "GetMatchesTo" },
  { impl_GetTree, "GetTree" },
  { impl_GetMatches, "GetMatches" },
  { NULL, NULL }
};
void
161
spi_initialize_collection (DRoutePath *path)
{
161
  spi_atk_add_interface (path,
                         ATSPI_DBUS_INTERFACE_COLLECTION, spi_org_a11y_atspi_Collection, methods, NULL);
161
};