Implemented output buffering.
authorrodin <rodin@ddfedd41-794f-dd11-ae45-00112f111e67>
Sat, 14 Nov 2009 15:36:47 +0000 (15:36 +0000)
committerrodin <rodin@ddfedd41-794f-dd11-ae45-00112f111e67>
Sat, 14 Nov 2009 15:36:47 +0000 (15:36 +0000)
12 files changed:
libchimara/Makefile.am
libchimara/event.c
libchimara/hyperlink.c
libchimara/input.c
libchimara/input.h
libchimara/stream.h
libchimara/strio.c
libchimara/strio.h [new file with mode: 0644]
libchimara/style.c
libchimara/style.h
libchimara/window.c
libchimara/window.h

index b7a79e3709791163304a1e813b2f5f8ef6292470..2024138495331ca2e51c2060503aa56619ace5a7 100644 (file)
@@ -30,7 +30,7 @@ libchimara_la_SOURCES = \
        resource.c resource.h \
        schannel.c \
        stream.c stream.h \
-       strio.c \
+       strio.c strio.h \
        style.c style.h \
        timer.c timer.h \
        window.c window.h 
index 765f6645bd234f01dd9b21d7b1871e664a970369..366c18929905840e06345a8957d422c6f9702462 100644 (file)
@@ -147,8 +147,15 @@ glk_select(event_t *event)
 {
        g_return_if_fail(event != NULL);
 
+       /* Flush all window buffers */
+       winid_t win;
+       for(win = glk_window_iterate(NULL, NULL); win != NULL; win = glk_window_iterate(win, NULL)) {
+               if(win->type == wintype_TextBuffer)
+                       flush_window_buffer(win);
+       }
+
        ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
-       
+
        get_appropriate_event(event);
 
        /* Check for interrupt */
index af8275e19a32da562919f26bb9e14de6a2b97b0a..6d0d263b099c028a633e0b1be61295f8f08eca1f 100644 (file)
@@ -1,21 +1,80 @@
-#include <libchimara/glk.h>
+#include "hyperlink.h"
+#include "chimara-glk-private.h"
 
+extern GPrivate *glk_data_key;
+
+/**
+ * glk_set_hyperlink:
+ * @linkval: Set to nonzero to initiate hyperlink mode. Set to zero to disengage.
+ *
+ * Use this function to create hyperlinks in a textbuffer. It sets the current stream
+ * to hyperlink mode, after which text will become a hyperlink until hyperlink mode
+ * is turned off. If the current stream does not write to a textbuffer window, this function
+ * does nothing.
+ *
+ * You can request hyperlink events with glk_request_hyperlink_event() to react
+ * to clicks on the link.
+ */
 void 
 glk_set_hyperlink(glui32 linkval)
 {
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
+       g_return_if_fail(glk_data->current_stream != NULL);
+       glk_set_hyperlink_stream(glk_data->current_stream, linkval);
 }
 
+/**
+ * glk_set_hyperlink:
+ * @str: The stream to set the hyperlink mode on.
+ * @linkval: Set to nonzero to initiate hyperlink mode. Set to zero to disengage.
+ *
+ * Use this function to create hyperlinks in a textbuffer. It sets a stream to a textbuffer
+ * window to hyperlink mode, after which text will become a hyperlink until hyperlink mode
+ * is turned off. Calling this function on a stream that does not write to a textbuffer does
+ * nothing.
+ *
+ * You can request hyperlink events with glk_request_hyperlink_event() to react
+ * to clicks on the link.
+ */
 void 
 glk_set_hyperlink_stream(strid_t str, glui32 linkval)
 {
+       g_return_if_fail(str != NULL);
+       g_return_if_fail(str->window != NULL);
+       g_return_if_fail(str->window->type == wintype_TextBuffer);
+
+       str->hyperlink_mode = (linkval != 0);
 }
 
 void 
 glk_request_hyperlink_event(winid_t win)
 {
+       VALID_WINDOW(win, return);
+       g_return_if_fail(win != NULL);
+       g_return_if_fail(win->mouse_click_handler != 0);
+       g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
+
+       g_signal_handler_unblock( G_OBJECT(win->widget), win->mouse_click_handler );
 }
 
 void 
 glk_cancel_hyperlink_event(winid_t win)
 {
+       VALID_WINDOW(win, return);
+       g_return_if_fail(win != NULL);
+       g_return_if_fail(win->mouse_click_handler != 0);
+       g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
+       
+       g_signal_handler_block( G_OBJECT(win->widget), win->mouse_click_handler );
+}
+
+/* Internal function: General callback for signal button-release-event on a
+ * text buffer or text grid window.  Used for detecting clicks on hyperlinks.
+ * Blocked when not in use.
+ */
+gboolean
+on_window_button_release_event(GtkWidget *widget, GdkEventButton *event, winid_t win)
+{
+       printf("Click on (%f,%f)\n", event->x, event->y);
+       return TRUE;
 }
index 86cbe34fd08657d98ca97af42412c5f65486554e..f48aa848fbc3ccda941dcac94e14d1e2474ae6be 100644 (file)
@@ -16,6 +16,8 @@ request_char_event_common(winid_t win, gboolean unicode)
        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);
+
+       flush_window_buffer(win);
        
        ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
 
@@ -166,6 +168,8 @@ text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert,
 static void
 text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
 {
+       flush_window_buffer(win);
+
        gdk_threads_enter();
        
        GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
index 56fb73c7b5aa6d5ecba7cd8b9094ea146639c205..94cc88c6a069056ab439ccc19b5529a7dbbb2422 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "window.h"
 #include "event.h"
+#include "strio.h"
 
 G_GNUC_INTERNAL gboolean on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win);
 G_GNUC_INTERNAL void after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win);
index 9636fac2c49b3c853c04ed9bbf522ae5cdfa0a42..1e5a720b141469fa54c46b654bdaeec276af7793 100644 (file)
@@ -49,6 +49,7 @@ struct glk_stream_struct
        gchar *filename; /* Displayable filename in UTF-8 for error handling */
 
        gchar *style; /* Name of the current style */
+       gboolean hyperlink_mode; /* WHen turned on, text written to the stream will be a hyperlink */
 };
 
 G_GNUC_INTERNAL strid_t file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode);
index f01099ae096c8606f43e52ca544c269315062930..3309392a8da71660abdfca5c6f26d385202a59bf 100644 (file)
@@ -61,29 +61,43 @@ write_utf8_to_grid(winid_t win, gchar *s)
     gdk_threads_leave();
 }
 
+
 /* Internal function: write a UTF-8 string to a text buffer window's text buffer. */
 static void
-write_utf8_to_window(winid_t win, gchar *s)
+write_utf8_to_window_buffer(winid_t win, gchar *s)
 {
        if(win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
        {
                ILLEGAL("Tried to print to a text buffer window with line input pending.");
                return;
        }
+
+       // Write to the buffer  
+       g_string_append(win->buffer, s);
+}
        
+/* Internal function: flush a window's text buffer to the screen. */
+void
+flush_window_buffer(winid_t win)
+{
+       if(win->buffer->len == 0)
+               return;
+
        gdk_threads_enter();
 
        GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
 
        GtkTextIter iter;
        gtk_text_buffer_get_end_iter(buffer, &iter);
-       gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, s, -1, win->window_stream->style, NULL);
+       gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, win->buffer->str, -1, win->window_stream->style, NULL);
 
        gdk_threads_leave();
        
        ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
        g_assert(glk);
-       g_signal_emit_by_name(glk, "text-buffer-output", win->rock, s);
+       g_signal_emit_by_name(glk, "text-buffer-output", win->rock, win->buffer->str);
+
+       g_string_truncate(win->buffer, 0);
 }
 
 /* Internal function: write a Latin-1 buffer with length to a stream. */
@@ -123,7 +137,7 @@ write_buffer_to_stream(strid_t str, gchar *buf, glui32 len)
                                        gchar *utf8 = convert_latin1_to_utf8(buf, len);
                                        if(utf8 != NULL)
                                        {
-                                               write_utf8_to_window(str->window, utf8);
+                                               write_utf8_to_window_buffer(str->window, utf8);
                                                g_free(utf8);
                                        }
                                }       
@@ -224,7 +238,7 @@ write_buffer_to_stream_uni(strid_t str, glui32 *buf, glui32 len)
                                        gchar *utf8 = convert_ucs4_to_utf8(buf, len);
                                        if(utf8 != NULL)
                                        {
-                                               write_utf8_to_window(str->window, utf8);
+                                               write_utf8_to_window_buffer(str->window, utf8);
                                                g_free(utf8);
                                        }
                                }       
@@ -341,6 +355,9 @@ void
 glk_put_string_stream(strid_t str, char *s)
 {
        VALID_STREAM(str, return);
+       if(*s == 0)
+               return;
+
        g_return_if_fail(str->file_mode != filemode_Read);
 
        write_buffer_to_stream(str, s, strlen(s));
@@ -359,6 +376,9 @@ void
 glk_put_string_stream_uni(strid_t str, glui32 *s)
 {
        VALID_STREAM(str, return);
+       if(*s == 0)
+               return;
+
        g_return_if_fail(str->file_mode != filemode_Read);
        
        /* An impromptu strlen() for glui32 arrays */
@@ -383,6 +403,9 @@ void
 glk_put_buffer_stream(strid_t str, char *buf, glui32 len)
 {
        VALID_STREAM(str, return);
+       if(len == 0)
+               return;
+
        g_return_if_fail(str->file_mode != filemode_Read);
        
        write_buffer_to_stream(str, buf, len);
@@ -402,6 +425,9 @@ void
 glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len)
 {
        VALID_STREAM(str, return);
+       if(len == 0)
+               return;
+
        g_return_if_fail(str->file_mode != filemode_Read);
        
        write_buffer_to_stream_uni(str, buf, len);
@@ -884,9 +910,14 @@ glk_get_line_stream(strid_t str, char *buf, glui32 len)
                                }
                                else /* Regular binary file */
                                {
-                                       fgets(buf, len, str->file_pointer);
-                                       str->read_count += strlen(buf);
-                                       return strlen(buf);
+                                       if( !fgets(buf, len, str->file_pointer) ) {
+                                               *buf = 0;
+                                               return 0;
+                                       }
+
+                                       int nread = strlen(buf);
+                                       str->read_count += nread;
+                                       return nread;
                                }
                        }
                        else /* Text mode is the same for Unicode and regular files */
@@ -1022,10 +1053,14 @@ glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len)
                                else /* Regular binary file */
                                {
                                        gchar *readbuffer = g_new0(gchar, len);
-                                       fgets(readbuffer, len, str->file_pointer);
-                                       glui32 count = strlen(readbuffer) + 1; /* Copy terminator */
+                                       if( !fgets(readbuffer, len, str->file_pointer) ) {
+                                               *buf = 0;
+                                               return 0;
+                                       }
+
+                                       glui32 count = strlen(readbuffer);
                                        int foo;
-                                       for(foo = 0; foo < count; foo++)
+                                       for(foo = 0; foo < count + 1; foo++) /* Copy terminator */
                                                buf[foo] = (unsigned char)(readbuffer[foo]);
                                        str->read_count += count;
                                        return count;
diff --git a/libchimara/strio.h b/libchimara/strio.h
new file mode 100644 (file)
index 0000000..57e98f4
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef STRIO_H
+#define STRIO_H
+
+G_GNUC_INTERNAL void flush_window_buffer(winid_t win);
+
+#endif
index 0734fd37847ce1244b7ed209acb846072a46c34e..cf20f0ca1ccbe58f77805b96db02016e55a61207 100644 (file)
@@ -67,6 +67,10 @@ get_tag_name(glui32 style)
  */
 void
 glk_set_style_stream(strid_t str, glui32 styl) {
+       if(str->window == NULL)
+               return;
+
+       flush_window_buffer(str->window);
        str->style = get_tag_name(styl);
 }
 
@@ -691,6 +695,20 @@ glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val)
        }
 }
 
+/**
+ * glk_stylehint_clear:
+ * @wintype: The window type to set a style hint on, or %wintype_AllTypes.
+ * @styl: The style to set a hint for.
+ * @hint: The type of style hint, one of the <code>stylehint_</code> constants.
+ *
+ * Resets a hint about the appearance of one style for a particular type of 
+ * window to it's default value. You can also set wintype to %wintype_AllTypes, which resets a hint for 
+ * all types of window.
+ * <note><para>
+ *  There is no equivalent constant to reset a hint for all styles of a single 
+ *  window type.
+ * </para></note>
+ */
 void
 glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint)
 {
@@ -710,12 +728,31 @@ glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint)
        }
 }
 
+/**
+ * glk_style_distinguish:
+ * @win: The window in which the styles are to be distinguished.
+ * @styl1: The first style to be distinguished from the second style.
+ * @styl2: The second styel to be distinguished from the first style.
+ * 
+ * Returns: TRUE if the two styles are visually distinguishable in the given window.
+ * If they are not, it returns FALSE.
+ */
 glui32
 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2)
 {
        return styl1 != styl2;
 }
 
+/**
+ * glk_style_measure:
+ * @win: The window from which to take the style.
+ * @styl: The style to perform the measurement on.
+ * @hint: The stylehint to measure.
+ * @result: Address to write the result to.
+ * 
+ * This function can be used to query the current value of a particular style hint.
+ * Returns: TRUE upon successul retrievel, otherwise FALSE.
+ */
 glui32
 glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result)
 {
index 7dc507f0abaaa3c1dee2212e4ce1aec2f9b9a32e..7b56760fc1fd5a9b4e8af154188f050a9e733887 100644 (file)
@@ -9,6 +9,7 @@
 #include "magic.h"
 #include "chimara-glk-private.h"
 #include "stream.h"
+#include "strio.h"
 
 G_GNUC_INTERNAL void style_init_textbuffer(GtkTextBuffer *buffer);
 G_GNUC_INTERNAL void style_init_textgrid(GtkTextBuffer *buffer);
index b3995f09189b8234982a3df80977ef9114f487b8..a31bed1b251f7b749d82a325a2b058c599bd4b93 100644 (file)
@@ -31,6 +31,9 @@ window_new_common(glui32 rock)
        win->line_input_buffer = NULL;
        win->line_input_buffer_unicode = NULL;
 
+       /* Initialise the buffer */
+       win->buffer = g_string_sized_new(1024);
+
        return win;
 }
 
@@ -48,6 +51,8 @@ window_close_common(winid_t win, gboolean destroy_node)
        if(destroy_node)
                g_node_destroy(win->window_node);
        win->magic = MAGIC_FREE;
+
+       g_string_free(win->buffer, TRUE);
        g_free(win);
 }
 
@@ -482,6 +487,10 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype,
                        win->keypress_handler = g_signal_connect( G_OBJECT(textview), "key-press-event", G_CALLBACK(on_window_key_press_event), win );
                        g_signal_handler_block( G_OBJECT(textview), win->keypress_handler );
 
+                       gtk_widget_add_events( GTK_WIDGET(textview), GDK_BUTTON_RELEASE_MASK );
+                       win->mouse_click_handler = g_signal_connect_after( G_OBJECT(textview), "button-release-event", G_CALLBACK(on_window_button_release_event), win );
+                       g_signal_handler_block( G_OBJECT(textview), win->mouse_click_handler );
+
                        /* Create the styles available to the window stream */
                        style_init_textgrid(textbuffer);
                }
@@ -520,9 +529,14 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype,
                        win->keypress_handler = g_signal_connect( G_OBJECT(textview), "key-press-event", G_CALLBACK(on_window_key_press_event), win );
                        g_signal_handler_block( G_OBJECT(textview), win->keypress_handler );
 
+                       gtk_widget_add_events( GTK_WIDGET(textview), GDK_BUTTON_RELEASE_MASK );
+                       win->mouse_click_handler = g_signal_connect_after( G_OBJECT(textview), "button-release-event", G_CALLBACK(on_window_button_release_event), win );
+                       g_signal_handler_block( G_OBJECT(textview), win->mouse_click_handler );
+
                        win->insert_text_handler = g_signal_connect_after( G_OBJECT(textbuffer), "insert-text", G_CALLBACK(after_window_insert_text), win );
                        g_signal_handler_block( G_OBJECT(textbuffer), win->insert_text_handler );
 
+
                        /* Create an editable tag to indicate uneditable parts of the window
                        (for line input) */
                        gtk_text_buffer_create_tag(textbuffer, "uneditable", "editable", FALSE, "editable-set", TRUE, NULL);
index fd61ca310cf64131726861fcdc173eede3bfbe56..71bbb7436168cbbe368ae5827920e05f58d7c422 100644 (file)
@@ -7,6 +7,7 @@
 #include "stream.h"
 #include "input.h"
 #include "style.h"
+#include "hyperlink.h"
 
 
 enum InputRequestType
@@ -65,6 +66,10 @@ struct glk_window_struct
        /* Signal handlers */
        gulong keypress_handler;
        gulong insert_text_handler;
+       gulong mouse_click_handler;
+       gulong mouse_move_handler;
+       /* Window buffer */
+       GString *buffer;
 };
 
 #endif