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
{
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 */
-#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;
}
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);
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) );
#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);
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);
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. */
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);
}
}
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);
}
}
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));
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 */
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);
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);
}
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 */
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;
--- /dev/null
+#ifndef STRIO_H
+#define STRIO_H
+
+G_GNUC_INTERNAL void flush_window_buffer(winid_t win);
+
+#endif
*/
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);
}
}
}
+/**
+ * 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)
{
}
}
+/**
+ * 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)
{
#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);
win->line_input_buffer = NULL;
win->line_input_buffer_unicode = NULL;
+ /* Initialise the buffer */
+ win->buffer = g_string_sized_new(1024);
+
return win;
}
if(destroy_node)
g_node_destroy(win->window_node);
win->magic = MAGIC_FREE;
+
+ g_string_free(win->buffer, TRUE);
g_free(win);
}
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);
}
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);
#include "stream.h"
#include "input.h"
#include "style.h"
+#include "hyperlink.h"
enum InputRequestType
/* Signal handlers */
gulong keypress_handler;
gulong insert_text_handler;
+ gulong mouse_click_handler;
+ gulong mouse_move_handler;
+ /* Window buffer */
+ GString *buffer;
};
#endif