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