Implemented glk_select_poll() (Fix #18)
[rodin/chimara.git] / libchimara / style.c
1 #include "style.h"
2
3 extern ChimaraGlkPrivate *glk_data;
4
5 /**
6  * glk_set_style:
7  * @styl: The style to apply
8  *
9  * Changes the style of the current output stream. @styl should be one of the
10  * <code>style_</code> constants listed above. However, any value is actually
11  * legal; if the interpreter does not recognize the style value, it will treat
12  * it as %style_Normal.
13  * <note><para>
14  *  This policy allows for the future definition of styles without breaking old
15  *  Glk libraries.
16  * </para></note>
17  */
18 void
19 glk_set_style(glui32 styl)
20 {
21         g_return_if_fail(glk_data->current_stream != NULL);
22         glk_set_style_stream(glk_data->current_stream, styl);
23 }
24
25 /* Internal function: mapping from style enum to tag name */
26 static gchar *
27 get_tag_name(glui32 style)
28 {
29         switch(style) {
30                 case style_Normal: return "normal";
31                 case style_Emphasized: return "emphasized";
32                 case style_Preformatted: return "preformatted";
33                 case style_Header: return "header";
34                 case style_Subheader: return "subheader";
35                 case style_Alert: return "alert";
36                 case style_Note: return "note";
37                 case style_BlockQuote: return "block-quote";
38                 case style_Input: return "input";
39                 case style_User1: return "user1";
40                 case style_User2: return "user2";
41         }
42
43         WARNING("Unsupported style");
44         return "normal";
45 }
46
47 /** 
48  * glk_set_style_stream:
49  * @str: Output stream to change the style of
50  * @styl: The style to apply
51  *
52  * This changes the style of the stream @str. See glk_set_style().
53  */
54 void
55 glk_set_style_stream(strid_t str, glui32 styl) {
56         str->style = get_tag_name(styl);
57 }
58
59 /* Internal function: call this to initialize the default styles to a textbuffer. */
60 void
61 style_init_textbuffer(GtkTextBuffer *buffer)
62 {
63         g_return_if_fail(buffer != NULL);
64
65         gtk_text_buffer_create_tag(buffer, "normal", NULL);
66         gtk_text_buffer_create_tag(buffer, "emphasized", "style", PANGO_STYLE_ITALIC, NULL);
67         gtk_text_buffer_create_tag(buffer, "preformatted", "font-desc", glk_data->monospace_font_desc, NULL);
68         gtk_text_buffer_create_tag(buffer, "header", "size-points", 18.0, "weight", PANGO_WEIGHT_BOLD, NULL);
69         gtk_text_buffer_create_tag(buffer, "subheader", "size-points", 14.0, "weight", PANGO_WEIGHT_BOLD, NULL);
70         gtk_text_buffer_create_tag(buffer, "alert", "foreground", "#aa0000", "weight", PANGO_WEIGHT_BOLD, NULL);
71         gtk_text_buffer_create_tag(buffer, "note", "foreground", "#aaaa00", "weight", PANGO_WEIGHT_BOLD, NULL);
72         gtk_text_buffer_create_tag(buffer, "block-quote", "justification", GTK_JUSTIFY_CENTER, "style", PANGO_STYLE_ITALIC, NULL);
73         gtk_text_buffer_create_tag(buffer, "input", NULL);
74         gtk_text_buffer_create_tag(buffer, "user1", NULL);
75         gtk_text_buffer_create_tag(buffer, "user2", NULL);
76 }
77
78 static void
79 color_format(glui32 val, gchar *buffer)
80 {
81         sprintf(buffer, "#%02X%02X%02X",
82                 ((val & 0xff0000) >> 16),
83                 ((val & 0x00ff00) >> 8),
84                 (val & 0x0000ff)
85         );
86 }
87
88 /* Internal function: changes a GTK tag to correspond with the given style. */
89 static void
90 apply_stylehint_to_tag(GtkTextTag *tag, glui32 hint, glsi32 val)
91 {
92         g_return_if_fail(tag != NULL);
93
94         GObject *tag_object = G_OBJECT(tag);
95         gint reverse_color = 0;
96
97         /* FIXME where should we keep track of this?
98         g_object_get(tag, "reverse_color", &reverse_color, NULL);
99         */
100
101         int i = 0;
102         gchar color[20];
103         switch(hint) {
104         case stylehint_Indentation:
105                 g_object_set(tag_object, "left_margin", 5*val, NULL);
106                 g_object_set(tag_object, "right_margin", 5*val, NULL);
107                 break;
108         
109         case stylehint_ParaIndentation:
110                 g_object_set(tag_object, "indent", 5*val, NULL);
111                 break;
112
113         case stylehint_Justification:
114                 switch(val) {
115                         case stylehint_just_LeftFlush:  i = GTK_JUSTIFY_LEFT; break;
116                         case stylehint_just_LeftRight:  i = GTK_JUSTIFY_FILL; break;
117                         case stylehint_just_Centered:   i = GTK_JUSTIFY_CENTER; break;
118                         case stylehint_just_RightFlush: i = GTK_JUSTIFY_RIGHT; break;
119                         default: 
120                                 WARNING("Unknown justification");
121                                 i = GTK_JUSTIFY_LEFT;
122                 }
123                 g_object_set(tag_object, "justification", i, NULL);
124                 break;
125
126         case stylehint_Weight:
127                 switch(val) {
128                         case -1: i = PANGO_WEIGHT_LIGHT; break;
129                         case  0: i = PANGO_WEIGHT_NORMAL; break;
130                         case  1: i = PANGO_WEIGHT_BOLD; break;
131                         default: WARNING("Unknown font weight");
132                 }
133                 g_object_set(tag_object, "weight", i, NULL);
134                 break;
135
136         case stylehint_Size:
137                 g_object_set(tag_object, "size", 14+(2*val), NULL);
138                 break;
139
140         case stylehint_Oblique:
141                 g_object_set(tag_object, "style", val ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL, NULL);
142                 break;
143
144         case stylehint_Proportional:
145                 g_object_set(tag_object, "font-desc", val ? glk_data->default_font_desc : glk_data->monospace_font_desc, NULL);
146                 break;
147
148         case stylehint_TextColor:
149                 color_format(val, color);
150
151                 if(!reverse_color)
152                         g_object_set(tag_object, "foreground", color, NULL);
153                 else
154                         g_object_set(tag_object, "background", color, NULL);
155
156                 break;
157
158         case stylehint_BackColor:
159                 color_format(val, color);
160
161                 if(!reverse_color)
162                         g_object_set(tag_object, "background", color, NULL);
163                 else
164                         g_object_set(tag_object, "foreground", color, NULL);
165
166                 break;
167
168         case stylehint_ReverseColor:
169                 if(reverse_color != val) {
170                         /* Flip the fore- and background colors */
171                         gchar* foreground_color;
172                         gchar* background_color;
173                         g_object_get(tag_object, "foreground", &foreground_color, NULL);
174                         g_object_get(tag_object, "background", &background_color, NULL);
175                         g_object_set(tag_object, "foreground", background_color, NULL);
176                         g_object_set(tag_object, "background", foreground_color, NULL);
177                         g_free(foreground_color);
178                         g_free(background_color);
179                 }
180                 break;
181
182         default:
183                 WARNING("Unknown style hint");
184         }
185 }
186
187 /**
188  * glk_stylehint_set:
189  * @wintype: The window type to set a style hint on, or %wintype_AllTypes.
190  * @styl: The style to set a hint for.
191  * @hint: The type of style hint, one of the <code>stylehint_</code> constants.
192  * @val: The style hint. The meaning of this depends on @hint.
193  *
194  * Sets a hint about the appearance of one style for a particular type of 
195  * window. You can also set wintype to %wintype_AllTypes, which sets a hint for 
196  * all types of window.
197  * <note><para>
198  *  There is no equivalent constant to set a hint for all styles of a single 
199  *  window type.
200  * </para></note>
201  */
202 void
203 glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val)
204 {
205         gchar *tag_name = get_tag_name(styl);
206
207         /* Iterate over all the window and update their styles if nessecary */
208         winid_t win = glk_window_iterate(NULL, NULL);
209         while(win != NULL) {
210                 if(wintype != wintype_TextBuffer)
211                         continue; /* FIXME: add support for text grid windows */
212
213                 if(wintype == wintype_AllTypes || glk_window_get_type(win) == wintype) {
214                         GtkWidget *textview = win->widget;
215                         GtkTextBuffer *textbuffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(textview) );
216                         GtkTextTagTable *table = gtk_text_buffer_get_tag_table(textbuffer);
217                         GtkTextTag *to_change = gtk_text_tag_table_lookup(table, tag_name);
218
219                         apply_stylehint_to_tag(to_change, hint, val);
220                 }
221         }
222 }
223
224 void
225 glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint)
226 {
227 }
228
229 glui32
230 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2)
231 {
232         return styl1 != styl2;
233 }