1
/* The GIMP -- an image manipulation program
2
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
4
* The GIMP Help plug-in
5
* Copyright (C) 1999-2004 Sven Neumann <sven@gimp.org>
6
* Michael Natterer <mitch@gimp.org>
7
* Henrik Brix Andersen <brix@gimp.org>
9
* This program is free software; you can redistribute it and/or modify
10
* it under the terms of the GNU General Public License as published by
11
* the Free Software Foundation; either version 2 of the License, or
12
* (at your option) any later version.
14
* This program 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
17
* GNU General Public License for more details.
19
* You should have received a copy of the GNU General Public License
20
* along with this program; if not, write to the Free Software
21
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
/* This code is written so that it can also be compiled standalone.
25
* It shouldn't depend on libgimp.
38
#define _(String) (String)
40
#include "libgimp/stdplugins-intl.h"
49
GHashTable *help_locales;
52
typedef struct _HelpLocale HelpLocale;
56
GHashTable *help_id_mapping;
61
/* local function prototypes */
63
static HelpDomain * domain_new (const gchar *domain_name,
64
const gchar *domain_uri,
65
const gchar *domain_root);
66
static void domain_free (HelpDomain *domain);
68
static HelpLocale * domain_locale_new (const gchar *locale_id);
69
static void domain_locale_free (HelpLocale *locale);
71
static HelpLocale * domain_locale_lookup (HelpDomain *domain,
72
const gchar *locale_id);
73
static const gchar * domain_locale_map (HelpLocale *locale,
74
const gchar *help_id);
76
static gboolean domain_locale_parse (HelpDomain *domain,
80
static void domain_error_set_message (GError **error,
82
const gchar *filename);
84
static gchar * filename_from_uri (const gchar *uri);
87
/* private variables */
89
static GHashTable *domain_hash = NULL;
92
/* public functions */
95
domain_register (const gchar *domain_name,
96
const gchar *domain_uri,
97
const gchar *domain_root)
99
g_return_if_fail (domain_name != NULL);
100
g_return_if_fail (domain_uri != NULL);
102
#ifdef GIMP_HELP_DEBUG
103
g_printerr ("help: registering help domain \"%s\" with base uri \"%s\"\n",
104
domain_name, domain_uri);
108
domain_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
109
g_free, (GDestroyNotify) domain_free);
111
g_hash_table_insert (domain_hash,
112
g_strdup (domain_name),
113
domain_new (domain_name, domain_uri, domain_root));
117
domain_lookup (const gchar *domain_name)
119
g_return_val_if_fail (domain_name, NULL);
122
return g_hash_table_lookup (domain_hash, domain_name);
128
domain_map (HelpDomain *domain,
130
const gchar *help_id)
132
HelpLocale *locale = NULL;
133
const gchar *ref = NULL;
136
g_return_val_if_fail (domain != NULL, NULL);
137
g_return_val_if_fail (help_locales != NULL, NULL);
138
g_return_val_if_fail (help_id != NULL, NULL);
140
/* first pass: look for a reference matching the help_id */
141
for (list = help_locales; list && !ref; list = list->next)
143
locale = domain_locale_lookup (domain, (const gchar *) list->data);
144
ref = domain_locale_map (locale, help_id);
147
/* second pass: look for a fallback */
148
for (list = help_locales; list && !ref; list = list->next)
150
locale = domain_locale_lookup (domain, (const gchar *) list->data);
151
ref = locale->help_missing;
156
return g_strconcat (domain->help_uri, "/",
157
locale->locale_id, "/",
161
else /* try to assemble a useful error message */
163
GError *error = NULL;
165
#ifdef GIMP_HELP_DEBUG
166
g_printerr ("help: help_id lookup and all fallbacks failed for '%s'\n",
170
locale = domain_locale_lookup (domain, GIMP_HELP_DEFAULT_LOCALE);
172
if (! domain_locale_parse (domain, locale, &error))
176
if (error->code == G_FILE_ERROR_NOENT)
177
msg = _("The GIMP help files are not installed.");
179
msg = _("There is a problem with the GIMP help files.");
181
g_message ("%s\n\n%s\n\n%s",
184
_("Please check your installation."));
186
g_error_free (error);
192
g_message (_("Help ID '%s' unknown"), help_id);
204
g_hash_table_destroy (domain_hash);
210
/* private functions */
213
domain_new (const gchar *domain_name,
214
const gchar *domain_uri,
215
const gchar *domain_root)
217
HelpDomain *domain = g_new0 (HelpDomain, 1);
219
domain->help_domain = g_strdup (domain_name);
220
domain->help_uri = g_strdup (domain_uri);
221
domain->help_root = g_strdup (domain_root);
225
/* strip trailing slash */
226
gint len = strlen (domain_uri);
229
domain_uri[len - 1] == '/')
230
domain->help_uri[len - 1] = '\0';
237
domain_free (HelpDomain *domain)
239
g_return_if_fail (domain != NULL);
241
if (domain->help_locales)
242
g_hash_table_destroy (domain->help_locales);
244
g_free (domain->help_domain);
245
g_free (domain->help_uri);
246
g_free (domain->help_root);
253
domain_locale_new (const gchar *locale_id)
255
HelpLocale *locale = g_new0 (HelpLocale, 1);
257
locale->locale_id = g_strdup (locale_id);
263
domain_locale_free (HelpLocale *locale)
265
if (locale->help_id_mapping)
266
g_hash_table_destroy (locale->help_id_mapping);
268
g_free (locale->locale_id);
269
g_free (locale->help_missing);
273
domain_locale_lookup (HelpDomain *domain,
274
const gchar *locale_id)
276
HelpLocale *locale = NULL;
278
if (domain->help_locales)
279
locale = g_hash_table_lookup (domain->help_locales, locale_id);
281
domain->help_locales =
282
g_hash_table_new_full (g_str_hash, g_str_equal,
284
(GDestroyNotify) domain_locale_free);
289
locale = domain_locale_new (locale_id);
290
g_hash_table_insert (domain->help_locales, g_strdup (locale_id), locale);
292
domain_locale_parse (domain, locale, NULL);
298
domain_locale_map (HelpLocale *locale,
299
const gchar *help_id)
301
if (! locale->help_id_mapping)
304
return g_hash_table_lookup (locale->help_id_mapping, help_id);
308
/* the domain mapping parser */
321
const gchar *filename;
322
DomainParserState state;
323
DomainParserState last_known_state;
333
static gboolean domain_parser_parse (GMarkupParseContext *context,
336
static void domain_parser_start_element (GMarkupParseContext *context,
337
const gchar *element_name,
338
const gchar **attribute_names,
339
const gchar **attribute_values,
342
static void domain_parser_end_element (GMarkupParseContext *context,
343
const gchar *element_name,
346
static void domain_parser_error (GMarkupParseContext *context,
349
static void domain_parser_start_unknown (DomainParser *parser);
350
static void domain_parser_end_unknown (DomainParser *parser);
351
static void domain_parser_parse_namespace (DomainParser *parser,
353
const gchar **values);
354
static void domain_parser_parse_item (DomainParser *parser,
356
const gchar **values);
357
static void domain_parser_parse_missing (DomainParser *parser,
359
const gchar **values);
361
static const GMarkupParser markup_parser =
363
domain_parser_start_element,
364
domain_parser_end_element,
365
NULL, /* characters */
366
NULL, /* passthrough */
371
domain_locale_parse (HelpDomain *domain,
375
GMarkupParseContext *context;
376
DomainParser *parser;
381
g_return_val_if_fail (domain != NULL, FALSE);
382
g_return_val_if_fail (locale != NULL, FALSE);
383
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
385
if (locale->help_id_mapping)
387
g_hash_table_destroy (locale->help_id_mapping);
388
locale->help_id_mapping = NULL;
390
if (locale->help_missing)
392
g_free (locale->help_missing);
393
locale->help_missing = NULL;
396
if (! domain->help_root)
397
domain->help_root = filename_from_uri (domain->help_uri);
399
if (! domain->help_root)
401
g_set_error (error, 0, 0,
402
"Cannot determine location of gimp-help.xml from '%s'",
407
filename = g_build_filename (domain->help_root,
412
#ifdef GIMP_HELP_DEBUG
413
g_printerr ("help (%s): parsing '%s' for domain \"%s\"\n",
416
domain->help_domain);
419
io = g_io_channel_new_file (filename, "r", error);
422
domain_error_set_message (error,
423
_("Could not open '%s' for reading: %s"),
429
parser = g_new0 (DomainParser, 1);
431
parser->filename = filename;
432
parser->value = g_string_new (NULL);
433
parser->id_attr_name = g_strdup ("id");
434
parser->domain = domain;
435
parser->locale = locale;
437
context = g_markup_parse_context_new (&markup_parser, 0, parser, NULL);
439
success = domain_parser_parse (context, io, error);
441
g_markup_parse_context_free (context);
442
g_io_channel_unref (io);
444
g_string_free (parser->value, TRUE);
445
g_free (parser->id_attr_name);
449
domain_error_set_message (error, _("Parse error in '%s':\n%s"), filename);
457
domain_parser_parse (GMarkupParseContext *context,
467
status = g_io_channel_read_chars (io,
468
buffer, sizeof (buffer), &len, error);
472
case G_IO_STATUS_ERROR:
474
case G_IO_STATUS_EOF:
475
return g_markup_parse_context_end_parse (context, error);
476
case G_IO_STATUS_NORMAL:
477
if (! g_markup_parse_context_parse (context, buffer, len, error))
480
case G_IO_STATUS_AGAIN:
489
domain_parser_start_element (GMarkupParseContext *context,
490
const gchar *element_name,
491
const gchar **attribute_names,
492
const gchar **attribute_values,
496
DomainParser *parser = (DomainParser *) user_data;
498
switch (parser->state)
501
if (strcmp (element_name, "gimp-help") == 0)
503
parser->state = DOMAIN_IN_HELP;
504
domain_parser_parse_namespace (parser,
505
attribute_names, attribute_values);
508
domain_parser_start_unknown (parser);
512
if (strcmp (element_name, "help-item") == 0)
514
parser->state = DOMAIN_IN_ITEM;
515
domain_parser_parse_item (parser,
516
attribute_names, attribute_values);
518
else if (strcmp (element_name, "help-missing") == 0)
520
parser->state = DOMAIN_IN_MISSING;
521
domain_parser_parse_missing (parser,
522
attribute_names, attribute_values);
525
domain_parser_start_unknown (parser);
529
case DOMAIN_IN_MISSING:
530
case DOMAIN_IN_UNKNOWN:
531
domain_parser_start_unknown (parser);
537
domain_parser_end_element (GMarkupParseContext *context,
538
const gchar *element_name,
542
DomainParser *parser = (DomainParser *) user_data;
544
switch (parser->state)
547
g_warning ("domain_parser: This shouldn't happen.");
551
parser->state = DOMAIN_START;
555
case DOMAIN_IN_MISSING:
556
parser->state = DOMAIN_IN_HELP;
559
case DOMAIN_IN_UNKNOWN:
560
domain_parser_end_unknown (parser);
566
domain_parser_error (GMarkupParseContext *context,
570
DomainParser *parser = (DomainParser *) user_data;
572
g_printerr ("help (parsing %s): %s", parser->filename, error->message);
576
domain_parser_start_unknown (DomainParser *parser)
578
if (parser->unknown_depth == 0)
579
parser->last_known_state = parser->state;
581
parser->state = DOMAIN_IN_UNKNOWN;
582
parser->unknown_depth++;
586
domain_parser_end_unknown (DomainParser *parser)
588
g_assert (parser->unknown_depth > 0 && parser->state == DOMAIN_IN_UNKNOWN);
590
parser->unknown_depth--;
592
if (parser->unknown_depth == 0)
593
parser->state = parser->last_known_state;
597
domain_parser_parse_namespace (DomainParser *parser,
599
const gchar **values)
601
for (; *names && *values; names++, values++)
603
if (! strncmp (*names, "xmlns:", 6) &&
604
! strcmp (*values, parser->domain->help_domain))
606
g_free (parser->id_attr_name);
607
parser->id_attr_name = g_strdup_printf ("%s:id", *names + 6);
609
#ifdef GIMP_HELP_DEBUG
610
g_printerr ("help (%s): id attribute name for \"%s\" is \"%s\"\n",
611
parser->locale->locale_id,
612
parser->domain->help_domain,
613
parser->id_attr_name);
620
domain_parser_parse_item (DomainParser *parser,
622
const gchar **values)
624
const gchar *id = NULL;
625
const gchar *ref = NULL;
627
for (; *names && *values; names++, values++)
629
if (! strcmp (*names, parser->id_attr_name))
632
if (! strcmp (*names, "ref"))
638
if (! parser->locale->help_id_mapping)
639
parser->locale->help_id_mapping = g_hash_table_new_full (g_str_hash,
644
g_hash_table_insert (parser->locale->help_id_mapping,
645
g_strdup (id), g_strdup (ref));
647
#ifdef GIMP_HELP_DEBUG
648
g_printerr ("help (%s): added mapping \"%s\" -> \"%s\"\n",
649
parser->locale->locale_id, id, ref);
655
domain_parser_parse_missing (DomainParser *parser,
657
const gchar **values)
659
const gchar *ref = NULL;
661
for (; *names && *values; names++, values++)
663
if (! strcmp (*names, "ref"))
668
parser->locale->help_missing == NULL)
670
parser->locale->help_missing = g_strdup (ref);
672
#ifdef GIMP_HELP_DEBUG
673
g_printerr ("help (%s): added fallback for missing help -> \"%s\"\n",
674
parser->locale->locale_id, ref);
680
domain_error_set_message (GError **error,
682
const gchar *filename)
686
gchar *name = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
687
gchar *msg = g_strdup_printf (format, name, (*error)->message);
691
g_free ((*error)->message);
692
(*error)->message = msg;
697
filename_from_uri (const gchar *uri)
702
g_return_val_if_fail (uri != NULL, NULL);
704
filename = g_filename_from_uri (uri, &hostname, NULL);
711
/* we have a file: URI with a hostname */
713
/* on Win32, create a valid UNC path and use it as the filename */
715
gchar *tmp = g_build_filename ("//", hostname, filename, NULL);
720
/* otherwise return NULL, caller should use URI then */