X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=libchimara%2Fstyle.c;h=07a10fef638ecccb3404972014d9397aae776c31;hb=1410fe4a16ca30242deb8273a8c5867ebeb66b65;hp=c3a8e43f9bafd67e018de7cc10a72e7f9435a117;hpb=e26270c20acfd9df67b82af594d96536cb6d202f;p=rodin%2Fchimara.git diff --git a/libchimara/style.c b/libchimara/style.c index c3a8e43..07a10fe 100644 --- a/libchimara/style.c +++ b/libchimara/style.c @@ -1,25 +1,37 @@ #include "style.h" +#include +#include -extern ChimaraGlkPrivate *glk_data; +extern GPrivate *glk_data_key; +static gboolean chimara_style_initialized = FALSE; +static gboolean style_accept(GScanner *scanner, GTokenType token); +static gboolean style_accept_style_identifier(GScanner *scanner); +static gboolean style_accept_style_hint(GScanner *scanner, GtkTextTag *current_tag); +static void style_add_tag_to_textbuffer(gpointer key, gpointer tag, gpointer tag_table); /** * glk_set_style: - * @styl The style to apply + * @styl: The style to apply * - * This changes the style of the current output stream. After a style change, - * new text which is printed to that stream will be given the new style. For a - * window stream, the text will appear in that style. For other types of - * streams, this has no effect. + * Changes the style of the current output stream. @styl should be one of the + * style_ constants listed above. However, any value is actually + * legal; if the interpreter does not recognize the style value, it will treat + * it as %style_Normal. + * + * This policy allows for the future definition of styles without breaking old + * Glk libraries. + * */ void -glk_set_style(glui32 style) +glk_set_style(glui32 styl) { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); g_return_if_fail(glk_data->current_stream != NULL); - glk_set_style_stream(glk_data->current_stream, style); + glk_set_style_stream(glk_data->current_stream, styl); } /* Internal function: mapping from style enum to tag name */ -gchar* +static gchar * get_tag_name(glui32 style) { switch(style) { @@ -40,9 +52,16 @@ get_tag_name(glui32 style) return "normal"; } +/** + * glk_set_style_stream: + * @str: Output stream to change the style of + * @styl: The style to apply + * + * This changes the style of the stream @str. See glk_set_style(). + */ void -glk_set_style_stream(strid_t stream, glui32 style) { - stream->style = get_tag_name(style); +glk_set_style_stream(strid_t str, glui32 styl) { + str->style = get_tag_name(styl); } /* Internal function: call this to initialize the default styles to a textbuffer. */ @@ -51,20 +70,268 @@ style_init_textbuffer(GtkTextBuffer *buffer) { g_return_if_fail(buffer != NULL); - gtk_text_buffer_create_tag(buffer, "normal", NULL); - gtk_text_buffer_create_tag(buffer, "emphasized", "style", PANGO_STYLE_ITALIC, NULL); - gtk_text_buffer_create_tag(buffer, "preformatted", "font-desc", glk_data->monospace_font_desc, NULL); - gtk_text_buffer_create_tag(buffer, "header", "size-points", 16.0, "weight", PANGO_WEIGHT_BOLD, NULL); - gtk_text_buffer_create_tag(buffer, "subheader", "size-points", 12.0, "weight", PANGO_WEIGHT_BOLD, NULL); - gtk_text_buffer_create_tag(buffer, "alert", "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL); - gtk_text_buffer_create_tag(buffer, "note", "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL); - gtk_text_buffer_create_tag(buffer, "block-quote", "justification", GTK_JUSTIFY_CENTER, "style", PANGO_STYLE_ITALIC, NULL); - gtk_text_buffer_create_tag(buffer, "input", NULL); - gtk_text_buffer_create_tag(buffer, "user1", NULL); - gtk_text_buffer_create_tag(buffer, "user2", NULL); + if( G_UNLIKELY(!chimara_style_initialized) ) { + style_init(); + } + + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + g_hash_table_foreach(glk_data->default_styles, style_add_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer)); +} + +static void +style_add_tag_to_textbuffer(gpointer key, gpointer tag, gpointer tag_table) +{ + gtk_text_tag_table_add(tag_table, tag); } void +style_init() +{ + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + GtkTextTag *tag; + + /* Create the CSS file scanner */ + GScanner *scanner = g_scanner_new(NULL); + int f = open(glk_data->css_file, O_RDONLY); + g_return_if_fail(f != -1); + g_scanner_input_file(scanner, f); + scanner->input_name = glk_data->css_file; + scanner->config->cset_identifier_first = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#"; + scanner->config->cset_identifier_nth = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_0123456789"; + scanner->config->symbol_2_token = TRUE; + scanner->config->cpair_comment_single = NULL; + + /* Initialise the default styles */ + g_hash_table_insert(glk_data->default_styles, "normal", gtk_text_tag_new("normal")); + + tag = gtk_text_tag_new("emphasized"); + g_object_set(tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL); + g_hash_table_insert(glk_data->default_styles, "emphasized", tag); + + tag = gtk_text_tag_new("preformatted"); + g_object_set(tag, "font-desc", glk_data->monospace_font_desc, NULL); + g_hash_table_insert(glk_data->default_styles, "preformatted", tag); + + tag = gtk_text_tag_new("header"); + g_object_set(tag, "size-points", 18.0, "weight", PANGO_WEIGHT_BOLD, NULL); + g_hash_table_insert(glk_data->default_styles, "header", tag); + + tag = gtk_text_tag_new("subheader"); + g_object_set(tag, "size-points", 14.0, "weight", PANGO_WEIGHT_BOLD, NULL); + g_hash_table_insert(glk_data->default_styles, "subheader", tag); + + tag = gtk_text_tag_new("alert"); + g_object_set(tag, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL); + g_hash_table_insert(glk_data->default_styles, "alert", tag); + + tag = gtk_text_tag_new("note"); + g_object_set(tag, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL); + g_hash_table_insert(glk_data->default_styles, "note", tag); + + tag = gtk_text_tag_new("block-quote"); + g_object_set(tag, "justification", GTK_JUSTIFY_CENTER, "style", PANGO_STYLE_ITALIC, NULL); + g_hash_table_insert(glk_data->default_styles, "block-quote", tag); + + g_hash_table_insert(glk_data->default_styles, "input", gtk_text_tag_new("input")); + g_hash_table_insert(glk_data->default_styles, "user1", gtk_text_tag_new("user1")); + g_hash_table_insert(glk_data->default_styles, "user2", gtk_text_tag_new("user2")); + + /* Run the scanner over the CSS file */ + while( g_scanner_peek_next_token(scanner) != G_TOKEN_EOF) { + if( !style_accept_style_identifier(scanner) ) + break; + } + + g_scanner_destroy(scanner); +} + +static gboolean +style_accept(GScanner *scanner, GTokenType token) +{ + if( g_scanner_get_next_token(scanner) != token ) { + g_scanner_unexp_token(scanner, token, NULL, NULL, NULL, "CSS Error", 1); + return FALSE; + } else { + return TRUE; + } +} + +static gboolean +style_accept_style_identifier(GScanner *scanner) +{ + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + + GtkTextTag *current_tag; + GTokenType token = g_scanner_get_next_token(scanner); + GTokenValue value = g_scanner_cur_value(scanner); + + if(token != G_TOKEN_IDENTIFIER) { + g_scanner_error(scanner, "CSS Error: style identifier expected"); + return FALSE; + } + + printf("Identifier: %s\n", value.v_identifier); + current_tag = g_hash_table_lookup(glk_data->default_styles, value.v_identifier); + + if(current_tag == NULL) { + g_scanner_error(scanner, "CSS Error: invalid style identifier"); + return FALSE; + } + + if( !style_accept(scanner, '{') ) + return FALSE; + + while( g_scanner_peek_next_token(scanner) != '}') { + if( !style_accept_style_hint(scanner, current_tag) ) + return FALSE; + } + + if( !style_accept(scanner, '}') ) + return FALSE; + + return TRUE; +} + +static gboolean +style_accept_style_hint(GScanner *scanner, GtkTextTag *current_tag) +{ + GTokenType token = g_scanner_get_next_token(scanner); + GTokenValue value = g_scanner_cur_value(scanner); + gchar *hint; + + if(token != G_TOKEN_IDENTIFIER) { + g_scanner_error(scanner, "CSS Error: style hint expected"); + return FALSE; + } + + hint = g_strdup(value.v_identifier); + printf("Hint: %s\n", hint); + + if( !style_accept(scanner, ':') ) + return FALSE; + + token = g_scanner_get_next_token(scanner); + value = g_scanner_cur_value(scanner); + + if( !strcmp(hint, "font-family") ) { + if(token != G_TOKEN_STRING) { + g_scanner_error(scanner, "CSS Error: string expected"); + return FALSE; + } + g_object_set(current_tag, "family", value.v_string, "family-set", TRUE, NULL); + } + else if( !strcmp(hint, "font-weight") ) { + if(token != G_TOKEN_IDENTIFIER) { + g_scanner_error(scanner, "CSS Error: bold/normal expected"); + return FALSE; + } + + if( !strcmp(value.v_identifier, "bold") ) + g_object_set(current_tag, "weight", PANGO_WEIGHT_BOLD, "weight-set", TRUE, NULL); + else if( !strcmp(value.v_identifier, "normal") ) + g_object_set(current_tag, "weight", PANGO_WEIGHT_NORMAL, "weight-set", TRUE, NULL); + else { + g_scanner_error(scanner, "CSS Error: bold/normal expected"); + return FALSE; + } + } + else if( !strcmp(hint, "font-style") ) { + if(token != G_TOKEN_IDENTIFIER) { + g_scanner_error(scanner, "CSS Error: italic/normal expected"); + return FALSE; + } + + if( !strcmp(value.v_identifier, "italic") ) + g_object_set(current_tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL); + else if( !strcmp(value.v_identifier, "normal") ) + g_object_set(current_tag, "style", PANGO_STYLE_NORMAL, "style-set", TRUE, NULL); + else { + g_scanner_error(scanner, "CSS Error: italic/normal expected"); + return FALSE; + } + } + else if( !strcmp(hint, "font-size") ) { + if(token == G_TOKEN_INT) + g_object_set(current_tag, "size-points", (float)value.v_int, "size-set", TRUE, NULL); + else if(token == G_TOKEN_FLOAT) + g_object_set(current_tag, "size-points", value.v_float, "size-set", TRUE, NULL); + else { + g_scanner_error(scanner, "CSS Error: integer or float expected"); + return FALSE; + } + } + else if( !strcmp(hint, "color") ) { + if(token != G_TOKEN_IDENTIFIER) { + g_scanner_error(scanner, "CSS Error: hex color expected"); + return FALSE; + } + g_object_set(current_tag, "foreground", value.v_identifier, "foreground-set", TRUE, NULL); + } + else if( !strcmp(hint, "background-color") ) { + if(token != G_TOKEN_IDENTIFIER) { + g_scanner_error(scanner, "CSS Error: hex color expected"); + return FALSE; + } + g_object_set(current_tag, "background", value.v_identifier, "background-set", TRUE, NULL); + } + else if( !strcmp(hint, "text-align") ) { + if(token != G_TOKEN_IDENTIFIER) { + g_scanner_error(scanner, "CSS Error: left/right/center expected"); + return FALSE; + } + + if( !strcmp(value.v_identifier, "left") ) + g_object_set(current_tag, "justification", GTK_JUSTIFY_LEFT, "justification-set", TRUE, NULL); + else if( !strcmp(value.v_identifier, "right") ) + g_object_set(current_tag, "justification", GTK_JUSTIFY_RIGHT, "justification-set", TRUE, NULL); + else if( !strcmp(value.v_identifier, "center") ) + g_object_set(current_tag, "justification", GTK_JUSTIFY_CENTER, "justification-set", TRUE, NULL); + else { + g_scanner_error(scanner, "CSS Error: left/right/center expected"); + return FALSE; + } + } + else if( !strcmp(hint, "margin-left") ) { + if(token != G_TOKEN_INT) { + g_scanner_error(scanner, "CSS Error: integer expected"); + return FALSE; + } + g_object_set(current_tag, "left-margin", value.v_int, "left-margin-set", TRUE, NULL); + } + else if( !strcmp(hint, "margin-right") ) { + if(token != G_TOKEN_INT) { + g_scanner_error(scanner, "CSS Error: integer expected"); + return FALSE; + } + g_object_set(current_tag, "right-margin", value.v_int, "right-margin-set", TRUE, NULL); + } + else if( !strcmp(hint, "margin-top") ) { + if(token != G_TOKEN_INT) { + g_scanner_error(scanner, "CSS Error: integer expected"); + return FALSE; + } + g_object_set(current_tag, "pixels-above-lines", value.v_int, "pixels-above-lines-set", TRUE, NULL); + } + else if( !strcmp(hint, "margin-bottom") ) { + if(token != G_TOKEN_INT) { + g_scanner_error(scanner, "CSS Error: integer expected"); + return FALSE; + } + g_object_set(current_tag, "pixels-below-lines", value.v_int, "pixels-below-lines-set", TRUE, NULL); + } + + else { + g_scanner_error(scanner, "CSS Error: invalid style hint %s", hint); + return FALSE; + } + + if( !style_accept(scanner, ';') ) + return FALSE; + + return TRUE; +} + +static void color_format(glui32 val, gchar *buffer) { sprintf(buffer, "#%02X%02X%02X", @@ -75,11 +342,12 @@ color_format(glui32 val, gchar *buffer) } /* Internal function: changes a GTK tag to correspond with the given style. */ -void +static void apply_stylehint_to_tag(GtkTextTag *tag, glui32 hint, glsi32 val) { g_return_if_fail(tag != NULL); + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); GObject *tag_object = G_OBJECT(tag); gint reverse_color = 0; @@ -173,15 +441,29 @@ apply_stylehint_to_tag(GtkTextTag *tag, glui32 hint, glsi32 val) } } +/** + * glk_stylehint_set: + * @wintype: The window type to set a style hint on, or %wintype_AllTypes. + * @styl: The style to set a hint for. + * @hint: The type of style hint, one of the stylehint_ constants. + * @val: The style hint. The meaning of this depends on @hint. + * + * Sets a hint about the appearance of one style for a particular type of + * window. You can also set wintype to %wintype_AllTypes, which sets a hint for + * all types of window. + * + * There is no equivalent constant to set a hint for all styles of a single + * window type. + * + */ void -glk_stylehint_set(glui32 wintype, glui32 style, glui32 hint, glsi32 val) +glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val) { - - gchar *tag_name = get_tag_name(style); + gchar *tag_name = get_tag_name(styl); /* Iterate over all the window and update their styles if nessecary */ - winid_t win = glk_window_iterate(NULL, NULL); - while(win != NULL) { + winid_t win; + for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) { if(wintype != wintype_TextBuffer) continue; /* FIXME: add support for text grid windows */ @@ -206,3 +488,9 @@ glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2) { return styl1 != styl2; } + +glui32 +glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result) +{ + return FALSE; +}