5 * glk_request_char_event:
6 * @win: A window to request char events from.
8 * Request input of a Latin-1 character or special key. A window cannot have
9 * requests for both character and line input at the same time. Nor can it have
10 * requests for character input of both types (Latin-1 and Unicode). It is
11 * illegal to call glk_request_char_event() if the window already has a pending
12 * request for either character or line input.
15 glk_request_char_event(winid_t win)
17 g_return_if_fail(win);
18 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
19 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
21 win->input_request_type = INPUT_REQUEST_CHARACTER;
22 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
26 * glk_request_char_event_uni:
27 * @win: A window to request char events from.
29 * Request input of a Unicode character or special key. See
30 * glk_request_char_event().
33 glk_request_char_event_uni(winid_t win)
35 g_return_if_fail(win);
36 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
37 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
39 win->input_request_type = INPUT_REQUEST_CHARACTER_UNICODE;
40 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
43 /* Internal function: Request either latin-1 or unicode line input, in a text grid window. */
45 text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
47 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
49 GtkTextMark *cursor = gtk_text_buffer_get_mark(buffer, "cursor_position");
50 GtkTextIter start_iter, end_iter;
51 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, cursor);
53 /* Determine the maximum length of the line input */
54 gint cursorpos = gtk_text_iter_get_line_offset(&start_iter);
55 /* Odd; the Glk spec says the maximum input length is
56 windowwidth - 1 - cursorposition. I say no, because if cursorposition is
57 zero, then the input should fill the whole line. FIXME??? */
58 win->input_length = MIN(win->width - cursorpos, win->line_input_buffer_max_len);
59 end_iter = start_iter;
60 gtk_text_iter_set_line_offset(&end_iter, cursorpos + win->input_length);
62 /* Erase the text currently in the input field and replace it with a GtkEntry */
63 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
64 win->input_anchor = gtk_text_buffer_create_child_anchor(buffer, &start_iter);
65 win->input_entry = gtk_entry_new();
66 /* Set the entry's font to match that of the window */
67 GtkRcStyle *style = gtk_widget_get_modifier_style(win->widget); /* Don't free */
68 gtk_widget_modify_font(win->input_entry, style->font_desc);
69 /* Make the entry as small as possible to fit with the text */
70 gtk_entry_set_has_frame(GTK_ENTRY(win->input_entry), FALSE);
71 GtkBorder border = { 0, 0, 0, 0 };
72 gtk_entry_set_inner_border(GTK_ENTRY(win->input_entry), &border);
73 gtk_entry_set_max_length(GTK_ENTRY(win->input_entry), win->input_length);
74 gtk_entry_set_width_chars(GTK_ENTRY(win->input_entry), win->input_length);
76 /* Insert pre-entered text if needed */
78 gtk_entry_set_text(GTK_ENTRY(win->input_entry), inserttext);
80 /* Set background color of entry (TODO: implement as property) */
82 gdk_color_parse("grey", &background);
83 gtk_widget_modify_base(win->input_entry, GTK_STATE_NORMAL, &background);
85 g_signal_connect(win->input_entry, "activate", G_CALLBACK(on_input_entry_activate), win);
87 gtk_widget_show(win->input_entry);
88 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(win->widget), win->input_entry, win->input_anchor);
90 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
93 /* Internal function: Request either latin-1 or unicode line input, in a text buffer window. */
95 text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
97 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
99 /* Move the input_position mark to the end of the window_buffer */
100 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
101 GtkTextIter end_iter;
102 gtk_text_buffer_get_end_iter(buffer, &end_iter);
103 gtk_text_buffer_move_mark(buffer, input_position, &end_iter);
105 /* Set the entire contents of the window_buffer as uneditable
106 * (so input can only be entered at the end) */
107 GtkTextIter start_iter;
108 gtk_text_buffer_get_start_iter(buffer, &start_iter);
109 gtk_text_buffer_remove_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
110 gtk_text_buffer_apply_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
112 /* Insert pre-entered text if needed */
114 gtk_text_buffer_insert(buffer, &end_iter, inserttext, -1);
116 /* Scroll to input point */
117 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position);
119 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE);
120 g_signal_handler_unblock(buffer, win->insert_text_handler);
124 * glk_request_line_event:
125 * @win: A text buffer or text grid window to request line input on.
126 * @buf: A buffer of at least @maxlen bytes.
127 * @maxlen: Length of the buffer.
128 * @initlen: The number of characters in @buf to pre-enter.
130 * Requests input of a line of Latin-1 characters. A window cannot have requests
131 * for both character and line input at the same time. Nor can it have requests
132 * for line input of both types (Latin-1 and Unicode). It is illegal to call
133 * glk_request_line_event() if the window already has a pending request for
134 * either character or line input.
136 * The @buf argument is a pointer to space where the line input will be stored.
137 * (This may not be %NULL.) @maxlen is the length of this space, in bytes; the
138 * library will not accept more characters than this. If @initlen is nonzero,
139 * then the first @initlen bytes of @buf will be entered as pre-existing input
140 * -- just as if the player had typed them himself. (The player can continue
141 * composing after this pre-entered input, or delete it or edit as usual.)
143 * The contents of the buffer are undefined until the input is completed (either
144 * by a line input event, or glk_cancel_line_event(). The library may or may not
145 * fill in the buffer as the player composes, while the input is still pending;
146 * it is illegal to change the contents of the buffer yourself.
149 glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen)
151 g_return_if_fail(win);
152 g_return_if_fail(buf);
153 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
154 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
155 g_return_if_fail(initlen <= maxlen);
157 win->input_request_type = INPUT_REQUEST_LINE;
158 win->line_input_buffer = buf;
159 win->line_input_buffer_max_len = maxlen;
161 gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup("");
164 case wintype_TextBuffer:
165 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
167 case wintype_TextGrid:
168 text_grid_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
171 g_assert_not_reached();
177 * glk_request_line_event_uni:
178 * @win: A text buffer or text grid window to request line input on.
179 * @buf: A buffer of at least @maxlen characters.
180 * @maxlen: Length of the buffer.
181 * @initlen: The number of characters in @buf to pre-enter.
183 * Request input of a line of Unicode characters. This works the same as
184 * glk_request_line_event(), except the result is stored in an array of
185 * <type>glui32</type> values instead of an array of characters, and the values
186 * may be any valid Unicode code points.
188 * The result will be in Unicode Normalization Form C. This basically means that
189 * composite characters will be single characters where possible, instead of
190 * sequences of base and combining marks. See
191 * <ulink url="http://www.unicode.org/reports/tr15/">Unicode Standard Annex #15
192 * </ulink> for the details.
195 glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
197 g_return_if_fail(win);
198 g_return_if_fail(buf);
199 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
200 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
201 g_return_if_fail(initlen <= maxlen);
203 win->input_request_type = INPUT_REQUEST_LINE_UNICODE;
204 win->line_input_buffer_unicode = buf;
205 win->line_input_buffer_max_len = maxlen;
209 utf8 = convert_ucs4_to_utf8(buf, initlen);
218 case wintype_TextBuffer:
219 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), utf8);
221 case wintype_TextGrid:
222 text_grid_request_line_event_common(win, maxlen, (initlen > 0), utf8);
225 g_assert_not_reached();
230 /* 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. */
232 on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
234 /* If this is a text grid window, and line input is active, then redirect the key press to the line input GtkEntry */
235 if( win->type == wintype_TextGrid && (win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE) )
237 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
238 || event->keyval == GDK_Down || event->keyval == GDK_KP_Down
239 || event->keyval == GDK_Left || event->keyval == GDK_KP_Left
240 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
241 || event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab
242 || event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up
243 || event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down
244 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
245 || event->keyval == GDK_End || event->keyval == GDK_KP_End)
246 return FALSE; /* Don't redirect these keys */
247 gtk_widget_grab_focus(win->input_entry);
248 gtk_editable_set_position(GTK_EDITABLE(win->input_entry), -1);
249 gboolean retval = TRUE;
250 g_signal_emit_by_name(win->input_entry, "key-press-event", event, &retval);
251 return retval; /* Block this key event if the entry handled it */
253 if(win->input_request_type != INPUT_REQUEST_CHARACTER &&
254 win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE)
259 switch(event->keyval) {
261 case GDK_KP_Up: keycode = keycode_Up; break;
263 case GDK_KP_Down: keycode = keycode_Down; break;
265 case GDK_KP_Left: keycode = keycode_Left; break;
267 case GDK_KP_Right: keycode = keycode_Right; break;
270 case GDK_KP_Enter: keycode = keycode_Return; break;
273 case GDK_KP_Delete: keycode = keycode_Delete; break;
274 case GDK_Escape: keycode = keycode_Escape; break;
276 case GDK_KP_Tab: keycode = keycode_Tab; break;
278 case GDK_KP_Page_Up: keycode = keycode_PageUp; break;
280 case GDK_KP_Page_Down: keycode = keycode_PageDown; break;
282 case GDK_KP_Home: keycode = keycode_Home; break;
284 case GDK_KP_End: keycode = keycode_End; break;
286 case GDK_KP_F1: keycode = keycode_Func1; break;
288 case GDK_KP_F2: keycode = keycode_Func2; break;
290 case GDK_KP_F3: keycode = keycode_Func3; break;
292 case GDK_KP_F4: keycode = keycode_Func4; break;
293 case GDK_F5: keycode = keycode_Func5; break;
294 case GDK_F6: keycode = keycode_Func6; break;
295 case GDK_F7: keycode = keycode_Func7; break;
296 case GDK_F8: keycode = keycode_Func8; break;
297 case GDK_F9: keycode = keycode_Func9; break;
298 case GDK_F10: keycode = keycode_Func10; break;
299 case GDK_F11: keycode = keycode_Func11; break;
300 case GDK_F12: keycode = keycode_Func12; break;
302 keycode = gdk_keyval_to_unicode(event->keyval);
303 /* If keycode is 0, then keyval was not recognized; also return
304 unknown if Latin-1 input was requested and the character is not in
306 if(keycode == 0 || (win->input_request_type == INPUT_REQUEST_CHARACTER && keycode > 255))
307 keycode = keycode_Unknown;
310 event_throw(evtype_CharInput, win, keycode, 0);
312 /* Only one keypress will be handled */
313 win->input_request_type = INPUT_REQUEST_NONE;
314 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
319 /* Internal function: finish handling a line input request, for both text grid and text buffer windows. */
321 end_line_input_request(winid_t win, const gchar *inserted_text)
323 /* Convert the string from UTF-8 to Latin-1 or Unicode */
324 if(win->input_request_type == INPUT_REQUEST_LINE)
327 gchar *latin1 = convert_utf8_to_latin1(inserted_text, &bytes_written);
331 event_throw(evtype_LineInput, win, 0, 0);
335 /* Place input in the echo stream */
336 if(win->echo_stream != NULL)
337 glk_put_string_stream(win->echo_stream, latin1);
339 /* Copy the string (bytes_written does not include the NULL at the end) */
340 int copycount = MIN(win->line_input_buffer_max_len, bytes_written);
341 memcpy(win->line_input_buffer, latin1, copycount);
343 event_throw(evtype_LineInput, win, copycount, 0);
345 else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
348 gunichar *unicode = convert_utf8_to_ucs4(inserted_text, &items_written);
352 event_throw(evtype_LineInput, win, 0, 0);
356 /* Place input in the echo stream */
357 if(win->echo_stream != NULL)
358 glk_put_string_stream_uni(win->echo_stream, unicode);
360 /* Copy the string (but not the NULL at the end) */
361 int copycount = MIN(win->line_input_buffer_max_len, items_written);
362 memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
364 event_throw(evtype_LineInput, win, copycount, 0);
367 g_warning("%s: Wrong input request type.", __func__);
369 win->input_request_type = INPUT_REQUEST_NONE;
372 /* Internal function: Callback for signal insert-text on a text buffer window.
373 Runs after the default handler has already inserted the text.
374 FIXME: This function assumes that newline was the last character typed into the
375 window. That assumption is wrong if, for example, text containing a newline was
376 pasted into the window. */
378 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win)
380 if( strchr(text, '\n') != NULL )
382 /* Remove signal handlers */
383 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
384 g_signal_handler_block(window_buffer, win->insert_text_handler);
386 /* Make the window uneditable again and retrieve the text that was input */
387 gchar *inserted_text;
388 GtkTextIter start_iter, end_iter;
390 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
391 GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
392 gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position);
393 gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
394 gtk_text_iter_backward_cursor_position(&end_iter); /* don't include \n */
396 inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
398 end_line_input_request(win, inserted_text);
399 g_free(inserted_text);
403 /* Internal function: Callback for signal activate on the line input GtkEntry
404 in a text grid window. */
406 on_input_entry_activate(GtkEntry *input_entry, winid_t win)
408 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
410 gchar *text = g_strdup(gtk_entry_get_text(input_entry));
411 /* Move the focus back into the text view */
412 gtk_widget_grab_focus(win->widget);
413 /* Remove entry widget from text view */
414 /* Should be ok even though this is the widget's own signal handler */
415 gtk_container_remove( GTK_CONTAINER(win->widget), GTK_WIDGET(input_entry) );
416 win->input_entry = NULL;
417 /* Delete the child anchor */
418 GtkTextIter start, end;
419 gtk_text_buffer_get_iter_at_child_anchor(buffer, &start, win->input_anchor);
421 gtk_text_iter_forward_char(&end); /* Point after the child anchor */
422 gtk_text_buffer_delete(buffer, &start, &end);
423 win->input_anchor = NULL;
425 gchar *spaces = g_strnfill(win->input_length - g_utf8_strlen(text, -1), ' ');
426 gchar *text_to_insert = g_strconcat(text, spaces, NULL);
428 gtk_text_buffer_insert(buffer, &start, text_to_insert, -1);
429 g_free(text_to_insert);
431 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
433 end_line_input_request(win, text);