5 extern GPrivate *glk_data_key;
6 static gboolean chimara_style_initialized = FALSE;
10 * @styl: The style to apply
12 * Changes the style of the current output stream. @styl should be one of the
13 * <code>style_</code> constants listed above. However, any value is actually
14 * legal; if the interpreter does not recognize the style value, it will treat
15 * it as %style_Normal.
17 * This policy allows for the future definition of styles without breaking old
22 glk_set_style(glui32 styl)
24 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
25 g_return_if_fail(glk_data->current_stream != NULL);
26 glk_set_style_stream(glk_data->current_stream, styl);
29 /* Internal function: mapping from style enum to tag name */
31 get_tag_name(glui32 style)
34 case style_Normal: return "normal";
35 case style_Emphasized: return "emphasized";
36 case style_Preformatted: return "preformatted";
37 case style_Header: return "header";
38 case style_Subheader: return "subheader";
39 case style_Alert: return "alert";
40 case style_Note: return "note";
41 case style_BlockQuote: return "block-quote";
42 case style_Input: return "input";
43 case style_User1: return "user1";
44 case style_User2: return "user2";
47 WARNING("Unsupported style");
52 * glk_set_style_stream:
53 * @str: Output stream to change the style of
54 * @styl: The style to apply
56 * This changes the style of the stream @str. See glk_set_style().
59 glk_set_style_stream(strid_t str, glui32 styl) {
60 str->style = get_tag_name(styl);
63 /* Internal function: call this to initialize the default styles to a textbuffer. */
65 style_init_textbuffer(GtkTextBuffer *buffer)
67 g_return_if_fail(buffer != NULL);
69 if( G_UNLIKELY(!chimara_style_initialized) ) {
73 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
74 g_hash_table_foreach(glk_data->default_styles, style_add_tag_to_textbuffer, gtk_text_buffer_get_tag_table(buffer));
78 style_add_tag_to_textbuffer(gpointer key, gpointer tag, gpointer tag_table)
80 gtk_text_tag_table_add(tag_table, tag);
86 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
89 /* Create the CSS file scanner */
90 GScanner *scanner = g_scanner_new(NULL);
91 int f = open(glk_data->css_file, O_RDONLY);
92 g_return_if_fail(f != -1);
93 g_scanner_input_file(scanner, f);
94 scanner->input_name = glk_data->css_file;
95 scanner->config->cset_identifier_first = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ#";
96 scanner->config->cset_identifier_nth = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_0123456789";
97 scanner->config->symbol_2_token = TRUE;
98 scanner->config->cpair_comment_single = NULL;
100 /* Initialise the default styles */
101 g_hash_table_insert(glk_data->default_styles, "normal", gtk_text_tag_new("normal"));
103 tag = gtk_text_tag_new("emphasized");
104 g_object_set(tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
105 g_hash_table_insert(glk_data->default_styles, "emphasized", tag);
107 tag = gtk_text_tag_new("preformatted");
108 g_object_set(tag, "font-desc", glk_data->monospace_font_desc, NULL);
109 g_hash_table_insert(glk_data->default_styles, "preformatted", tag);
111 tag = gtk_text_tag_new("header");
112 g_object_set(tag, "size-points", 18.0, "weight", PANGO_WEIGHT_BOLD, NULL);
113 g_hash_table_insert(glk_data->default_styles, "header", tag);
115 tag = gtk_text_tag_new("subheader");
116 g_object_set(tag, "size-points", 14.0, "weight", PANGO_WEIGHT_BOLD, NULL);
117 g_hash_table_insert(glk_data->default_styles, "subheader", tag);
119 tag = gtk_text_tag_new("alert");
120 g_object_set(tag, "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
121 g_hash_table_insert(glk_data->default_styles, "alert", tag);
123 tag = gtk_text_tag_new("note");
124 g_object_set(tag, "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
125 g_hash_table_insert(glk_data->default_styles, "note", tag);
127 tag = gtk_text_tag_new("block-quote");
128 g_object_set(tag, "justification", GTK_JUSTIFY_CENTER, "style", PANGO_STYLE_ITALIC, NULL);
129 g_hash_table_insert(glk_data->default_styles, "block-quote", tag);
131 g_hash_table_insert(glk_data->default_styles, "input", gtk_text_tag_new("input"));
132 g_hash_table_insert(glk_data->default_styles, "user1", gtk_text_tag_new("user1"));
133 g_hash_table_insert(glk_data->default_styles, "user2", gtk_text_tag_new("user2"));
135 /* Run the scanner over the CSS file */
136 while( g_scanner_peek_next_token(scanner) != G_TOKEN_EOF) {
137 if( !style_accept_style_identifier(scanner) )
141 g_scanner_destroy(scanner);
145 style_accept(GScanner *scanner, GTokenType token)
147 if( g_scanner_get_next_token(scanner) != token ) {
148 g_scanner_unexp_token(scanner, token, NULL, NULL, NULL, "CSS Error", 1);
156 style_accept_style_identifier(GScanner *scanner)
158 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
160 GtkTextTag *current_tag;
161 GTokenType token = g_scanner_get_next_token(scanner);
162 GTokenValue value = g_scanner_cur_value(scanner);
164 if(token != G_TOKEN_IDENTIFIER) {
165 g_scanner_error(scanner, "CSS Error: style identifier expected");
169 printf("Identifier: %s\n", value.v_identifier);
170 current_tag = g_hash_table_lookup(glk_data->default_styles, value.v_identifier);
172 if(current_tag == NULL) {
173 g_scanner_error(scanner, "CSS Error: invalid style identifier");
177 if( !style_accept(scanner, '{') )
180 while( g_scanner_peek_next_token(scanner) != '}') {
181 if( !style_accept_style_hint(scanner, current_tag) )
185 if( !style_accept(scanner, '}') )
192 style_accept_style_hint(GScanner *scanner, GtkTextTag *current_tag)
194 GTokenType token = g_scanner_get_next_token(scanner);
195 GTokenValue value = g_scanner_cur_value(scanner);
198 if(token != G_TOKEN_IDENTIFIER) {
199 g_scanner_error(scanner, "CSS Error: style hint expected");
203 hint = g_strdup(value.v_identifier);
204 printf("Hint: %s\n", hint);
206 if( !style_accept(scanner, ':') )
209 token = g_scanner_get_next_token(scanner);
210 value = g_scanner_cur_value(scanner);
212 if( !strcmp(hint, "font-family") ) {
213 if(token != G_TOKEN_STRING) {
214 g_scanner_error(scanner, "CSS Error: string expected");
217 g_object_set(current_tag, "family", value.v_string, "family-set", TRUE, NULL);
219 else if( !strcmp(hint, "font-weight") ) {
220 if(token != G_TOKEN_IDENTIFIER) {
221 g_scanner_error(scanner, "CSS Error: bold/normal expected");
225 if( !strcmp(value.v_identifier, "bold") )
226 g_object_set(current_tag, "weight", PANGO_WEIGHT_BOLD, "weight-set", TRUE, NULL);
227 else if( !strcmp(value.v_identifier, "normal") )
228 g_object_set(current_tag, "weight", PANGO_WEIGHT_NORMAL, "weight-set", TRUE, NULL);
230 g_scanner_error(scanner, "CSS Error: bold/normal expected");
234 else if( !strcmp(hint, "font-style") ) {
235 if(token != G_TOKEN_IDENTIFIER) {
236 g_scanner_error(scanner, "CSS Error: italic/normal expected");
240 if( !strcmp(value.v_identifier, "italic") )
241 g_object_set(current_tag, "style", PANGO_STYLE_ITALIC, "style-set", TRUE, NULL);
242 else if( !strcmp(value.v_identifier, "normal") )
243 g_object_set(current_tag, "style", PANGO_STYLE_NORMAL, "style-set", TRUE, NULL);
245 g_scanner_error(scanner, "CSS Error: italic/normal expected");
249 else if( !strcmp(hint, "font-size") ) {
250 if(token == G_TOKEN_INT)
251 g_object_set(current_tag, "size-points", (float)value.v_int, "size-set", TRUE, NULL);
252 else if(token == G_TOKEN_FLOAT)
253 g_object_set(current_tag, "size-points", value.v_float, "size-set", TRUE, NULL);
255 g_scanner_error(scanner, "CSS Error: integer or float expected");
259 else if( !strcmp(hint, "color") ) {
260 if(token != G_TOKEN_IDENTIFIER) {
261 g_scanner_error(scanner, "CSS Error: hex color expected");
264 g_object_set(current_tag, "foreground", value.v_identifier, "foreground-set", TRUE, NULL);
266 else if( !strcmp(hint, "background-color") ) {
267 if(token != G_TOKEN_IDENTIFIER) {
268 g_scanner_error(scanner, "CSS Error: hex color expected");
271 g_object_set(current_tag, "background", value.v_identifier, "background-set", TRUE, NULL);
273 else if( !strcmp(hint, "text-align") ) {
274 if(token != G_TOKEN_IDENTIFIER) {
275 g_scanner_error(scanner, "CSS Error: left/right/center expected");
279 if( !strcmp(value.v_identifier, "left") )
280 g_object_set(current_tag, "justification", GTK_JUSTIFY_LEFT, "justification-set", TRUE, NULL);
281 else if( !strcmp(value.v_identifier, "right") )
282 g_object_set(current_tag, "justification", GTK_JUSTIFY_RIGHT, "justification-set", TRUE, NULL);
283 else if( !strcmp(value.v_identifier, "center") )
284 g_object_set(current_tag, "justification", GTK_JUSTIFY_CENTER, "justification-set", TRUE, NULL);
286 g_scanner_error(scanner, "CSS Error: left/right/center expected");
290 else if( !strcmp(hint, "margin-left") ) {
291 if(token != G_TOKEN_INT) {
292 g_scanner_error(scanner, "CSS Error: integer expected");
295 g_object_set(current_tag, "left-margin", value.v_int, "left-margin-set", TRUE, NULL);
297 else if( !strcmp(hint, "margin-right") ) {
298 if(token != G_TOKEN_INT) {
299 g_scanner_error(scanner, "CSS Error: integer expected");
302 g_object_set(current_tag, "right-margin", value.v_int, "right-margin-set", TRUE, NULL);
304 else if( !strcmp(hint, "margin-top") ) {
305 if(token != G_TOKEN_INT) {
306 g_scanner_error(scanner, "CSS Error: integer expected");
309 g_object_set(current_tag, "pixels-above-lines", value.v_int, "pixels-above-lines-set", TRUE, NULL);
311 else if( !strcmp(hint, "margin-bottom") ) {
312 if(token != G_TOKEN_INT) {
313 g_scanner_error(scanner, "CSS Error: integer expected");
316 g_object_set(current_tag, "pixels-below-lines", value.v_int, "pixels-below-lines-set", TRUE, NULL);
320 g_scanner_error(scanner, "CSS Error: invalid style hint %s", hint);
324 if( !style_accept(scanner, ';') )
331 color_format(glui32 val, gchar *buffer)
333 sprintf(buffer, "#%02X%02X%02X",
334 ((val & 0xff0000) >> 16),
335 ((val & 0x00ff00) >> 8),
340 /* Internal function: changes a GTK tag to correspond with the given style. */
342 apply_stylehint_to_tag(GtkTextTag *tag, glui32 hint, glsi32 val)
344 g_return_if_fail(tag != NULL);
346 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
347 GObject *tag_object = G_OBJECT(tag);
348 gint reverse_color = 0;
350 /* FIXME where should we keep track of this?
351 g_object_get(tag, "reverse_color", &reverse_color, NULL);
357 case stylehint_Indentation:
358 g_object_set(tag_object, "left_margin", 5*val, NULL);
359 g_object_set(tag_object, "right_margin", 5*val, NULL);
362 case stylehint_ParaIndentation:
363 g_object_set(tag_object, "indent", 5*val, NULL);
366 case stylehint_Justification:
368 case stylehint_just_LeftFlush: i = GTK_JUSTIFY_LEFT; break;
369 case stylehint_just_LeftRight: i = GTK_JUSTIFY_FILL; break;
370 case stylehint_just_Centered: i = GTK_JUSTIFY_CENTER; break;
371 case stylehint_just_RightFlush: i = GTK_JUSTIFY_RIGHT; break;
373 WARNING("Unknown justification");
374 i = GTK_JUSTIFY_LEFT;
376 g_object_set(tag_object, "justification", i, NULL);
379 case stylehint_Weight:
381 case -1: i = PANGO_WEIGHT_LIGHT; break;
382 case 0: i = PANGO_WEIGHT_NORMAL; break;
383 case 1: i = PANGO_WEIGHT_BOLD; break;
384 default: WARNING("Unknown font weight");
386 g_object_set(tag_object, "weight", i, NULL);
390 g_object_set(tag_object, "size", 14+(2*val), NULL);
393 case stylehint_Oblique:
394 g_object_set(tag_object, "style", val ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL, NULL);
397 case stylehint_Proportional:
398 g_object_set(tag_object, "font-desc", val ? glk_data->default_font_desc : glk_data->monospace_font_desc, NULL);
401 case stylehint_TextColor:
402 color_format(val, color);
405 g_object_set(tag_object, "foreground", color, NULL);
407 g_object_set(tag_object, "background", color, NULL);
411 case stylehint_BackColor:
412 color_format(val, color);
415 g_object_set(tag_object, "background", color, NULL);
417 g_object_set(tag_object, "foreground", color, NULL);
421 case stylehint_ReverseColor:
422 if(reverse_color != val) {
423 /* Flip the fore- and background colors */
424 gchar* foreground_color;
425 gchar* background_color;
426 g_object_get(tag_object, "foreground", &foreground_color, NULL);
427 g_object_get(tag_object, "background", &background_color, NULL);
428 g_object_set(tag_object, "foreground", background_color, NULL);
429 g_object_set(tag_object, "background", foreground_color, NULL);
430 g_free(foreground_color);
431 g_free(background_color);
436 WARNING("Unknown style hint");
442 * @wintype: The window type to set a style hint on, or %wintype_AllTypes.
443 * @styl: The style to set a hint for.
444 * @hint: The type of style hint, one of the <code>stylehint_</code> constants.
445 * @val: The style hint. The meaning of this depends on @hint.
447 * Sets a hint about the appearance of one style for a particular type of
448 * window. You can also set wintype to %wintype_AllTypes, which sets a hint for
449 * all types of window.
451 * There is no equivalent constant to set a hint for all styles of a single
456 glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val)
458 gchar *tag_name = get_tag_name(styl);
460 /* Iterate over all the window and update their styles if nessecary */
461 winid_t win = glk_window_iterate(NULL, NULL);
463 if(wintype != wintype_TextBuffer)
464 continue; /* FIXME: add support for text grid windows */
466 if(wintype == wintype_AllTypes || glk_window_get_type(win) == wintype) {
467 GtkWidget *textview = win->widget;
468 GtkTextBuffer *textbuffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(textview) );
469 GtkTextTagTable *table = gtk_text_buffer_get_tag_table(textbuffer);
470 GtkTextTag *to_change = gtk_text_tag_table_lookup(table, tag_name);
472 apply_stylehint_to_tag(to_change, hint, val);
478 glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint)
483 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2)
485 return styl1 != styl2;
489 glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result)