diff options
Diffstat (limited to 'app/dialogs/tips-parser.c')
-rw-r--r-- | app/dialogs/tips-parser.c | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/app/dialogs/tips-parser.c b/app/dialogs/tips-parser.c new file mode 100644 index 0000000..9e94f0f --- /dev/null +++ b/app/dialogs/tips-parser.c @@ -0,0 +1,477 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * tips-parser.c - Parse the gimp-tips.xml file. + * Copyright (C) 2002, 2008 Sven Neumann <sven@gimp.org> + * + * 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 3 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, see <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <string.h> + +#include <gio/gio.h> + +#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_THETIP, + TIPS_IN_UNKNOWN +} TipsParserState; + +typedef enum +{ + TIPS_LOCALE_NONE, + TIPS_LOCALE_MATCH, + TIPS_LOCALE_MISMATCH +} TipsParserLocaleState; + +typedef struct +{ + TipsParserState state; + TipsParserState last_known_state; + const gchar *locale; + const gchar *help_id; + TipsParserLocaleState locale_state; + gint markup_depth; + gint unknown_depth; + GString *value; + GimpTip *current_tip; + GList *tips; +} TipsParser; + + +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 gchar * tips_parser_parse_help_id (TipsParser *parser, + const gchar **names, + const gchar **values); + +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 *title, + const gchar *format, + ...) +{ + GimpTip *tip = g_slice_new0 (GimpTip); + GString *str = g_string_new (NULL); + + if (title) + { + g_string_append (str, "<b>"); + g_string_append (str, title); + g_string_append (str, "</b>"); + + if (format) + g_string_append (str, "\n\n"); + } + + if (format) + { + va_list args; + + va_start (args, format); + g_string_append_vprintf (str, format, args); + va_end (args); + } + + tip->text = g_string_free (str, FALSE); + + return tip; +} + +void +gimp_tip_free (GimpTip *tip) +{ + if (! tip) + return; + + g_free (tip->text); + g_free (tip->help_id); + + g_slice_free (GimpTip, tip); +} + +/** + * gimp_tips_from_file: + * @file: 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 (GFile *file, + GError **error) +{ + GimpXmlParser *xml_parser; + TipsParser parser = { 0, }; + const gchar *tips_locale; + GList *tips = NULL; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + 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 (g_str_has_prefix (tips_locale, "tips-locale:")) + { + tips_locale += strlen ("tips-locale:"); + + 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_gfile (xml_parser, file, 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); + + 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 = 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_slice_new0 (GimpTip); + parser->current_tip->help_id = tips_parser_parse_help_id (parser, + attribute_names, + attribute_values); + } + else + { + tips_parser_start_unknown (parser); + } + break; + + case TIPS_IN_TIP: + 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_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 = user_data; + + switch (parser->state) + { + case TIPS_START: + g_warning ("%s: shouldn't get here", G_STRLOC); + 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_THETIP: + if (parser->markup_depth == 0) + { + tips_parser_set_by_locale (parser, &parser->current_tip->text); + 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 = user_data; + + switch (parser->state) + { + 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) +{ + gimp_assert (parser->markup_depth > 0); + + parser->markup_depth--; + g_string_append_printf (parser->value, "</%s>", 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) +{ + gimp_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 gchar * +tips_parser_parse_help_id (TipsParser *parser, + const gchar **names, + const gchar **values) +{ + while (*names && *values) + { + if (strcmp (*names, "help") == 0 && **values) + return g_strdup (*values); + + names++; + values++; + } + + return NULL; +} + +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; + } +} + |