+ /* 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);
+ break;
+ 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);
+ 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));
+ }
+
+ /* 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)
+ {
+ while(win->widget->allocation.width == 1 && win->widget->allocation.height == 1)
+ {
+ /* Release the GDK lock momentarily */
+ gdk_threads_leave();
+ gdk_threads_enter();
+ while(gtk_events_pending())
+ gtk_main_iteration();
+ }
+ win->width = (glui32)(win->widget->allocation.width / win->unit_width);
+ win->height = (glui32)(win->widget->allocation.height / win->unit_height);
+
+ /* Mark the cursor position */
+ GtkTextIter begin;
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
+ gtk_text_buffer_get_start_iter(buffer, &begin);
+ gtk_text_buffer_create_mark(buffer, "cursor_position", &begin, TRUE);
+
+ /* Fill the buffer with blanks and move the cursor to the upper left */
+ 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();
+
+ return win;
+}
+
+/**
+ * glk_window_close:
+ * @win: Window to close.
+ * @result: Pointer to a #stream_result_t in which to store the write count.
+ *
+ * Closes @win, which is pretty much exactly the opposite of opening a window.
+ * It is legal to close all your windows, or to close the root window (which is
+ * the same thing.)
+ *
+ * The @result argument is filled with the output character count of the window
+ * stream.
+ */
+void
+glk_window_close(winid_t win, stream_result_t *result)
+{
+ GNode* parent_node;
+
+ g_return_if_fail(win != NULL);
+
+ gdk_threads_enter();
+
+ switch(win->type)
+ {
+ case wintype_Blank:
+ gtk_widget_destroy(win->widget);
+ break;
+
+ case wintype_TextGrid:
+ case wintype_TextBuffer:
+ gtk_widget_destroy(win->frame);
+ /* 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);
+ }
+ break;
+
+ default:
+ g_warning("%s: unsupported window type", __func__);
+ 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 )
+ {
+ winid_t pair = (winid_t) parent_node->data;
+ if(parent_node->parent == NULL)
+ {
+ if(parent_node->next)
+ glk_data->root_window = parent_node->next;
+ else if(parent_node->prev)
+ glk_data->root_window = parent_node->prev;
+ } 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);
+ }
+
+ g_node_unlink(parent_node);
+ g_free(pair);
+ }
+
+ g_node_destroy(win->window_node);
+ g_free(win);
+
+ gdk_threads_leave();
+}
+
+/**
+ * glk_window_clear:
+ * @win: A window.
+ *
+ * Erases the window @win. The meaning of this depends on the window type.
+ *
+ * <itemizedlist>
+ * <listitem><para>
+ * Text buffer: This may do any number of things, such as delete all text in
+ * the window, or print enough blank lines to scroll all text beyond
+ * visibility, or insert a page-break marker which is treated specially by the
+ * display part of the library.
+ * </para></listitem>
+ * <listitem><para>
+ * Text grid: This will clear the window, filling all positions with blanks.
+ * The window cursor is moved to the top left corner (position 0,0).
+ * </para></listitem>
+ * <listitem><para>
+ * Graphics: Clears the entire window to its current background color.
+ * </para></listitem>
+ * <listitem><para>
+ * Other window types: No effect.
+ * </para></listitem>
+ * </itemizedlist>
+ *
+ * It is illegal to erase a window which has line input pending.
+ */
+void
+glk_window_clear(winid_t win)
+{
+ g_return_if_fail(win != NULL);
+ g_return_if_fail(win->input_request_type != INPUT_REQUEST_LINE && win->input_request_type != INPUT_REQUEST_LINE_UNICODE);
+
+ switch(win->type)
+ {
+ case wintype_Blank:
+ case wintype_Pair:
+ /* do nothing */
+ break;
+
+ case wintype_TextGrid:
+ /* fill the buffer with blanks */
+ {
+ gdk_threads_enter();
+
+ /* Manually put newlines at the end of each row of characters in the buffer; manual newlines make resizing the window's grid easier. */
+ gchar *blanks = g_strnfill(win->width, ' ');
+ gchar **blanklines = g_new0(gchar *, win->height + 1);
+ int count;
+ for(count = 0; count < win->height; count++)
+ blanklines[count] = blanks;
+ blanklines[win->height] = NULL;
+ gchar *text = g_strjoinv("\n", blanklines);
+ g_free(blanklines); /* not g_strfreev() */
+ g_free(blanks);
+
+ GtkTextBuffer *textbuffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
+ gtk_text_buffer_set_text(textbuffer, text, -1);
+ g_free(text);
+
+ GtkTextIter begin;
+ gtk_text_buffer_get_start_iter(textbuffer, &begin);
+ gtk_text_buffer_move_mark_by_name(textbuffer, "cursor_position", &begin);
+
+ gdk_threads_leave();
+ }
+ break;
+
+ case wintype_TextBuffer:
+ /* delete all text in the window */
+ {
+ gdk_threads_enter();
+
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
+ GtkTextIter start, end;
+ gtk_text_buffer_get_bounds(buffer, &start, &end);
+ gtk_text_buffer_delete(buffer, &start, &end);
+
+ gdk_threads_leave();
+ }
+ break;
+
+ default:
+ g_warning("glk_window_clear: unsupported window type");
+ }