]> git.eshelyaron.com Git - emacs.git/commitdiff
(Qfixed_window_size): New.
authorGerd Moellmann <gerd@gnu.org>
Sun, 15 Aug 1999 22:00:53 +0000 (22:00 +0000)
committerGerd Moellmann <gerd@gnu.org>
Sun, 15 Aug 1999 22:00:53 +0000 (22:00 +0000)
(syms_of_window): Initialiaze it.
(check_all_windows): Add return type void.
(window_fixed_size_p): New.  Return non-zero if window
is fixed-size.
(window_min_size_1): New.
(window_min_size): Handle fixed-size windows.
(size_window): New. Rewritten combination of set_window_height and
set_window_width that handles fixed-size windows.
(set_window_height): Call it.
(set_window_width): Call it.
(Fsplit_window): Give an error on attempt to split a fixed-size
window.
(change_window_height): Partly rewritten to handle fixed-size
windows.

src/window.c

index 5153ea191e2f59e6e73d8fafb41df23e4c7cefe5..5c0abd6e93d7a88997a9df115356b7f0ab5ca8a5 100644 (file)
@@ -43,6 +43,8 @@ Boston, MA 02111-1307, USA.  */
 
 
 Lisp_Object Qwindowp, Qwindow_live_p, Qwindow_configuration_p;
+Lisp_Object Qfixed_window_size;
+extern Lisp_Object Qheight, Qwidth;
 
 static struct window *decode_window P_ ((Lisp_Object));
 static Lisp_Object select_window_1 P_ ((Lisp_Object, int));
@@ -51,7 +53,10 @@ static int get_leaf_windows P_ ((struct window *, struct window **, int));
 static void window_scroll P_ ((Lisp_Object, int, int, int));
 static void window_scroll_pixel_based P_ ((Lisp_Object, int, int, int));
 static void window_scroll_line_based P_ ((Lisp_Object, int, int, int));
-static int window_min_size P_ ((struct window *, int));
+static int window_min_size_1 P_ ((struct window *, int));
+static int window_min_size P_ ((struct window *, int, int *));
+static int window_fixed_size_p P_ ((struct window *, int, int));
+static void size_window P_ ((Lisp_Object, int, int, int));
 
 
 /* This is the window in which the terminal's cursor should
@@ -1655,6 +1660,7 @@ window_loop (type, obj, mini, frames)
 
 /* Used for debugging.  Abort if any window has a dead buffer.  */
 
+void
 check_all_windows ()
 {
   window_loop (CHECK_ALL_WINDOWS, Qnil, 1, Qt);
@@ -1885,189 +1891,412 @@ check_frame_size (frame, rows, cols)
 }
 
 
-/* Return the minimum size of window W.  WIDTH_P non-zero means
-   return the minimum width, otherwise return the minimum height.  */
+/* Value is non-zero if window W is fixed-size.  WIDTH_P non-zero means
+   check if W's width can be changed, otherwise check W's height.
+   CHECK_SIBLINGS_P non-zero means check resizablity of WINDOW's
+   siblings, too.  If none of the siblings is resizable, WINDOW isn't
+   either.  */
 
-static INLINE int
-window_min_size (w, width_p)
+static int
+window_fixed_size_p (w, width_p, check_siblings_p)
+     struct window *w;
+     int width_p, check_siblings_p;
+{
+  int fixed_p;
+  struct window *c;
+  
+  if (!NILP (w->hchild))
+    {
+      c = XWINDOW (w->hchild);
+      
+      if (width_p)
+       {
+         /* A horiz. combination is fixed-width if all of if its
+            children are.  */
+         while (c && window_fixed_size_p (c, width_p, 0))
+           c = WINDOWP (c->next) ? XWINDOW (c->next) : NULL;
+         fixed_p = c == NULL;
+       }
+      else
+       {
+         /* A horiz. combination is fixed-height if one of if its
+            children is.  */
+         while (c && !window_fixed_size_p (c, width_p, 0))
+           c = WINDOWP (c->next) ? XWINDOW (c->next) : NULL;
+         fixed_p = c != NULL;
+       }
+    }
+  else if (!NILP (w->vchild))
+    {
+      c = XWINDOW (w->vchild);
+      
+      if (width_p)
+       {
+         /* A vert. combination is fixed-width if one of if its
+            children is.  */
+         while (c && !window_fixed_size_p (c, width_p, 0))
+           c = WINDOWP (c->next) ? XWINDOW (c->next) : NULL;
+         fixed_p = c != NULL;
+       }
+      else
+       {
+         /* A vert. combination is fixed-height if all of if its
+            children are.  */
+         while (c && window_fixed_size_p (c, width_p, 0))
+           c = WINDOWP (c->next) ? XWINDOW (c->next) : NULL;
+         fixed_p = c == NULL;
+       }
+    }
+  else if (BUFFERP (w->buffer))
+    {
+      Lisp_Object val;
+      struct buffer *old = current_buffer;
+
+      current_buffer = XBUFFER (w->buffer);
+      val = find_symbol_value (Qfixed_window_size);
+      current_buffer = old;
+
+      fixed_p = 0;
+      if (!EQ (val, Qunbound))
+       {
+         fixed_p = !NILP (val);
+         
+         if (fixed_p
+             && ((EQ (val, Qheight) && width_p)
+                 || (EQ (val, Qwidth) && !width_p)))
+           fixed_p = 0;
+       }
+
+      /* Can't tell if this one is resizable without looking at
+        siblings.  If all siblings are fixed-size this one is too.  */
+      if (!fixed_p && check_siblings_p && WINDOWP (w->parent))
+       {
+         Lisp_Object child;
+         
+         for (child = w->prev; !NILP (child); child = XWINDOW (child)->prev)
+           if (!window_fixed_size_p (XWINDOW (child), width_p, 0))
+             break;
+
+         if (NILP (child))
+           for (child = w->next; !NILP (child); child = XWINDOW (child)->next)
+             if (!window_fixed_size_p (XWINDOW (child), width_p, 0))
+               break;
+
+         if (NILP (child))
+           fixed_p = 1;
+       }
+    }
+  else
+    fixed_p = 1;
+
+  return fixed_p;
+}
+  
+
+/* Return the minimum size of window W, not taking fixed-width windows
+   into account.  WIDTH_P non-zero means return the minimum width,
+   otherwise return the minimum height.  If W is a combination window,
+   compute the minimum size from the minimum sizes of W's children.  */
+
+static int
+window_min_size_1 (w, width_p)
      struct window *w;
      int width_p;
 {
+  struct window *c;
   int size;
   
-  if (width_p)
-    size = window_min_width;
+  if (!NILP (w->hchild))
+    {
+      c = XWINDOW (w->hchild);
+      size = 0;
+      
+      if (width_p)
+       {
+         /* The min width of a horizontal combination is
+            the sum of the min widths of its children.  */
+         while (c)
+           {
+             size += window_min_size_1 (c, width_p);
+             c = WINDOWP (c->next) ? XWINDOW (c->next) : NULL;
+           }
+       }
+      else
+       {
+         /* The min height a horizontal combination equals
+            the maximum of all min height of its children.  */
+         while (c)
+           {
+             int min_size = window_min_size_1 (c, width_p);
+             size = max (min_size, size);
+             c = WINDOWP (c->next) ? XWINDOW (c->next) : NULL;
+           }
+       }
+    }
+  else if (!NILP (w->vchild))
+    {
+      c = XWINDOW (w->vchild);
+      size = 0;
+      
+      if (width_p)
+       {
+         /* The min width of a vertical combination is
+            the maximum of the min widths of its children.  */
+         while (c)
+           {
+             int min_size = window_min_size_1 (c, width_p);
+             size = max (min_size, size);
+             c = WINDOWP (c->next) ? XWINDOW (c->next) : NULL;
+           }
+       }
+      else
+       {
+         /* The min height of a vertical combination equals
+            the sum of the min height of its children.  */
+         while (c)
+           {
+             size += window_min_size_1 (c, width_p);
+             c = WINDOWP (c->next) ? XWINDOW (c->next) : NULL;
+           }
+       }
+    }
   else
     {
-      if (MINI_WINDOW_P (w)
-         || (!WINDOW_WANTS_MODELINE_P (w)
-             && !WINDOW_WANTS_TOP_LINE_P (w)))
-       size = 1;
+      if (width_p)
+       size = window_min_width;
       else
-       size = window_min_height;
+       {
+         if (MINI_WINDOW_P (w)
+             || (!WINDOW_WANTS_MODELINE_P (w)
+                 && !WINDOW_WANTS_TOP_LINE_P (w)))
+           size = 1;
+         else
+           size = window_min_height;
+       }
     }
 
   return size;
 }
 
 
-/* Normally the window is deleted if it gets too small.  nodelete
-   nonzero means do not do this.  (The caller should check later and
-   do so if appropriate) */
+/* Return the minimum size of window W, taking fixed-size windows into
+   account.  WIDTH_P non-zero means return the minimum width,
+   otherwise return the minimum height.  Set *FIXED to 1 if W is
+   fixed-size unless FIXED is null.  */
 
-void
-set_window_height (window, height, nodelete)
+static int
+window_min_size (w, width_p, fixed)
+     struct window *w;
+     int width_p, *fixed;
+{
+  int size, fixed_p;
+
+  fixed_p = window_fixed_size_p (w, width_p, 1);
+  if (fixed)
+    *fixed = fixed_p;
+  
+  if (fixed_p)
+    size = width_p ? XFASTINT (w->width) : XFASTINT (w->height);
+  else
+    size = window_min_size_1 (w, width_p);
+
+  return size;
+}
+
+
+/* Set WINDOW's height or width to SIZE.  WIDTH_P non-zero means set
+   WINDOW's width.  Resize WINDOW's children, if any, so that they
+   keep their proportionate size relative to WINDOW.  Propagate
+   WINDOW's top or left edge position to children.  Delete windows
+   that become too small unless NODELETE_P is non-zero.  */
+
+static void
+size_window (window, size, width_p, nodelete_p)
      Lisp_Object window;
-     int height;
-     int nodelete;
+     int size, width_p, nodelete_p;
 {
-  register struct window *w = XWINDOW (window);
-  register struct window *c;
-  int oheight = XFASTINT (w->height);
-  int top, pos, lastbot, opos, lastobot;
-  Lisp_Object child;
+  struct window *w = XWINDOW (window);
+  struct window *c;
+  Lisp_Object child, *forward, *sideward;
+  int old_size, min_size;
 
   check_min_window_sizes ();
-
+  
   /* If the window has been "too small" at one point,
      don't delete it for being "too small" in the future.
      Preserve it as long as that is at all possible.  */
-  if (oheight < window_min_height)
+  if (width_p)
+    {
+      old_size = XFASTINT (w->width);
+      min_size = window_min_width;
+    }
+  else
+    {
+      old_size = XFASTINT (w->height);
+      min_size = window_min_height;
+    }
+  
+  if (old_size < window_min_width)
     w->too_small_ok = Qt;
 
-  if (!nodelete && !NILP (w->parent))
+  /* Maybe delete WINDOW if it's too small.  */
+  if (!nodelete_p && !NILP (w->parent))
     {
-      int min_height;
+      int min_size;
 
       if (!MINI_WINDOW_P (w) && !NILP (w->too_small_ok))
-       min_height = MIN_SAFE_WINDOW_HEIGHT;
+       min_size = width_p ? MIN_SAFE_WINDOW_WIDTH : MIN_SAFE_WINDOW_HEIGHT;
       else
-       min_height = window_min_size (w, 0);
+       min_size = width_p ? window_min_width : window_min_height;
       
-      if (height < min_height)
+      if (size < min_size)
        {
          delete_window (window);
          return;
        }
     }
 
+  /* Set redisplay hints.  */
   XSETFASTINT (w->last_modified, 0);
   XSETFASTINT (w->last_overlay_modified, 0);
   windows_or_buffers_changed++;
   FRAME_WINDOW_SIZES_CHANGED (XFRAME (WINDOW_FRAME (w))) = 1;
 
-  XSETFASTINT (w->height, height);
-  if (!NILP (w->hchild))
+  if (width_p)
+    {
+      sideward = &w->vchild;
+      forward = &w->hchild;
+      XSETFASTINT (w->width, size);
+    }
+  else
+    {
+      sideward = &w->hchild;
+      forward = &w->vchild;
+      XSETFASTINT (w->height, size);
+    }
+
+  if (!NILP (*sideward))
     {
-      for (child = w->hchild; !NILP (child); child = XWINDOW (child)->next)
+      for (child = *sideward; !NILP (child); child = c->next)
        {
-         XWINDOW (child)->top = w->top;
-         set_window_height (child, height, nodelete);
+         c = XWINDOW (child);
+         if (width_p)
+           c->left = w->left;
+         else
+           c->top = w->top;
+         size_window (child, size, width_p, nodelete_p);
        }
     }
-  else if (!NILP (w->vchild))
+  else if (!NILP (*forward))
     {
-      lastbot = top = XFASTINT (w->top);
-      lastobot = 0;
-      for (child = w->vchild; !NILP (child); child = c->next)
+      int fixed_size, each, extra, n;
+      int resize_fixed_p, nfixed;
+      int last_pos, first_pos, nchildren;
+
+      /* Determine the fixed-size portion of the this window, and the
+        number of child windows.  */
+      fixed_size = nchildren = nfixed = 0;
+      for (child = *forward; !NILP (child); child = c->next, ++nchildren)
        {
          c = XWINDOW (child);
+         if (window_fixed_size_p (c, width_p, 0))
+           {
+             fixed_size += (width_p
+                            ? XFASTINT (c->width) : XFASTINT (c->height));
+             ++nfixed;
+           }
+       }
 
-         opos = lastobot + XFASTINT (c->height);
-
-         XSETFASTINT (c->top, lastbot);
-
-         pos = (((opos * height) << 1) + oheight) / (oheight << 1);
+      /* If the new size is smaller than fixed_size, or if there
+        aren't any resizable windows, allow resizing fixed-size
+        windows.  */
+      resize_fixed_p = nfixed == nchildren || size < fixed_size;
+
+      /* Compute how many lines/columns to add to each child.  The
+        value of extra takes care of rounding errors.  */
+      n = resize_fixed_p ? nchildren : nchildren - nfixed;
+      each = (size - old_size) / n;
+      extra = (size - old_size) - n * each;
+
+      /* Compute new children heights and edge positions.  */
+      first_pos = width_p ? XFASTINT (w->left) : XFASTINT (w->top);
+      last_pos = first_pos;
+      for (child = *forward; !NILP (child); child = c->next)
+       {
+         int new_size, old_size;
+         
+         c = XWINDOW (child);
+         old_size = width_p ? XFASTINT (c->width) : XFASTINT (c->height);
+         new_size = old_size;
 
-         /* Avoid confusion: inhibit deletion of child if becomes too small */
-         set_window_height (child, pos + top - lastbot, 1);
+         /* The top or left edge position of this child equals the
+            bottom or right edge of its predecessor.  */
+         if (width_p)
+           c->left = make_number (last_pos);
+         else
+           c->top = make_number (last_pos);
 
-         /* Now advance child to next window,
-            and set lastbot if child was not just deleted.  */
-         lastbot = pos + top;
-         lastobot = opos;
+         /* If this child can be resized, do it.  */
+         if (resize_fixed_p || !window_fixed_size_p (c, width_p, 0))
+           {
+             new_size = old_size + each + extra;
+             extra = 0;
+           }
+         
+         /* Set new height.  Note that size_window also propagates
+            edge positions to children, so it's not a no-op if we
+            didn't change the child's size.  */
+         size_window (child, new_size, width_p, 1);
+
+         /* Remember the bottom/right edge position of this child; it
+            will be used to set the top/left edge of the next child.  */
+         last_pos += new_size;
        }
+
+      /* We should have covered the parent exactly with child windows.  */
+      xassert (size == last_pos - first_pos);
+      
       /* Now delete any children that became too small.  */
-      if (!nodelete)
-       for (child = w->vchild; !NILP (child); child = XWINDOW (child)->next)
+      if (!nodelete_p)
+       for (child = *forward; !NILP (child); child = c->next)
          {
-           set_window_height (child, XINT (XWINDOW (child)->height), 0);
+           int child_size;
+           c = XWINDOW (child);
+           child_size = width_p ? XFASTINT (c->width) : XFASTINT (c->height);
+           size_window (child, child_size, width_p, 0);
          }
     }
 }
 
-/* Recursively set width of WINDOW and its inferiors. */
+/* Set WINDOW's height to HEIGHT, and recursively change the height of
+   WINDOW's children.  NODELETE non-zero means don't delete windows
+   that become too small in the process.  (The caller should check
+   later and do so if appropriate.)  */
 
 void
-set_window_width (window, width, nodelete)
+set_window_height (window, height, nodelete)
      Lisp_Object window;
-     int width;
+     int height;
      int nodelete;
 {
-  register struct window *w = XWINDOW (window);
-  register struct window *c;
-  int owidth = XFASTINT (w->width);
-  int left, pos, lastright, opos, lastoright;
-  Lisp_Object child;
-
-  /* If the window has been "too small" at one point,
-     don't delete it for being "too small" in the future.
-     Preserve it as long as that is at all possible.  */
-  if (owidth < window_min_width)
-    w->too_small_ok = Qt;
-
-  if (!nodelete && !NILP (w->parent)
-      && (! NILP (w->too_small_ok)
-         ? width < MIN_SAFE_WINDOW_WIDTH
-         : width < window_min_width))
-    {
-      delete_window (window);
-      return;
-    }
-
-  XSETFASTINT (w->last_modified, 0);
-  XSETFASTINT (w->last_overlay_modified, 0);
-  windows_or_buffers_changed++;
-  FRAME_WINDOW_SIZES_CHANGED (XFRAME (WINDOW_FRAME (w))) = 1;
-
-  XSETFASTINT (w->width, width);
-  if (!NILP (w->vchild))
-    {
-      for (child = w->vchild; !NILP (child); child = XWINDOW (child)->next)
-       {
-         XWINDOW (child)->left = w->left;
-         set_window_width (child, width, nodelete);
-       }
-    }
-  else if (!NILP (w->hchild))
-    {
-      lastright = left = XFASTINT (w->left);
-      lastoright = 0;
-      for (child = w->hchild; !NILP (child); child = c->next)
-       {
-         c = XWINDOW (child);
-
-         opos = lastoright + XFASTINT (c->width);
-
-         XSETFASTINT (c->left, lastright);
+  size_window (window, height, 0, nodelete);
+}
 
-         pos = (((opos * width) << 1) + owidth) / (owidth << 1);
 
-         /* Inhibit deletion for becoming too small */
-         set_window_width (child, pos + left - lastright, 1);
+/* Set WINDOW's width to WIDTH, and recursively change the width of
+   WINDOW's children.  NODELETE non-zero means don't delete windows
+   that become too small in the process.  (The caller should check
+   later and do so if appropriate.)  */
 
-         /* Now advance child to next window,
-            and set lastright if child was not just deleted.  */
-         lastright = pos + left, lastoright = opos;
-       }
-      /* Delete children that became too small */
-      if (!nodelete)
-       for (child = w->hchild; !NILP (child); child = XWINDOW (child)->next)
-         {
-           set_window_width (child, XINT (XWINDOW (child)->width), 0);
-         }
-    }
+void
+set_window_width (window, width, nodelete)
+     Lisp_Object window;
+     int width;
+     int nodelete;
+{
+  size_window (window, width, 1, nodelete);
 }
+
 \f
 int window_select_count;
 
@@ -2677,6 +2906,8 @@ SIZE includes that window's scroll bar, or the divider column to its right.")
 
   if (MINI_WINDOW_P (o))
     error ("Attempt to split minibuffer window");
+  else if (window_fixed_size_p (o, !NILP (horflag), 0))
+    error ("Attempt to split fixed-size window");
 
   check_min_window_sizes ();
 
@@ -2810,42 +3041,51 @@ window_width (window)
 #define CURSIZE(w) \
   *(widthflag ? (int *) &(XWINDOW (w)->width) : (int *) &(XWINDOW (w)->height))
 
-/* Unlike set_window_height, this function
-   also changes the heights of the siblings so as to
-   keep everything consistent. */
+
+/* Enlarge selected_window by DELTA.  WIDTHFLAG non-zero means
+   increase its width.  Siblings of the selected window are resized to
+   fullfil the size request.  If they become too small in the process,
+   they will be deleted.  */
 
 void
 change_window_height (delta, widthflag)
-     register int delta;
-     int widthflag;
+     int delta, widthflag;
 {
-  register Lisp_Object parent;
-  Lisp_Object window;
-  register struct window *p;
-  int *sizep;
+  Lisp_Object parent, window, next, prev;
+  struct window *p;
+  int *sizep, maximum;
   int (*sizefun) P_ ((Lisp_Object))
     = widthflag ? window_width : window_height;
-  register void (*setsizefun) P_ ((Lisp_Object, int, int))
+  void (*setsizefun) P_ ((Lisp_Object, int, int))
     = (widthflag ? set_window_width : set_window_height);
-  int maximum;
-  Lisp_Object next, prev;
 
+  /* Check values of window_min_width and window_min_height for
+     validity.  */
   check_min_window_sizes ();
 
+  /* Give up if this window cannot be resized.  */
   window = selected_window;
+  if (window_fixed_size_p (XWINDOW (window), widthflag, 1))
+    error ("Window is not resizable");
+
+  /* Find the parent of the selected window.  */
   while (1)
     {
       p = XWINDOW (window);
       parent = p->parent;
+      
       if (NILP (parent))
        {
          if (widthflag)
            error ("No other window to side of this one");
          break;
        }
-      if (widthflag ? !NILP (XWINDOW (parent)->hchild)
+      
+      if (widthflag
+         ? !NILP (XWINDOW (parent)->hchild)
          : !NILP (XWINDOW (parent)->vchild))
        break;
+      
       window = parent;
     }
 
@@ -2857,10 +3097,10 @@ change_window_height (delta, widthflag)
     maxdelta = (!NILP (parent) ? (*sizefun) (parent) - *sizep
                : !NILP (p->next) ? ((*sizefun) (p->next)
                                     - window_min_size (XWINDOW (p->next),
-                                                       widthflag))
+                                                       widthflag, 0))
                : !NILP (p->prev) ? ((*sizefun) (p->prev)
                                     - window_min_size (XWINDOW (p->prev),
-                                                       widthflag))
+                                                       widthflag, 0))
                /* This is a frame with only one window, a minibuffer-only
                   or a minibufferless frame.  */
                : (delta = 0));
@@ -2872,7 +3112,7 @@ change_window_height (delta, widthflag)
       delta = maxdelta;
   }
 
-  if (*sizep + delta < window_min_size (XWINDOW (window), widthflag))
+  if (*sizep + delta < window_min_size (XWINDOW (window), widthflag, 0))
     {
       delete_window (window);
       return;
@@ -2885,16 +3125,17 @@ change_window_height (delta, widthflag)
   maximum = 0;
   for (next = p->next; ! NILP (next); next = XWINDOW (next)->next)
     maximum += (*sizefun) (next) - window_min_size (XWINDOW (next),
-                                                   widthflag);
+                                                   widthflag, 0);
   for (prev = p->prev; ! NILP (prev); prev = XWINDOW (prev)->prev)
     maximum += (*sizefun) (prev) - window_min_size (XWINDOW (prev),
-                                                   widthflag);
+                                                   widthflag, 0);
 
   /* If we can get it all from them, do so.  */
   if (delta <= maximum)
     {
       Lisp_Object first_unaffected;
       Lisp_Object first_affected;
+      int fixed_p;
 
       next = p->next;
       prev = p->prev;
@@ -2902,42 +3143,54 @@ change_window_height (delta, widthflag)
       /* Look at one sibling at a time,
         moving away from this window in both directions alternately,
         and take as much as we can get without deleting that sibling.  */
-      while (delta != 0)
+      while (delta != 0 && (!NILP (next) || !NILP (prev)))
        {
-         if (delta == 0)
-           break;
          if (! NILP (next))
            {
              int this_one = ((*sizefun) (next)
-                             - window_min_size (XWINDOW (next), widthflag));
-             if (this_one > delta)
-               this_one = delta;
-
-             (*setsizefun) (next, (*sizefun) (next) - this_one, 0);
-             (*setsizefun) (window, *sizep + this_one, 0);
+                             - window_min_size (XWINDOW (next),
+                                                widthflag, &fixed_p));
+             if (!fixed_p)
+               {
+                 if (this_one > delta)
+                   this_one = delta;
+                 
+                 (*setsizefun) (next, (*sizefun) (next) - this_one, 0);
+                 (*setsizefun) (window, *sizep + this_one, 0);
 
-             delta -= this_one;
+                 delta -= this_one;
+               }
+             
              next = XWINDOW (next)->next;
            }
+         
          if (delta == 0)
            break;
+         
          if (! NILP (prev))
            {
              int this_one = ((*sizefun) (prev)
-                             - window_min_size (XWINDOW (prev), widthflag));
-             if (this_one > delta)
-               this_one = delta;
-
-             first_affected = prev;
-
-             (*setsizefun) (prev, (*sizefun) (prev) - this_one, 0);
-             (*setsizefun) (window, *sizep + this_one, 0);
-
-             delta -= this_one;
+                             - window_min_size (XWINDOW (prev),
+                                                widthflag, &fixed_p));
+             if (!fixed_p)
+               {
+                 if (this_one > delta)
+                   this_one = delta;
+                 
+                 first_affected = prev;
+                 
+                 (*setsizefun) (prev, (*sizefun) (prev) - this_one, 0);
+                 (*setsizefun) (window, *sizep + this_one, 0);
+
+                 delta -= this_one;
+               }
+             
              prev = XWINDOW (prev)->prev;
            }
        }
 
+      xassert (delta == 0);
+
       /* Now recalculate the edge positions of all the windows affected,
         based on the new sizes.  */
       first_unaffected = next;
@@ -2961,12 +3214,49 @@ change_window_height (delta, widthflag)
         all the siblings end up with less than one line and are deleted.  */
       if (opht <= *sizep + delta)
        delta1 = opht * opht * 2;
-      /* Otherwise, make delta1 just right so that if we add delta1
-        lines to this window and to the parent, and then shrink
-        the parent back to its original size, the new proportional
-        size of this window will increase by delta.  */
       else
-       delta1 = (delta * opht * 100) / ((opht - *sizep - delta) * 100);
+       {
+         /* Otherwise, make delta1 just right so that if we add
+            delta1 lines to this window and to the parent, and then
+            shrink the parent back to its original size, the new
+            proportional size of this window will increase by delta.
+
+            The function size_window will compute the new height h'
+            of the window from delta1 as:
+            
+            e = delta1/n
+            x = delta1 - delta1/n * n for the 1st resizable child
+            h' = h + e + x
+
+            where n is the number of children that can be resized.
+            We can ignore x by choosing a delta1 that is a multiple of
+            n.  We want the height of this window to come out as
+            
+            h' = h + delta
+
+            So, delta1 must be
+            
+            h + e = h + delta
+            delta1/n = delta
+            delta1 = n * delta.
+
+            The number of children n rquals the number of resizable
+            children of this window + 1 because we know window itself
+            is resizable (otherwise we would have signalled an error.  */
+
+         struct window *w = XWINDOW (window);
+         Lisp_Object s;
+         int n = 1;
+
+         for (s = w->next; !NILP (s); s = XWINDOW (s)->next)
+           if (!window_fixed_size_p (XWINDOW (s), widthflag, 0))
+             ++n;
+         for (s = w->prev; !NILP (s); s = XWINDOW (s)->prev)
+           if (!window_fixed_size_p (XWINDOW (s), widthflag, 0))
+             ++n;
+
+         delta1 = n * delta;
+       }
 
       /* Add delta1 lines or columns to this window, and to the parent,
         keeping things consistent while not affecting siblings.  */
@@ -4514,6 +4804,9 @@ init_window_once ()
 void
 syms_of_window ()
 {
+  Qfixed_window_size = intern ("fixed-window-size");
+  staticpro (&Qfixed_window_size);
+  
   staticpro (&Qwindow_configuration_change_hook);
   Qwindow_configuration_change_hook
     = intern ("window-configuration-change-hook");