/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * tips-parser.c -- Parse the gimp-tips.xml file. * Copyright (C) 2002 Sven Neumann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include "config/config-types.h" #include "config/gimpxmlparser.h" #include "tips-parser.h" #include "gimp-intl.h" typedef enum { TIPS_START, TIPS_IN_TIPS, TIPS_IN_TIP, TIPS_IN_WELCOME, TIPS_IN_THETIP, TIPS_IN_UNKNOWN } TipsParserState; typedef enum { TIPS_LOCALE_NONE, TIPS_LOCALE_MATCH, TIPS_LOCALE_MISMATCH, } TipsParserLocaleState; typedef struct _TipsParser TipsParser; struct _TipsParser { TipsParserState state; TipsParserState last_known_state; const gchar *locale; TipsParserLocaleState locale_state; gint markup_depth; gint unknown_depth; GString *value; GimpTip *current_tip; GList *tips; }; static void tips_parser_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error); static void tips_parser_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error); static void tips_parser_characters (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error); static void tips_parser_start_markup (TipsParser *parser, const gchar *markup_name); static void tips_parser_end_markup (TipsParser *parser, const gchar *markup_name); static void tips_parser_start_unknown (TipsParser *parser); static void tips_parser_end_unknown (TipsParser *parser); static void tips_parser_parse_locale (TipsParser *parser, const gchar **names, const gchar **values); static void tips_parser_set_by_locale (TipsParser *parser, gchar **dest); static const GMarkupParser markup_parser = { tips_parser_start_element, tips_parser_end_element, tips_parser_characters, NULL, /* passthrough */ NULL /* error */ }; GimpTip * gimp_tip_new (const gchar *welcome, const gchar *thetip) { GimpTip *tip = g_new (GimpTip, 1); tip->welcome = welcome ? g_strdup (welcome) : NULL; tip->thetip = thetip ? g_strdup (thetip) : NULL; return tip; } void gimp_tip_free (GimpTip *tip) { if (!tip) return; g_free (tip->welcome); g_free (tip->thetip); g_free (tip); } /** * gimp_tips_from_file: * @filename: the name of the tips file to parse * @error: return location for a #GError * * Reads a gimp-tips XML file, creates a new #GimpTip for * each tip entry and returns a #GList of them. If a parser * error occurs at some point, the uncompleted list is * returned and @error is set (unless @error is %NULL). * The message set in @error contains a detailed description * of the problem. * * Return value: a #Glist of #GimpTips. **/ GList * gimp_tips_from_file (const gchar *filename, GError **error) { GimpXmlParser *xml_parser; TipsParser *parser; const gchar *tips_locale; GList *tips = NULL; g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); parser = g_new0 (TipsParser, 1); parser->value = g_string_new (NULL); /* This is a special string to specify the language identifier to look for in the gimp-tips.xml file. Please translate the C in it according to the name of the po file used for gimp-tips.xml. E.g. for the german translation, that would be "tips-locale:de". */ tips_locale = _("tips-locale:C"); if (strncmp (tips_locale, "tips-locale:", 12) == 0) { tips_locale += 12; if (*tips_locale && *tips_locale != 'C') parser->locale = tips_locale; } else g_warning ("Wrong translation for 'tips-locale:', fix the translation!"); xml_parser = gimp_xml_parser_new (&markup_parser, parser); gimp_xml_parser_parse_file (xml_parser, filename, error); gimp_xml_parser_free (xml_parser); tips = g_list_reverse (parser->tips); gimp_tip_free (parser->current_tip); g_string_free (parser->value, TRUE); g_free (parser); return tips; } void gimp_tips_free (GList *tips) { GList *list; for (list = tips; list; list = list->next) gimp_tip_free (list->data); g_list_free (tips); } static void tips_parser_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error) { TipsParser *parser = (TipsParser *) user_data; switch (parser->state) { case TIPS_START: if (strcmp (element_name, "gimp-tips") == 0) parser->state = TIPS_IN_TIPS; else tips_parser_start_unknown (parser); break; case TIPS_IN_TIPS: if (strcmp (element_name, "tip") == 0) { parser->state = TIPS_IN_TIP; parser->current_tip = g_new0 (GimpTip, 1); } else tips_parser_start_unknown (parser); break; case TIPS_IN_TIP: if (strcmp (element_name, "welcome") == 0) { parser->state = TIPS_IN_WELCOME; tips_parser_parse_locale (parser, attribute_names, attribute_values); } else if (strcmp (element_name, "thetip") == 0) { parser->state = TIPS_IN_THETIP; tips_parser_parse_locale (parser, attribute_names, attribute_values); } else tips_parser_start_unknown (parser); break; case TIPS_IN_WELCOME: case TIPS_IN_THETIP: if (strcmp (element_name, "b" ) == 0 || strcmp (element_name, "big") == 0 || strcmp (element_name, "tt" ) == 0) tips_parser_start_markup (parser, element_name); else tips_parser_start_unknown (parser); break; case TIPS_IN_UNKNOWN: tips_parser_start_unknown (parser); break; } } static void tips_parser_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { TipsParser *parser = (TipsParser *) user_data; switch (parser->state) { case TIPS_START: g_warning ("tips_parser: This shouldn't happen.\n"); break; case TIPS_IN_TIPS: parser->state = TIPS_START; break; case TIPS_IN_TIP: parser->tips = g_list_prepend (parser->tips, parser->current_tip); parser->current_tip = NULL; parser->state = TIPS_IN_TIPS; break; case TIPS_IN_WELCOME: if (parser->markup_depth == 0) { tips_parser_set_by_locale (parser, &parser->current_tip->welcome); g_string_truncate (parser->value, 0); parser->state = TIPS_IN_TIP; } else tips_parser_end_markup (parser, element_name); break; case TIPS_IN_THETIP: if (parser->markup_depth == 0) { tips_parser_set_by_locale (parser, &parser->current_tip->thetip); g_string_truncate (parser->value, 0); parser->state = TIPS_IN_TIP; } else tips_parser_end_markup (parser, element_name); break; case TIPS_IN_UNKNOWN: tips_parser_end_unknown (parser); break; } } static void tips_parser_characters (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { TipsParser *parser = (TipsParser *) user_data; switch (parser->state) { case TIPS_IN_WELCOME: case TIPS_IN_THETIP: if (parser->locale_state != TIPS_LOCALE_MISMATCH) { gint i; /* strip tabs, newlines and adjacent whitespace */ for (i = 0; i < text_len; i++) { if (text[i] != ' ' && text[i] != '\t' && text[i] != '\n' && text[i] != '\r') g_string_append_c (parser->value, text[i]); else if (parser->value->len > 0 && parser->value->str[parser->value->len - 1] != ' ') g_string_append_c (parser->value, ' '); } } break; default: break; } } static void tips_parser_start_markup (TipsParser *parser, const gchar *markup_name) { parser->markup_depth++; g_string_append_printf (parser->value, "<%s>", markup_name); } static void tips_parser_end_markup (TipsParser *parser, const gchar *markup_name) { g_assert (parser->markup_depth > 0); parser->markup_depth--; g_string_append_printf (parser->value, "", markup_name); } static void tips_parser_start_unknown (TipsParser *parser) { if (parser->unknown_depth == 0) parser->last_known_state = parser->state; parser->state = TIPS_IN_UNKNOWN; parser->unknown_depth++; } static void tips_parser_end_unknown (TipsParser *parser) { g_assert (parser->unknown_depth > 0 && parser->state == TIPS_IN_UNKNOWN); parser->unknown_depth--; if (parser->unknown_depth == 0) parser->state = parser->last_known_state; } static void tips_parser_parse_locale (TipsParser *parser, const gchar **names, const gchar **values) { parser->locale_state = TIPS_LOCALE_NONE; while (*names && *values) { if (strcmp (*names, "xml:lang") == 0 && **values) { parser->locale_state = (parser->locale && strcmp (*values, parser->locale) == 0 ? TIPS_LOCALE_MATCH : TIPS_LOCALE_MISMATCH); } names++; values++; } } static void tips_parser_set_by_locale (TipsParser *parser, gchar **dest) { switch (parser->locale_state) { case TIPS_LOCALE_NONE: if (!parser->locale) { g_free (*dest); *dest = g_strdup (parser->value->str); } else if (*dest == NULL) { *dest = g_strdup (parser->value->str); } break; case TIPS_LOCALE_MATCH: g_free (*dest); *dest = g_strdup (parser->value->str); break; case TIPS_LOCALE_MISMATCH: break; } }