From 8e48218aa3a9423cc103122a338269e158f542e9 Mon Sep 17 00:00:00 2001 From: fliep Date: Sun, 3 May 2009 12:50:02 +0000 Subject: [PATCH] Fixed issue #1. Probably needs some more thorough testing. --- configure.ac | 2 + src/chimara-glk.c | 191 ++++++++++++++++++++++++++++++++++++++++++---- src/main.c | 2 +- src/window.c | 126 +++++++++++++----------------- src/window.h | 4 + 5 files changed, 237 insertions(+), 88 deletions(-) diff --git a/configure.ac b/configure.ac index 68ebb78..649fe2a 100644 --- a/configure.ac +++ b/configure.ac @@ -47,6 +47,8 @@ PKG_CHECK_MODULES([CHIMARA], [ gmodule-2.0 pango ]) +CHIMARA_LIBS="$CHIMARA_LIBS -lm" +AC_SUBST(CHIMARA_LIBS) # Libraries needed to build test programs PKG_CHECK_MODULES([TEST], [ gtk+-2.0 >= $GTK_REQUIRED_VERSION diff --git a/src/chimara-glk.c b/src/chimara-glk.c index 1a449b5..fd5d065 100644 --- a/src/chimara-glk.c +++ b/src/chimara-glk.c @@ -1,5 +1,6 @@ /* licensing and copyright information here */ +#include #include #include #include @@ -164,6 +165,59 @@ chimara_glk_finalize(GObject *object) G_OBJECT_CLASS(chimara_glk_parent_class)->finalize(object); } +/* Internal function: Recursively get the Glk window tree's size request */ +static void +request_recurse(winid_t win, GtkRequisition *requisition) +{ + if(win->type == wintype_Pair) + { + /* Get children's size requests */ + GtkRequisition child1, child2; + request_recurse(win->window_node->children->data, &child1); + request_recurse(win->window_node->children->next->data, &child2); + + /* If the split is fixed, get the size of the fixed child */ + if((win->split_method & winmethod_DivisionMask) == winmethod_Fixed) + { + switch(win->split_method & winmethod_DirMask) + { + case winmethod_Left: + child1.width = win->constraint_size * win->key_window->unit_width; + break; + case winmethod_Right: + child2.width = win->constraint_size * win->key_window->unit_width; + break; + case winmethod_Above: + child1.height = win->constraint_size * win->key_window->unit_height; + break; + case winmethod_Below: + child2.height = win->constraint_size * win->key_window->unit_height; + break; + } + } + + /* Add the children's requests */ + switch(win->split_method & winmethod_DirMask) + { + case winmethod_Left: + case winmethod_Right: + requisition->width = child1.width + child2.width; + requisition->height = MAX(child1.height, child2.height); + break; + case winmethod_Above: + case winmethod_Below: + requisition->width = MAX(child1.width, child2.width); + requisition->height = child1.height + child2.height; + break; + } + } + + /* For non-pair windows, just use the size that GTK requests */ + else + gtk_widget_size_request(win->frame, requisition); +} + +/* Overrides gtk_widget_size_request */ static void chimara_glk_size_request(GtkWidget *widget, GtkRequisition *requisition) { @@ -174,16 +228,109 @@ chimara_glk_size_request(GtkWidget *widget, GtkRequisition *requisition) ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(widget); /* For now, just pass the size request on to the root Glk window */ - if(priv->root_window) { - GtkWidget *child = ((winid_t)(priv->root_window->data))->frame; - if(GTK_WIDGET_VISIBLE(child)) - gtk_widget_size_request(child, requisition); - } else { + if(priv->root_window) + request_recurse(priv->root_window->data, requisition); + else { requisition->width = CHIMARA_GLK_MIN_WIDTH; requisition->height = CHIMARA_GLK_MIN_HEIGHT; } } +/* Recursively give the Glk windows their allocated space */ +static void +allocate_recurse(winid_t win, GtkAllocation *allocation) +{ + if(win->type == wintype_Pair) + { + GtkAllocation child1, child2; + child1.x = allocation->x; + child1.y = allocation->y; + + if((win->split_method & winmethod_DivisionMask) == winmethod_Fixed) + { + switch(win->split_method & winmethod_DirMask) + { + case winmethod_Left: + child1.width = win->constraint_size * win->key_window->unit_width; + if(child1.width > allocation->width) + child1.width = allocation->width; + break; + case winmethod_Right: + child2.width = win->constraint_size * win->key_window->unit_width; + if(child2.width > allocation->width) + child2.width = allocation->width; + break; + case winmethod_Above: + child1.height = win->constraint_size * win->key_window->unit_height; + if(child1.height > allocation->height) + child1.height = allocation->height; + break; + case winmethod_Below: + child2.height = win->constraint_size * win->key_window->unit_height; + if(child2.height > allocation->height) + child2.height = allocation->height; + break; + } + } + else /* proportional */ + { + switch(win->split_method & winmethod_DirMask) + { + case winmethod_Left: + child1.width = (glui32)ceil((win->constraint_size / 100.0) * allocation->width); + break; + case winmethod_Right: + child2.width = (glui32)ceil((win->constraint_size / 100.0) * allocation->width); + break; + case winmethod_Above: + child1.height = (glui32)ceil((win->constraint_size / 100.0) * allocation->height); + break; + case winmethod_Below: + child2.height = (glui32)ceil((win->constraint_size / 100.0) * allocation->height); + break; + } + } + + /* Fill in the rest of the size requisitions according to the child specified above */ + switch(win->split_method & winmethod_DirMask) + { + case winmethod_Left: + child2.width = allocation->width - child1.width; + child2.x = child1.x + child1.width; + child2.y = child1.y; + child1.height = child2.height = allocation->height; + break; + case winmethod_Right: + child1.width = allocation->width - child2.width; + child2.x = child1.x + child1.width; + child2.y = child1.y; + child1.height = child2.height = allocation->height; + break; + case winmethod_Above: + child2.height = allocation->height - child1.height; + child2.x = child1.x; + child2.y = child1.y + child1.height; + child1.width = child2.width = allocation->width; + break; + case winmethod_Below: + child1.height = allocation->height - child2.height; + child2.x = child1.x; + child2.y = child1.y + child1.height; + child1.width = child2.width = allocation->width; + break; + } + + /* Recurse */ + allocate_recurse(win->window_node->children->data, &child1); + allocate_recurse(win->window_node->children->next->data, &child2); + } + + /* For non-pair windows, just give them the size */ + else + gtk_widget_size_allocate(win->frame, allocation); +} + +/* Overrides gtk_widget_size_allocate */ static void chimara_glk_size_allocate(GtkWidget *widget, GtkAllocation *allocation) { @@ -195,26 +342,38 @@ chimara_glk_size_allocate(GtkWidget *widget, GtkAllocation *allocation) widget->allocation = *allocation; - if(priv->root_window) { - GtkWidget *child = ((winid_t)(priv->root_window->data))->frame; - if(GTK_WIDGET_VISIBLE(child)) - gtk_widget_size_allocate(child, allocation); - } + if(priv->root_window) + allocate_recurse(priv->root_window->data, allocation); +} + +/* Recursively invoke callback() on the GtkWidget of each non-pair window in the tree */ +static void +forall_recurse(winid_t win, GtkCallback callback, gpointer callback_data) +{ + if(win->type == wintype_Pair) + { + forall_recurse(win->window_node->children->data, callback, callback_data); + forall_recurse(win->window_node->children->next->data, callback, callback_data); + } + else + (*callback)(win->frame, callback_data); } +/* Overrides gtk_container_forall */ static void -chimara_glk_forall(GtkContainer *container, gboolean include_internals, - GtkCallback callback, gpointer callback_data) +chimara_glk_forall(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { g_return_if_fail(container); g_return_if_fail(CHIMARA_IS_GLK(container)); ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(container); - if(priv->root_window) { - GtkWidget *child = ((winid_t)(priv->root_window->data))->frame; - (*callback)(child, callback_data); - } + /* All the children are "internal" */ + if(!include_internals) + return; + + if(priv->root_window) + forall_recurse(priv->root_window->data, callback, callback_data); } static void diff --git a/src/main.c b/src/main.c index 3e04929..13de35c 100644 --- a/src/main.c +++ b/src/main.c @@ -117,7 +117,7 @@ main(int argc, char *argv[]) g_object_unref( G_OBJECT(builder) ); - if( !chimara_glk_run(CHIMARA_GLK(glk), ".libs/gridtest.so", &error) ) { + if( !chimara_glk_run(CHIMARA_GLK(glk), ".libs/first.so", &error) ) { error_dialog(GTK_WINDOW(window), error, "Error starting Glk library: "); return 1; } diff --git a/src/window.c b/src/window.c index d27e04d..6ae9f55 100644 --- a/src/window.c +++ b/src/window.c @@ -336,7 +336,7 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, gdk_threads_enter(); - /* We only create one window and don't support any more than that */ + /* Create the new window */ winid_t win = g_new0(struct glk_window_struct, 1); win->magic = MAGIC_WINDOW; win->rock = rock; @@ -406,6 +406,8 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, GtkWidget *textview = gtk_text_view_new(); GtkTextBuffer *textbuffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(textview) ); + gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC ); + gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR ); gtk_text_view_set_editable( GTK_TEXT_VIEW(textview), FALSE ); @@ -463,14 +465,18 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, /* When splitting, construct a new parent window * copying most characteristics from the window that is being split */ winid_t pair = g_new0(struct glk_window_struct, 1); + pair->magic = MAGIC_WINDOW; pair->rock = 0; pair->type = wintype_Pair; pair->window_node = g_node_new(pair); - pair->unit_width = split->unit_width; - pair->unit_height = split->unit_height; pair->window_stream = NULL; pair->echo_stream = NULL; + /* The pair window must know about its children's split method */ + pair->key_window = win; + pair->split_method = method; + pair->constraint_size = size; + /* Insert the new window into the window tree */ if(split->window_node->parent == NULL) { @@ -479,58 +485,30 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, g_node_append(split->window_node->parent, pair->window_node); g_node_unlink(split->window_node); } - - /* Keep track of the parent widget of the window that is being split */ - GtkWidget* old_parent = gtk_widget_get_parent(split->frame); - gtk_widget_ref(split->frame); - gtk_widget_unparent(split->frame); - /* Place the windows in the correct order */ switch(method & winmethod_DirMask) { case winmethod_Left: - pair->widget = gtk_hbox_new(FALSE, 0); - gtk_box_pack_end(GTK_BOX(pair->widget), split->frame, TRUE, TRUE, 0); - gtk_box_pack_end(GTK_BOX(pair->widget), win->frame, TRUE, TRUE, 0); - g_node_append(pair->window_node, split->window_node); - g_node_append(pair->window_node, win->window_node); - break; - case winmethod_Right: - pair->widget = gtk_hbox_new(FALSE, 0); - gtk_box_pack_end(GTK_BOX(pair->widget), win->frame, TRUE, TRUE, 0); - gtk_box_pack_end(GTK_BOX(pair->widget), split->frame, TRUE, TRUE, 0); - g_node_append(pair->window_node, win->window_node); - g_node_append(pair->window_node, split->window_node); - break; case winmethod_Above: - pair->widget = gtk_vbox_new(FALSE, 0); - gtk_box_pack_end(GTK_BOX(pair->widget), split->frame, TRUE, TRUE, 0); - gtk_box_pack_end(GTK_BOX(pair->widget), win->frame, TRUE, TRUE, 0); - g_node_append(pair->window_node, split->window_node); g_node_append(pair->window_node, win->window_node); + g_node_append(pair->window_node, split->window_node); break; + case winmethod_Right: case winmethod_Below: - pair->widget = gtk_vbox_new(FALSE, 0); - gtk_box_pack_end(GTK_BOX(pair->widget), win->frame, TRUE, TRUE, 0); - gtk_box_pack_end(GTK_BOX(pair->widget), split->frame, TRUE, TRUE, 0); - g_node_append(pair->window_node, win->window_node); g_node_append(pair->window_node, split->window_node); + g_node_append(pair->window_node, win->window_node); break; } - gtk_widget_unref(split->frame); - - /* TODO: set the new size of the windows */ - pair->frame = pair->widget; - gtk_widget_set_parent(pair->widget, old_parent); - gtk_widget_show(pair->widget); } else { /* Set the window as root window */ glk_data->root_window = win->window_node; - gtk_widget_set_parent(win->frame, GTK_WIDGET(glk_data->self)); - gtk_widget_queue_resize(GTK_WIDGET(glk_data->self)); } + /* Set the window as a child of the Glk widget */ + gtk_widget_set_parent(win->frame, GTK_WIDGET(glk_data->self)); + gtk_widget_queue_resize(GTK_WIDGET(glk_data->self)); + /* For text grid windows, wait until GTK draws the window (see note in glk_window_get_size() ), calculate the size and fill the buffer with blanks. */ if(wintype == wintype_TextGrid) { @@ -555,9 +533,6 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, gdk_threads_leave(); glk_window_clear(win); gdk_threads_enter(); - - /* Apparently this only works after the window has been realized */ - gtk_text_view_set_overwrite( GTK_TEXT_VIEW(win->widget), TRUE ); } gdk_threads_leave(); @@ -613,70 +588,78 @@ void glk_window_close(winid_t win, stream_result_t *result) { VALID_WINDOW(win, return); - - GNode* parent_node; - - gdk_threads_enter(); + /* First close the window stream before trashing the window tree */ + stream_close_common(win->window_stream, result); + switch(win->type) { case wintype_Blank: - gtk_widget_destroy(win->widget); + gdk_threads_enter(); + gtk_widget_unparent(win->widget); + gdk_threads_leave(); break; case wintype_TextGrid: case wintype_TextBuffer: - gtk_widget_destroy(win->frame); + gdk_threads_enter(); + gtk_widget_unparent(win->frame); + gdk_threads_leave(); /* TODO: Cancel all input requests */ break; case wintype_Pair: { - GNode* left_child = g_node_first_child(win->window_node); - GNode* right_child = g_node_last_child(win->window_node); - - glk_window_close((winid_t) left_child->data, result); - glk_window_close((winid_t) right_child->data, result); - - gtk_widget_destroy(win->widget); + GNode *left = win->window_node->children; + GNode *right = win->window_node->children->next; + glk_window_close(left->data, NULL); + glk_window_close(right->data, NULL); } break; default: ILLEGAL_PARAM("Unknown window type: %u", win->type); - gdk_threads_leave(); return; } - stream_close_common(win->window_stream, result); - - /* Parent window changes from a split window into the sibling window */ - if( (parent_node = win->window_node->parent) != NULL ) + /* Parent window changes from a split window into the sibling window */ + GNode *pair_node = win->window_node->parent; + g_node_destroy(win->window_node); + /* If win was not the root window, or was not unhooked from the tree: */ + if(pair_node != NULL) { - winid_t pair = (winid_t) parent_node->data; - if(parent_node->parent == NULL) + gboolean new_child_on_left = ( pair_node == g_node_first_sibling(pair_node) ); + GNode *sibling_node = pair_node->children; /* only one child left */ + GNode *new_parent_node = pair_node->parent; + g_node_unlink(pair_node); + g_node_unlink(sibling_node); + /* pair_node and sibling_node should now be totally unconnected to the tree */ + + if(new_parent_node == NULL) { - if(parent_node->next) - glk_data->root_window = parent_node->next; - else if(parent_node->prev) - glk_data->root_window = parent_node->prev; + glk_data->root_window = sibling_node; } else { - if(parent_node->next) - g_node_append(parent_node->parent, parent_node->next); - else if(parent_node->prev) - g_node_append(parent_node->parent, parent_node->prev); + if(new_child_on_left) + g_node_prepend(new_parent_node, sibling_node); + else + g_node_append(new_parent_node, sibling_node); } - g_node_unlink(parent_node); + winid_t pair = (winid_t) pair_node->data; + g_node_destroy(pair_node); + + pair->magic = MAGIC_FREE; g_free(pair); } - g_node_destroy(win->window_node); win->magic = MAGIC_FREE; g_free(win); + /* Schedule a redraw */ + gdk_threads_enter(); + gtk_widget_queue_resize( GTK_WIDGET(glk_data->self) ); gdk_threads_leave(); } @@ -884,6 +867,7 @@ glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) switch(win->type) { case wintype_Blank: + case wintype_Pair: if(widthptr != NULL) *widthptr = 0; if(heightptr != NULL) diff --git a/src/window.h b/src/window.h index e957de8..39c4314 100644 --- a/src/window.h +++ b/src/window.h @@ -46,6 +46,10 @@ struct glk_window_struct /* Width and height of the window, in characters (text grids only) */ glui32 width; glui32 height; + /* Window split data (pair windows only) */ + winid_t key_window; + glui32 split_method; + glui32 constraint_size; /* Input request stuff */ enum InputRequestType input_request_type; gchar *line_input_buffer; -- 2.30.2