first.c compilet en draait!
[rodin/chimara.git] / src / window.c
1 #include "window.h"
2
3 /* Global tree of all windows */
4 static GNode *root_window = NULL;
5
6 /**
7  * glk_window_iterate:
8  * @win: A window, or #NULL.
9  * @rockptr: Return location for the next window's rock, or #NULL.
10  *
11  * Iterates over the list of windows; if @win is #NULL, it returns the first
12  * window, otherwise the next window after @win. If there are no more, it
13  * returns #NULL. The window's rock is stored in @rockptr. If you don't want
14  * the rocks to be returned, you may set @rockptr to #NULL.
15  *
16  * The order in which windows are returned is arbitrary. The root window is
17  * not necessarily first, nor is it necessarily last. The order may change
18  * every time you create or destroy a window, invalidating the iteration.
19  *
20  * Returns: the next window, or #NULL if there are no more.
21  */
22 winid_t
23 glk_window_iterate(winid_t win, glui32 *rockptr)
24 {
25         GNode *retnode;
26         
27         if(win == NULL)
28                 retnode = root_window;
29         else
30         {
31                 GNode *node = win->window_node;
32                 if( G_NODE_IS_LEAF(node) )
33                 {
34                         while(node && node->next == NULL)
35                                 node = node->parent;
36                         if(node)
37                                 retnode = node->next;
38                         else
39                                 retnode = NULL;
40                 }
41                 else
42                         retnode = g_node_first_child(node);
43         }
44         winid_t retval = retnode? (winid_t)retnode->data : NULL;
45                 
46         /* Store the window's rock in rockptr */
47         if(retval && rockptr)
48                 *rockptr = glk_window_get_rock(retval);
49                 
50         return retval;
51 }
52
53 /**
54  * glk_window_get_rock:
55  * @win: A window.
56  * 
57  * Returns the window @win's rock value. Pair windows always have rock 0.
58  *
59  * Returns: A rock value.
60  */
61 glui32
62 glk_window_get_rock(winid_t win)
63 {
64         g_return_val_if_fail(win != NULL, 0);
65         return win->rock;
66 }
67
68 /**
69  * glk_window_get_type:
70  * @win: A window.
71  *
72  * Returns the window @win's type, one of #wintype_Blank, #wintype_Pair,
73  * #wintype_TextBuffer, #wintype_TextGrid, or #wintype_Graphics.
74  *
75  * Returns: The window's type.
76  */
77 glui32
78 glk_window_get_type(winid_t win)
79 {
80         g_return_val_if_fail(win != NULL, 0);
81         return win->window_type;
82 }
83
84 /**
85  * glk_window_get_parent:
86  * @win: A window.
87  *
88  * Returns the window @win's parent window. If @win is the root window, this
89  * returns #NULL, since the root window has no parent. Remember that the parent
90  * of every window is a pair window; other window types are always childless.
91  *
92  * Returns: A window.
93  */
94 winid_t
95 glk_window_get_parent(winid_t win)
96 {
97         g_return_val_if_fail(win != NULL, NULL);
98         /* Value will also be NULL if win is the root window */
99         return (winid_t)win->window_node->parent->data;
100 }
101
102 /**
103  * glk_window_get_sibling:
104  * @win: A window.
105  *
106  * Returns the other child of the window @win's parent. If @win is the
107  * root window, this returns #NULL.
108  *
109  * Returns: A window, or NULL.
110  */
111 winid_t
112 glk_window_get_sibling(winid_t win)
113 {
114         g_return_val_if_fail(win != NULL, NULL);
115         
116         if(G_NODE_IS_ROOT(win->window_node))
117                 return NULL;
118         if(win->window_node->next)
119                 return (winid_t)win->window_node->next;
120         return (winid_t)win->window_node->prev;
121 }
122
123 /**
124  * glk_window_get_root:
125  * 
126  * Returns the root window. If there are no windows, this returns #NULL.
127  *
128  * Returns: A window, or #NULL.
129  */
130 winid_t
131 glk_window_get_root()
132 {
133         if(root_window == NULL)
134                 return NULL;
135         return (winid_t)root_window->data;
136 }
137
138 /**
139  * glk_window_open:
140  * @split: The window to split to create the new window. Must be 0 if there
141  * are no windows yet.
142  * @method: Position of the new window and method of size computation. One of
143  * #winmethod_Above, #winmethod_Below, #winmethod_Left, or #winmethod_Right
144  * OR'ed with #winmethod_Fixed or #winmethod_Proportional. If @wintype is
145  * #wintype_Blank, then #winmethod_Fixed is not allowed.
146  * @size: Size of the new window, in percentage points if @method is
147  * #winmethod_Proportional, otherwise in characters if @wintype is 
148  * #wintype_TextBuffer or #wintype_TextGrid, or pixels if @wintype is
149  * #wintype_Graphics.
150  * @wintype: Type of the new window. One of #wintype_Blank, #wintype_TextGrid,
151  * #wintype_TextBuffer, or #wintype_Graphics.
152  * @rock: The new window's rock value.
153  *
154  * If there are no windows, create a new root window. @split must be 0, and
155  * @method and @size are ignored. Otherwise, split window @split into two, with
156  * position, size, and type specified by @method, @size, and @wintype. See the
157  * Glk documentation for the window placement algorithm.
158  *
159  * Returns: the new window.
160  */
161 winid_t
162 glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, 
163                 glui32 rock)
164 {
165         extern GtkBuilder *builder;
166
167         if(split)
168         {
169                 g_warning("glk_window_open: splitting of windows not implemented");
170                 return NULL;
171         }
172
173         if(root_window != NULL)
174         {
175                 g_warning("glk_window_open: there is already a window");
176                 return NULL;
177         }
178         /* We only create one window and don't support any more than that */
179         winid_t new_window = g_new0(struct glk_window_struct, 1);
180         root_window = g_node_new(new_window);
181
182         new_window->rock = rock;
183         new_window->window_type = wintype;
184
185         gdk_threads_enter();
186
187         GtkBox *vbox = GTK_BOX( gtk_builder_get_object(builder, "vbox") );                      
188         if(vbox == NULL)
189         {
190                 error_dialog(NULL, NULL, "Could not find vbox");
191                 gdk_threads_leave();
192                 return NULL;
193         }
194
195         switch(wintype)
196         {
197                 case wintype_Blank:
198                 {
199                         /* A blank window will be a label without any text */
200                         GtkWidget *window = gtk_label_new("");
201                         gtk_box_pack_end(vbox, window, TRUE, TRUE, 0);
202                         gtk_widget_show(window);
203                         
204                         new_window->widget = window;
205                         /* You can print to a blank window's stream, but it does nothing */
206                         new_window->window_stream = window_stream_new(new_window);
207                         new_window->echo_stream = NULL;
208                 }
209                         break;
210                         
211                 case wintype_TextBuffer:
212                 {
213                         GtkWidget *scroll_window = gtk_scrolled_window_new(NULL, NULL);
214                         GtkWidget *window = gtk_text_view_new();
215                         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(window) );
216
217                         gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(window), GTK_WRAP_WORD_CHAR );
218                         gtk_text_view_set_editable( GTK_TEXT_VIEW(window), FALSE );
219
220                         gtk_container_add( GTK_CONTAINER(scroll_window), window );
221                         gtk_box_pack_end(vbox, scroll_window, TRUE, TRUE, 0);
222                         gtk_widget_show_all(scroll_window);
223
224                         new_window->widget = window;
225                         new_window->window_stream = window_stream_new(new_window);
226                         new_window->echo_stream = NULL;
227                         new_window->input_request_type = INPUT_REQUEST_NONE;
228                         new_window->line_input_buffer = NULL;
229                         new_window->line_input_buffer_unicode = NULL;
230
231                         /* Connect signal handlers */
232                         new_window->keypress_handler = g_signal_connect( G_OBJECT(window), "key-press-event", G_CALLBACK(on_window_key_press_event), new_window );
233                         g_signal_handler_block( G_OBJECT(window), new_window->keypress_handler );
234
235                         new_window->insert_text_handler = g_signal_connect_after( G_OBJECT(buffer), "insert-text", G_CALLBACK(after_window_insert_text), new_window );
236                         g_signal_handler_block( G_OBJECT(buffer), new_window->insert_text_handler );
237
238                         /* Create an editable tag to indicate editable parts of the window (for line input) */
239                         gtk_text_buffer_create_tag(buffer, "uneditable", "editable", FALSE, "editable-set", TRUE, NULL);
240
241                         /* Mark the position where the user will input text */
242                         GtkTextIter end_iter;
243                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
244                         gtk_text_buffer_create_mark(buffer, "input_position", &end_iter, TRUE);
245                 }
246                         break;
247                         
248                 default:
249                         g_warning("glk_window_open: unsupported window type");
250                         g_free(new_window);
251                         gdk_threads_leave();
252                         return NULL;
253         }
254
255         new_window->window_node = root_window;
256
257         gdk_threads_leave();
258
259         return new_window;
260 }
261
262 void
263 glk_window_close(winid_t win, stream_result_t *result)
264 {
265         g_return_if_fail(win != NULL);
266
267         switch(win->window_type)
268         {
269                 case wintype_TextBuffer:
270                         gtk_widget_destroy( gtk_widget_get_parent(win->widget) );
271                         /* TODO: Cancel all input requests */
272                         break;
273
274                 case wintype_Blank:
275                         gtk_widget_destroy(win->widget);
276                         break;
277         }
278
279         stream_close_common(win->window_stream, result);
280
281         g_node_destroy(win->window_node);
282         /* TODO: iterate over child windows, closing them */
283
284         g_free(win);
285 }
286
287 /**
288  * glk_window_clear:
289  * @win: A window.
290  *
291  * Erases the window @win.
292  */
293 void
294 glk_window_clear(winid_t win)
295 {
296         g_return_if_fail(win != NULL);
297         
298         switch(win->window_type)
299         {
300                 case wintype_Blank:
301                         /* do nothing */
302                         break;
303                         
304                 case wintype_TextBuffer:
305                         /* delete all text in the window */
306                 {
307                         gdk_threads_enter();
308
309                         GtkTextBuffer *buffer = 
310                                 gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
311                         GtkTextIter start, end;
312                         gtk_text_buffer_get_bounds(buffer, &start, &end);
313                         gtk_text_buffer_delete(buffer, &start, &end);
314
315                         gdk_threads_leave();
316                 }
317                         break;
318                         
319                 default:
320                         g_warning("glk_window_clear: unsupported window type");
321         }
322 }
323
324 /**
325  * glk_set_window:
326  * @win: A window.
327  *
328  * Sets the current stream to @win's window stream.
329  */
330 void
331 glk_set_window(winid_t win)
332 {
333         glk_stream_set_current( glk_window_get_stream(win) );
334 }
335
336 /**
337  * glk_window_get_stream:
338  * @win: A window.
339  *
340  * Gets the stream associated with @win.
341  *
342  * Returns: The window stream.
343  */
344 strid_t glk_window_get_stream(winid_t win)
345 {
346         g_return_val_if_fail(win != NULL, NULL);
347         return win->window_stream;
348 }
349
350 /**
351  * glk_window_set_echo_stream:
352  * @win: A window.
353  * @str: A stream to attach to the window, or #NULL.
354  *
355  * Attaches the stream @str to @win as a second stream. Any text printed to the
356  * window is also echoed to this second stream, which is called the window's
357  * "echo stream."
358  *
359  * Effectively, any call to glk_put_char() (or the other output commands) which
360  * is directed to @win's window stream, is replicated to @win's echo stream.
361  * This also goes for the style commands such as glk_set_style().
362  *
363  * Note that the echoing is one-way. You can still print text directly to the
364  * echo stream, and it will go wherever the stream is bound, but it does not
365  * back up and appear in the window.
366  *
367  * It is illegal to set a window's echo stream to be its own window stream,
368  * which would create an infinite loop. It is similarly illegal to create a
369  * longer loop (two or more windows echoing to each other.)
370  *
371  * You can reset a window to stop echoing by setting @str to #NULL.
372  */
373 void
374 glk_window_set_echo_stream(winid_t win, strid_t str)
375 {
376         g_return_if_fail(win != NULL);
377         
378         /* Test for an infinite loop */
379         strid_t next_str;
380         for(next_str = str;
381                 next_str != NULL && next_str->stream_type == STREAM_TYPE_WINDOW;
382                 next_str = next_str->window->echo_stream)
383         {
384                 if(next_str == win->window_stream)
385                 {
386                         g_warning("glk_window_set_echo_stream: Infinite loop detected");
387                         win->echo_stream = NULL;
388                         return;
389                 }
390         }
391         
392         win->echo_stream = str; 
393 }
394
395 /**
396  * glk_window_get_echo_stream:
397  * @win: A window.
398  *
399  * Returns the echo stream of window @win. If the window has no echo stream (as
400  * is initially the case) then this returns #NULL.
401  *
402  * Returns: A stream, or #NULL.
403  */
404 strid_t
405 glk_window_get_echo_stream(winid_t win)
406 {
407         g_return_val_if_fail(win != NULL, NULL);
408         return win->echo_stream;
409 }
410
411 void
412 glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr)
413 {
414         g_return_if_fail(win != NULL);
415
416         if(widthptr != NULL) {
417                 *widthptr = 0;
418         }
419
420         if(heightptr != NULL) {
421                 *heightptr = 0;
422         }
423 }
424
425 void
426 glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos)
427 {
428         g_return_if_fail(win != NULL);
429 }