~ubuntu-branches/ubuntu/saucy/hud/saucy-proposed

« back to all changes in this revision

Viewing changes to src/hudkeywordmapping.c

  • Committer: Package Import Robot
  • Author(s): Ubuntu daily release
  • Date: 2013-06-05 12:33:44 UTC
  • mto: This revision was merged to the branch mainline in revision 5.
  • Revision ID: package-import@ubuntu.com-20130605123344-cpp4to647tyfv7kr
Tags: upstream-13.10.1daily13.06.05.1
ImportĀ upstreamĀ versionĀ 13.10.1daily13.06.05.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright Ā© 2012 Canonical Ltd.
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify it
 
5
 * under the terms of the GNU General Public License version 3, as
 
6
 * published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful, but
 
9
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 
10
 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
11
 * PURPOSE.  See the GNU General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License along
 
14
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authors: Pete Woods <pete.woods@canonical.com>
 
17
 */
 
18
 
 
19
#define G_LOG_DOMAIN "hudkeywordmapping"
 
20
 
 
21
#include <glib/gi18n.h>
 
22
 
 
23
#include "hudkeywordmapping.h"
 
24
 
 
25
/**
 
26
 * SECTION:hudkeywordmapping
 
27
 * @title: HudKeywordMapping
 
28
 * @short_description: Provides additional keywords for user actions.
 
29
 *
 
30
 * The #HudKeywordMapping service parses an XML file that provides a
 
31
 * mapping for additional keywords that a user action can match on.
 
32
 *
 
33
 * It also consults gettext to see if a translation of the form
 
34
 * "hud-keywords:<original action>" -> "keyword 1; keyword 2; keyword 3"
 
35
 * exists.
 
36
 *
 
37
 * An instance of #HudKeywordMapping is created for each separate
 
38
 * application.
 
39
 **/
 
40
 
 
41
struct _HudKeywordMapping
 
42
{
 
43
  GObject parent_instance;
 
44
 
 
45
  /* Hash table storing the keyword mappings */
 
46
  GHashTable *mappings;
 
47
};
 
48
 
 
49
typedef GObjectClass HudKeywordMappingClass;
 
50
 
 
51
G_DEFINE_TYPE(HudKeywordMapping, hud_keyword_mapping, G_TYPE_OBJECT);
 
52
 
 
53
static gint
 
54
hud_keyword_mapping_load_xml(HudKeywordMapping* self, const char* filename);
 
55
 
 
56
static void
 
57
hud_keyword_mapping_start_element (GMarkupParseContext *context,
 
58
    const gchar *element_name, const gchar **attribute_names,
 
59
    const gchar **attribute_values, gpointer user_data, GError **error);
 
60
 
 
61
static void
 
62
hud_keyword_mapping_end_element (GMarkupParseContext *context,
 
63
    const gchar *element_name, gpointer user_data, GError **error);
 
64
 
 
65
static void
 
66
hud_keyword_mapping_finalize (GObject *object)
 
67
{
 
68
  HudKeywordMapping *self = HUD_KEYWORD_MAPPING(object);
 
69
 
 
70
  g_hash_table_destroy(self->mappings);
 
71
 
 
72
  G_OBJECT_CLASS (hud_keyword_mapping_parent_class)
 
73
    ->finalize (object);
 
74
}
 
75
 
 
76
static void
 
77
hud_keyword_mapping_class_init (HudKeywordMappingClass *klass)
 
78
{
 
79
  klass->finalize = hud_keyword_mapping_finalize;
 
80
}
 
81
 
 
82
static void
 
83
hud_keyword_mapping_key_destroyed (gpointer data)
 
84
{
 
85
  g_free ((gchar*) data);
 
86
}
 
87
 
 
88
static void
 
89
hud_keyword_mapping_value_destroyed (gpointer data)
 
90
{
 
91
  g_ptr_array_free((GPtrArray*) data, TRUE);
 
92
}
 
93
 
 
94
static void
 
95
hud_keyword_mapping_init (HudKeywordMapping *self)
 
96
{
 
97
  self->mappings = g_hash_table_new_full (g_str_hash, g_str_equal,
 
98
      (GDestroyNotify) hud_keyword_mapping_key_destroyed,
 
99
      (GDestroyNotify) hud_keyword_mapping_value_destroyed);
 
100
}
 
101
 
 
102
typedef enum
 
103
{
 
104
  HUD_KEYWORD_START,
 
105
  HUD_KEYWORD_IN_KEYWORD_MAPPING,
 
106
  HUD_KEYWORD_IN_MAPPING,
 
107
  HUD_KEYWORD_IN_KEYWORD,
 
108
  HUD_KEYWORD_IN_UNKNOWN
 
109
} HudKeywordMappingParserState;
 
110
 
 
111
typedef struct
 
112
{
 
113
  GHashTable *mappings;
 
114
  HudKeywordMappingParserState state;
 
115
  gchar *original_translated;
 
116
  GPtrArray *keywords;
 
117
  gint translation_found;
 
118
} HudKeywordMappingParser;
 
119
 
 
120
static GMarkupParser hud_keyword_mapping_parser =
 
121
{ hud_keyword_mapping_start_element, hud_keyword_mapping_end_element,
 
122
    NULL, NULL, NULL };
 
123
 
 
124
/**
 
125
 * hud_keyword_mapping_new:
 
126
 *
 
127
 * Creates a #HudKeywordMapping.
 
128
 *
 
129
 * Returns: a new empty #HudKeywordMapping
 
130
 **/
 
131
HudKeywordMapping *
 
132
hud_keyword_mapping_new (void)
 
133
{
 
134
  return g_object_new (HUD_TYPE_KEYWORD_MAPPING, NULL);
 
135
}
 
136
 
 
137
/**
 
138
 * hud_keyword_mapping_load:
 
139
 *
 
140
 * Loads keyword mappings from the specified XML file for the given
 
141
 * application. The XML file is searched for in the path
 
142
 * "<datadir>/hud/keywords".
 
143
 *
 
144
 * Translations are performed using the locale information
 
145
 * provided.
 
146
 *
 
147
 * @self: Instance of #HudKeywordMapping
 
148
 * @app_id: Used to determine the identity of the application the mappings are for
 
149
 * @datadir: The base directory that HUD data is stored in.
 
150
 * @localedir: The directory to load the gettext locale from.
 
151
 */
 
152
void
 
153
hud_keyword_mapping_load (HudKeywordMapping *self,
 
154
    const gchar *app_id, const gchar *datadir, const gchar *localedir)
 
155
{
 
156
  /* Now expand the label into multiple keywords */
 
157
  gchar *mapping_filename = g_strdup_printf("%s.xml", app_id);
 
158
  gchar *mapping_path = g_build_filename(datadir, "hud", "keywords",
 
159
      mapping_filename, NULL );
 
160
 
 
161
  /* Set the textdomain to match the program we're translating */
 
162
  g_debug("Setting textdomain = (\"%s\", \"%s\")", app_id, localedir);
 
163
  bindtextdomain (app_id, localedir);
 
164
  textdomain (app_id);
 
165
 
 
166
  /* Do the main job */
 
167
  if (hud_keyword_mapping_load_xml(self, mapping_path) < 0)
 
168
  {
 
169
    g_debug("Unable to load %s", mapping_path);
 
170
  }
 
171
 
 
172
  g_free(mapping_filename);
 
173
  g_free(mapping_path);
 
174
 
 
175
  /* Restore the original textdomain */
 
176
  textdomain(GETTEXT_PACKAGE);
 
177
}
 
178
 
 
179
/*
 
180
 * hud_keyword_mapping_transform:
 
181
 *
 
182
 * Consults the table of keywords and provides the list of keywords for given action.
 
183
 *
 
184
 * @self: Instance of #HudKeywordMapping.
 
185
 * @label: The label of the action to provide keywords for.
 
186
 *
 
187
 * Returns: #GPtrArray of the keywords for the given action. Do not free
 
188
 * this result.
 
189
 */
 
190
GPtrArray*
 
191
hud_keyword_mapping_transform(HudKeywordMapping *self, const gchar* label)
 
192
{
 
193
  GPtrArray* results;
 
194
 
 
195
  results = g_hash_table_lookup (self->mappings, label);
 
196
 
 
197
  if (!results)
 
198
  {
 
199
    results = g_ptr_array_new_full(0, g_free);
 
200
    g_hash_table_insert(self->mappings, (gpointer) g_strdup(label), (gpointer) results);
 
201
  }
 
202
 
 
203
  return results;
 
204
}
 
205
 
 
206
/**
 
207
 * hud_keyword_mapping_load_xml:
 
208
 * @self: The #HudKeywordMapping
 
209
 * @filename:   the input XML filename.
 
210
 *
 
211
 * Parses input XML file, evaluates XPath expression and loads the keyword hash.
 
212
 *
 
213
 * Returns 0 on success and a negative value otherwise.
 
214
 */
 
215
static gint
 
216
hud_keyword_mapping_load_xml(HudKeywordMapping* self,
 
217
    const gchar* filename)
 
218
{
 
219
  gchar *text;
 
220
  gsize length;
 
221
  GMarkupParseContext *context;
 
222
  HudKeywordMappingParser parser = {self->mappings, HUD_KEYWORD_START, };
 
223
 
 
224
  g_assert(filename);
 
225
 
 
226
  if (g_file_get_contents (filename, &text, &length, NULL ) == FALSE)
 
227
  {
 
228
    g_debug("Unable to read XML file \"%s\"", filename);
 
229
    return (-1);
 
230
  }
 
231
 
 
232
  context = g_markup_parse_context_new (&hud_keyword_mapping_parser, 0, &parser, NULL );
 
233
 
 
234
  if (g_markup_parse_context_parse (context, text, length, NULL ) == FALSE)
 
235
  {
 
236
    g_warning("Unable to parse file \"%s\"", filename);
 
237
    g_free (text);
 
238
    g_markup_parse_context_free (context);
 
239
    return (-1);
 
240
  }
 
241
 
 
242
  g_free (text);
 
243
  g_markup_parse_context_free (context);
 
244
 
 
245
  return (0);
 
246
}
 
247
 
 
248
static gchar*
 
249
hud_keyword_mapping_get_attribute_value (const gchar* name,
 
250
    const gchar **attribute_names, const gchar **attribute_values)
 
251
{
 
252
  const gchar **name_cursor = attribute_names;
 
253
  const gchar **value_cursor = attribute_values;
 
254
 
 
255
  while (*name_cursor)
 
256
  {
 
257
    if (strcmp (*name_cursor, name) == 0)
 
258
      return g_strdup (*value_cursor);
 
259
 
 
260
    name_cursor++;
 
261
    value_cursor++;
 
262
  }
 
263
 
 
264
  return NULL;
 
265
}
 
266
 
 
267
static void
 
268
hud_keyword_mapping_start_keyword (HudKeywordMappingParser *parser,
 
269
    const gchar **attribute_names, const gchar **attribute_values)
 
270
{
 
271
  if (!parser->translation_found)
 
272
  {
 
273
    gchar* keyword = hud_keyword_mapping_get_attribute_value ("name", attribute_names,
 
274
                attribute_values);
 
275
    /* Check the keyword isn't empty */
 
276
    if(keyword && keyword[0] != '\0')
 
277
      g_ptr_array_add (parser->keywords, keyword);
 
278
  }
 
279
}
 
280
 
 
281
static void
 
282
hud_keyword_mapping_start_mapping (HudKeywordMappingParser *parser,
 
283
    const gchar **attribute_names, const gchar **attribute_values)
 
284
{
 
285
  gchar *original, *original_to_lookup, *keywords_translated;
 
286
 
 
287
  original = hud_keyword_mapping_get_attribute_value ("original",
 
288
      attribute_names, attribute_values);
 
289
 
 
290
  parser->original_translated = g_strdup (_((gchar*) original));
 
291
  parser->keywords = g_ptr_array_new_full (8, g_free);
 
292
 
 
293
  /* First try to translate hud-keywords:original. If it comes back without
 
294
   * translation, then read the keywords from the XML file.
 
295
   */
 
296
  original_to_lookup = g_strconcat ("hud-keywords:", original, NULL );
 
297
  keywords_translated = gettext (original_to_lookup);
 
298
 
 
299
  parser->translation_found = (original_to_lookup != keywords_translated);
 
300
 
 
301
  /* If we find a translation we can parse it here. Otherwise we'll have to wait
 
302
   * for the XML parser to move across each of our child elements.
 
303
   */
 
304
  if (parser->translation_found)
 
305
  {
 
306
    gint j;
 
307
    gchar** split = g_strsplit (keywords_translated, ";", 0);
 
308
 
 
309
    /* Go through each of the keywords */
 
310
    for (j = 0; split[j] != NULL ; j++)
 
311
    {
 
312
      gchar* keyword = g_strstrip(split[j]);
 
313
      g_ptr_array_add (parser->keywords, (gpointer) g_strdup (keyword));
 
314
    }
 
315
 
 
316
    g_strfreev (split);
 
317
  }
 
318
 
 
319
  g_free (original);
 
320
  g_free (original_to_lookup);
 
321
}
 
322
 
 
323
static void
 
324
hud_keyword_mapping_start_element (GMarkupParseContext *context, const gchar *element_name,
 
325
    const gchar **attribute_names, const gchar **attribute_values,
 
326
    gpointer user_data, GError **error)
 
327
{
 
328
  HudKeywordMappingParser *parser = (HudKeywordMappingParser*) user_data;
 
329
 
 
330
  switch (parser->state)
 
331
  {
 
332
  case HUD_KEYWORD_START:
 
333
    if (strcmp (element_name, "keywordMapping") == 0)
 
334
      parser->state = HUD_KEYWORD_IN_KEYWORD_MAPPING;
 
335
    else
 
336
      parser->state = HUD_KEYWORD_IN_UNKNOWN;
 
337
    break;
 
338
 
 
339
  case HUD_KEYWORD_IN_KEYWORD_MAPPING:
 
340
    if (strcmp (element_name, "mapping") == 0)
 
341
    {
 
342
      parser->state = HUD_KEYWORD_IN_MAPPING;
 
343
      hud_keyword_mapping_start_mapping (parser, attribute_names,
 
344
          attribute_values);
 
345
    }
 
346
    else
 
347
      parser->state = HUD_KEYWORD_IN_UNKNOWN;
 
348
    break;
 
349
 
 
350
  case HUD_KEYWORD_IN_MAPPING:
 
351
    if (strcmp (element_name, "keyword") == 0)
 
352
    {
 
353
      parser->state = HUD_KEYWORD_IN_KEYWORD;
 
354
      hud_keyword_mapping_start_keyword (parser, attribute_names,
 
355
                attribute_values);
 
356
    }
 
357
    else
 
358
      parser->state = HUD_KEYWORD_IN_UNKNOWN;
 
359
    break;
 
360
 
 
361
  case HUD_KEYWORD_IN_KEYWORD:
 
362
    g_warning("Shouldn't be entering any child elements from a keyword element.");
 
363
    parser->state = HUD_KEYWORD_IN_UNKNOWN;
 
364
    break;
 
365
 
 
366
  case HUD_KEYWORD_IN_UNKNOWN:
 
367
    g_error("Hud keyword parsing failed. In unknown state.");
 
368
    break;
 
369
  }
 
370
}
 
371
 
 
372
static void
 
373
hud_keyword_mapping_end_element (GMarkupParseContext *context, const gchar *element_name,
 
374
    gpointer user_data, GError **error)
 
375
{
 
376
  HudKeywordMappingParser *parser = (HudKeywordMappingParser*) user_data;
 
377
 
 
378
  switch (parser->state)
 
379
  {
 
380
  case HUD_KEYWORD_START:
 
381
    g_error("Should not be able to return to start state in keyword parser");
 
382
    break;
 
383
 
 
384
  case HUD_KEYWORD_IN_KEYWORD_MAPPING:
 
385
    parser->state = HUD_KEYWORD_START;
 
386
    break;
 
387
 
 
388
  case HUD_KEYWORD_IN_MAPPING:
 
389
    parser->state = HUD_KEYWORD_IN_KEYWORD_MAPPING;
 
390
 
 
391
    /* Build an entry from the collected keywords */
 
392
    g_hash_table_insert (parser->mappings,
 
393
        (gpointer) parser->original_translated, (gpointer) parser->keywords);
 
394
    break;
 
395
 
 
396
  case HUD_KEYWORD_IN_KEYWORD:
 
397
    parser->state = HUD_KEYWORD_IN_MAPPING;
 
398
    break;
 
399
 
 
400
  case HUD_KEYWORD_IN_UNKNOWN:
 
401
    g_warning("Hud keyword parsing failed. In unknown state.");
 
402
    break;
 
403
  }
 
404
}