Document that multiple attribute values can now be listed in match rules.
[platform/upstream/at-spi2-core.git] / atspi / atspi-matchrule.c
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 Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 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  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include "atspi-private.h"
26
27 G_DEFINE_TYPE (AtspiMatchRule, atspi_match_rule, G_TYPE_OBJECT)
28
29 static void
30 atspi_match_rule_init (AtspiMatchRule *match_rule)
31 {
32 }
33
34 static void
35 atspi_match_rule_dispose (GObject *object)
36 {
37   AtspiMatchRule *rule = ATSPI_MATCH_RULE (object);
38
39   if (rule->states)
40   {
41     g_object_unref (rule->states);
42     rule->states = NULL;
43   }
44
45   if (rule->attributes)
46   {
47     g_hash_table_unref (rule->attributes);
48     rule->attributes = NULL;
49   }
50
51   G_OBJECT_CLASS (atspi_match_rule_parent_class)->dispose (object);
52 }
53
54 static void
55 atspi_match_rule_finalize (GObject *object)
56 {
57   AtspiMatchRule *rule = ATSPI_MATCH_RULE (object);
58
59   /* TODO: Check that interfaces don't leak */
60   if (rule->interfaces)
61     g_array_free (rule->interfaces, TRUE);
62
63   if (rule->attributes)
64     g_hash_table_unref (rule->attributes);
65
66   G_OBJECT_CLASS (atspi_match_rule_parent_class)->finalize (object);
67 }
68
69 static void
70 atspi_match_rule_class_init (AtspiMatchRuleClass *klass)
71 {
72   GObjectClass *object_class = G_OBJECT_CLASS (klass);
73
74   object_class->dispose = atspi_match_rule_dispose;
75   object_class->finalize = atspi_match_rule_finalize;
76 }
77
78 /**
79  * atspi_match_rule_new:
80  * @states: An #AtspiStateSet specifying the states to match or NULL if none.
81  * @statematchtype: An #AtspiCollectionMatchType specifying how to interpret
82  *          @states.
83  * @attributes: (element-type gchar* gchar*): A #GHashTable specifying
84  *          attributes to match. To specify multiple attribute values,
85  *          separate each value with a :: If an attribute value contains a :,
86  *          then it can be escaped by preceding it with a \. A backslash can
87  *          likewise be escaped by inserting a double backslash.
88  * @attributematchtype: An #AtspiCollectionMatchType specifying how to
89  *          interpret @attributes.
90  * @interfaces: (element-type gchar*): An array of interfaces to match, or
91  *          NULL if not applicable.  Interface names should be specified
92  *          by their DBus names (org.a11y.Atspi.Accessible,
93  *          org.a11y.Atspi.Component, etc).
94  * @interfacematchtype: An #AtspiCollectionMatchType specifying how to
95  *          interpret @interfaces.
96  * @roles: (element-type AtspiRole): A #GArray of roles to match, or NULL if
97  *          not applicable.
98  * @rolematchtype: An #AtspiCollectionMatchType specifying how to
99  *          interpret @roles.
100  * @invert: if #TRUE, the match rule should be denied (inverted); if #FALSE,
101  *          it should not. For example, if the match rule defines that a match is
102  *          an object of ROLE_HEADING which has STATE_FOCUSABLE and a click action,
103  *          inverting it would match all objects that are not of ROLE_HEADING,
104  *          focusable and clickable at the same time.
105  *
106  * Creates a new #AtspiMatchRule with specified @states, @attributes, 
107  * @interfaces, and @roles.
108  *
109  * Returns: (transfer full): A new #AtspiMatchRule.
110  **/
111 AtspiMatchRule *
112 atspi_match_rule_new (AtspiStateSet *states,
113                       AtspiCollectionMatchType statematchtype,
114                       GHashTable *attributes,
115                       AtspiCollectionMatchType attributematchtype,
116                       GArray *roles,
117                       AtspiCollectionMatchType rolematchtype,
118                       GArray *interfaces,
119                       AtspiCollectionMatchType interfacematchtype,
120                       gboolean invert)
121 {
122   AtspiMatchRule *rule = g_object_new (ATSPI_TYPE_MATCH_RULE, NULL);
123   int i;
124
125   if (!rule)
126     return NULL;
127
128   if (states)
129     rule->states = g_object_ref (states);
130   rule->statematchtype = statematchtype;
131
132   if (attributes)
133   {
134     GHashTableIter hash_table_iter;
135     gchar *key, *value;
136     rule->attributes = g_hash_table_new_full (g_str_hash, g_str_equal,
137                                               (GDestroyNotify) g_free,
138                                               (GDestroyNotify) g_free);
139     g_hash_table_iter_init (&hash_table_iter, attributes);
140             while (g_hash_table_iter_next (&hash_table_iter, (gpointer *)&key,
141                    (gpointer *)&value))
142       g_hash_table_insert (rule->attributes, g_strdup (key), g_strdup (value));
143   } else
144     rule->attributes = NULL;
145   rule->attributematchtype = attributematchtype;
146
147   if (interfaces)
148     rule->interfaces = g_array_ref (interfaces);
149   rule->interfacematchtype = interfacematchtype;
150
151   if (roles)
152   {
153     for (i = 0; i < roles->len; i++)
154     {
155       AtspiRole role = g_array_index (roles, AtspiRole, i);
156       if (role < 128)
157         rule->roles [role / 32] |= (1 << (role % 32));
158       else
159         g_warning ("Atspi: unexpected role %d\n", role);
160     }
161   }
162   else
163     rule->roles [0] = rule->roles [1] = 0;
164   rule->rolematchtype = rolematchtype;
165
166   rule->invert = invert;
167
168   return rule;
169 }
170
171 static void
172 append_entry (gpointer key, gpointer val, gpointer data)
173 {
174   DBusMessageIter *iter = data;
175   DBusMessageIter iter_entry;
176
177   if (!dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY, NULL,
178                                         &iter_entry))
179     return;
180   dbus_message_iter_append_basic (&iter_entry, DBUS_TYPE_STRING, &key);
181   dbus_message_iter_append_basic (&iter_entry, DBUS_TYPE_STRING, &val);
182   dbus_message_iter_close_container (iter, &iter_entry);
183 }
184
185 gboolean
186 _atspi_match_rule_marshal (AtspiMatchRule *rule, DBusMessageIter *iter)
187 {
188   DBusMessageIter iter_struct, iter_array, iter_dict;
189   dbus_int32_t states [2];
190   dbus_int32_t d_statematchtype = rule->statematchtype;
191   dbus_int32_t d_attributematchtype = rule->attributematchtype;
192   dbus_int32_t d_interfacematchtype = rule->interfacematchtype;
193   dbus_uint32_t d_rolematchtype = rule->rolematchtype;
194   dbus_bool_t d_invert = rule->invert;
195   gint i;
196   dbus_int32_t d_role;
197
198   if (!dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL,
199                                          &iter_struct))
200     return FALSE;
201
202   /* states */
203   if (rule->states)
204   {
205     states [0] = rule->states->states & 0xffffffff;
206     states [1] = rule->states->states >> 32;
207   }
208   else
209   {
210     states [0] = states [1] = 0;
211   }
212   dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "i", &iter_array);
213   dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &states [0]);
214   dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &states [1]);
215   dbus_message_iter_close_container (&iter_struct, &iter_array);
216   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &d_statematchtype);
217
218   /* attributes */
219   if (!dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{ss}",
220                                          &iter_dict))
221     return FALSE;
222   g_hash_table_foreach (rule->attributes, append_entry, &iter_dict);
223   dbus_message_iter_close_container (&iter_struct, &iter_dict);
224   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &d_attributematchtype);
225
226   if (!dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "i",
227       &iter_array))
228     return FALSE;
229   d_role = rule->roles [0];
230   dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &d_role);
231   d_role = rule->roles [1];
232   dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &d_role);
233   d_role = rule->roles [2];
234   dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &d_role);
235   d_role = rule->roles [3];
236   dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_INT32, &d_role);
237   dbus_message_iter_close_container (&iter_struct, &iter_array);
238   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32,
239                                   &d_rolematchtype);
240
241   /* interfaces */
242   if (!dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s",
243       &iter_array))
244     return FALSE;
245   if (rule->interfaces)
246   {
247     for (i = 0; i < rule->interfaces->len; i++)
248     {
249       char *val = g_array_index (rule->interfaces, gchar *, i);
250       dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_STRING, &val);
251     }
252   }
253   dbus_message_iter_close_container (&iter_struct, &iter_array);
254   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &d_interfacematchtype);
255
256   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &d_invert);
257
258   dbus_message_iter_close_container (iter, &iter_struct);
259   return TRUE;
260 }