44334b40f660c9e6c9316508cace9541985438ba
[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 flush_text_buffer(winid_t win);
10 static int flush_text_grid(winid_t win);
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 = flush_text_grid(win);
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 = flush_text_buffer(win);
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         event_throw(CHIMARA_GLK(gtk_widget_get_ancestor(widget, CHIMARA_TYPE_GLK)), evtype_CharInput, win, keycode, 0);
450         
451         /* Only one keypress will be handled */
452         win->input_request_type = INPUT_REQUEST_NONE;
453         g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
454
455         return TRUE;
456 }
457
458 /* Internal function: finish handling a line input request, for both text grid and text buffer windows. */
459 static int
460 write_to_window_buffer(winid_t win, const gchar *inserted_text)
461 {
462         int copycount = 0;
463
464     /* Convert the string from UTF-8 to Latin-1 or Unicode */
465     if(win->input_request_type == INPUT_REQUEST_LINE) 
466     {
467         gsize bytes_written;
468         gchar *latin1 = convert_utf8_to_latin1(inserted_text, &bytes_written);
469         
470         if(latin1 == NULL)
471             return 0;
472
473         /* Place input in the echo stream */
474         if(win->echo_stream != NULL) 
475             glk_put_string_stream(win->echo_stream, latin1);
476
477         /* Copy the string (bytes_written does not include the NULL at the end) */
478         copycount = MIN(win->line_input_buffer_max_len, bytes_written);
479         memcpy(win->line_input_buffer, latin1, copycount);
480         g_free(latin1);
481     }
482     else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE) 
483     {
484         glong items_written;
485         gunichar *unicode = convert_utf8_to_ucs4(inserted_text, &items_written);
486         
487         if(unicode == NULL)
488             return 0;
489
490         /* Place input in the echo stream */
491         if(win->echo_stream != NULL) 
492             glk_put_string_stream_uni(win->echo_stream, unicode);
493
494         /* Copy the string (but not the NULL at the end) */
495         copycount = MIN(win->line_input_buffer_max_len, items_written);
496         memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
497         g_free(unicode);
498     }
499     else 
500         WARNING("Wrong input request type");
501
502     win->input_request_type = INPUT_REQUEST_NONE;
503         return copycount;
504 }
505
506 /* Internal function: Retrieves the input of a TextBuffer window and stores it in the window buffer.
507  * Returns the number of characters written, suitable for inclusion in a line input event. */
508 static int
509 flush_text_buffer(winid_t win)
510 {
511         VALID_WINDOW(win, return 0);
512         g_return_val_if_fail(win->type == wintype_TextBuffer, 0);
513
514         GtkTextIter start_iter, end_iter, last_character;
515
516         GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
517         GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
518         gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position);
519         gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
520         gtk_text_buffer_get_end_iter(window_buffer, &last_character);
521         gtk_text_iter_backward_cursor_position(&last_character);
522
523         gchar* last_char = gtk_text_buffer_get_text(window_buffer, &last_character, &end_iter, FALSE);
524
525         if( strchr(last_char, '\n') != NULL )
526                 gtk_text_iter_backward_cursor_position(&end_iter);
527
528         gchar* inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
529
530         int chars_written = write_to_window_buffer(win, inserted_text);
531         g_free(inserted_text);
532
533         return chars_written;
534 }
535
536 /* Internal function: Retrieves the input of a TextGrid window and stores it in the window buffer.
537  * Returns the number of characters written, suitable for inclusion in a line input event. */
538 static int
539 flush_text_grid(winid_t win)
540 {
541         VALID_WINDOW(win, return 0);
542         g_return_val_if_fail(win->type == wintype_TextGrid, 0);
543
544         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
545         
546         gchar *text = g_strdup( gtk_entry_get_text(GTK_ENTRY(win->input_entry)) );
547         /* Move the focus back into the text view */
548         gtk_widget_grab_focus(win->widget);
549         /* Remove entry widget from text view */
550         /* Should be ok even though this is the widget's own signal handler */
551         gtk_container_remove( GTK_CONTAINER(win->widget), GTK_WIDGET(win->input_entry) );
552         win->input_entry = NULL;
553         /* Delete the child anchor */
554         GtkTextIter start, end;
555         gtk_text_buffer_get_iter_at_child_anchor(buffer, &start, win->input_anchor);
556         end = start;
557         gtk_text_iter_forward_char(&end); /* Point after the child anchor */
558         gtk_text_buffer_delete(buffer, &start, &end);
559         win->input_anchor = NULL;
560         
561     gchar *spaces = g_strnfill(win->input_length - g_utf8_strlen(text, -1), ' ');
562     gchar *text_to_insert = g_strconcat(text, spaces, NULL);
563         g_free(spaces);
564     gtk_text_buffer_insert(buffer, &start, text_to_insert, -1);
565     g_free(text_to_insert);
566     
567     int chars_written = write_to_window_buffer(win, text);
568         g_free(text);
569
570         return chars_written;
571 }
572
573 /* Internal function: Callback for signal insert-text on a text buffer window.
574 Runs after the default handler has already inserted the text.
575 FIXME: This function assumes that newline was the last character typed into the
576 window. That assumption is wrong if, for example, text containing a newline was
577 pasted into the window. */
578 void
579 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win) 
580 {
581         if( strchr(text, '\n') != NULL ) 
582         {
583                 /* Remove signal handlers */
584                 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
585                 g_signal_handler_block(window_buffer, win->insert_text_handler);
586                 
587                 /* Make the window uneditable again and retrieve the text that was input */
588         gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
589
590         int chars_written = flush_text_buffer(win);
591                 event_throw(CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK)), evtype_LineInput, win, chars_written, 0);
592         }
593 }
594
595 /* Internal function: Callback for signal activate on the line input GtkEntry
596 in a text grid window. */
597 void
598 on_input_entry_activate(GtkEntry *input_entry, winid_t win)
599 {
600         g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
601
602         int chars_written = flush_text_grid(win);
603         event_throw(CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK)), evtype_LineInput, win, chars_written, 0);
604 }
605