1
/*
2
 * AT-SPI - Assistive Technology Service Provider Interface
3
 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4
 *
5
 * Copyright 2001, 2002 Sun Microsystems Inc.,
6
 * Copyright 2001, 2002 Ximian, Inc.
7
 * Copyright 2010, 2011 Novell, Inc.
8
 *
9
 * This library is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU Lesser General Public
11
 * License as published by the Free Software Foundation; either
12
 * version 2.1 of the License, or (at your option) any later version.
13
 *
14
 * This library is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
 * Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public
20
 * License along with this library; if not, write to the
21
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22
 * Boston, MA 02110-1301, USA.
23
 */
24

            
25
#include "atspi-private.h"
26

            
27
/**
28
 * AtspiMatchRule:
29
 *
30
 * An interface that allows the definition of match rules
31
 * for accessible objects.
32
 */
33

            
34
1
G_DEFINE_TYPE (AtspiMatchRule, atspi_match_rule, G_TYPE_OBJECT)
35

            
36
static void
37
5
atspi_match_rule_init (AtspiMatchRule *match_rule)
38
{
39
5
}
40

            
41
static void
42
5
atspi_match_rule_dispose (GObject *object)
43
{
44
5
  AtspiMatchRule *rule = ATSPI_MATCH_RULE (object);
45

            
46
5
  if (rule->states)
47
    {
48
3
      g_object_unref (rule->states);
49
3
      rule->states = NULL;
50
    }
51

            
52
5
  if (rule->attributes)
53
    {
54
1
      g_hash_table_unref (rule->attributes);
55
1
      rule->attributes = NULL;
56
    }
57

            
58
5
  G_OBJECT_CLASS (atspi_match_rule_parent_class)->dispose (object);
59
5
}
60

            
61
static void
62
5
atspi_match_rule_finalize (GObject *object)
63
{
64
5
  AtspiMatchRule *rule = ATSPI_MATCH_RULE (object);
65
  gint i;
66

            
67
5
  if (rule->interfaces)
68
    {
69
      for (i = 0; i < rule->interfaces->len; i++)
70
        g_free (g_array_index (rule->interfaces, gchar *, i));
71
      g_array_free (rule->interfaces, TRUE);
72
    }
73

            
74
5
  if (rule->attributes)
75
    g_hash_table_unref (rule->attributes);
76

            
77
5
  G_OBJECT_CLASS (atspi_match_rule_parent_class)->finalize (object);
78
5
}
79

            
80
static void
81
1
atspi_match_rule_class_init (AtspiMatchRuleClass *klass)
82
{
83
1
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
84

            
85
1
  object_class->dispose = atspi_match_rule_dispose;
86
1
  object_class->finalize = atspi_match_rule_finalize;
87
1
}
88

            
89
/**
90
 * atspi_match_rule_new:
91
 * @states: An #AtspiStateSet specifying the states to match or NULL if none.
92
 * @statematchtype: An #AtspiCollectionMatchType specifying how to interpret
93
 *          @states.
94
 * @attributes: (element-type gchar* gchar*): A #GHashTable specifying
95
 *          attributes to match. To specify multiple attribute values,
96
 *          separate each value with a :: If an attribute value contains a :,
97
 *          then it can be escaped by preceding it with a \. A backslash can
98
 *          likewise be escaped by inserting a double backslash.
99
 * @attributematchtype: An #AtspiCollectionMatchType specifying how to
100
 *          interpret @attributes.
101
 * @interfaces: (element-type gchar*): An array of interfaces to match, or
102
 *          NULL if not applicable.  Interface names should be specified
103
 *          by the final component of their DBus names (Accessible,
104
 *          Component, etc).
105
 * @interfacematchtype: An #AtspiCollectionMatchType specifying how to
106
 *          interpret @interfaces.
107
 * @roles: (element-type AtspiRole): A #GArray of roles to match, or NULL if
108
 *          not applicable.
109
 * @rolematchtype: An #AtspiCollectionMatchType specifying how to
110
 *          interpret @roles.
111
 * @invert: if #TRUE, the match rule should be denied (inverted); if #FALSE,
112
 *          it should not. For example, if the match rule defines that a match is
113
 *          an object of ROLE_HEADING which has STATE_FOCUSABLE and a click action,
114
 *          inverting it would match all objects that are not of ROLE_HEADING,
115
 *          focusable and clickable at the same time.
116
 *
117
 * Creates a new #AtspiMatchRule with specified @states, @attributes,
118
 * @interfaces, and @roles.
119
 *
120
 * Returns: (transfer full): A new #AtspiMatchRule.
121
 **/
122
AtspiMatchRule *
123
5
atspi_match_rule_new (AtspiStateSet *states,
124
                      AtspiCollectionMatchType statematchtype,
125
                      GHashTable *attributes,
126
                      AtspiCollectionMatchType attributematchtype,
127
                      GArray *roles,
128
                      AtspiCollectionMatchType rolematchtype,
129
                      GArray *interfaces,
130
                      AtspiCollectionMatchType interfacematchtype,
131
                      gboolean invert)
132
{
133
5
  AtspiMatchRule *rule = g_object_new (ATSPI_TYPE_MATCH_RULE, NULL);
134
  int i;
135

            
136
5
  if (states)
137
3
    rule->states = g_object_ref (states);
138
5
  rule->statematchtype = statematchtype;
139

            
140
5
  if (attributes)
141
    {
142
      GHashTableIter hash_table_iter;
143
      gchar *key, *value;
144
1
      rule->attributes = g_hash_table_new_full (g_str_hash, g_str_equal,
145
                                                (GDestroyNotify) g_free,
146
                                                (GDestroyNotify) g_free);
147
1
      g_hash_table_iter_init (&hash_table_iter, attributes);
148
2
      while (g_hash_table_iter_next (&hash_table_iter, (gpointer *) &key,
149
                                     (gpointer *) &value))
150
2
        g_hash_table_insert (rule->attributes, g_strdup (key), g_strdup (value));
151
    }
152
  else
153
4
    rule->attributes = NULL;
154
5
  rule->attributematchtype = attributematchtype;
155

            
156
5
  if (interfaces)
157
    {
158
      rule->interfaces = g_array_new (TRUE, TRUE, sizeof (gchar *));
159
      for (i = 0; i < interfaces->len; i++)
160
        {
161
          gchar *val = g_strdup (g_array_index (interfaces, gchar *, i));
162
          rule->interfaces = g_array_append_val (rule->interfaces, val);
163
        }
164
    }
165
5
  rule->interfacematchtype = interfacematchtype;
166

            
167
5
  if (roles)
168
    {
169
2
      for (i = 0; i < roles->len; i++)
170
        {
171
1
          AtspiRole role = g_array_index (roles, AtspiRole, i);
172
1
          if (role < 128)
173
1
            rule->roles[role / 32] |= (1 << (role % 32));
174
          else
175
            g_warning ("AT-SPI: unexpected role %d\n", role);
176
        }
177
    }
178
  else
179
4
    rule->roles[0] = rule->roles[1] = 0;
180
5
  rule->rolematchtype = rolematchtype;
181

            
182
5
  rule->invert = invert;
183

            
184
5
  return rule;
185
}
186

            
187
static void
188
1
append_entry (gpointer key, gpointer val, gpointer data)
189
{
190
1
  DBusMessageIter *iter = data;
191
  DBusMessageIter iter_entry;
192

            
193
1
  if (!dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY, NULL,
194
                                         &iter_entry))
195
    return;
196
1
  dbus_message_iter_append_basic (&iter_entry, DBUS_TYPE_STRING, &key);
197
1
  dbus_message_iter_append_basic (&iter_entry, DBUS_TYPE_STRING, &val);
198
1
  dbus_message_iter_close_container (iter, &iter_entry);
199
}
200

            
201
gboolean
202
5
_atspi_match_rule_marshal (AtspiMatchRule *rule, DBusMessageIter *iter)
203
{
204
  DBusMessageIter iter_struct, iter_array, iter_dict;
205
  dbus_int32_t states[2];
206
5
  dbus_int32_t d_statematchtype = rule->statematchtype;
207
5
  dbus_int32_t d_attributematchtype = rule->attributematchtype;
208
5
  dbus_int32_t d_interfacematchtype = rule->interfacematchtype;
209
5
  dbus_uint32_t d_rolematchtype = rule->rolematchtype;
210
5
  dbus_bool_t d_invert = rule->invert;
211
  gint i;
212
  dbus_int32_t d_role;
213

            
214
5
  if (!dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL,
215
                                         &iter_struct))
216
    return FALSE;
217

            
218
  /* states */
219
5
  if (rule->states)
220
    {
221
3
      states[0] = rule->states->states & 0xffffffff;
222
3
      states[1] = rule->states->states >> 32;
223
    }
224
  else
225
    {
226
2
      states[0] = states[1] = 0;
227
    }
228
5
  dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "i", &iter_array);
229
5
  dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &states[0]);
230
5
  dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &states[1]);
231
5
  dbus_message_iter_close_container (&iter_struct, &iter_array);
232
5
  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &d_statematchtype);
233

            
234
  /* attributes */
235
5
  if (!dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{ss}",
236
                                         &iter_dict))
237
    return FALSE;
238
5
  if (rule->attributes)
239
1
    g_hash_table_foreach (rule->attributes, append_entry, &iter_dict);
240
5
  dbus_message_iter_close_container (&iter_struct, &iter_dict);
241
5
  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &d_attributematchtype);
242

            
243
5
  if (!dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "i",
244
                                         &iter_array))
245
    return FALSE;
246
5
  d_role = rule->roles[0];
247
5
  dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &d_role);
248
5
  d_role = rule->roles[1];
249
5
  dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &d_role);
250
5
  d_role = rule->roles[2];
251
5
  dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &d_role);
252
5
  d_role = rule->roles[3];
253
5
  dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &d_role);
254
5
  dbus_message_iter_close_container (&iter_struct, &iter_array);
255
5
  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32,
256
                                  &d_rolematchtype);
257

            
258
  /* interfaces */
259
5
  if (!dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s",
260
                                         &iter_array))
261
    return FALSE;
262
5
  if (rule->interfaces)
263
    {
264
      for (i = 0; i < rule->interfaces->len; i++)
265
        {
266
          char *val = g_array_index (rule->interfaces, gchar *, i);
267
          dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_STRING, &val);
268
        }
269
    }
270
5
  dbus_message_iter_close_container (&iter_struct, &iter_array);
271
5
  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &d_interfacematchtype);
272

            
273
5
  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &d_invert);
274

            
275
5
  dbus_message_iter_close_container (iter, &iter_struct);
276
5
  return TRUE;
277
}