119304Speter/*- 219304Speter * Copyright (c) 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: vs_split.c,v 10.42 2001/06/25 15:19:38 skimo Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <errno.h> 2219304Speter#include <limits.h> 2319304Speter#include <stdio.h> 2419304Speter#include <stdlib.h> 2519304Speter#include <string.h> 2619304Speter 2719304Speter#include "../common/common.h" 2819304Speter#include "vi.h" 2919304Speter 30254225Spetertypedef enum { HORIZ_FOLLOW, HORIZ_PRECEDE, VERT_FOLLOW, VERT_PRECEDE } jdir_t; 3119304Speter 32254225Speterstatic SCR *vs_getbg __P((SCR *, char *)); 33254225Speterstatic void vs_insert __P((SCR *sp, GS *gp)); 34254225Speterstatic int vs_join __P((SCR *, SCR **, jdir_t *)); 35254225Speter 3619304Speter/* 3719304Speter * vs_split -- 38254225Speter * Create a new screen, horizontally. 3919304Speter * 4019304Speter * PUBLIC: int vs_split __P((SCR *, SCR *, int)); 4119304Speter */ 4219304Speterint 43254225Spetervs_split( 44254225Speter SCR *sp, 45254225Speter SCR *new, 46254225Speter int ccl) /* Colon-command line split. */ 4719304Speter{ 4819304Speter GS *gp; 4919304Speter SMAP *smp; 5019304Speter size_t half; 5119304Speter int issmallscreen, splitup; 5219304Speter 5319304Speter gp = sp->gp; 5419304Speter 5519304Speter /* Check to see if it's possible. */ 5619304Speter /* XXX: The IS_ONELINE fix will change this, too. */ 5719304Speter if (sp->rows < 4) { 5819304Speter msgq(sp, M_ERR, 5919304Speter "222|Screen must be larger than %d lines to split", 4 - 1); 6019304Speter return (1); 6119304Speter } 6219304Speter 6319304Speter /* Wait for any messages in the screen. */ 6419304Speter vs_resolve(sp, NULL, 1); 6519304Speter 6619304Speter /* Get a new screen map. */ 6719304Speter CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP)); 6819304Speter if (_HMAP(new) == NULL) 6919304Speter return (1); 7019304Speter _HMAP(new)->lno = sp->lno; 7119304Speter _HMAP(new)->coff = 0; 7219304Speter _HMAP(new)->soff = 1; 7319304Speter 74254225Speter /* Split the screen in half. */ 75254225Speter half = sp->rows / 2; 76254225Speter if (ccl && half > 6) 77254225Speter half = 6; 78254225Speter 7919304Speter /* 8019304Speter * Small screens: see vs_refresh.c section 6a. Set a flag so 8119304Speter * we know to fix the screen up later. 8219304Speter */ 8319304Speter issmallscreen = IS_SMALL(sp); 8419304Speter 8519304Speter /* The columns in the screen don't change. */ 86254225Speter new->coff = sp->coff; 8719304Speter new->cols = sp->cols; 8819304Speter 8919304Speter /* 9019304Speter * Split the screen, and link the screens together. If creating a 9119304Speter * screen to edit the colon command line or the cursor is in the top 9219304Speter * half of the current screen, the new screen goes under the current 9319304Speter * screen. Else, it goes above the current screen. 9419304Speter * 9519304Speter * Recalculate current cursor position based on sp->lno, we're called 9619304Speter * with the cursor on the colon command line. Then split the screen 9719304Speter * in half and update the shared information. 9819304Speter */ 9919304Speter splitup = 10019304Speter !ccl && (vs_sm_cursor(sp, &smp) ? 0 : (smp - HMAP) + 1) >= half; 10119304Speter if (splitup) { /* Old is bottom half. */ 10219304Speter new->rows = sp->rows - half; /* New. */ 103254225Speter new->roff = sp->roff; 10419304Speter sp->rows = half; /* Old. */ 105254225Speter sp->roff += new->rows; 10619304Speter 10719304Speter /* 10819304Speter * If the parent is the bottom half of the screen, shift 10919304Speter * the map down to match on-screen text. 11019304Speter */ 111254225Speter memcpy(_HMAP(sp), _HMAP(sp) + new->rows, 11219304Speter (sp->t_maxrows - new->rows) * sizeof(SMAP)); 11319304Speter } else { /* Old is top half. */ 11419304Speter new->rows = half; /* New. */ 11519304Speter sp->rows -= half; /* Old. */ 116254225Speter new->roff = sp->roff + sp->rows; 11719304Speter } 11819304Speter 11919304Speter /* Adjust maximum text count. */ 12019304Speter sp->t_maxrows = IS_ONELINE(sp) ? 1 : sp->rows - 1; 12119304Speter new->t_maxrows = IS_ONELINE(new) ? 1 : new->rows - 1; 12219304Speter 12319304Speter /* 12419304Speter * Small screens: see vs_refresh.c, section 6a. 12519304Speter * 12619304Speter * The child may have different screen options sizes than the parent, 12719304Speter * so use them. Guarantee that text counts aren't larger than the 12819304Speter * new screen sizes. 12919304Speter */ 13019304Speter if (issmallscreen) { 13119304Speter /* Fix the text line count for the parent. */ 13219304Speter if (splitup) 13319304Speter sp->t_rows -= new->rows; 13419304Speter 13519304Speter /* Fix the parent screen. */ 13619304Speter if (sp->t_rows > sp->t_maxrows) 13719304Speter sp->t_rows = sp->t_maxrows; 13819304Speter if (sp->t_minrows > sp->t_maxrows) 13919304Speter sp->t_minrows = sp->t_maxrows; 14019304Speter 14119304Speter /* Fix the child screen. */ 14219304Speter new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW); 14319304Speter if (new->t_rows > new->t_maxrows) 14419304Speter new->t_rows = new->t_maxrows; 14519304Speter if (new->t_minrows > new->t_maxrows) 14619304Speter new->t_minrows = new->t_maxrows; 14719304Speter } else { 14819304Speter sp->t_minrows = sp->t_rows = IS_ONELINE(sp) ? 1 : sp->rows - 1; 14919304Speter 15019304Speter /* 15119304Speter * The new screen may be a small screen, even if the parent 15219304Speter * was not. Don't complain if O_WINDOW is too large, we're 15319304Speter * splitting the screen so the screen is much smaller than 15419304Speter * normal. 15519304Speter */ 15619304Speter new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW); 15719304Speter if (new->t_rows > new->rows - 1) 15819304Speter new->t_minrows = new->t_rows = 15919304Speter IS_ONELINE(new) ? 1 : new->rows - 1; 16019304Speter } 16119304Speter 16219304Speter /* Adjust the ends of the new and old maps. */ 16319304Speter _TMAP(sp) = IS_ONELINE(sp) ? 16419304Speter _HMAP(sp) : _HMAP(sp) + (sp->t_rows - 1); 16519304Speter _TMAP(new) = IS_ONELINE(new) ? 16619304Speter _HMAP(new) : _HMAP(new) + (new->t_rows - 1); 16719304Speter 16819304Speter /* Reset the length of the default scroll. */ 16919304Speter if ((sp->defscroll = sp->t_maxrows / 2) == 0) 17019304Speter sp->defscroll = 1; 17119304Speter if ((new->defscroll = new->t_maxrows / 2) == 0) 17219304Speter new->defscroll = 1; 17319304Speter 174254225Speter /* Fit the screen into the logical chain. */ 175254225Speter vs_insert(new, sp->gp); 176254225Speter 177254225Speter /* Tell the display that we're splitting. */ 178254225Speter (void)gp->scr_split(sp, new); 179254225Speter 18019304Speter /* 18119304Speter * Initialize the screen flags: 18219304Speter * 18319304Speter * If we're in vi mode in one screen, we don't have to reinitialize. 18419304Speter * This isn't just a cosmetic fix. The path goes like this: 18519304Speter * 18619304Speter * return into vi(), SC_SSWITCH set 18719304Speter * call vs_refresh() with SC_STATUS set 18819304Speter * call vs_resolve to display the status message 18919304Speter * call vs_refresh() because the SC_SCR_VI bit isn't set 19019304Speter * 19119304Speter * Things go downhill at this point. 19219304Speter * 19319304Speter * Draw the new screen from scratch, and add a status line. 19419304Speter */ 19519304Speter F_SET(new, 19619304Speter SC_SCR_REFORMAT | SC_STATUS | 19719304Speter F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX)); 19819304Speter return (0); 19919304Speter} 20019304Speter 20119304Speter/* 202254225Speter * vs_vsplit -- 203254225Speter * Create a new screen, vertically. 204254225Speter * 205254225Speter * PUBLIC: int vs_vsplit __P((SCR *, SCR *)); 206254225Speter */ 207254225Speterint 208254225Spetervs_vsplit(SCR *sp, SCR *new) 209254225Speter{ 210254225Speter GS *gp; 211254225Speter size_t cols; 212254225Speter 213254225Speter gp = sp->gp; 214254225Speter 215254225Speter /* Check to see if it's possible. */ 216254225Speter if (sp->cols / 2 <= MINIMUM_SCREEN_COLS) { 217254225Speter msgq(sp, M_ERR, 218254225Speter "288|Screen must be larger than %d columns to split", 219254225Speter MINIMUM_SCREEN_COLS * 2); 220254225Speter return (1); 221254225Speter } 222254225Speter 223254225Speter /* Wait for any messages in the screen. */ 224254225Speter vs_resolve(sp, NULL, 1); 225254225Speter 226254225Speter /* Get a new screen map. */ 227254225Speter CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP)); 228254225Speter if (_HMAP(new) == NULL) 229254225Speter return (1); 230254225Speter _HMAP(new)->lno = sp->lno; 231254225Speter _HMAP(new)->coff = 0; 232254225Speter _HMAP(new)->soff = 1; 233254225Speter 234254225Speter /* 235254225Speter * Split the screen in half; we have to sacrifice a column to delimit 236254225Speter * the screens. 237254225Speter * 238254225Speter * XXX 239254225Speter * We always split to the right... that makes more sense to me, and 240254225Speter * I don't want to play the stupid games that I play when splitting 241254225Speter * horizontally. 242254225Speter * 243254225Speter * XXX 244254225Speter * We reserve a column for the screen, "knowing" that curses needs 245254225Speter * one. This should be worked out with the display interface. 246254225Speter */ 247254225Speter cols = sp->cols / 2; 248254225Speter new->cols = sp->cols - cols - 1; 249254225Speter sp->cols = cols; 250254225Speter new->coff = sp->coff + cols + 1; 251254225Speter sp->cno = 0; 252254225Speter 253254225Speter /* Nothing else changes. */ 254254225Speter new->rows = sp->rows; 255254225Speter new->t_rows = sp->t_rows; 256254225Speter new->t_maxrows = sp->t_maxrows; 257254225Speter new->t_minrows = sp->t_minrows; 258254225Speter new->roff = sp->roff; 259254225Speter new->defscroll = sp->defscroll; 260254225Speter _TMAP(new) = _HMAP(new) + (new->t_rows - 1); 261254225Speter 262254225Speter /* Fit the screen into the logical chain. */ 263254225Speter vs_insert(new, sp->gp); 264254225Speter 265254225Speter /* Tell the display that we're splitting. */ 266254225Speter (void)gp->scr_split(sp, new); 267254225Speter 268254225Speter /* Redraw the old screen from scratch. */ 269254225Speter F_SET(sp, SC_SCR_REFORMAT | SC_STATUS); 270254225Speter 271254225Speter /* 272254225Speter * Initialize the screen flags: 273254225Speter * 274254225Speter * If we're in vi mode in one screen, we don't have to reinitialize. 275254225Speter * This isn't just a cosmetic fix. The path goes like this: 276254225Speter * 277254225Speter * return into vi(), SC_SSWITCH set 278254225Speter * call vs_refresh() with SC_STATUS set 279254225Speter * call vs_resolve to display the status message 280254225Speter * call vs_refresh() because the SC_SCR_VI bit isn't set 281254225Speter * 282254225Speter * Things go downhill at this point. 283254225Speter * 284254225Speter * Draw the new screen from scratch, and add a status line. 285254225Speter */ 286254225Speter F_SET(new, 287254225Speter SC_SCR_REFORMAT | SC_STATUS | 288254225Speter F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX)); 289254225Speter return (0); 290254225Speter} 291254225Speter 292254225Speter/* 293254225Speter * vs_insert -- 294254225Speter * Insert the new screen into the correct place in the logical 295254225Speter * chain. 296254225Speter */ 297254225Speterstatic void 298254225Spetervs_insert(SCR *sp, GS *gp) 299254225Speter{ 300254225Speter SCR *tsp; 301254225Speter 302254225Speter gp = sp->gp; 303254225Speter 304254225Speter /* Move past all screens with lower row numbers. */ 305254225Speter TAILQ_FOREACH(tsp, gp->dq, q) 306254225Speter if (tsp->roff >= sp->roff) 307254225Speter break; 308254225Speter /* 309254225Speter * Move past all screens with the same row number and lower 310254225Speter * column numbers. 311254225Speter */ 312254225Speter for (; tsp != NULL; tsp = TAILQ_NEXT(tsp, q)) 313254225Speter if (tsp->roff != sp->roff || tsp->coff > sp->coff) 314254225Speter break; 315254225Speter 316254225Speter /* 317254225Speter * If we reached the end, this screen goes there. Otherwise, 318254225Speter * put it before or after the screen where we stopped. 319254225Speter */ 320254225Speter if (tsp == NULL) { 321254225Speter TAILQ_INSERT_TAIL(gp->dq, sp, q); 322254225Speter } else if (tsp->roff < sp->roff || 323254225Speter (tsp->roff == sp->roff && tsp->coff < sp->coff)) { 324254225Speter TAILQ_INSERT_AFTER(gp->dq, tsp, sp, q); 325254225Speter } else 326254225Speter TAILQ_INSERT_BEFORE(tsp, sp, q); 327254225Speter} 328254225Speter 329254225Speter/* 33019304Speter * vs_discard -- 33119304Speter * Discard the screen, folding the real-estate into a related screen, 33219304Speter * if one exists, and return that screen. 33319304Speter * 33419304Speter * PUBLIC: int vs_discard __P((SCR *, SCR **)); 33519304Speter */ 33619304Speterint 337254225Spetervs_discard(SCR *sp, SCR **spp) 33819304Speter{ 339254225Speter GS *gp; 340254225Speter SCR *tsp, **lp, *list[100]; 341254225Speter jdir_t jdir; 34219304Speter 343254225Speter gp = sp->gp; 344254225Speter 34519304Speter /* 34619304Speter * Save the old screen's cursor information. 34719304Speter * 34819304Speter * XXX 34919304Speter * If called after file_end(), and the underlying file was a tmp 35019304Speter * file, it may have gone away. 35119304Speter */ 35219304Speter if (sp->frp != NULL) { 35319304Speter sp->frp->lno = sp->lno; 35419304Speter sp->frp->cno = sp->cno; 35519304Speter F_SET(sp->frp, FR_CURSORSET); 35619304Speter } 35719304Speter 358254225Speter /* If no other screens to join, we're done. */ 359254225Speter if (!IS_SPLIT(sp)) { 360254225Speter (void)gp->scr_discard(sp, NULL); 36119304Speter 362254225Speter if (spp != NULL) 363254225Speter *spp = NULL; 36419304Speter return (0); 365254225Speter } 366254225Speter 36719304Speter /* 368254225Speter * Find a set of screens that cover one of the screen's borders. 369254225Speter * Check the vertical axis first, for no particular reason. 37019304Speter * 371254225Speter * XXX 372254225Speter * It's possible (I think?), to create a screen that shares no full 373254225Speter * border with any other set of screens, so we can't discard it. We 374254225Speter * just complain at the user until they clean it up. 37519304Speter */ 376254225Speter if (vs_join(sp, list, &jdir)) 377254225Speter return (1); 37819304Speter 37919304Speter /* 380254225Speter * Modify the affected screens. Redraw the modified screen(s) from 381254225Speter * scratch, setting a status line. If this is ever a performance 382254225Speter * problem we could play games with the map, but I wrote that code 383254225Speter * before and it was never clean or easy. 38419304Speter * 385254225Speter * Don't clean up the discarded screen's information. If the screen 386254225Speter * isn't exiting, we'll do the work when the user redisplays it. 38719304Speter */ 388254225Speter switch (jdir) { 389254225Speter case HORIZ_FOLLOW: 390254225Speter case HORIZ_PRECEDE: 391254225Speter for (lp = &list[0]; (tsp = *lp) != NULL; ++lp) { 392254225Speter /* 393254225Speter * Small screens: see vs_refresh.c section 6a. Adjust 394254225Speter * text line info, unless it's a small screen. 395254225Speter * 396254225Speter * Reset the length of the default scroll. 397254225Speter * 398254225Speter * Reset the map references. 399254225Speter */ 400254225Speter tsp->rows += sp->rows; 401254225Speter if (!IS_SMALL(tsp)) 402254225Speter tsp->t_rows = tsp->t_minrows = tsp->rows - 1; 403254225Speter tsp->t_maxrows = tsp->rows - 1; 404254225Speter 405254225Speter tsp->defscroll = tsp->t_maxrows / 2; 406254225Speter 407254225Speter *(_HMAP(tsp) + (tsp->t_rows - 1)) = *_TMAP(tsp); 408254225Speter _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1); 409254225Speter 410254225Speter switch (jdir) { 411254225Speter case HORIZ_FOLLOW: 412254225Speter tsp->roff = sp->roff; 413254225Speter vs_sm_fill(tsp, OOBLNO, P_TOP); 414254225Speter break; 415254225Speter case HORIZ_PRECEDE: 416254225Speter vs_sm_fill(tsp, OOBLNO, P_BOTTOM); 417254225Speter break; 418254225Speter default: 419254225Speter abort(); 420254225Speter } 421254225Speter F_SET(tsp, SC_STATUS); 422254225Speter } 42319304Speter break; 424254225Speter case VERT_FOLLOW: 425254225Speter case VERT_PRECEDE: 426254225Speter for (lp = &list[0]; (tsp = *lp) != NULL; ++lp) { 427254225Speter if (jdir == VERT_FOLLOW) 428254225Speter tsp->coff = sp->coff; 429254225Speter tsp->cols += sp->cols + 1; /* XXX: DIVIDER */ 430254225Speter vs_sm_fill(tsp, OOBLNO, P_TOP); 431254225Speter F_SET(tsp, SC_STATUS); 432254225Speter } 43319304Speter break; 43419304Speter default: 43519304Speter abort(); 43619304Speter } 43719304Speter 438254225Speter /* Find the closest screen that changed and move to it. */ 439254225Speter tsp = list[0]; 440254225Speter if (spp != NULL) 441254225Speter *spp = tsp; 442254225Speter 443254225Speter /* Tell the display that we're discarding a screen. */ 444254225Speter (void)gp->scr_discard(sp, list); 445254225Speter 44619304Speter return (0); 44719304Speter} 44819304Speter 44919304Speter/* 450254225Speter * vs_join -- 451254225Speter * Find a set of screens that covers a screen's border. 452254225Speter */ 453254225Speterstatic int 454254225Spetervs_join(SCR *sp, SCR **listp, jdir_t *jdirp) 455254225Speter{ 456254225Speter GS *gp; 457254225Speter SCR **lp, *tsp; 458254225Speter int first; 459254225Speter size_t tlen; 460254225Speter 461254225Speter gp = sp->gp; 462254225Speter 463254225Speter /* Check preceding vertical. */ 464254225Speter for (lp = listp, tlen = sp->rows, 465254225Speter tsp = TAILQ_FIRST(gp->dq); 466254225Speter tsp != NULL; tsp = TAILQ_NEXT(tsp, q)) { 467254225Speter if (sp == tsp) 468254225Speter continue; 469254225Speter /* Test if precedes the screen vertically. */ 470254225Speter if (tsp->coff + tsp->cols + 1 != sp->coff) 471254225Speter continue; 472254225Speter /* 473254225Speter * Test if a subset on the vertical axis. If overlaps the 474254225Speter * beginning or end, we can't join on this axis at all. 475254225Speter */ 476254225Speter if (tsp->roff > sp->roff + sp->rows) 477254225Speter continue; 478254225Speter if (tsp->roff < sp->roff) { 479254225Speter if (tsp->roff + tsp->rows >= sp->roff) 480254225Speter break; 481254225Speter continue; 482254225Speter } 483254225Speter if (tsp->roff + tsp->rows > sp->roff + sp->rows) 484254225Speter break; 485254225Speter#ifdef DEBUG 486254225Speter if (tlen < tsp->rows) 487254225Speter abort(); 488254225Speter#endif 489254225Speter tlen -= tsp->rows; 490254225Speter *lp++ = tsp; 491254225Speter } 492254225Speter if (tlen == 0) { 493254225Speter *lp = NULL; 494254225Speter *jdirp = VERT_PRECEDE; 495254225Speter return (0); 496254225Speter } 497254225Speter 498254225Speter /* Check following vertical. */ 499254225Speter for (lp = listp, tlen = sp->rows, 500254225Speter tsp = TAILQ_FIRST(gp->dq); 501254225Speter tsp != NULL; tsp = TAILQ_NEXT(tsp, q)) { 502254225Speter if (sp == tsp) 503254225Speter continue; 504254225Speter /* Test if follows the screen vertically. */ 505254225Speter if (tsp->coff != sp->coff + sp->cols + 1) 506254225Speter continue; 507254225Speter /* 508254225Speter * Test if a subset on the vertical axis. If overlaps the 509254225Speter * beginning or end, we can't join on this axis at all. 510254225Speter */ 511254225Speter if (tsp->roff > sp->roff + sp->rows) 512254225Speter continue; 513254225Speter if (tsp->roff < sp->roff) { 514254225Speter if (tsp->roff + tsp->rows >= sp->roff) 515254225Speter break; 516254225Speter continue; 517254225Speter } 518254225Speter if (tsp->roff + tsp->rows > sp->roff + sp->rows) 519254225Speter break; 520254225Speter#ifdef DEBUG 521254225Speter if (tlen < tsp->rows) 522254225Speter abort(); 523254225Speter#endif 524254225Speter tlen -= tsp->rows; 525254225Speter *lp++ = tsp; 526254225Speter } 527254225Speter if (tlen == 0) { 528254225Speter *lp = NULL; 529254225Speter *jdirp = VERT_FOLLOW; 530254225Speter return (0); 531254225Speter } 532254225Speter 533254225Speter /* Check preceding horizontal. */ 534254225Speter for (first = 0, lp = listp, tlen = sp->cols, 535254225Speter tsp = TAILQ_FIRST(gp->dq); 536254225Speter tsp != NULL; tsp = TAILQ_NEXT(tsp, q)) { 537254225Speter if (sp == tsp) 538254225Speter continue; 539254225Speter /* Test if precedes the screen horizontally. */ 540254225Speter if (tsp->roff + tsp->rows != sp->roff) 541254225Speter continue; 542254225Speter /* 543254225Speter * Test if a subset on the horizontal axis. If overlaps the 544254225Speter * beginning or end, we can't join on this axis at all. 545254225Speter */ 546254225Speter if (tsp->coff > sp->coff + sp->cols) 547254225Speter continue; 548254225Speter if (tsp->coff < sp->coff) { 549254225Speter if (tsp->coff + tsp->cols >= sp->coff) 550254225Speter break; 551254225Speter continue; 552254225Speter } 553254225Speter if (tsp->coff + tsp->cols > sp->coff + sp->cols) 554254225Speter break; 555254225Speter#ifdef DEBUG 556254225Speter if (tlen < tsp->cols) 557254225Speter abort(); 558254225Speter#endif 559254225Speter tlen -= tsp->cols + first; 560254225Speter first = 1; 561254225Speter *lp++ = tsp; 562254225Speter } 563254225Speter if (tlen == 0) { 564254225Speter *lp = NULL; 565254225Speter *jdirp = HORIZ_PRECEDE; 566254225Speter return (0); 567254225Speter } 568254225Speter 569254225Speter /* Check following horizontal. */ 570254225Speter for (first = 0, lp = listp, tlen = sp->cols, 571254225Speter tsp = TAILQ_FIRST(gp->dq); 572254225Speter tsp != NULL; tsp = TAILQ_NEXT(tsp, q)) { 573254225Speter if (sp == tsp) 574254225Speter continue; 575254225Speter /* Test if precedes the screen horizontally. */ 576254225Speter if (tsp->roff != sp->roff + sp->rows) 577254225Speter continue; 578254225Speter /* 579254225Speter * Test if a subset on the horizontal axis. If overlaps the 580254225Speter * beginning or end, we can't join on this axis at all. 581254225Speter */ 582254225Speter if (tsp->coff > sp->coff + sp->cols) 583254225Speter continue; 584254225Speter if (tsp->coff < sp->coff) { 585254225Speter if (tsp->coff + tsp->cols >= sp->coff) 586254225Speter break; 587254225Speter continue; 588254225Speter } 589254225Speter if (tsp->coff + tsp->cols > sp->coff + sp->cols) 590254225Speter break; 591254225Speter#ifdef DEBUG 592254225Speter if (tlen < tsp->cols) 593254225Speter abort(); 594254225Speter#endif 595254225Speter tlen -= tsp->cols + first; 596254225Speter first = 1; 597254225Speter *lp++ = tsp; 598254225Speter } 599254225Speter if (tlen == 0) { 600254225Speter *lp = NULL; 601254225Speter *jdirp = HORIZ_FOLLOW; 602254225Speter return (0); 603254225Speter } 604254225Speter return (1); 605254225Speter} 606254225Speter 607254225Speter/* 60819304Speter * vs_fg -- 60919304Speter * Background the current screen, and foreground a new one. 61019304Speter * 61119304Speter * PUBLIC: int vs_fg __P((SCR *, SCR **, CHAR_T *, int)); 61219304Speter */ 61319304Speterint 614254225Spetervs_fg(SCR *sp, SCR **nspp, CHAR_T *name, int newscreen) 61519304Speter{ 61619304Speter GS *gp; 61719304Speter SCR *nsp; 618254225Speter char *np; 619254225Speter size_t nlen; 62019304Speter 62119304Speter gp = sp->gp; 62219304Speter 623254225Speter if (name) 624254225Speter INT2CHAR(sp, name, STRLEN(name) + 1, np, nlen); 625254225Speter else 626254225Speter np = NULL; 62719304Speter if (newscreen) 62819304Speter /* Get the specified background screen. */ 629254225Speter nsp = vs_getbg(sp, np); 63019304Speter else 63119304Speter /* Swap screens. */ 632254225Speter if (vs_swap(sp, &nsp, np)) 63319304Speter return (1); 63419304Speter 63519304Speter if ((*nspp = nsp) == NULL) { 636254225Speter msgq_wstr(sp, M_ERR, name, 63719304Speter name == NULL ? 63819304Speter "223|There are no background screens" : 63919304Speter "224|There's no background screen editing a file named %s"); 64019304Speter return (1); 64119304Speter } 64219304Speter 64319304Speter if (newscreen) { 64419304Speter /* Remove the new screen from the background queue. */ 645254225Speter TAILQ_REMOVE(gp->hq, nsp, q); 64619304Speter 64719304Speter /* Split the screen; if we fail, hook the screen back in. */ 64819304Speter if (vs_split(sp, nsp, 0)) { 649254225Speter TAILQ_INSERT_TAIL(gp->hq, nsp, q); 65019304Speter return (1); 65119304Speter } 65219304Speter } else { 65319304Speter /* Move the old screen to the background queue. */ 654254225Speter TAILQ_REMOVE(gp->dq, sp, q); 655254225Speter TAILQ_INSERT_TAIL(gp->hq, sp, q); 65619304Speter } 65719304Speter return (0); 65819304Speter} 65919304Speter 66019304Speter/* 66119304Speter * vs_bg -- 66219304Speter * Background the screen, and switch to the next one. 66319304Speter * 66419304Speter * PUBLIC: int vs_bg __P((SCR *)); 66519304Speter */ 66619304Speterint 667254225Spetervs_bg(SCR *sp) 66819304Speter{ 66919304Speter GS *gp; 67019304Speter SCR *nsp; 67119304Speter 67219304Speter gp = sp->gp; 67319304Speter 67419304Speter /* Try and join with another screen. */ 67519304Speter if (vs_discard(sp, &nsp)) 67619304Speter return (1); 67719304Speter if (nsp == NULL) { 67819304Speter msgq(sp, M_ERR, 67919304Speter "225|You may not background your only displayed screen"); 68019304Speter return (1); 68119304Speter } 68219304Speter 68319304Speter /* Move the old screen to the background queue. */ 684254225Speter TAILQ_REMOVE(gp->dq, sp, q); 685254225Speter TAILQ_INSERT_TAIL(gp->hq, sp, q); 68619304Speter 68719304Speter /* Toss the screen map. */ 68819304Speter free(_HMAP(sp)); 68919304Speter _HMAP(sp) = NULL; 69019304Speter 69119304Speter /* Switch screens. */ 69219304Speter sp->nextdisp = nsp; 69319304Speter F_SET(sp, SC_SSWITCH); 69419304Speter 69519304Speter return (0); 69619304Speter} 69719304Speter 69819304Speter/* 69919304Speter * vs_swap -- 70019304Speter * Swap the current screen with a backgrounded one. 70119304Speter * 70219304Speter * PUBLIC: int vs_swap __P((SCR *, SCR **, char *)); 70319304Speter */ 70419304Speterint 705254225Spetervs_swap(SCR *sp, SCR **nspp, char *name) 70619304Speter{ 70719304Speter GS *gp; 708254225Speter SCR *nsp, *list[2]; 70919304Speter 71019304Speter gp = sp->gp; 71119304Speter 71219304Speter /* Get the specified background screen. */ 71319304Speter if ((*nspp = nsp = vs_getbg(sp, name)) == NULL) 71419304Speter return (0); 71519304Speter 71619304Speter /* 71719304Speter * Save the old screen's cursor information. 71819304Speter * 71919304Speter * XXX 72019304Speter * If called after file_end(), and the underlying file was a tmp 72119304Speter * file, it may have gone away. 72219304Speter */ 72319304Speter if (sp->frp != NULL) { 72419304Speter sp->frp->lno = sp->lno; 72519304Speter sp->frp->cno = sp->cno; 72619304Speter F_SET(sp->frp, FR_CURSORSET); 72719304Speter } 72819304Speter 72919304Speter /* Switch screens. */ 73019304Speter sp->nextdisp = nsp; 73119304Speter F_SET(sp, SC_SSWITCH); 73219304Speter 73319304Speter /* Initialize terminal information. */ 73419304Speter VIP(nsp)->srows = VIP(sp)->srows; 73519304Speter 73619304Speter /* Initialize screen information. */ 73719304Speter nsp->cols = sp->cols; 73819304Speter nsp->rows = sp->rows; /* XXX: Only place in vi that sets rows. */ 739254225Speter nsp->roff = sp->roff; 74019304Speter 74119304Speter /* 74219304Speter * Small screens: see vs_refresh.c, section 6a. 74319304Speter * 74419304Speter * The new screens may have different screen options sizes than the 74519304Speter * old one, so use them. Make sure that text counts aren't larger 74619304Speter * than the new screen sizes. 74719304Speter */ 74819304Speter if (IS_SMALL(nsp)) { 74919304Speter nsp->t_minrows = nsp->t_rows = O_VAL(nsp, O_WINDOW); 75019304Speter if (nsp->t_rows > sp->t_maxrows) 75119304Speter nsp->t_rows = nsp->t_maxrows; 75219304Speter if (nsp->t_minrows > sp->t_maxrows) 75319304Speter nsp->t_minrows = nsp->t_maxrows; 75419304Speter } else 75519304Speter nsp->t_rows = nsp->t_maxrows = nsp->t_minrows = nsp->rows - 1; 75619304Speter 75719304Speter /* Reset the length of the default scroll. */ 75819304Speter nsp->defscroll = nsp->t_maxrows / 2; 75919304Speter 76019304Speter /* Allocate a new screen map. */ 76119304Speter CALLOC_RET(nsp, _HMAP(nsp), SMAP *, SIZE_HMAP(nsp), sizeof(SMAP)); 76219304Speter _TMAP(nsp) = _HMAP(nsp) + (nsp->t_rows - 1); 76319304Speter 76419304Speter /* Fill the map. */ 765254225Speter nsp->gp = sp->gp; 76619304Speter if (vs_sm_fill(nsp, nsp->lno, P_FILL)) 76719304Speter return (1); 76819304Speter 76919304Speter /* 77019304Speter * The new screen replaces the old screen in the parent/child list. 77119304Speter * We insert the new screen after the old one. If we're exiting, 77219304Speter * the exit will delete the old one, if we're foregrounding, the fg 77319304Speter * code will move the old one to the background queue. 77419304Speter */ 775254225Speter TAILQ_REMOVE(gp->hq, nsp, q); 776254225Speter TAILQ_INSERT_AFTER(gp->dq, sp, nsp, q); 77719304Speter 77819304Speter /* 77919304Speter * Don't change the screen's cursor information other than to 78019304Speter * note that the cursor is wrong. 78119304Speter */ 78219304Speter F_SET(VIP(nsp), VIP_CUR_INVALID); 78319304Speter 78419304Speter /* Draw the new screen from scratch, and add a status line. */ 78519304Speter F_SET(nsp, SC_SCR_REDRAW | SC_STATUS); 786254225Speter 787254225Speter list[0] = nsp; list[1] = NULL; 788254225Speter (void)gp->scr_discard(sp, list); 789254225Speter 79019304Speter return (0); 79119304Speter} 79219304Speter 79319304Speter/* 79419304Speter * vs_resize -- 79519304Speter * Change the absolute size of the current screen. 79619304Speter * 79719304Speter * PUBLIC: int vs_resize __P((SCR *, long, adj_t)); 79819304Speter */ 79919304Speterint 800254225Spetervs_resize(SCR *sp, long int count, adj_t adj) 80119304Speter{ 80219304Speter GS *gp; 803254225Speter SCR *g, *s, *prev, *next, *list[3] = {NULL, NULL, NULL}; 80419304Speter size_t g_off, s_off; 80519304Speter 80619304Speter gp = sp->gp; 80719304Speter 80819304Speter /* 80919304Speter * Figure out which screens will grow, which will shrink, and 81019304Speter * make sure it's possible. 81119304Speter */ 81219304Speter if (count == 0) 81319304Speter return (0); 81419304Speter if (adj == A_SET) { 81519304Speter if (sp->t_maxrows == count) 81619304Speter return (0); 81719304Speter if (sp->t_maxrows > count) { 81819304Speter adj = A_DECREASE; 81919304Speter count = sp->t_maxrows - count; 82019304Speter } else { 82119304Speter adj = A_INCREASE; 82219304Speter count = count - sp->t_maxrows; 82319304Speter } 82419304Speter } 82519304Speter 826254225Speter /* Find first overlapping screen */ 827254225Speter for (next = TAILQ_NEXT(sp, q); next != NULL && 828254225Speter (next->coff >= sp->coff + sp->cols || 829254225Speter next->coff + next->cols <= sp->coff); 830254225Speter next = TAILQ_NEXT(next, q)); 831254225Speter /* See if we can use it */ 832254225Speter if (next != NULL && 833254225Speter (sp->coff != next->coff || sp->cols != next->cols)) 834254225Speter next = NULL; 835254225Speter for (prev = TAILQ_PREV(sp, _dqh, q); prev != NULL && 836254225Speter (prev->coff >= sp->coff + sp->cols || 837254225Speter prev->coff + prev->cols <= sp->coff); 838254225Speter prev = TAILQ_PREV(prev, _dqh, q)); 839254225Speter if (prev != NULL && 840254225Speter (sp->coff != prev->coff || sp->cols != prev->cols)) 841254225Speter prev = NULL; 842254225Speter 84319304Speter g_off = s_off = 0; 84419304Speter if (adj == A_DECREASE) { 84519304Speter if (count < 0) 84619304Speter count = -count; 84719304Speter s = sp; 84819304Speter if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) 84919304Speter goto toosmall; 850254225Speter if ((g = prev) == NULL) { 851254225Speter if ((g = next) == NULL) 85219304Speter goto toobig; 85319304Speter g_off = -count; 85419304Speter } else 85519304Speter s_off = count; 85619304Speter } else { 85719304Speter g = sp; 858254225Speter if ((s = next) != NULL && 859254225Speter s->t_maxrows >= MINIMUM_SCREEN_ROWS + count) 86019304Speter s_off = count; 86119304Speter else 86219304Speter s = NULL; 86319304Speter if (s == NULL) { 864254225Speter if ((s = prev) == NULL) { 86519304Spetertoobig: msgq(sp, M_BERR, adj == A_DECREASE ? 86619304Speter "227|The screen cannot shrink" : 86719304Speter "228|The screen cannot grow"); 86819304Speter return (1); 86919304Speter } 87019304Speter if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) { 87119304Spetertoosmall: msgq(sp, M_BERR, 87219304Speter "226|The screen can only shrink to %d rows", 87319304Speter MINIMUM_SCREEN_ROWS); 87419304Speter return (1); 87519304Speter } 87619304Speter g_off = -count; 87719304Speter } 87819304Speter } 87919304Speter 88019304Speter /* 88119304Speter * Fix up the screens; we could optimize the reformatting of the 88219304Speter * screen, but this isn't likely to be a common enough operation 88319304Speter * to make it worthwhile. 88419304Speter */ 88519304Speter s->rows += -count; 886254225Speter s->roff += s_off; 88719304Speter g->rows += count; 888254225Speter g->roff += g_off; 88919304Speter 89019304Speter g->t_rows += count; 89119304Speter if (g->t_minrows == g->t_maxrows) 89219304Speter g->t_minrows += count; 89319304Speter g->t_maxrows += count; 89419304Speter _TMAP(g) += count; 89519304Speter F_SET(g, SC_SCR_REFORMAT | SC_STATUS); 89619304Speter 89719304Speter s->t_rows -= count; 89819304Speter s->t_maxrows -= count; 89919304Speter if (s->t_minrows > s->t_maxrows) 90019304Speter s->t_minrows = s->t_maxrows; 90119304Speter _TMAP(s) -= count; 90219304Speter F_SET(s, SC_SCR_REFORMAT | SC_STATUS); 90319304Speter 904254225Speter /* XXXX */ 905254225Speter list[0] = g; list[1] = s; 906254225Speter gp->scr_discard(0, list); 907254225Speter 90819304Speter return (0); 90919304Speter} 91019304Speter 91119304Speter/* 91219304Speter * vs_getbg -- 91319304Speter * Get the specified background screen, or, if name is NULL, the first 91419304Speter * background screen. 91519304Speter */ 91619304Speterstatic SCR * 917254225Spetervs_getbg(SCR *sp, char *name) 91819304Speter{ 91919304Speter GS *gp; 92019304Speter SCR *nsp; 92119304Speter char *p; 92219304Speter 92319304Speter gp = sp->gp; 92419304Speter 92519304Speter /* If name is NULL, return the first background screen on the list. */ 926254225Speter if (name == NULL) 927254225Speter return (TAILQ_FIRST(gp->hq)); 92819304Speter 92919304Speter /* Search for a full match. */ 930254225Speter TAILQ_FOREACH(nsp, gp->hq, q) 93119304Speter if (!strcmp(nsp->frp->name, name)) 93219304Speter break; 933254225Speter if (nsp != NULL) 93419304Speter return (nsp); 93519304Speter 93619304Speter /* Search for a last-component match. */ 937254225Speter TAILQ_FOREACH(nsp, gp->hq, q) { 93819304Speter if ((p = strrchr(nsp->frp->name, '/')) == NULL) 93919304Speter p = nsp->frp->name; 94019304Speter else 94119304Speter ++p; 94219304Speter if (!strcmp(p, name)) 94319304Speter break; 94419304Speter } 945254225Speter if (nsp != NULL) 94619304Speter return (nsp); 94719304Speter 94819304Speter return (NULL); 94919304Speter} 950