Added a skeleton implementation of the ChimaraIF widget, which is a subclass of Chima...
[rodin/chimara.git] / libchimara / input.c
1 #include "charset.h"
2 #include "magic.h"
3 #include "input.h"
4 #include "chimara-glk-private.h"
5
6 extern GPrivate *glk_data_key;
7
8 /* Forward declarations */
9 static int finish_text_buffer_line_input(winid_t win, gboolean emit_signal);
10 static int finish_text_grid_line_input(winid_t win, gboolean emit_signal);
11
12 /* Internal function: code common to both flavors of char event request */
13 void
14 request_char_event_common(winid_t win, gboolean unicode)
15 {
16         VALID_WINDOW(win, return);
17         g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
18         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
19
20         win->input_request_type = unicode? INPUT_REQUEST_CHARACTER_UNICODE : INPUT_REQUEST_CHARACTER;
21         g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
22
23         gdk_threads_enter();
24         
25         /* If the request is in a text buffer window, scroll to the end of the
26          text buffer. TODO: This may scroll text off the top of the window that the
27          user hasn't read yet. We need to implement a paging mechanism. */
28         if(win->type == wintype_TextBuffer)
29         {
30                 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
31                 GtkTextIter iter;
32                 gtk_text_buffer_get_end_iter(buffer, &iter);
33                 gtk_text_buffer_place_cursor(buffer, &iter);
34                 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), gtk_text_buffer_get_insert(buffer));
35                 /* Why doesn't this always work?? */
36         }
37         
38         gtk_widget_grab_focus( GTK_WIDGET(win->widget) );
39         gdk_threads_leave();
40 }
41
42 /** 
43  * glk_request_char_event:
44  * @win: A window to request char events from.
45  *
46  * Request input of a Latin-1 character or special key. A window cannot have 
47  * requests for both character and line input at the same time. Nor can it have
48  * requests for character input of both types (Latin-1 and Unicode). It is
49  * illegal to call glk_request_char_event() if the window already has a pending
50  * request for either character or line input. 
51  */
52 void
53 glk_request_char_event(winid_t win)
54 {
55         request_char_event_common(win, FALSE);
56 }
57
58 /** 
59  * glk_request_char_event_uni:
60  * @win: A window to request char events from.
61  *
62  * Request input of a Unicode character or special key. See 
63  * glk_request_char_event().
64  */
65 void
66 glk_request_char_event_uni(winid_t win)
67 {
68         request_char_event_common(win, TRUE);
69 }
70
71 /**
72  * glk_cancel_char_event:
73  * @win: A window to cancel the latest char event request on.
74  *
75  * This cancels a pending request for character input. (Either Latin-1 or
76  * Unicode.) For convenience, it is legal to call glk_cancel_char_event() even
77  * if there is no charcter input request on that window. Glk will ignore the
78  * call in this case.
79  */
80 void
81 glk_cancel_char_event(winid_t win)
82 {
83         VALID_WINDOW(win, return);
84         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
85         
86         if(win->input_request_type == INPUT_REQUEST_CHARACTER || win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE)
87         {
88                 win->input_request_type = INPUT_REQUEST_NONE;
89                 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
90         }
91 }
92
93 /* Internal function: Request either latin-1 or unicode line input, in a text grid window. */
94 static void
95 text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
96 {
97         gdk_threads_enter();
98         
99         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
100
101     GtkTextMark *cursor = gtk_text_buffer_get_mark(buffer, "cursor_position");
102     GtkTextIter start_iter, end_iter;
103     gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, cursor);
104     
105     /* Determine the maximum length of the line input */
106     gint cursorpos = gtk_text_iter_get_line_offset(&start_iter);
107     /* Odd; the Glk spec says the maximum input length is
108     windowwidth - 1 - cursorposition. I say no, because if cursorposition is
109     zero, then the input should fill the whole line. FIXME??? */
110     win->input_length = MIN(win->width - cursorpos, win->line_input_buffer_max_len);
111     end_iter = start_iter;
112     gtk_text_iter_set_line_offset(&end_iter, cursorpos + win->input_length);
113     
114         /* If the buffer currently has a selection with one bound in the middle of
115         the input field, then deselect it. Otherwise the input field gets trashed */
116         GtkTextIter start_sel, end_sel;
117         if( gtk_text_buffer_get_selection_bounds(buffer, &start_sel, &end_sel) )
118         {
119                 if( gtk_text_iter_in_range(&start_sel, &start_iter, &end_iter) )
120                         gtk_text_buffer_place_cursor(buffer, &end_sel);
121                 if( gtk_text_iter_in_range(&end_sel, &start_iter, &end_iter) )
122                         gtk_text_buffer_place_cursor(buffer, &start_sel);
123         }
124         
125     /* Erase the text currently in the input field and replace it with a GtkEntry */
126     gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
127     win->input_anchor = gtk_text_buffer_create_child_anchor(buffer, &start_iter);
128     win->input_entry = gtk_entry_new();
129         /* Set the entry's font to match that of the window */
130     GtkRcStyle *style = gtk_widget_get_modifier_style(win->widget);     /* Don't free */
131         gtk_widget_modify_font(win->input_entry, style->font_desc);
132         /* Make the entry as small as possible to fit with the text */
133         gtk_entry_set_has_frame(GTK_ENTRY(win->input_entry), FALSE);
134         GtkBorder border = { 0, 0, 0, 0 };
135         gtk_entry_set_inner_border(GTK_ENTRY(win->input_entry), &border);
136     gtk_entry_set_max_length(GTK_ENTRY(win->input_entry), win->input_length);
137     gtk_entry_set_width_chars(GTK_ENTRY(win->input_entry), win->input_length);
138     
139     /* Insert pre-entered text if needed */
140     if(insert)
141         gtk_entry_set_text(GTK_ENTRY(win->input_entry), inserttext);
142     
143     /* Set background color of entry (TODO: implement as property) */
144     GdkColor background;
145         gdk_color_parse("grey", &background);
146     gtk_widget_modify_base(win->input_entry, GTK_STATE_NORMAL, &background);
147     
148     g_signal_connect(win->input_entry, "activate", G_CALLBACK(on_input_entry_activate), win);
149     
150     gtk_widget_show(win->input_entry);
151     gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(win->widget), win->input_entry, win->input_anchor);
152         
153         g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
154
155         gtk_widget_grab_focus(win->input_entry);
156         
157         gdk_threads_leave();
158 }
159     
160 /* Internal function: Request either latin-1 or unicode line input, in a text buffer window. */
161 static void
162 text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
163 {
164         gdk_threads_enter();
165         
166         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
167
168     /* Move the input_position mark to the end of the window_buffer */
169     GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
170     GtkTextIter end_iter;
171     gtk_text_buffer_get_end_iter(buffer, &end_iter);
172     gtk_text_buffer_move_mark(buffer, input_position, &end_iter);
173
174     /* Set the entire contents of the window_buffer as uneditable
175      * (so input can only be entered at the end) */
176     GtkTextIter start_iter;
177     gtk_text_buffer_get_start_iter(buffer, &start_iter);
178     gtk_text_buffer_remove_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
179     gtk_text_buffer_apply_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
180     
181     /* Insert pre-entered text if needed */
182     if(insert)
183         gtk_text_buffer_insert(buffer, &end_iter, inserttext, -1);
184     
185     /* Scroll to input point */
186     gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position);
187     
188     gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE);
189     g_signal_handler_unblock(buffer, win->insert_text_handler);
190
191         gtk_widget_grab_focus(win->widget);
192         
193         gdk_threads_leave();
194 }
195
196 /**
197  * glk_request_line_event:
198  * @win: A text buffer or text grid window to request line input on.
199  * @buf: A buffer of at least @maxlen bytes.
200  * @maxlen: Length of the buffer.
201  * @initlen: The number of characters in @buf to pre-enter.
202  *
203  * Requests input of a line of Latin-1 characters. A window cannot have requests
204  * for both character and line input at the same time. Nor can it have requests
205  * for line input of both types (Latin-1 and Unicode). It is illegal to call
206  * glk_request_line_event() if the window already has a pending request for
207  * either character or line input.
208  * 
209  * The @buf argument is a pointer to space where the line input will be stored.
210  * (This may not be %NULL.) @maxlen is the length of this space, in bytes; the
211  * library will not accept more characters than this. If @initlen is nonzero,
212  * then the first @initlen bytes of @buf will be entered as pre-existing input
213  * — just as if the player had typed them himself. (The player can continue
214  * composing after this pre-entered input, or delete it or edit as usual.)
215  * 
216  * The contents of the buffer are undefined until the input is completed (either
217  * by a line input event, or glk_cancel_line_event(). The library may or may not
218  * fill in the buffer as the player composes, while the input is still pending;
219  * it is illegal to change the contents of the buffer yourself. 
220  */
221 void
222 glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen)
223 {
224         VALID_WINDOW(win, return);
225         g_return_if_fail(buf);
226         g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
227         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
228         g_return_if_fail(initlen <= maxlen);
229
230         /* Register the buffer */
231         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
232         if(glk_data->register_arr)
233         win->buffer_rock = (*glk_data->register_arr)(buf, maxlen, "&+#!Cn");
234         
235         win->input_request_type = INPUT_REQUEST_LINE;
236         win->line_input_buffer = buf;
237         win->line_input_buffer_max_len = maxlen;
238
239         gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup("");
240         switch(win->type)
241         {
242             case wintype_TextBuffer:
243                 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
244                 break;
245             case wintype_TextGrid:
246                 text_grid_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
247                 break;
248     }
249         g_free(inserttext);
250 }
251
252 /**
253  * glk_request_line_event_uni:
254  * @win: A text buffer or text grid window to request line input on.
255  * @buf: A buffer of at least @maxlen characters.
256  * @maxlen: Length of the buffer.
257  * @initlen: The number of characters in @buf to pre-enter.
258  *
259  * Request input of a line of Unicode characters. This works the same as
260  * glk_request_line_event(), except the result is stored in an array of
261  * <type>glui32</type> values instead of an array of characters, and the values
262  * may be any valid Unicode code points.
263  * 
264  * The result will be in Unicode Normalization Form C. This basically means that
265  * composite characters will be single characters where possible, instead of
266  * sequences of base and combining marks. See 
267  * <ulink url="http://www.unicode.org/reports/tr15/">Unicode Standard Annex #15
268  * </ulink> for the details.
269  */
270 void
271 glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
272 {
273         VALID_WINDOW(win, return);
274         g_return_if_fail(buf);
275         g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
276         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
277         g_return_if_fail(initlen <= maxlen);
278
279         /* Register the buffer */
280         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
281         if(glk_data->register_arr)
282         win->buffer_rock = (*glk_data->register_arr)(buf, maxlen, "&+#!Iu");
283
284         win->input_request_type = INPUT_REQUEST_LINE_UNICODE;
285         win->line_input_buffer_unicode = buf;
286         win->line_input_buffer_max_len = maxlen;
287
288         gchar *utf8;
289         if(initlen > 0) {
290                 utf8 = convert_ucs4_to_utf8(buf, initlen);
291                 if(utf8 == NULL)
292                         return;
293         }
294         else
295                 utf8 = g_strdup("");
296
297     switch(win->type)
298         {
299             case wintype_TextBuffer:
300                 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), utf8);
301                 break;
302             case wintype_TextGrid:
303                 text_grid_request_line_event_common(win, maxlen, (initlen > 0), utf8);
304                 break;
305     }           
306         g_free(utf8);
307 }
308
309 /**
310  * glk_cancel_line_event:
311  * @win: A text buffer or text grid window to cancel line input on.
312  * @event: Will be filled in if the user had already input something.
313  *
314  * This cancels a pending request for line input. (Either Latin-1 or Unicode.)
315  *
316  * The event pointed to by the event argument will be filled in as if the
317  * player had hit <keycap>enter</keycap>, and the input composed so far will be stored in the
318  * buffer; see below. If you do not care about this information, pass %NULL as
319  * the @event argument. (The buffer will still be filled.) 
320  *
321  * For convenience, it is legal to call glk_cancel_line_event() even if there
322  * is no line input request on that window. The event type will be set to
323  * %evtype_None in this case.
324  */
325 void
326 glk_cancel_line_event(winid_t win, event_t *event)
327 {
328         VALID_WINDOW(win, return);
329         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
330
331         if(event != NULL) {
332                 event->type = evtype_None;
333                 event->win = win;
334                 event->val1 = 0;
335                 event->val2 = 0;
336         }
337
338         if(win->input_request_type != INPUT_REQUEST_LINE && win->input_request_type != INPUT_REQUEST_LINE_UNICODE)
339                 return;
340
341         g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
342
343         int chars_written = 0;
344
345         if(win->type == wintype_TextGrid) {
346                 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
347                 chars_written = finish_text_grid_line_input(win, FALSE);
348         } else if(win->type == wintype_TextBuffer) {
349                 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
350                 g_signal_handler_block(window_buffer, win->insert_text_handler);
351                 chars_written = finish_text_buffer_line_input(win, FALSE);
352         }
353
354         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
355         if(glk_data->unregister_arr) 
356         {
357                 if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
358                         (*glk_data->unregister_arr)(win->line_input_buffer_unicode, win->line_input_buffer_max_len, "&+#!Iu", win->buffer_rock);
359                 else
360                 (*glk_data->unregister_arr)(win->line_input_buffer, win->line_input_buffer_max_len, "&+#!Cn", win->buffer_rock);
361     }
362         
363         if(event != NULL && chars_written > 0) {
364                 event->type = evtype_LineInput;
365                 event->val1 = chars_written;
366         }
367 }
368
369 /* Internal function: General callback for signal key-press-event on a text buffer or text grid window. Used in character input on both text buffers and grids, and also in line input on grids, to redirect keystrokes to the line input field. Blocked when not in use. */
370 gboolean
371 on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
372 {
373         /* If this is a text grid window, and line input is active, then redirect the key press to the line input GtkEntry */
374         if( win->type == wintype_TextGrid && (win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE) )
375         {
376                 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
377                     || event->keyval == GDK_Down || event->keyval == GDK_KP_Down
378                     || event->keyval == GDK_Left || event->keyval == GDK_KP_Left
379                     || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
380                     || event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab
381                     || event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up
382                     || event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down
383                     || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
384                     || event->keyval == GDK_End || event->keyval == GDK_KP_End)
385                         return FALSE; /* Don't redirect these keys */
386                 gtk_widget_grab_focus(win->input_entry);
387                 gtk_editable_set_position(GTK_EDITABLE(win->input_entry), -1);
388                 gboolean retval = TRUE;
389                 g_signal_emit_by_name(win->input_entry, "key-press-event", event, &retval);
390                 return retval; /* Block this key event if the entry handled it */
391         }
392         if(win->input_request_type != INPUT_REQUEST_CHARACTER && 
393                 win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE)
394                 return FALSE;
395
396         int keycode;
397
398         switch(event->keyval) {
399                 case GDK_Up:
400                 case GDK_KP_Up: keycode = keycode_Up; break;
401                 case GDK_Down: 
402                 case GDK_KP_Down: keycode = keycode_Down; break;
403                 case GDK_Left:
404                 case GDK_KP_Left: keycode = keycode_Left; break;
405                 case GDK_Right:
406                 case GDK_KP_Right: keycode = keycode_Right; break;
407                 case GDK_Linefeed:
408                 case GDK_Return:
409                 case GDK_KP_Enter: keycode = keycode_Return; break;
410                 case GDK_Delete:
411                 case GDK_BackSpace:
412                 case GDK_KP_Delete: keycode = keycode_Delete; break;
413                 case GDK_Escape: keycode = keycode_Escape; break;
414                 case GDK_Tab: 
415                 case GDK_KP_Tab: keycode = keycode_Tab; break;
416                 case GDK_Page_Up:
417                 case GDK_KP_Page_Up: keycode = keycode_PageUp; break;
418                 case GDK_Page_Down:
419                 case GDK_KP_Page_Down: keycode = keycode_PageDown; break;
420                 case GDK_Home:
421                 case GDK_KP_Home: keycode = keycode_Home; break;
422                 case GDK_End:
423                 case GDK_KP_End: keycode = keycode_End; break;
424                 case GDK_F1: 
425                 case GDK_KP_F1: keycode = keycode_Func1; break;
426                 case GDK_F2: 
427                 case GDK_KP_F2: keycode = keycode_Func2; break;
428                 case GDK_F3: 
429                 case GDK_KP_F3: keycode = keycode_Func3; break;
430                 case GDK_F4: 
431                 case GDK_KP_F4: keycode = keycode_Func4; break;
432                 case GDK_F5: keycode = keycode_Func5; break;
433                 case GDK_F6: keycode = keycode_Func6; break;
434                 case GDK_F7: keycode = keycode_Func7; break;
435                 case GDK_F8: keycode = keycode_Func8; break;
436                 case GDK_F9: keycode = keycode_Func9; break;
437                 case GDK_F10: keycode = keycode_Func10; break;
438                 case GDK_F11: keycode = keycode_Func11; break;
439                 case GDK_F12: keycode = keycode_Func12; break;
440                 default:
441                         keycode = gdk_keyval_to_unicode(event->keyval);
442                         /* If keycode is 0, then keyval was not recognized; also return
443                         unknown if Latin-1 input was requested and the character is not in
444                         Latin-1 */
445                         if(keycode == 0 || (win->input_request_type == INPUT_REQUEST_CHARACTER && keycode > 255))
446                                 keycode = keycode_Unknown;      
447         }
448
449         ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(widget, CHIMARA_TYPE_GLK));
450         g_assert(glk);
451         event_throw(glk, evtype_CharInput, win, keycode, 0);
452         g_signal_emit_by_name(glk, "char-input", win->rock, event->keyval);
453
454         /* Only one keypress will be handled */
455         win->input_request_type = INPUT_REQUEST_NONE;
456         g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
457
458         return TRUE;
459 }
460
461 /* Internal function: finish handling a line input request, for both text grid and text buffer windows. */
462 static int
463 write_to_window_buffer(winid_t win, const gchar *inserted_text)
464 {
465         int copycount = 0;
466
467     /* Convert the string from UTF-8 to Latin-1 or Unicode */
468     if(win->input_request_type == INPUT_REQUEST_LINE) 
469     {
470         gsize bytes_written;
471         gchar *latin1 = convert_utf8_to_latin1(inserted_text, &bytes_written);
472         
473         if(latin1 == NULL)
474             return 0;
475
476         /* Place input in the echo stream */
477         if(win->echo_stream != NULL) 
478             glk_put_string_stream(win->echo_stream, latin1);
479
480         /* Copy the string (bytes_written does not include the NULL at the end) */
481         copycount = MIN(win->line_input_buffer_max_len, bytes_written);
482         memcpy(win->line_input_buffer, latin1, copycount);
483         g_free(latin1);
484     }
485     else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE) 
486     {
487         glong items_written;
488         gunichar *unicode = convert_utf8_to_ucs4(inserted_text, &items_written);
489         
490         if(unicode == NULL)
491             return 0;
492
493         /* Place input in the echo stream */
494         if(win->echo_stream != NULL) 
495             glk_put_string_stream_uni(win->echo_stream, unicode);
496
497         /* Copy the string (but not the NULL at the end) */
498         copycount = MIN(win->line_input_buffer_max_len, items_written);
499         memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
500         g_free(unicode);
501     }
502     else 
503         WARNING("Wrong input request type");
504
505     win->input_request_type = INPUT_REQUEST_NONE;
506         return copycount;
507 }
508
509 /* Internal function: Retrieves the input of a TextBuffer window and stores it in the window buffer.
510  * Returns the number of characters written, suitable for inclusion in a line input event. */
511 static int
512 finish_text_buffer_line_input(winid_t win, gboolean emit_signal)
513 {
514         VALID_WINDOW(win, return 0);
515         g_return_val_if_fail(win->type == wintype_TextBuffer, 0);
516
517         GtkTextIter start_iter, end_iter, last_character;
518
519         GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
520         GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
521         gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position);
522         gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
523         gtk_text_buffer_get_end_iter(window_buffer, &last_character);
524         gtk_text_iter_backward_cursor_position(&last_character);
525
526         gchar* last_char = gtk_text_buffer_get_text(window_buffer, &last_character, &end_iter, FALSE);
527
528         if( strchr(last_char, '\n') != NULL )
529                 gtk_text_iter_backward_cursor_position(&end_iter);
530
531         gchar* inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
532
533         int chars_written = write_to_window_buffer(win, inserted_text);
534         if(emit_signal)
535         {
536                 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
537                 g_assert(glk);
538                 g_signal_emit_by_name(glk, "line-input", win->rock, inserted_text);
539         }
540         g_free(inserted_text);
541
542         return chars_written;
543 }
544
545 /* Internal function: Retrieves the input of a TextGrid window and stores it in the window buffer.
546  * Returns the number of characters written, suitable for inclusion in a line input event. */
547 static int
548 finish_text_grid_line_input(winid_t win, gboolean emit_signal)
549 {
550         VALID_WINDOW(win, return 0);
551         g_return_val_if_fail(win->type == wintype_TextGrid, 0);
552
553         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
554         
555         gchar *text = g_strdup( gtk_entry_get_text(GTK_ENTRY(win->input_entry)) );
556         /* Move the focus back into the text view */
557         gtk_widget_grab_focus(win->widget);
558         /* Remove entry widget from text view */
559         /* Should be ok even though this is the widget's own signal handler */
560         gtk_container_remove( GTK_CONTAINER(win->widget), GTK_WIDGET(win->input_entry) );
561         win->input_entry = NULL;
562         /* Delete the child anchor */
563         GtkTextIter start, end;
564         gtk_text_buffer_get_iter_at_child_anchor(buffer, &start, win->input_anchor);
565         end = start;
566         gtk_text_iter_forward_char(&end); /* Point after the child anchor */
567         gtk_text_buffer_delete(buffer, &start, &end);
568         win->input_anchor = NULL;
569         
570     gchar *spaces = g_strnfill(win->input_length - g_utf8_strlen(text, -1), ' ');
571     gchar *text_to_insert = g_strconcat(text, spaces, NULL);
572         g_free(spaces);
573     gtk_text_buffer_insert(buffer, &start, text_to_insert, -1);
574     g_free(text_to_insert);
575     
576     int chars_written = write_to_window_buffer(win, text);
577     if(emit_signal)
578     {
579                 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
580                 g_assert(glk);
581                 g_signal_emit_by_name(glk, "line-input", win->rock, text);
582     }
583         g_free(text);
584
585         return chars_written;
586 }
587
588 /* Internal function: Callback for signal insert-text on a text buffer window.
589 Runs after the default handler has already inserted the text.
590 FIXME: This function assumes that newline was the last character typed into the
591 window. That assumption is wrong if, for example, text containing a newline was
592 pasted into the window. */
593 void
594 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win) 
595 {
596         if( strchr(text, '\n') != NULL ) 
597         {
598                 /* Remove signal handlers */
599                 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
600                 g_signal_handler_block(window_buffer, win->insert_text_handler);
601                 
602                 /* Make the window uneditable again and retrieve the text that was input */
603         gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
604
605         int chars_written = finish_text_buffer_line_input(win, TRUE);
606         ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
607                 event_throw(glk, evtype_LineInput, win, chars_written, 0);
608         }
609 }
610
611 /* Internal function: Callback for signal activate on the line input GtkEntry
612 in a text grid window. */
613 void
614 on_input_entry_activate(GtkEntry *input_entry, winid_t win)
615 {
616         g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
617
618         int chars_written = finish_text_grid_line_input(win, TRUE);
619         event_throw(CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK)), evtype_LineInput, win, chars_written, 0);
620 }
621