2
* Copyright Ā© 2012 Canonical Ltd.
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.
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.
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/>.
16
* Authors: Pete Woods <pete.woods@canonical.com>
19
#define G_LOG_DOMAIN "hudkeywordmapping"
21
#include <glib/gi18n.h>
23
#include "hudkeywordmapping.h"
26
* SECTION:hudkeywordmapping
27
* @title: HudKeywordMapping
28
* @short_description: Provides additional keywords for user actions.
30
* The #HudKeywordMapping service parses an XML file that provides a
31
* mapping for additional keywords that a user action can match on.
33
* It also consults gettext to see if a translation of the form
34
* "hud-keywords:<original action>" -> "keyword 1; keyword 2; keyword 3"
37
* An instance of #HudKeywordMapping is created for each separate
41
struct _HudKeywordMapping
43
GObject parent_instance;
45
/* Hash table storing the keyword mappings */
49
typedef GObjectClass HudKeywordMappingClass;
51
G_DEFINE_TYPE(HudKeywordMapping, hud_keyword_mapping, G_TYPE_OBJECT);
54
hud_keyword_mapping_load_xml(HudKeywordMapping* self, const char* filename);
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);
62
hud_keyword_mapping_end_element (GMarkupParseContext *context,
63
const gchar *element_name, gpointer user_data, GError **error);
66
hud_keyword_mapping_finalize (GObject *object)
68
HudKeywordMapping *self = HUD_KEYWORD_MAPPING(object);
70
g_hash_table_destroy(self->mappings);
72
G_OBJECT_CLASS (hud_keyword_mapping_parent_class)
77
hud_keyword_mapping_class_init (HudKeywordMappingClass *klass)
79
klass->finalize = hud_keyword_mapping_finalize;
83
hud_keyword_mapping_key_destroyed (gpointer data)
85
g_free ((gchar*) data);
89
hud_keyword_mapping_value_destroyed (gpointer data)
91
g_ptr_array_free((GPtrArray*) data, TRUE);
95
hud_keyword_mapping_init (HudKeywordMapping *self)
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);
105
HUD_KEYWORD_IN_KEYWORD_MAPPING,
106
HUD_KEYWORD_IN_MAPPING,
107
HUD_KEYWORD_IN_KEYWORD,
108
HUD_KEYWORD_IN_UNKNOWN
109
} HudKeywordMappingParserState;
113
GHashTable *mappings;
114
HudKeywordMappingParserState state;
115
gchar *original_translated;
117
gint translation_found;
118
} HudKeywordMappingParser;
120
static GMarkupParser hud_keyword_mapping_parser =
121
{ hud_keyword_mapping_start_element, hud_keyword_mapping_end_element,
125
* hud_keyword_mapping_new:
127
* Creates a #HudKeywordMapping.
129
* Returns: a new empty #HudKeywordMapping
132
hud_keyword_mapping_new (void)
134
return g_object_new (HUD_TYPE_KEYWORD_MAPPING, NULL);
138
* hud_keyword_mapping_load:
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".
144
* Translations are performed using the locale information
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.
153
hud_keyword_mapping_load (HudKeywordMapping *self,
154
const gchar *app_id, const gchar *datadir, const gchar *localedir)
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 );
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);
166
/* Do the main job */
167
if (hud_keyword_mapping_load_xml(self, mapping_path) < 0)
169
g_debug("Unable to load %s", mapping_path);
172
g_free(mapping_filename);
173
g_free(mapping_path);
175
/* Restore the original textdomain */
176
textdomain(GETTEXT_PACKAGE);
180
* hud_keyword_mapping_transform:
182
* Consults the table of keywords and provides the list of keywords for given action.
184
* @self: Instance of #HudKeywordMapping.
185
* @label: The label of the action to provide keywords for.
187
* Returns: #GPtrArray of the keywords for the given action. Do not free
191
hud_keyword_mapping_transform(HudKeywordMapping *self, const gchar* label)
195
results = g_hash_table_lookup (self->mappings, label);
199
results = g_ptr_array_new_full(0, g_free);
200
g_hash_table_insert(self->mappings, (gpointer) g_strdup(label), (gpointer) results);
207
* hud_keyword_mapping_load_xml:
208
* @self: The #HudKeywordMapping
209
* @filename: the input XML filename.
211
* Parses input XML file, evaluates XPath expression and loads the keyword hash.
213
* Returns 0 on success and a negative value otherwise.
216
hud_keyword_mapping_load_xml(HudKeywordMapping* self,
217
const gchar* filename)
221
GMarkupParseContext *context;
222
HudKeywordMappingParser parser = {self->mappings, HUD_KEYWORD_START, };
226
if (g_file_get_contents (filename, &text, &length, NULL ) == FALSE)
228
g_debug("Unable to read XML file \"%s\"", filename);
232
context = g_markup_parse_context_new (&hud_keyword_mapping_parser, 0, &parser, NULL );
234
if (g_markup_parse_context_parse (context, text, length, NULL ) == FALSE)
236
g_warning("Unable to parse file \"%s\"", filename);
238
g_markup_parse_context_free (context);
243
g_markup_parse_context_free (context);
249
hud_keyword_mapping_get_attribute_value (const gchar* name,
250
const gchar **attribute_names, const gchar **attribute_values)
252
const gchar **name_cursor = attribute_names;
253
const gchar **value_cursor = attribute_values;
257
if (strcmp (*name_cursor, name) == 0)
258
return g_strdup (*value_cursor);
268
hud_keyword_mapping_start_keyword (HudKeywordMappingParser *parser,
269
const gchar **attribute_names, const gchar **attribute_values)
271
if (!parser->translation_found)
273
gchar* keyword = hud_keyword_mapping_get_attribute_value ("name", attribute_names,
275
/* Check the keyword isn't empty */
276
if(keyword && keyword[0] != '\0')
277
g_ptr_array_add (parser->keywords, keyword);
282
hud_keyword_mapping_start_mapping (HudKeywordMappingParser *parser,
283
const gchar **attribute_names, const gchar **attribute_values)
285
gchar *original, *original_to_lookup, *keywords_translated;
287
original = hud_keyword_mapping_get_attribute_value ("original",
288
attribute_names, attribute_values);
290
parser->original_translated = g_strdup (_((gchar*) original));
291
parser->keywords = g_ptr_array_new_full (8, g_free);
293
/* First try to translate hud-keywords:original. If it comes back without
294
* translation, then read the keywords from the XML file.
296
original_to_lookup = g_strconcat ("hud-keywords:", original, NULL );
297
keywords_translated = gettext (original_to_lookup);
299
parser->translation_found = (original_to_lookup != keywords_translated);
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.
304
if (parser->translation_found)
307
gchar** split = g_strsplit (keywords_translated, ";", 0);
309
/* Go through each of the keywords */
310
for (j = 0; split[j] != NULL ; j++)
312
gchar* keyword = g_strstrip(split[j]);
313
g_ptr_array_add (parser->keywords, (gpointer) g_strdup (keyword));
320
g_free (original_to_lookup);
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)
328
HudKeywordMappingParser *parser = (HudKeywordMappingParser*) user_data;
330
switch (parser->state)
332
case HUD_KEYWORD_START:
333
if (strcmp (element_name, "keywordMapping") == 0)
334
parser->state = HUD_KEYWORD_IN_KEYWORD_MAPPING;
336
parser->state = HUD_KEYWORD_IN_UNKNOWN;
339
case HUD_KEYWORD_IN_KEYWORD_MAPPING:
340
if (strcmp (element_name, "mapping") == 0)
342
parser->state = HUD_KEYWORD_IN_MAPPING;
343
hud_keyword_mapping_start_mapping (parser, attribute_names,
347
parser->state = HUD_KEYWORD_IN_UNKNOWN;
350
case HUD_KEYWORD_IN_MAPPING:
351
if (strcmp (element_name, "keyword") == 0)
353
parser->state = HUD_KEYWORD_IN_KEYWORD;
354
hud_keyword_mapping_start_keyword (parser, attribute_names,
358
parser->state = HUD_KEYWORD_IN_UNKNOWN;
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;
366
case HUD_KEYWORD_IN_UNKNOWN:
367
g_error("Hud keyword parsing failed. In unknown state.");
373
hud_keyword_mapping_end_element (GMarkupParseContext *context, const gchar *element_name,
374
gpointer user_data, GError **error)
376
HudKeywordMappingParser *parser = (HudKeywordMappingParser*) user_data;
378
switch (parser->state)
380
case HUD_KEYWORD_START:
381
g_error("Should not be able to return to start state in keyword parser");
384
case HUD_KEYWORD_IN_KEYWORD_MAPPING:
385
parser->state = HUD_KEYWORD_START;
388
case HUD_KEYWORD_IN_MAPPING:
389
parser->state = HUD_KEYWORD_IN_KEYWORD_MAPPING;
391
/* Build an entry from the collected keywords */
392
g_hash_table_insert (parser->mappings,
393
(gpointer) parser->original_translated, (gpointer) parser->keywords);
396
case HUD_KEYWORD_IN_KEYWORD:
397
parser->state = HUD_KEYWORD_IN_MAPPING;
400
case HUD_KEYWORD_IN_UNKNOWN:
401
g_warning("Hud keyword parsing failed. In unknown state.");