(read_char): Save and restore echo_string when
[bpt/emacs.git] / src / window.c
index c3603c2..6fe547b 100644 (file)
@@ -204,7 +204,7 @@ static int window_initialized;
 Lisp_Object Qwindow_configuration_change_hook;
 Lisp_Object Vwindow_configuration_change_hook;
 
-/* Nonzero means scroll commands try to put point
+/* Non-nil means scroll commands try to put point
    at the same screen height as previously.  */
 
 Lisp_Object Vscroll_preserve_screen_position;
@@ -241,7 +241,8 @@ make_window ()
   register struct window *p;
 
   p = allocate_window ();
-  XSETFASTINT (p->sequence_number, ++sequence_number);
+  ++sequence_number;
+  XSETFASTINT (p->sequence_number, sequence_number);
   XSETFASTINT (p->left_col, 0);
   XSETFASTINT (p->top_line, 0);
   XSETFASTINT (p->total_lines, 0);
@@ -607,9 +608,6 @@ coordinates_in_window (w, x, y)
   int grabbable_width = ux;
   int lmargin_width, rmargin_width, text_left, text_right;
 
-  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)
@@ -659,6 +657,9 @@ coordinates_in_window (w, x, y)
            return ON_VERTICAL_BORDER;
        }
 
+      if (*x < x0 || *x >= x1)
+       return ON_NOTHING;
+
       /* Convert X and Y to window relative coordinates.
         Mode line starts at left edge of window.  */
       *x -= x0;
@@ -673,6 +674,9 @@ coordinates_in_window (w, x, y)
       goto header_vertical_border_check;
     }
 
+  if (*x < x0 || *x >= x1)
+    return ON_NOTHING;
+
   /* Outside any interesting column?  */
   if (*x < left_x || *x > right_x)
     return ON_SCROLL_BAR;
@@ -1607,7 +1611,7 @@ decode_next_window_args (window, minibuf, all_frames)
                   : Qnil);
   else if (EQ (*all_frames, Qvisible))
     ;
-  else if (XFASTINT (*all_frames) == 0)
+  else if (EQ (*all_frames, make_number (0)))
     ;
   else if (FRAMEP (*all_frames))
     ;
@@ -1828,7 +1832,7 @@ window_list_1 (window, minibuf, all_frames)
   rest = Fmemq (window, list);
   if (!NILP (rest) && !EQ (rest, list))
     {
-      for (tail = list; XCDR (tail) != rest; tail = XCDR (tail))
+      for (tail = list; !EQ (XCDR (tail), rest); tail = XCDR (tail))
        ;
       XSETCDR (tail, Qnil);
       list = nconc2 (rest, list);
@@ -1881,7 +1885,7 @@ window_loop (type, obj, mini, frames)
 
   if (f)
     frame_arg = Qlambda;
-  else if (XFASTINT (frames) == 0)
+  else if (EQ (frames, make_number (0)))
     frame_arg = frames;
   else if (EQ (frames, Qvisible))
     frame_arg = frames;
@@ -2093,6 +2097,8 @@ DEFUN ("get-lru-window", Fget_lru_window, Sget_lru_window, 0, 1, 0,
        doc: /* Return the window least recently selected or used for display.
 Return a full-width window if possible.
 A minibuffer window is never a candidate.
+A dedicated window is never a candidate, so if all windows are dedicated,
+the value is nil.
 If optional argument FRAME is `visible', search all visible frames.
 If FRAME is 0, search all visible and iconified frames.
 If FRAME is t, search all frames.
@@ -2113,6 +2119,8 @@ If FRAME is a frame, search only that frame.  */)
 DEFUN ("get-largest-window", Fget_largest_window, Sget_largest_window, 0, 1, 0,
        doc: /* Return the largest window in area.
 A minibuffer window is never a candidate.
+A dedicated window is never a candidate, so if all windows are dedicated,
+the value is nil.
 If optional argument FRAME is `visible', search all visible frames.
 If FRAME is 0, search all visible and iconified frames.
 If FRAME is t, search all frames.
@@ -2654,6 +2662,9 @@ shrink_windows (total, size, nchildren, shrinkable,
             --shrinkable;
             total_removed += smallest;
 
+            /* We don't know what the smallest is now.  */
+            smallest = total;
+
             /* Out of for, just remove one window at the time and
                check again if we have enough space.  */
             break;
@@ -2678,6 +2689,16 @@ shrink_windows (total, size, nchildren, shrinkable,
      that are left and still can be shrunk.  */
   while (total_shrink > total_removed)
     {
+      int nonzero_sizes = 0;
+      int nonzero_idx = -1;
+
+      for (i = 0; i < nchildren; ++i)
+        if (new_sizes[i] > 0)
+          {
+            ++nonzero_sizes;
+            nonzero_idx = i;
+          }
+
       for (i = 0; i < nchildren; ++i)
         if (new_sizes[i] > min_size)
           {
@@ -2688,6 +2709,25 @@ shrink_windows (total, size, nchildren, shrinkable,
                check again if we have enough space.  */
             break;
           }
+
+
+      /* Special case, only one window left.  */
+      if (nonzero_sizes == 1)
+        break;
+    }
+
+  /* Any surplus due to rounding, we add to windows that are left.  */
+  while (total_shrink < total_removed)
+    {
+      for (i = 0; i < nchildren; ++i)
+        {
+          if (new_sizes[i] != 0 && total_shrink < total_removed)
+            {
+              ++new_sizes[i];
+              --total_removed;
+              break;
+            }
+        }
     }
 
   return new_sizes;
@@ -2976,6 +3016,9 @@ set_window_buffer (window, buffer, run_hooks_p, keep_margins_p)
   if (EQ (window, selected_window))
     b->last_selected_window = window;
 
+  /* Let redisplay errors through.  */
+  b->display_error_modiff = 0;
+
   /* Update time stamps of buffer display.  */
   if (INTEGERP (b->display_count))
     XSETINT (b->display_count, XINT (b->display_count) + 1);
@@ -3057,7 +3100,9 @@ BUFFER can be a buffer or the name of an existing buffer.
 Optional third arg KEEP-MARGINS non-nil means that WINDOW's current
 display margins, fringe widths, and scroll bar settings are maintained;
 the default is to reset these from BUFFER's local settings or the frame
-defaults.  */)
+defaults.
+
+This function runs the hook `window-scroll-functions'.  */)
      (window, buffer, keep_margins)
      register Lisp_Object window, buffer, keep_margins;
 {
@@ -3112,7 +3157,8 @@ selects the buffer of the selected window before each command.  */)
   w = XWINDOW (window);
   w->frozen_window_start_p = 0;
 
-  XSETFASTINT (w->use_time, ++window_select_count);
+  ++window_select_count;
+  XSETFASTINT (w->use_time, window_select_count);
   if (EQ (window, selected_window))
     return window;
 
@@ -3209,10 +3255,13 @@ display_buffer_1 (window)
 }
 
 DEFUN ("special-display-p", Fspecial_display_p, Sspecial_display_p, 1, 1, 0,
-       doc: /* Returns non-nil if a buffer named BUFFER-NAME would be created specially.
-The value is actually t if the frame should be called with default frame
-parameters, and a list of frame parameters if they were specified.
-See `special-display-buffer-names', and `special-display-regexps'.  */)
+       doc: /* Returns non-nil if a buffer named BUFFER-NAME gets a special frame.
+If the value is t, a frame would be created for that buffer
+using the default frame parameters.  If the value is a list,
+it is a list of frame parameters that would be used
+to make a frame for that buffer.
+The variables `special-display-buffer-names'
+and `special-display-regexps' control this.  */)
      (buffer_name)
      Lisp_Object buffer_name;
 {
@@ -3600,7 +3649,8 @@ make_dummy_parent (window)
       = ((struct Lisp_Vector *)o)->contents[i];
   XSETWINDOW (new, p);
 
-  XSETFASTINT (p->sequence_number, ++sequence_number);
+  ++sequence_number;
+  XSETFASTINT (p->sequence_number, sequence_number);
 
   /* Put new into window structure in place of window */
   replace_window (window, new);
@@ -4498,7 +4548,7 @@ window_scroll_pixel_based (window, n, whole, noerror)
         results for variable height lines.  */
       init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
       it.current_y = it.last_visible_y;
-      move_it_vertically (&it, - window_box_height (w) / 2);
+      move_it_vertically_backward (&it, window_box_height (w) / 2);
 
       /* The function move_iterator_vertically may move over more than
         the specified y-distance.  If it->w is small, e.g. a
@@ -4508,14 +4558,14 @@ window_scroll_pixel_based (window, n, whole, noerror)
       if (it.current_y <= 0)
        {
          init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
-         move_it_vertically (&it, 0);
+         move_it_vertically_backward (&it, 0);
          it.current_y = 0;
        }
 
       start = it.current.pos;
     }
 
-  /* If scroll_preserve_screen_position is non-zero, we try to set
+  /* If scroll_preserve_screen_position is non-nil, we try to set
      point in the same window line as it is now, so get that line.  */
   if (!NILP (Vscroll_preserve_screen_position))
     {
@@ -4617,17 +4667,25 @@ window_scroll_pixel_based (window, n, whole, noerror)
       w->force_start = Qt;
     }
 
+  /* The rest of this function uses current_y in a nonstandard way,
+     not including the height of the header line if any.  */
   it.current_y = it.vpos = 0;
 
-  /* Preserve the screen position if we must.  */
+  /* Preserve the screen position if we should.  */
   if (preserve_y >= 0)
     {
+      /* If we have a header line, take account of it.  */
+      if (WINDOW_WANTS_HEADER_LINE_P (w))
+       preserve_y -= CURRENT_HEADER_LINE_HEIGHT (w);
+
       move_it_to (&it, -1, -1, preserve_y, -1, MOVE_TO_Y);
       SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
     }
   else
     {
-      /* Move PT out of scroll margins.  */
+      /* Move PT out of scroll margins.
+        This code wants current_y to be zero at the window start position
+        even if there is a header line.  */
       this_scroll_margin = max (0, scroll_margin);
       this_scroll_margin = min (this_scroll_margin, XFASTINT (w->total_lines) / 4);
       this_scroll_margin *= FRAME_LINE_HEIGHT (it.f);
@@ -4707,7 +4765,7 @@ window_scroll_line_based (window, n, whole, noerror)
 
   posit = *compute_motion (startpos, 0, 0, 0,
                           PT, ht, 0,
-                          window_box_text_cols (w), XINT (w->hscroll),
+                          -1, XINT (w->hscroll),
                           0, w);
   original_vpos = posit.vpos;
 
@@ -4982,17 +5040,17 @@ specifies the window to scroll.  This takes precedence over
   return Qnil;
 }
 \f
-DEFUN ("scroll-left", Fscroll_left, Sscroll_left, 0, 1, "P",
+DEFUN ("scroll-left", Fscroll_left, Sscroll_left, 0, 2, "P\np",
        doc: /* Scroll selected window display ARG columns left.
 Default for ARG is window width minus 2.
 Value is the total amount of leftward horizontal scrolling in
 effect after the change.
-If `automatic-hscrolling' is non-nil, the argument ARG modifies
-lower bound for automatic scrolling, i.e. automatic scrolling
+If SET_MINIMUM is non-nil, the new scroll amount becomes the
+lower bound for automatic scrolling, i.e. automatic scrolling
 will not scroll a window to a column less than the value returned
-by this function.  */)
-     (arg)
-     register Lisp_Object arg;
+by this function.  This happens in an interactive call.  */)
+     (arg, set_minimum)
+     register Lisp_Object arg, set_minimum;
 {
   Lisp_Object result;
   int hscroll;
@@ -5006,23 +5064,23 @@ by this function.  */)
   hscroll = XINT (w->hscroll) + XINT (arg);
   result = Fset_window_hscroll (selected_window, make_number (hscroll));
 
-  if (interactive_p (0))
+  if (!NILP (set_minimum))
     w->min_hscroll = w->hscroll;
 
   return result;
 }
 
-DEFUN ("scroll-right", Fscroll_right, Sscroll_right, 0, 1, "P",
+DEFUN ("scroll-right", Fscroll_right, Sscroll_right, 0, 2, "P\np",
        doc: /* Scroll selected window display ARG columns right.
 Default for ARG is window width minus 2.
 Value is the total amount of leftward horizontal scrolling in
 effect after the change.
-If `automatic-hscrolling' is non-nil, the argument ARG modifies
-lower bound for automatic scrolling, i.e. automatic scrolling
+If SET_MINIMUM is non-nil, the new scroll amount becomes the
+lower bound for automatic scrolling, i.e. automatic scrolling
 will not scroll a window to a column less than the value returned
-by this function.  */)
-     (arg)
-     register Lisp_Object arg;
+by this function.  This happens in an interactive call.  */)
+     (arg, set_minimum)
+     register Lisp_Object arg, set_minimum;
 {
   Lisp_Object result;
   int hscroll;
@@ -5036,7 +5094,7 @@ by this function.  */)
   hscroll = XINT (w->hscroll) - XINT (arg);
   result = Fset_window_hscroll (selected_window, make_number (hscroll));
 
-  if (interactive_p (0))
+  if (!NILP (set_minimum))
     w->min_hscroll = w->hscroll;
 
   return result;
@@ -5169,7 +5227,7 @@ and redisplay normally--don't erase and redraw the frame.  */)
 
          SET_TEXT_POS (pt, PT, PT_BYTE);
          start_display (&it, w, pt);
-         move_it_vertically (&it, - window_box_height (w) / 2);
+         move_it_vertically_backward (&it, window_box_height (w) / 2);
          charpos = IT_CHARPOS (it);
          bytepos = IT_BYTEPOS (it);
        }
@@ -5177,29 +5235,62 @@ and redisplay normally--don't erase and redraw the frame.  */)
        {
          struct it it;
          struct text_pos pt;
-         int y0, y1, h, nlines;
+         int nlines = - XINT (arg);
+         int extra_line_spacing;
+         int h = window_box_height (w);
 
          SET_TEXT_POS (pt, PT, PT_BYTE);
          start_display (&it, w, pt);
-         y0 = it.current_y;
+
+         /* Be sure we have the exact height of the full line containing PT.  */
+         move_it_by_lines (&it, 0, 1);
 
          /* The amount of pixels we have to move back is the window
             height minus what's displayed in the line containing PT,
             and the lines below.  */
-         nlines = - XINT (arg) - 1;
+         it.current_y = 0;
+         it.vpos = 0;
          move_it_by_lines (&it, nlines, 1);
 
-         y1 = line_bottom_y (&it);
+         if (it.vpos == nlines)
+           h -= it.current_y;
+         else
+           {
+             /* Last line has no newline */
+             h -= line_bottom_y (&it);
+             it.vpos++;
+           }
+
+         /* Don't reserve space for extra line spacing of last line.  */
+         extra_line_spacing = it.max_extra_line_spacing;
 
          /* If we can't move down NLINES lines because we hit
             the end of the buffer, count in some empty lines.  */
          if (it.vpos < nlines)
-           y1 += (nlines - it.vpos) * FRAME_LINE_HEIGHT (it.f);
-
-         h = window_box_height (w) - (y1 - y0);
+           {
+             nlines -= it.vpos;
+             extra_line_spacing = it.extra_line_spacing;
+             h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing);
+           }
+         if (h <= 0)
+           return Qnil;
 
+         /* Now find the new top line (starting position) of the window.  */
          start_display (&it, w, pt);
-         move_it_vertically (&it, - h);
+         it.current_y = 0;
+         move_it_vertically_backward (&it, h);
+
+         /* If extra line spacing is present, we may move too far
+            back.  This causes the last line to be only partially
+            visible (which triggers redisplay to recenter that line
+            in the middle), so move forward.
+            But ignore extra line spacing on last line, as it is not
+            considered to be part of the visible height of the line.
+         */
+         h += extra_line_spacing;
+         while (-it.current_y > h)
+           move_it_by_lines (&it, 1, 1);
+
          charpos = IT_CHARPOS (it);
          bytepos = IT_BYTEPOS (it);
        }
@@ -5801,7 +5892,7 @@ save_window_save (window, vector, i)
       p = SAVED_WINDOW_N (vector, i);
       w = XWINDOW (window);
 
-      XSETFASTINT (w->temslot, i++);
+      XSETFASTINT (w->temslot, i); i++;
       p->window = window;
       p->buffer = w->buffer;
       p->left_col = w->left_col;
@@ -6032,9 +6123,9 @@ display marginal areas and the text area.  */)
   struct window *w = decode_window (window);
 
   if (!NILP (left))
-    CHECK_NUMBER (left);
+    CHECK_NATNUM (left);
   if (!NILP (right))
-    CHECK_NUMBER (right);
+    CHECK_NATNUM (right);
 
   if (!EQ (w->left_fringe_width, left)
       || !EQ (w->right_fringe_width, right)
@@ -6094,10 +6185,12 @@ If TYPE is t, use the frame's scroll-bar type.  */)
   struct window *w = decode_window (window);
 
   if (!NILP (width))
-    CHECK_NUMBER (width);
+    {
+      CHECK_NATNUM (width);
 
-  if (XINT (width) == 0)
-    vertical_type = Qnil;
+      if (XINT (width) == 0)
+       vertical_type = Qnil;
+    }
 
   if (!(EQ (vertical_type, Qnil)
        || EQ (vertical_type, Qleft)