Fixed some default styling
[projects/chimara/chimara.git] / libchimara / style.c
1 #include <stdio.h>
2 #include <string.h>
3 #include "chimara-glk-private.h"
4 #include "glk.h"
5 #include "style.h"
6 #include "magic.h"
7 #include "stream.h"
8 #include "strio.h"
9
10 extern GPrivate *glk_data_key;
11
12 static gboolean style_accept(GScanner *scanner, GTokenType token);
13 static gboolean style_accept_style_selector(GScanner *scanner, ChimaraGlk *glk);
14 static gboolean style_accept_style_hint(GScanner *scanner, GtkTextTag *current_tag);
15 static void style_add_tag_to_textbuffer(gpointer key, gpointer tag, gpointer tag_table);
16 static void style_copy_tag_to_textbuffer(gpointer key, gpointer tag, gpointer target_table);
17 static void text_tag_to_attr_list(GtkTextTag *tag, PangoAttrList *list);
18 GtkTextTag* gtk_text_tag_copy(GtkTextTag *tag);
19
20 /**
21  * glk_set_style:
22  * @styl: The style to apply
23  *
24  * Changes the style of the current output stream. @styl should be one of the
25  * <code>style_</code> constants. However, any value is actually legal; if the 
26  * interpreter does not recognize the style value, it will treat it as 
27  * %style_Normal.
28  * <note><para>
29  *  This policy allows for the future definition of styles without breaking old
30  *  Glk libraries.
31  * </para></note>
32  */
33 void
34 glk_set_style(glui32 styl)
35 {
36         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
37         g_return_if_fail(glk_data->current_stream != NULL);
38         glk_set_style_stream(glk_data->current_stream, styl);
39 }
40
41 /* The first 11 tag names must correspond to the first 11 glk tag names as defined below */
42 static const gchar* TAG_NAMES[] = {
43         "normal",
44         "emphasized",
45         "preformatted",
46         "header",
47         "subheader",
48         "alert",
49         "note",
50         "block-quote",
51         "input",
52         "user1",
53         "user2",
54         "hyperlink",
55         "pager",
56         "default"
57 };
58
59 /* The first 11 glk tag names must correspond to the first 11 tag names as defined above */
60 static const gchar* GLK_TAG_NAMES[] = {
61         "glk-normal",
62         "glk-emphasized",
63         "glk-preformatted",
64         "glk-header",
65         "glk-subheader",
66         "glk-alert",
67         "glk-note",
68         "glk-block-quote",
69         "glk-input",
70         "glk-user1",
71         "glk-user2"
72 };
73
74 const gchar**
75 style_get_tag_names()
76 {
77         return TAG_NAMES;
78 }
79
80 /* Internal function: mapping from style enum to tag name */
81 static const gchar*
82 get_tag_name(glui32 style)
83 {
84         if(style >= CHIMARA_NUM_STYLES) {
85                 WARNING("Unsupported style");
86                 return "normal";
87         } else {
88                 return (gchar*) TAG_NAMES[style];
89         }
90 }
91
92 /* Internal function: mapping from glk style enum to tag name */
93 static const gchar*
94 get_glk_tag_name(glui32 style)
95 {
96         if(style >= style_NUMSTYLES) {
97                 WARNING("Unsupported style");
98                 return "normal";
99         } else {
100                 return (gchar*) GLK_TAG_NAMES[style];
101         }
102 }
103
104 /** 
105  * glk_set_style_stream:
106  * @str: Output stream to change the style of
107  * @styl: The style to apply
108  *
109  * This changes the style of the stream @str. See glk_set_style().
110  */
111 void
112 glk_set_style_stream(strid_t str, glui32 styl) {
113         if(str->window == NULL)
114                 return;
115
116         flush_window_buffer(str->window);
117         str->style = (gchar*) get_tag_name(styl);
118         str->glk_style = (gchar*) get_glk_tag_name(styl);
119 }
120
121 /* Internal function: call this to initialize the layout of the 'more' prompt. */
122 void
123 style_init_more_prompt(winid_t win)
124 {
125         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
126
127         win->pager_layout = gtk_widget_create_pango_layout(win->widget, "More");
128         pango_layout_set_attributes(win->pager_layout, glk_data->pager_attr_list);
129 }
130
131 /* Internal function: call this to initialize the default styles to a textbuffer. */
132 void
133 style_init_textbuffer(GtkTextBuffer *buffer)
134 {
135         g_return_if_fail(buffer != NULL);
136
137         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
138
139         /* Place the default text tags in the textbuffer's tag table */
140         g_hash_table_foreach(glk_data->styles->text_buffer, style_add_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
141
142         /* Copy the override text tags to the textbuffers's tag table */
143         g_hash_table_foreach(glk_data->glk_styles->text_buffer, style_copy_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
144 }
145
146
147 /* Internal function: call this to initialize the default styles to a textgrid. */
148 void
149 style_init_textgrid(GtkTextBuffer *buffer)
150 {
151         g_return_if_fail(buffer != NULL);
152         
153         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
154
155         /* Place the default text tags in the textbuffer's tag table */
156         g_hash_table_foreach(glk_data->styles->text_grid, style_add_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
157
158         /* Copy the current text tags to the textbuffers's tag table */
159         g_hash_table_foreach(glk_data->glk_styles->text_grid, style_copy_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
160 }
161
162 /* Internal function used to iterate over the default text tag table, applying them to a textbuffer */
163 static void
164 style_add_tag_to_textbuffer(gpointer key, gpointer tag, gpointer tag_table)
165 {
166         g_return_if_fail(key != NULL);
167         g_return_if_fail(tag != NULL);
168         g_return_if_fail(tag_table != NULL);
169
170         gtk_text_tag_table_add(tag_table, tag);
171 }
172
173 /* Internal function used to iterate over a style table, copying it */
174 static void
175 style_copy_tag_to_textbuffer(gpointer key, gpointer tag, gpointer target_table)
176 {
177         g_return_if_fail(key != NULL);
178         g_return_if_fail(tag != NULL);
179         g_return_if_fail(target_table != NULL);
180
181         gtk_text_tag_table_add(target_table, gtk_text_tag_copy( GTK_TEXT_TAG(tag) ));
182 }
183
184 /* Internal function that copies a text tag */
185 GtkTextTag *
186 gtk_text_tag_copy(GtkTextTag *tag)
187 {
188         GtkTextTag *copy;
189
190         g_return_val_if_fail(tag != NULL, NULL);
191
192         copy = gtk_text_tag_new(tag->name);
193         gtk_text_attributes_copy_values(tag->values, copy->values);
194         
195         #define _COPY_FLAG(flag) copy->flag = tag->flag
196                 _COPY_FLAG (bg_color_set);
197                 _COPY_FLAG (bg_color_set);
198                 _COPY_FLAG (bg_stipple_set);
199                 _COPY_FLAG (fg_color_set);
200                 _COPY_FLAG (fg_stipple_set);
201                 _COPY_FLAG (justification_set);
202                 _COPY_FLAG (left_margin_set);
203                 _COPY_FLAG (indent_set);
204                 _COPY_FLAG (rise_set);
205                 _COPY_FLAG (strikethrough_set);
206                 _COPY_FLAG (right_margin_set);
207                 _COPY_FLAG (pixels_above_lines_set);
208                 _COPY_FLAG (pixels_below_lines_set);
209                 _COPY_FLAG (pixels_inside_wrap_set);
210                 _COPY_FLAG (tabs_set);
211                 _COPY_FLAG (underline_set);
212                 _COPY_FLAG (wrap_mode_set);
213                 _COPY_FLAG (bg_full_height_set);
214                 _COPY_FLAG (invisible_set);
215                 _COPY_FLAG (editable_set);
216                 _COPY_FLAG (language_set);
217         #undef _COPY_FLAG
218
219         /* Copy the data that was added manually */
220         gpointer reverse_color = g_object_get_data( G_OBJECT(tag), "reverse_color" );
221
222         if(reverse_color)
223                 g_object_set_data( G_OBJECT(copy), "reverse_color", reverse_color );
224
225         return copy;
226 }
227
228 /* Internal function that constructs the default styles */
229 void
230 style_init(ChimaraGlk *glk)
231 {
232         CHIMARA_GLK_USE_PRIVATE(glk, priv);
233         
234         GHashTable *default_text_grid_styles = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref);
235         GHashTable *default_text_buffer_styles = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref);
236         GHashTable *glk_text_grid_styles = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref);
237         GHashTable *glk_text_buffer_styles = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref);
238         GtkTextTag *tag;
239
240         PangoFontDescription *default_font_desc = pango_font_description_from_string("Serif");
241         PangoFontDescription *monospace_font_desc = pango_font_description_from_string("Monospace");
242         
243         /* Initialise the default styles for a text grid */
244         tag = gtk_text_tag_new("default");
245         g_object_set(tag, "font-desc", monospace_font_desc, NULL);
246         g_hash_table_insert(default_text_grid_styles, "default", tag);
247
248         tag = gtk_text_tag_new("normal");
249         //g_object_set(tag, "font-desc", monospace_font_desc, NULL);
250         g_hash_table_insert(default_text_grid_styles, "normal", tag);
251
252         tag = gtk_text_tag_new("emphasized");
253         //g_object_set(tag, "font-desc", monospace_font_desc, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
254         g_object_set(tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
255         g_hash_table_insert(default_text_grid_styles, "emphasized", tag);
256
257         tag = gtk_text_tag_new("preformatted");
258         g_object_set(tag, "font-desc", monospace_font_desc, NULL);
259         g_hash_table_insert(default_text_grid_styles, "preformatted", tag);
260
261         tag = gtk_text_tag_new("header");
262         //g_object_set(tag, "font-desc", monospace_font_desc, "weight", PANGO_WEIGHT_BOLD, NULL);
263         g_object_set(tag, "weight", PANGO_WEIGHT_BOLD, NULL);
264         g_hash_table_insert(default_text_grid_styles, "header", tag);
265
266         tag = gtk_text_tag_new("subheader");
267         //g_object_set(tag, "font-desc", monospace_font_desc, "weight", PANGO_WEIGHT_BOLD, NULL);
268         g_object_set(tag, "weight", PANGO_WEIGHT_BOLD, NULL);
269         g_hash_table_insert(default_text_grid_styles, "subheader", tag);
270
271         tag = gtk_text_tag_new("alert");
272         //g_object_set(tag, "font-desc", monospace_font_desc, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
273         g_object_set(tag, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
274         g_hash_table_insert(default_text_grid_styles, "alert", tag);
275
276         tag = gtk_text_tag_new("note");
277         //g_object_set(tag, "font-desc", monospace_font_desc, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
278         g_object_set(tag, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
279         g_hash_table_insert(default_text_grid_styles, "note", tag);
280
281         tag = gtk_text_tag_new("block-quote");
282         //g_object_set(tag, "font-desc", monospace_font_desc, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
283         g_object_set(tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
284         g_hash_table_insert(default_text_grid_styles, "block-quote", tag);
285
286         tag = gtk_text_tag_new("input");
287         //g_object_set(tag, "font-desc", monospace_font_desc, NULL);
288         g_hash_table_insert(default_text_grid_styles, "input", tag);
289
290         tag = gtk_text_tag_new("user1");
291         //g_object_set(tag, "font-desc", monospace_font_desc, NULL);
292         g_hash_table_insert(default_text_grid_styles, "user1", tag);
293
294         tag = gtk_text_tag_new("user2");
295         //g_object_set(tag, "font-desc", monospace_font_desc, NULL);
296         g_hash_table_insert(default_text_grid_styles, "user2", tag);
297
298         tag = gtk_text_tag_new("hyperlink");
299         g_object_set(tag, "foreground", "#0000ff", "underline", PANGO_UNDERLINE_SINGLE, "underline-set", TRUE, NULL);
300         g_hash_table_insert(default_text_grid_styles, "hyperlink", tag);
301
302         /* Initialise the default styles for a text buffer */
303         tag = gtk_text_tag_new("default");
304         g_object_set(tag, "font-desc", default_font_desc, NULL);
305         g_hash_table_insert(default_text_buffer_styles, "default", tag);
306
307         tag = gtk_text_tag_new("normal");
308         //g_object_set(tag, "font-desc", default_font_desc, NULL);
309         g_hash_table_insert(default_text_buffer_styles, "normal", tag);
310
311         tag = gtk_text_tag_new("emphasized");
312         //g_object_set(tag, "font-desc", default_font_desc, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
313         g_object_set(tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
314         g_hash_table_insert(default_text_buffer_styles, "emphasized", tag);
315
316         tag = gtk_text_tag_new("preformatted");
317         g_object_set(tag, "font-desc", monospace_font_desc, NULL);
318         g_hash_table_insert(default_text_buffer_styles, "preformatted", tag);
319
320         tag = gtk_text_tag_new("header");
321         //g_object_set(tag, "font-desc", default_font_desc, "size-points", 18.0, "weight", PANGO_WEIGHT_BOLD, NULL);
322         g_object_set(tag, "weight", PANGO_WEIGHT_BOLD, NULL);
323         g_hash_table_insert(default_text_buffer_styles, "header", tag);
324
325         tag = gtk_text_tag_new("subheader");
326         //g_object_set(tag, "font-desc", default_font_desc, "size-points", 14.0, "weight", PANGO_WEIGHT_BOLD, NULL);
327         g_object_set(tag, "weight", PANGO_WEIGHT_BOLD, NULL);
328         g_hash_table_insert(default_text_buffer_styles, "subheader", tag);
329
330         tag = gtk_text_tag_new("alert");
331         //g_object_set(tag, "font-desc", default_font_desc, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
332         g_object_set(tag, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
333         g_hash_table_insert(default_text_buffer_styles, "alert", tag);
334
335         tag = gtk_text_tag_new("note");
336         //g_object_set(tag, "font-desc", default_font_desc, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
337         g_object_set(tag, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
338         g_hash_table_insert(default_text_buffer_styles, "note", tag);
339
340         tag = gtk_text_tag_new("block-quote");
341         //g_object_set(tag, "font-desc", default_font_desc, "justification", GTK_JUSTIFY_CENTER, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
342         g_object_set(tag, "justification", GTK_JUSTIFY_CENTER, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
343         g_hash_table_insert(default_text_buffer_styles, "block-quote", tag);
344
345         tag = gtk_text_tag_new("input");
346         //g_object_set(tag, "font-desc", default_font_desc, NULL);
347         g_hash_table_insert(default_text_buffer_styles, "input", tag);
348
349         tag = gtk_text_tag_new("user1");
350         //g_object_set(tag, "font-desc", default_font_desc, NULL);
351         g_hash_table_insert(default_text_buffer_styles, "user1", tag);
352
353         tag = gtk_text_tag_new("user2");
354         //g_object_set(tag, "font-desc", default_font_desc, NULL);
355         g_hash_table_insert(default_text_buffer_styles, "user2", tag);
356
357         tag = gtk_text_tag_new("hyperlink");
358         //g_object_set(tag, "font-desc", default_font_desc, "foreground", "#0000ff", "underline", PANGO_UNDERLINE_SINGLE, "underline-set", TRUE, NULL);
359         g_object_set(tag, "foreground", "#0000ff", "underline", PANGO_UNDERLINE_SINGLE, "underline-set", TRUE, NULL);
360         g_hash_table_insert(default_text_buffer_styles, "hyperlink", tag);
361
362         GtkTextTag *pager_tag = gtk_text_tag_new("pager");
363         g_object_set(pager_tag, "font-desc", default_font_desc, "foreground", "#ffffff", "background", "#000000", NULL);
364         g_hash_table_insert(default_text_buffer_styles, "pager", pager_tag);
365         text_tag_to_attr_list(pager_tag, priv->pager_attr_list);
366
367         pango_font_description_free(default_font_desc);
368         pango_font_description_free(monospace_font_desc);
369         
370         priv->styles->text_grid = default_text_grid_styles;
371         priv->styles->text_buffer = default_text_buffer_styles;
372
373
374         /* Initialize the GLK styles to empty tags */
375         int i;
376         for(i=0; i<style_NUMSTYLES; i++) {
377                 tag = gtk_text_tag_new(GLK_TAG_NAMES[i]);
378                 g_hash_table_insert(glk_text_grid_styles, (gchar*) GLK_TAG_NAMES[i], tag);
379                 tag = gtk_text_tag_new(GLK_TAG_NAMES[i]);
380                 g_hash_table_insert(glk_text_buffer_styles, (gchar*) GLK_TAG_NAMES[i], tag);
381         }
382
383         priv->glk_styles->text_grid = glk_text_grid_styles;
384         priv->glk_styles->text_buffer = glk_text_buffer_styles;
385
386 }
387
388 /* Reset style tables to the library's internal defaults */
389 void
390 reset_default_styles(ChimaraGlk *glk)
391 {
392         /* TODO: write this function */
393 }
394
395 /* Copy the default styles to the current styles
396  FIXME: This function is temporary and will not be needed later on */
397 void
398 copy_default_styles_to_current_styles(ChimaraGlk *glk)
399 {
400         /*
401         CHIMARA_GLK_USE_PRIVATE(glk, priv);
402         g_hash_table_foreach(priv->styles->text_grid, style_table_copy, priv->glk_styles->text_grid);
403         g_hash_table_foreach(priv->styles->text_buffer, style_table_copy, priv->glk_styles->text_buffer);
404
405         GtkTextTag *pager_tag = GTK_TEXT_TAG( g_hash_table_lookup(priv->styles->text_buffer, "pager") );
406         text_tag_to_attr_list(pager_tag, priv->pager_attr_list);
407         */
408 }
409
410 /* Create the CSS file scanner */
411 GScanner *
412 create_css_file_scanner(void)
413 {
414         GScanner *scanner = g_scanner_new(NULL);
415         scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "#";
416         scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z "-_" G_CSET_DIGITS;
417         scanner->config->symbol_2_token = TRUE;
418         scanner->config->cpair_comment_single = NULL;
419         scanner->config->scan_float = FALSE;
420         return scanner;
421 }
422
423 /* Run the scanner over the CSS file, overriding the default styles */
424 void
425 scan_css_file(GScanner *scanner, ChimaraGlk *glk)
426 {
427         CHIMARA_GLK_USE_PRIVATE(glk, priv);
428
429         while( g_scanner_peek_next_token(scanner) != G_TOKEN_EOF) {
430                 if( !style_accept_style_selector(scanner, glk) )
431                         break;
432         }
433
434         g_scanner_destroy(scanner);
435
436         /* Update the pager prompt to the new style */
437         GtkTextTag *pager_tag = GTK_TEXT_TAG( g_hash_table_lookup(priv->styles->text_buffer, "pager") );
438         text_tag_to_attr_list(pager_tag, priv->pager_attr_list);
439 }
440
441 /* Internal function: parses a token */
442 static gboolean
443 style_accept(GScanner *scanner, GTokenType token)
444 {
445         GTokenType next = g_scanner_get_next_token(scanner);
446         if(next != token) {
447                 g_scanner_unexp_token(scanner, token, NULL, NULL, NULL, "CSS Error", 1);
448                 return FALSE;
449         }
450         return TRUE;
451 }
452
453 /* Internal function: parses a style selector */
454 static gboolean
455 style_accept_style_selector(GScanner *scanner, ChimaraGlk *glk)
456 {
457         CHIMARA_GLK_USE_PRIVATE(glk, priv);
458
459         GtkTextTag *current_tag;
460         gchar *field;
461         GTokenType token = g_scanner_get_next_token(scanner);
462         GTokenValue value = g_scanner_cur_value(scanner);
463
464         if(
465                 token != G_TOKEN_IDENTIFIER ||
466                 ( strcmp(value.v_identifier, "buffer") && strcmp(value.v_identifier, "grid") )
467         ) {
468                 g_scanner_error(scanner, "CSS Error: buffer/grid expected");
469                 return FALSE;
470         }
471
472         field = g_strdup(value.v_identifier);
473
474         /* Parse the tag name to change */
475         if( g_scanner_peek_next_token(scanner) == '{') {
476                 style_accept(scanner, '{');
477                 if( !strcmp(field, "buffer") )
478                         current_tag = g_hash_table_lookup(priv->styles->text_buffer, "default");
479                 else
480                         current_tag = g_hash_table_lookup(priv->styles->text_grid, "default");
481         } else {
482                 if( !style_accept(scanner, '.') )
483                         return FALSE;
484
485                 token = g_scanner_get_next_token(scanner);
486                 value = g_scanner_cur_value(scanner);
487
488                 if(token != G_TOKEN_IDENTIFIER) {
489                         g_scanner_error(scanner, "CSS Error: style selector expected");
490                         return FALSE;
491                 }
492
493                 if( !strcmp(field, "buffer") )
494                         current_tag = g_hash_table_lookup(priv->styles->text_buffer, value.v_identifier);
495                 else
496                         current_tag = g_hash_table_lookup(priv->styles->text_grid, value.v_identifier);
497
498                 if(current_tag == NULL) {
499                         g_scanner_error(scanner, "CSS Error: invalid style identifier");
500                         return FALSE;
501                 }
502
503                 if( !style_accept(scanner, '{') )
504                         return FALSE;
505         }
506
507         while( g_scanner_peek_next_token(scanner) != '}') {
508                 if( !style_accept_style_hint(scanner, current_tag) )
509                         return FALSE;
510         }
511                 
512         if( !style_accept(scanner, '}') )
513                 return FALSE;
514
515         return TRUE;
516 }
517
518 /* Internal function: parses a style hint */
519 static gboolean
520 style_accept_style_hint(GScanner *scanner, GtkTextTag *current_tag)
521 {
522         GTokenType token = g_scanner_get_next_token(scanner);
523         GTokenValue value = g_scanner_cur_value(scanner);
524         gchar *hint;
525
526         if(token != G_TOKEN_IDENTIFIER) {
527                 g_scanner_error(scanner, "CSS Error: style hint expected");
528                 return FALSE;
529         }
530
531         hint = g_strdup(value.v_identifier);
532
533         if( !style_accept(scanner, ':') )
534                 return FALSE;
535
536         token = g_scanner_get_next_token(scanner);
537         value = g_scanner_cur_value(scanner);
538
539         if( !strcmp(hint, "font-family") ) {
540                 if(token != G_TOKEN_STRING) {
541                         g_scanner_error(scanner, "CSS Error: string expected");
542                         return FALSE;
543                 }
544                 g_object_set(current_tag, "family", value.v_string, "family-set", TRUE, NULL);
545         }
546         else if( !strcmp(hint, "font-weight") ) {
547                 if(token != G_TOKEN_IDENTIFIER) {
548                         g_scanner_error(scanner, "CSS Error: bold/normal expected");
549                         return FALSE;
550                 }
551
552                 if( !strcmp(value.v_identifier, "bold") )
553                         g_object_set(current_tag, "weight", PANGO_WEIGHT_BOLD, "weight-set", TRUE, NULL);
554                 else if( !strcmp(value.v_identifier, "normal") )
555                         g_object_set(current_tag, "weight", PANGO_WEIGHT_NORMAL, "weight-set", TRUE, NULL);
556                 else {
557                         g_scanner_error(scanner, "CSS Error: bold/normal expected");
558                         return FALSE;
559                 }
560         }
561         else if( !strcmp(hint, "font-style") ) {
562                 if(token != G_TOKEN_IDENTIFIER) {
563                         g_scanner_error(scanner, "CSS Error: italic/normal expected");
564                         return FALSE;
565                 }
566
567                 if( !strcmp(value.v_identifier, "italic") )
568                         g_object_set(current_tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
569                 else if( !strcmp(value.v_identifier, "normal") )
570                         g_object_set(current_tag, "style", PANGO_STYLE_NORMAL, "style-set", TRUE, NULL);
571                 else {
572                         g_scanner_error(scanner, "CSS Error: italic/normal expected");
573                         return FALSE;
574                 }
575         }
576         else if( !strcmp(hint, "font-size") ) {
577                 if(token == G_TOKEN_INT) 
578                         g_object_set(current_tag, "size-points", (float)value.v_int, "size-set", TRUE, NULL);
579                 else if(token == G_TOKEN_FLOAT)
580                         g_object_set(current_tag, "size-points", value.v_float, "size-set", TRUE, NULL);
581                 else {
582                         g_scanner_error(scanner, "CSS Error: integer or float expected");
583                         return FALSE;
584                 }
585         }
586         else if( !strcmp(hint, "color") ) {
587                 if(token != G_TOKEN_IDENTIFIER) {
588                         g_scanner_error(scanner, "CSS Error: hex color expected");
589                         return FALSE;
590                 }
591
592                 g_object_set(current_tag, "foreground", value.v_identifier, "foreground-set", TRUE, NULL);
593         }
594         else if( !strcmp(hint, "background-color") ) {
595                 if(token != G_TOKEN_IDENTIFIER) {
596                         g_scanner_error(scanner, "CSS Error: hex color expected");
597                         return FALSE;
598                 }
599                 g_object_set(current_tag, "background", value.v_identifier, "background-set", TRUE, NULL);
600         }
601         else if( !strcmp(hint, "text-align") ) {
602                 if(token != G_TOKEN_IDENTIFIER) {
603                         g_scanner_error(scanner, "CSS Error: left/right/center expected");
604                         return FALSE;
605                 }
606                 
607                 if( !strcmp(value.v_identifier, "left") )
608                         g_object_set(current_tag, "justification", GTK_JUSTIFY_LEFT, "justification-set", TRUE, NULL);
609                 else if( !strcmp(value.v_identifier, "right") )
610                         g_object_set(current_tag, "justification", GTK_JUSTIFY_RIGHT, "justification-set", TRUE, NULL);
611                 else if( !strcmp(value.v_identifier, "center") )
612                         g_object_set(current_tag, "justification", GTK_JUSTIFY_CENTER, "justification-set", TRUE, NULL);
613                 else {
614                         g_scanner_error(scanner, "CSS Error: left/right/center expected");
615                         return FALSE;
616                 }
617         }
618         else if( !strcmp(hint, "margin-left") ) {
619                 if(token != G_TOKEN_INT) {
620                         g_scanner_error(scanner, "CSS Error: integer expected");
621                         return FALSE;
622                 }
623                 g_object_set(current_tag, "left-margin", value.v_int, "left-margin-set", TRUE, NULL);
624         }
625         else if( !strcmp(hint, "margin-right") ) {
626                 if(token != G_TOKEN_INT) {
627                         g_scanner_error(scanner, "CSS Error: integer expected");
628                         return FALSE;
629                 }
630                 g_object_set(current_tag, "right-margin", value.v_int, "right-margin-set", TRUE, NULL);
631         }
632         else if( !strcmp(hint, "margin-top") ) {
633                 if(token != G_TOKEN_INT) {
634                         g_scanner_error(scanner, "CSS Error: integer expected");
635                         return FALSE;
636                 }
637                 g_object_set(current_tag, "pixels-above-lines", value.v_int, "pixels-above-lines-set", TRUE, NULL);
638         }
639         else if( !strcmp(hint, "margin-bottom") ) {
640                 if(token != G_TOKEN_INT) {
641                         g_scanner_error(scanner, "CSS Error: integer expected");
642                         return FALSE;
643                 }
644                 g_object_set(current_tag, "pixels-below-lines", value.v_int, "pixels-below-lines-set", TRUE, NULL);
645         }
646                 
647         else {
648                 g_scanner_error(scanner, "CSS Error: invalid style hint %s", hint);
649                 return FALSE;
650         }
651
652         if( !style_accept(scanner, ';') )
653                 return FALSE;
654
655         return TRUE;
656 }
657
658 /* Internal function: parses a glk color to a #hex-value */
659 static void
660 glkcolor_to_hex(glui32 val, gchar *buffer)
661 {
662         g_return_if_fail(buffer != NULL);
663
664         sprintf(buffer, "#%02X%02X%02X",
665                 ((val & 0xff0000) >> 16),
666                 ((val & 0x00ff00) >> 8),
667                 (val & 0x0000ff)
668         );
669 }
670
671 /* Internal function: parses a glk color to a GdkColor */
672 void
673 glkcolor_to_gdkcolor(glui32 val, GdkColor *color)
674 {
675         color->red = 256 * ((val & 0xff0000) >> 16);
676         color->green = 256 * ((val & 0x00ff00) >> 8);
677         color->blue = 256 * (val & 0x0000ff);
678 }
679
680 /* Internal function: parses a GdkColor to a glk color */
681 static glui32
682 gdkcolor_to_glkcolor(GdkColor *color)
683 {
684         g_return_val_if_fail(color != NULL, 0);
685         return (glui32) color->pixel;
686 }
687
688 /* Internal function: changes a GTK tag to correspond with the given style. */
689 static void
690 apply_stylehint_to_tag(GtkTextTag *tag, GtkTextTag *default_tag, glui32 wintype, glui32 hint, glsi32 val)
691 {
692         g_return_if_fail(tag != NULL);
693
694         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
695         GObject *tag_object = G_OBJECT(tag);
696
697         gint reverse_color = GPOINTER_TO_INT( g_object_get_data(tag_object, "reverse-color") );
698
699         int i = 0;
700         gchar color[20];
701         switch(hint) {
702         case stylehint_Indentation:
703                 g_object_set(tag_object, "left-margin", 5*val, "left-margin-set", TRUE, NULL);
704                 g_object_set(tag_object, "right-margin", 5*val, "right-margin-set", TRUE, NULL);
705                 break;
706         
707         case stylehint_ParaIndentation:
708                 g_object_set(tag_object, "indent", 5*val, "indent-set", TRUE, NULL);
709                 break;
710
711         case stylehint_Justification:
712                 switch(val) {
713                         case stylehint_just_LeftFlush:  i = GTK_JUSTIFY_LEFT; break;
714                         case stylehint_just_LeftRight:  i = GTK_JUSTIFY_FILL; break;
715                         case stylehint_just_Centered:   i = GTK_JUSTIFY_CENTER; break;
716                         case stylehint_just_RightFlush: i = GTK_JUSTIFY_RIGHT; break;
717                         default: 
718                                 WARNING("Unknown justification");
719                                 i = GTK_JUSTIFY_LEFT;
720                 }
721                 g_object_set(tag_object, "justification", i, "justification-set", TRUE, NULL);
722                 break;
723
724         case stylehint_Weight:
725                 switch(val) {
726                         case -1: i = PANGO_WEIGHT_LIGHT; break;
727                         case  0: i = PANGO_WEIGHT_NORMAL; break;
728                         case  1: i = PANGO_WEIGHT_BOLD; break;
729                         default: WARNING("Unknown font weight");
730                 }
731                 g_object_set(tag_object, "weight", i, "weight-set", TRUE, NULL);
732                 break;
733
734         case stylehint_Size:
735                 g_object_set(tag_object, "size", 14+(2*val), "size-set", TRUE, NULL);
736                 break;
737
738         case stylehint_Oblique:
739                 g_object_set(tag_object, "style", val ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL, "style-set", TRUE, NULL);
740                 break;
741
742         case stylehint_Proportional:
743         {
744                 gchar *font_family;
745                 GtkTextTag *font_tag = g_hash_table_lookup(
746                     wintype == wintype_TextBuffer? glk_data->styles->text_buffer : glk_data->styles->text_grid,
747                     val? "normal" : "preformatted");
748                 g_object_get(font_tag, "family", &font_family, NULL);
749                 g_object_set(tag_object, "family", font_family, "family-set", TRUE, NULL);
750                 g_free(font_family);
751         }
752                 break;
753
754         case stylehint_TextColor:
755                 glkcolor_to_hex(val, color);
756
757                 if(!reverse_color)
758                         g_object_set(tag_object, "foreground", color, "foreground-set", TRUE, NULL);
759                 else
760                         g_object_set(tag_object, "background", color, "background-set", TRUE, NULL);
761
762                 break;
763
764         case stylehint_BackColor:
765                 glkcolor_to_hex(val, color);
766
767                 if(!reverse_color)
768                         g_object_set(tag_object, "background", color, "background-set", TRUE, NULL);
769                 else
770                         g_object_set(tag_object, "foreground", color, "background-set", TRUE, NULL);
771
772                 break;
773
774         case stylehint_ReverseColor:
775                 if(reverse_color != val) {
776                         /* Flip the fore- and background colors */
777                         GdkColor* foreground_color;
778                         GdkColor* background_color;
779                         gint f_set, b_set, df_set, db_set = 0;
780                         g_object_get(tag_object, "foreground-set", &f_set, "background-set", &b_set, NULL);
781                         g_object_get(default_tag, "foreground-set", &df_set, "background-set", &db_set, NULL);
782
783                         if(f_set)
784                                 g_object_get(tag_object, "foreground-gdk", &foreground_color, NULL);
785                         else if(df_set)
786                                 g_object_get(default_tag, "foreground-gdk", &foreground_color, NULL);
787                         if(b_set)
788                                 g_object_get(tag_object, "background-gdk", &background_color, NULL);
789                         else if(db_set)
790                                 g_object_get(default_tag, "background-gdk", &background_color, NULL);
791
792                         if(b_set || db_set)
793                                 g_object_set(tag_object, "foreground-gdk", background_color, NULL);
794                         else
795                                 g_object_set(tag_object, "foreground", "#ffffff", NULL);
796
797                         if(f_set || df_set)
798                                 g_object_set(tag_object, "background-gdk", foreground_color, NULL);
799                         else
800                                 g_object_set(tag_object, "background", "#000000", NULL);
801
802                         g_object_set_data( tag_object, "reverse-color", GINT_TO_POINTER(val != 0) );
803                 }
804                 break;
805
806         default:
807                 WARNING("Unknown style hint");
808         }
809 }
810
811 /* Internal function: queries a text tag for the value of a given style hint */
812 static gint
813 query_tag(GtkTextTag *tag, glui32 wintype, glui32 hint)
814 {
815         gint intval;
816         GdkColor *colval;
817
818         g_return_val_if_fail(tag != NULL, 0);
819
820         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
821
822         switch(hint) {
823         case stylehint_Indentation:
824                 g_object_get(tag, "left_margin", &intval, NULL);
825                 return intval/5;
826         
827         case stylehint_ParaIndentation:
828                 g_object_get(tag, "indent", &intval, NULL);
829                 return intval/5;
830
831         case stylehint_Justification:
832                 g_object_get(tag, "justification", &intval, NULL);
833                 switch(intval) {
834                         case GTK_JUSTIFY_LEFT: return stylehint_just_LeftFlush;
835                         case GTK_JUSTIFY_FILL: return stylehint_just_LeftRight;
836                         case GTK_JUSTIFY_CENTER: return stylehint_just_Centered;
837                         case GTK_JUSTIFY_RIGHT: return stylehint_just_RightFlush;
838                         default: 
839                                 WARNING("Unknown justification");
840                                 return stylehint_just_LeftFlush;
841                 }
842
843         case stylehint_Weight:
844                 g_object_get(tag, "weight", &intval, NULL);
845                 switch(intval) {
846                         case PANGO_WEIGHT_LIGHT: return -1;
847                         case PANGO_WEIGHT_NORMAL: return 0;
848                         case PANGO_WEIGHT_BOLD: return 1;
849                         default: WARNING("Unknown font weight"); return 0;
850                 }
851
852         case stylehint_Size:
853                 g_object_get(tag, "size", &intval, NULL);
854                 return (intval/2)-14;
855
856         case stylehint_Oblique:
857                 g_object_get(tag, "style", &intval , NULL);
858                 return intval == PANGO_STYLE_ITALIC ? 1 : 0;
859
860         case stylehint_Proportional:
861                 /* Use pango_font_family_is_monospace()? */
862         {
863                 gchar *font_family, *query_font_family;
864                 GtkTextTag *font_tag = g_hash_table_lookup(
865                     wintype == wintype_TextBuffer? glk_data->styles->text_buffer : glk_data->styles->text_grid,
866                     "preformatted");
867                 g_object_get(font_tag, "family", &font_family, NULL);
868                 g_object_get(tag, "family", &query_font_family, NULL);
869                 gint retval = strcmp(font_family, query_font_family)? 0 : 1;
870                 g_free(font_family);
871                 g_free(query_font_family);
872                 return retval;
873         }
874
875         case stylehint_TextColor:
876                 g_object_get(tag, "foreground-gdk", &colval, NULL);
877                 return gdkcolor_to_glkcolor(colval);
878
879         case stylehint_BackColor:
880                 g_object_get(tag, "background-gdk", &colval, NULL);
881                 return gdkcolor_to_glkcolor(colval);
882
883         case stylehint_ReverseColor:
884                 return GPOINTER_TO_INT( g_object_get_data(G_OBJECT(tag), "reverse_color") );
885
886         default:
887                 WARNING("Unknown style hint");
888         }
889         
890         return 0;
891 }
892
893 /**
894  * glk_stylehint_set:
895  * @wintype: The window type to set a style hint on, or %wintype_AllTypes.
896  * @styl: The style to set a hint for.
897  * @hint: The type of style hint, one of the <code>stylehint_</code> constants.
898  * @val: The style hint. The meaning of this depends on @hint.
899  *
900  * Sets a hint about the appearance of one style for a particular type of 
901  * window. You can also set @wintype to %wintype_AllTypes, which sets a hint for 
902  * all types of window.
903  * <note><para>
904  *  There is no equivalent constant to set a hint for all styles of a single 
905  *  window type.
906  * </para></note>
907  */
908 void
909 glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val)
910 {
911         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
912
913         GtkTextTag *to_change, *default_tag;
914         if(wintype == wintype_TextBuffer || wintype == wintype_AllTypes) {
915                 to_change = g_hash_table_lookup( glk_data->glk_styles->text_buffer, get_glk_tag_name(styl) );
916                 default_tag = g_hash_table_lookup( glk_data->styles->text_buffer, get_tag_name(styl) );
917                 apply_stylehint_to_tag(to_change, default_tag, wintype_TextBuffer, hint, val);
918         }
919
920         if(wintype == wintype_TextGrid || wintype == wintype_AllTypes) {
921                 to_change = g_hash_table_lookup( glk_data->glk_styles->text_grid, get_glk_tag_name(styl) );
922                 default_tag = g_hash_table_lookup( glk_data->styles->text_grid, get_tag_name(styl) );
923                 apply_stylehint_to_tag(to_change, default_tag, wintype_TextGrid, hint, val);
924         }
925 }
926
927 /**
928  * glk_stylehint_clear:
929  * @wintype: The window type to set a style hint on, or %wintype_AllTypes.
930  * @styl: The style to set a hint for.
931  * @hint: The type of style hint, one of the <code>stylehint_</code> constants.
932  *
933  * Clears a hint about the appearance of one style for a particular type of 
934  * window to its default value. You can also set @wintype to %wintype_AllTypes, 
935  * which clears a hint for all types of window.
936  * <note><para>
937  *  There is no equivalent constant to reset a hint for all styles of a single 
938  *  window type.
939  * </para></note>
940  */
941 void
942 glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint)
943 {
944         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
945         GtkTextTag *tag;
946
947         switch(wintype) {
948         case wintype_TextBuffer:
949                 tag = g_hash_table_lookup( glk_data->glk_styles->text_buffer, get_glk_tag_name(styl) );
950                 if(tag) {
951                         glk_stylehint_set( wintype, styl, hint, query_tag(tag, wintype, hint) );
952                 }
953                 break;
954         case wintype_TextGrid:
955                 tag = g_hash_table_lookup( glk_data->glk_styles->text_grid, get_glk_tag_name(styl) );
956                 if(tag) {
957                         glk_stylehint_set( wintype, styl, hint, query_tag(tag, wintype, hint) );
958                 }
959         default:
960                 return;
961         }
962 }
963
964 /**
965  * glk_style_distinguish:
966  * @win: The window in which the styles are to be distinguished.
967  * @styl1: The first style to be distinguished from the second style.
968  * @styl2: The second style to be distinguished from the first style.
969  * 
970  * Decides whether two styles are visually distinguishable in the given window.
971  * The exact meaning of this is left for the library to determine.
972  * <note><title>Chimara</title><para>
973  *   Currently, all styles of one window are assumed to be mutually
974  *   distinguishable.
975  * </para></note>
976  * 
977  * Returns: %TRUE (1) if the two styles are visually distinguishable. If they 
978  * are not, it returns %FALSE (0).
979  */
980 glui32
981 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2)
982 {
983         /* FIXME */
984         return styl1 != styl2;
985 }
986
987 /**
988  * glk_style_measure:
989  * @win: The window from which to take the style.
990  * @styl: The style to perform the measurement on.
991  * @hint: The stylehint to measure.
992  * @result: Address to write the result to.
993  * 
994  * Tries to test an attribute of one style in the given window @win. The library
995  * may not be able to determine the attribute; if not, this returns %FALSE (0).
996  * If it can, it returns %TRUE (1) and stores the value in the location pointed
997  * at by @result. 
998  * <note><para>
999  *   As usual, it is legal for @result to be %NULL, although fairly pointless.
1000  * </para></note>
1001  *
1002  * The meaning of the value depends on the hint which was tested:
1003  * <variablelist>
1004  * <varlistentry>
1005  *   <term>%stylehint_Indentation, %stylehint_ParaIndentation</term>
1006  *   <listitem><para>The indentation and paragraph indentation. These are in a
1007  *   metric which is platform-dependent.</para>
1008  *   <note><para>Most likely either characters or pixels.</para></note>
1009  *   </listitem>
1010  * </varlistentry>
1011  * <varlistentry>
1012  *   <term>%stylehint_Justification</term>
1013  *   <listitem><para>One of the constants %stylehint_just_LeftFlush,
1014  *   %stylehint_just_LeftRight, %stylehint_just_Centered, or
1015  *   %stylehint_just_RightFlush.</para></listitem>
1016  * </varlistentry>
1017  * <varlistentry>
1018  *   <term>%stylehint_Size</term>
1019  *   <listitem><para>The font size. Again, this is in a platform-dependent
1020  *   metric.</para>
1021  *   <note><para>Pixels, points, or simply 1 if the library does not support
1022  *   varying font sizes.</para></note>
1023  *   </listitem>
1024  * </varlistentry>
1025  * <varlistentry>
1026  *   <term>%stylehint_Weight</term>
1027  *   <listitem><para>1 for heavy-weight fonts (boldface), 0 for normal weight,
1028  *   and -1 for light-weight fonts.</para></listitem>
1029  * </varlistentry>
1030  * <varlistentry>
1031  *   <term>%stylehint_Oblique</term>
1032  *   <listitem><para>1 for oblique fonts (italic), or 0 for normal angle.</para>
1033  *   </listitem>
1034  * </varlistentry>
1035  * <varlistentry>
1036  *   <term>%stylehint_Proportional</term>
1037  *   <listitem><para>1 for proportional-width fonts, or 0 for fixed-width.
1038  *   </para></listitem>
1039  * </varlistentry>
1040  * <varlistentry>
1041  *   <term>%stylehint_TextColor, %stylehint_BackColor</term>
1042  *   <listitem><para>These are values from 0x00000000 to 0x00FFFFFF, encoded as
1043  *   described in <link 
1044  *   linkend="chimara-Suggesting-the-Appearance-of-Styles">Suggesting the
1045  *   Appearance of Styles</link>.</para></listitem>
1046  * </varlistentry>
1047  * <varlistentry>
1048  *   <term>%stylehint_ReverseColor</term>
1049  *   <listitem><para>0 for normal printing, 1 if the foreground and background
1050  *   colors are reversed.</para></listitem>
1051  * </varlistentry>
1052  * </variablelist>
1053  * 
1054  * Returns: TRUE upon successul retrieval, otherwise FALSE.
1055  */
1056 glui32
1057 glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result)
1058 {
1059         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
1060         GtkTextTag *tag;
1061
1062         switch(win->type) {
1063         case wintype_TextBuffer:
1064                 tag = g_hash_table_lookup( glk_data->glk_styles->text_buffer, get_glk_tag_name(styl) );
1065                 if(result)
1066                         *result = query_tag(tag, win->type, hint);
1067                 break;
1068         case wintype_TextGrid:
1069                 tag = g_hash_table_lookup( glk_data->glk_styles->text_grid, get_glk_tag_name(styl) );
1070                 if(result)
1071                         *result = query_tag(tag, win->type, hint);
1072         default:
1073                 return FALSE;
1074         }
1075
1076         return TRUE;
1077 }
1078
1079 /* Internal function returning the current default font for a window type
1080  * This can be used later for size calculations. Only wintype_TextGrid and wintype_TextBuffer are
1081  * supported for now */
1082 PangoFontDescription *
1083 get_current_font(guint32 wintype)
1084 {
1085         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
1086         GtkTextTag *tag;
1087
1088         switch(wintype) {
1089         case wintype_TextGrid:
1090                 tag = g_hash_table_lookup(glk_data->styles->text_grid, "default");
1091                 break;
1092         case wintype_TextBuffer:
1093                 tag = g_hash_table_lookup(glk_data->styles->text_buffer, "default");
1094                 break;
1095         default:
1096                 return NULL;
1097         }
1098
1099         PangoFontDescription *font;
1100         g_object_get( G_OBJECT(tag), "font-desc", &font, NULL );
1101
1102         return font;
1103 }
1104
1105 /* Internal function copying the attributes of a text tag to a pango attribute list */
1106 static void
1107 text_tag_to_attr_list(GtkTextTag *tag, PangoAttrList *list)
1108 {
1109         gboolean set;
1110         GdkColor *foreground, *background;
1111         gchar *string;
1112         PangoFontDescription *font_desc;
1113         gboolean strikethrough;
1114         PangoUnderline underline;
1115
1116         g_object_get(tag, "foreground-set", &set, "foreground-gdk", &foreground, NULL);
1117         if(set) {
1118                 pango_attr_list_insert(
1119                         list,
1120                         pango_attr_foreground_new(foreground->red, foreground->green, foreground->blue)
1121                 );
1122         }
1123         g_object_get(tag, "background-set", &set, "background-gdk", &background, NULL);
1124         if(set) {
1125                 pango_attr_list_insert(
1126                         list,
1127                         pango_attr_background_new(background->red, background->green, background->blue)
1128                 );
1129         }
1130         g_object_get(tag, "language-set", &set, "language", &string, NULL);
1131         if(set) {
1132                 pango_attr_list_insert(
1133                         list,
1134                         pango_attr_language_new( pango_language_from_string(string) )
1135                 );
1136         }
1137
1138         /* Font description updates the following properties simultaniously:
1139          * family, style, weight, variant, stretch, size
1140          */
1141         g_object_get(tag, "font-desc", &font_desc, NULL);
1142         pango_attr_list_insert(
1143                 list,
1144                 pango_attr_font_desc_new(font_desc)
1145         );
1146
1147         g_object_get(tag, "strikethrough-set", &set, "strikethrough", &strikethrough, NULL);
1148         if(set) {
1149                 pango_attr_list_insert(
1150                         list,
1151                         pango_attr_strikethrough_new(strikethrough)
1152                 );
1153         }
1154         g_object_get(tag, "underline-set", &set, "underline", &underline, NULL);
1155         if(set) {
1156                 pango_attr_list_insert(
1157                         list,
1158                         pango_attr_underline_new(underline)
1159                 );
1160         }
1161 }
1162
1163 /* Update pager and reverse video tags */
1164 void
1165 style_update(ChimaraGlk *glk)
1166 {
1167         CHIMARA_GLK_USE_PRIVATE(glk, priv);
1168
1169         GtkTextTag *pager_tag = GTK_TEXT_TAG( g_hash_table_lookup(priv->styles->text_buffer, "pager") );
1170         text_tag_to_attr_list(pager_tag, priv->pager_attr_list);
1171 }