* Fixed flush_text_buffer() function. It always assumed a newline at the end and...
[rodin/chimara.git] / src / input.c
index 382592c152381d7f334eb650d70cf71085ed29c2..89356fae2ff913d8f9a6c07e085ca9556dad067a 100644 (file)
@@ -1,6 +1,9 @@
+#include "charset.h"
+#include "magic.h"
 #include "input.h"
 
-/** glk_request_char_event:
+/** 
+ * glk_request_char_event:
  * @win: A window to request char events from.
  *
  * Request input of a Latin-1 character or special key. A window cannot have 
@@ -12,7 +15,7 @@
 void
 glk_request_char_event(winid_t win)
 {
-       g_return_if_fail(win);
+       VALID_WINDOW(win, return);
        g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
        g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
 
@@ -20,7 +23,8 @@ glk_request_char_event(winid_t win)
        g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
 }
 
-/** glk_request_char_event_uni:
+/** 
+ * glk_request_char_event_uni:
  * @win: A window to request char events from.
  *
  * Request input of a Unicode character or special key. See 
@@ -29,7 +33,7 @@ glk_request_char_event(winid_t win)
 void
 glk_request_char_event_uni(winid_t win)
 {
-       g_return_if_fail(win);
+       VALID_WINDOW(win, return);
        g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
        g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
 
@@ -37,8 +41,30 @@ 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, in a text grid window. */
+/**
+ * glk_cancel_char_event:
+ * @win: A window to cancel the latest char event request on.
+ *
+ * This cancels a pending request for character input. (Either Latin-1 or
+ * Unicode.) For convenience, it is legal to call glk_cancel_char_event() even
+ * if there is no charcter input request on that window. Glk will ignore the
+ * call in this case.
+ */
 void
+glk_cancel_char_event(winid_t win)
+{
+       VALID_WINDOW(win, return);
+       g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
+       
+       if(win->input_request_type == INPUT_REQUEST_CHARACTER || win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE)
+       {
+               win->input_request_type = INPUT_REQUEST_NONE;
+               g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
+       }
+}
+
+/* Internal function: Request either latin-1 or unicode line input, in a text grid window. */
+static void
 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) );
@@ -49,31 +75,57 @@ text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert,
     
     /* Determine the maximum length of the line input */
     gint cursorpos = gtk_text_iter_get_line_offset(&start_iter);
-    gint input_length = MIN(win->width - cursorpos, win->line_input_buffer_max_len);
+    /* 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 + input_length);
+    gtk_text_iter_set_line_offset(&end_iter, cursorpos + win->input_length);
+    
+       /* If the buffer currently has a selection with one bound in the middle of
+       the input field, then deselect it. Otherwise the input field gets trashed */
+       GtkTextIter start_sel, end_sel;
+       if( gtk_text_buffer_get_selection_bounds(buffer, &start_sel, &end_sel) )
+       {
+               if( gtk_text_iter_in_range(&start_sel, &start_iter, &end_iter) )
+                       gtk_text_buffer_place_cursor(buffer, &end_sel);
+               if( gtk_text_iter_in_range(&end_sel, &start_iter, &end_iter) )
+                       gtk_text_buffer_place_cursor(buffer, &start_sel);
+       }
+       
+    /* 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 */
-    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);
-    }            
+    if(insert)
+       gtk_entry_set_text(GTK_ENTRY(win->input_entry), inserttext);
     
-    /* Erase the text currently in the input field and replace it with blanks or the insert text, with the editable "input_field" tag */
-    gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
-    gtk_text_buffer_insert_with_tags_by_name(buffer, &start_iter, text, -1, "input_field", NULL);
-    g_free(text);
-
-    g_signal_handler_unblock(GTK_TEXT_VIEW(win->widget), win->insert_text_handler);
+    /* 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);
+       
+       g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
 }
     
 /* Internal function: Request either latin-1 or unicode line input, in a text buffer window. */
-void
+static 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) );
@@ -119,7 +171,7 @@ text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean inser
  * (This may not be %NULL.) @maxlen is the length of this space, in bytes; the
  * library will not accept more characters than this. If @initlen is nonzero,
  * then the first @initlen bytes of @buf will be entered as pre-existing input
- * -- just as if the player had typed them himself. (The player can continue
+ * — just as if the player had typed them himself. (The player can continue
  * composing after this pre-entered input, or delete it or edit as usual.)
  * 
  * The contents of the buffer are undefined until the input is completed (either
@@ -130,7 +182,7 @@ text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean inser
 void
 glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen)
 {
-       g_return_if_fail(win);
+       VALID_WINDOW(win, return);
        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);
@@ -149,8 +201,6 @@ glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen)
            case wintype_TextGrid:
                text_grid_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
                break;
-        default:
-            g_assert_not_reached();
     }
        g_free(inserttext);
 }
@@ -176,10 +226,11 @@ glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen)
 void
 glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
 {
-       g_return_if_fail(win);
+       VALID_WINDOW(win, return);
        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;
@@ -187,14 +238,9 @@ glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initl
 
        gchar *utf8;
        if(initlen > 0) {
-               GError *error = NULL;
-               utf8 = g_ucs4_to_utf8(buf, initlen, NULL, NULL, &error);
-                       
+               utf8 = convert_ucs4_to_utf8(buf, initlen);
                if(utf8 == NULL)
-               {
-                       g_warning("Error during unicode->utf8 conversion: %s", error->message);
                        return;
-               }
        }
        else
                utf8 = g_strdup("");
@@ -207,16 +253,81 @@ glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initl
            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 or text grid window. */
+/**
+ * glk_cancel_line_event:
+ * @win: A text buffer or text grid window to cancel line input on.
+ * @event: Will be filled in if the user had already input something.
+ *
+ * This cancels a pending request for line input. (Either Latin-1 or Unicode.)
+ *
+ * The event pointed to by the event argument will be filled in as if the
+ * player had hit <keycap>enter</keycap>, and the input composed so far will be stored in the
+ * buffer; see below. If you do not care about this information, pass %NULL as
+ * the @event argument. (The buffer will still be filled.) 
+ *
+ * For convenience, it is legal to call glk_cancel_line_event() even if there
+ * is no line input request on that window. The event type will be set to
+ * #evtype_None in this case.
+ */
+void
+glk_cancel_line_event(winid_t win, event_t *event)
+{
+       /* TODO: write me */
+       VALID_WINDOW(win, return);
+       g_return_if_fail(win->input_request_type != INPUT_REQUEST_NONE);
+       g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
+
+       event->type = evtype_None;
+       event->win = win;
+       event->val1 = 0;
+       event->val2 = 0;
+
+       g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
+
+       int chars_written = 0;
+
+       if(win->type == wintype_TextGrid) {
+               g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
+               chars_written = flush_text_grid(win);
+       } else if(win->type == wintype_TextBuffer) {
+               GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
+               g_signal_handler_block(window_buffer, win->insert_text_handler);
+               chars_written = flush_text_buffer(win);
+       }
+
+       if(chars_written > 0) {
+               event->type = evtype_LineInput;
+               event->val1 = chars_written;
+       }
+}
+
+/* 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. */
 gboolean
 on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
-{    
+{
+       /* If this is a text grid window, and line input is active, then redirect the key press to the line input GtkEntry */
+       if( win->type == wintype_TextGrid && (win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE) )
+       {
+               if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
+                   || event->keyval == GDK_Down || event->keyval == GDK_KP_Down
+                   || event->keyval == GDK_Left || event->keyval == GDK_KP_Left
+                   || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
+                   || event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab
+                   || event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up
+                   || event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down
+                   || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
+                   || event->keyval == GDK_End || event->keyval == GDK_KP_End)
+                       return FALSE; /* Don't redirect these keys */
+               gtk_widget_grab_focus(win->input_entry);
+               gtk_editable_set_position(GTK_EDITABLE(win->input_entry), -1);
+               gboolean retval = TRUE;
+               g_signal_emit_by_name(win->input_entry, "key-press-event", event, &retval);
+               return retval; /* Block this key event if the entry handled it */
+       }
        if(win->input_request_type != INPUT_REQUEST_CHARACTER && 
                win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE)
                return FALSE;
@@ -284,190 +395,125 @@ 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)
+static int
+write_to_window_buffer(winid_t win, const gchar *inserted_text)
 {
+       int copycount = 0;
+
     /* 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);
+        gchar *latin1 = convert_utf8_to_latin1(inserted_text, &bytes_written);
         
         if(latin1 == NULL)
-        {
-            g_warning("Error during utf8->latin1 conversion: %s", error->message);
-            event_throw(evtype_LineInput, win, 0, 0);
-            return;
-        }
+            return 0;
 
         /* 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);
+        /* Copy the string (bytes_written does not include the NULL at the end) */
+        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);
+        gunichar *unicode = convert_utf8_to_ucs4(inserted_text, &items_written);
         
         if(unicode == NULL)
-        {
-            g_warning("Error during utf8->unicode conversion");
-            event_throw(evtype_LineInput, win, 0, 0);
-            return;
-        }
+            return 0;
 
         /* Place input in the echo stream */
-        /* TODO: fixme
         if(win->echo_stream != NULL) 
-            glk_put_string_stream_uni(window->echo_stream, unicode);*/
+            glk_put_string_stream_uni(win->echo_stream, unicode);
 
         /* Copy the string (but not the NULL at the end) */
-        int copycount = MIN(win->line_input_buffer_max_len, items_written);
+        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__);
+        WARNING("Wrong input request type");
 
     win->input_request_type = INPUT_REQUEST_NONE;
+       return copycount;
 }
 
-/* Internal function: calculate the bounds of the line input field of a text grid window. Fill them into start_iter and end_iter */
-static void
-text_grid_get_line_input_bounds(winid_t win, GtkTextIter *start_iter, GtkTextIter *end_iter)
+/* Internal function: Retrieves the input of a TextBuffer window and stores it in the window buffer.
+ * Returns the number of characters written, suitable for inclusion in a line input event. */
+static int
+flush_text_buffer(winid_t win)
 {
-    GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
-    /* "cursor_position" is the Glk output cursor position, which is also the position of the start of the line input buffer */
-    GtkTextMark *input_start = gtk_text_buffer_get_mark(buffer, "cursor_position");
-    gtk_text_buffer_get_iter_at_mark(buffer, start_iter, input_start);
-    gint startpos = gtk_text_iter_get_line_offset(start_iter);
-    gint input_length = MIN(win->width - startpos, win->line_input_buffer_max_len);
-    *end_iter = *start_iter;
-    gtk_text_iter_set_line_offset(end_iter, startpos + input_length);
-}
+       VALID_WINDOW(win, return 0);
+       g_return_val_if_fail(win->type == wintype_TextBuffer, 0);
 
-/* Internal function: Retag the input field bounds */
-static void
-text_grid_retag_line_input_bounds(winid_t win)
-{
-    GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
-    GtkTextIter start, end, input_start, input_end;
-    text_grid_get_line_input_bounds(win, &input_start, &input_end);
-    gtk_text_buffer_get_start_iter(buffer, &start);
-    gtk_text_buffer_get_end_iter(buffer, &end);
-    gtk_text_buffer_remove_tag_by_name(buffer, "input_field", &start, &end);
-    gtk_text_buffer_apply_tag_by_name(buffer, "input_field", &input_start, &input_end);
+       GtkTextIter start_iter, end_iter, last_character;
+
+       GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
+       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);
+       gtk_text_buffer_get_end_iter(window_buffer, &last_character);
+       gtk_text_iter_backward_cursor_position(&last_character);
+
+       gchar* last_char = gtk_text_buffer_get_text(window_buffer, &last_character, &end_iter, FALSE);
+
+       if( strchr(last_char, '\n') != NULL )
+               gtk_text_iter_backward_cursor_position(&end_iter);
+
+       gchar* inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
+
+       int chars_written = write_to_window_buffer(win, inserted_text);
+       g_free(inserted_text);
+
+       return chars_written;
 }
 
-/* Internal function: Callback for signal key-press-event on a text grid window. Only unblocked during line input, so that we can catch all the keys that have different functions in text grid line input, before they mess up the window. */
-gboolean
-on_text_grid_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
+/* Internal function: Retrieves the input of a TextGrid window and stores it in the window buffer.
+ * Returns the number of characters written, suitable for inclusion in a line input event. */
+static int
+flush_text_grid(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);
-    
-    GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
+       VALID_WINDOW(win, return 0);
+       g_return_val_if_fail(win->type == wintype_TextBuffer, 0);
+
+       GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
+       
+       gchar *text = g_strdup( gtk_entry_get_text(GTK_ENTRY(win->input_entry)) );
+       /* Move the focus back into the text view */
+       gtk_widget_grab_focus(win->widget);
+       /* 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(win->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);
     
-    switch(event->keyval) {
-        /* Backspace deletes the character before the cursor, moves the characters in the rest of the input field back one, and inserts a space at the end */
-        case GDK_BackSpace:
-        {
-            GtkTextIter insert, bound, input_start, input_end;
-            /* Backspace acts like Delete if there is text selected */
-            if( gtk_text_buffer_get_selection_bounds(buffer, &insert, &bound) )
-            {
-                text_grid_get_line_input_bounds(win, &input_start, &input_end);
-                
-                /* Determine whether part of the selected range is in the input field, and if so, what part */
-                gint insert_where = gtk_text_iter_compare(&insert, &input_start);
-                gint bound_where = gtk_text_iter_compare(&bound, &input_start);
-                if(insert_where < 0 && bound_where <= 0)
-                    return FALSE;
-                if(insert_where < 0)
-                    insert = input_start;
-                    
-                insert_where = gtk_text_iter_compare(&insert, &input_end);
-                bound_where = gtk_text_iter_compare(&bound, &input_end);
-                if(insert_where >= 0 && bound_where > 0)
-                    return FALSE;
-                if(bound_where > 0)
-                    bound = input_end;
-                
-                /* insert and bound are now within the input field */
-                gsize num_spaces = gtk_text_iter_get_offset(&bound) - gtk_text_iter_get_offset(&insert);
-                gchar *spaces = g_strnfill(num_spaces, ' ');
-                /* Create a temporary mark with right gravity at the end of the input field */
-                GtkTextMark *input_end_mark = gtk_text_buffer_create_mark(buffer, NULL, &input_end, FALSE);
-                gtk_text_buffer_delete(buffer, &insert, &bound);
-                gtk_text_buffer_get_iter_at_mark(buffer, &input_end, input_end_mark);
-                gtk_text_buffer_delete_mark(buffer, input_end_mark);
-                gtk_text_buffer_insert(buffer, &input_end, spaces, -1);
-                g_free(spaces);
-                text_grid_retag_line_input_bounds(win);
-                
-                return TRUE;
-            }
-            
-            text_grid_get_line_input_bounds(win, &input_start, &input_end);
-            /* if cursor is outside input field, continue as default */
-            if( !gtk_text_iter_in_range(&insert, &input_start, &input_end) )
-                return FALSE;
-            /* if cursor is at start of field, do nothing */
-            if( gtk_text_iter_equal(&insert, &input_start) )
-                return TRUE;
-            gtk_text_iter_backward_cursor_position(&insert);
-            /* Create a temporary mark with right gravity at the end of the input field */
-            GtkTextMark *input_end_mark = gtk_text_buffer_create_mark(buffer, NULL, &input_end, FALSE);
-            gtk_text_buffer_delete(buffer, &insert, &bound);
-            /* Revalidate end iterator */
-            gtk_text_buffer_get_iter_at_mark(buffer, &input_end, input_end_mark);
-            gtk_text_buffer_delete_mark(buffer, input_end_mark);
-            gtk_text_buffer_insert(buffer, &input_end, " ", -1);
-            text_grid_retag_line_input_bounds(win);
-        }
-            return TRUE;
-        
-        /* Various forms of Enter */
-        case GDK_Linefeed:
-        case GDK_Return:
-        case GDK_KP_Enter:
-        {
-            GtkTextIter input_start, input_end, start, end;
-            g_signal_handler_block(GTK_TEXT_VIEW(win->widget), win->insert_text_handler);
-        
-            /* Get the input text */
-            text_grid_get_line_input_bounds(win, &input_start, &input_end);
-            gtk_text_iter_forward_char(&input_end);
-            gchar *inserted_text = g_strchomp( gtk_text_buffer_get_text(buffer, &input_start, &input_end, 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(buffer, &start);
-            gtk_text_buffer_get_end_iter(buffer, &end);
-            gtk_text_buffer_remove_tag_by_name(buffer, "input_field", &start, &end);
-            
-            end_line_input_request(win, inserted_text);
-            g_free(inserted_text);
-            
-            /* Block the enter key */
-            return TRUE; 
-        }
-    }
-    return FALSE;
+    int chars_written = write_to_window_buffer(win, text);
+       g_free(text);
+
+       return chars_written;
 }
 
 /* Internal function: Callback for signal insert-text on a text buffer window.
-Runs after the default handler has already inserted the text.*/
+Runs after the default handler has already inserted the text.
+FIXME: This function assumes that newline was the last character typed into the
+window. That assumption is wrong if, for example, text containing a newline was
+pasted into the window. */
 void
 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win) 
 {
@@ -478,17 +524,21 @@ after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar
                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;
-
         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);
 
-        end_line_input_request(win, inserted_text);
-        g_free(inserted_text);
+        int chars_written = flush_text_buffer(win);
+               event_throw(evtype_LineInput, win, chars_written, 0);
        }
 }
+
+/* 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)
+{
+       g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
+
+       int chars_written = flush_text_grid(win);
+       event_throw(evtype_LineInput, win, chars_written, 0);
+}
+