/************************************************************************ * This program is Copyright (C) 1986 by Jonathan Payne. JOVE is * * provided to you without charge, and with no warranty. You may give * * away copies of JOVE, including sources, provided that this notice is * * included in all the files. * ************************************************************************/ /* This creates/deletes/divides/grows/shrinks windows. */ #include "jove.h" RCS("$Id: wind.c,v 14.32 1993/06/21 21:52:28 tom Exp tom $") #include "ctype.h" #include "maps.h" #include "termcap.h" private const char onlyone[] = "You only have one window!", toosmall[] = "Resulting window would be too small."; Window *curwind ZERO, *fwind ZERO; /* First line in a Window */ int FLine(w) register const Window *w; { register Window *wp = fwind; register int lineno = 0; while (wp != w) { lineno += wp->w_height; wp = wp->w_next; if (wp == fwind) complain("?window?"); } return lineno; } /* Set the topline of the window, calculating its number in the buffer. This is for numbering the lines only. */ void SetTop(w, line) register Window *w; register Line *line; { w->w_top = line; #ifdef AUTOSCROLL w->w_flags |= W_NOAUTOSCROLL; #endif if ( #ifdef WINDOWS w->w_control || #endif w->w_flags & (W_NUMLINES|W_TOPNUM)) w->w_topnum = lineno(line); } private void OnlyOne __(( void )); private void OnlyOne() { register const Window *w = fwind; if (one_windp(w)) complain(onlyone); } /* Make NEW the current Window */ void SetWind(new) register Window *new; { register Buffer *cb = curbuf; if (!Asking) { /* can you say kludge? */ register Window *cw = curwind; cw->w_line = /*curline*/ cb->b_dot; cw->w_char = /*curchar*/ cb->b_char; cw->w_bufp = /*curbuf*/ cb; } cb = new->w_bufp; SetBuf(cb); if (!inlist(/*curbuf*/ cb->b_first, new->w_line)) { new->w_line = /*curline*/ cb->b_dot; new->w_char = /*curchar*/ cb->b_char; } DotTo(new->w_line, new->w_char); #if NO /* [TRH] this is now assured by DotTo */ { register int len = strlen(linebuf); if (new->w_char > len) new->w_char = /*curchar*/ cb->b_char = len; } #endif curwind = new; } /* Delete `wp' from the screen. If it is the only window left on the screen, then complain. It gives its body to the next window if there is one, otherwise the previous window gets the body. */ void del_wind(wp) register Window *wp; { register Window *prev = wp->w_prev, *next = wp->w_next; OnlyOne(); prev->w_next = next; next->w_prev = prev; if (fwind == wp) { fwind = next; SetTop(next, prev_line(next->w_top, wp->w_height)); prev = next; /* this is a cheat... */ } prev->w_height += wp->w_height; if (curwind == wp) SetWind(prev); #ifdef WINDOWS RemoveScrollBar(wp); Windchange++; #endif free((void_*) wp); } /* Divide the window WP N times, or at least once. Complains if WP is too small to be split into that many pieces. It returns the new window. */ Window * div_wind(wp, n) register Window *wp; { register Window *new; register int amt; if (++n < 2) n = 2; amt = wp->w_height / n; if (amt < 2) complain(toosmall); while (--n) { new = (Window *) emalloc(sizeof (Window)); new->w_flags = 0; new->w_offset = 0; new->w_height = amt; wp->w_height -= amt; /* set the lines such that w_line is the center in each Window --NO: let redisplay handle that. */ new->w_line = wp->w_line; new->w_char = wp->w_char; new->w_bufp = wp->w_bufp; new->w_top = wp->w_top; /* prev_line(new->w_line, HALF(new)); */ /* Link the new window into the list */ new->w_prev = wp; new->w_next = wp->w_next; new->w_next->w_prev = new; wp->w_next = new; #ifdef WINDOWS new->w_control = 0; Windchange++; #endif } return new; } /* Initialize the first window setting the bounds to the size of the screen. There is no buffer with this window. See parse for the setting of this window. */ void winit() { register Window *w; curwind = fwind = w = (Window *) emalloc(sizeof (Window)); bzero(w, sizeof(Window)); w->w_next = w->w_prev = w; w->w_height = ILI; #ifdef WINDOWS Windchange++; #endif } /* Change window into the next window. Curwind becomes the new window. */ DEF_CMD( "next-window", NextWindow, NO ) { OnlyOne(); SetWind(curwind->w_next); } DEF_CMD( "previous-window", PrevWindow, NO ) { OnlyOne(); SetWind(curwind->w_prev); } /* delete the current window if it isn't the only one left */ DEF_CMD( "delete-current-window", DelCurWindow, NO ) { register Window *cw = curwind; register Buffer *b; if (exp_p) if (exp < 0) cw = cw->w_prev; else cw = cw->w_next; b = cw->w_bufp; del_wind(cw); if (b != curbuf) SetABuf(b); } /* put the current line of `w' in the middle of the window */ void CentWind(w) register Window *w; { SetTop(w, prev_line(w->w_line, HALF(w))); } DEF_INT( "scroll-step", ScrollStep, V_BASE10) ZERO; _IF(def PRIVATE) /* Calculate the new topline of the window. If ScrollStep == 0 it means we should center the current line in the window. */ void CalcWind(w) register Window *w; { register Line *newtop; register int nlines, up; if ((nlines = ScrollStep) > 0 && (up = inorder(w->w_line, 0, w->w_top, 0)) >= 0) { /* nlines = min(ScrollStep - 1, HALF(w)); */ if (--nlines > HALF(w)) nlines = HALF(w); newtop = next_line(w->w_line, (up) ? -nlines : nlines - (SIZE(w) - 1)); if (LineDist(newtop, w->w_top) < SIZE(w) - 1) { SetTop(w, newtop); return; } } CentWind(w); } /* This is bound to C-X 4 [BFT^T]. To make the screen stay the same we have to remember various things, like the current top line in the current window. It's sorta gross, but it's necessary because of the way this is implemented. (i.e., in terms of do_find(), do_select() which manipulate the windows. */ DEF_CMD( "window-find", WindFind, SPARSE|ARG(WINDFINDMAP_INDEX) ) { register Window *cw = curwind; register Buffer *obuf = curbuf; register Line *otop = cw->w_top; Bufpos odot; DOTsave(&odot); #ifndef FIXED_MAPS # define WFindMap ((sparsemap *) Maps[ObjArg(LastCmd)].k_bind) #endif ExecNow(findsparse(WFindMap, waitchar())); # undef WFindMap if (one_windp(cw)) div_wind(cw, 0); tiewind(cw->w_next, curbuf); SetTop(cw->w_next, cw->w_top); /* find-tag sets it */ #if (HIGHLIGHT) /* Transfer highlight information (find-tag sets it.) */ WHighLight(cw->w_next, cw->w_highlighted_line); WUnHighLight(cw); #endif /* [TRH 5-Jul-89] this used to be before tiewind where find-tag to same buffer was screwed up by it. */ SetBuf(obuf); SetDot(&odot); SetTop(cw, otop); /* there! it's as if we did nothing */ NextWindow(); } /* Go into one window mode by deleting all the other windows */ DEF_CMD( "delete-other-windows", OneWindow, NO ) { register Window *cw = curwind; while (!one_windp(cw)) del_wind(cw->w_prev); } Window * windbp(bp) const Buffer *bp; { register const Buffer *b; register Window *w = fwind; if (b = bp) do { if (w->w_bufp == b) return w; } while ((w = w->w_next) != fwind); return NULL; } /* Scroll the next Window */ DEF_CMD( "page-next-window", PageNWind, NO ) { NextWindow(); NextPage(); PrevWindow(); } /* Put a window with the buffer `name' in it. Erase the buffer if `clobber' is non-zero. */ void pop_wind(name, clobber, btype) register const char *name; { register Window *wp; register Buffer *newb; if (newb = buf_exists(name)) { if (clobber) { initlist(newb); newb->b_modified = NO; } } else { /* create a new (empty) buffer and set its type. */ newb = do_select((Window *) 0, name); if (btype > 0) SETBUFTYPE(newb, btype); } if ((wp = windbp(newb)) == NULL) { /* Buffer is not tied to any window. Use any window -other than curwind- with matching buffer type. If there isn't any, use previous window, or split window if we have just a single one. */ wp = curwind; do { if ((wp = wp->w_prev) == curwind) { wp = wp->w_prev; if (one_windp(wp)) wp = div_wind(wp, 0); break; } } while (wp->w_bufp->b_type != btype); } tiewind(wp, newb); SetWind(wp); } /* Change the size of the window by inc. First arg is the window, second is the increment. */ void WindSize(w, inc) register Window *w; int inc; { register int i; OnlyOne(); /* Change made from original code so that growing a window exactly offsets effect of shrinking a window, i.e., doing either followed by the other restores original sizes of all affected windows. */ if (((i = inc) < 0 ? w->w_height + i : w->w_prev->w_height - i) < 2) complain(toosmall); /* {{Should we try to steal from other windows if a resulting window gets too small? Should we then REMOVE windows if there is still insufficient size?}} */ w->w_height += i; w->w_prev->w_height -= i; #ifdef WINDOWS Windchange++; #endif } DEF_CMD( "shrink-window", GrowWindow, NEGATE ); DEF_CMD( "grow-window", GrowWindow, NO ) { WindSize(curwind, exp); } DEF_CMD( "number-lines-in-window", WNumLines, NO ) { register Window *cw = curwind; cw->w_flags ^= W_NUMLINES; SetTop(cw, cw->w_top); } DEF_CMD( "visible-spaces-in-window", WVisSpace, NO ) { register Window *cw = curwind; cw->w_flags ^= W_VISSPACE; ClrWindow(cw); } /* Return the line number that `line' occupies in `w' */ int in_window(w, line) register Window *w; Line *line; { register int i = 0; register Line *top; if (top = w->w_top) do { if (++i >= w->w_height) break; if (top == line) return FLine(w) + i - 1; } while (top = top->l_next); return -1; } DEF_CMD( "split-current-window", SplitWind, NO ) { SetWind(div_wind(curwind, exp - 1)); } /* * Scroll window left or right. (replaces [TRH] "horizontal-offset-in-window") */ DEF_CMD( "scroll-left", WindScroll, NEGATE ); DEF_CMD( "scroll-right", WindScroll, NO ) { register Window *w = curwind; register int num; if (num = exp) { /* num == 0 means reset */ if (exp_p != YES) num *= 10; if ((num += w->w_offset) < 0) num = 0; } w->w_offset = num; updmodline(); } /* * [TRH] set size of window to specific number of lines */ DEF_CMD( "resize-window", WindResize, NO ) { register Window *w = curwind; register int wsize = SIZE(w); OnlyOne(); WindSize(w, exp_ask_int(wsize) - wsize); } /* Goto the window with the named buffer. If no such window exists, pop one and attach the buffer to it. [TRH] in fact, pop_wind does just the job. */ DEF_CMD( "goto-window-with-buffer", GotoWind, NO ) { register Buffer *cb = curbuf; pop_wind(ask_buf(def_buf(cb)), NO, NO); if (cb != curbuf) SetABuf(cb); } #ifdef RESHAPING /* Reshape the screen (and the windows it displays) if its size changes. */ void win_reshape() { register int old_LI = LI, old_CO = CO, diff; /* * Get new line/col info. */ ttsize(); /* * LI has changed, and now holds the * new value. See how much the size * changed. */ if ((diff = LI - old_LI) || (CO != old_CO)) { if (diff) { register Window *w = curwind; register int n = 0; /* * count windows. If there are more than the new * screen can contain, keep deleting windows * until it fits. * spread the difference evenly over the windows. */ do n++; while ((w = w->w_next) != curwind); if (diff < 0) { n *= 2; while (n > ILI) { del_wind(w->w_next); n -= 2; } } do { if ((n = (w->w_height * LI) / old_LI) < 2) n = 2; diff -= n - w->w_height; w->w_height = n; } while ((w = w->w_next) != curwind); if (diff < 0) do { if (w->w_height > 2) w->w_height--; w = w->w_next; } while (++diff < 0); else while (--diff >= 0) { w->w_height++; w = w->w_next; } } make_scr(); ClAndRedraw(); /* just to be sure... */ #ifdef WINDOWS WindChange++; #endif redisplay(); } } #endif /* RESHAPING */ /*====================================================================== * $Log: wind.c,v $ * Revision 14.32 1993/06/21 21:52:28 tom * (win_reshape): moved from tty.c. * * Revision 14.31 1993/02/15 02:01:52 tom * remove (void) casts; a random optimization. * * Revision 14.30 1993/02/05 00:07:33 tom * cleanup whitespace; some random optimizations. * * Revision 14.28 1992/10/24 01:24:24 tom * convert to "port{ansi,defs}.h" conventions; "window-find" is made * transparent to keymap completion, preserves highlighting to other window. * * Revision 14.26 1992/08/26 23:57:01 tom * use ExecNow(0 in "wind-find" for immediate macro execution; * PRIVATE-ized some Variable defs; add RCS directives. * */