Fixes for garglk_set_zcolors()
[projects/chimara/chimara.git] / libchimara / garglk.c
1 #include <glib/gi18n.h>
2 #include <libchimara/glk.h>
3 #include "chimara-glk-private.h"
4 #include "stream.h"
5 #include "fileref.h"
6 #include "style.h"
7 #include "garglk.h"
8
9 extern GPrivate *glk_data_key;
10
11 /**
12  * garglk_fileref_get_name:
13  * @fref: A file reference.
14  *
15  * Gets the actual disk filename that @fref refers to, in the platform's
16  * native filename encoding. The string is owned by @fref and must not be
17  * changed or freed.
18  *
19  * Returns: a string in filename encoding.
20  */
21 char * 
22 garglk_fileref_get_name(frefid_t fref)
23 {
24         VALID_FILEREF(fref, return NULL);
25         return fref->filename;
26 }
27
28 /**
29  * garglk_set_program_name:
30  * @name: Name of the Glk program that is running.
31  *
32  * This function is used to let the library know the name of the currently
33  * running Glk program, in case it wants to display this information somewhere
34  * &mdash; for example, in the title bar of a window. A typical use of this
35  * function would be:
36  * |[ garglk_set_program_name("SuperGlkFrotz 0.1"); ]|
37  */
38 void 
39 garglk_set_program_name(const char *name)
40 {
41         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
42         glk_data->program_name = g_strdup(name);
43         g_object_notify(G_OBJECT(glk_data->self), "program-name");
44 }
45
46 /**
47  * garglk_set_program_info:
48  * @info: Information about the Glk program that is running.
49  *
50  * This function is used to provide the library with additional information
51  * about the currently running Glk program, in case it wants to display this
52  * information somewhere &mdash; for example, in an About box. A typical use of
53  * this function would be:
54  * |[ 
55  * garglk_set_program_info("SuperGlkFrotz, version 0.1\n"
56  *     "Original Frotz by Stefan Jokisch\n"
57  *     "Unix port by Jim Dunleavy and David Griffith\n"
58  *     "Glk port by Tor Andersson\n"
59  *     "Animation, networking, and evil AI by Sven Metcalfe");
60  * ]|
61  */
62 void 
63 garglk_set_program_info(const char *info)
64 {
65         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
66         glk_data->program_info = g_strdup(info);
67         g_object_notify(G_OBJECT(glk_data->self), "program-info");
68 }
69
70 /**
71  * garglk_set_story_name:
72  * @name: Name of the story that the Glk program is currently interpreting.
73  *
74  * If the Glk program running is an interactive fiction interpreter, then this
75  * function can be used to let the library know the name of the story currently
76  * loaded in the interpreter, in case it wants to display this information
77  * anywhere &mdash; for example, in the title bar of a window. A typical use of
78  * this function would be:
79  * |[ garglk_set_story_name("Lighan Ses Lion, el Zarf"); ]|
80  */
81 void 
82 garglk_set_story_name(const char *name)
83 {
84         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
85         glk_data->story_name = g_strdup(name);
86         g_object_notify(G_OBJECT(glk_data->self), "story-name");
87 }
88
89 /**
90  * garglk_set_line_terminators:
91  * @win: A window.
92  * @keycodes: An array of <code>keycode_</code> constants.
93  * @numkeycodes: The length of @keycodes.
94  *
95  * Amends the current line input request of @win to include terminating key
96  * codes. Any of the specified key codes will terminate the line input request 
97  * (without printing a newline). 
98  *
99  * Usually, in the event structure returned from a line input request, @val2 is
100  * zero, but if garglk_set_line_terminators() has been called during that input
101  * request, @val2 will be filled in with the key code that terminated the input
102  * request.
103  *
104  * This function only applies to one input request; any subsequent line input
105  * requests on that window are treated normally.
106  *
107  * If @numkeycodes is zero, then any previous call to 
108  * garglk_set_line_terminators() is cancelled and the input request is treated
109  * normally.
110  *
111  * <warning><para>This function is not currently implemented.</para></warning>
112  */
113 void 
114 garglk_set_line_terminators(winid_t win, const glui32 *keycodes, glui32 numkeycodes)
115 {
116         VALID_WINDOW(win, return);
117         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
118         
119         if(win->input_request_type != INPUT_REQUEST_LINE && win->input_request_type != INPUT_REQUEST_LINE_UNICODE) {
120                 ILLEGAL(_("Tried to set the line terminators on a window without a line input request."));
121                 return;
122         }
123
124         WARNING(_("Not implemented"));
125 }
126
127 /**
128  * garglk_unput_string:
129  * @str: a null-terminated string.
130  *
131  * Removes @str from the end of the current stream, if indeed it is there. The
132  * stream's write count is decreased accordingly, and the stream's echo stream
133  * is also modified, if it has one.
134  *
135  * <warning><para>This function is not currently implemented.</para></warning>
136  */
137 void 
138 garglk_unput_string(char *str)
139 {
140         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
141         g_return_if_fail(glk_data->current_stream != NULL);
142
143         WARNING(_("Not implemented"));
144 }
145
146 /**
147  * garglk_unput_string_uni:
148  * @str: a zero-terminated array of Unicode code points.
149  *
150  * Like garglk_unput_string(), but for Unicode streams.
151  *
152  * <warning><para>This function is not currently implemented.</para></warning>
153  */
154 void 
155 garglk_unput_string_uni(glui32 *str)
156 {
157         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
158         g_return_if_fail(glk_data->current_stream != NULL);
159         
160         WARNING(_("Not implemented"));
161 }
162
163 /* TODO document */
164 void
165 garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg)
166 {
167         VALID_STREAM(str, return);
168         g_return_if_fail(str->window != NULL);
169
170         winid_t window = str->window;
171
172         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(window->widget) );
173         GtkTextTagTable *tags = gtk_text_buffer_get_tag_table(buffer);
174         GdkColor fore, back;
175         GdkColor *fore_pointer = NULL;
176         GdkColor *back_pointer = NULL;
177         gchar *fore_name;
178         gchar *back_name;
179
180         switch(fg) {
181         case zcolor_Transparent:
182         case zcolor_Cursor:
183                 WARNING(_("zcolor_Transparent, zcolor_Cursor not implemented"));
184                 // Fallthrough to default
185         case zcolor_Default:
186                 fore_name = "default";
187                 break;
188         case zcolor_Current:
189         {
190                 if(window->zcolor) {
191                         // Get the current foreground color
192                         GdkColor *current_color;
193                         g_object_get(window->zcolor, "foreground-gdk", &current_color, NULL);
194                         fore_name = g_strdup_printf("%02X%02X%02X", current_color->red, current_color->green, current_color->blue);
195
196                         // Copy the color and use it
197                         fore.red = current_color->red;
198                         fore.green = current_color->green;
199                         fore.blue = current_color->blue;
200                         fore_pointer = &fore;
201                 } else {
202                         fore_name = "default";
203                 }
204                 break;
205         }
206         default:
207                 glkcolor_to_gdkcolor(fg, &fore);
208                 fore_pointer = &fore;
209                 fore_name = g_strdup_printf("%02X%02X%02X",
210                         ((fg & 0xff0000) >> 16),
211                         ((fg & 0x00ff00) >> 8),
212                         (fg & 0x0000ff)
213                 );
214         }
215
216         switch(bg) {
217         case zcolor_Transparent:
218         case zcolor_Cursor:
219                 WARNING(_("zcolor_Transparent, zcolor_Cursor not implemented"));
220                 // Fallthrough to default
221         case zcolor_Default:
222                 back_name = "default";
223                 break;
224         case zcolor_Current:
225         {
226                 if(window->zcolor) {
227                         // Get the current background color
228                         GdkColor *current_color;
229                         g_object_get(window->zcolor, "background-gdk", &current_color, NULL);
230                         back_name = g_strdup_printf("%02X%02X%02X", current_color->red, current_color->green, current_color->blue);
231
232                         // Copy the color and use it
233                         back.red = current_color->red;
234                         back.green = current_color->green;
235                         back.blue = current_color->blue;
236                         back_pointer = &back;
237                 } else {
238                         back_name = "default";
239                 }
240                 break;
241         }
242         default:
243                 glkcolor_to_gdkcolor(bg, &back);
244                 back_pointer = &back;
245                 back_name = g_strdup_printf("%02X%02X%02X",
246                         ((bg & 0xff0000) >> 16),
247                         ((bg & 0x00ff00) >> 8),
248                         (bg & 0x0000ff)
249                 );
250         }
251
252         char *name = g_strdup_printf("zcolor:#%s/#%s", fore_name, back_name);
253
254         if(fore_pointer == NULL && back_pointer == NULL) {
255                 // NULL value means to ignore the zcolor property altogether
256                 window->zcolor = NULL;
257         } else {
258                 // See if we have used this color combination before
259                 GtkTextTag *tag = gtk_text_tag_table_lookup(tags, name);
260
261                 if(tag == NULL) {
262                         // Create a new texttag with the specified colors
263                         tag = gtk_text_buffer_create_tag(
264                                 buffer,
265                                 name,
266                                 "foreground-gdk", fore_pointer,
267                                 "foreground-set", fore_pointer != NULL,
268                                 "background-gdk", back_pointer,
269                                 "background-set", back_pointer != NULL,
270                                 NULL
271                         );
272                 }
273
274                 // From now on, text will be drawn in the specified colors
275                 window->zcolor = tag;
276         }
277 }
278
279 /**
280  * garglk_set_zcolors:
281  * @fg: one of the <code>zcolor_</code> constants.
282  * @bg: one of the <code>zcolor_</code> constants.
283  *
284  * Glk works with styles, not specific colors. This is not quite compatible with
285  * the Z-machine, so this Glk extension implements Z-machine style colors.
286  *
287  * This function changes the foreground color of the current stream to @fg and 
288  * the background color to @bg.
289  *
290  * <warning><para>This function is not currently implemented.</para></warning>
291  */
292 void 
293 garglk_set_zcolors(glui32 fg, glui32 bg)
294 {
295         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
296         g_return_if_fail(glk_data->current_stream != NULL);
297
298         garglk_set_zcolors_stream(glk_data->current_stream, fg, bg);
299 }
300
301 static void
302 apply_reverse_color(GtkTextTag *tag, gpointer data)
303 {
304         const gchar *tag_name;
305         g_object_get(tag, "name", &tag_name, NULL);
306
307         if( g_str_has_prefix(tag_name, "glk-") )
308                 g_object_set_data( G_OBJECT(tag), "reverse_color", data );
309 }
310
311 /* TODO document */
312 void
313 garglk_set_reversevideo_stream(strid_t str, glui32 reverse)
314 {
315         VALID_STREAM(str, return);
316         
317         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(str->window->widget) );
318         GtkTextTagTable *tags = gtk_text_buffer_get_tag_table(buffer);
319         gtk_text_tag_table_foreach( tags, apply_reverse_color, GINT_TO_POINTER(reverse) );
320 }
321
322 /**
323  * garglk_set_reversevideo:
324  * @reverse: nonzero for reverse colors, zero for normal colors.
325  *
326  * If @reverse is not zero, uses the foreground color of the current stream as
327  * its background and vice versa. If @reverse is zero, changes the colors of the
328  * current stream back to normal.
329  */
330 void 
331 garglk_set_reversevideo(glui32 reverse)
332 {
333         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
334         g_return_if_fail(glk_data->current_stream != NULL);
335         g_return_if_fail(glk_data->current_stream->window != NULL);
336
337         garglk_set_reversevideo_stream(glk_data->current_stream, reverse);
338 }