X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=src%2Finput.c;h=b3255cf5d926b4dd6eb6dcb4898691b5dd586d9b;hb=c9ef2863a85d550397cea202b188c606b3a6b867;hp=d9b0965bd3f9e1d4666cbd5e7ef90294889d4d01;hpb=5d71044dd816436be6c5d7a93bc87e53c94d9f31;p=projects%2Fchimara%2Fchimara.git diff --git a/src/input.c b/src/input.c index d9b0965..b3255cf 100644 --- a/src/input.c +++ b/src/input.c @@ -37,33 +37,82 @@ glk_request_char_event_uni(winid_t win) g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler ); } -/* Internal function: Request either latin-1 or unicode line input. */ +/* Internal function: Request either latin-1 or unicode line input, in a text grid window. */ void -request_line_event_common(winid_t win, gboolean insert, gchar *inserttext) +text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext) { - GtkTextBuffer *window_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(win->widget)); + GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); - /* Move the input_position mark to the end of the window_buffer */ - GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position"); - GtkTextIter end_iter; - gtk_text_buffer_get_end_iter(window_buffer, &end_iter); - gtk_text_buffer_move_mark(window_buffer, input_position, &end_iter); + GtkTextMark *cursor = gtk_text_buffer_get_mark(buffer, "cursor_position"); + GtkTextIter start_iter, end_iter; + gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, cursor); + + /* Determine the maximum length of the line input */ + gint cursorpos = gtk_text_iter_get_line_offset(&start_iter); + /* Odd; the Glk spec says the maximum input length is + windowwidth - 1 - cursorposition. I say no, because if cursorposition is + zero, then the input should fill the whole line. FIXME??? */ + win->input_length = MIN(win->width - cursorpos, win->line_input_buffer_max_len); + end_iter = start_iter; + gtk_text_iter_set_line_offset(&end_iter, cursorpos + win->input_length); + + /* Erase the text currently in the input field and replace it with a GtkEntry */ + gtk_text_buffer_delete(buffer, &start_iter, &end_iter); + win->input_anchor = gtk_text_buffer_create_child_anchor(buffer, &start_iter); + win->input_entry = gtk_entry_new(); + /* Set the entry's font to match that of the window */ + GtkRcStyle *style = gtk_widget_get_modifier_style(win->widget); /* Don't free */ + gtk_widget_modify_font(win->input_entry, style->font_desc); + /* Make the entry as small as possible to fit with the text */ + gtk_entry_set_has_frame(GTK_ENTRY(win->input_entry), FALSE); + GtkBorder border = { 0, 0, 0, 0 }; + gtk_entry_set_inner_border(GTK_ENTRY(win->input_entry), &border); + gtk_entry_set_max_length(GTK_ENTRY(win->input_entry), win->input_length); + gtk_entry_set_width_chars(GTK_ENTRY(win->input_entry), win->input_length); + + /* Insert pre-entered text if needed */ + if(insert) + gtk_entry_set_text(GTK_ENTRY(win->input_entry), inserttext); + + /* Set background color of entry (TODO: implement as property) */ + GdkColor background; + gdk_color_parse("grey", &background); + gtk_widget_modify_base(win->input_entry, GTK_STATE_NORMAL, &background); + + g_signal_connect(win->input_entry, "activate", G_CALLBACK(on_input_entry_activate), win); + + gtk_widget_show(win->input_entry); + gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(win->widget), win->input_entry, win->input_anchor); +} + +/* Internal function: Request either latin-1 or unicode line input, in a text buffer window. */ +void +text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext) +{ + GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); - /* Set the entire contents of the window_buffer as uneditable - * (so input can only be entered at the end) */ - GtkTextIter start_iter; - gtk_text_buffer_get_start_iter(window_buffer, &start_iter); - gtk_text_buffer_remove_tag_by_name(window_buffer, "uneditable", &start_iter, &end_iter); - gtk_text_buffer_apply_tag_by_name(window_buffer, "uneditable", &start_iter, &end_iter); - - /* Insert pre-entered text if needed */ - if(insert) - gtk_text_buffer_insert(window_buffer, &end_iter, inserttext, -1); - - /* Scroll to input point */ - gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position); - g_signal_handler_unblock( G_OBJECT(window_buffer), win->insert_text_handler ); - gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE); + /* Move the input_position mark to the end of the window_buffer */ + GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position"); + GtkTextIter end_iter; + gtk_text_buffer_get_end_iter(buffer, &end_iter); + gtk_text_buffer_move_mark(buffer, input_position, &end_iter); + + /* Set the entire contents of the window_buffer as uneditable + * (so input can only be entered at the end) */ + GtkTextIter start_iter; + gtk_text_buffer_get_start_iter(buffer, &start_iter); + gtk_text_buffer_remove_tag_by_name(buffer, "uneditable", &start_iter, &end_iter); + gtk_text_buffer_apply_tag_by_name(buffer, "uneditable", &start_iter, &end_iter); + + /* Insert pre-entered text if needed */ + if(insert) + gtk_text_buffer_insert(buffer, &end_iter, inserttext, -1); + + /* Scroll to input point */ + gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position); + + gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE); + g_signal_handler_unblock(buffer, win->insert_text_handler); } /** @@ -98,13 +147,24 @@ glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen) g_return_if_fail(buf); g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE); g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid); + g_return_if_fail(initlen <= maxlen); win->input_request_type = INPUT_REQUEST_LINE; win->line_input_buffer = buf; win->line_input_buffer_max_len = maxlen; gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup(""); - request_line_event_common(win, (initlen > 0), inserttext); + switch(win->type) + { + case wintype_TextBuffer: + text_buffer_request_line_event_common(win, maxlen, (initlen > 0), inserttext); + break; + case wintype_TextGrid: + text_grid_request_line_event_common(win, maxlen, (initlen > 0), inserttext); + break; + default: + g_assert_not_reached(); + } g_free(inserttext); } @@ -133,6 +193,7 @@ glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initl g_return_if_fail(buf); g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE); g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid); + g_return_if_fail(initlen <= maxlen); win->input_request_type = INPUT_REQUEST_LINE_UNICODE; win->line_input_buffer_unicode = buf; @@ -145,22 +206,31 @@ glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initl if(utf8 == NULL) { - error_dialog(NULL, error, "Error during unicode->utf8 conversion: "); + g_warning("Error during unicode->utf8 conversion: %s", error->message); return; } } else utf8 = g_strdup(""); - request_line_event_common(win, (initlen > 0), utf8); + switch(win->type) + { + case wintype_TextBuffer: + text_buffer_request_line_event_common(win, maxlen, (initlen > 0), utf8); + break; + case wintype_TextGrid: + text_grid_request_line_event_common(win, maxlen, (initlen > 0), utf8); + break; + default: + g_assert_not_reached(); + } g_free(utf8); } -/* Internal function: Callback for signal key-press-event on a text buffer -window. */ +/* Internal function: Callback for signal key-press-event on a text buffer or text grid window. */ gboolean on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win) -{ +{ if(win->input_request_type != INPUT_REQUEST_CHARACTER && win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE) return FALSE; @@ -227,17 +297,64 @@ on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win) return TRUE; } -/* Internal function: Callback for signal insert-text on a text buffer window. -Not used; cannot modify the text buffer from within the callback?? Segfault! */ -/*void -on_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, gpointer user_data) +/* Internal function: finish handling a line input request, for both text grid and text buffer windows. */ +static void +end_line_input_request(winid_t win, const gchar *inserted_text) { - gchar *newline_pos = strchr(text, '\n'); - if(newline_pos != NULL) { - printf("position: %d\n", newline_pos-text); - *newline_pos = 'a'; - } -}*/ + /* Convert the string from UTF-8 to Latin-1 or Unicode */ + if(win->input_request_type == INPUT_REQUEST_LINE) + { + GError *error = NULL; + gchar *latin1; + gsize bytes_written; + latin1 = g_convert_with_fallback(inserted_text, -1, "ISO-8859-1", "UTF-8", "?", NULL, &bytes_written, &error); + + if(latin1 == NULL) + { + g_warning("Error during utf8->latin1 conversion: %s", error->message); + event_throw(evtype_LineInput, win, 0, 0); + return; + } + + /* Place input in the echo stream */ + if(win->echo_stream != NULL) + glk_put_string_stream(win->echo_stream, latin1); + + /* Copy the string (bytes_written does not include the NULL at the end) */ + int copycount = MIN(win->line_input_buffer_max_len, bytes_written); + memcpy(win->line_input_buffer, latin1, copycount); + g_free(latin1); + event_throw(evtype_LineInput, win, copycount, 0); + } + else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE) + { + gunichar *unicode; + glong items_written; + unicode = g_utf8_to_ucs4_fast(inserted_text, -1, &items_written); + + if(unicode == NULL) + { + g_warning("Error during utf8->unicode conversion"); + event_throw(evtype_LineInput, win, 0, 0); + return; + } + + /* Place input in the echo stream */ + /* TODO: glk_put_string_stream_uni not implemented yet + if(win->echo_stream != NULL) + glk_put_string_stream_uni(window->echo_stream, unicode);*/ + + /* Copy the string (but not the NULL at the end) */ + int copycount = MIN(win->line_input_buffer_max_len, items_written); + memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar)); + g_free(unicode); + event_throw(evtype_LineInput, win, copycount, 0); + } + else + g_warning("%s: Wrong input request type.", __func__); + + win->input_request_type = INPUT_REQUEST_NONE; +} /* Internal function: Callback for signal insert-text on a text buffer window. Runs after the default handler has already inserted the text.*/ @@ -245,74 +362,54 @@ void after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win) { if( strchr(text, '\n') != NULL ) - { - /* Make the window uneditable again and remove signal handlers */ + { + /* Remove signal handlers */ GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); - gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE); g_signal_handler_block(window_buffer, win->insert_text_handler); + + /* Make the window uneditable again and retrieve the text that was input */ + gchar *inserted_text; + GtkTextIter start_iter, end_iter; - /* Retrieve the text that was input */ - GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position"); - GtkTextIter start_iter; - GtkTextIter end_iter; - gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position); - gtk_text_buffer_get_end_iter(window_buffer, &end_iter); - gchar *inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE); + gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE); + GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position"); + gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position); + gtk_text_buffer_get_end_iter(window_buffer, &end_iter); + + inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE); - /* Convert the string from UTF-8 to Latin-1 or Unicode */ - if(win->input_request_type == INPUT_REQUEST_LINE) - { - GError *error = NULL; - gchar *latin1; - gsize bytes_written; - latin1 = g_convert_with_fallback(inserted_text, -1, "ISO-8859-1", "UTF-8", "?", NULL, &bytes_written, &error); - g_free(inserted_text); - - if(latin1 == NULL) - { - error_dialog(NULL, error, "Error during utf8->latin1 conversion: "); - event_throw(evtype_LineInput, win, 0, 0); - return; - } - - /* Place input in the echo stream */ - if(win->echo_stream != NULL) - glk_put_string_stream(win->echo_stream, latin1); - - /* Copy the string (but not the NULL at the end) */ - int copycount = MIN(win->line_input_buffer_max_len, bytes_written - 1); - memcpy(win->line_input_buffer, latin1, copycount); - g_free(latin1); - event_throw(evtype_LineInput, win, copycount, 0); - } - else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE) - { - gunichar *unicode; - glong items_written; - unicode = g_utf8_to_ucs4_fast(inserted_text, -1, &items_written); - g_free(inserted_text); - - if(unicode == NULL) - { - error_dialog(NULL, NULL, "Error during utf8->unicode conversion"); - event_throw(evtype_LineInput, win, 0, 0); - return; - } - - /* Place input in the echo stream */ - /* TODO: fixme - if(win->echo_stream != NULL) - glk_put_string_stream_uni(window->echo_stream, unicode);*/ - - /* Copy the string (but not the NULL at the end) */ - int copycount = MIN(win->line_input_buffer_max_len, items_written); - memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar)); - g_free(unicode); - event_throw(evtype_LineInput, win, copycount, 0); - } - else - g_warning("%s: Wrong input request type.", __func__); - - win->input_request_type = INPUT_REQUEST_NONE; + end_line_input_request(win, inserted_text); + g_free(inserted_text); } } + +/* Internal function: Callback for signal activate on the line input GtkEntry +in a text grid window. */ +void +on_input_entry_activate(GtkEntry *input_entry, winid_t win) +{ + GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); + + gchar *text = g_strdup(gtk_entry_get_text(input_entry)); + + /* Remove entry widget from text view */ + /* Should be ok even though this is the widget's own signal handler */ + gtk_container_remove( GTK_CONTAINER(win->widget), GTK_WIDGET(input_entry) ); + win->input_entry = NULL; + /* Delete the child anchor */ + GtkTextIter start, end; + gtk_text_buffer_get_iter_at_child_anchor(buffer, &start, win->input_anchor); + end = start; + gtk_text_iter_forward_char(&end); /* Point after the child anchor */ + gtk_text_buffer_delete(buffer, &start, &end); + win->input_anchor = NULL; + + gchar *spaces = g_strnfill(win->input_length - g_utf8_strlen(text, -1), ' '); + gchar *text_to_insert = g_strconcat(text, spaces, NULL); + g_free(spaces); + gtk_text_buffer_insert(buffer, &start, text_to_insert, -1); + g_free(text_to_insert); + + end_line_input_request(win, text); + g_free(text); +}