Added first support for CSS files.
authorrodin <rodin@ddfedd41-794f-dd11-ae45-00112f111e67>
Mon, 5 Oct 2009 10:02:40 +0000 (10:02 +0000)
committerrodin <rodin@ddfedd41-794f-dd11-ae45-00112f111e67>
Mon, 5 Oct 2009 10:02:40 +0000 (10:02 +0000)
The default style.css is not added and can be changed to alter the visual appearance.
More style options can be added in time.

Changing styles from the GLK interface is still a mess though.

libchimara/chimara-glk-private.h
libchimara/chimara-glk.c
libchimara/style.c
libchimara/style.h
tests/chimara.menus
tests/style.css [new file with mode: 0644]

index e1067b247506fd78aea1b759709182e470807175..25417d368524d95c18533cd5b783c1d5fa767611 100644 (file)
@@ -27,6 +27,10 @@ struct _ChimaraGlkPrivate {
        PangoFontDescription *monospace_font_desc;
        /* Spacing between Glk windows */
        guint spacing;
+       /* The CSS file to read style defaults from */
+       gchar *css_file;
+       /* Hashtable containing the default styles */
+       GHashTable *default_styles;
 
        /* *** Threading data *** */
     /* Glk program loaded in widget */
index ae39f168c892e49639e39af559b9ebaf5cb0c3c8..e0b60592b2e842ff988e35f53c5cb4fd8c70e184 100644 (file)
@@ -78,6 +78,8 @@ chimara_glk_init(ChimaraGlk *self)
     priv->protect = FALSE;
        priv->default_font_desc = pango_font_description_from_string("Sans");
        priv->monospace_font_desc = pango_font_description_from_string("Monospace");
+       priv->css_file = "style.css";
+       priv->default_styles = g_hash_table_new(g_str_hash, g_str_equal);
     priv->program = NULL;
     priv->thread = NULL;
     priv->event_queue = NULL;
@@ -188,6 +190,7 @@ chimara_glk_finalize(GObject *object)
        pango_font_description_free(priv->default_font_desc);
        pango_font_description_free(priv->monospace_font_desc);
        g_free(priv->current_dir);
+       g_hash_table_destroy(priv->default_styles);
        
     G_OBJECT_CLASS(chimara_glk_parent_class)->finalize(object);
 }
@@ -688,7 +691,7 @@ chimara_glk_new(void)
     priv->abort_lock = g_mutex_new();
        priv->arrange_lock = g_mutex_new();
        priv->rearranged = g_cond_new();
-    
+
     return GTK_WIDGET(self);
 }
 
index 3826ebfc4d1fcf482de4fc8c8ee7cb81f97166f6..3f86bad2573b2f54565e6ef605b266c1183f03f9 100644 (file)
@@ -1,6 +1,9 @@
 #include "style.h"
+#include <stdio.h>
+#include <fcntl.h>
 
 extern GPrivate *glk_data_key;
+static gboolean chimara_style_initialized = FALSE;
 
 /**
  * glk_set_style:
@@ -63,19 +66,265 @@ style_init_textbuffer(GtkTextBuffer *buffer)
 {
        g_return_if_fail(buffer != 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;
 
-       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", 18.0, "weight", PANGO_WEIGHT_BOLD, NULL);
-       gtk_text_buffer_create_tag(buffer, "subheader", "size-points", 14.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);
+       return TRUE;
 }
 
 static void
index fcd53022359b8b730e7512a73f8caddda52813b7..72af60a4083067dcda00e82eb07e842eb4614658 100644 (file)
@@ -2,11 +2,19 @@
 #define STYLE_H
 
 #include <gtk/gtk.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <stdio.h>
 #include "glk.h"
 #include "magic.h"
 #include "chimara-glk-private.h"
 #include "stream.h"
 
 G_GNUC_INTERNAL void style_init_textbuffer(GtkTextBuffer *buffer);
+G_GNUC_INTERNAL void style_init();
+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);
 
 #endif
index 233ec4c9b3cbb570a51f049d7e50231ac5698a24..4e3e7d45719db1c666449ff3d9ed4cc071ae0396 100644 (file)
@@ -10,5 +10,6 @@
   </menubar>
   <toolbar>
     <toolitem action="save"/>
+    <toolitem action="quit"/>
   </toolbar>
 </ui>
diff --git a/tests/style.css b/tests/style.css
new file mode 100644 (file)
index 0000000..0b97e76
--- /dev/null
@@ -0,0 +1,63 @@
+/* Possible selectors:
+ * normal
+ * emphasized
+ * preformatted
+ * header
+ * subheader
+ * alert
+ * note
+ * block-quote
+ * input
+ * user1
+ * user2
+ *
+ * Possible style hints:
+ * font-family (string)
+ * font-size (float)
+ * font-weight (normal/bold)
+ * font-style (normal/italic)
+ * color (#hex-value)
+ * background-color (#hex-value)
+ * text-align (left/right/center)
+ */
+
+
+normal {
+       font-family: 'times';
+       font-size: 12;
+}
+
+header {
+       font-size: 18;
+       font-weight: bold;
+       text-align: center;
+}
+
+subheader {
+       font-size: 14;
+       font-weight: bold;
+}
+
+alert {
+       color: #aa0000;
+       font-weight: bold;
+}
+
+note {
+       color: #aaaa00;
+       font-weight: bold;
+}
+
+block-quote {
+       text-align: center;
+       font-style: italic;
+}
+
+input {
+}
+
+user1 {
+}
+
+user2 {
+}