No need to actually cancel input requests when closing a window
[rodin/chimara.git] / libchimara / window.c
index 5f2bd5b349673bb2b46a755e46c3da8906c93240..bb5fde32cf5d6e168dc06a7b247b046ff46008b1 100644 (file)
@@ -374,6 +374,8 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype,
                 glui32 rock)
 {
        VALID_WINDOW_OR_NULL(split, return NULL);
+       g_return_val_if_fail(method == (method & (winmethod_DirMask | winmethod_DivisionMask)), NULL);
+       g_return_val_if_fail(!(((method & winmethod_DivisionMask) == winmethod_Proportional) && size > 100), NULL);     
 
        if(split == NULL && glk_data->root_window != NULL)
        {
@@ -559,7 +561,10 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype,
                glk_data->root_window = win->window_node;
        }
 
-       /* Set the window as a child of the Glk widget */
+       /* Set the window as a child of the Glk widget, don't trigger an arrange event */
+       g_mutex_lock(glk_data->arrange_lock);
+       glk_data->ignore_next_arrange_event = TRUE;
+       g_mutex_unlock(glk_data->arrange_lock);
        gtk_widget_set_parent(win->frame, GTK_WIDGET(glk_data->self));
        gtk_widget_queue_resize(GTK_WIDGET(glk_data->self));
        
@@ -601,24 +606,16 @@ remove_key_windows(GNode *node, winid_t closing_win)
 }
 
 /* Internal function: destroy this window's GTK widgets, window streams, 
- and those of all its children */
+ and those of all its children. GDK threads must be locked. */
 static void
 destroy_windows_below(winid_t win, stream_result_t *result)
 {
        switch(win->type)
        {
                case wintype_Blank:
-                       gdk_threads_enter();
-                       gtk_widget_unparent(win->widget);
-                       gdk_threads_leave();
-                       break;
-       
            case wintype_TextGrid:
                case wintype_TextBuffer:
-                       gdk_threads_enter();
                        gtk_widget_unparent(win->frame);
-                       gdk_threads_leave();
-                       /* TODO: Cancel all input requests */
                        break;
 
                case wintype_Pair:
@@ -712,6 +709,8 @@ glk_window_close(winid_t win, stream_result_t *result)
 {
        VALID_WINDOW(win, return);
        
+       gdk_threads_enter(); /* Prevent redraw while we're trashing the window */
+       
        /* If any pair windows have this window or its children as a key window,
         set their key window to NULL */
        g_node_traverse(glk_data->root_window, G_IN_ORDER, G_TRAVERSE_NON_LEAVES, -1, (GNodeTraverseFunc)remove_key_windows, win);
@@ -768,9 +767,10 @@ glk_window_close(winid_t win, stream_result_t *result)
        g_free(win);
 
        /* Schedule a redraw */
-       gdk_threads_enter();
+       g_mutex_lock(glk_data->arrange_lock);
+       glk_data->ignore_next_arrange_event = TRUE;
+       g_mutex_unlock(glk_data->arrange_lock);
        gtk_widget_queue_resize( GTK_WIDGET(glk_data->self) );
-       gdk_window_process_all_updates();
        gdk_threads_leave();
 }
 
@@ -1041,7 +1041,122 @@ glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr)
             ILLEGAL_PARAM("Unknown window type: %u", win->type);
     }
 }
+
+/**
+ * glk_window_set_arrangement:
+ * @win: a pair window to rearrange.
+ * @method: new method of size computation. One of #winmethod_Above, 
+ * #winmethod_Below, #winmethod_Left, or #winmethod_Right OR'ed with 
+ * #winmethod_Fixed or #winmethod_Proportional.
+ * @size: new size constraint, in percentage points if @method is
+ * #winmethod_Proportional, otherwise in characters if @win's type is 
+ * #wintype_TextBuffer or #wintype_TextGrid, or pixels if @win's type is
+ * #wintype_Graphics.
+ * @keywin: new key window, or %NULL to leave the key window unchanged.
+ *
+ * Changes the size of an existing split — that is, it changes the 
+ * constraint of a given pair window.
+ * 
+ * Consider the example above, where D has collapsed to zero height. Say D was a
+ * text buffer window. You could make a more useful layout by doing
+ * |[
+ * #winid_t o2;
+ * o2 = #glk_window_get_parent(d);
+ * glk_window_set_arrangement(o2, #winmethod_Above | #winmethod_Fixed, 3, d);
+ * ]|
+ * That would set D (the upper child of O2) to be O2's key window, and give it a
+ * fixed size of 3 rows.
+ * 
+ * If you later wanted to expand D, you could do
+ * |[ glk_window_set_arrangement(o2, #winmethod_Above | #winmethod_Fixed, 5, NULL); ]|
+ * That expands D to five rows. Note that, since O2's key window is already set 
+ * to D, it is not necessary to provide the @keywin argument; you can pass %NULL
+ * to mean <quote>leave the key window unchanged.</quote>
+ * 
+ * If you do change the key window of a pair window, the new key window 
+ * <emphasis>must</emphasis> be a descendant of that pair window. In the current
+ * example, you could change O2's key window to be A, but not B. The key window
+ * also cannot be a pair window itself.
+ * 
+ * |[ glk_window_set_arrangement(o2, #winmethod_Below | #winmethod_Fixed, 3, NULL); ]|
+ * This changes the constraint to be on the <emphasis>lower</emphasis> child of 
+ * O2, which is A. The key window is still D; so A would then be three rows high
+ * as measured in D's font, and D would get the rest of O2's space. That may not
+ * be what you want. To set A to be three rows high as measured in A's font, you
+ * would do
+ * |[ glk_window_set_arrangement(o2, #winmethod_Below | #winmethod_Fixed, 3, a); ]|
+ * 
+ * Or you could change O2 to a proportional split:
+ * |[ glk_window_set_arrangement(o2, #winmethod_Below | #winmethod_Proportional, 30, NULL); ]|
+ * or
+ * |[ glk_window_set_arrangement(o2, #winmethod_Above | #winmethod_Proportional, 70, NULL); ]|
+ * These do exactly the same thing, since 30&percnt; above is the same as 
+ * 70&percnt; below. You don't need to specify a key window with a proportional
+ * split, so the @keywin argument is %NULL. (You could actually specify either A
+ * or D as the key window, but it wouldn't affect the result.)
+ * 
+ * Whatever constraint you set, glk_window_get_size() will tell you the actual 
+ * window size you got.
+ * 
+ * Note that you can resize windows, but you can't flip or rotate them. You 
+ * can't move A above D, or change O2 to a vertical split where A is left or 
+ * right of D. 
+ * <note><para>
+ *   To get this effect you could close one of the windows, and re-split the 
+ *   other one with glk_window_open().
+ * </para></note>
+ */
+void
+glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keywin)
+{
+       VALID_WINDOW(win, return);
+       VALID_WINDOW_OR_NULL(keywin, return);
+       g_return_if_fail(win->type == wintype_Pair);
+       if(keywin)
+       {
+               g_return_if_fail(keywin->type != wintype_Pair);
+               g_return_if_fail(g_node_is_ancestor(win->window_node, keywin->window_node));
+       }
+       g_return_if_fail(method == (method & (winmethod_DirMask | winmethod_DivisionMask)));
+       g_return_if_fail(!(((method & winmethod_DivisionMask) == winmethod_Proportional) && size > 100));
+       
+       win->split_method = method;
+       win->constraint_size = size;
+       if(keywin)
+               win->key_window = keywin;
+
+       /* Tell GTK to rearrange the windows */
+       gdk_threads_enter();
+       g_mutex_lock(glk_data->arrange_lock);
+       glk_data->ignore_next_arrange_event = TRUE;
+       g_mutex_unlock(glk_data->arrange_lock);
+       gtk_widget_queue_resize(GTK_WIDGET(glk_data->self));
+       gdk_threads_leave();
+}
+
+/**
+ * glk_window_get_arrangement:
+ * @win: a pair window.
+ * @methodptr: return location for the constraint flags of @win, or %NULL.
+ * @sizeptr: return location for the constraint size of @win, or %NULL.
+ * @keywinptr: return location for the key window of @win, or %NULL.
+ *
+ * Queries the constraint of a given pair window.
+ */
+void
+glk_window_get_arrangement(winid_t win, glui32 *methodptr, glui32 *sizeptr, winid_t *keywinptr)
+{
+       VALID_WINDOW(win, return);
+       g_return_if_fail(win->type == wintype_Pair);
+       
+       if(methodptr)
+               *methodptr = win->split_method;
+       if(sizeptr)
+               *sizeptr = win->constraint_size;
+       if(keywinptr)
+               *keywinptr = win->key_window;
+}
+
 /**
  * glk_window_move_cursor:
  * @win: A text grid window.
@@ -1078,6 +1193,10 @@ glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos)
        VALID_WINDOW(win, return);
        g_return_if_fail(win->type == wintype_TextGrid);
        
+       /* Don't do anything if the window is shrunk down to nothing */
+       if(win->width == 0 || win->height == 0)
+               return;
+       
        /* Calculate actual position if cursor is moved past the right edge */
        if(xpos >= win->width)
        {