5 extern GPrivate *glk_data_key;
6 static gboolean chimara_style_initialized = FALSE;
7 static gboolean style_accept(GScanner *scanner, GTokenType token);
8 static gboolean style_accept_style_identifier(GScanner *scanner);
9 static gboolean style_accept_style_hint(GScanner *scanner, GtkTextTag *current_tag);
10 static void style_add_tag_to_textbuffer(gpointer key, gpointer tag, gpointer tag_table);
14 * @styl: The style to apply
16 * Changes the style of the current output stream. @styl should be one of the
17 * <code>style_</code> constants listed above. However, any value is actually
18 * legal; if the interpreter does not recognize the style value, it will treat
19 * it as %style_Normal.
21 * This policy allows for the future definition of styles without breaking old
26 glk_set_style(glui32 styl)
28 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
29 g_return_if_fail(glk_data->current_stream != NULL);
30 glk_set_style_stream(glk_data->current_stream, styl);
33 /* Internal function: mapping from style enum to tag name */
35 get_tag_name(glui32 style)
38 case style_Normal: return "normal";
39 case style_Emphasized: return "emphasized";
40 case style_Preformatted: return "preformatted";
41 case style_Header: return "header";
42 case style_Subheader: return "subheader";
43 case style_Alert: return "alert";
44 case style_Note: return "note";
45 case style_BlockQuote: return "block-quote";
46 case style_Input: return "input";
47 case style_User1: return "user1";
48 case style_User2: return "user2";
51 WARNING("Unsupported style");
56 * glk_set_style_stream:
57 * @str: Output stream to change the style of
58 * @styl: The style to apply
60 * This changes the style of the stream @str. See glk_set_style().
63 glk_set_style_stream(strid_t str, glui32 styl) {
64 str->style = get_tag_name(styl);
67 /* Internal function: call this to initialize the default styles to a textbuffer. */
69 style_init_textbuffer(GtkTextBuffer *buffer)
71 g_return_if_fail(buffer != NULL);
73 if( G_UNLIKELY(!chimara_style_initialized) ) {
77 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
78 g_hash_table_foreach(glk_data->default_styles, style_add_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
82 style_add_tag_to_textbuffer(gpointer key, gpointer tag, gpointer tag_table)
84 gtk_text_tag_table_add(tag_table, tag);
90 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
93 /* Create the CSS file scanner */
94 GScanner *scanner = g_scanner_new(NULL);
95 int f = open(glk_data->css_file, O_RDONLY);
96 g_return_if_fail(f != -1);
97 g_scanner_input_file(scanner, f);
98 scanner->input_name = glk_data->css_file;
99 scanner->config->cset_identifier_first = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#";
100 scanner->config->cset_identifier_nth = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_0123456789";
101 scanner->config->symbol_2_token = TRUE;
102 scanner->config->cpair_comment_single = NULL;
104 /* Initialise the default styles */
105 g_hash_table_insert(glk_data->default_styles, "normal", gtk_text_tag_new("normal"));
107 tag = gtk_text_tag_new("emphasized");
108 g_object_set(tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
109 g_hash_table_insert(glk_data->default_styles, "emphasized", tag);
111 tag = gtk_text_tag_new("preformatted");
112 g_object_set(tag, "font-desc", glk_data->monospace_font_desc, NULL);
113 g_hash_table_insert(glk_data->default_styles, "preformatted", tag);
115 tag = gtk_text_tag_new("header");
116 g_object_set(tag, "size-points", 18.0, "weight", PANGO_WEIGHT_BOLD, NULL);
117 g_hash_table_insert(glk_data->default_styles, "header", tag);
119 tag = gtk_text_tag_new("subheader");
120 g_object_set(tag, "size-points", 14.0, "weight", PANGO_WEIGHT_BOLD, NULL);
121 g_hash_table_insert(glk_data->default_styles, "subheader", tag);
123 tag = gtk_text_tag_new("alert");
124 g_object_set(tag, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
125 g_hash_table_insert(glk_data->default_styles, "alert", tag);
127 tag = gtk_text_tag_new("note");
128 g_object_set(tag, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
129 g_hash_table_insert(glk_data->default_styles, "note", tag);
131 tag = gtk_text_tag_new("block-quote");
132 g_object_set(tag, "justification", GTK_JUSTIFY_CENTER, "style", PANGO_STYLE_ITALIC, NULL);
133 g_hash_table_insert(glk_data->default_styles, "block-quote", tag);
135 g_hash_table_insert(glk_data->default_styles, "input", gtk_text_tag_new("input"));
136 g_hash_table_insert(glk_data->default_styles, "user1", gtk_text_tag_new("user1"));
137 g_hash_table_insert(glk_data->default_styles, "user2", gtk_text_tag_new("user2"));
139 /* Run the scanner over the CSS file */
140 while( g_scanner_peek_next_token(scanner) != G_TOKEN_EOF) {
141 if( !style_accept_style_identifier(scanner) )
145 g_scanner_destroy(scanner);
149 style_accept(GScanner *scanner, GTokenType token)
151 if( g_scanner_get_next_token(scanner) != token ) {
152 g_scanner_unexp_token(scanner, token, NULL, NULL, NULL, "CSS Error", 1);
160 style_accept_style_identifier(GScanner *scanner)
162 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
164 GtkTextTag *current_tag;
165 GTokenType token = g_scanner_get_next_token(scanner);
166 GTokenValue value = g_scanner_cur_value(scanner);
168 if(token != G_TOKEN_IDENTIFIER) {
169 g_scanner_error(scanner, "CSS Error: style identifier expected");
173 printf("Identifier: %s\n", value.v_identifier);
174 current_tag = g_hash_table_lookup(glk_data->default_styles, value.v_identifier);
176 if(current_tag == NULL) {
177 g_scanner_error(scanner, "CSS Error: invalid style identifier");
181 if( !style_accept(scanner, '{') )
184 while( g_scanner_peek_next_token(scanner) != '}') {
185 if( !style_accept_style_hint(scanner, current_tag) )
189 if( !style_accept(scanner, '}') )
196 style_accept_style_hint(GScanner *scanner, GtkTextTag *current_tag)
198 GTokenType token = g_scanner_get_next_token(scanner);
199 GTokenValue value = g_scanner_cur_value(scanner);
202 if(token != G_TOKEN_IDENTIFIER) {
203 g_scanner_error(scanner, "CSS Error: style hint expected");
207 hint = g_strdup(value.v_identifier);
208 printf("Hint: %s\n", hint);
210 if( !style_accept(scanner, ':') )
213 token = g_scanner_get_next_token(scanner);
214 value = g_scanner_cur_value(scanner);
216 if( !strcmp(hint, "font-family") ) {
217 if(token != G_TOKEN_STRING) {
218 g_scanner_error(scanner, "CSS Error: string expected");
221 g_object_set(current_tag, "family", value.v_string, "family-set", TRUE, NULL);
223 else if( !strcmp(hint, "font-weight") ) {
224 if(token != G_TOKEN_IDENTIFIER) {
225 g_scanner_error(scanner, "CSS Error: bold/normal expected");
229 if( !strcmp(value.v_identifier, "bold") )
230 g_object_set(current_tag, "weight", PANGO_WEIGHT_BOLD, "weight-set", TRUE, NULL);
231 else if( !strcmp(value.v_identifier, "normal") )
232 g_object_set(current_tag, "weight", PANGO_WEIGHT_NORMAL, "weight-set", TRUE, NULL);
234 g_scanner_error(scanner, "CSS Error: bold/normal expected");
238 else if( !strcmp(hint, "font-style") ) {
239 if(token != G_TOKEN_IDENTIFIER) {
240 g_scanner_error(scanner, "CSS Error: italic/normal expected");
244 if( !strcmp(value.v_identifier, "italic") )
245 g_object_set(current_tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
246 else if( !strcmp(value.v_identifier, "normal") )
247 g_object_set(current_tag, "style", PANGO_STYLE_NORMAL, "style-set", TRUE, NULL);
249 g_scanner_error(scanner, "CSS Error: italic/normal expected");
253 else if( !strcmp(hint, "font-size") ) {
254 if(token == G_TOKEN_INT)
255 g_object_set(current_tag, "size-points", (float)value.v_int, "size-set", TRUE, NULL);
256 else if(token == G_TOKEN_FLOAT)
257 g_object_set(current_tag, "size-points", value.v_float, "size-set", TRUE, NULL);
259 g_scanner_error(scanner, "CSS Error: integer or float expected");
263 else if( !strcmp(hint, "color") ) {
264 if(token != G_TOKEN_IDENTIFIER) {
265 g_scanner_error(scanner, "CSS Error: hex color expected");
268 g_object_set(current_tag, "foreground", value.v_identifier, "foreground-set", TRUE, NULL);
270 else if( !strcmp(hint, "background-color") ) {
271 if(token != G_TOKEN_IDENTIFIER) {
272 g_scanner_error(scanner, "CSS Error: hex color expected");
275 g_object_set(current_tag, "background", value.v_identifier, "background-set", TRUE, NULL);
277 else if( !strcmp(hint, "text-align") ) {
278 if(token != G_TOKEN_IDENTIFIER) {
279 g_scanner_error(scanner, "CSS Error: left/right/center expected");
283 if( !strcmp(value.v_identifier, "left") )
284 g_object_set(current_tag, "justification", GTK_JUSTIFY_LEFT, "justification-set", TRUE, NULL);
285 else if( !strcmp(value.v_identifier, "right") )
286 g_object_set(current_tag, "justification", GTK_JUSTIFY_RIGHT, "justification-set", TRUE, NULL);
287 else if( !strcmp(value.v_identifier, "center") )
288 g_object_set(current_tag, "justification", GTK_JUSTIFY_CENTER, "justification-set", TRUE, NULL);
290 g_scanner_error(scanner, "CSS Error: left/right/center expected");
294 else if( !strcmp(hint, "margin-left") ) {
295 if(token != G_TOKEN_INT) {
296 g_scanner_error(scanner, "CSS Error: integer expected");
299 g_object_set(current_tag, "left-margin", value.v_int, "left-margin-set", TRUE, NULL);
301 else if( !strcmp(hint, "margin-right") ) {
302 if(token != G_TOKEN_INT) {
303 g_scanner_error(scanner, "CSS Error: integer expected");
306 g_object_set(current_tag, "right-margin", value.v_int, "right-margin-set", TRUE, NULL);
308 else if( !strcmp(hint, "margin-top") ) {
309 if(token != G_TOKEN_INT) {
310 g_scanner_error(scanner, "CSS Error: integer expected");
313 g_object_set(current_tag, "pixels-above-lines", value.v_int, "pixels-above-lines-set", TRUE, NULL);
315 else if( !strcmp(hint, "margin-bottom") ) {
316 if(token != G_TOKEN_INT) {
317 g_scanner_error(scanner, "CSS Error: integer expected");
320 g_object_set(current_tag, "pixels-below-lines", value.v_int, "pixels-below-lines-set", TRUE, NULL);
324 g_scanner_error(scanner, "CSS Error: invalid style hint %s", hint);
328 if( !style_accept(scanner, ';') )
335 color_format(glui32 val, gchar *buffer)
337 sprintf(buffer, "#%02X%02X%02X",
338 ((val & 0xff0000) >> 16),
339 ((val & 0x00ff00) >> 8),
344 /* Internal function: changes a GTK tag to correspond with the given style. */
346 apply_stylehint_to_tag(GtkTextTag *tag, glui32 hint, glsi32 val)
348 g_return_if_fail(tag != NULL);
350 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
351 GObject *tag_object = G_OBJECT(tag);
352 gint reverse_color = 0;
354 /* FIXME where should we keep track of this?
355 g_object_get(tag, "reverse_color", &reverse_color, NULL);
361 case stylehint_Indentation:
362 g_object_set(tag_object, "left_margin", 5*val, NULL);
363 g_object_set(tag_object, "right_margin", 5*val, NULL);
366 case stylehint_ParaIndentation:
367 g_object_set(tag_object, "indent", 5*val, NULL);
370 case stylehint_Justification:
372 case stylehint_just_LeftFlush: i = GTK_JUSTIFY_LEFT; break;
373 case stylehint_just_LeftRight: i = GTK_JUSTIFY_FILL; break;
374 case stylehint_just_Centered: i = GTK_JUSTIFY_CENTER; break;
375 case stylehint_just_RightFlush: i = GTK_JUSTIFY_RIGHT; break;
377 WARNING("Unknown justification");
378 i = GTK_JUSTIFY_LEFT;
380 g_object_set(tag_object, "justification", i, NULL);
383 case stylehint_Weight:
385 case -1: i = PANGO_WEIGHT_LIGHT; break;
386 case 0: i = PANGO_WEIGHT_NORMAL; break;
387 case 1: i = PANGO_WEIGHT_BOLD; break;
388 default: WARNING("Unknown font weight");
390 g_object_set(tag_object, "weight", i, NULL);
394 g_object_set(tag_object, "size", 14+(2*val), NULL);
397 case stylehint_Oblique:
398 g_object_set(tag_object, "style", val ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL, NULL);
401 case stylehint_Proportional:
402 g_object_set(tag_object, "font-desc", val ? glk_data->default_font_desc : glk_data->monospace_font_desc, NULL);
405 case stylehint_TextColor:
406 color_format(val, color);
409 g_object_set(tag_object, "foreground", color, NULL);
411 g_object_set(tag_object, "background", color, NULL);
415 case stylehint_BackColor:
416 color_format(val, color);
419 g_object_set(tag_object, "background", color, NULL);
421 g_object_set(tag_object, "foreground", color, NULL);
425 case stylehint_ReverseColor:
426 if(reverse_color != val) {
427 /* Flip the fore- and background colors */
428 gchar* foreground_color;
429 gchar* background_color;
430 g_object_get(tag_object, "foreground", &foreground_color, NULL);
431 g_object_get(tag_object, "background", &background_color, NULL);
432 g_object_set(tag_object, "foreground", background_color, NULL);
433 g_object_set(tag_object, "background", foreground_color, NULL);
434 g_free(foreground_color);
435 g_free(background_color);
440 WARNING("Unknown style hint");
446 * @wintype: The window type to set a style hint on, or %wintype_AllTypes.
447 * @styl: The style to set a hint for.
448 * @hint: The type of style hint, one of the <code>stylehint_</code> constants.
449 * @val: The style hint. The meaning of this depends on @hint.
451 * Sets a hint about the appearance of one style for a particular type of
452 * window. You can also set wintype to %wintype_AllTypes, which sets a hint for
453 * all types of window.
455 * There is no equivalent constant to set a hint for all styles of a single
460 glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val)
462 gchar *tag_name = get_tag_name(styl);
464 /* Iterate over all the window and update their styles if nessecary */
465 winid_t win = glk_window_iterate(NULL, NULL);
467 if(wintype != wintype_TextBuffer)
468 continue; /* FIXME: add support for text grid windows */
470 if(wintype == wintype_AllTypes || glk_window_get_type(win) == wintype) {
471 GtkWidget *textview = win->widget;
472 GtkTextBuffer *textbuffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(textview) );
473 GtkTextTagTable *table = gtk_text_buffer_get_tag_table(textbuffer);
474 GtkTextTag *to_change = gtk_text_tag_table_lookup(table, tag_name);
476 apply_stylehint_to_tag(to_change, hint, val);
482 glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint)
487 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2)
489 return styl1 != styl2;
493 glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result)