Added a better default font, line spacing and margins
[rodin/chimara.git] / libchimara / window.c
index 223d7a24a71fca70089dbf43a97b80183bbed6fd..5e0ec5015ff95cfded035c18a4b5882d08499a5d 100644 (file)
@@ -73,8 +73,8 @@ glk_window_get_rock(winid_t win)
  * glk_window_get_type:
  * @win: A window.
  *
- * Returns @win's type, one of #wintype_Blank, #wintype_Pair,
- * #wintype_TextBuffer, #wintype_TextGrid, or #wintype_Graphics.
+ * Returns @win's type, one of %wintype_Blank, %wintype_Pair,
+ * %wintype_TextBuffer, %wintype_TextGrid, or %wintype_Graphics.
  *
  * Returns: The window's type.
  */
@@ -145,15 +145,15 @@ glk_window_get_root()
  * @split: The window to split to create the new window. Must be 0 if there
  * are no windows yet.
  * @method: Position of the new window and method of size computation. One of
- * #winmethod_Above, #winmethod_Below, #winmethod_Left, or #winmethod_Right
- * OR'ed with #winmethod_Fixed or #winmethod_Proportional. If @wintype is
- * #wintype_Blank, then #winmethod_Fixed is not allowed.
+ * %winmethod_Above, %winmethod_Below, %winmethod_Left, or %winmethod_Right
+ * OR'ed with %winmethod_Fixed or %winmethod_Proportional. If @wintype is
+ * %wintype_Blank, then %winmethod_Fixed is not allowed.
  * @size: Size of the new window, in percentage points if @method is
- * #winmethod_Proportional, otherwise in characters if @wintype is 
- * #wintype_TextBuffer or #wintype_TextGrid, or pixels if @wintype is
- * #wintype_Graphics.
- * @wintype: Type of the new window. One of #wintype_Blank, #wintype_TextGrid,
- * #wintype_TextBuffer, or #wintype_Graphics.
+ * %winmethod_Proportional, otherwise in characters if @wintype is 
+ * %wintype_TextBuffer or %wintype_TextGrid, or pixels if @wintype is
+ * %wintype_Graphics.
+ * @wintype: Type of the new window. One of %wintype_Blank, %wintype_TextGrid,
+ * %wintype_TextBuffer, or %wintype_Graphics.
  * @rock: The new window's rock value.
  *
  * Creates a new window. If there are no windows, the first three arguments are
@@ -218,12 +218,12 @@ glk_window_get_root()
  * |[ newwin = #glk_window_open(win, #winmethod_Below | #winmethod_Fixed, 5, #wintype_TextGrid, 0); ]|
  * 
  * Note that the meaning of the @size argument depends on the @method argument.
- * If the method is #winmethod_Fixed, it also depends on the @wintype argument.
+ * If the method is %winmethod_Fixed, it also depends on the @wintype argument.
  * The new window is then called the <quote>key window</quote> of this split,
  * because its window type determines how the split size is computed.
  * 
  * <note><para>
- *   For #winmethod_Proportional splits, you can still call the new window the
+ *   For %winmethod_Proportional splits, you can still call the new window the
  *   <quote>key window</quote>. But the key window is not important for
  *   proportional splits, because the size will always be computed as a simple
  *   ratio of the available space, not a fixed size of one child window.
@@ -454,6 +454,9 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype,
                        
                        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_text_view_set_pixels_inside_wrap( GTK_TEXT_VIEW(textview), 3 );
+                       gtk_text_view_set_left_margin( GTK_TEXT_VIEW(textview), 20 );
+                       gtk_text_view_set_right_margin( GTK_TEXT_VIEW(textview), 20 );
 
                        gtk_container_add( GTK_CONTAINER(scrolledwindow), textview );
                        gtk_widget_show_all(scrolledwindow);
@@ -561,33 +564,26 @@ 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->needs_rearrange = TRUE;
+       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));
        
-       gdk_threads_leave();
-       
-       /* For blank or pair windows, this is almost a no-op. For text grid and
-        text buffer windows, this will wait for GTK to draw the window. Otherwise,
-        opening a window and getting its size immediately will give you the wrong
-        size. */
-       glk_window_get_size(win, NULL, NULL);
-       
     /* For text grid windows, fill the buffer with blanks. */
     if(wintype == wintype_TextGrid)
     {
         /* Create the cursor position mark */
-               gdk_threads_enter();
         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);
-        gdk_threads_leave();
-               
-        /* Fill the buffer with blanks and move the cursor to the upper left */
-        glk_window_clear(win);
-    }
+       }
 
+       gdk_threads_leave();
+    glk_window_clear(win);
        return win;
 }
 
@@ -603,24 +599,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:
@@ -714,6 +702,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);
@@ -770,9 +760,11 @@ 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->needs_rearrange = TRUE;
+       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();
 }
 
@@ -829,6 +821,12 @@ glk_window_clear(winid_t win)
                case wintype_TextGrid:
                    /* fill the buffer with blanks */
                {
+                       /* Wait for the window's size to be updated */
+                       g_mutex_lock(glk_data->arrange_lock);
+                       if(glk_data->needs_rearrange)
+                               g_cond_wait(glk_data->rearranged, glk_data->arrange_lock);
+                       g_mutex_unlock(glk_data->arrange_lock);
+                       
                    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. */
@@ -878,12 +876,13 @@ glk_window_clear(winid_t win)
  * @win: A window.
  *
  * Sets the current stream to @win's window stream. It is exactly equivalent to
- * <code>#glk_stream_set_current(#glk_window_get_stream(@win))</code>.
+ * |[ #glk_stream_set_current(#glk_window_get_stream(@win)) ]| 
+ * See <link linkend="chimara-Streams">Streams</link>.
  */
 void
 glk_set_window(winid_t win)
 {
-       VALID_WINDOW_OR_NULL(win, return);
+       VALID_WINDOW(win, return);
        glk_stream_set_current( glk_window_get_stream(win) );
 }
 
@@ -988,18 +987,14 @@ glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr)
             break;
             
         case wintype_TextGrid:
+                       /* Wait until the window's size is current */
+                       g_mutex_lock(glk_data->arrange_lock);
+                       if(glk_data->needs_rearrange)
+                               g_cond_wait(glk_data->rearranged, glk_data->arrange_lock);
+                       g_mutex_unlock(glk_data->arrange_lock);
+                       
                        gdk_threads_enter();
-                       /* Wait for the window to be drawn, and then cache the width and height */
-                       gdk_window_process_all_updates();
-                       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();
-                   }
-                   
+                       /* Cache the width and height */
                        win->width = (glui32)(win->widget->allocation.width / win->unit_width);
                    win->height = (glui32)(win->widget->allocation.height / win->unit_height);
             gdk_threads_leave();
@@ -1011,26 +1006,13 @@ glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr)
             break;
             
         case wintype_TextBuffer:
-            /* TODO: Glk wants to be able to get its windows' sizes as soon as they are created, but GTK doesn't decide on their sizes until they are drawn. The drawing happens somewhere in an idle function. A good method would be to make an educated guess of the window's size using the ChimaraGlk widget's size. */
+            /* Wait until the window's size is current */
+                       g_mutex_lock(glk_data->arrange_lock);
+                       if(glk_data->needs_rearrange)
+                               g_cond_wait(glk_data->rearranged, glk_data->arrange_lock);
+                       g_mutex_unlock(glk_data->arrange_lock);
+                       
             gdk_threads_enter();
-            /*if(win->widget->allocation.width == 1 && win->widget->allocation.height == 1)
-            {
-                g_warning("glk_window_get_size: The Glk program requested the size of a window before it was allocated screen space by GTK. The window size is just an educated guess.");
-                guess the size from the parent window;
-                break;
-            } */
-            
-            /* Instead, we wait for GTK to draw the widget. This is probably very slow and should be fixed. */
-            gdk_window_process_all_updates();
-                       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();
-            }
-                
             if(widthptr != NULL)
                 *widthptr = (glui32)(win->widget->allocation.width / win->unit_width);
             if(heightptr != NULL)
@@ -1047,13 +1029,13 @@ glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr)
 /**
  * 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.
+ * @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.
+ * %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 &mdash; that is, it changes the 
@@ -1129,8 +1111,11 @@ glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keyw
 
        /* Tell GTK to rearrange the windows */
        gdk_threads_enter();
+       g_mutex_lock(glk_data->arrange_lock);
+       glk_data->needs_rearrange = TRUE;
+       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();
 }
 
@@ -1193,6 +1178,16 @@ glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos)
        VALID_WINDOW(win, return);
        g_return_if_fail(win->type == wintype_TextGrid);
        
+       /* Wait until the window's size is current */
+       g_mutex_lock(glk_data->arrange_lock);
+       if(glk_data->needs_rearrange)
+               g_cond_wait(glk_data->rearranged, glk_data->arrange_lock);
+       g_mutex_unlock(glk_data->arrange_lock);
+
+       /* 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)
        {