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