Finished implementing garglk_set_zcolors()
[projects/chimara/chimara.git] / libchimara / style.c
index 14ca359d00f887860016c68ad6d319bc93a1f91d..557f3dcaa5170f3b467c034d87f45e6b7cdc0502 100644 (file)
@@ -1,5 +1,6 @@
 #include <stdio.h>
 #include <string.h>
+#include <math.h>
 #include "chimara-glk-private.h"
 #include "glk.h"
 #include "style.h"
@@ -38,7 +39,7 @@ glk_set_style(glui32 styl)
        glk_set_style_stream(glk_data->current_stream, styl);
 }
 
-#define NUM_STYLES 13
+/* The first 11 tag names must correspond to the first 11 glk tag names as defined below */
 static const gchar* TAG_NAMES[] = {
        "normal",
        "emphasized",
@@ -52,9 +53,11 @@ static const gchar* TAG_NAMES[] = {
        "user1",
        "user2",
        "hyperlink",
-       "pager"
+       "pager",
+       "default"
 };
 
+/* The first 11 glk tag names must correspond to the first 11 tag names as defined above */
 static const gchar* GLK_TAG_NAMES[] = {
        "glk-normal",
        "glk-emphasized",
@@ -69,11 +72,17 @@ static const gchar* GLK_TAG_NAMES[] = {
        "glk-user2"
 };
 
+const gchar**
+style_get_tag_names()
+{
+       return TAG_NAMES;
+}
+
 /* Internal function: mapping from style enum to tag name */
-static gchar*
+static const gchar*
 get_tag_name(glui32 style)
 {
-       if(style >= NUM_STYLES) {
+       if(style >= CHIMARA_NUM_STYLES) {
                WARNING("Unsupported style");
                return "normal";
        } else {
@@ -82,7 +91,7 @@ get_tag_name(glui32 style)
 }
 
 /* Internal function: mapping from glk style enum to tag name */
-static gchar*
+static const gchar*
 get_glk_tag_name(glui32 style)
 {
        if(style >= style_NUMSTYLES) {
@@ -106,8 +115,8 @@ glk_set_style_stream(strid_t str, glui32 styl) {
                return;
 
        flush_window_buffer(str->window);
-       str->style = get_tag_name(styl);
-       str->glk_style = get_glk_tag_name(styl);
+       str->style = (gchar*) get_tag_name(styl);
+       str->glk_style = (gchar*) get_glk_tag_name(styl);
 }
 
 /* Internal function: call this to initialize the layout of the 'more' prompt. */
@@ -129,7 +138,7 @@ style_init_textbuffer(GtkTextBuffer *buffer)
        ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
 
        /* Place the default text tags in the textbuffer's tag table */
-       g_hash_table_foreach(glk_data->styles->text_buffer, style_add_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
+       g_hash_table_foreach(glk_data->styles->text_buffer, style_copy_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
 
        /* Copy the override text tags to the textbuffers's tag table */
        g_hash_table_foreach(glk_data->glk_styles->text_buffer, style_copy_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
@@ -145,7 +154,7 @@ style_init_textgrid(GtkTextBuffer *buffer)
        ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
 
        /* Place the default text tags in the textbuffer's tag table */
-       g_hash_table_foreach(glk_data->styles->text_grid, style_add_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
+       g_hash_table_foreach(glk_data->styles->text_grid, style_copy_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
 
        /* Copy the current text tags to the textbuffers's tag table */
        g_hash_table_foreach(glk_data->glk_styles->text_grid, style_copy_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
@@ -206,6 +215,7 @@ gtk_text_tag_copy(GtkTextTag *tag)
                _COPY_FLAG (invisible_set);
                _COPY_FLAG (editable_set);
                _COPY_FLAG (language_set);
+               _COPY_FLAG (scale_set);
        #undef _COPY_FLAG
 
        /* Copy the data that was added manually */
@@ -233,12 +243,17 @@ style_init(ChimaraGlk *glk)
        PangoFontDescription *monospace_font_desc = pango_font_description_from_string("Monospace");
        
        /* Initialise the default styles for a text grid */
-       tag = gtk_text_tag_new("normal");
+       tag = gtk_text_tag_new("default");
        g_object_set(tag, "font-desc", monospace_font_desc, NULL);
+       g_hash_table_insert(default_text_grid_styles, "default", tag);
+
+       tag = gtk_text_tag_new("normal");
+       //g_object_set(tag, "font-desc", monospace_font_desc, NULL);
        g_hash_table_insert(default_text_grid_styles, "normal", tag);
 
        tag = gtk_text_tag_new("emphasized");
-       g_object_set(tag, "font-desc", monospace_font_desc, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
+       //g_object_set(tag, "font-desc", monospace_font_desc, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
+       g_object_set(tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
        g_hash_table_insert(default_text_grid_styles, "emphasized", tag);
 
        tag = gtk_text_tag_new("preformatted");
@@ -246,35 +261,40 @@ style_init(ChimaraGlk *glk)
        g_hash_table_insert(default_text_grid_styles, "preformatted", tag);
 
        tag = gtk_text_tag_new("header");
-       g_object_set(tag, "font-desc", monospace_font_desc, "weight", PANGO_WEIGHT_BOLD, NULL);
+       //g_object_set(tag, "font-desc", monospace_font_desc, "weight", PANGO_WEIGHT_BOLD, NULL);
+       g_object_set(tag, "weight", PANGO_WEIGHT_BOLD, NULL);
        g_hash_table_insert(default_text_grid_styles, "header", tag);
 
        tag = gtk_text_tag_new("subheader");
-       g_object_set(tag, "font-desc", monospace_font_desc, "weight", PANGO_WEIGHT_BOLD, NULL);
+       //g_object_set(tag, "font-desc", monospace_font_desc, "weight", PANGO_WEIGHT_BOLD, NULL);
+       g_object_set(tag, "weight", PANGO_WEIGHT_BOLD, NULL);
        g_hash_table_insert(default_text_grid_styles, "subheader", tag);
 
        tag = gtk_text_tag_new("alert");
-       g_object_set(tag, "font-desc", monospace_font_desc, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
+       //g_object_set(tag, "font-desc", monospace_font_desc, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
+       g_object_set(tag, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
        g_hash_table_insert(default_text_grid_styles, "alert", tag);
 
        tag = gtk_text_tag_new("note");
-       g_object_set(tag, "font-desc", monospace_font_desc, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
+       //g_object_set(tag, "font-desc", monospace_font_desc, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
+       g_object_set(tag, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
        g_hash_table_insert(default_text_grid_styles, "note", tag);
 
        tag = gtk_text_tag_new("block-quote");
-       g_object_set(tag, "font-desc", monospace_font_desc, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
+       //g_object_set(tag, "font-desc", monospace_font_desc, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
+       g_object_set(tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
        g_hash_table_insert(default_text_grid_styles, "block-quote", tag);
 
        tag = gtk_text_tag_new("input");
-       g_object_set(tag, "font-desc", monospace_font_desc, NULL);
+       //g_object_set(tag, "font-desc", monospace_font_desc, NULL);
        g_hash_table_insert(default_text_grid_styles, "input", tag);
 
        tag = gtk_text_tag_new("user1");
-       g_object_set(tag, "font-desc", monospace_font_desc, NULL);
+       //g_object_set(tag, "font-desc", monospace_font_desc, NULL);
        g_hash_table_insert(default_text_grid_styles, "user1", tag);
 
        tag = gtk_text_tag_new("user2");
-       g_object_set(tag, "font-desc", monospace_font_desc, NULL);
+       //g_object_set(tag, "font-desc", monospace_font_desc, NULL);
        g_hash_table_insert(default_text_grid_styles, "user2", tag);
 
        tag = gtk_text_tag_new("hyperlink");
@@ -282,12 +302,17 @@ style_init(ChimaraGlk *glk)
        g_hash_table_insert(default_text_grid_styles, "hyperlink", tag);
 
        /* Initialise the default styles for a text buffer */
-       tag = gtk_text_tag_new("normal");
+       tag = gtk_text_tag_new("default");
        g_object_set(tag, "font-desc", default_font_desc, NULL);
+       g_hash_table_insert(default_text_buffer_styles, "default", tag);
+
+       tag = gtk_text_tag_new("normal");
+       //g_object_set(tag, "font-desc", default_font_desc, NULL);
        g_hash_table_insert(default_text_buffer_styles, "normal", tag);
 
        tag = gtk_text_tag_new("emphasized");
-       g_object_set(tag, "font-desc", default_font_desc, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
+       //g_object_set(tag, "font-desc", default_font_desc, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
+       g_object_set(tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
        g_hash_table_insert(default_text_buffer_styles, "emphasized", tag);
 
        tag = gtk_text_tag_new("preformatted");
@@ -295,39 +320,45 @@ style_init(ChimaraGlk *glk)
        g_hash_table_insert(default_text_buffer_styles, "preformatted", tag);
 
        tag = gtk_text_tag_new("header");
-       g_object_set(tag, "font-desc", default_font_desc, "size-points", 18.0, "weight", PANGO_WEIGHT_BOLD, NULL);
+       //g_object_set(tag, "font-desc", default_font_desc, "size-points", 18.0, "weight", PANGO_WEIGHT_BOLD, NULL);
+       g_object_set(tag, "weight", PANGO_WEIGHT_BOLD, NULL);
        g_hash_table_insert(default_text_buffer_styles, "header", tag);
 
        tag = gtk_text_tag_new("subheader");
-       g_object_set(tag, "font-desc", default_font_desc, "size-points", 14.0, "weight", PANGO_WEIGHT_BOLD, NULL);
+       //g_object_set(tag, "font-desc", default_font_desc, "size-points", 14.0, "weight", PANGO_WEIGHT_BOLD, NULL);
+       g_object_set(tag, "weight", PANGO_WEIGHT_BOLD, NULL);
        g_hash_table_insert(default_text_buffer_styles, "subheader", tag);
 
        tag = gtk_text_tag_new("alert");
-       g_object_set(tag, "font-desc", default_font_desc, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
+       //g_object_set(tag, "font-desc", default_font_desc, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
+       g_object_set(tag, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
        g_hash_table_insert(default_text_buffer_styles, "alert", tag);
 
        tag = gtk_text_tag_new("note");
-       g_object_set(tag, "font-desc", default_font_desc, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
+       //g_object_set(tag, "font-desc", default_font_desc, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
+       g_object_set(tag, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
        g_hash_table_insert(default_text_buffer_styles, "note", tag);
 
        tag = gtk_text_tag_new("block-quote");
-       g_object_set(tag, "font-desc", default_font_desc, "justification", GTK_JUSTIFY_CENTER, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
+       //g_object_set(tag, "font-desc", default_font_desc, "justification", GTK_JUSTIFY_CENTER, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
+       g_object_set(tag, "justification", GTK_JUSTIFY_CENTER, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
        g_hash_table_insert(default_text_buffer_styles, "block-quote", tag);
 
        tag = gtk_text_tag_new("input");
-       g_object_set(tag, "font-desc", default_font_desc, NULL);
+       //g_object_set(tag, "font-desc", default_font_desc, NULL);
        g_hash_table_insert(default_text_buffer_styles, "input", tag);
 
        tag = gtk_text_tag_new("user1");
-       g_object_set(tag, "font-desc", default_font_desc, NULL);
+       //g_object_set(tag, "font-desc", default_font_desc, NULL);
        g_hash_table_insert(default_text_buffer_styles, "user1", tag);
 
        tag = gtk_text_tag_new("user2");
-       g_object_set(tag, "font-desc", default_font_desc, NULL);
+       //g_object_set(tag, "font-desc", default_font_desc, NULL);
        g_hash_table_insert(default_text_buffer_styles, "user2", tag);
 
        tag = gtk_text_tag_new("hyperlink");
-       g_object_set(tag, "font-desc", default_font_desc, "foreground", "#0000ff", "underline", PANGO_UNDERLINE_SINGLE, "underline-set", TRUE, NULL);
+       //g_object_set(tag, "font-desc", default_font_desc, "foreground", "#0000ff", "underline", PANGO_UNDERLINE_SINGLE, "underline-set", TRUE, NULL);
+       g_object_set(tag, "foreground", "#0000ff", "underline", PANGO_UNDERLINE_SINGLE, "underline-set", TRUE, NULL);
        g_hash_table_insert(default_text_buffer_styles, "hyperlink", tag);
 
        GtkTextTag *pager_tag = gtk_text_tag_new("pager");
@@ -347,6 +378,7 @@ style_init(ChimaraGlk *glk)
        for(i=0; i<style_NUMSTYLES; i++) {
                tag = gtk_text_tag_new(GLK_TAG_NAMES[i]);
                g_hash_table_insert(glk_text_grid_styles, (gchar*) GLK_TAG_NAMES[i], tag);
+               tag = gtk_text_tag_new(GLK_TAG_NAMES[i]);
                g_hash_table_insert(glk_text_buffer_styles, (gchar*) GLK_TAG_NAMES[i], tag);
        }
 
@@ -441,29 +473,38 @@ style_accept_style_selector(GScanner *scanner, ChimaraGlk *glk)
 
        field = g_strdup(value.v_identifier);
 
-       if( !style_accept(scanner, '.') )
-               return FALSE;
+       /* Parse the tag name to change */
+       if( g_scanner_peek_next_token(scanner) == '{') {
+               style_accept(scanner, '{');
+               if( !strcmp(field, "buffer") )
+                       current_tag = g_hash_table_lookup(priv->styles->text_buffer, "default");
+               else
+                       current_tag = g_hash_table_lookup(priv->styles->text_grid, "default");
+       } else {
+               if( !style_accept(scanner, '.') )
+                       return FALSE;
 
-       token = g_scanner_get_next_token(scanner);
-       value = g_scanner_cur_value(scanner);
+               token = g_scanner_get_next_token(scanner);
+               value = g_scanner_cur_value(scanner);
 
-       if(token != G_TOKEN_IDENTIFIER) {
-               g_scanner_error(scanner, "CSS Error: style selector expected");
-               return FALSE;
-       }
+               if(token != G_TOKEN_IDENTIFIER) {
+                       g_scanner_error(scanner, "CSS Error: style selector expected");
+                       return FALSE;
+               }
 
-       if( !strcmp(field, "buffer") )
-               current_tag = g_hash_table_lookup(priv->styles->text_buffer, value.v_identifier);
-       else
-               current_tag = g_hash_table_lookup(priv->styles->text_grid, value.v_identifier);
+               if( !strcmp(field, "buffer") )
+                       current_tag = g_hash_table_lookup(priv->styles->text_buffer, value.v_identifier);
+               else
+                       current_tag = g_hash_table_lookup(priv->styles->text_grid, value.v_identifier);
 
-       if(current_tag == NULL) {
-               g_scanner_error(scanner, "CSS Error: invalid style identifier");
-               return FALSE;
-       }
+               if(current_tag == NULL) {
+                       g_scanner_error(scanner, "CSS Error: invalid style identifier");
+                       return FALSE;
+               }
 
-       if( !style_accept(scanner, '{') )
-               return FALSE;
+               if( !style_accept(scanner, '{') )
+                       return FALSE;
+       }
 
        while( g_scanner_peek_next_token(scanner) != '}') {
                if( !style_accept_style_hint(scanner, current_tag) )
@@ -616,19 +657,6 @@ style_accept_style_hint(GScanner *scanner, GtkTextTag *current_tag)
        return TRUE;
 }
 
-/* Internal function: parses a glk color to a #hex-value */
-static void
-glkcolor_to_hex(glui32 val, gchar *buffer)
-{
-       g_return_if_fail(buffer != NULL);
-
-       sprintf(buffer, "#%02X%02X%02X",
-               ((val & 0xff0000) >> 16),
-               ((val & 0x00ff00) >> 8),
-               (val & 0x0000ff)
-       );
-}
-
 /* Internal function: parses a glk color to a GdkColor */
 void
 glkcolor_to_gdkcolor(glui32 val, GdkColor *color)
@@ -658,7 +686,7 @@ apply_stylehint_to_tag(GtkTextTag *tag, GtkTextTag *default_tag, glui32 wintype,
        gint reverse_color = GPOINTER_TO_INT( g_object_get_data(tag_object, "reverse-color") );
 
        int i = 0;
-       gchar color[20];
+       GdkColor color;
        switch(hint) {
        case stylehint_Indentation:
                g_object_set(tag_object, "left-margin", 5*val, "left-margin-set", TRUE, NULL);
@@ -693,7 +721,23 @@ apply_stylehint_to_tag(GtkTextTag *tag, GtkTextTag *default_tag, glui32 wintype,
                break;
 
        case stylehint_Size:
-               g_object_set(tag_object, "size", 14+(2*val), "size-set", TRUE, NULL);
+               {
+                       gdouble scale = PANGO_SCALE_MEDIUM;
+                       switch(val) {
+                               case -3: scale = PANGO_SCALE_XX_SMALL; break;
+                               case -2: scale = PANGO_SCALE_X_SMALL; break;
+                               case -1: scale = PANGO_SCALE_SMALL; break;
+                               case  0: scale = PANGO_SCALE_MEDIUM; break;
+                               case  1: scale = PANGO_SCALE_LARGE; break;
+                               case  2: scale = PANGO_SCALE_X_LARGE; break;
+                               case  3: scale = PANGO_SCALE_XX_LARGE; break;
+                               default:
+                                       /* We follow Pango's convention of having each magnification
+                                       step be a scaling of 1.2 */
+                                       scale = pow(1.2, (double)val);
+                       }
+                       g_object_set(tag_object, "scale", scale, "scale-set", TRUE, NULL);
+               }
                break;
 
        case stylehint_Oblique:
@@ -705,7 +749,7 @@ apply_stylehint_to_tag(GtkTextTag *tag, GtkTextTag *default_tag, glui32 wintype,
                gchar *font_family;
                GtkTextTag *font_tag = g_hash_table_lookup(
                    wintype == wintype_TextBuffer? glk_data->styles->text_buffer : glk_data->styles->text_grid,
-                   val? "normal" : "preformatted");
+                   val? "default" : "preformatted");
                g_object_get(font_tag, "family", &font_family, NULL);
                g_object_set(tag_object, "family", font_family, "family-set", TRUE, NULL);
                g_free(font_family);
@@ -713,22 +757,22 @@ apply_stylehint_to_tag(GtkTextTag *tag, GtkTextTag *default_tag, glui32 wintype,
                break;
 
        case stylehint_TextColor:
-               glkcolor_to_hex(val, color);
+               glkcolor_to_gdkcolor(val, &color);
 
                if(!reverse_color)
-                       g_object_set(tag_object, "foreground", color, "foreground-set", TRUE, NULL);
+                       g_object_set(tag_object, "foreground", &color, "foreground-set", TRUE, NULL);
                else
-                       g_object_set(tag_object, "background", color, "background-set", TRUE, NULL);
+                       g_object_set(tag_object, "background", &color, "background-set", TRUE, NULL);
 
                break;
 
        case stylehint_BackColor:
-               glkcolor_to_hex(val, color);
+               glkcolor_to_gdkcolor(val, &color);
 
                if(!reverse_color)
-                       g_object_set(tag_object, "background", color, "background-set", TRUE, NULL);
+                       g_object_set(tag_object, "background", &color, "background-set", TRUE, NULL);
                else
-                       g_object_set(tag_object, "foreground", color, "background-set", TRUE, NULL);
+                       g_object_set(tag_object, "foreground", &color, "background-set", TRUE, NULL);
 
                break;
 
@@ -774,6 +818,7 @@ static gint
 query_tag(GtkTextTag *tag, glui32 wintype, glui32 hint)
 {
        gint intval;
+       gdouble doubleval;
        GdkColor *colval;
 
        g_return_val_if_fail(tag != NULL, 0);
@@ -811,8 +856,8 @@ query_tag(GtkTextTag *tag, glui32 wintype, glui32 hint)
                }
 
        case stylehint_Size:
-               g_object_get(tag, "size", &intval, NULL);
-               return (intval/2)-14;
+               g_object_get(tag, "scale", &doubleval, NULL);
+               return (gint)round(log(doubleval) / log(1.2));
 
        case stylehint_Oblique:
                g_object_get(tag, "style", &intval , NULL);
@@ -1044,21 +1089,21 @@ PangoFontDescription *
 get_current_font(guint32 wintype)
 {
        ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
-       GtkTextTag *normal;
+       GtkTextTag *tag;
 
        switch(wintype) {
        case wintype_TextGrid:
-               normal = g_hash_table_lookup(glk_data->styles->text_grid, "normal");
+               tag = g_hash_table_lookup(glk_data->styles->text_grid, "default");
                break;
        case wintype_TextBuffer:
-               normal = g_hash_table_lookup(glk_data->styles->text_buffer, "normal");
+               tag = g_hash_table_lookup(glk_data->styles->text_buffer, "default");
                break;
        default:
                return NULL;
        }
 
        PangoFontDescription *font;
-       g_object_get( G_OBJECT(normal), "font-desc", &font, NULL );
+       g_object_get( G_OBJECT(tag), "font-desc", &font, NULL );
 
        return font;
 }
@@ -1120,3 +1165,13 @@ text_tag_to_attr_list(GtkTextTag *tag, PangoAttrList *list)
                );
        }
 }
+
+/* Update pager and reverse video tags */
+void
+style_update(ChimaraGlk *glk)
+{
+       CHIMARA_GLK_USE_PRIVATE(glk, priv);
+
+       GtkTextTag *pager_tag = GTK_TEXT_TAG( g_hash_table_lookup(priv->styles->text_buffer, "pager") );
+       text_tag_to_attr_list(pager_tag, priv->pager_attr_list);
+}