Fixed issue #1. Probably needs some more thorough testing.
[rodin/chimara.git] / src / window.c
index f533cc73da389cc5d5879b29571561fb53af648c..6ae9f554341092dd1ff75d19d05431708d3829a7 100644 (file)
@@ -1,4 +1,5 @@
 #include "window.h"
+#include "magic.h"
 #include "chimara-glk-private.h"
 
 extern ChimaraGlkPrivate *glk_data;
@@ -22,6 +23,8 @@ extern ChimaraGlkPrivate *glk_data;
 winid_t
 glk_window_iterate(winid_t win, glui32 *rockptr)
 {
+       VALID_WINDOW_OR_NULL(win, return NULL);
+       
        GNode *retnode;
        
        if(win == NULL)
@@ -62,7 +65,7 @@ glk_window_iterate(winid_t win, glui32 *rockptr)
 glui32
 glk_window_get_rock(winid_t win)
 {
-       g_return_val_if_fail(win != NULL, 0);
+       VALID_WINDOW(win, return 0);
        return win->rock;
 }
 
@@ -78,7 +81,7 @@ glk_window_get_rock(winid_t win)
 glui32
 glk_window_get_type(winid_t win)
 {
-       g_return_val_if_fail(win != NULL, 0);
+       VALID_WINDOW(win, return 0);
        return win->type;
 }
 
@@ -96,7 +99,7 @@ glk_window_get_type(winid_t win)
 winid_t
 glk_window_get_parent(winid_t win)
 {
-       g_return_val_if_fail(win != NULL, NULL);
+       VALID_WINDOW(win, return NULL);
        /* Value will also be NULL if win is the root window */
        return (winid_t)win->window_node->parent->data;
 }
@@ -113,7 +116,7 @@ glk_window_get_parent(winid_t win)
 winid_t
 glk_window_get_sibling(winid_t win)
 {
-       g_return_val_if_fail(win != NULL, NULL);
+       VALID_WINDOW(win, return NULL);
        
        if(G_NODE_IS_ROOT(win->window_node))
                return NULL;
@@ -137,15 +140,6 @@ glk_window_get_root()
        return (winid_t)glk_data->root_window->data;
 }
 
-/* Determine the size of a "0" character in pixels */
-static void
-text_window_get_char_size(GtkWidget *textview, int *width, int *height)
-{
-    PangoLayout *zero = gtk_widget_create_pango_layout(textview, "0");
-    pango_layout_get_pixel_size(zero, width, height);
-    g_object_unref(zero);
-}
-
 /**
  * glk_window_open:
  * @split: The window to split to create the new window. Must be 0 if there
@@ -332,24 +326,19 @@ winid_t
 glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, 
                 glui32 rock)
 {
-       /*
-       if(split)
-       {
-               g_warning("glk_window_open: splitting of windows not implemented");
-               return NULL;
-       }
-       */
+       VALID_WINDOW_OR_NULL(split, return NULL);
 
        if(split == NULL && glk_data->root_window != NULL)
        {
-               g_warning("glk_window_open: there is already a root window");
+               ILLEGAL("Tried to open a new root window, but there is already a root window");
                return NULL;
        }
        
        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;
        win->type = wintype;
        win->window_node = g_node_new(win);
@@ -387,14 +376,16 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype,
                    gtk_widget_show_all(scrolledwindow);
                                
                        /* Set the window's font */
-                   /* TODO: Use Pango to pick out a monospace font on the system */
-                       PangoFontDescription *font = pango_font_description_from_string("Monospace");
-                       gtk_widget_modify_font(textview, font);
-                       pango_font_description_free(font);
+                       gtk_widget_modify_font(textview, glk_data->monospace_font_desc);
                    
                    win->widget = textview;
                    win->frame = scrolledwindow;
-                   text_window_get_char_size( textview, &(win->unit_width), &(win->unit_height) );
+                       
+                       /* Determine the size of a "0" character in pixels */
+                       PangoLayout *zero = gtk_widget_create_pango_layout(textview, "0");
+                       pango_layout_set_font_description(zero, glk_data->monospace_font_desc);
+                       pango_layout_get_pixel_size(zero, &(win->unit_width), &(win->unit_height));
+                       g_object_unref(zero);
                        
                        /* Set the other parameters (width and height are set later) */
                        win->window_stream = window_stream_new(win);
@@ -415,15 +406,25 @@ 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 );
 
                        gtk_container_add( GTK_CONTAINER(scrolledwindow), textview );
                        gtk_widget_show_all(scrolledwindow);
 
+                       /* Set the window's font */
+                       gtk_widget_modify_font(textview, glk_data->default_font_desc);
+                       
                        win->widget = textview;
                        win->frame = scrolledwindow;
-            text_window_get_char_size( textview, &(win->unit_width), &(win->unit_height) );
+                       
+                       /* Determine the size of a "0" character in pixels */
+                       PangoLayout *zero = gtk_widget_create_pango_layout(textview, "0");
+                       pango_layout_set_font_description(zero, glk_data->default_font_desc);
+                       pango_layout_get_pixel_size(zero, &(win->unit_width), &(win->unit_height));
+                       g_object_unref(zero);
                        
                        /* Set the other parameters */
                        win->window_stream = window_stream_new(win);
@@ -452,7 +453,7 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype,
                        
                default:
                        gdk_threads_leave();
-                       g_warning("%s: unsupported window type", __func__);
+                       ILLEGAL_PARAM("Unknown window type: %u", wintype);
                        g_free(win);
                        g_node_destroy(glk_data->root_window);
                        glk_data->root_window = NULL;
@@ -464,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)
                {
@@ -480,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)
     {
@@ -556,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,68 +587,79 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype,
 void
 glk_window_close(winid_t win, stream_result_t *result)
 {
-       GNode* parent_node;
-
-       g_return_if_fail(win != NULL);
-
-       gdk_threads_enter();
+       VALID_WINDOW(win, return);
 
+       /* 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:
-                       g_warning("%s: unsupported window type", __func__);
-                       gdk_threads_leave();
+                       ILLEGAL_PARAM("Unknown window type: %u", win->type);
                        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;
-               } 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);
+                       glk_data->root_window = sibling_node;
+               } 
+               else 
+               {
+                       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();
 }
 
@@ -718,7 +703,7 @@ glk_window_close(winid_t win, stream_result_t *result)
 void
 glk_window_clear(winid_t win)
 {
-       g_return_if_fail(win != NULL);
+       VALID_WINDOW(win, return);
        g_return_if_fail(win->input_request_type != INPUT_REQUEST_LINE && win->input_request_type != INPUT_REQUEST_LINE_UNICODE);
        
        switch(win->type)
@@ -771,7 +756,7 @@ glk_window_clear(winid_t win)
                        break;
                
                default:
-                       g_warning("glk_window_clear: unsupported window type");
+                       ILLEGAL_PARAM("Unknown window type: %d", win->type);
        }
 }
 
@@ -785,6 +770,7 @@ glk_window_clear(winid_t win)
 void
 glk_set_window(winid_t win)
 {
+       VALID_WINDOW_OR_NULL(win, return);
        glk_stream_set_current( glk_window_get_stream(win) );
 }
 
@@ -805,7 +791,7 @@ glk_set_window(winid_t win)
  */
 strid_t glk_window_get_stream(winid_t win)
 {
-       g_return_val_if_fail(win != NULL, NULL);
+       VALID_WINDOW(win, return NULL);
        return win->window_stream;
 }
 
@@ -826,7 +812,8 @@ strid_t glk_window_get_stream(winid_t win)
 void
 glk_window_set_echo_stream(winid_t win, strid_t str)
 {
-       g_return_if_fail(win != NULL);
+       VALID_WINDOW(win, return);
+       VALID_STREAM_OR_NULL(str, return);
        
        /* Test for an infinite loop */
        strid_t next = str;
@@ -834,7 +821,7 @@ glk_window_set_echo_stream(winid_t win, strid_t str)
        {
                if(next == win->window_stream)
                {
-                       g_warning("%s: Infinite loop detected", __func__);
+                       ILLEGAL("Infinite loop detected");
                        win->echo_stream = NULL;
                        return;
                }
@@ -855,7 +842,7 @@ glk_window_set_echo_stream(winid_t win, strid_t str)
 strid_t
 glk_window_get_echo_stream(winid_t win)
 {
-       g_return_val_if_fail(win != NULL, NULL);
+       VALID_WINDOW(win, return NULL);
        return win->echo_stream;
 }
 
@@ -875,11 +862,12 @@ glk_window_get_echo_stream(winid_t win)
 void
 glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr)
 {
-       g_return_if_fail(win != NULL);
+       VALID_WINDOW(win, return);
 
     switch(win->type)
     {
         case wintype_Blank:
+               case wintype_Pair:
             if(widthptr != NULL)
                 *widthptr = 0;
             if(heightptr != NULL)
@@ -923,7 +911,7 @@ glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr)
             break;
             
         default:
-            g_warning("glk_window_get_size: Unsupported window type");
+            ILLEGAL_PARAM("Unknown window type: %u", win->type);
     }
 }
  
@@ -960,7 +948,7 @@ glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr)
 void
 glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos)
 {
-       g_return_if_fail(win != NULL);
+       VALID_WINDOW(win, return);
        g_return_if_fail(win->type == wintype_TextGrid);
        
        /* Calculate actual position if cursor is moved past the right edge */