X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=src%2Finput.c;h=b3255cf5d926b4dd6eb6dcb4898691b5dd586d9b;hb=c9ef2863a85d550397cea202b188c606b3a6b867;hp=bf15211267292148fd1f6b582582e6bf666a7317;hpb=6962155a9256d96424ff4a0c474c7f055dfb9b4a;p=projects%2Fchimara%2Fchimara.git diff --git a/src/input.c b/src/input.c index bf15211..b3255cf 100644 --- a/src/input.c +++ b/src/input.c @@ -37,74 +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, glui32 maxlen, gboolean insert, gchar *inserttext) +text_grid_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) ); - if(win->type == wintype_TextGrid) - { - 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); - gint input_length = MIN(win->width - 1 - cursorpos, win->line_input_buffer_max_len); - end_iter = start_iter; - gtk_text_iter_set_line_offset(&end_iter, cursorpos + input_length); - - /* Insert pre-entered text if needed */ - gchar *text; - if(!insert) - text = g_strnfill(input_length, ' '); - else - { - gchar *blanks = g_strnfill(input_length - g_utf8_strlen(inserttext, -1), ' '); - text = g_strconcat(inserttext, blanks, NULL); - g_free(blanks); - } - - /* Erase the text currently in the input field and replace it with blanks or the insert text, with the editable "input_field" tag */ - GtkTextIter text_start, text_end; - GtkTextTag *uneditable = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "uneditable"); - gtk_text_buffer_delete(buffer, &start_iter, &end_iter); - gtk_text_buffer_get_start_iter(buffer, &text_start); - gtk_text_buffer_apply_tag(buffer, uneditable, &text_start, &start_iter); - gtk_text_buffer_insert_with_tags_by_name(buffer, &start_iter, text, -1, "input_field", NULL); - g_free(text); - gtk_text_buffer_get_end_iter(buffer, &text_end); - gtk_text_buffer_apply_tag(buffer, uneditable, &start_iter, &text_end); + 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) ); - gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE); - g_signal_handler_unblock(GTK_TEXT_VIEW(win->widget), win->insert_text_handler); - } - else if(win->type == wintype_TextBuffer) - { - /* 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); + /* 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); - /* 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); - } + /* 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); } /** @@ -139,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, maxlen, (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); } @@ -174,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; @@ -193,7 +213,17 @@ glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initl else utf8 = g_strdup(""); - request_line_event_common(win, maxlen, (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); } @@ -269,7 +299,7 @@ on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win) /* 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, gchar *inserted_text) +end_line_input_request(winid_t win, const gchar *inserted_text) { /* Convert the string from UTF-8 to Latin-1 or Unicode */ if(win->input_request_type == INPUT_REQUEST_LINE) @@ -290,8 +320,8 @@ end_line_input_request(winid_t win, gchar *inserted_text) 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); + /* 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); @@ -310,7 +340,7 @@ end_line_input_request(winid_t win, gchar *inserted_text) } /* Place input in the echo stream */ - /* TODO: fixme + /* TODO: glk_put_string_stream_uni not implemented yet if(win->echo_stream != NULL) glk_put_string_stream_uni(window->echo_stream, unicode);*/ @@ -326,47 +356,6 @@ end_line_input_request(winid_t win, gchar *inserted_text) win->input_request_type = INPUT_REQUEST_NONE; } - -/* Internal function: Callback for signal key-press-event on a text grid window. Only unblocked during line input, so that we can catch the enter key before it inserts an enter into the window. */ -gboolean -on_text_grid_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win) -{ - g_return_val_if_fail(win->type == wintype_TextGrid, FALSE); - g_return_val_if_fail(win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE, FALSE); - - if(event->keyval == GDK_Linefeed || event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) - { - GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); - GtkTextIter start_iter, end_iter; - g_signal_handler_block(GTK_TEXT_VIEW(win->widget), win->insert_text_handler); - - /* Determine the bounds of the line input */ - GtkTextMark *cursor = gtk_text_buffer_get_mark(window_buffer, "cursor_position"); - gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, cursor); - gint cursorpos = gtk_text_iter_get_line_offset(&start_iter); - gint input_length = MIN(win->width - 1 - cursorpos, win->line_input_buffer_max_len); - end_iter = start_iter; - gtk_text_iter_set_line_offset(&end_iter, cursorpos + input_length); - - gchar *inserted_text = g_strchomp( gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE) ); - - /* Make the buffer uneditable again and remove the special tags */ - gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE); - gtk_text_buffer_get_start_iter(window_buffer, &start_iter); - gtk_text_buffer_get_end_iter(window_buffer, &end_iter); - gtk_text_buffer_remove_tag_by_name(window_buffer, "uneditable", &start_iter, &end_iter); - gtk_text_buffer_remove_tag_by_name(window_buffer, "input_field", &start_iter, &end_iter); - - end_line_input_request(win, inserted_text); - g_free(inserted_text); - - /* Block the enter key */ - return TRUE; - } - - return FALSE; -} - /* Internal function: Callback for signal insert-text on a text buffer window. Runs after the default handler has already inserted the text.*/ void @@ -393,3 +382,34 @@ after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar 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); +}