From a7665032bcdacab5d3441eb9d03cfe6b0c0c4923 Mon Sep 17 00:00:00 2001
From: rodin <rodin@ddfedd41-794f-dd11-ae45-00112f111e67>
Date: Fri, 22 May 2009 20:09:30 +0000
Subject: [PATCH]  * Added initial support for styles! (no style hints as of
 yet though)  * Added glkstart.c, a dummy file to help us implement the
 unixstart functionality

---
 src/Makefile.am           |   2 +-
 src/chimara-glk-private.h |   2 +-
 src/chimara-glk.c         |   2 +-
 src/glkstart.c            |  24 +++++
 src/main.c                |   2 +-
 src/stream.c              |   1 +
 src/stream.h              |   2 +
 src/strio.c               |   2 +-
 src/style.c               | 197 +++++++++++++++++++++++++++++++++++++-
 src/style.h               |  12 +++
 src/window.c              |   3 +
 src/window.h              |   2 +
 12 files changed, 242 insertions(+), 9 deletions(-)
 create mode 100644 src/glkstart.c
 create mode 100644 src/style.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 29d42f7..4a2806e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -29,11 +29,11 @@ libchimara_la_SOURCES = \
 	input.c input.h \
 	stream.c stream.h \
 	strio.c \
-	style.c \
 	timer.c timer.h \
 	window.c window.h \
 	gi_blorb.c gi_blorb.h \
 	resource.c resource.h \
+	style.c style.h \
 	glkstart.h
 libchimara_la_CPPFLAGS = \
 	-DG_LOG_DOMAIN=\"Chimara\"
diff --git a/src/chimara-glk-private.h b/src/chimara-glk-private.h
index 9520f1b..ba638a0 100644
--- a/src/chimara-glk-private.h
+++ b/src/chimara-glk-private.h
@@ -52,7 +52,7 @@ struct _ChimaraGlkPrivate {
 	/* Current resource blorb map */
 	giblorb_map_t *resource_map;
 	/* File stream pointing to the blorb used as current resource map */
-	strid_t *resource_file;
+	strid_t resource_file;
 };
 
 #define CHIMARA_GLK_PRIVATE(obj) \
diff --git a/src/chimara-glk.c b/src/chimara-glk.c
index 14d1e44..16e6749 100644
--- a/src/chimara-glk.c
+++ b/src/chimara-glk.c
@@ -923,7 +923,7 @@ chimara_glk_run(ChimaraGlk *glk, gchar *plugin, GError **error)
     /* Set the thread's private data */
     /* TODO: Do this with a GPrivate */
     glk_data = priv;
-    
+
     /* Run in a separate thread */
 	priv->thread = g_thread_create(glk_enter, glk_main, TRUE, error);
 	
diff --git a/src/glkstart.c b/src/glkstart.c
new file mode 100644
index 0000000..e9baf36
--- /dev/null
+++ b/src/glkstart.c
@@ -0,0 +1,24 @@
+/* glkstart.c: Unix-specific startup code -- sample file.
+    Designed by Andrew Plotkin <erkyrath@eblong.com>
+    http://www.eblong.com/zarf/glk/index.html
+
+    This is Unix startup code for the simplest possible kind of Glk
+    program -- no command-line arguments; no startup files; no nothing.
+
+    Remember, this is a sample file. You should copy it into the Glk
+    program you are compiling, and modify it to your needs. This should
+    *not* be compiled into the Glk library itself.
+*/
+
+#include "glk.h"
+#include "glkstart.h"
+
+glkunix_argumentlist_t glkunix_arguments[] = {
+    { NULL, glkunix_arg_End, NULL }
+};
+
+int glkunix_startup_code(glkunix_startup_t *data)
+{
+    return TRUE;
+}
+
diff --git a/src/main.c b/src/main.c
index 9362559..c34e4c5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -118,7 +118,7 @@ main(int argc, char *argv[])
 
 	g_object_unref( G_OBJECT(builder) );
 
-    if( !chimara_glk_run(CHIMARA_GLK(glk), "../interpreters/nitfol/.libs/nitfol.so", &error) ) {
+    if( !chimara_glk_run(CHIMARA_GLK(glk), ".libs/first.so", &error) ) {
         error_dialog(GTK_WINDOW(window), error, "Error starting Glk library: ");
         return 1;
     }
diff --git a/src/stream.c b/src/stream.c
index 42d77ba..8043992 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -33,6 +33,7 @@ window_stream_new(winid_t window)
 	/* Create stream and connect it to window */
 	strid_t str = stream_new_common(0, filemode_Write, STREAM_TYPE_WINDOW);
 	str->window = window;
+	str->style = "normal";
 	return str;
 }
 
diff --git a/src/stream.h b/src/stream.h
index 578b140..0ea398e 100644
--- a/src/stream.h
+++ b/src/stream.h
@@ -43,6 +43,8 @@ struct glk_stream_struct
 	FILE *file_pointer;
 	gboolean binary;
 	gchar *filename; /* Displayable filename in UTF-8 for error handling */
+
+	gchar *style; /* Name of the current style */
 };
 
 G_GNUC_INTERNAL strid_t window_stream_new(winid_t window);
diff --git a/src/strio.c b/src/strio.c
index e30d52b..921b75c 100644
--- a/src/strio.c
+++ b/src/strio.c
@@ -77,7 +77,7 @@ write_utf8_to_window(winid_t win, gchar *s)
 
 	GtkTextIter iter;
 	gtk_text_buffer_get_end_iter(buffer, &iter);
-	gtk_text_buffer_insert(buffer, &iter, s, -1);
+	gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, s, -1, win->window_stream->style, NULL);
 
 	gdk_threads_leave();
 }
diff --git a/src/style.c b/src/style.c
index bce1516..d2eb235 100644
--- a/src/style.c
+++ b/src/style.c
@@ -1,8 +1,197 @@
-#include "glk.h"
+#include "style.h"
 
+extern ChimaraGlkPrivate *glk_data;
+
+/**
+ * glk_set_style:
+ * @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.
+ */
+void
+glk_set_style(glui32 style)
+{
+	g_return_if_fail(glk_data->current_stream != NULL);
+	glk_set_style_stream(glk_data->current_stream, style);
+}
+
+/* Internal function: mapping from style enum to tag name */
+gchar*
+get_tag_name(glui32 style)
+{
+	switch(style) {
+		case style_Normal: return "normal";
+		case style_Emphasized: return "emphasized";
+		case style_Preformatted: return "preformatted";
+		case style_Header: return "header";
+		case style_Subheader: return "subheader";
+		case style_Alert: return "alert";
+		case style_Note: return "note";
+		case style_BlockQuote: return "block-quote";
+		case style_Input: return "input";
+		case style_User1: return "user1";
+		case style_User2: return "user2";
+	}
+
+	WARNING("Unsupported style");
+	return "normal";
+}
+
+void
+glk_set_style_stream(strid_t stream, glui32 style) {
+	stream->style = get_tag_name(style);
+}
+
+/* Internal function: call this to initialize the default styles to a textbuffer. */
 void
-glk_set_style(glui32 styl)
+style_init_textbuffer(GtkTextBuffer *buffer)
 {
-	/* No nothing yet */
-	return;
+	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);
+}
+
+void
+color_format(glui32 val, gchar *buffer)
+{
+	sprintf(buffer, "#%02X%02X%02X",
+		((val & 0xff0000) >> 16),
+		((val & 0x00ff00) >> 8),
+		(val & 0x0000ff)
+	);
+}
+
+/* Internal function: changes a GTK tag to correspond with the given style. */
+void
+apply_stylehint_to_tag(GtkTextTag *tag, glui32 hint, glsi32 val)
+{
+	g_return_if_fail(tag != NULL);
+
+	GObject *tag_object = G_OBJECT(tag);
+	gint reverse_color = 0;
+
+	/* FIXME where should we keep track of this?
+	g_object_get(tag, "reverse_color", &reverse_color, NULL);
+	*/
+
+	int i = 0;
+	gchar color[20];
+	switch(hint) {
+	case stylehint_Indentation:
+		g_object_set(tag_object, "left_margin", 5*val, NULL);
+		g_object_set(tag_object, "right_margin", 5*val, NULL);
+		break;
+	
+	case stylehint_ParaIndentation:
+		g_object_set(tag_object, "indent", 5*val, NULL);
+		break;
+
+	case stylehint_Justification:
+		switch(val) {
+			case stylehint_just_LeftFlush:  i = GTK_JUSTIFY_LEFT; break;
+			case stylehint_just_LeftRight:  i = GTK_JUSTIFY_FILL; break;
+			case stylehint_just_Centered:   i = GTK_JUSTIFY_CENTER; break;
+			case stylehint_just_RightFlush: i = GTK_JUSTIFY_RIGHT; break;
+			default: 
+				WARNING("Unknown justification");
+				i = GTK_JUSTIFY_LEFT;
+		}
+		g_object_set(tag_object, "justification", i, NULL);
+		break;
+
+	case stylehint_Weight:
+		switch(val) {
+			case -1: i = PANGO_WEIGHT_LIGHT; break;
+			case  0: i = PANGO_WEIGHT_NORMAL; break;
+			case  1: i = PANGO_WEIGHT_BOLD; break;
+			default: WARNING("Unknown font weight");
+		}
+		g_object_set(tag_object, "weight", i, NULL);
+		break;
+
+	case stylehint_Size:
+		g_object_set(tag_object, "size", 14+(2*val), NULL);
+		break;
+
+	case stylehint_Oblique:
+		g_object_set(tag_object, "style", val ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL, NULL);
+		break;
+
+	case stylehint_Proportional:
+		g_object_set(tag_object, "font-desc", val ? glk_data->default_font_desc : glk_data->monospace_font_desc, NULL);
+		break;
+
+	case stylehint_TextColor:
+		color_format(val, color);
+
+		if(!reverse_color)
+			g_object_set(tag_object, "foreground", color, NULL);
+		else
+			g_object_set(tag_object, "background", color, NULL);
+
+		break;
+
+	case stylehint_BackColor:
+		color_format(val, color);
+
+		if(!reverse_color)
+			g_object_set(tag_object, "background", color, NULL);
+		else
+			g_object_set(tag_object, "foreground", color, NULL);
+
+		break;
+
+	case stylehint_ReverseColor:
+		if(reverse_color != val) {
+			/* Flip the fore- and background colors */
+			gchar* foreground_color;
+			gchar* background_color;
+			g_object_get(tag_object, "foreground", &foreground_color, NULL);
+			g_object_get(tag_object, "background", &background_color, NULL);
+			g_object_set(tag_object, "foreground", background_color, NULL);
+			g_object_set(tag_object, "background", foreground_color, NULL);
+			g_free(foreground_color);
+			g_free(background_color);
+		}
+		break;
+
+	default:
+		WARNING("Unknown style hint");
+	}
+}
+
+void
+glk_stylehint_set(glui32 wintype, glui32 style, glui32 hint, glsi32 val)
+{
+
+	gchar *tag_name = get_tag_name(style);
+
+	/* Iterate over all the window and update their styles if nessecary */
+	winid_t win = glk_window_iterate(NULL, NULL);
+	while(win != NULL) {
+		if(wintype != wintype_TextBuffer)
+			continue; /* FIXME: add support for text grid windows */
+
+		if(wintype == wintype_AllTypes || glk_window_get_type(win) == wintype) {
+			GtkWidget *textview = win->widget;
+			GtkTextBuffer *textbuffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(textview) );
+			GtkTextTagTable *table = gtk_text_buffer_get_tag_table(textbuffer);
+			GtkTextTag *to_change = gtk_text_tag_table_lookup(table, tag_name);
+
+			apply_stylehint_to_tag(to_change, hint, val);
+		}
+	}
 }
diff --git a/src/style.h b/src/style.h
new file mode 100644
index 0000000..fcd5302
--- /dev/null
+++ b/src/style.h
@@ -0,0 +1,12 @@
+#ifndef STYLE_H
+#define STYLE_H
+
+#include <gtk/gtk.h>
+#include "glk.h"
+#include "magic.h"
+#include "chimara-glk-private.h"
+#include "stream.h"
+
+G_GNUC_INTERNAL void style_init_textbuffer(GtkTextBuffer *buffer);
+
+#endif
diff --git a/src/window.c b/src/window.c
index 719816f..5f2bd5b 100644
--- a/src/window.c
+++ b/src/window.c
@@ -486,6 +486,9 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype,
 			(for line input) */
 			gtk_text_buffer_create_tag(textbuffer, "uneditable", "editable", FALSE, "editable-set", TRUE, NULL);
 
+			/* Create the default styles available to the window stream */
+			style_init_textbuffer(textbuffer);
+
 			/* Mark the position where the user will input text */
 			GtkTextIter end;
 			gtk_text_buffer_get_end_iter(textbuffer, &end);
diff --git a/src/window.h b/src/window.h
index 39c4314..b3b824a 100644
--- a/src/window.h
+++ b/src/window.h
@@ -8,6 +8,8 @@
 #include "error.h"
 #include "callbacks.h"
 #include "input.h"
+#include "style.h"
+
 
 enum InputRequestType
 {
-- 
2.30.2