static char rcsid[] = "$Id: window.c,v 1.3 1992/12/18 02:47:02 mike Exp $"; /* $Log: window.c,v $ * Revision 1.3 1992/12/18 02:47:02 mike * - Fixed bug: Added `sync_dot' to `nextwind'. * * Revision 1.2 1992/10/12 21:20:44 mike * - Lots'a changes ... * * Revision 1.1 1992/09/05 01:13:32 mike * Initial revision * */ /* * * WINDOW.C MODULE * * Window management. Some of the functions are internal, and some are * attached to keys that the user actually types. */ /* Copyright 1990, 1991, 1992 Craig Durland * Distributed under the terms of the GNU General Public License. * Distributed "as is", without warranties of any kind, but comments, * suggestions and bug reports are welcome. */ #include "me2.h" extern int t_nrow; static Window *alloc_window(); /* Current window, NULL until window system is initialized */ Window *curwp = NULL; /* Routines so that the current buffer dot and current window dot can be * synchronized. */ #define sync_wdot() if (curwp->wbuffer == curbp) *the_dot = curwp->dot void sync_dot() { if (curwp->wbuffer == curbp) { curwp->w_flag |= WFMOVE; curwp->dot = *the_dot; } } #if 0 #define sync_dot() \ if (curwp->wbuffer == curbp) \ { curwp->w_flag |= WFMOVE; curwp->dot = *the_dot; } #endif /* Return pointer to nth window starting from wp. If n < 0, count * backwards. */ Window *nthwindow(wp,n) register Window *wp; int n; { if (n >= 0) { while (n--) if ((wp = wp->nextw) == NULL) wp = first_window; } else /* count backwards */ { int cw, windows; windows = cntwindows((Window *)NULL); cw = cntwindows(wp); if ((n = (cw -(-n) % windows)) < 0) n += windows; /* !!!??? is n always >=0??? */ wp = nthwindow(first_window,n); } return wp; } /* return number of window wx. Use NULL to get number of windows */ cntwindows(wx) Window *wx; { int j; register Window *wp; for (wp = first_window, j = 0; wp && wp!=wx; wp = wp->nextw, j++) ; return j; } /* Make wp the current window. Make the current buffer the one that is * displayed in wp. Set the new buffer dot to be the dot in the * window. This makes moving amongst windows look right. * Don't need to set any window flags because the window is already * visually correct. */ void use_window(wp) register Window *wp; { curwp = wp; use_buffer(wp->wbuffer,FALSE); *the_dot = wp->dot; } /* return the first window that is displaying bp else NULL */ Window *window_on_buffer(bp) register Buffer *bp; { register Window *wp; for (wp = first_window; wp; wp = wp->nextw) if (wp->wbuffer == bp) return wp; return NULL; } /* * Display the current buffer in window wp. If wp is already showing bp, * just sync dots. Otherwise set the frame at the top of the buffer * (since that is easy to do) and set the the FORCE hint so the display * code will center the frame around the dot. Also, redraw the entire * frame since its very likely all different. * Note: * I'm pretty sure that if the window is already displaying bp, the dot is * correct or dot has been moved since last update() and WFMOVE was set * so I don't have to set WFMOVE. */ void display_buffer(wp) Window *wp; { register Buffer *bp = curbp; wp->dot = *the_dot; /* sync dots */ if (wp->wbuffer != bp) { wp->wbuffer = bp; wp->top_line = BUFFER_FIRST_LINE(bp); wp->w_flag |= (WFMODE | WFFORCE | WFHARD); /* Quite nasty */ } /* else wp->w_flag |= WFMOVE; ??? */ } /* Make all windows showing bp show the start of bp. * If want bp to be the current buffer, the first window showing bp will * become the current window. * If bp is the current buffer, the dot is sent to the start of the * buffer. */ void fixwin(bp,makecurrent) Buffer *bp; int makecurrent; { register Window *wp; for (wp = first_window; wp; wp = wp->nextw) if (wp->wbuffer == bp) { if (makecurrent) /* top most window will be current one */ { use_buffer(bp,FALSE); curwp = wp; makecurrent = FALSE; } wp->top_line = wp->dot.line = BUFFER_FIRST_LINE(bp); wp->dot.offset = 0; wp->w_flag |= (WFMODE | WFFORCE | WFHARD); if (bp == curbp) goto_BoB(FALSE,1); } } /* * Reposition window dot to the nth line from the top (or bottom if n is * negative) of the current window. If n is 0 the window is centered * (this is what the standard redisplay code does). With no argument it * defaults to 1. Because of the default, it works like in Gosling. * Bound to M-!. */ reposition(f,n) { curwp->w_force = n; curwp->w_flag |= WFFORCE; return TRUE; } /* * Refresh the screen. With no argument, it just does the refresh. With an * argument it recenters dot in the current window. * Bound to "C-l". */ refresh(f,n) { if (f==FALSE) screen_is_garbage = TRUE; else reposition(FALSE,0); /* Center dot */ return TRUE; } /* * The command make the next window (next => down the screen) the current * window. There are no real errors, although the command does nothing if * there is only 1 window on the screen. * Bound to "C-x C-n". */ nextwind(f,n) { sync_dot(); use_window(nthwindow(curwp,n)); return TRUE; } /* * This command makes the previous window (previous => up the screen) the * current window. */ prevwind(f,n) { return nextwind(f,-n); } /* Move the top line of window wp up n lines. If n < 0, move the top * line down. */ static void move_top_line(wp,n) Window *wp; int n; { register Line *lp; lp = wp->top_line; if (n < 0) /* move top line down */ { register Line *last_line = BUFFER_LAST_LINE(wp->wbuffer); while (n++ && lp != last_line) lp = lforw(lp); } else /* move top line up */ { register Line *first_line = BUFFER_FIRST_LINE(wp->wbuffer); while (n-- && lp != first_line) lp = lback(lp); } wp->top_line = lp; /* the new top line of the window */ } /* * Scroll the current window forward (or backwards if n is negative) by n * lines, or by a full page if no argument. The dot is left at the top of * the window if possible - if the top line of the window ends up at the * last line of the buffer, the dot is centered. * The "2" in the arithmetic on the window size is the overlap; this value * is the default overlap value in ITS EMACS. * Because this zaps the top line in the display window, we have to do a * hard update. */ static int pager(f,n,forward) { if (f == FALSE) { n = curwp->w_ntrows -2; /* Default scroll */ if (n <= 0) n = 1; /* Forget the overlap if tiny window */ } if (forward) n = -n; move_top_line(curwp,n); curwp->dot.line = curwp->top_line; curwp->dot.offset = 0; reposition(FALSE, (n >= 0) ? 1 : (curwp->top_line != BUFFER_LAST_LINE(curwp->wbuffer))); sync_wdot(); return TRUE; } /* * Scroll the current window forward. * Bound to "C-v". */ forwpage(f,n) { return pager(f,n,TRUE); } /* * This command is like forwpage(), but it goes backwards. * Bound to "M-v". */ backpage(f,n) { return pager(f,n,FALSE); } /* * Move the current window up by n lines ie display text that is before the * dot. Recompute the new top line of the window. Look to see if dot is * still on the screen. If it is, you win. If it isn't, then move dot to * center it in the new framing of the window (this command does not * really move dot - it moves the frame). * Bound to "C-x C-p". */ mvupwind(f,n) { register Line *lp, *last_line; register int i; move_top_line(curwp,n); curwp->w_flag |= WFHARD; /* Mode line is OK */ lp = curwp->top_line; last_line = BUFFER_LAST_LINE(curwp->wbuffer); for (i = curwp->w_ntrows; i--; lp = lforw(lp)) { if (lp == curwp->dot.line) { sync_wdot(); return TRUE; } /* dot is in the frame */ if (lp == last_line) break; } lp = curwp->top_line; i = curwp->w_ntrows/2; while (i-- && lp != last_line) lp = lforw(lp); curwp->dot.line = lp; curwp->dot.offset = 0; sync_wdot(); return TRUE; } /* Move the current window down by n lines. Bound to "C-x C-n". */ mvdnwind(f,n) { return mvupwind(f,-n); } /* * This command makes the current window the only window on the screen. * Bound to "C-x 1". */ onlywind(f,n) { register Window *wp, *nw; for (wp = first_window; wp; wp = nw) { nw = wp->nextw; /* since pointer is gone after free_window() */ if (wp != curwp) free_window(wp); } return TRUE; } /* * Split the current window. A window smaller than 3 lines cannot be split. * The only other error possible is a malloc() failure allocating the * structure for the new window. * Bound to "C-x 2". */ splitwind(f,n) { register Window *wp, *wp1, *wp2; register Line *lp; register int ntru, ntrl, ntrd; if (curwp->w_ntrows < 3) { mlwrite("Cannot split a %d line window", curwp->w_ntrows); return FALSE; } if (!(wp = alloc_window(curwp->wbuffer))) { mlwrite("Cannot allocate Window"); return ABORT; } wp->dot = curwp->dot; ntru = (curwp->w_ntrows-1) /2; /* Upper size */ ntrl = (curwp->w_ntrows-1) -ntru; /* Lower size */ lp = curwp->top_line; ntrd = 0; while (lp != curwp->dot.line) { ++ntrd; lp = lforw(lp); } lp = curwp->top_line; if (ntrd <= ntru) /* Old is upper window */ { if (ntrd == ntru) lp = lforw(lp); /* Hit mode line */ curwp->w_ntrows = ntru; wp->nextw = curwp->nextw; curwp->nextw = wp; /* link new window */ wp->w_toprow = curwp->w_toprow+ntru +1; wp->w_ntrows = ntrl; } else /* Old is lower window */ { wp1 = NULL; wp2 = first_window; while (wp2 != curwp) { wp1 = wp2; wp2 = wp2->nextw; } if (wp1 == NULL) first_window = wp; else wp1->nextw = wp; wp->nextw = curwp; wp->w_toprow = curwp->w_toprow; wp->w_ntrows = ntru; ++ntru; /* skip mode line */ curwp->w_toprow += ntru; curwp->w_ntrows = ntrl; while (ntru--) lp = lforw(lp); } curwp->top_line = wp->top_line = lp; /* Adjust top lines if necessary */ curwp->w_flag |= (WFMODE | WFHARD); return TRUE; } /* * Enlarge window wp by shrinking adjacent window wx. * Hack the window descriptions, and ask redisplay to do * all the hard work. You don't just set "force reframe" because dot * would move. */ void grow_window(wp,wx,n,above) Window *wp, *wx; { if (above) /* if wp above wx, lower top line of wx */ { move_top_line(wx,-n); wx->w_toprow += n; } else /* wx above wp, raise top line of wp */ { move_top_line(wp,n); wp->w_toprow -= n; } wp->w_ntrows += n; wx->w_ntrows -= n; wp->w_flag |= WFMODE | WFHARD; wx->w_flag |= WFMODE | WFHARD; } /* Change the size of a window. * Input: * wp: Window to resize * n: Number of lines you want in the window (not including the * modeline). * Returns: * TRUE: window resized * FALSE: invalid size, couldn't change other windows to make the change * fit or only one window. * Find the window best suited to gain/lose space. I use an adjacent window * because thats easy. Make sure change is OK. * Notes: * If n < 0, its an "Impossible change". */ change_window_size(wp, n) Window *wp; { register int an = 0, bn = 0; Window *wa, *wb; if (first_window->nextw == NULL) { mlwrite("Only one window"); return FALSE; } n = n - wp->w_ntrows; /* number of lines to add or take away */ if (n == 0) return TRUE; /* that was easy */ if ((wa = first_window) != wp) /* find window above */ { while (wa->nextw != wp) wa = wa->nextw; an = wa->w_ntrows; } if ((wb = wp->nextw) != NULL) bn = wb->w_ntrows; /* window below */ if (n > 0) /* grow current window by shrinking biggest adjacent window */ { if (n >= an && n >= bn) { mlwrite("Impossible change"); return FALSE; } if (an > bn) grow_window(wp,wa,n,FALSE); else grow_window(wp,wb,n,TRUE); } else /* shrink current window by growing smallest adjacent window */ { n = -n; if (wp->w_ntrows <= n) { mlwrite("Impossible change"); return FALSE; } if (bn == 0 || (an != 0 && an < bn)) grow_window(wa,wp,n,TRUE); else grow_window(wb,wp,n,FALSE); } return TRUE; } /* * Pick a window for a pop-up. Split the screen if there is only one * window. Pick the uppermost window that isn't the current window. An * LRU algorithm might be better. * Returns: a pointer, or NULL on error. */ Window *wpopup() { register Window *wp; if (first_window->nextw == NULL && /* Only 1 window */ splitwind(FALSE,0) != TRUE) /* and it won't split */ return NULL; wp = first_window; /* Find window to use */ while (wp && wp == curwp) wp = wp->nextw; return wp; } /* ******************************************************************** */ /* ******************************************************************** */ /* ******************************************************************** */ Window *first_window = NULL; static Window *freed_windows = NULL; /* Initialize the window system. * This must be called after the display has been opened (need to know * about the display parameters to configure the windows) and the * buffers have been inited (need to display the current buffer). * Might want to cache some windows since its very likely that more than * one will be used and having a few already allocated at the front of * the heap might help malloc() avoid thrashing. */ #define WNX 5 win_init() { int j; register Window *wp; /* cache some windows */ if (!(wp = (Window *)malloc(WNX*sizeof(Window)))) return FALSE; for (j = WNX; j--; wp++) { wp->nextw = freed_windows; freed_windows = wp; } /* set window stuff */ #if 0 /* !!!??? */ if (!(wp = alloc_window(curbp))) return FALSE; /* First window */ first_window = curwp = wp; wp->top_line = BUFFER_FIRST_LINE(curbp); wp->dot = *the_dot; #else if (!(wp = alloc_window((Buffer *)NULL))) return FALSE; /* First window */ first_window = curwp = wp; display_buffer(wp); #endif wp->w_toprow = 0; wp->w_ntrows = t_nrow -1; /* "-1" for mode line */ return TRUE; } /* Allocate a window that will display buffer bp. * Not much is initialized in here. */ static Window *alloc_window(bp) Buffer *bp; { register Window *wp; if (freed_windows) /* there are windows in the free pool */ { wp = freed_windows; freed_windows = freed_windows->nextw; } else /* malloc a window */ if (!(wp = (Window *)malloc(sizeof(Window)))) return NULL; wp->nextw = NULL; wp->w_force = wp->lmargin = 0; wp->w_flag = WFMODE | WFHARD; /* Full redisplay */ /* !!!??? display_buffer(bp,wp); */ wp->wbuffer = bp; return wp; } /* * Free a window. * Two cases: * 1. delete the top window: * Raise the top of the next window. Try to set the framing so that * dot does not have to move on the display. * 2. there is a window above the deleted one: * Lower the bottom of the above window. * Some care has to be taken to keep the dot in the buffer structures right * if the distruction of the window makes a buffer become undisplayed. * If deleting the current window, select another current window. * Return the window to the window pool. * Notes: * Does not use t_nrow or t_ncol. * WARNING! * if free a freed window, core dump city. */ free_window(wp) Window *wp; { register Window *wx; if (first_window->nextw == NULL) return FALSE; /* only one window */ /* relink wp out of the window chain */ if (first_window == wp) /* wp is the top window */ { first_window = wx = first_window->nextw; move_top_line(wx,wx->w_toprow); wx->w_toprow = 0; } else /* theres a window above wp */ { for (wx = first_window; wx->nextw != wp; wx = wx->nextw) ; wx->nextw = wp->nextw; } /* add wp to the free list */ wp->nextw = freed_windows; freed_windows = wp; /* adjust window wx to use wp's screen space */ wx->w_ntrows += wp->w_ntrows +1; /* +1 for modeline */ wx->w_flag |= WFMODE | WFHARD; if (curwp == wp) use_window(wx); /* deleted current window */ return TRUE; }