X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/4d3edcb4c751613c7f0986924db91a1cf9290292..43ccb598c7fc2c5c0601c8f9869a6b6f7a78b121:/src/window.c diff --git a/src/window.c b/src/window.c index f156c46970..d9c807ccf9 100644 --- a/src/window.c +++ b/src/window.c @@ -22,13 +22,13 @@ Boston, MA 02111-1307, USA. */ #include #include "lisp.h" #include "buffer.h" +#include "keyboard.h" #include "frame.h" #include "window.h" #include "commands.h" #include "indent.h" #include "termchar.h" #include "disptab.h" -#include "keyboard.h" #include "dispextern.h" #include "blockinput.h" #include "intervals.h" @@ -62,12 +62,20 @@ static void window_scroll_line_based P_ ((Lisp_Object, int, int, int)); static int window_min_size_1 P_ ((struct window *, int)); static int window_min_size P_ ((struct window *, int, int, int *)); static void size_window P_ ((Lisp_Object, int, int, int)); -static void foreach_window_1 P_ ((struct window *, void (*fn) (), int, int, - int, int)); -static void freeze_window_start P_ ((struct window *, int)); +static int freeze_window_start P_ ((struct window *, void *)); static int window_fixed_size_p P_ ((struct window *, int, int)); static void enlarge_window P_ ((Lisp_Object, int, int)); - +static Lisp_Object window_list P_ ((void)); +static int add_window_to_list P_ ((struct window *, void *)); +static int candidate_window_p P_ ((Lisp_Object, Lisp_Object, Lisp_Object, + Lisp_Object)); +static Lisp_Object next_window P_ ((Lisp_Object, Lisp_Object, + Lisp_Object, int)); +static void decode_next_window_args P_ ((Lisp_Object *, Lisp_Object *, + Lisp_Object *)); +static int foreach_window_1 P_ ((struct window *, + int (* fn) (struct window *, void *), + void *)); /* This is the window in which the terminal's cursor should be left when nothing is being done with it. This must @@ -79,6 +87,12 @@ static void enlarge_window P_ ((Lisp_Object, int, int)); Lisp_Object selected_window; +/* A list of all windows for use by next_window and Fwindow_list. + Functions creating or deleting windows should invalidate this cache + by setting it to nil. */ + +Lisp_Object Vwindow_list; + /* The mini-buffer window of the selected frame. Note that you cannot test for mini-bufferness of an arbitrary window by comparing against this; but you can test for mini-bufferness of @@ -112,6 +126,10 @@ int pop_up_windows; int pop_up_frames; +/* Nonzero means reuse existing frames for displaying buffers. */ + +int display_buffer_reuse_frames; + /* Non-nil means use this function instead of default */ Lisp_Object Vpop_up_frame_function; @@ -238,6 +256,8 @@ make_window () XSETWINDOW (val, p); XSETFASTINT (p->last_point, 0); p->frozen_window_start_p = 0; + + Vwindow_list = Qnil; return val; } @@ -458,10 +478,12 @@ coordinates_in_window (w, x, y) int left_x, right_x, top_y, bottom_y; int flags_area_width = FRAME_LEFT_FLAGS_AREA_WIDTH (f); + /* 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); + right_x = XFASTINT (w->width) * CANON_Y_UNIT (f) - 1; top_y = WINDOW_DISPLAY_TOP_EDGE_PIXEL_Y (w); bottom_y = WINDOW_DISPLAY_BOTTOM_EDGE_PIXEL_Y (w); } @@ -469,7 +491,7 @@ coordinates_in_window (w, x, y) { left_x = (WINDOW_DISPLAY_LEFT_EDGE_PIXEL_X (w) - FRAME_INTERNAL_BORDER_WIDTH_SAFE (f)); - right_x = WINDOW_DISPLAY_RIGHT_EDGE_PIXEL_X (w); + right_x = WINDOW_DISPLAY_RIGHT_EDGE_PIXEL_X (w) - 1; top_y = (WINDOW_DISPLAY_TOP_EDGE_PIXEL_Y (w) - FRAME_INTERNAL_BORDER_WIDTH_SAFE (f)); bottom_y = WINDOW_DISPLAY_BOTTOM_EDGE_PIXEL_Y (w); @@ -584,50 +606,84 @@ If they are on the border between WINDOW and its right sibling,\n\ } } + +/* Callback for foreach_window, used in window_from_coordinates. + Check if window W contains coordinates specified by USER_DATA which + is actually a pointer to a struct check_window_data CW. + + Check if window W contains coordinates *CW->x and *CW->y. If it + does, return W in *CW->window, as Lisp_Object, and return in + *CW->part the part of the window under coordinates *X,*Y. Return + zero from this function to stop iterating over windows. */ + +struct check_window_data +{ + Lisp_Object *window; + int *x, *y, *part; +}; + +static int +check_window_containing (w, user_data) + struct window *w; + void *user_data; +{ + struct check_window_data *cw = (struct check_window_data *) user_data; + int found; + + found = coordinates_in_window (w, cw->x, cw->y); + if (found) + { + *cw->part = found - 1; + XSETWINDOW (*cw->window, w); + } + + return !found; +} + + /* Find the window containing frame-relative pixel position X/Y and return it as a Lisp_Object. If X, Y is on the window's modeline, set *PART to 1; if it is on the separating line between the window and its right sibling, set it to 2; otherwise set it to 0. If there is no window under X, Y return nil and leave *PART - unmodified. TOOL_BAR_P non-zero means detect tool-bar windows. */ + unmodified. TOOL_BAR_P non-zero means detect tool-bar windows. + + This function was previously implemented with a loop cycling over + windows with Fnext_window, and starting with the frame's selected + window. It turned out that this doesn't work with an + implementation of next_window using Vwindow_list, because + FRAME_SELECTED_WINDOW (F) is not always contained in the window + tree of F when this function is called asynchronously from + note_mouse_highlight. The original loop didn't terminate in this + case. */ Lisp_Object -window_from_coordinates (frame, x, y, part, tool_bar_p) - FRAME_PTR frame; +window_from_coordinates (f, x, y, part, tool_bar_p) + struct frame *f; int x, y; int *part; int tool_bar_p; { - register Lisp_Object tem, first; - int found; - - tem = first = FRAME_SELECTED_WINDOW (frame); - - do - { - found = coordinates_in_window (XWINDOW (tem), &x, &y); - - if (found) - { - *part = found - 1; - return tem; - } - - tem = Fnext_window (tem, Qt, Qlambda); - } - while (!EQ (tem, first)); + Lisp_Object window; + struct check_window_data cw; - /* See if it's in the tool bar window, if a tool bar exists. */ - if (tool_bar_p - && WINDOWP (frame->tool_bar_window) - && XFASTINT (XWINDOW (frame->tool_bar_window)->height) - && coordinates_in_window (XWINDOW (frame->tool_bar_window), &x, &y)) + window = Qnil; + cw.window = &window, cw.x = &x, cw.y = &y; cw.part = part; + foreach_window (f, check_window_containing, &cw); + + /* If not found above, see if it's in the tool bar window, if a tool + bar exists. */ + if (NILP (window) + && tool_bar_p + && WINDOWP (f->tool_bar_window) + && XINT (XWINDOW (f->tool_bar_window)->height) > 0 + && coordinates_in_window (XWINDOW (f->tool_bar_window), &x, &y)) { *part = 0; - return frame->tool_bar_window; + window = f->tool_bar_window; } - return Qnil; + return window; } DEFUN ("window-at", Fwindow_at, Swindow_at, 2, 3, 0, @@ -727,29 +783,29 @@ if it isn't already recorded.") && ! (! NILP (w->window_end_valid) && XFASTINT (w->last_modified) >= MODIFF)) { - int opoint = PT, opoint_byte = PT_BYTE; + struct text_pos startp; + struct it it; /* In case W->start is out of the range, use something reasonable. This situation occured when loading a file with `-l' containing a call to `rmail' with subsequent other commands. At the end, W->start happened to be BEG, while - rmail had already narrowed the buffer. This leads to an - abort in temp_set_pt_both. */ + rmail had already narrowed the buffer. */ if (XMARKER (w->start)->charpos < BEGV) - TEMP_SET_PT_BOTH (BEGV, BEGV_BYTE); + SET_TEXT_POS (startp, BEGV, BEGV_BYTE); else if (XMARKER (w->start)->charpos > ZV) - TEMP_SET_PT_BOTH (ZV, ZV_BYTE); + SET_TEXT_POS (startp, ZV, ZV_BYTE); else - TEMP_SET_PT_BOTH (XMARKER (w->start)->charpos, - XMARKER (w->start)->bytepos); - - Fvertical_motion (make_number (window_internal_height (w)), Qnil); - XSETINT (value, PT); - TEMP_SET_PT_BOTH (opoint, opoint_byte); + SET_TEXT_POS_FROM_MARKER (startp, w->start); + + /* Cannot use Fvertical_motion because that function doesn't + cope with variable-height lines. */ + start_display (&it, w, startp); + move_it_vertically (&it, window_box_height (w)); + value = make_number (IT_CHARPOS (it)); } else - XSETINT (value, - BUF_Z (XBUFFER (buf)) - XFASTINT (w->window_end_pos)); + XSETINT (value, BUF_Z (XBUFFER (buf)) - XFASTINT (w->window_end_pos)); return value; } @@ -843,20 +899,21 @@ struct Lisp_Char_Table * window_display_table (w) struct window *w; { - Lisp_Object tem; - tem = w->display_table; - if (DISP_TABLE_P (tem)) - return XCHAR_TABLE (tem); - if (NILP (w->buffer)) - return 0; + struct Lisp_Char_Table *dp = NULL; - tem = XBUFFER (w->buffer)->display_table; - if (DISP_TABLE_P (tem)) - return XCHAR_TABLE (tem); - tem = Vstandard_display_table; - if (DISP_TABLE_P (tem)) - return XCHAR_TABLE (tem); - return 0; + if (DISP_TABLE_P (w->display_table)) + dp = XCHAR_TABLE (w->display_table); + else if (BUFFERP (w->buffer)) + { + struct buffer *b = XBUFFER (w->buffer); + + if (DISP_TABLE_P (b->display_table)) + dp = XCHAR_TABLE (b->display_table); + else if (DISP_TABLE_P (Vstandard_display_table)) + dp = XCHAR_TABLE (Vstandard_display_table); + } + + return dp; } DEFUN ("set-window-display-table", Fset_window_display_table, Sset_window_display_table, 2, 2, 0, @@ -1019,6 +1076,7 @@ delete_window (window) par = XWINDOW (parent); windows_or_buffers_changed++; + Vwindow_list = Qnil; frame = XFRAME (WINDOW_FRAME (p)); FRAME_WINDOW_SIZES_CHANGED (frame) = 1; @@ -1131,9 +1189,237 @@ delete_window (window) adjust_glyphs (frame); UNBLOCK_INPUT; } + + +/*********************************************************************** + Window List + ***********************************************************************/ + +/* Add window W to *USER_DATA. USER_DATA is actually a Lisp_Object + pointer. This is a callback function for foreach_window, used in + function window_list. */ + +static int +add_window_to_list (w, user_data) + struct window *w; + void *user_data; +{ + Lisp_Object *list = (Lisp_Object *) user_data; + Lisp_Object window; + XSETWINDOW (window, w); + *list = Fcons (window, *list); + return 1; +} + + +/* Return a list of all windows, for use by next_window. If + Vwindow_list is a list, return that list. Otherwise, build a new + list, cache it in Vwindow_list, and return that. */ + +static Lisp_Object +window_list () +{ + if (!CONSP (Vwindow_list)) + { + Lisp_Object tail; + + Vwindow_list = Qnil; + for (tail = Vframe_list; CONSP (tail); tail = XCDR (tail)) + { + Lisp_Object args[2]; + + /* We are visiting windows in canonical order, and add + new windows at the front of args[1], which means we + have to reverse this list at the end. */ + args[1] = Qnil; + foreach_window (XFRAME (XCAR (tail)), add_window_to_list, &args[1]); + args[0] = Vwindow_list; + args[1] = Fnreverse (args[1]); + Vwindow_list = Fnconc (2, args); + } + } + + return Vwindow_list; +} + + +/* Value is non-zero if WINDOW satisfies the constraints given by + OWINDOW, MINIBUF and ALL_FRAMES. + + MINIBUF t means WINDOW may be minibuffer windows. + `lambda' means WINDOW may not be a minibuffer window. + a window means a specific minibuffer window + + ALL_FRAMES t means search all frames, + nil means search just current frame, + `visible' means search just visible frames, + 0 means search visible and iconified frames, + a window means search the frame that window belongs to, + a frame means consider windows on that frame, only. */ + +static int +candidate_window_p (window, owindow, minibuf, all_frames) + Lisp_Object window, owindow, minibuf, all_frames; +{ + struct window *w = XWINDOW (window); + struct frame *f = XFRAME (w->frame); + int candidate_p = 1; + + if (!BUFFERP (w->buffer)) + candidate_p = 0; + else if (MINI_WINDOW_P (w) + && (EQ (minibuf, Qlambda) + || (WINDOWP (minibuf) && !EQ (minibuf, window)))) + { + /* If MINIBUF is `lambda' don't consider any mini-windows. + If it is a window, consider only that one. */ + candidate_p = 0; + } + else if (EQ (all_frames, Qt)) + candidate_p = 1; + else if (NILP (all_frames)) + { + xassert (WINDOWP (owindow)); + candidate_p = EQ (w->frame, XWINDOW (owindow)->frame); + } + else if (EQ (all_frames, Qvisible)) + { + FRAME_SAMPLE_VISIBILITY (f); + candidate_p = FRAME_VISIBLE_P (f); + } + else if (INTEGERP (all_frames) && XINT (all_frames) == 0) + { + FRAME_SAMPLE_VISIBILITY (f); + candidate_p = FRAME_VISIBLE_P (f) || FRAME_ICONIFIED_P (f); + } + else if (WINDOWP (all_frames)) + candidate_p = (EQ (FRAME_MINIBUF_WINDOW (f), all_frames) + || EQ (XWINDOW (all_frames)->frame, w->frame) + || EQ (XWINDOW (all_frames)->frame, FRAME_FOCUS_FRAME (f))); + else if (FRAMEP (all_frames)) + candidate_p = EQ (all_frames, w->frame); + + return candidate_p; +} + + +/* Decode arguments as allowed by Fnext_window, Fprevious_window, and + Fwindow_list. See there for the meaning of WINDOW, MINIBUF, and + ALL_FRAMES. */ + +static void +decode_next_window_args (window, minibuf, all_frames) + Lisp_Object *window, *minibuf, *all_frames; +{ + if (NILP (*window)) + *window = selected_window; + else + CHECK_LIVE_WINDOW (*window, 0); + + /* MINIBUF nil may or may not include minibuffers. Decide if it + does. */ + if (NILP (*minibuf)) + *minibuf = minibuf_level ? minibuf_window : Qlambda; + else if (!EQ (*minibuf, Qt)) + *minibuf = Qlambda; + + /* Now *MINIBUF can be t => count all minibuffer windows, `lambda' + => count none of them, or a specific minibuffer window (the + active one) to count. */ + + /* ALL_FRAMES nil doesn't specify which frames to include. */ + if (NILP (*all_frames)) + *all_frames = (!EQ (*minibuf, Qlambda) + ? FRAME_MINIBUF_WINDOW (XFRAME (XWINDOW (*window)->frame)) + : Qnil); + else if (EQ (*all_frames, Qvisible)) + ; + else if (XFASTINT (*all_frames) == 0) + ; + else if (FRAMEP (*all_frames)) + ; + else if (!EQ (*all_frames, Qt)) + *all_frames = Qnil; + + /* Now *ALL_FRAMES is t meaning search all frames, nil meaning + search just current frame, `visible' meaning search just visible + frames, 0 meaning search visible and iconified frames, or a + window, meaning search the frame that window belongs to, or a + frame, meaning consider windows on that frame, only. */ +} + + +/* Return the next or previous window of WINDOW in canonical ordering + of windows. NEXT_P non-zero means return the next window. See the + documentation string of next-window for the meaning of MINIBUF and + ALL_FRAMES. */ + +static Lisp_Object +next_window (window, minibuf, all_frames, next_p) + Lisp_Object window, minibuf, all_frames; + int next_p; +{ + decode_next_window_args (&window, &minibuf, &all_frames); + + /* If ALL_FRAMES is a frame, and WINDOW isn't on that frame, just + return the first window on the frame. */ + if (FRAMEP (all_frames) + && !EQ (all_frames, XWINDOW (window)->frame)) + return Fframe_first_window (all_frames); + + if (next_p) + { + Lisp_Object list; + + /* Find WINDOW in the list of all windows. */ + list = Fmemq (window, window_list ()); + + /* Scan forward from WINDOW to the end of the window list. */ + if (CONSP (list)) + for (list = XCDR (list); CONSP (list); list = XCDR (list)) + if (candidate_window_p (XCAR (list), window, minibuf, all_frames)) + break; + + /* Scan from the start of the window list up to WINDOW. */ + if (!CONSP (list)) + for (list = Vwindow_list; + CONSP (list) && !EQ (XCAR (list), window); + list = XCDR (list)) + if (candidate_window_p (XCAR (list), window, minibuf, all_frames)) + break; + + if (CONSP (list)) + window = XCAR (list); + } + else + { + Lisp_Object candidate, list; + + /* Scan through the list of windows for candidates. If there are + candidate windows in front of WINDOW, the last one of these + is the one we want. If there are candidates following WINDOW + in the list, again the last one of these is the one we want. */ + candidate = Qnil; + for (list = window_list (); CONSP (list); list = XCDR (list)) + { + if (EQ (XCAR (list), window)) + { + if (WINDOWP (candidate)) + break; + } + else if (candidate_window_p (XCAR (list), window, minibuf, + all_frames)) + candidate = XCAR (list); + } + + if (WINDOWP (candidate)) + window = candidate; + } + + return window; +} -extern Lisp_Object next_frame (), prev_frame (); /* This comment supplies the doc string for `next-window', for make-docfile to see. We cannot put this in the real DEFUN @@ -1170,114 +1456,12 @@ windows, eventually ending up back at the window you started with.\n\ DEFUN ("next-window", Fnext_window, Snext_window, 0, 3, 0, 0) (window, minibuf, all_frames) - register Lisp_Object window, minibuf, all_frames; + Lisp_Object window, minibuf, all_frames; { - register Lisp_Object tem; - Lisp_Object start_window; - - if (NILP (window)) - window = selected_window; - else - CHECK_LIVE_WINDOW (window, 0); - - start_window = window; - - /* minibuf == nil may or may not include minibuffers. - Decide if it does. */ - if (NILP (minibuf)) - minibuf = (minibuf_level ? minibuf_window : Qlambda); - else if (! EQ (minibuf, Qt)) - minibuf = Qlambda; - /* Now minibuf can be t => count all minibuffer windows, - lambda => count none of them, - or a specific minibuffer window (the active one) to count. */ - - /* all_frames == nil doesn't specify which frames to include. */ - if (NILP (all_frames)) - all_frames = (! EQ (minibuf, Qlambda) - ? (FRAME_MINIBUF_WINDOW - (XFRAME - (WINDOW_FRAME - (XWINDOW (window))))) - : Qnil); - else if (EQ (all_frames, Qvisible)) - ; - else if (XFASTINT (all_frames) == 0) - ; - else if (FRAMEP (all_frames) && ! EQ (all_frames, Fwindow_frame (window))) - /* If all_frames is a frame and window arg isn't on that frame, just - return the first window on the frame. */ - return Fframe_first_window (all_frames); - else if (! EQ (all_frames, Qt)) - all_frames = Qnil; - /* Now all_frames is t meaning search all frames, - nil meaning search just current frame, - visible meaning search just visible frames, - 0 meaning search visible and iconified frames, - or a window, meaning search the frame that window belongs to. */ - - /* Do this loop at least once, to get the next window, and perhaps - again, if we hit the minibuffer and that is not acceptable. */ - do - { - /* Find a window that actually has a next one. This loop - climbs up the tree. */ - while (tem = XWINDOW (window)->next, NILP (tem)) - if (tem = XWINDOW (window)->parent, !NILP (tem)) - window = tem; - else - { - /* We've reached the end of this frame. - Which other frames are acceptable? */ - tem = WINDOW_FRAME (XWINDOW (window)); - if (! NILP (all_frames)) - { - Lisp_Object tem1; - - tem1 = tem; - tem = next_frame (tem, all_frames); - /* In the case where the minibuffer is active, - and we include its frame as well as the selected one, - next_frame may get stuck in that frame. - If that happens, go back to the selected frame - so we can complete the cycle. */ - if (EQ (tem, tem1)) - tem = selected_frame; - } - tem = FRAME_ROOT_WINDOW (XFRAME (tem)); - - break; - } - - window = tem; - - /* If we're in a combination window, find its first child and - recurse on that. Otherwise, we've found the window we want. */ - while (1) - { - if (!NILP (XWINDOW (window)->hchild)) - window = XWINDOW (window)->hchild; - else if (!NILP (XWINDOW (window)->vchild)) - window = XWINDOW (window)->vchild; - else break; - } - - QUIT; - } - /* Which windows are acceptable? - Exit the loop and accept this window if - this isn't a minibuffer window, - or we're accepting all minibuffer windows, - or this is the active minibuffer and we are accepting that one, or - we've come all the way around and we're back at the original window. */ - while (MINI_WINDOW_P (XWINDOW (window)) - && ! EQ (minibuf, Qt) - && ! EQ (minibuf, window) - && ! EQ (window, start_window)); - - return window; + return next_window (window, minibuf, all_frames, 1); } + /* This comment supplies the doc string for `previous-window', for make-docfile to see. We cannot put this in the real DEFUN due to limits in the Unix cpp. @@ -1314,128 +1498,12 @@ windows, eventually ending up back at the window you started with.\n\ DEFUN ("previous-window", Fprevious_window, Sprevious_window, 0, 3, 0, 0) (window, minibuf, all_frames) - register Lisp_Object window, minibuf, all_frames; + Lisp_Object window, minibuf, all_frames; { - register Lisp_Object tem; - Lisp_Object start_window; - - if (NILP (window)) - window = selected_window; - else - CHECK_LIVE_WINDOW (window, 0); - - start_window = window; - - /* minibuf == nil may or may not include minibuffers. - Decide if it does. */ - if (NILP (minibuf)) - minibuf = (minibuf_level ? minibuf_window : Qlambda); - else if (! EQ (minibuf, Qt)) - minibuf = Qlambda; - /* Now minibuf can be t => count all minibuffer windows, - lambda => count none of them, - or a specific minibuffer window (the active one) to count. */ - - /* all_frames == nil doesn't specify which frames to include. - Decide which frames it includes. */ - if (NILP (all_frames)) - all_frames = (! EQ (minibuf, Qlambda) - ? (FRAME_MINIBUF_WINDOW - (XFRAME - (WINDOW_FRAME - (XWINDOW (window))))) - : Qnil); - else if (EQ (all_frames, Qvisible)) - ; - else if (XFASTINT (all_frames) == 0) - ; - else if (FRAMEP (all_frames) && ! EQ (all_frames, Fwindow_frame (window))) - /* If all_frames is a frame and window arg isn't on that frame, just - return the first window on the frame. */ - return Fframe_first_window (all_frames); - else if (! EQ (all_frames, Qt)) - all_frames = Qnil; - /* Now all_frames is t meaning search all frames, - nil meaning search just current frame, - visible meaning search just visible frames, - 0 meaning search visible and iconified frames, - or a window, meaning search the frame that window belongs to. */ - - /* Do this loop at least once, to get the previous window, and perhaps - again, if we hit the minibuffer and that is not acceptable. */ - do - { - /* Find a window that actually has a previous one. This loop - climbs up the tree. */ - while (tem = XWINDOW (window)->prev, NILP (tem)) - if (tem = XWINDOW (window)->parent, !NILP (tem)) - window = tem; - else - { - /* We have found the top window on the frame. - Which frames are acceptable? */ - tem = WINDOW_FRAME (XWINDOW (window)); - if (! NILP (all_frames)) - /* It's actually important that we use prev_frame here, - rather than next_frame. All the windows acceptable - according to the given parameters should form a ring; - Fnext_window and Fprevious_window should go back and - forth around the ring. If we use next_frame here, - then Fnext_window and Fprevious_window take different - paths through the set of acceptable windows. - window_loop assumes that these `ring' requirement are - met. */ - { - Lisp_Object tem1; - - tem1 = tem; - tem = prev_frame (tem, all_frames); - /* In the case where the minibuffer is active, - and we include its frame as well as the selected one, - next_frame may get stuck in that frame. - If that happens, go back to the selected frame - so we can complete the cycle. */ - if (EQ (tem, tem1)) - tem = selected_frame; - } - /* If this frame has a minibuffer, find that window first, - because it is conceptually the last window in that frame. */ - if (FRAME_HAS_MINIBUF_P (XFRAME (tem))) - tem = FRAME_MINIBUF_WINDOW (XFRAME (tem)); - else - tem = FRAME_ROOT_WINDOW (XFRAME (tem)); - - break; - } - - window = tem; - /* If we're in a combination window, find its last child and - recurse on that. Otherwise, we've found the window we want. */ - while (1) - { - if (!NILP (XWINDOW (window)->hchild)) - window = XWINDOW (window)->hchild; - else if (!NILP (XWINDOW (window)->vchild)) - window = XWINDOW (window)->vchild; - else break; - while (tem = XWINDOW (window)->next, !NILP (tem)) - window = tem; - } - } - /* Which windows are acceptable? - Exit the loop and accept this window if - this isn't a minibuffer window, - or we're accepting all minibuffer windows, - or this is the active minibuffer and we are accepting that one, or - we've come all the way around and we're back at the original window. */ - while (MINI_WINDOW_P (XWINDOW (window)) - && ! EQ (minibuf, Qt) - && ! EQ (minibuf, window) - && ! EQ (window, start_window)); - - return window; + return next_window (window, minibuf, all_frames, 0); } + DEFUN ("other-window", Fother_window, Sother_window, 1, 2, "p", "Select the ARG'th different window on this frame.\n\ All windows on current frame are arranged in a cyclic order.\n\ @@ -1443,28 +1511,43 @@ This command selects the window ARG steps away in that order.\n\ A negative ARG moves in the opposite order. If the optional second\n\ argument ALL_FRAMES is non-nil, cycle through all frames.") (arg, all_frames) - register Lisp_Object arg, all_frames; + Lisp_Object arg, all_frames; { - register int i; - register Lisp_Object w; + Lisp_Object window; + int i; CHECK_NUMBER (arg, 0); - w = selected_window; - i = XINT (arg); - - while (i > 0) - { - w = Fnext_window (w, Qnil, all_frames); - i--; - } - while (i < 0) - { - w = Fprevious_window (w, Qnil, all_frames); - i++; - } - Fselect_window (w); + window = selected_window; + + for (i = XINT (arg); i > 0; --i) + window = Fnext_window (window, Qnil, all_frames); + for (; i < 0; ++i) + window = Fprevious_window (window, Qnil, all_frames); + + Fselect_window (window); return Qnil; } + + +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) + Lisp_Object window, minibuf, all_frames; +{ + Lisp_Object tail, list; + + decode_next_window_args (&window, &minibuf, &all_frames); + list = Qnil; + + for (tail = window_list (); CONSP (tail); tail = XCDR (tail)) + if (candidate_window_p (XCAR (tail), window, minibuf, all_frames)) + list = Fcons (XCAR (tail), list); + + return Fnreverse (list); +} + + /* Look at all windows, performing an operation specified by TYPE with argument OBJ. @@ -1490,32 +1573,31 @@ enum window_loop static Lisp_Object window_loop (type, obj, mini, frames) enum window_loop type; - register Lisp_Object obj, frames; + Lisp_Object obj, frames; int mini; { - register Lisp_Object w; - register Lisp_Object best_window; - register Lisp_Object next_window; - register Lisp_Object last_window; - FRAME_PTR frame; - Lisp_Object frame_arg; - frame_arg = Qt; - + Lisp_Object window, windows, best_window, frame_arg; + struct frame *f; + struct gcpro gcpro1; + /* If we're only looping through windows on a particular frame, frame points to that frame. If we're looping through windows on all frames, frame is 0. */ if (FRAMEP (frames)) - frame = XFRAME (frames); + f = XFRAME (frames); else if (NILP (frames)) - frame = SELECTED_FRAME (); + f = SELECTED_FRAME (); else - frame = 0; - if (frame) + f = NULL; + + if (f) frame_arg = Qlambda; else if (XFASTINT (frames) == 0) frame_arg = frames; else if (EQ (frames, Qvisible)) frame_arg = frames; + else + frame_arg = Qt; /* frame_arg is Qlambda to stick to one frame, Qvisible to consider all visible frames, @@ -1523,11 +1605,11 @@ window_loop (type, obj, mini, frames) /* Pick a window to start with. */ if (WINDOWP (obj)) - w = obj; - else if (frame) - w = FRAME_SELECTED_WINDOW (frame); + window = obj; + else if (f) + window = FRAME_SELECTED_WINDOW (f); else - w = FRAME_SELECTED_WINDOW (SELECTED_FRAME ()); + window = FRAME_SELECTED_WINDOW (SELECTED_FRAME ()); /* Figure out the last window we're going to mess with. Since Fnext_window, given the same options, is guaranteed to go in a @@ -1536,178 +1618,162 @@ window_loop (type, obj, mini, frames) We can't just wait until we hit the first window again, because it might be deleted. */ - last_window = Fprevious_window (w, mini ? Qt : Qnil, frame_arg); - + windows = Fwindow_list (window, mini ? Qt : Qnil, frame_arg); + GCPRO1 (windows); best_window = Qnil; - for (;;) + + for (; CONSP (windows); windows = CDR (windows)) { - /* Pick the next window now, since some operations will delete - the current window. */ - next_window = Fnext_window (w, mini ? Qt : Qnil, frame_arg); - - /* Note that we do not pay attention here to whether - the frame is visible, since Fnext_window skips non-visible frames - if that is desired, under the control of frame_arg. */ - if (! MINI_WINDOW_P (XWINDOW (w)) + struct window *w; + + window = XCAR (windows); + w = XWINDOW (window); + + /* Note that we do not pay attention here to whether the frame + is visible, since Fwindow_list skips non-visible frames if + that is desired, under the control of frame_arg. */ + if (!MINI_WINDOW_P (w) /* For UNSHOW_BUFFER, we must always consider all windows. */ || type == UNSHOW_BUFFER || (mini && minibuf_level > 0)) switch (type) { case GET_BUFFER_WINDOW: - if (XBUFFER (XWINDOW (w)->buffer) == XBUFFER (obj) + if (EQ (w->buffer, obj) /* Don't find any minibuffer window except the one that is currently in use. */ - && (MINI_WINDOW_P (XWINDOW (w)) - ? EQ (w, minibuf_window) : 1)) - return w; + && (MINI_WINDOW_P (w) + ? EQ (window, minibuf_window) + : 1)) + { + UNGCPRO; + return window; + } break; case GET_LRU_WINDOW: /* t as arg means consider only full-width windows */ - if (!NILP (obj) && !WINDOW_FULL_WIDTH_P (XWINDOW (w))) + if (!NILP (obj) && !WINDOW_FULL_WIDTH_P (w)) break; /* Ignore dedicated windows and minibuffers. */ - if (MINI_WINDOW_P (XWINDOW (w)) - || !NILP (XWINDOW (w)->dedicated)) + if (MINI_WINDOW_P (w) || !NILP (w->dedicated)) break; if (NILP (best_window) || (XFASTINT (XWINDOW (best_window)->use_time) - > XFASTINT (XWINDOW (w)->use_time))) - best_window = w; + > XFASTINT (w->use_time))) + best_window = window; break; case DELETE_OTHER_WINDOWS: - if (XWINDOW (w) != XWINDOW (obj)) - Fdelete_window (w); + if (!EQ (window, obj)) + Fdelete_window (window); break; case DELETE_BUFFER_WINDOWS: - if (EQ (XWINDOW (w)->buffer, obj)) + if (EQ (w->buffer, obj)) { - FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (w))); + struct frame *f = XFRAME (WINDOW_FRAME (w)); /* If this window is dedicated, and in a frame of its own, kill the frame. */ - if (EQ (w, FRAME_ROOT_WINDOW (f)) - && !NILP (XWINDOW (w)->dedicated) + if (EQ (window, FRAME_ROOT_WINDOW (f)) + && !NILP (w->dedicated) && other_visible_frames (f)) { /* Skip the other windows on this frame. There might be one, the minibuffer! */ - if (! EQ (w, last_window)) - while (f == XFRAME (WINDOW_FRAME (XWINDOW (next_window)))) - { - /* As we go, check for the end of the loop. - We mustn't start going around a second time. */ - if (EQ (next_window, last_window)) - { - last_window = w; - break; - } - next_window = Fnext_window (next_window, - mini ? Qt : Qnil, - frame_arg); - } + while (CONSP (XCDR (windows)) + && EQ (XWINDOW (XCAR (windows))->frame, + XWINDOW (XCAR (XCDR (windows)))->frame)) + windows = XCDR (windows); + /* Now we can safely delete the frame. */ - Fdelete_frame (WINDOW_FRAME (XWINDOW (w)), Qnil); + Fdelete_frame (w->frame, Qnil); + } + else if (NILP (w->parent)) + { + /* If we're deleting the buffer displayed in the + only window on the frame, find a new buffer to + display there. */ + Lisp_Object buffer; + buffer = Fother_buffer (obj, Qnil, w->frame); + if (NILP (buffer)) + buffer = Fget_buffer_create (build_string ("*scratch*")); + Fset_window_buffer (window, buffer); + if (EQ (window, selected_window)) + Fset_buffer (w->buffer); } else - /* If we're deleting the buffer displayed in the only window - on the frame, find a new buffer to display there. */ - if (NILP (XWINDOW (w)->parent)) - { - Lisp_Object new_buffer; - new_buffer = Fother_buffer (obj, Qnil, - XWINDOW (w)->frame); - if (NILP (new_buffer)) - new_buffer - = Fget_buffer_create (build_string ("*scratch*")); - Fset_window_buffer (w, new_buffer); - if (EQ (w, selected_window)) - Fset_buffer (XWINDOW (w)->buffer); - } - else - Fdelete_window (w); + Fdelete_window (window); } break; case GET_LARGEST_WINDOW: - /* Ignore dedicated windows and minibuffers. */ - if (MINI_WINDOW_P (XWINDOW (w)) - || !NILP (XWINDOW (w)->dedicated) - || NILP (best_window)) - break; { - struct window *best_window_ptr = XWINDOW (best_window); - struct window *w_ptr = XWINDOW (w); - if (NILP (best_window) - || (XFASTINT (w_ptr->height) * XFASTINT (w_ptr->width) - > (XFASTINT (best_window_ptr->height) - * XFASTINT (best_window_ptr->width)))) - best_window = w; + /* Ignore dedicated windows and minibuffers. */ + if (MINI_WINDOW_P (w) || !NILP (w->dedicated)) + break; + + if (NILP (best_window)) + best_window = window; + else + { + struct window *b = XWINDOW (best_window); + if (XFASTINT (w->height) * XFASTINT (w->width) + > XFASTINT (b->height) * XFASTINT (b->width)) + best_window = window; + } } break; case UNSHOW_BUFFER: - if (EQ (XWINDOW (w)->buffer, obj)) + if (EQ (w->buffer, obj)) { + Lisp_Object buffer; + struct frame *f = XFRAME (w->frame); + /* Find another buffer to show in this window. */ - Lisp_Object another_buffer; - FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (w))); - another_buffer = Fother_buffer (obj, Qnil, XWINDOW (w)->frame); - if (NILP (another_buffer)) - another_buffer - = Fget_buffer_create (build_string ("*scratch*")); + buffer = Fother_buffer (obj, Qnil, w->frame); + if (NILP (buffer)) + buffer = Fget_buffer_create (build_string ("*scratch*")); + /* If this window is dedicated, and in a frame of its own, kill the frame. */ - if (EQ (w, FRAME_ROOT_WINDOW (f)) - && !NILP (XWINDOW (w)->dedicated) + if (EQ (window, FRAME_ROOT_WINDOW (f)) + && !NILP (w->dedicated) && other_visible_frames (f)) { /* Skip the other windows on this frame. There might be one, the minibuffer! */ - if (! EQ (w, last_window)) - while (f == XFRAME (WINDOW_FRAME (XWINDOW (next_window)))) - { - /* As we go, check for the end of the loop. - We mustn't start going around a second time. */ - if (EQ (next_window, last_window)) - { - last_window = w; - break; - } - next_window = Fnext_window (next_window, - mini ? Qt : Qnil, - frame_arg); - } + while (CONSP (XCDR (windows)) + && EQ (XWINDOW (XCAR (windows))->frame, + XWINDOW (XCAR (XCDR (windows)))->frame)) + windows = XCDR (windows); + /* Now we can safely delete the frame. */ - Fdelete_frame (WINDOW_FRAME (XWINDOW (w)), Qnil); + Fdelete_frame (w->frame, Qnil); } else { /* Otherwise show a different buffer in the window. */ - XWINDOW (w)->dedicated = Qnil; - Fset_window_buffer (w, another_buffer); - if (EQ (w, selected_window)) - Fset_buffer (XWINDOW (w)->buffer); + w->dedicated = Qnil; + Fset_window_buffer (window, buffer); + if (EQ (window, selected_window)) + Fset_buffer (w->buffer); } } break; /* Check for a window that has a killed buffer. */ case CHECK_ALL_WINDOWS: - if (! NILP (XWINDOW (w)->buffer) - && NILP (XBUFFER (XWINDOW (w)->buffer)->name)) + if (! NILP (w->buffer) + && NILP (XBUFFER (w->buffer)->name)) abort (); + break; } - - if (EQ (w, last_window)) - break; - - w = next_window; } + UNGCPRO; return best_window; } @@ -2662,6 +2728,8 @@ unless the window is the selected window and the optional second\n\ argument NOT-THIS-WINDOW is non-nil (interactively, with prefix arg).\n\ If `pop-up-frames' is non-nil, make a new frame if no window shows BUFFER.\n\ Returns the window displaying BUFFER.\n\ +If `display-reuse-frames' is non-nil, and another frame is currently\n\ +displaying BUFFER, then simply raise that frame.\n\ \n\ The variables `special-display-buffer-names', `special-display-regexps',\n\ `same-window-buffer-names', and `same-window-regexps' customize how certain\n\ @@ -2673,7 +2741,7 @@ If FRAME is t, search all frames.\n\ 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' is non-nil,\n\ + unless `pop-up-frames' or `display-reuse-frames' is non-nil,\n\ which means search visible and iconified frames.") (buffer, not_this_window, frame) register Lisp_Object buffer, not_this_window, frame; @@ -2704,21 +2772,22 @@ If FRAME is nil, search only the selected frame\n\ } } - /* If pop_up_frames, + /* If the user wants pop-up-frames or display-reuse-frames, then look for a window showing BUFFER on any visible or iconified frame. Otherwise search only the current frame. */ if (! NILP (frame)) tem = frame; - else if (pop_up_frames || last_nonminibuf_frame == 0) + else if (pop_up_frames + || display_buffer_reuse_frames + || last_nonminibuf_frame == 0) XSETFASTINT (tem, 0); else XSETFRAME (tem, last_nonminibuf_frame); + window = Fget_buffer_window (buffer, tem); if (!NILP (window) && (NILP (not_this_window) || !EQ (window, selected_window))) - { - return display_buffer_1 (window); - } + return display_buffer_1 (window); /* Certain buffer names get special handling. */ if (!NILP (Vspecial_display_function) && NILP (swp)) @@ -4214,6 +4283,36 @@ redraws with point in the center of the current window.") return Qnil; } + + +/* Value is the number of lines actually displayed in window W, + as opposed to its height. */ + +static int +displayed_window_lines (w) + struct window *w; +{ + struct it it; + struct text_pos start; + int height = window_box_height (w); + + SET_TEXT_POS_FROM_MARKER (start, w->start); + start_display (&it, w, start); + move_it_vertically (&it, height); + + /* Add in empty lines at the bottom of the window. */ + if (it.current_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); + it.vpos += lines; + } + + return it.vpos; +} + + DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line, 1, 1, "P", @@ -4222,26 +4321,17 @@ With no argument, position point at center of window.\n\ An argument specifies vertical position within the window;\n\ zero means top of window, negative means relative to bottom of window.") (arg) - register Lisp_Object arg; + Lisp_Object arg; { - register struct window *w = XWINDOW (selected_window); - register int height = window_internal_height (w); - register int start; + struct window *w = XWINDOW (selected_window); + int lines, start; Lisp_Object window; - if (NILP (arg)) - XSETFASTINT (arg, height / 2); - else - { - arg = Fprefix_numeric_value (arg); - if (XINT (arg) < 0) - XSETINT (arg, XINT (arg) + height); - } - + window = selected_window; start = marker_position (w->start); - XSETWINDOW (window, w); if (start < BEGV || start > ZV) { + int height = window_internal_height (w); Fvertical_motion (make_number (- (height / 2)), window); set_marker_both (w->start, w->buffer, PT, PT_BYTE); w->start_at_line_beg = Fbolp (); @@ -4250,6 +4340,16 @@ zero means top of window, negative means relative to bottom of window.") else Fgoto_char (w->start); + lines = displayed_window_lines (w); + if (NILP (arg)) + XSETFASTINT (arg, lines / 2); + else + { + arg = Fprefix_numeric_value (arg); + if (XINT (arg) < 0) + XSETINT (arg, XINT (arg) + lines); + } + return Fvertical_motion (arg, window); } @@ -4650,6 +4750,8 @@ delete_all_subwindows (w) w->buffer = Qnil; w->vchild = Qnil; w->hchild = Qnil; + + Vwindow_list = Qnil; } static int @@ -4997,58 +5099,65 @@ non-negative multiple of the canonical character height of WINDOW.") /* Call FN for all leaf windows on frame F. FN is called with the first argument being a pointer to the leaf window, and with - additional arguments A1..A4. */ + additional argument USER_DATA. Stops when FN returns 0. */ void -foreach_window (f, fn, a1, a2, a3, a4) +foreach_window (f, fn, user_data) struct frame *f; - void (* fn) (); - int a1, a2, a3, a4; + int (* fn) P_ ((struct window *, void *)); + void *user_data; { - foreach_window_1 (XWINDOW (FRAME_ROOT_WINDOW (f)), fn, a1, a2, a3, a4); + foreach_window_1 (XWINDOW (FRAME_ROOT_WINDOW (f)), fn, user_data); } /* Helper function for foreach_window. Call FN for all leaf windows reachable from W. FN is called with the first argument being a - pointer to the leaf window, and with additional arguments A1..A4. */ + pointer to the leaf window, and with additional argument USER_DATA. + Stop when FN returns 0. Value is 0 if stopped by FN. */ -static void -foreach_window_1 (w, fn, a1, a2, a3, a4) +static int +foreach_window_1 (w, fn, user_data) struct window *w; - void (* fn) (); - int a1, a2, a3, a4; + int (* fn) P_ ((struct window *, void *)); + void *user_data; { - while (w) + int cont; + + for (cont = 1; w && cont;) { if (!NILP (w->hchild)) - foreach_window_1 (XWINDOW (w->hchild), fn, a1, a2, a3, a4); + cont = foreach_window_1 (XWINDOW (w->hchild), fn, user_data); else if (!NILP (w->vchild)) - foreach_window_1 (XWINDOW (w->vchild), fn, a1, a2, a3, a4); - else - fn (w, a1, a2, a3, a4); + cont = foreach_window_1 (XWINDOW (w->vchild), fn, user_data); + else + cont = fn (w, user_data); w = NILP (w->next) ? 0 : XWINDOW (w->next); } + + return cont; } /* Freeze or unfreeze the window start of W if unless it is a - mini-window or the selected window. FREEZE_P non-zero means freeze + mini-window or the selected window. FREEZE_P non-null means freeze the window start. */ -static void +static int freeze_window_start (w, freeze_p) struct window *w; - int freeze_p; + void *freeze_p; { if (w == XWINDOW (selected_window) || MINI_WINDOW_P (w) || (MINI_WINDOW_P (XWINDOW (selected_window)) + && ! NILP (Vminibuf_scroll_window) && w == XWINDOW (Vminibuf_scroll_window))) - freeze_p = 0; + freeze_p = NULL; - w->frozen_window_start_p = freeze_p; + w->frozen_window_start_p = freeze_p != NULL; + return 1; } @@ -5061,7 +5170,7 @@ freeze_window_starts (f, freeze_p) struct frame *f; int freeze_p; { - foreach_window (f, freeze_window_start, freeze_p); + foreach_window (f, freeze_window_start, (void *) freeze_p); } @@ -5199,6 +5308,12 @@ init_window_once () window_initialized = 1; } +void +init_window () +{ + Vwindow_list = Qnil; +} + void syms_of_window () { @@ -5226,6 +5341,8 @@ syms_of_window () Qtemp_buffer_show_hook = intern ("temp-buffer-show-hook"); staticpro (&Qtemp_buffer_show_hook); + staticpro (&Vwindow_list); + DEFVAR_LISP ("temp-buffer-show-function", &Vtemp_buffer_show_function, "Non-nil means call as function to display a help buffer.\n\ The function is called with one argument, the buffer to be displayed.\n\ @@ -5254,6 +5371,11 @@ work using this function."); "*Non-nil means `display-buffer' should make a separate frame."); pop_up_frames = 0; + DEFVAR_BOOL ("display-buffer-reuse-frames", &display_buffer_reuse_frames, + "*Non-nil means `display-buffer' should reuse frames.\n\ +If the buffer in question is already displayed in a frame, raise that frame."); + display_buffer_reuse_frames = 0; + DEFVAR_LISP ("pop-up-frame-function", &Vpop_up_frame_function, "Function to call to handle automatic new frame creation.\n\ It is called with no arguments and should return a newly created frame.\n\ @@ -5433,6 +5555,7 @@ The selected frame is the one whose configuration has changed."); defsubr (&Swindow_vscroll); defsubr (&Sset_window_vscroll); defsubr (&Scompare_window_configurations); + defsubr (&Swindow_list); } void