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 );
45 * glk_cancel_char_event:
46 * @win: A window to cancel the latest char event request on.
48 * Cancels the last char event request on the given window.
51 glk_cancel_char_event(winid_t win)
54 VALID_WINDOW(win, return);
55 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
56 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
59 /* Internal function: Request either latin-1 or unicode line input, in a text grid window. */
61 text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
63 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
65 GtkTextMark *cursor = gtk_text_buffer_get_mark(buffer, "cursor_position");
66 GtkTextIter start_iter, end_iter;
67 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, cursor);
69 /* Determine the maximum length of the line input */
70 gint cursorpos = gtk_text_iter_get_line_offset(&start_iter);
71 /* Odd; the Glk spec says the maximum input length is
72 windowwidth - 1 - cursorposition. I say no, because if cursorposition is
73 zero, then the input should fill the whole line. FIXME??? */
74 win->input_length = MIN(win->width - cursorpos, win->line_input_buffer_max_len);
75 end_iter = start_iter;
76 gtk_text_iter_set_line_offset(&end_iter, cursorpos + win->input_length);
78 /* If the buffer currently has a selection with one bound in the middle of
79 the input field, then deselect it. Otherwise the input field gets trashed */
80 GtkTextIter start_sel, end_sel;
81 if( gtk_text_buffer_get_selection_bounds(buffer, &start_sel, &end_sel) )
83 if( gtk_text_iter_in_range(&start_sel, &start_iter, &end_iter) )
84 gtk_text_buffer_place_cursor(buffer, &end_sel);
85 if( gtk_text_iter_in_range(&end_sel, &start_iter, &end_iter) )
86 gtk_text_buffer_place_cursor(buffer, &start_sel);
89 /* Erase the text currently in the input field and replace it with a GtkEntry */
90 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
91 win->input_anchor = gtk_text_buffer_create_child_anchor(buffer, &start_iter);
92 win->input_entry = gtk_entry_new();
93 /* Set the entry's font to match that of the window */
94 GtkRcStyle *style = gtk_widget_get_modifier_style(win->widget); /* Don't free */
95 gtk_widget_modify_font(win->input_entry, style->font_desc);
96 /* Make the entry as small as possible to fit with the text */
97 gtk_entry_set_has_frame(GTK_ENTRY(win->input_entry), FALSE);
98 GtkBorder border = { 0, 0, 0, 0 };
99 gtk_entry_set_inner_border(GTK_ENTRY(win->input_entry), &border);
100 gtk_entry_set_max_length(GTK_ENTRY(win->input_entry), win->input_length);
101 gtk_entry_set_width_chars(GTK_ENTRY(win->input_entry), win->input_length);
103 /* Insert pre-entered text if needed */
105 gtk_entry_set_text(GTK_ENTRY(win->input_entry), inserttext);
107 /* Set background color of entry (TODO: implement as property) */
109 gdk_color_parse("grey", &background);
110 gtk_widget_modify_base(win->input_entry, GTK_STATE_NORMAL, &background);
112 g_signal_connect(win->input_entry, "activate", G_CALLBACK(on_input_entry_activate), win);
114 gtk_widget_show(win->input_entry);
115 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(win->widget), win->input_entry, win->input_anchor);
117 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
120 /* Internal function: Request either latin-1 or unicode line input, in a text buffer window. */
122 text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
124 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
126 /* Move the input_position mark to the end of the window_buffer */
127 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
128 GtkTextIter end_iter;
129 gtk_text_buffer_get_end_iter(buffer, &end_iter);
130 gtk_text_buffer_move_mark(buffer, input_position, &end_iter);
132 /* Set the entire contents of the window_buffer as uneditable
133 * (so input can only be entered at the end) */
134 GtkTextIter start_iter;
135 gtk_text_buffer_get_start_iter(buffer, &start_iter);
136 gtk_text_buffer_remove_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
137 gtk_text_buffer_apply_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
139 /* Insert pre-entered text if needed */
141 gtk_text_buffer_insert(buffer, &end_iter, inserttext, -1);
143 /* Scroll to input point */
144 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position);
146 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE);
147 g_signal_handler_unblock(buffer, win->insert_text_handler);
151 * glk_request_line_event:
152 * @win: A text buffer or text grid window to request line input on.
153 * @buf: A buffer of at least @maxlen bytes.
154 * @maxlen: Length of the buffer.
155 * @initlen: The number of characters in @buf to pre-enter.
157 * Requests input of a line of Latin-1 characters. A window cannot have requests
158 * for both character and line input at the same time. Nor can it have requests
159 * for line input of both types (Latin-1 and Unicode). It is illegal to call
160 * glk_request_line_event() if the window already has a pending request for
161 * either character or line input.
163 * The @buf argument is a pointer to space where the line input will be stored.
164 * (This may not be %NULL.) @maxlen is the length of this space, in bytes; the
165 * library will not accept more characters than this. If @initlen is nonzero,
166 * then the first @initlen bytes of @buf will be entered as pre-existing input
167 * -- just as if the player had typed them himself. (The player can continue
168 * composing after this pre-entered input, or delete it or edit as usual.)
170 * The contents of the buffer are undefined until the input is completed (either
171 * by a line input event, or glk_cancel_line_event(). The library may or may not
172 * fill in the buffer as the player composes, while the input is still pending;
173 * it is illegal to change the contents of the buffer yourself.
176 glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen)
178 VALID_WINDOW(win, return);
179 g_return_if_fail(buf);
180 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
181 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
182 g_return_if_fail(initlen <= maxlen);
184 win->input_request_type = INPUT_REQUEST_LINE;
185 win->line_input_buffer = buf;
186 win->line_input_buffer_max_len = maxlen;
188 gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup("");
191 case wintype_TextBuffer:
192 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
194 case wintype_TextGrid:
195 text_grid_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
202 * glk_request_line_event_uni:
203 * @win: A text buffer or text grid window to request line input on.
204 * @buf: A buffer of at least @maxlen characters.
205 * @maxlen: Length of the buffer.
206 * @initlen: The number of characters in @buf to pre-enter.
208 * Request input of a line of Unicode characters. This works the same as
209 * glk_request_line_event(), except the result is stored in an array of
210 * <type>glui32</type> values instead of an array of characters, and the values
211 * may be any valid Unicode code points.
213 * The result will be in Unicode Normalization Form C. This basically means that
214 * composite characters will be single characters where possible, instead of
215 * sequences of base and combining marks. See
216 * <ulink url="http://www.unicode.org/reports/tr15/">Unicode Standard Annex #15
217 * </ulink> for the details.
220 glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
222 VALID_WINDOW(win, return);
223 g_return_if_fail(buf);
224 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
225 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
226 g_return_if_fail(initlen <= maxlen);
228 win->input_request_type = INPUT_REQUEST_LINE_UNICODE;
229 win->line_input_buffer_unicode = buf;
230 win->line_input_buffer_max_len = maxlen;
234 utf8 = convert_ucs4_to_utf8(buf, initlen);
243 case wintype_TextBuffer:
244 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), utf8);
246 case wintype_TextGrid:
247 text_grid_request_line_event_common(win, maxlen, (initlen > 0), utf8);
254 * glk_cancel_line_event:
255 * @win: A text buffer or text grid window to cancel line input on.
256 * @event: Will be filled in if the user had already input something.
258 * This cancels a pending request for line input. (Either Latin-1 or Unicode.)
260 * The event pointed to by the event argument will be filled in as if the
261 * player had hit enter, and the input composed so far will be stored in the
262 * buffer; see below. If you do not care about this information, pass NULL as
263 * the event argument. (The buffer will still be filled.)
265 * For convenience, it is legal to call glk_cancel_line_event() even if there
266 * is no line input request on that window. The event type will be set to
267 * evtype_None in this case.
270 glk_cancel_line_event(winid_t win, event_t *event)
273 VALID_WINDOW(win, return);
274 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
275 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
280 /* 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. */
282 on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
284 /* If this is a text grid window, and line input is active, then redirect the key press to the line input GtkEntry */
285 if( win->type == wintype_TextGrid && (win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE) )
287 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
288 || event->keyval == GDK_Down || event->keyval == GDK_KP_Down
289 || event->keyval == GDK_Left || event->keyval == GDK_KP_Left
290 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
291 || event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab
292 || event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up
293 || event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down
294 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
295 || event->keyval == GDK_End || event->keyval == GDK_KP_End)
296 return FALSE; /* Don't redirect these keys */
297 gtk_widget_grab_focus(win->input_entry);
298 gtk_editable_set_position(GTK_EDITABLE(win->input_entry), -1);
299 gboolean retval = TRUE;
300 g_signal_emit_by_name(win->input_entry, "key-press-event", event, &retval);
301 return retval; /* Block this key event if the entry handled it */
303 if(win->input_request_type != INPUT_REQUEST_CHARACTER &&
304 win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE)
309 switch(event->keyval) {
311 case GDK_KP_Up: keycode = keycode_Up; break;
313 case GDK_KP_Down: keycode = keycode_Down; break;
315 case GDK_KP_Left: keycode = keycode_Left; break;
317 case GDK_KP_Right: keycode = keycode_Right; break;
320 case GDK_KP_Enter: keycode = keycode_Return; break;
323 case GDK_KP_Delete: keycode = keycode_Delete; break;
324 case GDK_Escape: keycode = keycode_Escape; break;
326 case GDK_KP_Tab: keycode = keycode_Tab; break;
328 case GDK_KP_Page_Up: keycode = keycode_PageUp; break;
330 case GDK_KP_Page_Down: keycode = keycode_PageDown; break;
332 case GDK_KP_Home: keycode = keycode_Home; break;
334 case GDK_KP_End: keycode = keycode_End; break;
336 case GDK_KP_F1: keycode = keycode_Func1; break;
338 case GDK_KP_F2: keycode = keycode_Func2; break;
340 case GDK_KP_F3: keycode = keycode_Func3; break;
342 case GDK_KP_F4: keycode = keycode_Func4; break;
343 case GDK_F5: keycode = keycode_Func5; break;
344 case GDK_F6: keycode = keycode_Func6; break;
345 case GDK_F7: keycode = keycode_Func7; break;
346 case GDK_F8: keycode = keycode_Func8; break;
347 case GDK_F9: keycode = keycode_Func9; break;
348 case GDK_F10: keycode = keycode_Func10; break;
349 case GDK_F11: keycode = keycode_Func11; break;
350 case GDK_F12: keycode = keycode_Func12; break;
352 keycode = gdk_keyval_to_unicode(event->keyval);
353 /* If keycode is 0, then keyval was not recognized; also return
354 unknown if Latin-1 input was requested and the character is not in
356 if(keycode == 0 || (win->input_request_type == INPUT_REQUEST_CHARACTER && keycode > 255))
357 keycode = keycode_Unknown;
360 event_throw(evtype_CharInput, win, keycode, 0);
362 /* Only one keypress will be handled */
363 win->input_request_type = INPUT_REQUEST_NONE;
364 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
369 /* Internal function: finish handling a line input request, for both text grid and text buffer windows. */
371 end_line_input_request(winid_t win, const gchar *inserted_text)
373 /* Convert the string from UTF-8 to Latin-1 or Unicode */
374 if(win->input_request_type == INPUT_REQUEST_LINE)
377 gchar *latin1 = convert_utf8_to_latin1(inserted_text, &bytes_written);
381 event_throw(evtype_LineInput, win, 0, 0);
385 /* Place input in the echo stream */
386 if(win->echo_stream != NULL)
387 glk_put_string_stream(win->echo_stream, latin1);
389 /* Copy the string (bytes_written does not include the NULL at the end) */
390 int copycount = MIN(win->line_input_buffer_max_len, bytes_written);
391 memcpy(win->line_input_buffer, latin1, copycount);
393 event_throw(evtype_LineInput, win, copycount, 0);
395 else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
398 gunichar *unicode = convert_utf8_to_ucs4(inserted_text, &items_written);
402 event_throw(evtype_LineInput, win, 0, 0);
406 /* Place input in the echo stream */
407 if(win->echo_stream != NULL)
408 glk_put_string_stream_uni(win->echo_stream, unicode);
410 /* Copy the string (but not the NULL at the end) */
411 int copycount = MIN(win->line_input_buffer_max_len, items_written);
412 memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
414 event_throw(evtype_LineInput, win, copycount, 0);
417 WARNING("Wrong input request type");
419 win->input_request_type = INPUT_REQUEST_NONE;
422 /* Internal function: Callback for signal insert-text on a text buffer window.
423 Runs after the default handler has already inserted the text.
424 FIXME: This function assumes that newline was the last character typed into the
425 window. That assumption is wrong if, for example, text containing a newline was
426 pasted into the window. */
428 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win)
430 if( strchr(text, '\n') != NULL )
432 /* Remove signal handlers */
433 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
434 g_signal_handler_block(window_buffer, win->insert_text_handler);
436 /* Make the window uneditable again and retrieve the text that was input */
437 gchar *inserted_text;
438 GtkTextIter start_iter, end_iter;
440 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
441 GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
442 gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position);
443 gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
444 gtk_text_iter_backward_cursor_position(&end_iter); /* don't include \n */
446 inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
448 end_line_input_request(win, inserted_text);
449 g_free(inserted_text);
453 /* Internal function: Callback for signal activate on the line input GtkEntry
454 in a text grid window. */
456 on_input_entry_activate(GtkEntry *input_entry, winid_t win)
458 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
460 gchar *text = g_strdup(gtk_entry_get_text(input_entry));
461 /* Move the focus back into the text view */
462 gtk_widget_grab_focus(win->widget);
463 /* Remove entry widget from text view */
464 /* Should be ok even though this is the widget's own signal handler */
465 gtk_container_remove( GTK_CONTAINER(win->widget), GTK_WIDGET(input_entry) );
466 win->input_entry = NULL;
467 /* Delete the child anchor */
468 GtkTextIter start, end;
469 gtk_text_buffer_get_iter_at_child_anchor(buffer, &start, win->input_anchor);
471 gtk_text_iter_forward_char(&end); /* Point after the child anchor */
472 gtk_text_buffer_delete(buffer, &start, &end);
473 win->input_anchor = NULL;
475 gchar *spaces = g_strnfill(win->input_length - g_utf8_strlen(text, -1), ' ');
476 gchar *text_to_insert = g_strconcat(text, spaces, NULL);
478 gtk_text_buffer_insert(buffer, &start, text_to_insert, -1);
479 g_free(text_to_insert);
481 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
483 end_line_input_request(win, text);