3 /** glk_request_char_event:
4 * @win: A window to request char events from.
6 * Request input of a Latin-1 character or special key. A window cannot have
7 * requests for both character and line input at the same time. Nor can it have
8 * requests for character input of both types (Latin-1 and Unicode). It is
9 * illegal to call glk_request_char_event() if the window already has a pending
10 * request for either character or line input.
13 glk_request_char_event(winid_t win)
15 g_return_if_fail(win);
16 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
17 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
19 win->input_request_type = INPUT_REQUEST_CHARACTER;
20 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
23 /** glk_request_char_event_uni:
24 * @win: A window to request char events from.
26 * Request input of a Unicode character or special key. See
27 * glk_request_char_event().
30 glk_request_char_event_uni(winid_t win)
32 g_return_if_fail(win);
33 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
34 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
36 win->input_request_type = INPUT_REQUEST_CHARACTER_UNICODE;
37 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
40 /* Internal function: Request either latin-1 or unicode line input, in a text grid window. */
42 text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
44 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
46 GtkTextMark *cursor = gtk_text_buffer_get_mark(buffer, "cursor_position");
47 GtkTextIter start_iter, end_iter;
48 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, cursor);
50 /* Determine the maximum length of the line input */
51 gint cursorpos = gtk_text_iter_get_line_offset(&start_iter);
52 /* Odd; the Glk spec says the maximum input length is
53 windowwidth - 1 - cursorposition. I say no, because if cursorposition is
54 zero, then the input should fill the whole line. FIXME??? */
55 win->input_length = MIN(win->width - cursorpos, win->line_input_buffer_max_len);
56 end_iter = start_iter;
57 gtk_text_iter_set_line_offset(&end_iter, cursorpos + win->input_length);
59 /* Erase the text currently in the input field and replace it with a GtkEntry */
60 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
61 win->input_anchor = gtk_text_buffer_create_child_anchor(buffer, &start_iter);
62 win->input_entry = gtk_entry_new();
63 /* Set the entry's font to match that of the window */
64 GtkRcStyle *style = gtk_widget_get_modifier_style(win->widget); /* Don't free */
65 gtk_widget_modify_font(win->input_entry, style->font_desc);
66 /* Make the entry as small as possible to fit with the text */
67 gtk_entry_set_has_frame(GTK_ENTRY(win->input_entry), FALSE);
68 GtkBorder border = { 0, 0, 0, 0 };
69 gtk_entry_set_inner_border(GTK_ENTRY(win->input_entry), &border);
70 gtk_entry_set_max_length(GTK_ENTRY(win->input_entry), win->input_length);
71 gtk_entry_set_width_chars(GTK_ENTRY(win->input_entry), win->input_length);
73 /* Insert pre-entered text if needed */
75 gtk_entry_set_text(GTK_ENTRY(win->input_entry), inserttext);
77 /* Set background color of entry (TODO: implement as property) */
79 gdk_color_parse("grey", &background);
80 gtk_widget_modify_base(win->input_entry, GTK_STATE_NORMAL, &background);
82 g_signal_connect(win->input_entry, "activate", G_CALLBACK(on_input_entry_activate), win);
84 gtk_widget_show(win->input_entry);
85 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(win->widget), win->input_entry, win->input_anchor);
87 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
90 /* Internal function: Request either latin-1 or unicode line input, in a text buffer window. */
92 text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
94 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
96 /* Move the input_position mark to the end of the window_buffer */
97 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
99 gtk_text_buffer_get_end_iter(buffer, &end_iter);
100 gtk_text_buffer_move_mark(buffer, input_position, &end_iter);
102 /* Set the entire contents of the window_buffer as uneditable
103 * (so input can only be entered at the end) */
104 GtkTextIter start_iter;
105 gtk_text_buffer_get_start_iter(buffer, &start_iter);
106 gtk_text_buffer_remove_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
107 gtk_text_buffer_apply_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
109 /* Insert pre-entered text if needed */
111 gtk_text_buffer_insert(buffer, &end_iter, inserttext, -1);
113 /* Scroll to input point */
114 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position);
116 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE);
117 g_signal_handler_unblock(buffer, win->insert_text_handler);
121 * glk_request_line_event:
122 * @win: A text buffer or text grid window to request line input on.
123 * @buf: A buffer of at least @maxlen bytes.
124 * @maxlen: Length of the buffer.
125 * @initlen: The number of characters in @buf to pre-enter.
127 * Requests input of a line of Latin-1 characters. A window cannot have requests
128 * for both character and line input at the same time. Nor can it have requests
129 * for line input of both types (Latin-1 and Unicode). It is illegal to call
130 * glk_request_line_event() if the window already has a pending request for
131 * either character or line input.
133 * The @buf argument is a pointer to space where the line input will be stored.
134 * (This may not be %NULL.) @maxlen is the length of this space, in bytes; the
135 * library will not accept more characters than this. If @initlen is nonzero,
136 * then the first @initlen bytes of @buf will be entered as pre-existing input
137 * -- just as if the player had typed them himself. (The player can continue
138 * composing after this pre-entered input, or delete it or edit as usual.)
140 * The contents of the buffer are undefined until the input is completed (either
141 * by a line input event, or glk_cancel_line_event(). The library may or may not
142 * fill in the buffer as the player composes, while the input is still pending;
143 * it is illegal to change the contents of the buffer yourself.
146 glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen)
148 g_return_if_fail(win);
149 g_return_if_fail(buf);
150 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
151 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
152 g_return_if_fail(initlen <= maxlen);
154 win->input_request_type = INPUT_REQUEST_LINE;
155 win->line_input_buffer = buf;
156 win->line_input_buffer_max_len = maxlen;
158 gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup("");
161 case wintype_TextBuffer:
162 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
164 case wintype_TextGrid:
165 text_grid_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
168 g_assert_not_reached();
174 * glk_request_line_event_uni:
175 * @win: A text buffer or text grid window to request line input on.
176 * @buf: A buffer of at least @maxlen characters.
177 * @maxlen: Length of the buffer.
178 * @initlen: The number of characters in @buf to pre-enter.
180 * Request input of a line of Unicode characters. This works the same as
181 * glk_request_line_event(), except the result is stored in an array of
182 * <type>glui32</type> values instead of an array of characters, and the values
183 * may be any valid Unicode code points.
185 * The result will be in Unicode Normalization Form C. This basically means that
186 * composite characters will be single characters where possible, instead of
187 * sequences of base and combining marks. See
188 * <ulink url="http://www.unicode.org/reports/tr15/">Unicode Standard Annex #15
189 * </ulink> for the details.
192 glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
194 g_return_if_fail(win);
195 g_return_if_fail(buf);
196 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
197 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
198 g_return_if_fail(initlen <= maxlen);
200 win->input_request_type = INPUT_REQUEST_LINE_UNICODE;
201 win->line_input_buffer_unicode = buf;
202 win->line_input_buffer_max_len = maxlen;
206 GError *error = NULL;
207 utf8 = g_ucs4_to_utf8(buf, initlen, NULL, NULL, &error);
211 g_warning("Error during unicode->utf8 conversion: %s", error->message);
220 case wintype_TextBuffer:
221 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), utf8);
223 case wintype_TextGrid:
224 text_grid_request_line_event_common(win, maxlen, (initlen > 0), utf8);
227 g_assert_not_reached();
232 /* 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. */
234 on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
236 /* If this is a text grid window, and line input is active, then redirect the key press to the line input GtkEntry */
237 if( win->type == wintype_TextGrid && (win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE) )
239 gboolean retval = TRUE;
240 g_signal_emit_by_name(win->input_entry, "key-press-event", event, &retval);
241 gtk_widget_grab_focus(win->input_entry);
242 gtk_editable_set_position(GTK_EDITABLE(win->input_entry), -1);
243 return retval; /* Block this key event if the entry handled it */
245 if(win->input_request_type != INPUT_REQUEST_CHARACTER &&
246 win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE)
251 switch(event->keyval) {
253 case GDK_KP_Up: keycode = keycode_Up; break;
255 case GDK_KP_Down: keycode = keycode_Down; break;
257 case GDK_KP_Left: keycode = keycode_Left; break;
259 case GDK_KP_Right: keycode = keycode_Right; break;
262 case GDK_KP_Enter: keycode = keycode_Return; break;
265 case GDK_KP_Delete: keycode = keycode_Delete; break;
266 case GDK_Escape: keycode = keycode_Escape; break;
268 case GDK_KP_Tab: keycode = keycode_Tab; break;
270 case GDK_KP_Page_Up: keycode = keycode_PageUp; break;
272 case GDK_KP_Page_Down: keycode = keycode_PageDown; break;
274 case GDK_KP_Home: keycode = keycode_Home; break;
276 case GDK_KP_End: keycode = keycode_End; break;
278 case GDK_KP_F1: keycode = keycode_Func1; break;
280 case GDK_KP_F2: keycode = keycode_Func2; break;
282 case GDK_KP_F3: keycode = keycode_Func3; break;
284 case GDK_KP_F4: keycode = keycode_Func4; break;
285 case GDK_F5: keycode = keycode_Func5; break;
286 case GDK_F6: keycode = keycode_Func6; break;
287 case GDK_F7: keycode = keycode_Func7; break;
288 case GDK_F8: keycode = keycode_Func8; break;
289 case GDK_F9: keycode = keycode_Func9; break;
290 case GDK_F10: keycode = keycode_Func10; break;
291 case GDK_F11: keycode = keycode_Func11; break;
292 case GDK_F12: keycode = keycode_Func12; break;
294 keycode = gdk_keyval_to_unicode(event->keyval);
295 /* If keycode is 0, then keyval was not recognized; also return
296 unknown if Latin-1 input was requested and the character is not in
298 if(keycode == 0 || (win->input_request_type == INPUT_REQUEST_CHARACTER && keycode > 255))
299 keycode = keycode_Unknown;
302 event_throw(evtype_CharInput, win, keycode, 0);
304 /* Only one keypress will be handled */
305 win->input_request_type = INPUT_REQUEST_NONE;
306 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
311 /* Internal function: finish handling a line input request, for both text grid and text buffer windows. */
313 end_line_input_request(winid_t win, const gchar *inserted_text)
315 /* Convert the string from UTF-8 to Latin-1 or Unicode */
316 if(win->input_request_type == INPUT_REQUEST_LINE)
318 GError *error = NULL;
321 latin1 = g_convert_with_fallback(inserted_text, -1, "ISO-8859-1", "UTF-8", "?", NULL, &bytes_written, &error);
325 g_warning("Error during utf8->latin1 conversion: %s", error->message);
326 event_throw(evtype_LineInput, win, 0, 0);
330 /* Place input in the echo stream */
331 if(win->echo_stream != NULL)
332 glk_put_string_stream(win->echo_stream, latin1);
334 /* Copy the string (bytes_written does not include the NULL at the end) */
335 int copycount = MIN(win->line_input_buffer_max_len, bytes_written);
336 memcpy(win->line_input_buffer, latin1, copycount);
338 event_throw(evtype_LineInput, win, copycount, 0);
340 else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
344 unicode = g_utf8_to_ucs4_fast(inserted_text, -1, &items_written);
348 g_warning("Error during utf8->unicode conversion");
349 event_throw(evtype_LineInput, win, 0, 0);
353 /* Place input in the echo stream */
354 /* TODO: glk_put_string_stream_uni not implemented yet
355 if(win->echo_stream != NULL)
356 glk_put_string_stream_uni(window->echo_stream, unicode);*/
358 /* Copy the string (but not the NULL at the end) */
359 int copycount = MIN(win->line_input_buffer_max_len, items_written);
360 memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
362 event_throw(evtype_LineInput, win, copycount, 0);
365 g_warning("%s: Wrong input request type.", __func__);
367 win->input_request_type = INPUT_REQUEST_NONE;
370 /* Internal function: Callback for signal insert-text on a text buffer window.
371 Runs after the default handler has already inserted the text.*/
373 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win)
375 if( strchr(text, '\n') != NULL )
377 /* Remove signal handlers */
378 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
379 g_signal_handler_block(window_buffer, win->insert_text_handler);
381 /* Make the window uneditable again and retrieve the text that was input */
382 gchar *inserted_text;
383 GtkTextIter start_iter, end_iter;
385 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
386 GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
387 gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position);
388 gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
390 inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
392 end_line_input_request(win, inserted_text);
393 g_free(inserted_text);
397 /* Internal function: Callback for signal activate on the line input GtkEntry
398 in a text grid window. */
400 on_input_entry_activate(GtkEntry *input_entry, winid_t win)
402 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
404 gchar *text = g_strdup(gtk_entry_get_text(input_entry));
405 /* Move the focus back into the text view */
406 gtk_widget_grab_focus(win->widget);
407 /* Remove entry widget from text view */
408 /* Should be ok even though this is the widget's own signal handler */
409 gtk_container_remove( GTK_CONTAINER(win->widget), GTK_WIDGET(input_entry) );
410 win->input_entry = NULL;
411 /* Delete the child anchor */
412 GtkTextIter start, end;
413 gtk_text_buffer_get_iter_at_child_anchor(buffer, &start, win->input_anchor);
415 gtk_text_iter_forward_char(&end); /* Point after the child anchor */
416 gtk_text_buffer_delete(buffer, &start, &end);
417 win->input_anchor = NULL;
419 gchar *spaces = g_strnfill(win->input_length - g_utf8_strlen(text, -1), ' ');
420 gchar *text_to_insert = g_strconcat(text, spaces, NULL);
422 gtk_text_buffer_insert(buffer, &start, text_to_insert, -1);
423 g_free(text_to_insert);
425 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
427 end_line_input_request(win, text);