]> code.delx.au - gnu-emacs/blobdiff - src/window.c
(size_window): When setting the window's too_small_ok
[gnu-emacs] / src / window.c
index 2460e00bf5c044e057d055f9d44f7191c16b4caa..f2af5e3305ac05665b7a15416cb48d96bbadc167 100644 (file)
@@ -1,6 +1,7 @@
 /* Window creation, deletion and examination for GNU Emacs.
    Does not include redisplay.
-   Copyright (C) 1985,86,87,93,94,95,96,97,1998,2000 Free Software Foundation, Inc.
+   Copyright (C) 1985,86,87,93,94,95,96,97,1998,2000
+   Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -142,6 +143,10 @@ Lisp_Object Vpop_up_frame_function;
 
 Lisp_Object Vdisplay_buffer_function;
 
+/* Non-nil means that Fdisplay_buffer should even the heights of windows.  */
+
+Lisp_Object Veven_window_heights;
+
 /* List of buffer *names* for buffers that should have their own frames.  */
 
 Lisp_Object Vspecial_display_buffer_names;
@@ -239,6 +244,7 @@ make_window ()
   XSETFASTINT (p->height, 0);
   XSETFASTINT (p->width, 0);
   XSETFASTINT (p->hscroll, 0);
+  XSETFASTINT (p->min_hscroll, 0);
   p->orig_top = p->orig_height = Qnil;
   p->start = Fmake_marker ();
   p->pointm = Fmake_marker ();
@@ -299,10 +305,11 @@ DEFUN ("pos-visible-in-window-p", Fpos_visible_in_window_p,
   Spos_visible_in_window_p, 0, 3, 0,
   "Return t if position POS is currently on the frame in WINDOW.\n\
 Return nil if that position is scrolled vertically out of view.\n\
-If FULLY is non-nil, then only return t when POS is completely visible.\n\
+If a character is only partially visible, nil is returned, unless the\n\
+optional argument PARTIALLY is non-nil.\n\
 POS defaults to point in WINDOW; WINDOW defaults to the selected window.")
-  (pos, window, fully)
-     Lisp_Object pos, window, fully;
+  (pos, window, partially)
+     Lisp_Object pos, window, partially;
 {
   register struct window *w;
   register int posint;
@@ -336,9 +343,9 @@ POS defaults to point in WINDOW; WINDOW defaults to the selected window.")
         that info.  This doesn't work for POSINT == end pos, because
         the window end pos is actually the position _after_ the last
         char in the window.  */
-      if (!NILP (fully))
+      if (NILP (partially))
        {
-         pos_visible_p (w, posint, &fully_p, !NILP (fully));
+         pos_visible_p (w, posint, &fully_p, NILP (partially));
          in_window = fully_p ? Qt : Qnil;
        }
       else
@@ -351,8 +358,8 @@ POS defaults to point in WINDOW; WINDOW defaults to the selected window.")
     in_window = Qnil;
   else
     {
-      if (pos_visible_p (w, posint, &fully_p, !NILP (fully)))
-       in_window = NILP (fully) || fully_p ? Qt : Qnil;
+      if (pos_visible_p (w, posint, &fully_p, NILP (partially)))
+       in_window = !NILP (partially) || fully_p ? Qt : Qnil;
       else
        in_window = Qnil;
     }
@@ -411,17 +418,19 @@ DEFUN ("set-window-hscroll", Fset_window_hscroll, Sset_window_hscroll, 2, 2, 0,
   "Set number of columns WINDOW is scrolled from left margin to NCOL.\n\
 NCOL should be zero or positive.")
   (window, ncol)
-     register Lisp_Object window, ncol;
+     Lisp_Object window, ncol;
 {
-  register struct window *w;
+  struct window *w = decode_window (window);
+  int hscroll;
 
   CHECK_NUMBER (ncol, 1);
-  if (XINT (ncol) < 0) XSETFASTINT (ncol, 0);
-  w = decode_window (window);
-  if (XINT (w->hscroll) != XINT (ncol))
-    /* Prevent redisplay shortcuts */
+  hscroll = max (0, XINT (ncol));
+  
+  /* Prevent redisplay shortcuts when changing the hscroll.  */
+  if (XINT (w->hscroll) != hscroll)
     XBUFFER (w->buffer)->prevent_redisplay_optimizations_p = 1;
-  w->hscroll = ncol;
+  
+  w->hscroll = w->min_hscroll = make_number (hscroll);
   return ncol;
 }
 
@@ -491,21 +500,28 @@ coordinates_in_window (w, x, y)
      register struct window *w;
      register int *x, *y;
 {
-  struct frame *f = XFRAME (WINDOW_FRAME (w));
-  int left_x, right_x, top_y, bottom_y;
-  int flags_area_width = FRAME_LEFT_FLAGS_AREA_WIDTH (f);
-
   /* Let's make this a global enum later, instead of using numbers
      everywhere.  */
   enum {ON_NOTHING, ON_TEXT, ON_MODE_LINE, ON_VERTICAL_BORDER,
        ON_HEADER_LINE, ON_LEFT_FRINGE, ON_RIGHT_FRINGE};
 
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  int left_x, right_x, top_y, bottom_y;
+  int flags_area_width = FRAME_LEFT_FLAGS_AREA_WIDTH (f);
+  int part;
+  int ux = CANON_X_UNIT (f), uy = CANON_Y_UNIT (f);
+  int x0 = XFASTINT (w->left) * ux;
+  int x1 = x0 + XFASTINT (w->width) * ux;
+
+  if (*x < x0 || *x >= x1)
+    return ON_NOTHING;
+  
   /* In what's below, we subtract 1 when computing right_x because we
      want the rightmost pixel, which is given by left_pixel+width-1.  */
   if (w->pseudo_window_p)
     {
       left_x = 0;
-      right_x = XFASTINT (w->width) * CANON_Y_UNIT (f) - 1;
+      right_x = XFASTINT (w->width) * CANON_X_UNIT (f) - 1;
       top_y = WINDOW_DISPLAY_TOP_EDGE_PIXEL_Y (w);
       bottom_y = WINDOW_DISPLAY_BOTTOM_EDGE_PIXEL_Y (w);
     }
@@ -519,53 +535,64 @@ coordinates_in_window (w, x, y)
       bottom_y = WINDOW_DISPLAY_BOTTOM_EDGE_PIXEL_Y (w);
     }
 
-  /* Completely outside anything interesting?  */
-  if (*y < top_y
-      || *y >= bottom_y
-      || *x < (left_x
-              - flags_area_width
-              - (FRAME_LEFT_SCROLL_BAR_WIDTH (f)
-                 * CANON_X_UNIT (f)))
-      || *x > right_x + flags_area_width)
-    return ON_NOTHING;
-  
   /* On the mode line or header line?  If it's near the start of
      the mode or header line of window that's has a horizontal
      sibling, say it's on the vertical line.  That's to be able
      to resize windows horizontally in case we're using toolkit
      scroll bars.  */
-  
+
   if (WINDOW_WANTS_MODELINE_P (w)
-      && *y >= bottom_y - CURRENT_MODE_LINE_HEIGHT (w))
+      && *y >= bottom_y - CURRENT_MODE_LINE_HEIGHT (w)
+      && *y < bottom_y)
     {
-      if (!WINDOW_RIGHTMOST_P (w)
-         && (abs (*x - ((XFASTINT (w->left) + XFASTINT (w->width))
-                        * CANON_X_UNIT (f)))
-             < CANON_X_UNIT (f) / 2))
-       return ON_VERTICAL_BORDER;
-      return ON_MODE_LINE;
+      /* We're somewhere on the mode line.  We consider the place
+        between mode lines of horizontally adjacent mode lines
+        as the vertical border.    If scroll bars on the left,
+        return the right window.  */
+      part = ON_MODE_LINE;
+      
+      if (FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT (f))
+       {
+         if (abs (*x - x0) < ux / 2)
+           part = ON_VERTICAL_BORDER;
+       }
+      else if (!WINDOW_RIGHTMOST_P (w) && abs (*x - x1) < ux / 2)
+       part = ON_VERTICAL_BORDER;
     }
-  
-  if (WINDOW_WANTS_HEADER_LINE_P (w)
-      && *y < top_y + CURRENT_HEADER_LINE_HEIGHT (w))
+  else if (WINDOW_WANTS_HEADER_LINE_P (w)
+          && *y < top_y + CURRENT_HEADER_LINE_HEIGHT (w)
+          && *y >= top_y)
     {
-      if (!WINDOW_RIGHTMOST_P (w)
-         && (abs (*x - ((XFASTINT (w->left) + XFASTINT (w->width))
-                        * CANON_X_UNIT (f)))
-             < CANON_X_UNIT (f) / 2))
-       return ON_VERTICAL_BORDER;
-      return ON_HEADER_LINE;
+      part = ON_HEADER_LINE;
+      
+      if (FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT (f))
+       {
+         if (abs (*x - x0) < ux / 2)
+           part = ON_VERTICAL_BORDER;
+       }
+      else if (!WINDOW_RIGHTMOST_P (w) && abs (*x - x1) < ux / 2)
+       part = ON_VERTICAL_BORDER;
     }
-
-  if (FRAME_WINDOW_P (f))
+  /* Outside anything interesting?  */
+  else if (*y < top_y
+          || *y >= bottom_y
+          || *x < (left_x
+                   - flags_area_width
+                   - FRAME_LEFT_SCROLL_BAR_WIDTH (f) * ux)
+          || *x > right_x + flags_area_width)
+    {
+      part = ON_NOTHING;
+    }
+  else if (FRAME_WINDOW_P (f))
     {
       if (!w->pseudo_window_p
          && !FRAME_HAS_VERTICAL_SCROLL_BARS (f)
          && !WINDOW_RIGHTMOST_P (w)
-         && (abs (*x - right_x - flags_area_width) < CANON_X_UNIT (f) / 2))
-       return ON_VERTICAL_BORDER;
-
-      if (*x < left_x || *x > right_x)
+         && (abs (*x - right_x - flags_area_width) < ux / 2))
+       {
+         part = ON_VERTICAL_BORDER;
+       }
+      else if (*x < left_x || *x > right_x)
        {
          /* Other lines than the mode line don't include flags areas and
             scroll bars on the left.  */
@@ -573,7 +600,13 @@ coordinates_in_window (w, x, y)
          /* Convert X and Y to window-relative pixel coordinates.  */
          *x -= left_x;
          *y -= top_y;
-         return *x < left_x ? ON_LEFT_FRINGE : ON_RIGHT_FRINGE;
+         part = *x < left_x ? ON_LEFT_FRINGE : ON_RIGHT_FRINGE;
+       }
+      else
+       {
+         *x -= left_x;
+         *y -= top_y;
+         part = ON_TEXT;
        }
     }
   else
@@ -588,24 +621,30 @@ coordinates_in_window (w, x, y)
          /* Convert X and Y to window-relative pixel coordinates.  */
          *x -= left_x;
          *y -= top_y;
-         return *x < left_x ? ON_LEFT_FRINGE : ON_RIGHT_FRINGE;
+         part = *x < left_x ? ON_LEFT_FRINGE : ON_RIGHT_FRINGE;
        }
-  
       /* Here, too, "*x > right_x" is because of character terminals.  */
-      if (!w->pseudo_window_p
-         && !WINDOW_RIGHTMOST_P (w)
-         && *x > right_x - CANON_X_UNIT (f))
-       /* On the border on the right side of the window?  Assume that
-          this area begins at RIGHT_X minus a canonical char width.  */
-       return ON_VERTICAL_BORDER;
+      else if (!w->pseudo_window_p
+              && !WINDOW_RIGHTMOST_P (w)
+              && *x > right_x - ux)
+       {
+         /* On the border on the right side of the window?  Assume that
+            this area begins at RIGHT_X minus a canonical char width.  */
+         part = ON_VERTICAL_BORDER;
+       }
+      else
+       {
+         /* Convert X and Y to window-relative pixel coordinates.  */
+         *x -= left_x;
+         *y -= top_y;
+         part = ON_TEXT;
+       }
     }
-  
-  /* Convert X and Y to window-relative pixel coordinates.  */
-  *x -= left_x;
-  *y -= top_y;
-  return ON_TEXT;
+
+  return part;
 }
 
+
 DEFUN ("coordinates-in-window-p", Fcoordinates_in_window_p,
   Scoordinates_in_window_p, 2, 2, 0,
   "Return non-nil if COORDINATES are in WINDOW.\n\
@@ -890,6 +929,11 @@ DEFUN ("set-window-point", Fset_window_point, Sset_window_point, 2, 2, 0,
     Fgoto_char (pos);
   else
     set_marker_restricted (w->pointm, pos, w->buffer);
+
+  /* We have to make sure that redisplay updates the window to show
+     the new value of point.  */
+  if (!EQ (window, selected_window))
+    ++windows_or_buffers_changed;
   
   return pos;
 }
@@ -1120,7 +1164,7 @@ delete_window (window)
   register Lisp_Object tem, parent, sib;
   register struct window *p;
   register struct window *par;
-  FRAME_PTR frame;
+  struct frame *f;
 
   /* Because this function is called by other C code on non-leaf
      windows, the CHECK_LIVE_WINDOW macro would choke inappropriately,
@@ -1144,18 +1188,17 @@ delete_window (window)
 
   windows_or_buffers_changed++;
   Vwindow_list = Qnil;
-  frame = XFRAME (WINDOW_FRAME (p));
-  FRAME_WINDOW_SIZES_CHANGED (frame) = 1;
+  f = XFRAME (WINDOW_FRAME (p));
+  FRAME_WINDOW_SIZES_CHANGED (f) = 1;
 
   /* Are we trying to delete any frame's selected window?  */
   {
-    Lisp_Object frame, pwindow;
+    Lisp_Object pwindow;
 
     /* See if the frame's selected window is either WINDOW
        or any subwindow of it, by finding all that window's parents
        and comparing each one with WINDOW.  */
-    frame = WINDOW_FRAME (XWINDOW (window));
-    pwindow = FRAME_SELECTED_WINDOW (XFRAME (frame));
+    pwindow = FRAME_SELECTED_WINDOW (f);
 
     while (!NILP (pwindow))
       {
@@ -1177,7 +1220,7 @@ delete_window (window)
        if (EQ (window, selected_window))
          Fselect_window (alternative);
        else
-         FRAME_SELECTED_WINDOW (XFRAME (frame)) = alternative;
+         FRAME_SELECTED_WINDOW (f) = alternative;
       }
   }
 
@@ -1196,7 +1239,7 @@ delete_window (window)
      events and other events that access glyph matrices are not
      processed while we are changing them.  */
   BLOCK_INPUT;
-  free_window_matrices (XWINDOW (FRAME_ROOT_WINDOW (frame)));
+  free_window_matrices (XWINDOW (FRAME_ROOT_WINDOW (f)));
 
   tem = p->next;
   if (!NILP (tem))
@@ -1253,7 +1296,7 @@ delete_window (window)
   p->buffer = p->hchild = p->vchild = Qnil;
 
   /* Adjust glyph matrices. */
-  adjust_glyphs (frame);
+  adjust_glyphs (f);
   UNBLOCK_INPUT;
 }
 
@@ -1607,7 +1650,6 @@ MINIBUF neither nil nor t means never include the minibuffer window.")
   (frame, minibuf, window)
      Lisp_Object frame, minibuf, window;
 {
-  Lisp_Object list;
 
   if (NILP (window))
     window = selected_window;
@@ -2362,7 +2404,7 @@ size_window (window, size, width_p, nodelete_p)
       min_size = window_min_height;
     }
   
-  if (old_size < window_min_width)
+  if (old_size < min_size)
     w->too_small_ok = Qt;
 
   /* Maybe delete WINDOW if it's too small.  */
@@ -2567,6 +2609,7 @@ set_window_buffer (window, buffer, run_hooks_p)
   bzero (&w->last_cursor, sizeof w->last_cursor);
   w->window_end_valid = Qnil;
   XSETFASTINT (w->hscroll, 0);
+  XSETFASTINT (w->min_hscroll, 0);
   set_marker_both (w->pointm, buffer, BUF_PT (b), BUF_PT_BYTE (b));
   set_marker_restricted (w->start,
                         make_number (b->last_window_start),
@@ -2843,7 +2886,11 @@ If FRAME is a frame, search only that frame.\n\
 If FRAME is nil, search only the selected frame\n\
  (actually the last nonminibuffer frame),\n\
  unless `pop-up-frames' or `display-buffer-reuse-frames' is non-nil,\n\
- which means search visible and iconified frames.")
+ which means search visible and iconified frames.\n\
+\n\
+If `even-window-heights' is non-nil, window heights will be evened out\n\
+if displaying the buffer causes two vertically adjacent windows to be\n\
+displayed.")
   (buffer, not_this_window, frame)
      register Lisp_Object buffer, not_this_window, frame;
 {
@@ -2992,6 +3039,7 @@ If FRAME is nil, search only the selected frame\n\
          if (!NILP (XWINDOW (window)->next))
            other = lower = XWINDOW (window)->next, upper = window;
          if (!NILP (other)
+             && !NILP (Veven_window_heights)
              /* Check that OTHER and WINDOW are vertically arrayed.  */
              && !EQ (XWINDOW (other)->top, XWINDOW (window)->top)
              && (XFASTINT (XWINDOW (other)->height)
@@ -3041,6 +3089,7 @@ temp_output_buffer_show (buf)
       Vminibuf_scroll_window = window;
       w = XWINDOW (window);
       XSETFASTINT (w->hscroll, 0);
+      XSETFASTINT (w->min_hscroll, 0);
       set_marker_restricted_both (w->start, buf, 1, 1);
       set_marker_restricted_both (w->pointm, buf, 1, 1);
 
@@ -3873,13 +3922,14 @@ window_scroll_pixel_based (window, n, whole, noerror)
   Lisp_Object tem;
   int this_scroll_margin;
   int preserve_y;
+  /* True if we fiddled the window vscroll field without really scrolling.   */
+  int vscrolled = 0;
 
   SET_TEXT_POS_FROM_MARKER (start, w->start);
   
   /* If PT is not visible in WINDOW, move back one half of
      the screen.  */
-  XSETFASTINT (tem, PT);
-  tem = Fpos_visible_in_window_p (tem, window, Qnil);
+  tem = Fpos_visible_in_window_p (make_number (PT), window, Qnil);
   if (NILP (tem))
     {
       /* Move backward half the height of the window.  Performance note:
@@ -3923,7 +3973,16 @@ window_scroll_pixel_based (window, n, whole, noerror)
       int screen_full = (it.last_visible_y
                         - next_screen_context_lines * CANON_Y_UNIT (it.f));
       int direction = n < 0 ? -1 : 1;
-      move_it_vertically (&it, direction * screen_full);
+      int dy = direction * screen_full;
+
+      /* Note that move_it_vertically always moves the iterator to the
+         start of a line.  So, if the last line doesn't have a newline,
+        we would end up at the start of the line ending at ZV.  */
+      if (dy <= 0)
+       move_it_vertically_backward (&it, -dy);
+      else if (dy > 0)
+       move_it_to (&it, ZV, -1, it.current_y + dy, -1,
+                   MOVE_TO_POS | MOVE_TO_Y);
     }
   else
     move_it_by_lines (&it, n, 1);
@@ -3932,23 +3991,52 @@ window_scroll_pixel_based (window, n, whole, noerror)
   if ((n > 0 && IT_CHARPOS (it) == ZV)
       || (n < 0 && IT_CHARPOS (it) == CHARPOS (start)))
     {
-      if (noerror)
-       return;
-      else if (IT_CHARPOS (it) == ZV)
-       Fsignal (Qend_of_buffer, Qnil);
+      if (IT_CHARPOS (it) == ZV)
+       {
+         if (it.current_y + it.max_ascent + it.max_descent
+             > it.last_visible_y)
+           /* The last line was only partially visible, make it fully
+              visible.  */
+           w->vscroll = (it.last_visible_y
+                         - it.current_y + it.max_ascent + it.max_descent);
+         else if (noerror)
+           return;
+         else
+           Fsignal (Qend_of_buffer, Qnil);
+       }
       else
-       Fsignal (Qbeginning_of_buffer, Qnil);
+       {
+         if (w->vscroll != 0)
+           /* The first line was only partially visible, make it fully
+              visible. */
+           w->vscroll = 0;
+         else if (noerror)
+           return;
+         else
+           Fsignal (Qbeginning_of_buffer, Qnil);
+       }
+
+      /* If control gets here, then we vscrolled.  */
+
+      XBUFFER (w->buffer)->prevent_redisplay_optimizations_p = 1;
+
+      /* Don't try to change the window start below.  */
+      vscrolled = 1;
     }
 
-  /* Set the window start, and set up the window for redisplay.  */
-  set_marker_restricted (w->start, make_number (IT_CHARPOS (it)), w->buffer);
-  w->start_at_line_beg = Fbolp ();
-  w->update_mode_line = Qt;
-  XSETFASTINT (w->last_modified, 0);
-  XSETFASTINT (w->last_overlay_modified, 0);
-  /* Set force_start so that redisplay_window will run the
-     window-scroll-functions.  */
-  w->force_start = Qt;
+  if (! vscrolled)
+    {
+      /* Set the window start, and set up the window for redisplay.  */
+      set_marker_restricted (w->start, make_number (IT_CHARPOS (it)),
+                            w->buffer);
+      w->start_at_line_beg = Fbolp ();
+      w->update_mode_line = Qt;
+      XSETFASTINT (w->last_modified, 0);
+      XSETFASTINT (w->last_overlay_modified, 0);
+      /* Set force_start so that redisplay_window will run the
+        window-scroll-functions.  */
+      w->force_start = Qt;
+    }
   
   it.current_y = it.vpos = 0;
   
@@ -3976,18 +4064,30 @@ window_scroll_pixel_based (window, n, whole, noerror)
        }
       else if (n < 0)
        {
+         int charpos, bytepos;
+
          /* We moved the window start towards BEGV, so PT may be now
             in the scroll margin at the bottom.  */
          move_it_to (&it, PT, -1,
                      it.last_visible_y - this_scroll_margin - 1, -1,
                      MOVE_TO_POS | MOVE_TO_Y);
+
+         /* Save our position, in case it's correct.  */
+         charpos = IT_CHARPOS (it);
+         bytepos = IT_BYTEPOS (it);
       
-         /* Don't put point on a partially visible line at the end.  */
-         if (it.current_y + it.max_ascent + it.max_descent
-             > it.last_visible_y)
-           move_it_by_lines (&it, -1, 0);
-      
-         SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+         /* See if point is on a partially visible line at the end.  */
+         move_it_by_lines (&it, 1, 1);
+         if (it.current_y > it.last_visible_y)
+           /* The last line was only partially visible, so back up two
+              lines to make sure we're on a fully visible line.  */
+           {
+             move_it_by_lines (&it, -2, 0);
+             SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+           }
+         else
+           /* No, the position we saved is OK, so use it.  */
+           SET_PT_BOTH (charpos, bytepos);
        }
     }
 }
@@ -4416,23 +4516,40 @@ displayed_window_lines (w)
   if (old_buffer)
     set_buffer_internal (old_buffer);
 
-  /* Add in empty lines at the bottom of the window.  */
   bottom_y = it.current_y + it.max_ascent + it.max_descent;
+
+  if (bottom_y > it.current_y && bottom_y <= it.last_visible_y)
+    /* Hit a line without a terminating newline.  */
+    it.vpos++;
+
+  /* Add in empty lines at the bottom of the window.  */
   if (bottom_y < height)
     {
       struct frame *f = XFRAME (w->frame);
       int rest = height - bottom_y;
-      int lines = (rest + CANON_Y_UNIT (f) - 1) / CANON_Y_UNIT (f);
+      int lines = rest / CANON_Y_UNIT (f);
       it.vpos += lines;
     }
-  else if (it.current_y < height && bottom_y > height)
-    /* Partially visible line at the bottom.  */
-    ++it.vpos;
-  
+
   return it.vpos;
 }
 
 
+DEFUN ("window-text-height", Fwindow_text_height, Swindow_text_height,
+  0, 1, 0,
+  "Return the height in lines of the text display area of WINDOW.\n\
+This doesn't include the mode-line (or header-line if any) or any\n\
+partial-height lines in the text display area.")
+  (window)
+     Lisp_Object window;
+{
+  struct window *w = decode_window (window);
+  int pixel_height = window_box_height (w);
+  int line_height = pixel_height / CANON_Y_UNIT (XFRAME (w->frame));
+  return make_number (line_height);
+}
+
+
 \f
 DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line,
   1, 1, "P",
@@ -4470,6 +4587,10 @@ zero means top of window, negative means relative to bottom of window.")
        XSETINT (arg, XINT (arg) + lines);
     }
 
+  if (w->vscroll)
+    /* Skip past a partially visible first line.  */
+    XSETINT (arg, XINT (arg) + 1);
+
   return Fvertical_motion (arg, window);
 }
 
@@ -4501,20 +4622,21 @@ struct save_window_data
 
 /* This is saved as a Lisp_Vector  */
 struct saved_window
-  {
-    /* these first two must agree with struct Lisp_Vector in lisp.h */
-    EMACS_INT size_from_Lisp_Vector_struct;
-    struct Lisp_Vector *next_from_Lisp_Vector_struct;
+{
+  /* these first two must agree with struct Lisp_Vector in lisp.h */
+  EMACS_INT size_from_Lisp_Vector_struct;
+  struct Lisp_Vector *next_from_Lisp_Vector_struct;
 
-    Lisp_Object window;
-    Lisp_Object buffer, start, pointm, mark;
-    Lisp_Object left, top, width, height, hscroll;
-    Lisp_Object parent, prev;
-    Lisp_Object start_at_line_beg;
-    Lisp_Object display_table;
-    Lisp_Object orig_top, orig_height;
-  };
-#define SAVED_WINDOW_VECTOR_SIZE 16 /* Arg to Fmake_vector */
+  Lisp_Object window;
+  Lisp_Object buffer, start, pointm, mark;
+  Lisp_Object left, top, width, height, hscroll, min_hscroll;
+  Lisp_Object parent, prev;
+  Lisp_Object start_at_line_beg;
+  Lisp_Object display_table;
+  Lisp_Object orig_top, orig_height;
+};
+
+#define SAVED_WINDOW_VECTOR_SIZE 17 /* Arg to Fmake_vector */
 
 #define SAVED_WINDOW_N(swv,n) \
   ((struct saved_window *) (XVECTOR ((swv)->contents[(n)])))
@@ -4580,7 +4702,7 @@ the return value is nil.  Otherwise the value is t.")
 
   frame = XWINDOW (SAVED_WINDOW_N (saved_windows, 0)->window)->frame;
   f = XFRAME (frame);
-
+  
   /* If f is a dead frame, don't bother rebuilding its window tree.
      However, there is other stuff we should still try to do below.  */
   if (FRAME_LIVE_P (f))
@@ -4705,6 +4827,7 @@ the return value is nil.  Otherwise the value is t.")
          w->width = p->width;
          w->height = p->height;
          w->hscroll = p->hscroll;
+         w->min_hscroll = p->min_hscroll;
          w->display_table = p->display_table;
          w->orig_top = p->orig_top;
          w->orig_height = p->orig_height;
@@ -4807,7 +4930,6 @@ the return value is nil.  Otherwise the value is t.")
              xassert (NILP (leaf_windows[i]->hchild) 
                       && NILP (leaf_windows[i]->vchild));
              free_window_matrices (leaf_windows[i]);
-             SET_FRAME_GARBAGED (f);
            }
          else if (EQ (leaf_windows[i]->buffer, new_current_buffer))
            ++n;
@@ -4972,6 +5094,7 @@ save_window_save (window, vector, i)
       p->width = w->width;
       p->height = w->height;
       p->hscroll = w->hscroll;
+      p->min_hscroll = w->min_hscroll;
       p->display_table = w->display_table;
       p->orig_top = w->orig_top;
       p->orig_height = w->orig_height;
@@ -5400,6 +5523,8 @@ compare_window_configurations (c1, c2, ignore_positions)
        {
          if (! EQ (p1->hscroll, p2->hscroll))
            return 0;
+         if (!EQ (p1->min_hscroll, p2->min_hscroll))
+           return 0;
          if (! EQ (p1->start_at_line_beg, p2->start_at_line_beg))
            return 0;
          if (NILP (Fequal (p1->start, p2->start)))
@@ -5491,6 +5616,11 @@ Commands such as `switch-to-buffer-other-window' and `find-file-other-window'\n\
 work using this function.");
   Vdisplay_buffer_function = Qnil;
 
+  DEFVAR_LISP ("even-window-heights", &Veven_window_heights,
+    "*If non-nil, `display-buffer' should even the window heights.\n\
+If nil, `display-buffer' will leave the window configuration alone.");
+  Veven_window_heights = Qt;
+
   DEFVAR_LISP ("minibuffer-scroll-window", &Vminibuf_scroll_window,
     "Non-nil means it is the window that C-M-v in minibuffer should scroll.");
   Vminibuf_scroll_window = Qnil;
@@ -5676,6 +5806,7 @@ The selected frame is the one whose configuration has changed.");
   defsubr (&Sother_window_for_scrolling);
   defsubr (&Sscroll_other_window);
   defsubr (&Srecenter);
+  defsubr (&Swindow_text_height);
   defsubr (&Smove_to_window_line);
   defsubr (&Swindow_configuration_p);
   defsubr (&Swindow_configuration_frame);