6 * glk_request_char_event:
7 * @win: A window to request char events from.
9 * Request input of a Latin-1 character or special key. A window cannot have
10 * requests for both character and line input at the same time. Nor can it have
11 * requests for character input of both types (Latin-1 and Unicode). It is
12 * illegal to call glk_request_char_event() if the window already has a pending
13 * request for either character or line input.
16 glk_request_char_event(winid_t win)
18 VALID_WINDOW(win, return);
19 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
20 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
22 win->input_request_type = INPUT_REQUEST_CHARACTER;
23 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
27 * glk_request_char_event_uni:
28 * @win: A window to request char events from.
30 * Request input of a Unicode character or special key. See
31 * glk_request_char_event().
34 glk_request_char_event_uni(winid_t win)
36 VALID_WINDOW(win, return);
37 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
38 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
40 win->input_request_type = INPUT_REQUEST_CHARACTER_UNICODE;
41 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
44 /* Internal function: Request either latin-1 or unicode line input, in a text grid window. */
46 text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
48 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
50 GtkTextMark *cursor = gtk_text_buffer_get_mark(buffer, "cursor_position");
51 GtkTextIter start_iter, end_iter;
52 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, cursor);
54 /* Determine the maximum length of the line input */
55 gint cursorpos = gtk_text_iter_get_line_offset(&start_iter);
56 /* Odd; the Glk spec says the maximum input length is
57 windowwidth - 1 - cursorposition. I say no, because if cursorposition is
58 zero, then the input should fill the whole line. FIXME??? */
59 win->input_length = MIN(win->width - cursorpos, win->line_input_buffer_max_len);
60 end_iter = start_iter;
61 gtk_text_iter_set_line_offset(&end_iter, cursorpos + win->input_length);
63 /* Erase the text currently in the input field and replace it with a GtkEntry */
64 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
65 win->input_anchor = gtk_text_buffer_create_child_anchor(buffer, &start_iter);
66 win->input_entry = gtk_entry_new();
67 /* Set the entry's font to match that of the window */
68 GtkRcStyle *style = gtk_widget_get_modifier_style(win->widget); /* Don't free */
69 gtk_widget_modify_font(win->input_entry, style->font_desc);
70 /* Make the entry as small as possible to fit with the text */
71 gtk_entry_set_has_frame(GTK_ENTRY(win->input_entry), FALSE);
72 GtkBorder border = { 0, 0, 0, 0 };
73 gtk_entry_set_inner_border(GTK_ENTRY(win->input_entry), &border);
74 gtk_entry_set_max_length(GTK_ENTRY(win->input_entry), win->input_length);
75 gtk_entry_set_width_chars(GTK_ENTRY(win->input_entry), win->input_length);
77 /* Insert pre-entered text if needed */
79 gtk_entry_set_text(GTK_ENTRY(win->input_entry), inserttext);
81 /* Set background color of entry (TODO: implement as property) */
83 gdk_color_parse("grey", &background);
84 gtk_widget_modify_base(win->input_entry, GTK_STATE_NORMAL, &background);
86 g_signal_connect(win->input_entry, "activate", G_CALLBACK(on_input_entry_activate), win);
88 gtk_widget_show(win->input_entry);
89 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(win->widget), win->input_entry, win->input_anchor);
91 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
94 /* Internal function: Request either latin-1 or unicode line input, in a text buffer window. */
96 text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
98 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
100 /* Move the input_position mark to the end of the window_buffer */
101 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
102 GtkTextIter end_iter;
103 gtk_text_buffer_get_end_iter(buffer, &end_iter);
104 gtk_text_buffer_move_mark(buffer, input_position, &end_iter);
106 /* Set the entire contents of the window_buffer as uneditable
107 * (so input can only be entered at the end) */
108 GtkTextIter start_iter;
109 gtk_text_buffer_get_start_iter(buffer, &start_iter);
110 gtk_text_buffer_remove_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
111 gtk_text_buffer_apply_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
113 /* Insert pre-entered text if needed */
115 gtk_text_buffer_insert(buffer, &end_iter, inserttext, -1);
117 /* Scroll to input point */
118 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position);
120 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE);
121 g_signal_handler_unblock(buffer, win->insert_text_handler);
125 * glk_request_line_event:
126 * @win: A text buffer or text grid window to request line input on.
127 * @buf: A buffer of at least @maxlen bytes.
128 * @maxlen: Length of the buffer.
129 * @initlen: The number of characters in @buf to pre-enter.
131 * Requests input of a line of Latin-1 characters. A window cannot have requests
132 * for both character and line input at the same time. Nor can it have requests
133 * for line input of both types (Latin-1 and Unicode). It is illegal to call
134 * glk_request_line_event() if the window already has a pending request for
135 * either character or line input.
137 * The @buf argument is a pointer to space where the line input will be stored.
138 * (This may not be %NULL.) @maxlen is the length of this space, in bytes; the
139 * library will not accept more characters than this. If @initlen is nonzero,
140 * then the first @initlen bytes of @buf will be entered as pre-existing input
141 * -- just as if the player had typed them himself. (The player can continue
142 * composing after this pre-entered input, or delete it or edit as usual.)
144 * The contents of the buffer are undefined until the input is completed (either
145 * by a line input event, or glk_cancel_line_event(). The library may or may not
146 * fill in the buffer as the player composes, while the input is still pending;
147 * it is illegal to change the contents of the buffer yourself.
150 glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen)
152 VALID_WINDOW(win, return);
153 g_return_if_fail(buf);
154 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
155 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
156 g_return_if_fail(initlen <= maxlen);
158 win->input_request_type = INPUT_REQUEST_LINE;
159 win->line_input_buffer = buf;
160 win->line_input_buffer_max_len = maxlen;
162 gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup("");
165 case wintype_TextBuffer:
166 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
168 case wintype_TextGrid:
169 text_grid_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
176 * glk_request_line_event_uni:
177 * @win: A text buffer or text grid window to request line input on.
178 * @buf: A buffer of at least @maxlen characters.
179 * @maxlen: Length of the buffer.
180 * @initlen: The number of characters in @buf to pre-enter.
182 * Request input of a line of Unicode characters. This works the same as
183 * glk_request_line_event(), except the result is stored in an array of
184 * <type>glui32</type> values instead of an array of characters, and the values
185 * may be any valid Unicode code points.
187 * The result will be in Unicode Normalization Form C. This basically means that
188 * composite characters will be single characters where possible, instead of
189 * sequences of base and combining marks. See
190 * <ulink url="http://www.unicode.org/reports/tr15/">Unicode Standard Annex #15
191 * </ulink> for the details.
194 glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
196 VALID_WINDOW(win, return);
197 g_return_if_fail(buf);
198 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
199 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
200 g_return_if_fail(initlen <= maxlen);
202 win->input_request_type = INPUT_REQUEST_LINE_UNICODE;
203 win->line_input_buffer_unicode = buf;
204 win->line_input_buffer_max_len = maxlen;
208 utf8 = convert_ucs4_to_utf8(buf, initlen);
217 case wintype_TextBuffer:
218 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), utf8);
220 case wintype_TextGrid:
221 text_grid_request_line_event_common(win, maxlen, (initlen > 0), utf8);
227 /* 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. */
229 on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
231 /* If this is a text grid window, and line input is active, then redirect the key press to the line input GtkEntry */
232 if( win->type == wintype_TextGrid && (win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE) )
234 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
235 || event->keyval == GDK_Down || event->keyval == GDK_KP_Down
236 || event->keyval == GDK_Left || event->keyval == GDK_KP_Left
237 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
238 || event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab
239 || event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up
240 || event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down
241 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
242 || event->keyval == GDK_End || event->keyval == GDK_KP_End)
243 return FALSE; /* Don't redirect these keys */
244 gtk_widget_grab_focus(win->input_entry);
245 gtk_editable_set_position(GTK_EDITABLE(win->input_entry), -1);
246 gboolean retval = TRUE;
247 g_signal_emit_by_name(win->input_entry, "key-press-event", event, &retval);
248 return retval; /* Block this key event if the entry handled it */
250 if(win->input_request_type != INPUT_REQUEST_CHARACTER &&
251 win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE)
256 switch(event->keyval) {
258 case GDK_KP_Up: keycode = keycode_Up; break;
260 case GDK_KP_Down: keycode = keycode_Down; break;
262 case GDK_KP_Left: keycode = keycode_Left; break;
264 case GDK_KP_Right: keycode = keycode_Right; break;
267 case GDK_KP_Enter: keycode = keycode_Return; break;
270 case GDK_KP_Delete: keycode = keycode_Delete; break;
271 case GDK_Escape: keycode = keycode_Escape; break;
273 case GDK_KP_Tab: keycode = keycode_Tab; break;
275 case GDK_KP_Page_Up: keycode = keycode_PageUp; break;
277 case GDK_KP_Page_Down: keycode = keycode_PageDown; break;
279 case GDK_KP_Home: keycode = keycode_Home; break;
281 case GDK_KP_End: keycode = keycode_End; break;
283 case GDK_KP_F1: keycode = keycode_Func1; break;
285 case GDK_KP_F2: keycode = keycode_Func2; break;
287 case GDK_KP_F3: keycode = keycode_Func3; break;
289 case GDK_KP_F4: keycode = keycode_Func4; break;
290 case GDK_F5: keycode = keycode_Func5; break;
291 case GDK_F6: keycode = keycode_Func6; break;
292 case GDK_F7: keycode = keycode_Func7; break;
293 case GDK_F8: keycode = keycode_Func8; break;
294 case GDK_F9: keycode = keycode_Func9; break;
295 case GDK_F10: keycode = keycode_Func10; break;
296 case GDK_F11: keycode = keycode_Func11; break;
297 case GDK_F12: keycode = keycode_Func12; break;
299 keycode = gdk_keyval_to_unicode(event->keyval);
300 /* If keycode is 0, then keyval was not recognized; also return
301 unknown if Latin-1 input was requested and the character is not in
303 if(keycode == 0 || (win->input_request_type == INPUT_REQUEST_CHARACTER && keycode > 255))
304 keycode = keycode_Unknown;
307 event_throw(evtype_CharInput, win, keycode, 0);
309 /* Only one keypress will be handled */
310 win->input_request_type = INPUT_REQUEST_NONE;
311 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
316 /* Internal function: finish handling a line input request, for both text grid and text buffer windows. */
318 end_line_input_request(winid_t win, const gchar *inserted_text)
320 /* Convert the string from UTF-8 to Latin-1 or Unicode */
321 if(win->input_request_type == INPUT_REQUEST_LINE)
324 gchar *latin1 = convert_utf8_to_latin1(inserted_text, &bytes_written);
328 event_throw(evtype_LineInput, win, 0, 0);
332 /* Place input in the echo stream */
333 if(win->echo_stream != NULL)
334 glk_put_string_stream(win->echo_stream, latin1);
336 /* Copy the string (bytes_written does not include the NULL at the end) */
337 int copycount = MIN(win->line_input_buffer_max_len, bytes_written);
338 memcpy(win->line_input_buffer, latin1, copycount);
340 event_throw(evtype_LineInput, win, copycount, 0);
342 else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
345 gunichar *unicode = convert_utf8_to_ucs4(inserted_text, &items_written);
349 event_throw(evtype_LineInput, win, 0, 0);
353 /* Place input in the echo stream */
354 if(win->echo_stream != NULL)
355 glk_put_string_stream_uni(win->echo_stream, unicode);
357 /* Copy the string (but not the NULL at the end) */
358 int copycount = MIN(win->line_input_buffer_max_len, items_written);
359 memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
361 event_throw(evtype_LineInput, win, copycount, 0);
364 WARNING("Wrong input request type");
366 win->input_request_type = INPUT_REQUEST_NONE;
369 /* Internal function: Callback for signal insert-text on a text buffer window.
370 Runs after the default handler has already inserted the text.
371 FIXME: This function assumes that newline was the last character typed into the
372 window. That assumption is wrong if, for example, text containing a newline was
373 pasted into the window. */
375 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win)
377 if( strchr(text, '\n') != NULL )
379 /* Remove signal handlers */
380 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
381 g_signal_handler_block(window_buffer, win->insert_text_handler);
383 /* Make the window uneditable again and retrieve the text that was input */
384 gchar *inserted_text;
385 GtkTextIter start_iter, end_iter;
387 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
388 GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
389 gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position);
390 gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
391 gtk_text_iter_backward_cursor_position(&end_iter); /* don't include \n */
393 inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
395 end_line_input_request(win, inserted_text);
396 g_free(inserted_text);
400 /* Internal function: Callback for signal activate on the line input GtkEntry
401 in a text grid window. */
403 on_input_entry_activate(GtkEntry *input_entry, winid_t win)
405 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
407 gchar *text = g_strdup(gtk_entry_get_text(input_entry));
408 /* Move the focus back into the text view */
409 gtk_widget_grab_focus(win->widget);
410 /* Remove entry widget from text view */
411 /* Should be ok even though this is the widget's own signal handler */
412 gtk_container_remove( GTK_CONTAINER(win->widget), GTK_WIDGET(input_entry) );
413 win->input_entry = NULL;
414 /* Delete the child anchor */
415 GtkTextIter start, end;
416 gtk_text_buffer_get_iter_at_child_anchor(buffer, &start, win->input_anchor);
418 gtk_text_iter_forward_char(&end); /* Point after the child anchor */
419 gtk_text_buffer_delete(buffer, &start, &end);
420 win->input_anchor = NULL;
422 gchar *spaces = g_strnfill(win->input_length - g_utf8_strlen(text, -1), ' ');
423 gchar *text_to_insert = g_strconcat(text, spaces, NULL);
425 gtk_text_buffer_insert(buffer, &start, text_to_insert, -1);
426 g_free(text_to_insert);
428 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
430 end_line_input_request(win, text);