]> 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 b408c40f8fdec7393298d7f9f3e6c980417b67a4..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.
 
@@ -79,6 +80,7 @@ static void decode_next_window_args P_ ((Lisp_Object *, Lisp_Object *,
 static int foreach_window_1 P_ ((struct window *,
                                 int (* fn) (struct window *, void *),
                                 void *));
+static Lisp_Object window_list_1 P_ ((Lisp_Object, Lisp_Object, Lisp_Object));
 
 /* This is the window in which the terminal's cursor should
    be left when nothing is being done with it.  This must
@@ -141,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;
@@ -238,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 ();
@@ -298,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;
@@ -335,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);
+         pos_visible_p (w, posint, &fully_p, NILP (partially));
          in_window = fully_p ? Qt : Qnil;
        }
       else
@@ -350,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))
-       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;
     }
@@ -410,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;
 }
 
@@ -490,16 +500,28 @@ coordinates_in_window (w, x, y)
      register struct window *w;
      register int *x, *y;
 {
+  /* 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);
     }
@@ -513,51 +535,116 @@ coordinates_in_window (w, x, y)
       bottom_y = WINDOW_DISPLAY_BOTTOM_EDGE_PIXEL_Y (w);
     }
 
-  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)
-    /* Completely outside anything interesting.  */
-    return 0;
-  else if (WINDOW_WANTS_MODELINE_P (w)
-          && *y >= bottom_y - CURRENT_MODE_LINE_HEIGHT (w))
-    /* On the mode line.  */
-    return 2;
+  /* 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)
+    {
+      /* 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;
+    }
   else if (WINDOW_WANTS_HEADER_LINE_P (w)
-          && *y < top_y + CURRENT_HEADER_LINE_HEIGHT (w))
-    /* On the top line.  */
-    return 4;
-  /* Need to say "*x > right_x" rather than >=, since on character
-     terminals, the vertical line's x coordinate is right_x.  */
-  else if (*x < left_x || *x > right_x)
+          && *y < top_y + CURRENT_HEADER_LINE_HEIGHT (w)
+          && *y >= top_y)
     {
-      /* Other lines than the mode line don't include flags areas and
-        scroll bars on the left.  */
+      part = ON_HEADER_LINE;
       
-      /* Convert X and Y to window-relative pixel coordinates.  */
-      *x -= left_x;
-      *y -= top_y;
-      return *x < left_x ? 5 : 6;
+      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;
+    }
+  /* 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) < 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.  */
+      
+         /* Convert X and Y to window-relative pixel coordinates.  */
+         *x -= left_x;
+         *y -= top_y;
+         part = *x < left_x ? ON_LEFT_FRINGE : ON_RIGHT_FRINGE;
+       }
+      else
+       {
+         *x -= left_x;
+         *y -= top_y;
+         part = ON_TEXT;
+       }
     }
-  /* Here, too, "*x > right_x" is because of character terminals.  */
-  else 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 3;
   else
     {
-      /* Convert X and Y to window-relative pixel coordinates.  */
-      *x -= left_x;
-      *y -= top_y;
-      return 1;
+      /* Need to say "*x > right_x" rather than >=, since on character
+        terminals, the vertical line's x coordinate is right_x.  */
+      if (*x < left_x || *x > right_x)
+       {
+         /* Other lines than the mode line don't include flags areas and
+            scroll bars on the left.  */
+      
+         /* Convert X and Y to window-relative pixel coordinates.  */
+         *x -= left_x;
+         *y -= top_y;
+         part = *x < left_x ? ON_LEFT_FRINGE : ON_RIGHT_FRINGE;
+       }
+      /* Here, too, "*x > right_x" is because of character terminals.  */
+      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;
+       }
     }
+
+  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\
@@ -842,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;
 }
@@ -1072,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,
@@ -1096,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))
       {
@@ -1129,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;
       }
   }
 
@@ -1148,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))
@@ -1205,7 +1296,7 @@ delete_window (window)
   p->buffer = p->hchild = p->vchild = Qnil;
 
   /* Adjust glyph matrices. */
-  adjust_glyphs (frame);
+  adjust_glyphs (f);
   UNBLOCK_INPUT;
 }
 
@@ -1549,9 +1640,34 @@ argument ALL_FRAMES is non-nil, cycle through all frames.")
 
 
 DEFUN ("window-list", Fwindow_list, Swindow_list, 0, 3, 0,
-  "Return a list of windows in canonical ordering.\n\
-Arguments are like for `next-window'.")
-  (window, minibuf, all_frames)
+  "Return a list of windows on FRAME, starting with WINDOW.\n\
+FRAME nil or omitted means use the selected frame.\n\
+WINDOW nil or omitted means use the selected window.\n\
+MINIBUF t means include the minibuffer window, even if it isn't active.\n\
+MINIBUF nil or omitted means include the minibuffer window only\n\
+if it's active.\n\
+MINIBUF neither nil nor t means never include the minibuffer window.")
+  (frame, minibuf, window)
+     Lisp_Object frame, minibuf, window;
+{
+
+  if (NILP (window))
+    window = selected_window;
+  if (NILP (frame))
+    frame = selected_frame;
+
+  if (!EQ (frame, XWINDOW (window)->frame))
+    error ("Window is on a different frame");
+
+  return window_list_1 (window, minibuf, frame);
+}
+
+
+/* Return a list of windows in canonical ordering.  Arguments are like
+   for `next-window'.  */
+
+static Lisp_Object
+window_list_1 (window, minibuf, all_frames)
      Lisp_Object window, minibuf, all_frames;
 {
   Lisp_Object tail, list;
@@ -1637,7 +1753,7 @@ window_loop (type, obj, mini, frames)
      We can't just wait until we hit the first window again, because
      it might be deleted.  */
 
-  windows = Fwindow_list (window, mini ? Qt : Qnil, frame_arg);
+  windows = window_list_1 (window, mini ? Qt : Qnil, frame_arg);
   GCPRO1 (windows);
   best_window = Qnil;
 
@@ -2288,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.  */
@@ -2493,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),
@@ -2769,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;
 {
@@ -2918,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)
@@ -2967,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);
 
@@ -3799,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:
@@ -3849,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);
@@ -3858,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;
   
@@ -3902,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);
        }
     }
 }
@@ -4324,24 +4498,58 @@ displayed_window_lines (w)
   struct it it;
   struct text_pos start;
   int height = window_box_height (w);
+  struct buffer *old_buffer;
+  int bottom_y;
+
+  if (XBUFFER (w->buffer) != current_buffer)
+    {
+      old_buffer = current_buffer;
+      set_buffer_internal (XBUFFER (w->buffer));
+    }
+  else
+    old_buffer = NULL;
 
   SET_TEXT_POS_FROM_MARKER (start, w->start);
   start_display (&it, w, start);
   move_it_vertically (&it, height);
 
+  if (old_buffer)
+    set_buffer_internal (old_buffer);
+
+  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 (it.current_y < height)
+  if (bottom_y < height)
     {
       struct frame *f = XFRAME (w->frame);
-      int rest = height - it.current_y;
-      int lines = (rest + CANON_Y_UNIT (f) - 1) / CANON_Y_UNIT (f);
+      int rest = height - bottom_y;
+      int lines = rest / CANON_Y_UNIT (f);
       it.vpos += lines;
     }
-  
+
   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",
@@ -4379,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);
 }
 
@@ -4410,19 +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;
-  };
-#define SAVED_WINDOW_VECTOR_SIZE 14 /* 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)])))
@@ -4484,12 +4698,11 @@ the return value is nil.  Otherwise the value is t.")
     {
       if (XBUFFER (new_current_buffer) == current_buffer)
        old_point = PT;
-
     }
 
   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))
@@ -4499,7 +4712,7 @@ the return value is nil.  Otherwise the value is t.")
       struct window *root_window;
       struct window **leaf_windows;
       int n_leaf_windows;
-      int k, i;
+      int k, i, n;
 
       /* If the frame has been resized since this window configuration was
         made, we change the frame to the size specified in the
@@ -4614,7 +4827,10 @@ 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;
          XSETFASTINT (w->last_modified, 0);
          XSETFASTINT (w->last_overlay_modified, 0);
 
@@ -4706,15 +4922,23 @@ the return value is nil.  Otherwise the value is t.")
 #endif
 
       /* Now, free glyph matrices in windows that were not reused.  */
-      for (i = 0; i < n_leaf_windows; ++i)
-       if (NILP (leaf_windows[i]->buffer))
-         {
-           /* Assert it's not reused as a combination.  */
-           xassert (NILP (leaf_windows[i]->hchild) 
-                    && NILP (leaf_windows[i]->vchild));
-           free_window_matrices (leaf_windows[i]);
-           SET_FRAME_GARBAGED (f);
-         }
+      for (i = n = 0; i < n_leaf_windows; ++i)
+       {
+         if (NILP (leaf_windows[i]->buffer))
+           {
+             /* Assert it's not reused as a combination.  */
+             xassert (NILP (leaf_windows[i]->hchild) 
+                      && NILP (leaf_windows[i]->vchild));
+             free_window_matrices (leaf_windows[i]);
+           }
+         else if (EQ (leaf_windows[i]->buffer, new_current_buffer))
+           ++n;
+       }
+
+      /* If more than one window shows the new and old current buffer,
+        don't try to preserve point in that buffer.  */
+      if (old_point > 0 && n > 1)
+       old_point = -1;
       
       adjust_glyphs (f);
 
@@ -4870,7 +5094,10 @@ 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;
       if (!NILP (w->buffer))
        {
          /* Save w's value of point in the window configuration.
@@ -4968,8 +5195,7 @@ redirection (see `redirect-frame-focus').")
   for (i = 0; i < n_windows; i++)
     XVECTOR (tem)->contents[i]
       = Fmake_vector (make_number (SAVED_WINDOW_VECTOR_SIZE), Qnil);
-  save_window_save (FRAME_ROOT_WINDOW (f),
-                   XVECTOR (tem), 0);
+  save_window_save (FRAME_ROOT_WINDOW (f), XVECTOR (tem), 0);
   XSETWINDOW_CONFIGURATION (tem, data);
   return (tem);
 }
@@ -5297,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)))
@@ -5388,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;
@@ -5573,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);