/* * Window system * Copyright * (C) 1992 Joseph H. Allen * * This file is part of JOE (Joe's Own Editor) */ #include "types.h" /* Count no. of main windows */ int countmain(Screen *t) { int nmain = 1; W *m = t->curwin->main; W *q; for (q = t->curwin->link.next; q != t->curwin; q = q->link.next) if (q->main != m) { ++nmain; m = q->main; } return nmain; } /* Redraw a window */ void wredraw(W *w) { msetI(w->t->t->updtab + w->y, 1, w->h); } /* Find first window in a group */ W *findtopw(W *w) { W *x; for (x = w; x->link.prev->main == w->main && x->link.prev != w; x = x->link.prev) ; return x; } /* Determine height of a window. Returns reqh if it is set, otherwise * used fixed or hh scaled to the current screen size */ static int geth(W *w) { if (w->reqh) return w->reqh; else if (w->fixed) return w->fixed; else return (((long) w->t->h - w->t->wind) * w->hh) / 1000; } /* Set the height of a window */ static void seth(W *w, int h) { long tmp; w->reqh = h; tmp = 1000L * h; w->hh = tmp / (w->t->h - w->t->wind) + (tmp % (w->t->h - w->t->wind) ? 1 : 0); } /* Determine height of a family of windows. Uses 'reqh' if it's set */ int getgrouph(W *w) { W *x; int h; /* Find first window in family */ x = findtopw(w); /* Add heights of all windows in family */ for (w = x, h = geth(w); w->link.next != x && w->link.next->main == x->main; w = w->link.next, h += geth(w)) ; return h; } /* Determine minimum height of a family */ static int getminh(W *w) { W *x; int h; x = findtopw(w); for (w = x, h = (w->fixed ? w->fixed : 2); w->link.next != x && w->link.next->main == x->main; w = w->link.next, h += (w->fixed ? w->fixed : 2)) ; return h; } /* Find last window in a group */ W *findbotw(W *w) { W *x; for (x = w; x->link.next->main == w->main && x->link.next != w; x = x->link.next) ; return x; } /* Demote group of window to end of window list. Returns true if top window was demoted */ int demotegroup(W *w) { W *top = findtopw(w); W *bot = findbotw(w); W *next; int flg = 0; for (w = top; w != bot; w = next) { next = w->link.next; if (w == w->t->topwin) { flg = 1; w->t->topwin = next; } else demote(W, link, w->t->topwin, w); w->y = -1; } if (w == w->t->topwin) flg = 1; else demote(W, link, w->t->topwin, w); w->y = -1; return flg; } /* Find last window on the screen */ W *lastw(Screen *t) { W *x; for (x = t->topwin; x->link.next != t->topwin && x->link.next->y >= 0; x = x->link.next) ; return x; } /* Create a screen object */ Screen *scr; Screen *screate(SCRN *scrn) { Screen *t = (Screen *) joe_malloc(sizeof(Screen)); t->t = scrn; t->w = scrn->co; t->h = scrn->li; t->topwin = NULL; t->curwin = NULL; t->wind = skiptop; scr = t; return t; } void sresize(Screen *t) { SCRN *scrn = t->t; W *w; t->w = scrn->co; t->h = scrn->li; if (t->h - t->wind < FITHEIGHT) t->wind = t->h - FITHEIGHT; if (t->wind < 0) t->wind = 0; w = t->topwin; do { w->y = -1; w->w = t->w - 1; w = w->link.next; } while (w != t->topwin); wfit(t); updall(); } void updall(void) { int y; for (y = 0; y != scr->h; ++y) { scr->t->updtab[y] = 1; } } void scrins(B *b, long l, long n, int flg) { W *w; if ((w = scr->topwin) != NULL) { do { if (w->y >= 0) { if (w->object && w->watom->ins) w->watom->ins(w->object, b, l, n, flg); } w = w->link.next; } while (w != scr->topwin); } } void scrdel(B *b, long l, long n, int flg) { W *w; if ((w = scr->topwin) != NULL) { do { if (w->y >= 0) { if (w->object && w->watom->del) w->watom->del(w->object, b, l, n, flg); } w = w->link.next; } while (w != scr->topwin); } } W *watpos(Screen *t,int x,int y) { W *w=t->topwin; do if(w->y>=0 && w->y<=y && w->y+w->h>y && w->x<=x && w->x+w->w>x) return w; while(w=w->link.next, w!=t->topwin); return 0; } /* Fit as many windows on the screen as is possible beginning with the window * at topwin. Give any extra space which couldn't be used to fit in another * window to the last text window on the screen. This function guarentees * to fit on the window with the cursor in it (moves topwin to next group * of windows until window with cursor fits on screen). */ static int doabort(W *w, int *ret); void wfit(Screen *t) { int y; /* Where next window goes */ int left; /* Lines left on screen */ W *w; /* Current window we're fitting */ W *pw; /* Main window of previous family */ int req; /* Amount this family needs */ int adj; /* Amount family needs to be adjusted */ int flg = 0; /* Set if cursor window was placed on screen */ int ret; dostaupd = 1; tryagain: y = t->wind; left = t->h - y; pw = NULL; w = t->topwin; do { w->ny = -1; w->nh = geth(w); w = w->link.next; } while (w != t->topwin); /* Fit a group of windows on the screen */ w = t->topwin; do { req = getgrouph(w); if (req > left) /* If group is taller than lines left */ adj = req - left; /* then family gets shorter */ else adj = 0; /* Fit a family of windows on the screen */ do { w->ny = y; /* Set window's y position */ if (!w->win) { pw = w; w->nh -= adj; /* Adjust main window of the group */ } if (!w->win && w->nh < 2) while (w->nh < 2) w->nh += doabort(w->link.next, &ret); if (w == t->curwin) flg = 1; /* Set if we got window with cursor */ y += w->nh; left -= w->nh; /* Increment y value by height of window */ w = w->link.next; /* Next window */ } while (w != t->topwin && w->main == w->link.prev->main); } while (w != t->topwin && left >= FITHEIGHT); /* We can't use extra space to fit a new family on, so give space to parent of * previous family */ pw->nh += left; /* Adjust that family's children which are below the parent */ while ((pw = pw->link.next) != w) pw->ny += left; /* Make sure the cursor window got on the screen */ if (!flg) { t->topwin = findbotw(t->topwin)->link.next; goto tryagain; } /* All of the windows are now on the screen. Scroll the screen to reflect what * happened */ w = t->topwin; do { if (w->y >= 0 && w->ny >= 0) if (w->ny > w->y) { W *l = pw = w; while (pw->link.next != t->topwin && (pw->link.next->y < 0 || pw->link.next->ny < 0 || pw->link.next->ny > pw->link.next->y)) { pw = pw->link.next; if (pw->ny >= 0 && pw->y >= 0) l = pw; } /* Scroll windows between l and w */ loop1: if (l->ny >= 0 && l->y >= 0) nscrldn(t->t, l->y, l->ny + uns_min(l->h, l->nh), l->ny - l->y); if (w != l) { l = l->link.prev; goto loop1; } w = pw->link.next; } else if (w->ny < w->y) { W *l = pw = w; while (pw->link.next != t->topwin && (pw->link.next->y < 0 || pw->link.next->ny < 0 || pw->link.next->ny < pw->link.next->y)) { pw = pw->link.next; if (pw->ny >= 0 && pw->y >= 0) l = pw; } /* Scroll windows between l and w */ loop0: if (w->ny >= 0 && w->y >= 0) nscrlup(t->t, w->ny, w->y + uns_min(w->h, w->nh), w->y - w->ny); if (w != l) { w = w->link.next; goto loop0; } w = pw->link.next; } else w = w->link.next; else w = w->link.next; } while (w != t->topwin); /* Update current height and position values */ w = t->topwin; do { if (w->ny >= 0) { if (w->object) { if (w->watom->move) w->watom->move(w->object, w->x, w->ny); if (w->watom->resize) w->watom->resize(w->object, w->w, w->nh); } if (w->y == -1) { msetI(t->t->updtab + w->ny, 1, w->nh); } w->y = w->ny; } else w->y = -1; w->h = w->nh; w->reqh = 0; w = w->link.next; } while (w != t->topwin); } /* Goto next window */ int wnext(Screen *t) { if (t->curwin->link.next != t->curwin) { t->curwin = t->curwin->link.next; if (t->curwin->y == -1) wfit(t); return 0; } else return -1; } /* Goto previous window */ int wprev(Screen *t) { if (t->curwin->link.prev != t->curwin) { t->curwin = t->curwin->link.prev; if (t->curwin->y == -1) { t->topwin = findtopw(t->curwin); wfit(t); } return 0; } else return -1; } /* Grow window */ int wgrow(W *w) { W *nextw; /* If we're the last window on the screen, shrink the previous window */ if ((w->link.next == w->t->topwin || w->link.next->y == -1) && w != w->t->topwin) return wshrink(w->link.prev->main); /* Get to next variable size window */ for (nextw = w->link.next; nextw->fixed && nextw != w->t->topwin; nextw = nextw->link.next) ; /* Is it below us, on screen and big enough to take space from? */ if (nextw == w->t->topwin || nextw->y == -1 || nextw->h <= FITHEIGHT) return -1; /* Increase this window's height */ seth(w, w->h + 1); /* Decrease next window's height */ seth(nextw, nextw->h - 1); /* Do it */ wfit(w->t); return 0; } /* Shrink window */ int wshrink(W *w) { W *nextw; /* If we're the last window on the screen, grow the previous window */ if ((w->link.next == w->t->topwin || w->link.next->y == -1) && w != w->t->topwin) return wgrow(w->link.prev->main); /* Is this window too small already? */ if (w->h <= FITHEIGHT) return -1; /* Get to window below us */ for (nextw = w->link.next; nextw != w->t->topwin && nextw->fixed; nextw = nextw->link.next) ; if (nextw == w->t->topwin) return -1; /* Decrease the size of this window */ seth(w, w->h - 1); /* Increase the size of next window */ seth(nextw, nextw->h + 1); /* Do it */ wfit(w->t); return 0; } /* Grow window up */ int wgrowup(W *w) { return wshrink(w->link.prev->main); } /* Grow window down */ int wgrowdown(W *w) { return wgrow(w->link.prev->main); } /* Show all windows */ void wshowall(Screen *t) { int n = 0; int set; W *w; /* Count no. of main windows */ w = t->topwin; do { if (!w->win) ++n; w = w->link.next; } while (w != t->topwin); /* Compute size to set each window */ if ((set = (t->h - t->wind) / n) < FITHEIGHT) set = FITHEIGHT; /* Set size of each variable size window */ w = t->topwin; do { if (!w->win) { int h = getminh(w); if (h >= set) seth(w, 2); else seth(w, set - (h - 2)); w->orgwin = NULL; } w = w->link.next; } while (w != t->topwin); /* Do it */ wfit(t); } static void wspread(Screen *t) { int n = 0; W *w = t->topwin; do { if (w->y >= 0 && !w->win) ++n; w = w->link.next; } while (w != t->topwin); if (!n) { wfit(t); return; } if ((t->h - t->wind) / n >= FITHEIGHT) n = (t->h - t->wind) / n; else n = FITHEIGHT; w = t->topwin; do { if (!w->win) { int h = getminh(w); if (h >= n) seth(w, 2); else seth(w, n - (h - 2)); w->orgwin = NULL; } w = w->link.next; } while (w != t->topwin); wfit(t); } /* Show just one family of windows */ void wshowone(W *w) { W *q = w->t->topwin; do { if (!q->win) { seth(q, w->t->h - w->t->wind - (getminh(q) - 2)); q->orgwin = NULL; } q = q->link.next; } while (q != w->t->topwin); wfit(w->t); } /* Create a window */ W *wcreate(Screen *t, WATOM *watom, W *where, W *target, W *original, int height, unsigned char *huh, int *notify) { W *new; if (height < 1) return NULL; /* Create the window */ new = (W *) joe_malloc(sizeof(W)); new->notify = notify; new->t = t; new->w = t->w - 1; seth(new, height); new->h = new->reqh; new->y = -1; new->ny = 0; new->nh = 0; new->x = 0; new->huh = huh; new->orgwin = original; new->watom = watom; new->object = NULL; new->msgb = NULL; new->msgt = NULL; /* Set window's target and family */ /* was: if (new->win = target) { which may be mistyped == */ if ((new->win = target) != NULL) { /* A subwindow */ new->main = target->main; new->fixed = height; } else { /* A parent window */ new->main = new; new->fixed = 0; } /* Get space for window */ if (original) { if (original->h - height <= 2) { /* Not enough space for window */ joe_free(new); return NULL; } else seth(original, original->h - height); } /* Create new keyboard handler for window */ if (watom->context) new->kbd = mkkbd(kmap_getcontext(watom->context)); else new->kbd = NULL; /* Put window on the screen */ if (where) enquef(W, link, where, new); else { if (t->topwin) enqueb(W, link, t->topwin, new); else { izque(W, link, new); t->curwin = t->topwin = new; } } return new; } /* Abort group of windows */ static int doabort(W *w, int *ret) { int amnt = geth(w); W *z; w->y = -2; if (w->t->topwin == w) w->t->topwin = w->link.next; loop: z = w->t->topwin; do { if (z->orgwin == w) z->orgwin = NULL; if ((z->win == w || z->main == w) && z->y != -2) { amnt += doabort(z, ret); goto loop; } } while (z = z->link.next, z != w->t->topwin); if (w->orgwin) seth(w->orgwin, geth(w->orgwin) + geth(w)); if (w->t->curwin == w) { if (w->t->curwin->win) w->t->curwin = w->t->curwin->win; else if (w->orgwin) w->t->curwin = w->orgwin; else w->t->curwin = w->link.next; } if (qempty(W, link, w)) { leave = 1; amnt = 0; } deque(W, link, w); if (w->watom->abort && w->object) { *ret = w->watom->abort(w->object); if (w->notify) *w->notify = -1; } else { *ret = -1; if (w->notify) *w->notify = 1; } rmkbd(w->kbd); joe_free(w); windie(w); return amnt; } /* Abort a window and its children */ int wabort(W *w) { Screen *t = w->t; int ret; if (w != w->main) { doabort(w, &ret); if (!leave) wfit(t); } else { doabort(w, &ret); if (!leave) { if (lastw(t)->link.next != t->topwin) wfit(t); else wspread(t); } } return ret; } /* Display a message and skip the next key */ int bg_msg; static void mdisp(SCRN *t, int y, unsigned char *s) { int ofst; int len; len = fmtlen(s); if (len <= (t->co - 1)) ofst = 0; else ofst = len - (t->co - 1); genfmt(t, 0, y, ofst, s, BG_COLOR(bg_msg), 1); t->updtab[y] = 1; } void msgout(W *w) { SCRN *t = w->t->t; if (w->msgb) { mdisp(t, w->y + w->h - 1, w->msgb); w->msgb = 0; } if (w->msgt) { mdisp(t, w->y + ((w->h > 1 && (w->y || !staen)) ? 1 : 0), w->msgt); w->msgt = 0; } } /* Set temporary message */ unsigned char msgbuf[JOE_MSGBUFSIZE]; /* display message on bottom line of window */ void msgnw(W *w, unsigned char *s) { w->msgb = s; } void msgnwt(W *w, unsigned char *s) { w->msgt = s; } int urtn(BASE *b, int k) { if (b->parent->watom->rtn) return b->parent->watom->rtn(b, k); else return -1; } int utype(BASE *b, int k) { if (b->parent->watom->type) return b->parent->watom->type(b, k); else return -1; } /* Window user commands */ int uprevw(BASE *bw) { return wprev(bw->parent->t); } int unextw(BASE *bw) { return wnext(bw->parent->t); } int ugroww(BASE *bw) { return wgrow(bw->parent); } int ushrnk(BASE *bw) { return wshrink(bw->parent); } int uexpld(BASE *bw) { if (bw->parent->t->h - bw->parent->t->wind == getgrouph(bw->parent)) wshowall(bw->parent->t); else wshowone(bw->parent); return 0; } int uretyp(BASE *bw) { nredraw(bw->parent->t->t); return 0; } /* Get message window on screen */ W *find_window(Screen *t, B *b) { W *w = t->topwin; do { if (w->watom == &watomtw && ((BW *)w->object)->b == b) return w; w = w->link.next; } while(w != t->topwin); return 0; } int umwind(BW *bw) { W *msgw; if (!errbuf) { msgnw(bw->parent, joe_gettext(_("There is no message buffer"))); return -1; } /* Find message window */ msgw = find_window(bw->parent->t, errbuf); if (msgw) { /* The window exists */ bw->parent->t->curwin = msgw; wshowone(msgw); return 0; } else { /* Make it the current window */ msgw = bw->parent; get_buffer_in_window(bw, errbuf); wshowone(msgw); return 0; } } /* Fit previous window and current window on screen. If there is no previous window, * split the current window to create one. */ int umfit(BW *bw) { W *p; W *w = bw->parent->main; Screen *t = w->t; wshowone(w); p = findtopw(w)->link.prev->main; if (p == w) { /* We have to split */ usplitw(bw); } w = t->curwin; p = findtopw(w)->link.prev->main; if (p == w) { return -1; } /* Request size */ if (p->t->h - 6 < 3) return -1; seth(p, p->t->h - 6); t->topwin = p; t->curwin = p; /* Fit them on the screen */ wfit(t); t->curwin = w; wfit(t); return 0; }