X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/81159bb955c88e1315e21be0df6c6cf7f0bd3766..69036b87bb3c5aa2beff7440a05dcbae5d7b2d41:/src/window.c diff --git a/src/window.c b/src/window.c index 8a574becd0..36d0c89bc4 100644 --- a/src/window.c +++ b/src/window.c @@ -1,7 +1,7 @@ /* Window creation, deletion and examination for GNU Emacs. Does not include redisplay. - Copyright (C) 1985,86,87, 1993,94,95,96,97,98, 2000,01,02,03,04 - Free Software Foundation, Inc. + Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1996, 1997, 1998, 2000, + 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -17,8 +17,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to -the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ #include #include "lisp.h" @@ -79,10 +79,6 @@ static int foreach_window_1 P_ ((struct window *, void *)); static Lisp_Object window_list_1 P_ ((Lisp_Object, Lisp_Object, Lisp_Object)); -/* The value of `window-size-fixed'. */ - -int window_size_fixed; - /* This is the window in which the terminal's cursor should be left when nothing is being done with it. This must always be a leaf window, and its buffer is selected by @@ -124,6 +120,11 @@ Lisp_Object Vother_window_scroll_buffer; Lisp_Object Vtemp_buffer_show_function; +/* Non-zero means line and page scrolling on tall lines (with images) + does partial scrolling by modifying window-vscroll. */ + +int auto_window_vscroll_p; + /* Non-zero means to use mode-line-inactive face in all windows but the selected-window and the minibuffer-scroll-window when the minibuffer is active. */ @@ -204,7 +205,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 +242,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); @@ -280,7 +282,6 @@ make_window () p->fringes_outside_margins = Qnil; p->scroll_bar_width = Qnil; p->vertical_scroll_bar_type = Qt; - p->overlay_arrow_bitmap = 0; Vwindow_list = Qnil; return val; @@ -327,9 +328,10 @@ If POS is only out of view because of horizontal scrolling, return non-nil. POS defaults to point in WINDOW; WINDOW defaults to the selected window. If POS is visible, return t if PARTIALLY is nil; if PARTIALLY is non-nil, -return value is a list (X Y FULLY) where X and Y are the pixel coordinates -relative to the top left corner of the window, and FULLY is t if the -character after POS is fully visible and nil otherwise. */) +return value is a list (X Y PARTIAL) where X and Y are the pixel coordinates +relative to the top left corner of the window. PARTIAL is nil if the character +after POS is fully visible; otherwise it is a cons (RTOP . RBOT) where RTOP +and RBOT are the number of pixels invisible at the top and bottom of the row. */) (pos, window, partially) Lisp_Object pos, window, partially; { @@ -338,7 +340,7 @@ character after POS is fully visible and nil otherwise. */) register struct buffer *buf; struct text_pos top; Lisp_Object in_window = Qnil; - int fully_p = 1; + int rtop, rbot, fully_p = 1; int x, y; w = decode_window (window); @@ -361,14 +363,17 @@ character after POS is fully visible and nil otherwise. */) && posint <= BUF_ZV (buf) && CHARPOS (top) >= BUF_BEGV (buf) && CHARPOS (top) <= BUF_ZV (buf) - && pos_visible_p (w, posint, &fully_p, &x, &y, NILP (partially)) - && (!NILP (partially) || fully_p)) + && pos_visible_p (w, posint, &x, &y, &rtop, &rbot, NILP (partially)) + && (fully_p = !rtop && !rbot, (!NILP (partially) || fully_p))) in_window = Qt; if (!NILP (in_window) && !NILP (partially)) in_window = Fcons (make_number (x), Fcons (make_number (y), - Fcons (fully_p ? Qt : Qnil, Qnil))); + Fcons ((fully_p ? Qnil + : Fcons (make_number (rtop), + make_number (rbot))), + Qnil))); return in_window; } @@ -539,7 +544,7 @@ display margins, fringes, header line, and/or mode line. */) + WINDOW_LEFT_FRINGE_COLS (w)), make_number (WINDOW_TOP_EDGE_LINE (w) + WINDOW_HEADER_LINE_LINES (w)), - make_number (WINDOW_RIGHT_EDGE_COL (w) + make_number (WINDOW_BOX_RIGHT_EDGE_COL (w) - WINDOW_RIGHT_MARGIN_COLS (w) - WINDOW_RIGHT_FRINGE_COLS (w)), make_number (WINDOW_BOTTOM_EDGE_LINE (w) @@ -563,7 +568,7 @@ display margins, fringes, header line, and/or mode line. */) + WINDOW_LEFT_FRINGE_WIDTH (w)), make_number (WINDOW_TOP_EDGE_Y (w) + WINDOW_HEADER_LINE_HEIGHT (w)), - make_number (WINDOW_RIGHT_EDGE_X (w) + make_number (WINDOW_BOX_RIGHT_EDGE_X (w) - WINDOW_RIGHT_MARGIN_WIDTH (w) - WINDOW_RIGHT_FRINGE_WIDTH (w)), make_number (WINDOW_BOTTOM_EDGE_Y (w) @@ -579,6 +584,8 @@ display margins, fringes, header line, and/or mode line. */) if it is on the window's modeline, return ON_MODE_LINE; if it is on the border between the window and its right sibling, return ON_VERTICAL_BORDER. + if it is on a scroll bar, + return ON_SCROLL_BAR. if it is on the window's top line, return ON_HEADER_LINE; if it is in left or right fringe of the window, return ON_LEFT_FRINGE or ON_RIGHT_FRINGE, and convert *X and *Y @@ -605,9 +612,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) @@ -657,6 +661,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; @@ -671,9 +678,12 @@ 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_VERTICAL_BORDER; + return ON_SCROLL_BAR; lmargin_width = window_box_width (w, LEFT_MARGIN_AREA); rmargin_width = window_box_width (w, RIGHT_MARGIN_AREA); @@ -818,6 +828,10 @@ If they are in the windows's left or right marginal areas, `left-margin'\n\ case ON_RIGHT_MARGIN: return Qright_margin; + case ON_SCROLL_BAR: + /* Historically we are supposed to return nil in this case. */ + return Qnil; + default: abort (); } @@ -1438,8 +1452,10 @@ delete_window (window) tem = par->hchild; if (NILP (tem)) tem = par->vchild; - if (NILP (XWINDOW (tem)->next)) + if (NILP (XWINDOW (tem)->next)) { replace_window (parent, tem); + par = XWINDOW (tem); + } /* Since we may be deleting combination windows, we must make sure that not only p but all its children have been marked as deleted. */ @@ -1451,6 +1467,51 @@ delete_window (window) /* Mark this window as deleted. */ p->buffer = p->hchild = p->vchild = Qnil; + if (! NILP (par->parent)) + par = XWINDOW (par->parent); + + /* Check if we have a v/hchild with a v/hchild. In that case remove + one of them. */ + + if (! NILP (par->vchild) && ! NILP (XWINDOW (par->vchild)->vchild)) + { + p = XWINDOW (par->vchild); + par->vchild = p->vchild; + tem = p->vchild; + } + else if (! NILP (par->hchild) && ! NILP (XWINDOW (par->hchild)->hchild)) + { + p = XWINDOW (par->hchild); + par->hchild = p->hchild; + tem = p->hchild; + } + else + p = 0; + + if (p) + { + while (! NILP (tem)) { + XWINDOW (tem)->parent = p->parent; + if (NILP (XWINDOW (tem)->next)) + break; + tem = XWINDOW (tem)->next; + } + if (! NILP (tem)) { + /* The next of the v/hchild we are removing is now the next of the + last child for the v/hchild: + Before v/hchild -> v/hchild -> next1 -> next2 + | + -> next3 + After: v/hchild -> next1 -> next2 -> next3 + */ + XWINDOW (tem)->next = p->next; + if (! NILP (p->next)) + XWINDOW (p->next)->prev = tem; + } + p->next = p->prev = p->vchild = p->hchild = p->buffer = Qnil; + } + + /* Adjust glyph matrices. */ adjust_glyphs (f); UNBLOCK_INPUT; @@ -1601,7 +1662,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)) ; @@ -1822,7 +1883,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); @@ -1875,7 +1936,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; @@ -2087,6 +2148,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. @@ -2107,6 +2170,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. @@ -2648,6 +2713,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; @@ -2672,6 +2740,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) { @@ -2682,6 +2760,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; @@ -2970,6 +3067,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); @@ -3051,7 +3151,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; { @@ -3106,10 +3208,14 @@ 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; + /* Store the current buffer's actual point into the + old selected window. It belongs to that window, + and when the window is not selected, must be in the window. */ if (!NILP (selected_window)) { ow = XWINDOW (selected_window); @@ -3128,7 +3234,7 @@ selects the buffer of the selected window before each command. */) so that FRAME_FOCUS_FRAME is moved appropriately as we move around in the state where a minibuffer in a separate frame is active. */ - Fselect_frame (WINDOW_FRAME (w), Qnil); + Fselect_frame (WINDOW_FRAME (w)); } else sf->selected_window = window; @@ -3203,10 +3309,14 @@ 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, `display-buffer' or `pop-to-buffer' would create a +special frame 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; { @@ -3237,7 +3347,9 @@ See `special-display-buffer-names', and `special-display-regexps'. */) } DEFUN ("same-window-p", Fsame_window_p, Ssame_window_p, 1, 1, 0, - doc: /* Returns non-nil if a new buffer named BUFFER-NAME would use the same window. + doc: /* Returns non-nil if a buffer named BUFFER-NAME would use the same window. +More precisely, if `display-buffer' or `pop-to-buffer' would display +that buffer in the selected window rather than (as usual) in some other window. See `same-window-buffer-names' and `same-window-regexps'. */) (buffer_name) Lisp_Object buffer_name; @@ -3594,7 +3706,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); @@ -4067,7 +4180,7 @@ enlarge_window (window, delta, widthflag, preserve_before) The number of children n equals the number of resizable children of this window + 1 because we know window itself - is resizable (otherwise we would have signalled an error. */ + is resizable (otherwise we would have signalled an error). */ struct window *w = XWINDOW (window); Lisp_Object s; @@ -4492,7 +4605,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 @@ -4502,14 +4615,41 @@ 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; } + else if (auto_window_vscroll_p) + { + if (tem = XCAR (XCDR (XCDR (tem))), CONSP (tem)) + { + int px; + int dy = WINDOW_FRAME_LINE_HEIGHT (w); + if (whole) + dy = max ((window_box_height (w) + - next_screen_context_lines * dy), + dy); + dy *= n; + + if (n < 0 && (px = XINT (XCAR (tem))) > 0) + { + px = max (0, -w->vscroll - min (px, -dy)); + Fset_window_vscroll (window, make_number (px), Qt); + return; + } + if (n > 0 && (px = XINT (XCDR (tem))) > 0) + { + px = max (0, -w->vscroll + min (px, dy)); + Fset_window_vscroll (window, make_number (px), Qt); + return; + } + } + Fset_window_vscroll (window, make_number (0), Qt); + } - /* 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)) { @@ -4525,18 +4665,34 @@ window_scroll_pixel_based (window, n, whole, noerror) start_display (&it, w, start); if (whole) { - int screen_full = (window_box_height (w) - - next_screen_context_lines * FRAME_LINE_HEIGHT (it.f)); - int dy = n * screen_full; + int start_pos = IT_CHARPOS (it); + int dy = WINDOW_FRAME_LINE_HEIGHT (w); + dy = max ((window_box_height (w) + - next_screen_context_lines * dy), + dy) * n; /* 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); + { + move_it_vertically_backward (&it, -dy); + /* Ensure we actually does move, e.g. in case we are currently + looking at an image that is taller that the window height. */ + while (start_pos == IT_CHARPOS (it) + && start_pos > BEGV) + move_it_by_lines (&it, -1, 1); + } else if (dy > 0) - move_it_to (&it, ZV, -1, it.current_y + dy, -1, - MOVE_TO_POS | MOVE_TO_Y); + { + move_it_to (&it, ZV, -1, it.current_y + dy, -1, + MOVE_TO_POS | MOVE_TO_Y); + /* Ensure we actually does move, e.g. in case we are currently + looking at an image that is taller that the window height. */ + while (start_pos == IT_CHARPOS (it) + && start_pos < ZV) + move_it_by_lines (&it, 1, 1); + } } else move_it_by_lines (&it, n, 1); @@ -4611,26 +4767,39 @@ 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. */ - if (preserve_y >= 0) - { - 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. + 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); + + if (n > 0) { - /* Move PT out of scroll margins. */ - 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); + /* We moved the window start towards ZV, so PT may be now + in the scroll margin at the top. */ + move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); + if (IT_CHARPOS (it) == PT && it.current_y >= this_scroll_margin + && (NILP (Vscroll_preserve_screen_position) + || EQ (Vscroll_preserve_screen_position, Qt))) + /* We found PT at a legitimate height. Leave it alone. */ + ; + else if (preserve_y >= 0) + { + /* If we have a header line, take account of it. + This is necessary because we set it.current_y to 0, above. */ + if (WINDOW_WANTS_HEADER_LINE_P (w)) + preserve_y -= CURRENT_HEADER_LINE_HEIGHT (w); - if (n > 0) + move_it_to (&it, -1, -1, preserve_y, -1, MOVE_TO_Y); + SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it)); + } + else { - /* We moved the window start towards ZV, so PT may be now - in the scroll margin at the top. */ - move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); while (it.current_y < this_scroll_margin) { int prev = it.current_y; @@ -4640,23 +4809,61 @@ window_scroll_pixel_based (window, n, whole, noerror) } SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it)); } - else if (n < 0) + } + else if (n < 0) + { + int charpos, bytepos; + int partial_p; + + /* Save our position, for the preserve_y case. */ + charpos = IT_CHARPOS (it); + bytepos = IT_BYTEPOS (it); + + /* 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 - CURRENT_HEADER_LINE_HEIGHT (w) + - 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); + + /* See if point is on a partially visible line at the end. */ + if (it.what == IT_EOB) + partial_p = it.current_y + it.ascent + it.descent > it.last_visible_y; + else { - 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); + move_it_by_lines (&it, 1, 1); + partial_p = it.current_y > it.last_visible_y; + } - /* Save our position, in case it's correct. */ - charpos = IT_CHARPOS (it); - bytepos = IT_BYTEPOS (it); + if (charpos == PT && !partial_p + && (NILP (Vscroll_preserve_screen_position) + || EQ (Vscroll_preserve_screen_position, Qt))) + /* We found PT before we found the display margin, so PT is ok. */ + ; + else if (preserve_y >= 0) + { + SET_TEXT_POS_FROM_MARKER (start, w->start); + start_display (&it, w, start); +#if 0 /* It's wrong to subtract this here + because we called start_display again + and did not alter it.current_y this time. */ + + /* If we have a header line, take account of it. */ + if (WINDOW_WANTS_HEADER_LINE_P (w)) + preserve_y -= CURRENT_HEADER_LINE_HEIGHT (w); +#endif - /* 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) + move_it_to (&it, -1, -1, preserve_y, -1, MOVE_TO_Y); + SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it)); + } + else + { + if (partial_p) /* The last line was only partially visible, so back up two lines to make sure we're on a fully visible line. */ { @@ -4701,7 +4908,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; @@ -4750,7 +4957,8 @@ window_scroll_line_based (window, n, whole, noerror) the window-scroll-functions. */ w->force_start = Qt; - if (whole && !NILP (Vscroll_preserve_screen_position)) + if (!NILP (Vscroll_preserve_screen_position) + && (whole || !EQ (Vscroll_preserve_screen_position, Qt))) { SET_PT_BOTH (pos, pos_byte); Fvertical_motion (make_number (original_vpos), window); @@ -4976,17 +5184,17 @@ specifies the window to scroll. This takes precedence over return Qnil; } -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 -a 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; @@ -5000,23 +5208,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 -a 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; @@ -5030,7 +5238,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; @@ -5125,6 +5333,8 @@ and redisplay normally--don't erase and redraw the frame. */) struct buffer *obuf = current_buffer; int center_p = 0; int charpos, bytepos; + int iarg; + int this_scroll_margin; /* If redisplay is suppressed due to an error, try again. */ obuf->display_error_modiff = 0; @@ -5147,10 +5357,17 @@ and redisplay normally--don't erase and redraw the frame. */) { arg = Fprefix_numeric_value (arg); CHECK_NUMBER (arg); + iarg = XINT (arg); } set_buffer_internal (buf); + /* Do this after making BUF current + in case scroll_margin is buffer-local. */ + this_scroll_margin = max (0, scroll_margin); + this_scroll_margin = min (this_scroll_margin, + XFASTINT (w->total_lines) / 4); + /* Handle centering on a graphical frame specially. Such frames can have variable-height lines and centering point on the basis of line counts would lead to strange effects. */ @@ -5163,44 +5380,82 @@ 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); } - else if (XINT (arg) < 0) + else if (iarg < 0) { struct it it; struct text_pos pt; - int y0, y1, h, nlines; + int nlines = -iarg; + int extra_line_spacing; + int h = window_box_height (w); + + iarg = - max (-iarg, this_scroll_margin); 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); } else { struct position pos; - pos = *vmotion (PT, - XINT (arg), w); + + iarg = max (iarg, this_scroll_margin); + + pos = *vmotion (PT, -iarg, w); charpos = pos.bufpos; bytepos = pos.bytepos; } @@ -5211,11 +5466,15 @@ and redisplay normally--don't erase and redraw the frame. */) int ht = window_internal_height (w); if (center_p) - arg = make_number (ht / 2); - else if (XINT (arg) < 0) - arg = make_number (XINT (arg) + ht); + iarg = ht / 2; + else if (iarg < 0) + iarg += ht; - pos = *vmotion (PT, - XINT (arg), w); + /* Don't let it get into the margin at either top or bottom. */ + iarg = max (iarg, this_scroll_margin); + iarg = min (iarg, ht - this_scroll_margin - 1); + + pos = *vmotion (PT, - iarg, w); charpos = pos.bufpos; bytepos = pos.bytepos; } @@ -5264,6 +5523,9 @@ zero means top of window, negative means relative to bottom of window. */) struct window *w = XWINDOW (selected_window); int lines, start; Lisp_Object window; +#if 0 + int this_scroll_margin; +#endif window = selected_window; start = marker_position (w->start); @@ -5279,13 +5541,33 @@ zero means top of window, negative means relative to bottom of window. */) Fgoto_char (w->start); lines = displayed_window_lines (w); + +#if 0 + this_scroll_margin = max (0, scroll_margin); + this_scroll_margin = min (this_scroll_margin, lines / 4); +#endif + if (NILP (arg)) XSETFASTINT (arg, lines / 2); else { - arg = Fprefix_numeric_value (arg); - if (XINT (arg) < 0) - XSETINT (arg, XINT (arg) + lines); + int iarg = XINT (Fprefix_numeric_value (arg)); + + if (iarg < 0) + iarg = iarg + lines; + +#if 0 /* This code would prevent move-to-window-line from moving point + to a place inside the scroll margins (which would cause the + next redisplay to scroll). I wrote this code, but then concluded + it is probably better not to install it. However, it is here + inside #if 0 so as not to lose it. -- rms. */ + + /* Don't let it get into the margin at either top or bottom. */ + iarg = max (iarg, this_scroll_margin); + iarg = min (iarg, lines - this_scroll_margin - 1); +#endif + + arg = make_number (iarg); } /* Skip past a partially visible first line. */ @@ -5342,8 +5624,6 @@ struct saved_window Lisp_Object scroll_bar_width, vertical_scroll_bar_type; }; -#define SAVED_WINDOW_VECTOR_SIZE 24 /* Arg to Fmake_vector */ - #define SAVED_WINDOW_N(swv,n) \ ((struct saved_window *) (XVECTOR ((swv)->contents[(n)]))) @@ -5405,7 +5685,20 @@ the return value is nil. Otherwise the value is t. */) if (XBUFFER (new_current_buffer) == current_buffer) old_point = PT; else - old_point = BUF_PT (XBUFFER (new_current_buffer)); + /* BUF_PT (XBUFFER (new_current_buffer)) gives us the position of + point in new_current_buffer as of the last time this buffer was + used. This can be non-deterministic since it can be changed by + things like jit-lock by mere temporary selection of some random + window that happens to show this buffer. + So if possible we want this arbitrary choice of "which point" to + be the one from the to-be-selected-window so as to prevent this + window's cursor from being copied from another window. */ + if (EQ (XWINDOW (data->current_window)->buffer, new_current_buffer) + /* If current_window = selected_window, its point is in BUF_PT. */ + && !EQ (selected_window, data->current_window)) + old_point = XMARKER (XWINDOW (data->current_window)->pointm)->charpos; + else + old_point = BUF_PT (XBUFFER (new_current_buffer)); } frame = XWINDOW (SAVED_WINDOW_N (saved_windows, 0)->window)->frame; @@ -5450,8 +5743,9 @@ the return value is nil. Otherwise the value is t. */) #endif #endif - /* "Swap out" point from the selected window - into its buffer. We do this now, before + /* "Swap out" point from the selected window's buffer + into the window itself. (Normally the pointm of the selected + window holds garbage.) We do this now, before restoring the window contents, and prevent it from being done later on when we select a new window. */ if (! NILP (XWINDOW (selected_window)->buffer)) @@ -5601,10 +5895,11 @@ the return value is nil. Otherwise the value is t. */) FRAME_ROOT_WINDOW (f) = data->root_window; /* Prevent "swapping out point" in the old selected window using the buffer that has been restored into it. - Use the point value from the beginning of this function - since unshow_buffer (called from delete_all_subwindows) - could have altered it. */ + We already swapped out point that from that window's old buffer. */ selected_window = Qnil; + + /* Arrange *not* to restore point in the buffer that was + current when the window configuration was saved. */ if (EQ (XWINDOW (data->current_window)->buffer, new_current_buffer)) set_marker_restricted (XWINDOW (data->current_window)->pointm, make_number (old_point), @@ -5795,7 +6090,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; @@ -5908,7 +6203,7 @@ redirection (see `redirect-frame-focus'). */) data->saved_windows = tem; for (i = 0; i < n_windows; i++) XVECTOR (tem)->contents[i] - = Fmake_vector (make_number (SAVED_WINDOW_VECTOR_SIZE), Qnil); + = Fmake_vector (make_number (VECSIZE (struct saved_window)), Qnil); save_window_save (FRAME_ROOT_WINDOW (f), XVECTOR (tem), 0); XSETWINDOW_CONFIGURATION (tem, data); return (tem); @@ -5949,33 +6244,33 @@ Second arg LEFT-WIDTH specifies the number of character cells to reserve for the left marginal area. Optional third arg RIGHT-WIDTH does the same for the right marginal area. A nil width parameter means no margin. */) - (window, left, right) - Lisp_Object window, left, right; + (window, left_width, right_width) + Lisp_Object window, left_width, right_width; { struct window *w = decode_window (window); /* Translate negative or zero widths to nil. Margins that are too wide have to be checked elsewhere. */ - if (!NILP (left)) + if (!NILP (left_width)) { - CHECK_NUMBER (left); - if (XINT (left) <= 0) - left = Qnil; + CHECK_NUMBER (left_width); + if (XINT (left_width) <= 0) + left_width = Qnil; } - if (!NILP (right)) + if (!NILP (right_width)) { - CHECK_NUMBER (right); - if (XINT (right) <= 0) - right = Qnil; + CHECK_NUMBER (right_width); + if (XINT (right_width) <= 0) + right_width = Qnil; } - if (!EQ (w->left_margin_cols, left) - || !EQ (w->right_margin_cols, right)) + if (!EQ (w->left_margin_cols, left_width) + || !EQ (w->right_margin_cols, right_width)) { - w->left_margin_cols = left; - w->right_margin_cols = right; + w->left_margin_cols = left_width; + w->right_margin_cols = right_width; adjust_window_margins (w); @@ -6020,22 +6315,22 @@ the command `set-fringe-style'. If optional fourth arg OUTSIDE-MARGINS is non-nil, draw the fringes outside of the display margins. By default, fringes are drawn between display marginal areas and the text area. */) - (window, left, right, outside_margins) - Lisp_Object window, left, right, outside_margins; + (window, left_width, right_width, outside_margins) + Lisp_Object window, left_width, right_width, outside_margins; { struct window *w = decode_window (window); - if (!NILP (left)) - CHECK_NUMBER (left); - if (!NILP (right)) - CHECK_NUMBER (right); + if (!NILP (left_width)) + CHECK_NATNUM (left_width); + if (!NILP (right_width)) + CHECK_NATNUM (right_width); - if (!EQ (w->left_fringe_width, left) - || !EQ (w->right_fringe_width, right) + if (!EQ (w->left_fringe_width, left_width) + || !EQ (w->right_fringe_width, right_width) || !EQ (w->fringes_outside_margins, outside_margins)) { - w->left_fringe_width = left; - w->right_fringe_width = right; + w->left_fringe_width = left_width; + w->right_fringe_width = right_width; w->fringes_outside_margins = outside_margins; adjust_window_margins (w); @@ -6088,10 +6383,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) @@ -6206,13 +6503,16 @@ If PIXELS-P is non-nil, the return value is VSCROLL. */) : XFLOATINT (vscroll)); w->vscroll = min (w->vscroll, 0); - /* Adjust glyph matrix of the frame if the virtual display - area becomes larger than before. */ - if (w->vscroll < 0 && w->vscroll < old_dy) - adjust_glyphs (f); + if (w->vscroll != old_dy) + { + /* Adjust glyph matrix of the frame if the virtual display + area becomes larger than before. */ + if (w->vscroll < 0 && w->vscroll < old_dy) + adjust_glyphs (f); - /* Prevent redisplay shortcuts. */ - XBUFFER (w->buffer)->prevent_redisplay_optimizations_p = 1; + /* Prevent redisplay shortcuts. */ + XBUFFER (w->buffer)->prevent_redisplay_optimizations_p = 1; + } } return Fwindow_vscroll (window, pixels_p); @@ -6461,6 +6761,7 @@ syms_of_window () { Qwindow_size_fixed = intern ("window-size-fixed"); staticpro (&Qwindow_size_fixed); + Fset (Qwindow_size_fixed, Qnil); staticpro (&Qwindow_configuration_change_hook); Qwindow_configuration_change_hook @@ -6524,6 +6825,10 @@ is displayed in the `mode-line' face. */); doc: /* *Non-nil means `display-buffer' should make a separate frame. */); pop_up_frames = 0; + DEFVAR_BOOL ("auto-window-vscroll", &auto_window_vscroll_p, + doc: /* *Non-nil means to automatically adjust `window-vscroll' to view tall lines. */); + auto_window_vscroll_p = 1; + DEFVAR_BOOL ("display-buffer-reuse-frames", &display_buffer_reuse_frames, doc: /* *Non-nil means `display-buffer' should reuse frames. If the buffer in question is already displayed in a frame, raise that frame. */); @@ -6539,7 +6844,8 @@ where `pop-up-frame-alist' would hold the default frame parameters. */); DEFVAR_LISP ("special-display-buffer-names", &Vspecial_display_buffer_names, doc: /* *List of buffer names that should have their own special frames. -Displaying a buffer whose name is in this list makes a special frame for it +Displaying a buffer with `display-buffer' or `pop-to-buffer', +if its name is in this list, makes a special frame for it using `special-display-function'. See also `special-display-regexps'. An element of the list can be a list instead of just a string. @@ -6564,9 +6870,9 @@ Those variables take precedence over this one. */); DEFVAR_LISP ("special-display-regexps", &Vspecial_display_regexps, doc: /* *List of regexps saying which buffers should have their own special frames. -If a buffer name matches one of these regexps, it gets its own frame. -Displaying a buffer whose name is in this list makes a special frame for it -using `special-display-function'. +When displaying a buffer with `display-buffer' or `pop-to-buffer', +if any regexp in this list matches the buffer name, it makes a +special frame for the buffer by calling `special-display-function'. An element of the list can be a list instead of just a string. There are two ways to use a list as an element: @@ -6654,7 +6960,13 @@ If there is only one window, it is split regardless of this value. */); DEFVAR_LISP ("scroll-preserve-screen-position", &Vscroll_preserve_screen_position, - doc: /* *Non-nil means scroll commands move point to keep its screen line unchanged. */); + doc: /* *Controls if scroll commands move point to keep its screen line unchanged. +A value of nil means point does not keep its screen position except +at the scroll margin or window boundary respectively. +A value of t means point keeps its screen position if the scroll +command moved it vertically out of the window, e.g. when scrolling +by full screens. +Any other value means point always keeps its screen position. */); Vscroll_preserve_screen_position = Qnil; DEFVAR_LISP ("window-configuration-change-hook", @@ -6663,16 +6975,6 @@ If there is only one window, it is split regardless of this value. */); The selected frame is the one whose configuration has changed. */); Vwindow_configuration_change_hook = Qnil; - DEFVAR_BOOL ("window-size-fixed", &window_size_fixed, - doc: /* Non-nil in a buffer means windows displaying the buffer are fixed-size. -If the value is`height', then only the window's height is fixed. -If the value is `width', then only the window's width is fixed. -Any other non-nil value fixes both the width and the height. -Emacs won't change the size of any window displaying that buffer, -unless you explicitly change the size, or Emacs has no other choice. */); - Fmake_variable_buffer_local (Qwindow_size_fixed); - window_size_fixed = 0; - defsubr (&Sselected_window); defsubr (&Sminibuffer_window); defsubr (&Swindow_minibuffer_p);