diff --git a/Makefile.am b/Makefile.am index 3962b6152..c768d0cec 100644 --- a/Makefile.am +++ b/Makefile.am @@ -126,13 +126,14 @@ awesome_SOURCES = \ common/xutil.c common/xutil.h \ common/configopts.h common/configopts.c \ common/xscreen.h common/xscreen.c \ + common/draw.c common/draw.h \ + common/markup.c common/markup.h \ common/list.h \ structs.h \ client.c client.h \ titlebar.c titlebar.h \ placement.c placement.h \ focus.c focus.h \ - common/draw.c common/draw.h \ event.c event.h \ layout.c layout.h \ awesome.c awesome.h \ @@ -165,6 +166,7 @@ bin_PROGRAMS += awesome-message awesome_message_SOURCES = \ common/swindow.c common/swindow.h \ common/draw.c common/draw.h \ + common/markup.c common/markup.h \ common/util.h common/util.c \ common/version.h common/version.c \ common/configopts.h common/configopts.c \ @@ -178,6 +180,7 @@ bin_PROGRAMS += awesome-menu awesome_menu_SOURCES = \ common/swindow.c common/swindow.h \ common/draw.c common/draw.h \ + common/markup.c common/markup.h \ common/util.h common/util.c \ common/version.h common/version.c \ common/configopts.h common/configopts.c \ diff --git a/common/draw.c b/common/draw.c index 26da718c4..5036ed917 100644 --- a/common/draw.c +++ b/common/draw.c @@ -44,6 +44,7 @@ #include "common/draw.h" #include "common/util.h" #include "common/xutil.h" +#include "common/markup.h" /** Convert text from any charset to UTF-8 using iconv * \param iso the ISO string to convert @@ -223,117 +224,31 @@ typedef struct xcolor_t bg_color; } draw_parser_data_t; -static void -draw_text_markup_parse_start_element(GMarkupParseContext *context __attribute__ ((unused)), - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - gpointer user_data, - GError **error __attribute__ ((unused))) -{ - draw_parser_data_t *p = (draw_parser_data_t *) user_data; - char *newtext; - int i = 0; - ssize_t len; - - if(!a_strcmp(element_name, "bg")) - { - for(i = 0; attribute_names[i]; i++) - if(!p->has_bg_color && !a_strcmp(attribute_names[i], "color")) - p->has_bg_color = draw_color_new(p->connection, p->phys_screen, - attribute_values[i], &p->bg_color); - } - else if(a_strcmp(element_name, "markup")) - { - if(!(len = asprintf(&newtext, "%s<%s ", NONULL(p->text), element_name))) - return; - len++; /* add \0 that asprintf does not return in len */ - for(i = 0; attribute_names[i]; i++) - { - len += a_strlen(attribute_names[i]) + a_strlen(attribute_values[i]) + 5; - p_realloc(&newtext, len); - a_strcat(newtext, len, attribute_names[i]); - a_strcat(newtext, len, "=\""); - a_strcat(newtext, len, attribute_values[i]); - a_strcat(newtext, len, "\" "); - } - p_realloc(&newtext, ++len); - a_strcat(newtext, len, ">"); - p_delete(&p->text); - p->text = newtext; - } -} - -static void -draw_text_markup_parse_end_element(GMarkupParseContext *context __attribute__ ((unused)), - const gchar *element_name, - gpointer user_data, - GError **error __attribute__ ((unused))) -{ - draw_parser_data_t *p = (draw_parser_data_t *) user_data; - char *newtext; - - if(a_strcmp(element_name, "markup") - && a_strcmp(element_name, "bg")) - { - asprintf(&newtext, "%s", p->text, element_name); - p_delete(&p->text); - p->text = newtext; - } -} - -static void -draw_text_markup_parse_text(GMarkupParseContext *context __attribute__ ((unused)), - const gchar *text, - gsize text_len, - gpointer user_data, - GError **error __attribute__ ((unused))) -{ - draw_parser_data_t *p = (draw_parser_data_t *) user_data; - ssize_t len, rlen; - - len = a_strlen(p->text); - rlen = len + 1 + text_len; - p_realloc(&p->text, rlen); - if(len) - a_strncat(p->text, rlen, text, len); - else - a_strncpy(p->text, rlen, text, text_len); -} - static bool draw_text_markup_expand(draw_parser_data_t *data, const char *str, ssize_t slen) { - GMarkupParseContext *mkp_ctx; - GMarkupParser parser = - { - /* start_element */ - draw_text_markup_parse_start_element, - /* end_element */ - draw_text_markup_parse_end_element, - /* text */ - draw_text_markup_parse_text, - /* passthrough */ - NULL, - /* error */ - NULL - }; - GError *error = NULL; + const char *elements[] = { "bg", NULL}; + markup_parser_data_t *p; + int i; - mkp_ctx = g_markup_parse_context_new(&parser, 0, data, NULL); + p = markup_parser_data_new(elements, countof(elements)); - if(!g_markup_parse_context_parse(mkp_ctx, "", -1, &error) - || !g_markup_parse_context_parse(mkp_ctx, str, slen, &error) - || !g_markup_parse_context_parse(mkp_ctx, "", -1, &error) - || !g_markup_parse_context_end_parse(mkp_ctx, &error)) - { - warn("unable to parse text: %s\n", error->message); - g_error_free(error); + if(!markup_parse(p, str, slen)) return false; - } - g_markup_parse_context_free(mkp_ctx); + /* bg */ + if(p->attribute_values[0]) + for(i = 0; p->attribute_values[0][i]; i++) + if(!strcmp(p->attribute_values[0][i], "color")) + data->has_bg_color = draw_color_new(data->connection, data->phys_screen, + p->attribute_values[0][i], &data->bg_color); + + /* stole text */ + data->text = p->text; + p->text = NULL; + + markup_parser_data_delete(&p); return true; } @@ -343,11 +258,7 @@ draw_text_markup_expand(draw_parser_data_t *data, * \param area area to draw to * \param align alignment * \param padding padding to add before drawing the text - * \param font font to use * \param text text to draw - * \param enable shadow - * \param fg foreground color - * \param bg background color * \return area_t with width and height are set to what used */ void @@ -373,8 +284,8 @@ draw_text(DrawCtx *ctx, len = a_strlen(utf8); else utf8 = a_strdup(text); - - bzero(&parser_data, sizeof(parser_data)); + + p_clear(&parser_data, 1); parser_data.connection = ctx->connection; parser_data.phys_screen = ctx->phys_screen; if(draw_text_markup_expand(&parser_data, utf8, len)) @@ -1014,8 +925,8 @@ draw_text_extents(xcb_connection_t *conn, int default_screen, font_t *font, cons len = a_strlen(utf8); else utf8 = a_strdup(text); - - bzero(&parser_data, sizeof(parser_data)); + + p_clear(&parser_data, 1); parser_data.connection = conn; parser_data.phys_screen = default_screen; if(draw_text_markup_expand(&parser_data, utf8, len)) diff --git a/common/markup.c b/common/markup.c new file mode 100644 index 000000000..06c6786b9 --- /dev/null +++ b/common/markup.c @@ -0,0 +1,237 @@ +/* + * markup.c - markup functions + * + * Copyright © 2008 Julien Danjou + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* asprintf */ +#define _GNU_SOURCE + +#include + +#include +#include + +#include "common/markup.h" + +/** Callback to invoke when the opening tag of an element is seen. + * Here is just copies element elements we do not care about, and copies + * values we care. + * \param context the context of GMarkup + * \param element_name element name + * \param attribute_names array of attribute names + * \param attribute_values array of attribute values + * \param user_data pointer to user data, here a markup_parser_data_t pointer + * \param error a GError + */ +static void +markup_parse_start_element(GMarkupParseContext *context __attribute__ ((unused)), + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error __attribute__ ((unused))) +{ + markup_parser_data_t *p = (markup_parser_data_t *) user_data; + char *newtext; + int i, j; + ssize_t len; + + for(i = 0; p->elements[i]; i++) + if(!a_strcmp(element_name, p->elements[i])) + { + for(j = 0; attribute_names[j]; j++); + p->attribute_names[i] = p_new(char *, j); + p->attribute_values[i] = p_new(char *, j); + + for(j = 0; attribute_names[j]; j++) + { + p->attribute_names[i][j] = a_strdup(attribute_names[j]); + p->attribute_values[i][j] = a_strdup(attribute_values[j]); + } + return; + } + + if(a_strcmp(element_name, "markup")) + { + if(!(len = asprintf(&newtext, "%s<%s ", NONULL(p->text), element_name))) + return; + len++; /* add \0 that asprintf does not return in len */ + for(i = 0; attribute_names[i]; i++) + { + len += a_strlen(attribute_names[i]) + a_strlen(attribute_values[i]) + 5; + p_realloc(&newtext, len); + a_strcat(newtext, len, attribute_names[i]); + a_strcat(newtext, len, "=\""); + a_strcat(newtext, len, attribute_values[i]); + a_strcat(newtext, len, "\" "); + } + p_realloc(&newtext, ++len); + a_strcat(newtext, len, ">"); + p_delete(&p->text); + p->text = newtext; + } +} + +/** Callback to invoke when the closing tag of an element is seen. Note that + * this is also called for empty tags like . + * Here is just copies element elements we do not care about. + * \param context the context of GMarkup + * \param element_name element name + * \param user_data pointer to user data, here a markup_parser_data_t pointer + * \param error a GError + */ +static void +markup_parse_end_element(GMarkupParseContext *context __attribute__ ((unused)), + const gchar *element_name, + gpointer user_data, + GError **error __attribute__ ((unused))) +{ + markup_parser_data_t *p = (markup_parser_data_t *) user_data; + char *newtext; + int i; + + for(i = 0; p->elements[i]; i++) + if(!a_strcmp(element_name, p->elements[i])) + return; + + if(a_strcmp(element_name, "markup")) + { + asprintf(&newtext, "%s", p->text, element_name); + p_delete(&p->text); + p->text = newtext; + } +} + +/** Callback to invoke when some text is seen (text is always inside an + * element). Note that the text of an element may be spread over multiple calls + * of this function. If the G_MARKUP_TREAT_CDATA_AS_TEXT flag is set, this + * function is also called for the content of CDATA marked sections. + * Here it recopies blindly the text in the text attribute of user_data. + * \param context the context of GMarkup + * \param text the text + * \param text_len the text length + * \param user_data pointer to user data, here a markup_parser_data_t pointer + * \param error a GError + */ +static void +markup_parse_text(GMarkupParseContext *context __attribute__ ((unused)), + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error __attribute__ ((unused))) +{ + markup_parser_data_t *p = (markup_parser_data_t *) user_data; + ssize_t len, rlen; + + len = a_strlen(p->text); + rlen = len + 1 + text_len; + p_realloc(&p->text, rlen); + if(len) + a_strncat(p->text, rlen, text, len); + else + a_strncpy(p->text, rlen, text, text_len); +} + +/** Create a markup_parser_data_t structure with elements list. + * \param elements an array of elements to look for, NULL terminated + * \param number of elements in the array (without NULL) + * \return a pointer to an allocated markup_parser_data_t which must be freed + * with markup_parser_data_delete() + */ +markup_parser_data_t * +markup_parser_data_new(const char **elements, ssize_t nelem) +{ + markup_parser_data_t *p; + + p = p_new(markup_parser_data_t, 1); + + p->elements = elements; + p->attribute_names = p_new(char **, nelem); + p->attribute_values = p_new(char **, nelem); + + return p; +} + +/** Delete a markup_parser_data_t allocated. + * \param p markup_parser_data_t pointer address + */ +void +markup_parser_data_delete(markup_parser_data_t **p) +{ + int i, j; + + for(i = 0; (*p)->elements[i]; i++) + if((*p)->attribute_names[i]) + { + for(j = 0; (*p)->attribute_names[i][j]; j++); + { + p_delete(&(*p)->attribute_names[i][j]); + p_delete(&(*p)->attribute_values[i][j]); + } + p_delete(&(*p)->attribute_names[i]); + p_delete(&(*p)->attribute_values[i]); + } + + p_delete(&(*p)->text); + p_delete(p); +} + +/** Parse markup defined in data on the string str. + * \param data a markup_parser_data_t allocated by markup_parser_data_new() + * \param str a string to parse markup from + * \param slen str length + * \return true if success, false otherwise + */ +bool +markup_parse(markup_parser_data_t *data, const char *str, ssize_t slen) +{ + GMarkupParseContext *mkp_ctx; + GMarkupParser parser = + { + /* start_element */ + markup_parse_start_element, + /* end_element */ + markup_parse_end_element, + /* text */ + markup_parse_text, + /* passthrough */ + NULL, + /* error */ + NULL + }; + GError *error = NULL; + + mkp_ctx = g_markup_parse_context_new(&parser, 0, data, NULL); + + if(!g_markup_parse_context_parse(mkp_ctx, "", -1, &error) + || !g_markup_parse_context_parse(mkp_ctx, str, slen, &error) + || !g_markup_parse_context_parse(mkp_ctx, "", -1, &error) + || !g_markup_parse_context_end_parse(mkp_ctx, &error)) + { + warn("unable to parse text: %s\n", error->message); + g_error_free(error); + return false; + } + + g_markup_parse_context_free(mkp_ctx); + + return true; +} + +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/common/markup.h b/common/markup.h new file mode 100644 index 000000000..17b9dc4f2 --- /dev/null +++ b/common/markup.h @@ -0,0 +1,40 @@ +/* + * markup.h - markup header + * + * Copyright © 2008 Julien Danjou + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef AWESOME_COMMON_MARKUP_H +#define AWESOME_COMMON_MARKUP_H + +#include "common/util.h" + +typedef struct +{ + char *text; + const char **elements; + char ***attribute_names; + char ***attribute_values; +} markup_parser_data_t; + +markup_parser_data_t * markup_parser_data_new(const char **, ssize_t); +void markup_parser_data_delete(markup_parser_data_t **); +bool markup_parse(markup_parser_data_t *data, const char *, ssize_t); + +#endif +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80