WIP
WIP WIP: keymap generation test passes meta: Update features and version WiP: cargo.lock WIP: don't crash WIP: no outlines parsing: New tests WIP: base level works WIP: remove old keyboard symbols correctly input WIP: lodaing files WIP: fallback works Valid fallback
This commit is contained in:
		@ -29,7 +29,6 @@
 | 
			
		||||
 | 
			
		||||
#include "eek-keyboard.h"
 | 
			
		||||
#include "src/keyboard.h"
 | 
			
		||||
#include "src/symbol.h"
 | 
			
		||||
 | 
			
		||||
#include "squeekboard-resources.h"
 | 
			
		||||
 | 
			
		||||
@ -62,21 +61,6 @@ G_DEFINE_BOXED_TYPE(EekXmlKeyboardDesc, eek_xml_keyboard_desc, eek_xml_keyboard_
 | 
			
		||||
 | 
			
		||||
static GList        *parse_keyboards (const gchar         *path,
 | 
			
		||||
                                      GError             **error);
 | 
			
		||||
static GList        *parse_prerequisites
 | 
			
		||||
                                     (const gchar         *path,
 | 
			
		||||
                                      GError             **error);
 | 
			
		||||
static gboolean      parse_geometry  (const gchar         *path,
 | 
			
		||||
                                      struct squeek_view **views, GArray *outline_array, GHashTable *name_button_hash,
 | 
			
		||||
                                      GError             **error);
 | 
			
		||||
static gboolean      parse_symbols_with_prerequisites
 | 
			
		||||
                                     (const gchar         *keyboards_dir,
 | 
			
		||||
                                      const gchar         *name,
 | 
			
		||||
                                      LevelKeyboard *keyboard,
 | 
			
		||||
                                      GList             **loaded,
 | 
			
		||||
                                      GError             **error);
 | 
			
		||||
static gboolean      parse_symbols   (const gchar         *path,
 | 
			
		||||
                                      LevelKeyboard *keyboard,
 | 
			
		||||
                                      GError             **error);
 | 
			
		||||
 | 
			
		||||
static gboolean      validate        (const gchar        **valid_path_list,
 | 
			
		||||
                                      gsize                valid_path_list_len,
 | 
			
		||||
@ -253,380 +237,6 @@ struct _GeometryParseData {
 | 
			
		||||
};
 | 
			
		||||
typedef struct _GeometryParseData GeometryParseData;
 | 
			
		||||
 | 
			
		||||
static GeometryParseData *
 | 
			
		||||
geometry_parse_data_new (struct squeek_view **views, GHashTable *name_button_hash, GArray *outline_array)
 | 
			
		||||
{
 | 
			
		||||
    GeometryParseData *data = g_slice_new0 (GeometryParseData);
 | 
			
		||||
 | 
			
		||||
    data->views = views;
 | 
			
		||||
    data->outline_array = outline_array;
 | 
			
		||||
    data->keyname_oref_hash =
 | 
			
		||||
        g_hash_table_new_full (g_str_hash,
 | 
			
		||||
                               g_str_equal,
 | 
			
		||||
                               g_free,
 | 
			
		||||
                               NULL);
 | 
			
		||||
    data->outlineid_oref_hash =
 | 
			
		||||
        g_hash_table_new_full (g_str_hash,
 | 
			
		||||
                               g_str_equal,
 | 
			
		||||
                               g_free,
 | 
			
		||||
                               NULL);
 | 
			
		||||
 | 
			
		||||
    data->name_button_hash = name_button_hash;
 | 
			
		||||
    data->text = g_string_sized_new (BUFSIZE);
 | 
			
		||||
    data->keycode = 8;
 | 
			
		||||
    return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
geometry_parse_data_free (GeometryParseData *data)
 | 
			
		||||
{
 | 
			
		||||
    g_hash_table_destroy (data->keyname_oref_hash);
 | 
			
		||||
    g_hash_table_destroy (data->outlineid_oref_hash);
 | 
			
		||||
    g_string_free (data->text, TRUE);
 | 
			
		||||
    g_slice_free (GeometryParseData, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const gchar *geometry_valid_path_list[] = {
 | 
			
		||||
    "geometry",
 | 
			
		||||
    "button/geometry",
 | 
			
		||||
    "bounds/geometry",
 | 
			
		||||
    "view/geometry",
 | 
			
		||||
    "section/view/geometry",
 | 
			
		||||
    "outline/geometry",
 | 
			
		||||
    "point/outline/geometry",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
geometry_start_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                                 const gchar         *element_name,
 | 
			
		||||
                                 const gchar        **attribute_names,
 | 
			
		||||
                                 const gchar        **attribute_values,
 | 
			
		||||
                                 gpointer             user_data,
 | 
			
		||||
                                 GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    GeometryParseData *data = user_data;
 | 
			
		||||
    const gchar *attribute;
 | 
			
		||||
 | 
			
		||||
    if (!validate (geometry_valid_path_list,
 | 
			
		||||
                   G_N_ELEMENTS (geometry_valid_path_list),
 | 
			
		||||
                   element_name,
 | 
			
		||||
                   data->element_stack,
 | 
			
		||||
                   error)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "bounds") == 0) {
 | 
			
		||||
        EekBounds bounds;
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values, "x");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"x\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        bounds.x = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "y");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"y\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        bounds.y = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "width");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"width\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        bounds.width = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "height");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"height\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        bounds.height = g_strtod (attribute, NULL);
 | 
			
		||||
        data->bounds = bounds;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "view") == 0) {
 | 
			
		||||
        /* Create an empty keyboard to which geometry and symbols
 | 
			
		||||
           information are applied. */
 | 
			
		||||
        struct squeek_view *view = squeek_view_new(data->bounds);
 | 
			
		||||
        data->views[data->view_idx] = view;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "section") == 0) {
 | 
			
		||||
        gint angle = 0;
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "angle");
 | 
			
		||||
        if (attribute != NULL) {
 | 
			
		||||
            angle = strtol (attribute, NULL, 10);
 | 
			
		||||
        }
 | 
			
		||||
        data->row = squeek_view_create_row(data->views[data->view_idx], angle);
 | 
			
		||||
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "button") == 0) {
 | 
			
		||||
        const gchar *base_name = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "name");
 | 
			
		||||
        if (base_name == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"name\" attribute for \"button\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const gchar *oref_name = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "oref");
 | 
			
		||||
        if (oref_name == NULL) {
 | 
			
		||||
            oref_name = "default";
 | 
			
		||||
        }
 | 
			
		||||
        const gchar *keycode_name = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "keycode");
 | 
			
		||||
 | 
			
		||||
        guint oref = GPOINTER_TO_UINT(g_hash_table_lookup(data->outlineid_oref_hash,
 | 
			
		||||
                                      oref_name));
 | 
			
		||||
        const gchar *name = base_name;
 | 
			
		||||
        g_hash_table_insert (data->keyname_oref_hash,
 | 
			
		||||
                             g_strdup(name),
 | 
			
		||||
                             GUINT_TO_POINTER(oref));
 | 
			
		||||
 | 
			
		||||
        struct squeek_button *button = g_hash_table_lookup(data->name_button_hash, name);
 | 
			
		||||
        // never gets used! this section gets executed before any buttons get defined
 | 
			
		||||
        if (button) {
 | 
			
		||||
            if (keycode_name != NULL) {
 | 
			
		||||
                // This sets the keycode for all buttons,
 | 
			
		||||
                // since they share state
 | 
			
		||||
                // TODO: get rid of this in the parser;
 | 
			
		||||
                // this belongs after keymap is defined
 | 
			
		||||
                struct squeek_key *key = squeek_button_get_key(button);
 | 
			
		||||
                squeek_key_set_keycode(key, strtol (keycode_name, NULL, 10));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "outline") == 0) {
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values, "id");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"id\" attribute for \"outline\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        data->outline_id = g_strdup (attribute);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "corner-radius");
 | 
			
		||||
        if (attribute != NULL)
 | 
			
		||||
            data->corner_radius = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "point") == 0) {
 | 
			
		||||
        EekPoint *point;
 | 
			
		||||
        gdouble x, y;
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values, "x");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"x\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        x = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values, "y");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"y\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        y = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        point = g_slice_new (EekPoint);
 | 
			
		||||
        point->x = x;
 | 
			
		||||
        point->y = y;
 | 
			
		||||
 | 
			
		||||
        data->points = g_slist_prepend (data->points, point);
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
    data->element_stack = g_slist_prepend (data->element_stack,
 | 
			
		||||
                                           g_strdup (element_name));
 | 
			
		||||
    data->text->len = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * eek_keyboard_add_outline:
 | 
			
		||||
 * @keyboard: an #EekKeyboard
 | 
			
		||||
 * @outline: an #EekOutline
 | 
			
		||||
 *
 | 
			
		||||
 * Register an outline of @keyboard.
 | 
			
		||||
 * Returns: an unsigned integer ID of the registered outline, for
 | 
			
		||||
 * later reference
 | 
			
		||||
 */
 | 
			
		||||
static guint
 | 
			
		||||
add_outline (GArray *outline_array,
 | 
			
		||||
                          EekOutline  *outline)
 | 
			
		||||
{
 | 
			
		||||
    EekOutline *_outline;
 | 
			
		||||
 | 
			
		||||
    _outline = eek_outline_copy (outline);
 | 
			
		||||
    g_array_append_val (outline_array, *_outline);
 | 
			
		||||
    /* don't use eek_outline_free here, so as to keep _outline->points */
 | 
			
		||||
    g_slice_free (EekOutline, _outline);
 | 
			
		||||
    return outline_array->len - 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
geometry_end_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                               const gchar         *element_name,
 | 
			
		||||
                               gpointer             user_data,
 | 
			
		||||
                               GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    GeometryParseData *data = user_data;
 | 
			
		||||
    GSList *head = data->element_stack;
 | 
			
		||||
 | 
			
		||||
    g_free (head->data);
 | 
			
		||||
    data->element_stack = g_slist_next (data->element_stack);
 | 
			
		||||
    g_slist_free1 (head);
 | 
			
		||||
 | 
			
		||||
    const gchar *text = g_strndup (data->text->str, data->text->len);
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "view") == 0) {
 | 
			
		||||
        data->view_idx++;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "section") == 0) {
 | 
			
		||||
        // Split text on spaces and process each part
 | 
			
		||||
        unsigned head = 0;
 | 
			
		||||
        while (head < strlen(text)) {
 | 
			
		||||
            // Skip to the first non-space character
 | 
			
		||||
            for (; head < strlen(text); head++) {
 | 
			
		||||
                if (text[head] != ' ') {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            unsigned start = head;
 | 
			
		||||
 | 
			
		||||
            // Skip to the first space character
 | 
			
		||||
            for (; head < strlen(text); head++) {
 | 
			
		||||
                if (text[head] == ' ') {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            unsigned end = head;
 | 
			
		||||
 | 
			
		||||
            /// Reached the end
 | 
			
		||||
            if (start == end) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            gchar *name = g_strndup (&text[start], end - start);
 | 
			
		||||
            struct squeek_button *button = g_hash_table_lookup(data->name_button_hash, name);
 | 
			
		||||
            if (!button) {
 | 
			
		||||
                // Save button name together with its level,
 | 
			
		||||
                // to account for buttons with the same name in multiple levels
 | 
			
		||||
                guint keycode = data->keycode++;
 | 
			
		||||
 | 
			
		||||
                guint oref = GPOINTER_TO_UINT(g_hash_table_lookup(data->keyname_oref_hash, name));
 | 
			
		||||
                // default value gives idx 0, which is guaranteed to be occupied
 | 
			
		||||
                button = squeek_row_create_button (data->row, keycode, oref);
 | 
			
		||||
                g_hash_table_insert (data->name_button_hash,
 | 
			
		||||
                                     g_strdup(name),
 | 
			
		||||
                                     button);
 | 
			
		||||
            } else {
 | 
			
		||||
                struct squeek_button *new_button = squeek_row_create_button_with_state(data->row, button);
 | 
			
		||||
                if (!new_button) {
 | 
			
		||||
                    g_set_error (error,
 | 
			
		||||
                                 G_MARKUP_ERROR,
 | 
			
		||||
                                 G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                                 "Couldn't create a shared button");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        data->row = NULL;
 | 
			
		||||
        data->num_rows = 0;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "outline") == 0) {
 | 
			
		||||
        EekOutline *outline = g_slice_new (EekOutline);
 | 
			
		||||
 | 
			
		||||
        outline->corner_radius = data->corner_radius;
 | 
			
		||||
        data->corner_radius = 0.0;
 | 
			
		||||
 | 
			
		||||
        outline->num_points = g_slist_length (data->points);
 | 
			
		||||
        outline->points = g_slice_alloc0 (sizeof (EekPoint) *
 | 
			
		||||
                                          outline->num_points);
 | 
			
		||||
        guint i;
 | 
			
		||||
        for (i = 0, head = data->points = g_slist_reverse (data->points);
 | 
			
		||||
             head && i < outline->num_points;
 | 
			
		||||
             head = g_slist_next (head), i++) {
 | 
			
		||||
            memcpy (&outline->points[i], head->data, sizeof (EekPoint));
 | 
			
		||||
            g_slice_free1 (sizeof (EekPoint), head->data);
 | 
			
		||||
        }
 | 
			
		||||
        g_slist_free (data->points);
 | 
			
		||||
        data->points = NULL;
 | 
			
		||||
 | 
			
		||||
        guint oref = add_outline (data->outline_array, outline);
 | 
			
		||||
 | 
			
		||||
        g_hash_table_insert (data->outlineid_oref_hash,
 | 
			
		||||
                             data->outline_id,
 | 
			
		||||
                             GUINT_TO_POINTER(oref));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
geometry_text_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                       const gchar         *text,
 | 
			
		||||
                       gsize                text_len,
 | 
			
		||||
                       gpointer             user_data,
 | 
			
		||||
                       GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    GeometryParseData *data = user_data;
 | 
			
		||||
    g_string_append_len (data->text, text, (gssize)text_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GMarkupParser geometry_parser = {
 | 
			
		||||
    geometry_start_element_callback,
 | 
			
		||||
    geometry_end_element_callback,
 | 
			
		||||
    geometry_text_callback,
 | 
			
		||||
    0,
 | 
			
		||||
    0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _SymbolsParseData {
 | 
			
		||||
    GSList *element_stack;
 | 
			
		||||
    GString *text;
 | 
			
		||||
@ -641,139 +251,6 @@ struct _SymbolsParseData {
 | 
			
		||||
};
 | 
			
		||||
typedef struct _SymbolsParseData SymbolsParseData;
 | 
			
		||||
 | 
			
		||||
static SymbolsParseData *
 | 
			
		||||
symbols_parse_data_new (LevelKeyboard *keyboard)
 | 
			
		||||
{
 | 
			
		||||
    SymbolsParseData *data = g_slice_new0 (SymbolsParseData);
 | 
			
		||||
 | 
			
		||||
    data->keyboard = keyboard;
 | 
			
		||||
    data->text = g_string_sized_new (BUFSIZE);
 | 
			
		||||
    return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
symbols_parse_data_free (SymbolsParseData *data)
 | 
			
		||||
{
 | 
			
		||||
    g_string_free (data->text, TRUE);
 | 
			
		||||
    g_slice_free (SymbolsParseData, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const gchar *symbols_valid_path_list[] = {
 | 
			
		||||
    "symbols",
 | 
			
		||||
    "symbol/symbols",
 | 
			
		||||
    "include/symbols",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
symbols_start_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                                const gchar         *element_name,
 | 
			
		||||
                                const gchar        **attribute_names,
 | 
			
		||||
                                const gchar        **attribute_values,
 | 
			
		||||
                                gpointer             user_data,
 | 
			
		||||
                                GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    SymbolsParseData *data = user_data;
 | 
			
		||||
    const gchar *attribute;
 | 
			
		||||
 | 
			
		||||
    if (!validate (symbols_valid_path_list,
 | 
			
		||||
                   G_N_ELEMENTS (symbols_valid_path_list),
 | 
			
		||||
                   element_name,
 | 
			
		||||
                   data->element_stack,
 | 
			
		||||
                   error))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "symbol") == 0) {
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "label");
 | 
			
		||||
        if (attribute != NULL)
 | 
			
		||||
            data->label = g_strdup (attribute);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "icon");
 | 
			
		||||
        if (attribute != NULL)
 | 
			
		||||
            data->icon = g_strdup (attribute);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "tooltip");
 | 
			
		||||
        if (attribute != NULL)
 | 
			
		||||
            data->tooltip = g_strdup (attribute);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data->element_stack = g_slist_prepend (data->element_stack,
 | 
			
		||||
                                           g_strdup (element_name));
 | 
			
		||||
    data->text->len = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
symbols_end_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                              const gchar         *element_name,
 | 
			
		||||
                              gpointer             user_data,
 | 
			
		||||
                              GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    SymbolsParseData *data = user_data;
 | 
			
		||||
    GSList *head = data->element_stack;
 | 
			
		||||
    gchar *text;
 | 
			
		||||
 | 
			
		||||
    g_free (head->data);
 | 
			
		||||
    data->element_stack = g_slist_next (data->element_stack);
 | 
			
		||||
    g_slist_free1 (head);
 | 
			
		||||
 | 
			
		||||
    // TODO: this could all be moved to text handler
 | 
			
		||||
    text = g_strndup (data->text->str, data->text->len);
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "symbol") == 0) {
 | 
			
		||||
 | 
			
		||||
        gchar *name = text;
 | 
			
		||||
        struct squeek_button *button = eek_keyboard_find_button_by_name (data->keyboard,
 | 
			
		||||
                                                   name);
 | 
			
		||||
        if (button) {
 | 
			
		||||
            squeek_key_add_symbol(
 | 
			
		||||
                squeek_button_get_key(button),
 | 
			
		||||
                element_name,
 | 
			
		||||
                text,
 | 
			
		||||
                data->keyval,
 | 
			
		||||
                data->label,
 | 
			
		||||
                data->icon,
 | 
			
		||||
                data->tooltip
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        data->keyval = 0;
 | 
			
		||||
        g_free(data->label);
 | 
			
		||||
        data->label = NULL;
 | 
			
		||||
        g_free(data->icon);
 | 
			
		||||
        data->icon = NULL;
 | 
			
		||||
        g_free(data->tooltip);
 | 
			
		||||
        data->tooltip = NULL;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "invalid") == 0) {
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
    g_free (text);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
symbols_text_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                       const gchar         *text,
 | 
			
		||||
                       gsize                text_len,
 | 
			
		||||
                       gpointer             user_data,
 | 
			
		||||
                       GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    SymbolsParseData *data = user_data;
 | 
			
		||||
    g_string_append_len (data->text, text, text_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GMarkupParser symbols_parser = {
 | 
			
		||||
    symbols_start_element_callback,
 | 
			
		||||
    symbols_end_element_callback,
 | 
			
		||||
    symbols_text_callback,
 | 
			
		||||
    0,
 | 
			
		||||
    0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _PrerequisitesParseData {
 | 
			
		||||
    GSList *element_stack;
 | 
			
		||||
@ -783,157 +260,13 @@ struct _PrerequisitesParseData {
 | 
			
		||||
};
 | 
			
		||||
typedef struct _PrerequisitesParseData PrerequisitesParseData;
 | 
			
		||||
 | 
			
		||||
static PrerequisitesParseData *
 | 
			
		||||
prerequisites_parse_data_new (void)
 | 
			
		||||
{
 | 
			
		||||
    PrerequisitesParseData *data = g_slice_new0 (PrerequisitesParseData);
 | 
			
		||||
    data->text = g_string_sized_new (BUFSIZE);
 | 
			
		||||
    return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
prerequisites_parse_data_free (PrerequisitesParseData *data)
 | 
			
		||||
{
 | 
			
		||||
    g_list_free_full (data->prerequisites, g_free);
 | 
			
		||||
    g_string_free (data->text, TRUE);
 | 
			
		||||
    g_slice_free (PrerequisitesParseData, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
prerequisites_start_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                                      const gchar         *element_name,
 | 
			
		||||
                                      const gchar        **attribute_names,
 | 
			
		||||
                                      const gchar        **attribute_values,
 | 
			
		||||
                                      gpointer             user_data,
 | 
			
		||||
                                      GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    PrerequisitesParseData *data = user_data;
 | 
			
		||||
 | 
			
		||||
    if (!validate (symbols_valid_path_list,
 | 
			
		||||
                   G_N_ELEMENTS (symbols_valid_path_list),
 | 
			
		||||
                   element_name,
 | 
			
		||||
                   data->element_stack,
 | 
			
		||||
                   error))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    data->element_stack = g_slist_prepend (data->element_stack,
 | 
			
		||||
                                           g_strdup (element_name));
 | 
			
		||||
    data->text->len = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
prerequisites_end_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                                    const gchar         *element_name,
 | 
			
		||||
                                    gpointer             user_data,
 | 
			
		||||
                                    GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    PrerequisitesParseData *data = user_data;
 | 
			
		||||
    GSList *head = data->element_stack;
 | 
			
		||||
 | 
			
		||||
    g_free (head->data);
 | 
			
		||||
    data->element_stack = g_slist_next (data->element_stack);
 | 
			
		||||
    g_slist_free1 (head);
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "include") == 0) {
 | 
			
		||||
        data->prerequisites = g_list_append (data->prerequisites,
 | 
			
		||||
                                              g_strndup (data->text->str,
 | 
			
		||||
                                                         data->text->len));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
prerequisites_text_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                             const gchar         *text,
 | 
			
		||||
                             gsize                text_len,
 | 
			
		||||
                             gpointer             user_data,
 | 
			
		||||
                             GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    PrerequisitesParseData *data = user_data;
 | 
			
		||||
    g_string_append_len (data->text, text, text_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GMarkupParser prerequisites_parser = {
 | 
			
		||||
    prerequisites_start_element_callback,
 | 
			
		||||
    prerequisites_end_element_callback,
 | 
			
		||||
    prerequisites_text_callback,
 | 
			
		||||
    0,
 | 
			
		||||
    0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
LevelKeyboard *
 | 
			
		||||
eek_xml_layout_real_create_keyboard (EekLayout *self,
 | 
			
		||||
eek_xml_layout_real_create_keyboard (const char *keyboard_type,
 | 
			
		||||
                                     EekboardContextService *manager)
 | 
			
		||||
{
 | 
			
		||||
    EekXmlLayout *layout = EEK_XML_LAYOUT (self);
 | 
			
		||||
    EekXmlLayoutPrivate *priv = eek_xml_layout_get_instance_private (layout);
 | 
			
		||||
    gboolean retval;
 | 
			
		||||
 | 
			
		||||
    /* Read geometry information. */
 | 
			
		||||
    gchar *filename = g_strdup_printf ("%s.xml", priv->desc->geometry);
 | 
			
		||||
    gchar *path = g_build_filename (priv->keyboards_dir, "geometry", filename, NULL);
 | 
			
		||||
    g_free (filename);
 | 
			
		||||
 | 
			
		||||
    GArray *outline_array = g_array_new (FALSE, TRUE, sizeof (EekOutline));
 | 
			
		||||
 | 
			
		||||
    // char* -> struct squeek_button*
 | 
			
		||||
    GHashTable *name_button_hash =
 | 
			
		||||
            g_hash_table_new_full (g_str_hash,
 | 
			
		||||
                                   g_str_equal,
 | 
			
		||||
                                   g_free,
 | 
			
		||||
                                   NULL);
 | 
			
		||||
    // One view for each level
 | 
			
		||||
    struct squeek_view *views[4] = {0};
 | 
			
		||||
 | 
			
		||||
    GError *error = NULL;
 | 
			
		||||
    retval = parse_geometry (path, views, outline_array, name_button_hash, &error);
 | 
			
		||||
    g_free (path);
 | 
			
		||||
    if (!retval) {
 | 
			
		||||
        for (uint i = 0; i < 4; i++) {
 | 
			
		||||
            g_object_unref (views[i]);
 | 
			
		||||
        }
 | 
			
		||||
        g_warning ("can't parse geometry file %s: %s",
 | 
			
		||||
                   priv->desc->geometry,
 | 
			
		||||
                   error->message);
 | 
			
		||||
        g_error_free (error);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    LevelKeyboard *keyboard = level_keyboard_new(manager, views, name_button_hash);
 | 
			
		||||
 | 
			
		||||
    keyboard->outline_array = outline_array;
 | 
			
		||||
    // FIXME: are symbols shared betwen views?
 | 
			
		||||
 | 
			
		||||
    /* Read symbols information. */
 | 
			
		||||
    GList *loaded = NULL;
 | 
			
		||||
    retval = parse_symbols_with_prerequisites (priv->keyboards_dir,
 | 
			
		||||
                                               priv->desc->symbols,
 | 
			
		||||
                                               keyboard,
 | 
			
		||||
                                               &loaded,
 | 
			
		||||
                                               &error);
 | 
			
		||||
    g_list_free_full (loaded, g_free);
 | 
			
		||||
    if (!retval) {
 | 
			
		||||
        for (uint i = 0; i < 4; i++) {
 | 
			
		||||
            if (views[i]) {
 | 
			
		||||
                g_object_unref(views[i]);
 | 
			
		||||
                views[i] = NULL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        //g_object_unref (view);
 | 
			
		||||
        g_warning ("can't parse symbols file %s: %s",
 | 
			
		||||
                   priv->desc->symbols,
 | 
			
		||||
                   error->message);
 | 
			
		||||
        g_error_free (error);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (uint i = 0; i < 4; i++) {
 | 
			
		||||
        if (views[i]) {
 | 
			
		||||
            squeek_view_place_contents(views[i], keyboard);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return keyboard;
 | 
			
		||||
    struct squeek_layout *layout = squeek_load_layout(keyboard_type);
 | 
			
		||||
    squeek_layout_place_contents(layout);
 | 
			
		||||
    return level_keyboard_new(manager, layout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@ -1116,190 +449,6 @@ eek_xml_keyboard_desc_free (EekXmlKeyboardDesc *desc)
 | 
			
		||||
    g_slice_free (EekXmlKeyboardDesc, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
parse_geometry (const gchar *path, struct squeek_view **views, GArray *outline_array, GHashTable *name_button_hash, GError **error)
 | 
			
		||||
{
 | 
			
		||||
    GeometryParseData *data;
 | 
			
		||||
    GMarkupParseContext *pcontext;
 | 
			
		||||
    GFile *file;
 | 
			
		||||
    GFileInputStream *input;
 | 
			
		||||
    gboolean retval;
 | 
			
		||||
 | 
			
		||||
    file = g_str_has_prefix (path, "resource://")
 | 
			
		||||
         ? g_file_new_for_uri  (path)
 | 
			
		||||
         : g_file_new_for_path (path);
 | 
			
		||||
 | 
			
		||||
    input = g_file_read (file, NULL, error);
 | 
			
		||||
    g_object_unref (file);
 | 
			
		||||
    if (input == NULL)
 | 
			
		||||
        return FALSE;
 | 
			
		||||
 | 
			
		||||
    data = geometry_parse_data_new (views, name_button_hash, outline_array);
 | 
			
		||||
    pcontext = g_markup_parse_context_new (&geometry_parser,
 | 
			
		||||
                                           0,
 | 
			
		||||
                                           data,
 | 
			
		||||
                                           NULL);
 | 
			
		||||
 | 
			
		||||
    retval = parse (pcontext, G_INPUT_STREAM (input), error);
 | 
			
		||||
    g_markup_parse_context_free (pcontext);
 | 
			
		||||
    g_object_unref (input);
 | 
			
		||||
    if (!retval) {
 | 
			
		||||
        geometry_parse_data_free (data);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Resolve outline references. */
 | 
			
		||||
    /*
 | 
			
		||||
     * GHashTable *oref_hash;
 | 
			
		||||
    GHashTableIter iter;
 | 
			
		||||
    gpointer k, v;
 | 
			
		||||
 | 
			
		||||
    oref_hash = g_hash_table_new (g_str_hash, g_str_equal);
 | 
			
		||||
    g_hash_table_iter_init (&iter, data->oref_outline_hash);
 | 
			
		||||
    while (g_hash_table_iter_next (&iter, &k, &v)) {
 | 
			
		||||
        EekOutline *outline = v;
 | 
			
		||||
        gulong oref;
 | 
			
		||||
 | 
			
		||||
        oref = add_outline (outline_array, outline);
 | 
			
		||||
        g_hash_table_insert (oref_hash, k, GUINT_TO_POINTER(oref));
 | 
			
		||||
    }*/
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    g_hash_table_iter_init (&iter, data->key_oref_hash);
 | 
			
		||||
    while (g_hash_table_iter_next (&iter, &k, &v)) {
 | 
			
		||||
        gpointer oref;
 | 
			
		||||
        if (g_hash_table_lookup_extended (oref_hash, v, NULL, &oref))
 | 
			
		||||
            eek_key_set_oref (EEK_KEY(k), GPOINTER_TO_UINT(oref));
 | 
			
		||||
    }*/
 | 
			
		||||
//    g_hash_table_destroy (oref_hash);
 | 
			
		||||
 | 
			
		||||
    geometry_parse_data_free (data);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
parse_symbols_with_prerequisites (const gchar *keyboards_dir,
 | 
			
		||||
                                  const gchar *name,
 | 
			
		||||
                                  LevelKeyboard *keyboard,
 | 
			
		||||
                                  GList     **loaded,
 | 
			
		||||
                                  GError     **error)
 | 
			
		||||
{
 | 
			
		||||
    gchar *filename, *path;
 | 
			
		||||
    GList *prerequisites, *p;
 | 
			
		||||
    GError *prerequisites_error;
 | 
			
		||||
    gboolean retval;
 | 
			
		||||
 | 
			
		||||
    for (p = *loaded; p; p = p->next) {
 | 
			
		||||
        if (g_strcmp0 (p->data, name) == 0) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         EEK_ERROR,
 | 
			
		||||
                         EEK_ERROR_LAYOUT_ERROR,
 | 
			
		||||
                         "%s already loaded",
 | 
			
		||||
                         name);
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    *loaded = g_list_prepend (*loaded, g_strdup (name));
 | 
			
		||||
 | 
			
		||||
    filename = g_strdup_printf ("%s.xml", name);
 | 
			
		||||
    path = g_build_filename (keyboards_dir, "symbols", filename, NULL);
 | 
			
		||||
    g_free (filename);
 | 
			
		||||
 | 
			
		||||
    prerequisites_error = NULL;
 | 
			
		||||
    prerequisites = parse_prerequisites (path, &prerequisites_error);
 | 
			
		||||
    if (prerequisites_error != NULL) {
 | 
			
		||||
        g_propagate_error (error, prerequisites_error);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (p = prerequisites; p; p = p->next) {
 | 
			
		||||
        retval = parse_symbols_with_prerequisites (keyboards_dir,
 | 
			
		||||
                                                   p->data,
 | 
			
		||||
                                                   keyboard,
 | 
			
		||||
                                                   loaded,
 | 
			
		||||
                                                   error);
 | 
			
		||||
        if (!retval)
 | 
			
		||||
            return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
    g_list_free_full (prerequisites, (GDestroyNotify)g_free);
 | 
			
		||||
 | 
			
		||||
    retval = parse_symbols (path, keyboard, error);
 | 
			
		||||
    g_free (path);
 | 
			
		||||
 | 
			
		||||
    return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
parse_symbols (const gchar *path, LevelKeyboard *keyboard, GError **error)
 | 
			
		||||
{
 | 
			
		||||
    SymbolsParseData *data;
 | 
			
		||||
    GMarkupParseContext *pcontext;
 | 
			
		||||
    GFile *file;
 | 
			
		||||
    GFileInputStream *input;
 | 
			
		||||
    gboolean retval;
 | 
			
		||||
 | 
			
		||||
    file = g_str_has_prefix (path, "resource://")
 | 
			
		||||
         ? g_file_new_for_uri  (path)
 | 
			
		||||
         : g_file_new_for_path (path);
 | 
			
		||||
 | 
			
		||||
    input = g_file_read (file, NULL, error);
 | 
			
		||||
    g_object_unref (file);
 | 
			
		||||
    if (input == NULL)
 | 
			
		||||
        return FALSE;
 | 
			
		||||
 | 
			
		||||
    data = symbols_parse_data_new (keyboard);
 | 
			
		||||
    pcontext = g_markup_parse_context_new (&symbols_parser,
 | 
			
		||||
                                           0,
 | 
			
		||||
                                           data,
 | 
			
		||||
                                           NULL);
 | 
			
		||||
    retval = parse (pcontext, G_INPUT_STREAM (input), error);
 | 
			
		||||
    g_markup_parse_context_free (pcontext);
 | 
			
		||||
    g_object_unref (input);
 | 
			
		||||
    if (!retval) {
 | 
			
		||||
        symbols_parse_data_free (data);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
    symbols_parse_data_free (data);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GList *
 | 
			
		||||
parse_prerequisites (const gchar *path, GError **error)
 | 
			
		||||
{
 | 
			
		||||
    PrerequisitesParseData *data;
 | 
			
		||||
    GMarkupParseContext *pcontext;
 | 
			
		||||
    GFile *file;
 | 
			
		||||
    GFileInputStream *input;
 | 
			
		||||
    GList *prerequisites;
 | 
			
		||||
    gboolean retval;
 | 
			
		||||
 | 
			
		||||
    file = g_str_has_prefix (path, "resource://")
 | 
			
		||||
         ? g_file_new_for_uri  (path)
 | 
			
		||||
         : g_file_new_for_path (path);
 | 
			
		||||
 | 
			
		||||
    input = g_file_read (file, NULL, error);
 | 
			
		||||
    g_object_unref (file);
 | 
			
		||||
    if (input == NULL)
 | 
			
		||||
        return FALSE;
 | 
			
		||||
 | 
			
		||||
    data = prerequisites_parse_data_new ();
 | 
			
		||||
    pcontext = g_markup_parse_context_new (&prerequisites_parser,
 | 
			
		||||
                                           0,
 | 
			
		||||
                                           data,
 | 
			
		||||
                                           NULL);
 | 
			
		||||
    retval = parse (pcontext, G_INPUT_STREAM (input), error);
 | 
			
		||||
    g_markup_parse_context_free (pcontext);
 | 
			
		||||
    g_object_unref (input);
 | 
			
		||||
    if (!retval) {
 | 
			
		||||
        prerequisites_parse_data_free (data);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    prerequisites = data->prerequisites;
 | 
			
		||||
    data->prerequisites = NULL;
 | 
			
		||||
    prerequisites_parse_data_free (data);
 | 
			
		||||
    return prerequisites;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GList *
 | 
			
		||||
parse_keyboards (const gchar *path, GError **error)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user