~ubuntu-branches/ubuntu/vivid/gnome-desktop3/vivid-proposed

« back to all changes in this revision

Viewing changes to libgnome-desktop/gnome-xkb-info.c

  • Committer: Package Import Robot
  • Author(s): Tim Lunn
  • Date: 2013-05-28 09:10:46 UTC
  • mfrom: (1.6.1) (21.1.10 experimental)
  • Revision ID: package-import@ubuntu.com-20130528091046-b0oc28za9l97fgq1
Tags: 3.8.2-0ubuntu1
* New upstream release
* Sync with Debian (LP: #1184812) Remaining changes:
  - debian/patches:
    + 04_compute_average_color.patch: Compute the avergage color in
      gnome-desktop itself, not in unity to fix some races (LP #963140)
    + tweak_color_computation.patch, Patch from Gord, no patch header,
      no bug link.
    + git_revert_draw_background.patch
    + ubuntu_language.patch, Ported relevant bits from g-c-c 
      52_region_language.patch, as required for gnome 3.8 region panel
    + ubuntu_language_list_from_SUPPORTED.patch,
      adds api to get list of available languages from SUPPORTED file.
      To be used by gnome 3.8 region panel language installation.
  - debian/control.in:
    + Don't break gnome-shell << 3.7.90
    + Use source:Version for gnome-desktop3-data Depend
    + Add epoch to gnome-desktop3-data's Breaks/Replaces, as our old
      gnome-desktop source package introduced an epoch. This needs to be
      kept until after 14.04 LTS.
 - Install helper tools into a versioned directory (by overriding
   libexecdir). They could alternatively be installed in a separate package
* Dropped changes:
  - 02_refuse_to_break_GL_compositors.patch:
    + Doesn't appear to be needed any more
 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2012 Red Hat, Inc.
 
3
 *
 
4
 * Written by: Rui Matos <rmatos@redhat.com>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2, or (at your option)
 
9
 * any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
19
 * 02110-1301, USA.
 
20
 */
 
21
 
 
22
#include <config.h>
 
23
 
 
24
#include <stdio.h>
 
25
#include <stdlib.h>
 
26
#include <string.h>
 
27
 
 
28
#include <X11/XKBlib.h>
 
29
#include <X11/extensions/XKBrules.h>
 
30
 
 
31
#include <gdk/gdkx.h>
 
32
 
 
33
#include <glib/gi18n-lib.h>
 
34
#define XKEYBOARD_CONFIG_(String) ((char *) g_dgettext ("xkeyboard-config", String))
 
35
 
 
36
#define GNOME_DESKTOP_USE_UNSTABLE_API
 
37
#include "gnome-languages.h"
 
38
#include "gnome-xkb-info.h"
 
39
 
 
40
#ifndef XKB_RULES_FILE
 
41
#define XKB_RULES_FILE "evdev"
 
42
#endif
 
43
#ifndef XKB_LAYOUT
 
44
#define XKB_LAYOUT "us"
 
45
#endif
 
46
#ifndef XKB_MODEL
 
47
#define XKB_MODEL "pc105+inet"
 
48
#endif
 
49
 
 
50
typedef struct _Layout Layout;
 
51
struct _Layout
 
52
{
 
53
  gchar *id;
 
54
  gchar *xkb_name;
 
55
  gchar *short_desc;
 
56
  gchar *description;
 
57
  gboolean is_variant;
 
58
  const Layout *main_layout;
 
59
};
 
60
 
 
61
typedef struct _XkbOption XkbOption;
 
62
struct _XkbOption
 
63
{
 
64
  gchar *id;
 
65
  gchar *description;
 
66
};
 
67
 
 
68
typedef struct _XkbOptionGroup XkbOptionGroup;
 
69
struct _XkbOptionGroup
 
70
{
 
71
  gchar *id;
 
72
  gchar *description;
 
73
  gboolean allow_multiple_selection;
 
74
  GHashTable *options_table;
 
75
};
 
76
 
 
77
struct _GnomeXkbInfoPrivate
 
78
{
 
79
  GHashTable *option_groups_table;
 
80
  GHashTable *layouts_by_country;
 
81
  GHashTable *layouts_by_language;
 
82
  GHashTable *layouts_table;
 
83
 
 
84
  /* Only used while parsing */
 
85
  XkbOptionGroup *current_parser_group;
 
86
  XkbOption *current_parser_option;
 
87
  Layout *current_parser_layout;
 
88
  Layout *current_parser_variant;
 
89
  gchar  *current_parser_iso639Id;
 
90
  gchar  *current_parser_iso3166Id;
 
91
  gchar **current_parser_text;
 
92
};
 
93
 
 
94
G_DEFINE_TYPE (GnomeXkbInfo, gnome_xkb_info, G_TYPE_OBJECT);
 
95
 
 
96
static void
 
97
free_layout (gpointer data)
 
98
{
 
99
  Layout *layout = data;
 
100
 
 
101
  g_return_if_fail (layout != NULL);
 
102
 
 
103
  g_free (layout->id);
 
104
  g_free (layout->xkb_name);
 
105
  g_free (layout->short_desc);
 
106
  g_free (layout->description);
 
107
  g_slice_free (Layout, layout);
 
108
}
 
109
 
 
110
static void
 
111
free_option (gpointer data)
 
112
{
 
113
  XkbOption *option = data;
 
114
 
 
115
  g_return_if_fail (option != NULL);
 
116
 
 
117
  g_free (option->id);
 
118
  g_free (option->description);
 
119
  g_slice_free (XkbOption, option);
 
120
}
 
121
 
 
122
static void
 
123
free_option_group (gpointer data)
 
124
{
 
125
  XkbOptionGroup *group = data;
 
126
 
 
127
  g_return_if_fail (group != NULL);
 
128
 
 
129
  g_free (group->id);
 
130
  g_free (group->description);
 
131
  g_hash_table_destroy (group->options_table);
 
132
  g_slice_free (XkbOptionGroup, group);
 
133
}
 
134
 
 
135
/**
 
136
 * gnome_xkb_info_get_var_defs: (skip)
 
137
 * @rules: (out) (transfer full): location to store the rules file
 
138
 * path. Use g_free() when it's no longer needed
 
139
 * @var_defs: (out) (transfer full): location to store a
 
140
 * #XkbRF_VarDefsRec pointer. Use gnome_xkb_info_free_var_defs() to
 
141
 * free it
 
142
 *
 
143
 * Gets both the XKB rules file path and the current XKB parameters in
 
144
 * use by the X server.
 
145
 *
 
146
 * Since: 3.6
 
147
 */
 
148
void
 
149
gnome_xkb_info_get_var_defs (gchar            **rules,
 
150
                             XkbRF_VarDefsRec **var_defs)
 
151
{
 
152
  Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
 
153
  char *tmp;
 
154
 
 
155
  g_return_if_fail (rules != NULL);
 
156
  g_return_if_fail (var_defs != NULL);
 
157
 
 
158
  *rules = NULL;
 
159
  *var_defs = g_new0 (XkbRF_VarDefsRec, 1);
 
160
 
 
161
  gdk_error_trap_push ();
 
162
 
 
163
  /* Get it from the X property or fallback on defaults */
 
164
  if (!XkbRF_GetNamesProp (display, rules, *var_defs) || !*rules)
 
165
    {
 
166
      *rules = strdup (XKB_RULES_FILE);
 
167
      (*var_defs)->model = strdup (XKB_MODEL);
 
168
      (*var_defs)->layout = strdup (XKB_LAYOUT);
 
169
      (*var_defs)->variant = NULL;
 
170
      (*var_defs)->options = NULL;
 
171
    }
 
172
 
 
173
  gdk_error_trap_pop_ignored ();
 
174
 
 
175
  tmp = *rules;
 
176
 
 
177
  if (*rules[0] == '/')
 
178
    *rules = g_strdup (*rules);
 
179
  else
 
180
    *rules = g_build_filename (XKB_BASE, "rules", *rules, NULL);
 
181
 
 
182
  free (tmp);
 
183
}
 
184
 
 
185
/**
 
186
 * gnome_xkb_info_free_var_defs: (skip)
 
187
 * @var_defs: #XkbRF_VarDefsRec instance to free
 
188
 *
 
189
 * Frees an #XkbRF_VarDefsRec instance allocated by
 
190
 * gnome_xkb_info_get_var_defs().
 
191
 *
 
192
 * Since: 3.6
 
193
 */
 
194
void
 
195
gnome_xkb_info_free_var_defs (XkbRF_VarDefsRec *var_defs)
 
196
{
 
197
  g_return_if_fail (var_defs != NULL);
 
198
 
 
199
  free (var_defs->model);
 
200
  free (var_defs->layout);
 
201
  free (var_defs->variant);
 
202
  free (var_defs->options);
 
203
 
 
204
  g_free (var_defs);
 
205
}
 
206
 
 
207
static gchar *
 
208
get_xml_rules_file_path (const gchar *suffix)
 
209
{
 
210
  XkbRF_VarDefsRec *xkb_var_defs;
 
211
  gchar *rules_file;
 
212
  gchar *xml_rules_file;
 
213
 
 
214
  gnome_xkb_info_get_var_defs (&rules_file, &xkb_var_defs);
 
215
  gnome_xkb_info_free_var_defs (xkb_var_defs);
 
216
 
 
217
  xml_rules_file = g_strdup_printf ("%s%s", rules_file, suffix);
 
218
  g_free (rules_file);
 
219
 
 
220
  return xml_rules_file;
 
221
}
 
222
 
 
223
static void
 
224
parse_start_element (GMarkupParseContext  *context,
 
225
                     const gchar          *element_name,
 
226
                     const gchar         **attribute_names,
 
227
                     const gchar         **attribute_values,
 
228
                     gpointer              data,
 
229
                     GError              **error)
 
230
{
 
231
  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
 
232
 
 
233
  if (priv->current_parser_text)
 
234
    {
 
235
      g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
236
                   "Expected character data but got element '%s'", element_name);
 
237
      return;
 
238
    }
 
239
 
 
240
  if (strcmp (element_name, "name") == 0)
 
241
    {
 
242
      if (priv->current_parser_variant)
 
243
        priv->current_parser_text = &priv->current_parser_variant->xkb_name;
 
244
      else if (priv->current_parser_layout)
 
245
        priv->current_parser_text = &priv->current_parser_layout->xkb_name;
 
246
      else if (priv->current_parser_option)
 
247
        priv->current_parser_text = &priv->current_parser_option->id;
 
248
      else if (priv->current_parser_group)
 
249
        priv->current_parser_text = &priv->current_parser_group->id;
 
250
    }
 
251
  else if (strcmp (element_name, "description") == 0)
 
252
    {
 
253
      if (priv->current_parser_variant)
 
254
        priv->current_parser_text = &priv->current_parser_variant->description;
 
255
      else if (priv->current_parser_layout)
 
256
        priv->current_parser_text = &priv->current_parser_layout->description;
 
257
      else if (priv->current_parser_option)
 
258
        priv->current_parser_text = &priv->current_parser_option->description;
 
259
      else if (priv->current_parser_group)
 
260
        priv->current_parser_text = &priv->current_parser_group->description;
 
261
    }
 
262
  else if (strcmp (element_name, "shortDescription") == 0)
 
263
    {
 
264
      if (priv->current_parser_variant)
 
265
        priv->current_parser_text = &priv->current_parser_variant->short_desc;
 
266
      else if (priv->current_parser_layout)
 
267
        priv->current_parser_text = &priv->current_parser_layout->short_desc;
 
268
    }
 
269
  else if (strcmp (element_name, "iso639Id") == 0)
 
270
    {
 
271
      priv->current_parser_text = &priv->current_parser_iso639Id;
 
272
    }
 
273
  else if (strcmp (element_name, "iso3166Id") == 0)
 
274
    {
 
275
      priv->current_parser_text = &priv->current_parser_iso3166Id;
 
276
    }
 
277
  else if (strcmp (element_name, "layout") == 0)
 
278
    {
 
279
      if (priv->current_parser_layout)
 
280
        {
 
281
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
282
                       "'layout' elements can't be nested");
 
283
          return;
 
284
        }
 
285
 
 
286
      priv->current_parser_layout = g_slice_new0 (Layout);
 
287
    }
 
288
  else if (strcmp (element_name, "variant") == 0)
 
289
    {
 
290
      Layout *layout;
 
291
 
 
292
      if (priv->current_parser_variant)
 
293
        {
 
294
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
295
                       "'variant' elements can't be nested");
 
296
          return;
 
297
        }
 
298
 
 
299
      if (!priv->current_parser_layout)
 
300
        {
 
301
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
302
                       "'variant' elements must be inside 'layout' elements");
 
303
          return;
 
304
        }
 
305
 
 
306
      if (!priv->current_parser_layout->xkb_name)
 
307
        {
 
308
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
309
                       "'variant' elements must be inside named 'layout' elements");
 
310
          return;
 
311
        }
 
312
 
 
313
      layout = g_hash_table_lookup (priv->layouts_table, priv->current_parser_layout->xkb_name);
 
314
      if (!layout)
 
315
        layout = priv->current_parser_layout;
 
316
 
 
317
      priv->current_parser_variant = g_slice_new0 (Layout);
 
318
      priv->current_parser_variant->is_variant = TRUE;
 
319
      priv->current_parser_variant->main_layout = layout;
 
320
    }
 
321
  else if (strcmp (element_name, "group") == 0)
 
322
    {
 
323
      if (priv->current_parser_group)
 
324
        {
 
325
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
326
                       "'group' elements can't be nested");
 
327
          return;
 
328
        }
 
329
 
 
330
      priv->current_parser_group = g_slice_new0 (XkbOptionGroup);
 
331
      /* Maps option ids to XkbOption structs. Owns the XkbOption structs. */
 
332
      priv->current_parser_group->options_table = g_hash_table_new_full (g_str_hash, g_str_equal,
 
333
                                                                         NULL, free_option);
 
334
      g_markup_collect_attributes (element_name,
 
335
                                   attribute_names,
 
336
                                   attribute_values,
 
337
                                   error,
 
338
                                   G_MARKUP_COLLECT_BOOLEAN | G_MARKUP_COLLECT_OPTIONAL,
 
339
                                   "allowMultipleSelection",
 
340
                                   &priv->current_parser_group->allow_multiple_selection,
 
341
                                   G_MARKUP_COLLECT_INVALID);
 
342
    }
 
343
  else if (strcmp (element_name, "option") == 0)
 
344
    {
 
345
      if (priv->current_parser_option)
 
346
        {
 
347
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
348
                       "'option' elements can't be nested");
 
349
          return;
 
350
        }
 
351
 
 
352
      if (!priv->current_parser_group)
 
353
        {
 
354
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
355
                       "'option' elements must be inside 'group' elements");
 
356
          return;
 
357
        }
 
358
 
 
359
      priv->current_parser_option = g_slice_new0 (XkbOption);
 
360
    }
 
361
}
 
362
 
 
363
static void
 
364
add_layout_to_table (GHashTable  *table,
 
365
                     const gchar *key,
 
366
                     Layout      *layout)
 
367
{
 
368
  GHashTable *set;
 
369
 
 
370
  if (!layout->xkb_name)
 
371
    return;
 
372
 
 
373
  set = g_hash_table_lookup (table, key);
 
374
  if (!set)
 
375
    {
 
376
      set = g_hash_table_new (g_str_hash, g_str_equal);
 
377
      g_hash_table_replace (table, g_strdup (key), set);
 
378
    }
 
379
  else
 
380
    {
 
381
      if (g_hash_table_contains (set, layout->xkb_name))
 
382
        return;
 
383
    }
 
384
  g_hash_table_replace (set, layout->xkb_name, layout);
 
385
}
 
386
 
 
387
static void
 
388
parse_end_element (GMarkupParseContext  *context,
 
389
                   const gchar          *element_name,
 
390
                   gpointer              data,
 
391
                   GError              **error)
 
392
{
 
393
  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
 
394
 
 
395
  if (strcmp (element_name, "layout") == 0)
 
396
    {
 
397
      if (!priv->current_parser_layout->description || !priv->current_parser_layout->xkb_name)
 
398
        {
 
399
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
400
                       "'layout' elements must enclose 'description' and 'name' elements");
 
401
          return;
 
402
        }
 
403
 
 
404
      priv->current_parser_layout->id = g_strdup (priv->current_parser_layout->xkb_name);
 
405
 
 
406
      if (g_hash_table_contains (priv->layouts_table, priv->current_parser_layout->id))
 
407
        {
 
408
          g_clear_pointer (&priv->current_parser_layout, free_layout);
 
409
          return;
 
410
        }
 
411
 
 
412
      g_hash_table_replace (priv->layouts_table,
 
413
                            priv->current_parser_layout->id,
 
414
                            priv->current_parser_layout);
 
415
      priv->current_parser_layout = NULL;
 
416
    }
 
417
  else if (strcmp (element_name, "variant") == 0)
 
418
    {
 
419
      if (!priv->current_parser_variant->description || !priv->current_parser_variant->xkb_name)
 
420
        {
 
421
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
422
                       "'variant' elements must enclose 'description' and 'name' elements");
 
423
          return;
 
424
        }
 
425
 
 
426
      priv->current_parser_variant->id = g_strjoin ("+",
 
427
                                                    priv->current_parser_layout->xkb_name,
 
428
                                                    priv->current_parser_variant->xkb_name,
 
429
                                                    NULL);
 
430
 
 
431
      g_hash_table_replace (priv->layouts_table,
 
432
                            priv->current_parser_variant->id,
 
433
                            priv->current_parser_variant);
 
434
      priv->current_parser_variant = NULL;
 
435
    }
 
436
  else if (strcmp (element_name, "iso639Id") == 0)
 
437
    {
 
438
      gchar *language;
 
439
 
 
440
      if (!priv->current_parser_iso639Id)
 
441
        {
 
442
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
443
                       "'iso639Id' elements must enclose text");
 
444
          return;
 
445
        }
 
446
 
 
447
      language = gnome_get_language_from_code (priv->current_parser_iso639Id, NULL);
 
448
      if (language)
 
449
        {
 
450
          if (priv->current_parser_variant)
 
451
            add_layout_to_table (priv->layouts_by_language, language, priv->current_parser_variant);
 
452
          else if (priv->current_parser_layout)
 
453
            add_layout_to_table (priv->layouts_by_language, language, priv->current_parser_layout);
 
454
 
 
455
          g_free (language);
 
456
        }
 
457
 
 
458
      g_clear_pointer (&priv->current_parser_iso639Id, g_free);
 
459
    }
 
460
  else if (strcmp (element_name, "iso3166Id") == 0)
 
461
    {
 
462
      gchar *country;
 
463
 
 
464
      if (!priv->current_parser_iso3166Id)
 
465
        {
 
466
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
467
                       "'iso3166Id' elements must enclose text");
 
468
          return;
 
469
        }
 
470
 
 
471
      country = gnome_get_country_from_code (priv->current_parser_iso3166Id, NULL);
 
472
      if (country)
 
473
        {
 
474
          if (priv->current_parser_variant)
 
475
            add_layout_to_table (priv->layouts_by_country, country, priv->current_parser_variant);
 
476
          else if (priv->current_parser_layout)
 
477
            add_layout_to_table (priv->layouts_by_country, country, priv->current_parser_layout);
 
478
 
 
479
          g_free (country);
 
480
        }
 
481
 
 
482
      g_clear_pointer (&priv->current_parser_iso3166Id, g_free);
 
483
    }
 
484
  else if (strcmp (element_name, "group") == 0)
 
485
    {
 
486
      if (!priv->current_parser_group->description || !priv->current_parser_group->id)
 
487
        {
 
488
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
489
                       "'group' elements must enclose 'description' and 'name' elements");
 
490
          return;
 
491
        }
 
492
 
 
493
      g_hash_table_replace (priv->option_groups_table,
 
494
                            priv->current_parser_group->id,
 
495
                            priv->current_parser_group);
 
496
      priv->current_parser_group = NULL;
 
497
    }
 
498
  else if (strcmp (element_name, "option") == 0)
 
499
    {
 
500
      if (!priv->current_parser_option->description || !priv->current_parser_option->id)
 
501
        {
 
502
          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 
503
                       "'option' elements must enclose 'description' and 'name' elements");
 
504
          return;
 
505
        }
 
506
 
 
507
      g_hash_table_replace (priv->current_parser_group->options_table,
 
508
                            priv->current_parser_option->id,
 
509
                            priv->current_parser_option);
 
510
      priv->current_parser_option = NULL;
 
511
    }
 
512
}
 
513
 
 
514
static void
 
515
parse_text (GMarkupParseContext  *context,
 
516
            const gchar          *text,
 
517
            gsize                 text_len,
 
518
            gpointer              data,
 
519
            GError              **error)
 
520
{
 
521
  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
 
522
 
 
523
  if (priv->current_parser_text)
 
524
    {
 
525
      *priv->current_parser_text = g_strndup (text, text_len);
 
526
      priv->current_parser_text = NULL;
 
527
    }
 
528
}
 
529
 
 
530
static void
 
531
parse_error (GMarkupParseContext *context,
 
532
             GError              *error,
 
533
             gpointer             data)
 
534
{
 
535
  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
 
536
 
 
537
  free_option_group (priv->current_parser_group);
 
538
  free_option (priv->current_parser_option);
 
539
  free_layout (priv->current_parser_layout);
 
540
  free_layout (priv->current_parser_variant);
 
541
  g_free (priv->current_parser_iso639Id);
 
542
  g_free (priv->current_parser_iso3166Id);
 
543
}
 
544
 
 
545
static const GMarkupParser markup_parser = {
 
546
  parse_start_element,
 
547
  parse_end_element,
 
548
  parse_text,
 
549
  NULL,
 
550
  parse_error
 
551
};
 
552
 
 
553
static void
 
554
parse_rules_file (GnomeXkbInfo  *self,
 
555
                  const gchar   *path,
 
556
                  GError       **error)
 
557
{
 
558
  gchar *buffer;
 
559
  gsize length;
 
560
  GMarkupParseContext *context;
 
561
  GError *sub_error = NULL;
 
562
 
 
563
  g_file_get_contents (path, &buffer, &length, &sub_error);
 
564
  if (sub_error)
 
565
    {
 
566
      g_propagate_error (error, sub_error);
 
567
      return;
 
568
    }
 
569
 
 
570
  context = g_markup_parse_context_new (&markup_parser, 0, self, NULL);
 
571
  g_markup_parse_context_parse (context, buffer, length, &sub_error);
 
572
  g_markup_parse_context_free (context);
 
573
  g_free (buffer);
 
574
  if (sub_error)
 
575
    g_propagate_error (error, sub_error);
 
576
}
 
577
 
 
578
static void
 
579
parse_rules (GnomeXkbInfo *self)
 
580
{
 
581
  GnomeXkbInfoPrivate *priv = self->priv;
 
582
  GSettings *settings;
 
583
  gboolean show_all_sources;
 
584
  gchar *file_path;
 
585
  GError *error = NULL;
 
586
 
 
587
  /* Maps option group ids to XkbOptionGroup structs. Owns the
 
588
     XkbOptionGroup structs. */
 
589
  priv->option_groups_table = g_hash_table_new_full (g_str_hash, g_str_equal,
 
590
                                                     NULL, free_option_group);
 
591
  /* Maps country strings to a GHashTable which is a set of Layout
 
592
     struct pointers into the Layout structs stored in
 
593
     layouts_table. */
 
594
  priv->layouts_by_country = g_hash_table_new_full (g_str_hash, g_str_equal,
 
595
                                                    g_free, (GDestroyNotify) g_hash_table_destroy);
 
596
  /* Maps language strings to a GHashTable which is a set of Layout
 
597
     struct pointers into the Layout structs stored in
 
598
     layouts_table. */
 
599
  priv->layouts_by_language = g_hash_table_new_full (g_str_hash, g_str_equal,
 
600
                                                     g_free, (GDestroyNotify) g_hash_table_destroy);
 
601
  /* Maps layout ids to Layout structs. Owns the Layout structs. */
 
602
  priv->layouts_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_layout);
 
603
 
 
604
  file_path = get_xml_rules_file_path (".xml");
 
605
  parse_rules_file (self, file_path, &error);
 
606
  if (error)
 
607
    goto cleanup;
 
608
  g_free (file_path);
 
609
 
 
610
  settings = g_settings_new ("org.gnome.desktop.input-sources");
 
611
  show_all_sources = g_settings_get_boolean (settings, "show-all-sources");
 
612
  g_object_unref (settings);
 
613
 
 
614
  if (!show_all_sources)
 
615
    return;
 
616
 
 
617
  file_path = get_xml_rules_file_path (".extras.xml");
 
618
  parse_rules_file (self, file_path, &error);
 
619
  if (error)
 
620
    goto cleanup;
 
621
  g_free (file_path);
 
622
 
 
623
  return;
 
624
 
 
625
 cleanup:
 
626
  g_warning ("Failed to load XKB rules file %s: %s", file_path, error->message);
 
627
  g_clear_pointer (&error, g_error_free);
 
628
  g_clear_pointer (&file_path, g_free);
 
629
  g_clear_pointer (&priv->option_groups_table, g_hash_table_destroy);
 
630
  g_clear_pointer (&priv->layouts_by_country, g_hash_table_destroy);
 
631
  g_clear_pointer (&priv->layouts_by_language, g_hash_table_destroy);
 
632
  g_clear_pointer (&priv->layouts_table, g_hash_table_destroy);
 
633
}
 
634
 
 
635
static gboolean
 
636
ensure_rules_are_parsed (GnomeXkbInfo *self)
 
637
{
 
638
  GnomeXkbInfoPrivate *priv = self->priv;
 
639
 
 
640
  if (!priv->layouts_table)
 
641
    parse_rules (self);
 
642
 
 
643
  return !!priv->layouts_table;
 
644
}
 
645
 
 
646
static void
 
647
gnome_xkb_info_init (GnomeXkbInfo *self)
 
648
{
 
649
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GNOME_TYPE_XKB_INFO, GnomeXkbInfoPrivate);
 
650
}
 
651
 
 
652
static void
 
653
gnome_xkb_info_finalize (GObject *self)
 
654
{
 
655
  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (self)->priv;
 
656
 
 
657
  if (priv->option_groups_table)
 
658
    g_hash_table_destroy (priv->option_groups_table);
 
659
  if (priv->layouts_by_country)
 
660
    g_hash_table_destroy (priv->layouts_by_country);
 
661
  if (priv->layouts_by_language)
 
662
    g_hash_table_destroy (priv->layouts_by_language);
 
663
  if (priv->layouts_table)
 
664
    g_hash_table_destroy (priv->layouts_table);
 
665
 
 
666
  G_OBJECT_CLASS (gnome_xkb_info_parent_class)->finalize (self);
 
667
}
 
668
 
 
669
static void
 
670
gnome_xkb_info_class_init (GnomeXkbInfoClass *klass)
 
671
{
 
672
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
673
 
 
674
  gobject_class->finalize = gnome_xkb_info_finalize;
 
675
 
 
676
  g_type_class_add_private (gobject_class, sizeof (GnomeXkbInfoPrivate));
 
677
}
 
678
 
 
679
/**
 
680
 * gnome_xkb_info_new:
 
681
 *
 
682
 * Returns: (transfer full): a new #GnomeXkbInfo instance.
 
683
 */
 
684
GnomeXkbInfo *
 
685
gnome_xkb_info_new (void)
 
686
{
 
687
  return g_object_new (GNOME_TYPE_XKB_INFO, NULL);
 
688
}
 
689
 
 
690
/**
 
691
 * gnome_xkb_info_get_all_layouts:
 
692
 * @self: a #GnomeXkbInfo
 
693
 *
 
694
 * Returns a list of all layout identifiers we know about.
 
695
 *
 
696
 * Return value: (transfer container) (element-type utf8): the list
 
697
 * of layout names. The caller takes ownership of the #GList but not
 
698
 * of the strings themselves, those are internally allocated and must
 
699
 * not be modified.
 
700
 *
 
701
 * Since: 3.6
 
702
 */
 
703
GList *
 
704
gnome_xkb_info_get_all_layouts (GnomeXkbInfo *self)
 
705
{
 
706
  GnomeXkbInfoPrivate *priv;
 
707
 
 
708
  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL);
 
709
 
 
710
  priv = self->priv;
 
711
 
 
712
  if (!ensure_rules_are_parsed (self))
 
713
    return NULL;
 
714
 
 
715
  return g_hash_table_get_keys (priv->layouts_table);
 
716
}
 
717
 
 
718
/**
 
719
 * gnome_xkb_info_get_all_option_groups:
 
720
 * @self: a #GnomeXkbInfo
 
721
 *
 
722
 * Returns a list of all option group identifiers we know about.
 
723
 *
 
724
 * Return value: (transfer container) (element-type utf8): the list
 
725
 * of option group ids. The caller takes ownership of the #GList but
 
726
 * not of the strings themselves, those are internally allocated and
 
727
 * must not be modified.
 
728
 *
 
729
 * Since: 3.6
 
730
 */
 
731
GList *
 
732
gnome_xkb_info_get_all_option_groups (GnomeXkbInfo *self)
 
733
{
 
734
  GnomeXkbInfoPrivate *priv;
 
735
 
 
736
  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL);
 
737
 
 
738
  priv = self->priv;
 
739
 
 
740
  if (!ensure_rules_are_parsed (self))
 
741
    return NULL;
 
742
 
 
743
  return g_hash_table_get_keys (priv->option_groups_table);
 
744
}
 
745
 
 
746
/**
 
747
 * gnome_xkb_info_description_for_group:
 
748
 * @self: a #GnomeXkbInfo
 
749
 * @group_id: identifier for group
 
750
 *
 
751
 * Return value: the translated description for the group @group_id.
 
752
 *
 
753
 * Since: 3.8
 
754
 */
 
755
const gchar *
 
756
gnome_xkb_info_description_for_group (GnomeXkbInfo *self,
 
757
                                      const gchar  *group_id)
 
758
{
 
759
  GnomeXkbInfoPrivate *priv;
 
760
  const XkbOptionGroup *group;
 
761
 
 
762
  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL);
 
763
 
 
764
  priv = self->priv;
 
765
 
 
766
  if (!ensure_rules_are_parsed (self))
 
767
    return NULL;
 
768
 
 
769
  group = g_hash_table_lookup (priv->option_groups_table, group_id);
 
770
  if (!group)
 
771
    return NULL;
 
772
 
 
773
  return XKEYBOARD_CONFIG_(group->description);
 
774
}
 
775
 
 
776
/**
 
777
 * gnome_xkb_info_get_options_for_group:
 
778
 * @self: a #GnomeXkbInfo
 
779
 * @group_id: group's identifier about which to retrieve the options
 
780
 *
 
781
 * Returns a list of all option identifiers we know about for group
 
782
 * @group_id.
 
783
 *
 
784
 * Return value: (transfer container) (element-type utf8): the list
 
785
 * of option ids. The caller takes ownership of the #GList but not of
 
786
 * the strings themselves, those are internally allocated and must not
 
787
 * be modified.
 
788
 *
 
789
 * Since: 3.6
 
790
 */
 
791
GList *
 
792
gnome_xkb_info_get_options_for_group (GnomeXkbInfo *self,
 
793
                                      const gchar  *group_id)
 
794
{
 
795
  GnomeXkbInfoPrivate *priv;
 
796
  const XkbOptionGroup *group;
 
797
 
 
798
  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL);
 
799
 
 
800
  priv = self->priv;
 
801
 
 
802
  if (!ensure_rules_are_parsed (self))
 
803
    return NULL;
 
804
 
 
805
  group = g_hash_table_lookup (priv->option_groups_table, group_id);
 
806
  if (!group)
 
807
    return NULL;
 
808
 
 
809
  return g_hash_table_get_keys (group->options_table);
 
810
}
 
811
 
 
812
/**
 
813
 * gnome_xkb_info_description_for_option:
 
814
 * @self: a #GnomeXkbInfo
 
815
 * @group_id: identifier for group containing the option
 
816
 * @id: option identifier
 
817
 *
 
818
 * Return value: the translated description for the option @id.
 
819
 *
 
820
 * Since: 3.6
 
821
 */
 
822
const gchar *
 
823
gnome_xkb_info_description_for_option (GnomeXkbInfo *self,
 
824
                                       const gchar  *group_id,
 
825
                                       const gchar  *id)
 
826
{
 
827
  GnomeXkbInfoPrivate *priv;
 
828
  const XkbOptionGroup *group;
 
829
  const XkbOption *option;
 
830
 
 
831
  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL);
 
832
 
 
833
  priv = self->priv;
 
834
 
 
835
  if (!ensure_rules_are_parsed (self))
 
836
    return NULL;
 
837
 
 
838
  group = g_hash_table_lookup (priv->option_groups_table, group_id);
 
839
  if (!group)
 
840
    return NULL;
 
841
 
 
842
  option = g_hash_table_lookup (group->options_table, id);
 
843
  if (!option)
 
844
    return NULL;
 
845
 
 
846
  return XKEYBOARD_CONFIG_(option->description);
 
847
}
 
848
 
 
849
/**
 
850
 * gnome_xkb_info_get_layout_info:
 
851
 * @self: a #GnomeXkbInfo
 
852
 * @id: layout's identifier about which to retrieve the info
 
853
 * @display_name: (out) (allow-none) (transfer none): location to store
 
854
 * the layout's display name, or %NULL
 
855
 * @short_name: (out) (allow-none) (transfer none): location to store
 
856
 * the layout's short name, or %NULL
 
857
 * @xkb_layout: (out) (allow-none) (transfer none): location to store
 
858
 * the layout's XKB name, or %NULL
 
859
 * @xkb_variant: (out) (allow-none) (transfer none): location to store
 
860
 * the layout's XKB variant, or %NULL
 
861
 *
 
862
 * Retrieves information about a layout. Both @display_name and
 
863
 * @short_name are suitable to show in UIs and might be localized if
 
864
 * translations are available.
 
865
 *
 
866
 * Some layouts don't provide a short name (2 or 3 letters) or don't
 
867
 * specify a XKB variant, in those cases @short_name or @xkb_variant
 
868
 * are empty strings, i.e. "".
 
869
 *
 
870
 * If the given layout doesn't exist the return value is %FALSE and
 
871
 * all the (out) parameters are set to %NULL.
 
872
 *
 
873
 * Return value: %TRUE if the layout exists or %FALSE otherwise.
 
874
 *
 
875
 * Since: 3.6
 
876
 */
 
877
gboolean
 
878
gnome_xkb_info_get_layout_info (GnomeXkbInfo *self,
 
879
                                const gchar  *id,
 
880
                                const gchar **display_name,
 
881
                                const gchar **short_name,
 
882
                                const gchar **xkb_layout,
 
883
                                const gchar **xkb_variant)
 
884
{
 
885
  GnomeXkbInfoPrivate *priv;
 
886
  const Layout *layout;
 
887
 
 
888
  if (display_name)
 
889
    *display_name = NULL;
 
890
  if (short_name)
 
891
    *short_name = NULL;
 
892
  if (xkb_layout)
 
893
    *xkb_layout = NULL;
 
894
  if (xkb_variant)
 
895
    *xkb_variant = NULL;
 
896
 
 
897
  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), FALSE);
 
898
 
 
899
  priv = self->priv;
 
900
 
 
901
  if (!ensure_rules_are_parsed (self))
 
902
    return FALSE;
 
903
 
 
904
  if (!g_hash_table_lookup_extended (priv->layouts_table, id, NULL, (gpointer *)&layout))
 
905
    return FALSE;
 
906
 
 
907
  if (display_name)
 
908
    *display_name = XKEYBOARD_CONFIG_(layout->description);
 
909
 
 
910
  if (!layout->is_variant)
 
911
    {
 
912
      if (short_name)
 
913
        *short_name = XKEYBOARD_CONFIG_(layout->short_desc ? layout->short_desc : "");
 
914
      if (xkb_layout)
 
915
        *xkb_layout = layout->xkb_name;
 
916
      if (xkb_variant)
 
917
        *xkb_variant = "";
 
918
    }
 
919
  else
 
920
    {
 
921
      if (short_name)
 
922
        *short_name = XKEYBOARD_CONFIG_(layout->short_desc ? layout->short_desc :
 
923
                        layout->main_layout->short_desc ? layout->main_layout->short_desc : "");
 
924
      if (xkb_layout)
 
925
        *xkb_layout = layout->main_layout->xkb_name;
 
926
      if (xkb_variant)
 
927
        *xkb_variant = layout->xkb_name;
 
928
    }
 
929
 
 
930
  return TRUE;
 
931
}
 
932
 
 
933
static void
 
934
collect_layout_ids (gpointer key,
 
935
                    gpointer value,
 
936
                    gpointer data)
 
937
{
 
938
  Layout *layout = value;
 
939
  GList **list = data;
 
940
 
 
941
  *list = g_list_prepend (*list, layout->id);
 
942
}
 
943
 
 
944
/**
 
945
 * gnome_xkb_info_get_layouts_for_language:
 
946
 * @self: a #GnomeXkbInfo
 
947
 * @language_code: an ISO 639 code string
 
948
 *
 
949
 * Returns a list of all layout identifiers we know about for
 
950
 * @language_code.
 
951
 *
 
952
 * Return value: (transfer container) (element-type utf8): the list
 
953
 * of layout ids. The caller takes ownership of the #GList but not of
 
954
 * the strings themselves, those are internally allocated and must not
 
955
 * be modified.
 
956
 *
 
957
 * Since: 3.8
 
958
 */
 
959
GList *
 
960
gnome_xkb_info_get_layouts_for_language (GnomeXkbInfo *self,
 
961
                                         const gchar  *language_code)
 
962
{
 
963
  GnomeXkbInfoPrivate *priv;
 
964
  GHashTable *layouts_for_language;
 
965
  gchar *language;
 
966
  GList *list;
 
967
 
 
968
  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL);
 
969
 
 
970
  priv = self->priv;
 
971
 
 
972
  if (!ensure_rules_are_parsed (self))
 
973
    return NULL;
 
974
 
 
975
  language = gnome_get_language_from_code (language_code, NULL);
 
976
 
 
977
  layouts_for_language = g_hash_table_lookup (priv->layouts_by_language, language);
 
978
  if (!layouts_for_language)
 
979
    return NULL;
 
980
 
 
981
  list = NULL;
 
982
  g_hash_table_foreach (layouts_for_language, collect_layout_ids, &list);
 
983
 
 
984
  return list;
 
985
}
 
986
 
 
987
/**
 
988
 * gnome_xkb_info_get_layouts_for_country:
 
989
 * @self: a #GnomeXkbInfo
 
990
 * @country_code: an ISO 3166 code string
 
991
 *
 
992
 * Returns a list of all layout identifiers we know about for
 
993
 * @country_code.
 
994
 *
 
995
 * Return value: (transfer container) (element-type utf8): the list
 
996
 * of layout ids. The caller takes ownership of the #GList but not of
 
997
 * the strings themselves, those are internally allocated and must not
 
998
 * be modified.
 
999
 *
 
1000
 * Since: 3.8
 
1001
 */
 
1002
GList *
 
1003
gnome_xkb_info_get_layouts_for_country (GnomeXkbInfo *self,
 
1004
                                        const gchar  *country_code)
 
1005
{
 
1006
  GnomeXkbInfoPrivate *priv;
 
1007
  GHashTable *layouts_for_country;
 
1008
  gchar *country;
 
1009
  GList *list;
 
1010
 
 
1011
  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL);
 
1012
 
 
1013
  priv = self->priv;
 
1014
 
 
1015
  if (!ensure_rules_are_parsed (self))
 
1016
    return NULL;
 
1017
 
 
1018
  country = gnome_get_country_from_code (country_code, NULL);
 
1019
 
 
1020
  layouts_for_country = g_hash_table_lookup (priv->layouts_by_country, country);
 
1021
  if (!layouts_for_country)
 
1022
    return NULL;
 
1023
 
 
1024
  list = NULL;
 
1025
  g_hash_table_foreach (layouts_for_country, collect_layout_ids, &list);
 
1026
 
 
1027
  return list;
 
1028
}