4 #include "chimara-glk-private.h"
6 extern GPrivate *glk_data_key;
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);
12 /* Internal function: code common to both flavors of char event request */
14 request_char_event_common(winid_t win, gboolean unicode)
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);
20 flush_window_buffer(win);
22 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
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 );
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)
34 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
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?? */
42 gtk_widget_grab_focus( GTK_WIDGET(win->widget) );
45 /* Emit the "waiting" signal to let listeners know we are ready for input */
46 g_signal_emit_by_name(glk_data->self, "waiting");
50 * glk_request_char_event:
51 * @win: A window to request char events from.
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.
60 glk_request_char_event(winid_t win)
62 request_char_event_common(win, FALSE);
66 * glk_request_char_event_uni:
67 * @win: A window to request char events from.
69 * Request input of a Unicode character or special key. See
70 * glk_request_char_event().
73 glk_request_char_event_uni(winid_t win)
75 request_char_event_common(win, TRUE);
79 * glk_cancel_char_event:
80 * @win: A window to cancel the latest char event request on.
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
88 glk_cancel_char_event(winid_t win)
90 VALID_WINDOW(win, return);
91 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
93 if(win->input_request_type == INPUT_REQUEST_CHARACTER || win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE)
95 win->input_request_type = INPUT_REQUEST_NONE;
96 g_signal_handler_block( G_OBJECT(win->widget), win->char_input_keypress_handler );
100 /* Internal function: Request either latin-1 or unicode line input, in a text grid window. */
102 text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
106 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
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);
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);
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) )
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);
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);
146 /* Insert pre-entered text if needed */
148 gtk_entry_set_text(GTK_ENTRY(win->input_entry), inserttext);
150 /* Set background color of entry (TODO: implement as property) */
152 gdk_color_parse("grey", &background);
153 gtk_widget_modify_base(win->input_entry, GTK_STATE_NORMAL, &background);
155 g_signal_connect(win->input_entry, "activate", G_CALLBACK(on_input_entry_activate), win);
156 g_signal_connect(win->input_entry, "key-press-event", G_CALLBACK(on_input_entry_key_press_event), win);
157 win->line_input_entry_changed = g_signal_connect(win->input_entry, "changed", G_CALLBACK(on_input_entry_changed), win);
159 gtk_widget_show(win->input_entry);
160 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(win->widget), win->input_entry, win->input_anchor);
162 g_signal_handler_unblock( G_OBJECT(win->widget), win->char_input_keypress_handler );
164 gtk_widget_grab_focus(win->input_entry);
169 /* Internal function: Request either latin-1 or unicode line input, in a text buffer window. */
171 text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
173 flush_window_buffer(win);
177 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
179 /* Move the input_position mark to the end of the window_buffer */
180 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
181 GtkTextIter end_iter;
182 gtk_text_buffer_get_end_iter(buffer, &end_iter);
183 gtk_text_buffer_move_mark(buffer, input_position, &end_iter);
185 /* Set the entire contents of the window_buffer as uneditable
186 * (so input can only be entered at the end) */
187 GtkTextIter start_iter;
188 gtk_text_buffer_get_start_iter(buffer, &start_iter);
189 gtk_text_buffer_remove_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
190 gtk_text_buffer_apply_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
192 /* Insert pre-entered text if needed */
194 gtk_text_buffer_insert(buffer, &end_iter, inserttext, -1);
196 /* Scroll to input point */
197 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position);
199 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE);
200 g_signal_handler_unblock(buffer, win->insert_text_handler);
202 gtk_widget_grab_focus(win->widget);
208 * glk_request_line_event:
209 * @win: A text buffer or text grid window to request line input on.
210 * @buf: A buffer of at least @maxlen bytes.
211 * @maxlen: Length of the buffer.
212 * @initlen: The number of characters in @buf to pre-enter.
214 * Requests input of a line of Latin-1 characters. A window cannot have requests
215 * for both character and line input at the same time. Nor can it have requests
216 * for line input of both types (Latin-1 and Unicode). It is illegal to call
217 * glk_request_line_event() if the window already has a pending request for
218 * either character or line input.
220 * The @buf argument is a pointer to space where the line input will be stored.
221 * (This may not be %NULL.) @maxlen is the length of this space, in bytes; the
222 * library will not accept more characters than this. If @initlen is nonzero,
223 * then the first @initlen bytes of @buf will be entered as pre-existing input
224 * — just as if the player had typed them himself. (The player can continue
225 * composing after this pre-entered input, or delete it or edit as usual.)
227 * The contents of the buffer are undefined until the input is completed (either
228 * by a line input event, or glk_cancel_line_event(). The library may or may not
229 * fill in the buffer as the player composes, while the input is still pending;
230 * it is illegal to change the contents of the buffer yourself.
233 glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen)
235 VALID_WINDOW(win, return);
236 g_return_if_fail(buf);
237 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
238 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
239 g_return_if_fail(initlen <= maxlen);
241 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
243 /* Register the buffer */
244 if(glk_data->register_arr)
245 win->buffer_rock = (*glk_data->register_arr)(buf, maxlen, "&+#!Cn");
247 win->input_request_type = INPUT_REQUEST_LINE;
248 win->line_input_buffer = buf;
249 win->line_input_buffer_max_len = maxlen;
251 gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup("");
254 case wintype_TextBuffer:
255 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
257 case wintype_TextGrid:
258 text_grid_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
263 /* Emit the "waiting" signal to let listeners know we are ready for input */
264 g_signal_emit_by_name(glk_data->self, "waiting");
268 * glk_request_line_event_uni:
269 * @win: A text buffer or text grid window to request line input on.
270 * @buf: A buffer of at least @maxlen characters.
271 * @maxlen: Length of the buffer.
272 * @initlen: The number of characters in @buf to pre-enter.
274 * Request input of a line of Unicode characters. This works the same as
275 * glk_request_line_event(), except the result is stored in an array of
276 * <type>glui32</type> values instead of an array of characters, and the values
277 * may be any valid Unicode code points.
279 * The result will be in Unicode Normalization Form C. This basically means that
280 * composite characters will be single characters where possible, instead of
281 * sequences of base and combining marks. See
282 * <ulink url="http://www.unicode.org/reports/tr15/">Unicode Standard Annex
283 * #15</ulink> for the details.
286 glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
288 VALID_WINDOW(win, return);
289 g_return_if_fail(buf);
290 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
291 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
292 g_return_if_fail(initlen <= maxlen);
294 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
296 /* Register the buffer */
297 if(glk_data->register_arr)
298 win->buffer_rock = (*glk_data->register_arr)(buf, maxlen, "&+#!Iu");
300 win->input_request_type = INPUT_REQUEST_LINE_UNICODE;
301 win->line_input_buffer_unicode = buf;
302 win->line_input_buffer_max_len = maxlen;
306 utf8 = convert_ucs4_to_utf8(buf, initlen);
315 case wintype_TextBuffer:
316 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), utf8);
318 case wintype_TextGrid:
319 text_grid_request_line_event_common(win, maxlen, (initlen > 0), utf8);
324 /* Emit the "waiting" signal to let listeners know we are ready for input */
325 g_signal_emit_by_name(glk_data->self, "waiting");
329 * glk_cancel_line_event:
330 * @win: A text buffer or text grid window to cancel line input on.
331 * @event: Will be filled in if the user had already input something.
333 * This cancels a pending request for line input. (Either Latin-1 or Unicode.)
335 * The event pointed to by the event argument will be filled in as if the
336 * player had hit <keycap>enter</keycap>, and the input composed so far will be
337 * stored in the buffer; see below. If you do not care about this information,
338 * pass %NULL as the @event argument. (The buffer will still be filled.)
340 * For convenience, it is legal to call glk_cancel_line_event() even if there
341 * is no line input request on that window. The event type will be set to
342 * %evtype_None in this case.
345 glk_cancel_line_event(winid_t win, event_t *event)
347 VALID_WINDOW(win, return);
348 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
351 event->type = evtype_None;
357 if(win->input_request_type != INPUT_REQUEST_LINE && win->input_request_type != INPUT_REQUEST_LINE_UNICODE)
360 g_signal_handler_block( G_OBJECT(win->widget), win->char_input_keypress_handler );
362 int chars_written = 0;
364 if(win->type == wintype_TextGrid) {
365 g_signal_handler_block( G_OBJECT(win->widget), win->char_input_keypress_handler );
366 chars_written = finish_text_grid_line_input(win, FALSE);
367 } else if(win->type == wintype_TextBuffer) {
368 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
369 g_signal_handler_block(window_buffer, win->insert_text_handler);
370 chars_written = finish_text_buffer_line_input(win, FALSE);
373 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
374 if(glk_data->unregister_arr)
376 if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
377 (*glk_data->unregister_arr)(win->line_input_buffer_unicode, win->line_input_buffer_max_len, "&+#!Iu", win->buffer_rock);
379 (*glk_data->unregister_arr)(win->line_input_buffer, win->line_input_buffer_max_len, "&+#!Cn", win->buffer_rock);
382 if(event != NULL && chars_written > 0) {
383 event->type = evtype_LineInput;
384 event->val1 = chars_written;
388 /* 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. */
390 on_char_input_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
392 glui32 keycode = keyval_to_glk_keycode(event->keyval, win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE);
394 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(widget, CHIMARA_TYPE_GLK));
396 event_throw(glk, evtype_CharInput, win, keycode, 0);
397 g_signal_emit_by_name(glk, "char-input", win->rock, event->keyval);
399 /* Only one keypress will be handled */
400 win->input_request_type = INPUT_REQUEST_NONE;
401 g_signal_handler_block( G_OBJECT(win->widget), win->char_input_keypress_handler );
407 on_line_input_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
411 case wintype_TextBuffer:
412 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
413 || event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
415 /* Prevent falling off the end of the history list */
416 if( (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
417 && win->history_pos && win->history_pos->next == NULL)
419 if( (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
420 && (win->history_pos == NULL || win->history_pos->prev == NULL) )
423 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(win->widget));
424 GtkTextIter start, end;
425 /* Erase any text that was already typed */
426 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
427 gtk_text_buffer_get_iter_at_mark(buffer, &start, input_position);
428 gtk_text_buffer_get_end_iter(buffer, &end);
430 if(win->history_pos == NULL) {
431 gchar *current_input = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
432 win->history = g_list_prepend(win->history, current_input);
433 win->history_pos = win->history;
436 gtk_text_buffer_delete(buffer, &start, &end);
438 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
441 win->history_pos = g_list_next(win->history_pos);
443 win->history_pos = win->history;
446 win->history_pos = g_list_previous(win->history_pos);
448 /* Insert the history item into the window */
449 gtk_text_buffer_get_end_iter(buffer, &end);
451 g_signal_handler_block(buffer, win->insert_text_handler);
452 gtk_text_buffer_insert_with_tags_by_name(buffer, &end, win->history_pos->data, -1, "input", NULL);
453 g_signal_handler_unblock(buffer, win->insert_text_handler);
458 /* If this is a text grid window, then redirect the key press to the line input GtkEntry */
459 case wintype_TextGrid:
461 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
462 || event->keyval == GDK_Down || event->keyval == GDK_KP_Down
463 || event->keyval == GDK_Left || event->keyval == GDK_KP_Left
464 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
465 || event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab
466 || event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up
467 || event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down
468 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
469 || event->keyval == GDK_End || event->keyval == GDK_KP_End)
470 return FALSE; /* Don't redirect these keys */
471 gtk_widget_grab_focus(win->input_entry);
472 gtk_editable_set_position(GTK_EDITABLE(win->input_entry), -1);
473 gboolean retval = TRUE;
474 g_signal_emit_by_name(win->input_entry, "key-press-event", event, &retval);
475 return retval; /* Block this key event if the entry handled it */
481 /* Internal function: finish handling a line input request, for both text grid and text buffer windows. */
483 write_to_window_buffer(winid_t win, const gchar *inserted_text)
487 /* Convert the string from UTF-8 to Latin-1 or Unicode */
488 if(win->input_request_type == INPUT_REQUEST_LINE)
491 gchar *latin1 = convert_utf8_to_latin1(inserted_text, &bytes_written);
496 /* Place input in the echo stream */
497 if(win->echo_stream != NULL)
498 glk_put_string_stream(win->echo_stream, latin1);
500 /* Copy the string (bytes_written does not include the NULL at the end) */
501 copycount = MIN(win->line_input_buffer_max_len, bytes_written);
502 memcpy(win->line_input_buffer, latin1, copycount);
505 else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
508 gunichar *unicode = convert_utf8_to_ucs4(inserted_text, &items_written);
513 /* Place input in the echo stream */
514 if(win->echo_stream != NULL)
515 glk_put_string_stream_uni(win->echo_stream, unicode);
517 /* Copy the string (but not the NULL at the end) */
518 copycount = MIN(win->line_input_buffer_max_len, items_written);
519 memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
523 WARNING("Wrong input request type");
525 win->input_request_type = INPUT_REQUEST_NONE;
529 /* Internal function: Retrieves the input of a TextBuffer window and stores it in the window buffer.
530 * Returns the number of characters written, suitable for inclusion in a line input event. */
532 finish_text_buffer_line_input(winid_t win, gboolean emit_signal)
534 VALID_WINDOW(win, return 0);
535 g_return_val_if_fail(win->type == wintype_TextBuffer, 0);
537 GtkTextIter start_iter, end_iter, last_character;
539 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
540 GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
541 gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position);
542 gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
543 gtk_text_buffer_get_end_iter(window_buffer, &last_character);
544 gtk_text_iter_backward_cursor_position(&last_character);
546 gchar* last_char = gtk_text_buffer_get_text(window_buffer, &last_character, &end_iter, FALSE);
548 if( strchr(last_char, '\n') != NULL )
549 gtk_text_iter_backward_cursor_position(&end_iter);
551 gchar* inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
553 int chars_written = write_to_window_buffer(win, inserted_text);
556 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
558 g_signal_emit_by_name(glk, "line-input", win->rock, inserted_text);
561 /* Add the text to the window input history */
562 if(win->history_pos != NULL)
564 g_free(win->history->data);
565 win->history = g_list_delete_link(win->history, win->history);
567 if(*inserted_text != 0)
568 win->history = g_list_prepend(win->history, g_strdup(inserted_text));
570 win->history_pos = NULL;
572 g_free(inserted_text);
574 return chars_written;
577 /* Internal function: Retrieves the input of a TextGrid window and stores it in the window buffer.
578 * Returns the number of characters written, suitable for inclusion in a line input event. */
580 finish_text_grid_line_input(winid_t win, gboolean emit_signal)
582 VALID_WINDOW(win, return 0);
583 g_return_val_if_fail(win->type == wintype_TextGrid, 0);
585 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
587 gchar *text = g_strdup( gtk_entry_get_text(GTK_ENTRY(win->input_entry)) );
588 /* Move the focus back into the text view */
589 gtk_widget_grab_focus(win->widget);
590 /* Remove entry widget from text view */
591 /* Should be ok even though this is the widget's own signal handler */
592 gtk_container_remove( GTK_CONTAINER(win->widget), GTK_WIDGET(win->input_entry) );
593 win->input_entry = NULL;
594 /* Delete the child anchor */
595 GtkTextIter start, end;
596 gtk_text_buffer_get_iter_at_child_anchor(buffer, &start, win->input_anchor);
598 gtk_text_iter_forward_char(&end); /* Point after the child anchor */
599 gtk_text_buffer_delete(buffer, &start, &end);
600 win->input_anchor = NULL;
602 gchar *spaces = g_strnfill(win->input_length - g_utf8_strlen(text, -1), ' ');
603 gchar *text_to_insert = g_strconcat(text, spaces, NULL);
605 gtk_text_buffer_insert(buffer, &start, text_to_insert, -1);
606 g_free(text_to_insert);
608 int chars_written = write_to_window_buffer(win, text);
611 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
613 g_signal_emit_by_name(glk, "line-input", win->rock, text);
616 /* Add the text to the window input history */
617 if(win->history_pos != NULL)
619 g_free(win->history->data);
620 win->history = g_list_delete_link(win->history, win->history);
623 win->history = g_list_prepend(win->history, g_strdup(text));
624 win->history_pos = NULL;
627 return chars_written;
630 /* Internal function: Callback for signal insert-text on a text buffer window.
631 Runs after the default handler has already inserted the text.
632 FIXME: This function assumes that newline was the last character typed into the
633 window. That assumption is wrong if, for example, text containing a newline was
634 pasted into the window. */
636 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win)
638 /* Set the history position to NULL and erase the text we were already editing */
639 if(win->history_pos != NULL)
641 g_free(win->history->data);
642 win->history = g_list_delete_link(win->history, win->history);
643 win->history_pos = NULL;
645 if( strchr(text, '\n') != NULL )
647 /* Remove signal handlers */
648 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
649 g_signal_handler_block(window_buffer, win->insert_text_handler);
651 /* Make the window uneditable again and retrieve the text that was input */
652 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
654 int chars_written = finish_text_buffer_line_input(win, TRUE);
655 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
656 event_throw(glk, evtype_LineInput, win, chars_written, 0);
660 /* Internal function: Callback for signal activate on the line input GtkEntry
661 in a text grid window. */
663 on_input_entry_activate(GtkEntry *input_entry, winid_t win)
665 g_signal_handler_block(win->widget, win->char_input_keypress_handler);
667 int chars_written = finish_text_grid_line_input(win, TRUE);
668 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
669 event_throw(glk, evtype_LineInput, win, chars_written, 0);
672 /* Internal function: Callback for signal key-press-event on the line input
673 GtkEntry in a text grid window. */
675 on_input_entry_key_press_event(GtkEntry *input_entry, GdkEventKey *event, winid_t win)
677 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
678 || event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
680 /* Prevent falling off the end of the history list */
681 if( (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
682 && win->history_pos && win->history_pos->next == NULL)
684 if( (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
685 && (win->history_pos == NULL || win->history_pos->prev == NULL) )
688 if(win->history_pos == NULL)
690 gchar *current_input = gtk_entry_get_text(input_entry);
691 win->history = g_list_prepend(win->history, current_input);
692 win->history_pos = win->history;
695 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
698 win->history_pos = g_list_next(win->history_pos);
700 win->history_pos = win->history;
703 win->history_pos = g_list_previous(win->history_pos);
705 /* Insert the history item into the window */
706 g_signal_handler_block(input_entry, win->line_input_entry_changed);
707 gtk_entry_set_text(input_entry, win->history_pos->data);
708 g_signal_handler_unblock(input_entry, win->line_input_entry_changed);
715 on_input_entry_changed(GtkEditable *editable, winid_t win)
717 /* Set the history position to NULL and erase the text we were already editing */
718 if(win->history_pos != NULL)
720 g_free(win->history->data);
721 win->history = g_list_delete_link(win->history, win->history);
722 win->history_pos = NULL;
727 keyval_to_glk_keycode(guint keyval, gboolean unicode)
732 case GDK_KP_Up: return keycode_Up;
734 case GDK_KP_Down: return keycode_Down;
736 case GDK_KP_Left: return keycode_Left;
738 case GDK_KP_Right: return keycode_Right;
741 case GDK_KP_Enter: return keycode_Return;
744 case GDK_KP_Delete: return keycode_Delete;
745 case GDK_Escape: return keycode_Escape;
747 case GDK_KP_Tab: return keycode_Tab;
749 case GDK_KP_Page_Up: return keycode_PageUp;
751 case GDK_KP_Page_Down: return keycode_PageDown;
753 case GDK_KP_Home: return keycode_Home;
755 case GDK_KP_End: return keycode_End;
757 case GDK_KP_F1: return keycode_Func1;
759 case GDK_KP_F2: return keycode_Func2;
761 case GDK_KP_F3: return keycode_Func3;
763 case GDK_KP_F4: return keycode_Func4;
764 case GDK_F5: return keycode_Func5;
765 case GDK_F6: return keycode_Func6;
766 case GDK_F7: return keycode_Func7;
767 case GDK_F8: return keycode_Func8;
768 case GDK_F9: return keycode_Func9;
769 case GDK_F10: return keycode_Func10;
770 case GDK_F11: return keycode_Func11;
771 case GDK_F12: return keycode_Func12;
773 keycode = gdk_keyval_to_unicode(keyval);
774 /* If keycode is 0, then keyval was not recognized; also return
775 unknown if Latin-1 input was requested and the character is not in
777 if(keycode == 0 || (!unicode && keycode > 255))
778 return keycode_Unknown;
784 force_char_input_from_queue(winid_t win, event_t *event)
786 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
787 guint keyval = GPOINTER_TO_UINT(g_async_queue_pop(glk_data->char_input_queue));
789 glk_cancel_char_event(win);
792 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
794 g_signal_emit_by_name(glk, "char-input", win->rock, keyval);
797 event->type = evtype_CharInput;
799 event->val1 = keyval_to_glk_keycode(keyval, win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE);
804 force_line_input_from_queue(winid_t win, event_t *event)
806 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
807 const gchar *text = g_async_queue_pop(glk_data->line_input_queue);
808 glui32 chars_written = 0;
811 if(win->type == wintype_TextBuffer)
813 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
814 GtkTextIter start, end;
816 /* Remove signal handlers so the line input doesn't get picked up again */
817 g_signal_handler_block(buffer, win->insert_text_handler);
819 /* Erase any text that was already typed */
820 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
821 gtk_text_buffer_get_iter_at_mark(buffer, &start, input_position);
822 gtk_text_buffer_get_end_iter(buffer, &end);
823 gtk_text_buffer_delete(buffer, &start, &end);
825 /* Make the window uneditable again */
826 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
828 /* Insert the forced input into the window */
829 gtk_text_buffer_get_end_iter(buffer, &end);
830 gchar *text_to_insert = g_strconcat(text, "\n", NULL);
831 gtk_text_buffer_insert_with_tags_by_name(buffer, &end, text_to_insert, -1, "input", NULL);
832 chars_written = finish_text_buffer_line_input(win, TRUE);
834 else if(win->type == wintype_TextGrid)
836 /* Remove signal handlers so the line input doesn't get picked up again */
837 g_signal_handler_block(win->widget, win->char_input_keypress_handler);
839 /* Insert the forced input into the window */
840 gtk_entry_set_text(GTK_ENTRY(win->input_entry), text);
841 chars_written = finish_text_grid_line_input(win, TRUE);
845 event->type = evtype_LineInput;
847 event->val1 = chars_written;