home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / vi / vs_split.c < prev   
C/C++ Source or Header  |  1996-10-23  |  14KB  |  608 lines

  1. /*-
  2.  * Copyright (c) 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 1993, 1994, 1995, 1996
  5.  *    Keith Bostic.  All rights reserved.
  6.  *
  7.  * See the LICENSE file for redistribution information.
  8.  */
  9.  
  10. #include "config.h"
  11.  
  12. #ifndef lint
  13. static const char sccsid[] = "@(#)vs_split.c    10.31 (Berkeley) 10/13/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18. #include <sys/time.h>
  19.  
  20. #include <bitstring.h>
  21. #include <errno.h>
  22. #include <limits.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26.  
  27. #include "../common/common.h"
  28. #include "vi.h"
  29.  
  30. static SCR *vs_getbg __P((SCR *, char *));
  31.  
  32. /*
  33.  * vs_split --
  34.  *    Create a new screen.
  35.  *
  36.  * PUBLIC: int vs_split __P((SCR *, SCR *, int));
  37.  */
  38. int
  39. vs_split(sp, new, ccl)
  40.     SCR *sp, *new;
  41.     int ccl;        /* Colon-command line split. */
  42. {
  43.     GS *gp;
  44.     SMAP *smp;
  45.     size_t half;
  46.     int issmallscreen, splitup;
  47.  
  48.     gp = sp->gp;
  49.  
  50.     /* Check to see if it's possible. */
  51.     /* XXX: The IS_ONELINE fix will change this, too. */
  52.     if (sp->rows < 4) {
  53.         msgq(sp, M_ERR,
  54.             "222|Screen must be larger than %d lines to split", 4 - 1);
  55.         return (1);
  56.     }
  57.  
  58.     /* Wait for any messages in the screen. */
  59.     vs_resolve(sp, NULL, 1);
  60.  
  61.     half = sp->rows / 2;
  62.     if (ccl && half > 6)
  63.         half = 6;
  64.  
  65.     /* Get a new screen map. */
  66.     CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
  67.     if (_HMAP(new) == NULL)
  68.         return (1);
  69.     _HMAP(new)->lno = sp->lno;
  70.     _HMAP(new)->coff = 0;
  71.     _HMAP(new)->soff = 1;
  72.  
  73.     /*
  74.      * Small screens: see vs_refresh.c section 6a.  Set a flag so
  75.      * we know to fix the screen up later.
  76.      */
  77.     issmallscreen = IS_SMALL(sp);
  78.  
  79.     /* The columns in the screen don't change. */
  80.     new->cols = sp->cols;
  81.  
  82.     /*
  83.      * Split the screen, and link the screens together.  If creating a
  84.      * screen to edit the colon command line or the cursor is in the top
  85.      * half of the current screen, the new screen goes under the current
  86.      * screen.  Else, it goes above the current screen.
  87.      *
  88.      * Recalculate current cursor position based on sp->lno, we're called
  89.      * with the cursor on the colon command line.  Then split the screen
  90.      * in half and update the shared information.
  91.      */
  92.     splitup =
  93.         !ccl && (vs_sm_cursor(sp, &smp) ? 0 : (smp - HMAP) + 1) >= half;
  94.     if (splitup) {                /* Old is bottom half. */
  95.         new->rows = sp->rows - half;    /* New. */
  96.         new->woff = sp->woff;
  97.         sp->rows = half;        /* Old. */
  98.         sp->woff += new->rows;
  99.                         /* Link in before old. */
  100.         CIRCLEQ_INSERT_BEFORE(&gp->dq, sp, new, q);
  101.  
  102.         /*
  103.          * If the parent is the bottom half of the screen, shift
  104.          * the map down to match on-screen text.
  105.          */
  106.         memmove(_HMAP(sp), _HMAP(sp) + new->rows,
  107.             (sp->t_maxrows - new->rows) * sizeof(SMAP));
  108.     } else {                /* Old is top half. */
  109.         new->rows = half;        /* New. */
  110.         sp->rows -= half;        /* Old. */
  111.         new->woff = sp->woff + sp->rows;
  112.                         /* Link in after old. */
  113.         CIRCLEQ_INSERT_AFTER(&gp->dq, sp, new, q);
  114.     }
  115.  
  116.     /* Adjust maximum text count. */
  117.     sp->t_maxrows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
  118.     new->t_maxrows = IS_ONELINE(new) ? 1 : new->rows - 1;
  119.  
  120.     /*
  121.      * Small screens: see vs_refresh.c, section 6a.
  122.      *
  123.      * The child may have different screen options sizes than the parent,
  124.      * so use them.  Guarantee that text counts aren't larger than the
  125.      * new screen sizes.
  126.      */
  127.     if (issmallscreen) {
  128.         /* Fix the text line count for the parent. */
  129.         if (splitup)
  130.             sp->t_rows -= new->rows;
  131.  
  132.         /* Fix the parent screen. */
  133.         if (sp->t_rows > sp->t_maxrows)
  134.             sp->t_rows = sp->t_maxrows;
  135.         if (sp->t_minrows > sp->t_maxrows)
  136.             sp->t_minrows = sp->t_maxrows;
  137.  
  138.         /* Fix the child screen. */
  139.         new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
  140.         if (new->t_rows > new->t_maxrows)
  141.             new->t_rows = new->t_maxrows;
  142.         if (new->t_minrows > new->t_maxrows)
  143.             new->t_minrows = new->t_maxrows;
  144.     } else {
  145.         sp->t_minrows = sp->t_rows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
  146.  
  147.         /*
  148.          * The new screen may be a small screen, even if the parent
  149.          * was not.  Don't complain if O_WINDOW is too large, we're
  150.          * splitting the screen so the screen is much smaller than
  151.          * normal.
  152.          */
  153.         new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
  154.         if (new->t_rows > new->rows - 1)
  155.             new->t_minrows = new->t_rows =
  156.                 IS_ONELINE(new) ? 1 : new->rows - 1;
  157.     }
  158.  
  159.     /* Adjust the ends of the new and old maps. */
  160.     _TMAP(sp) = IS_ONELINE(sp) ?
  161.         _HMAP(sp) : _HMAP(sp) + (sp->t_rows - 1);
  162.     _TMAP(new) = IS_ONELINE(new) ?
  163.         _HMAP(new) : _HMAP(new) + (new->t_rows - 1);
  164.  
  165.     /* Reset the length of the default scroll. */
  166.     if ((sp->defscroll = sp->t_maxrows / 2) == 0)
  167.         sp->defscroll = 1;
  168.     if ((new->defscroll = new->t_maxrows / 2) == 0)
  169.         new->defscroll = 1;
  170.  
  171.     /*
  172.      * Initialize the screen flags:
  173.      *
  174.      * If we're in vi mode in one screen, we don't have to reinitialize.
  175.      * This isn't just a cosmetic fix.  The path goes like this:
  176.      *
  177.      *    return into vi(), SC_SSWITCH set
  178.      *    call vs_refresh() with SC_STATUS set
  179.      *    call vs_resolve to display the status message
  180.      *    call vs_refresh() because the SC_SCR_VI bit isn't set
  181.      *
  182.      * Things go downhill at this point.
  183.      *
  184.      * Draw the new screen from scratch, and add a status line.
  185.      */
  186.     F_SET(new,
  187.         SC_SCR_REFORMAT | SC_STATUS |
  188.         F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX));
  189.     return (0);
  190. }
  191.  
  192. /*
  193.  * vs_discard --
  194.  *    Discard the screen, folding the real-estate into a related screen,
  195.  *    if one exists, and return that screen.
  196.  *
  197.  * PUBLIC: int vs_discard __P((SCR *, SCR **));
  198.  */
  199. int
  200. vs_discard(sp, spp)
  201.     SCR *sp, **spp;
  202. {
  203.     SCR *nsp;
  204.     dir_t dir;
  205.  
  206.     /*
  207.      * Save the old screen's cursor information.
  208.      *
  209.      * XXX
  210.      * If called after file_end(), and the underlying file was a tmp
  211.      * file, it may have gone away.
  212.      */
  213.     if (sp->frp != NULL) {
  214.         sp->frp->lno = sp->lno;
  215.         sp->frp->cno = sp->cno;
  216.         F_SET(sp->frp, FR_CURSORSET);
  217.     }
  218.  
  219.     /*
  220.      * Add into a previous screen and then into a subsequent screen, as
  221.      * they're the closest to the current screen.  If that doesn't work,
  222.      * there was no screen to join.
  223.      */
  224.     if ((nsp = sp->q.cqe_prev) != (void *)&sp->gp->dq) {
  225.         nsp->rows += sp->rows;
  226.         sp = nsp;
  227.         dir = FORWARD;
  228.     } else if ((nsp = sp->q.cqe_next) != (void *)&sp->gp->dq) {
  229.         nsp->woff = sp->woff;
  230.         nsp->rows += sp->rows;
  231.         sp = nsp;
  232.         dir = BACKWARD;
  233.     } else
  234.         sp = NULL;
  235.  
  236.     if (spp != NULL)
  237.         *spp = sp;
  238.     if (sp == NULL)
  239.         return (0);
  240.         
  241.     /*
  242.      * Make no effort to clean up the discarded screen's information.  If
  243.      * it's not exiting, we'll do the work when the user redisplays it.
  244.      *
  245.      * Small screens: see vs_refresh.c section 6a.  Adjust text line info,
  246.      * unless it's a small screen.
  247.      *
  248.      * Reset the length of the default scroll.
  249.      */
  250.     if (!IS_SMALL(sp))
  251.         sp->t_rows = sp->t_minrows = sp->rows - 1;
  252.     sp->t_maxrows = sp->rows - 1;
  253.     sp->defscroll = sp->t_maxrows / 2;
  254.     *(HMAP + (sp->t_rows - 1)) = *TMAP;
  255.     TMAP = HMAP + (sp->t_rows - 1);
  256.  
  257.     /*
  258.      * Draw the new screen from scratch, and add a status line.
  259.      *
  260.      * XXX
  261.      * We could play games with the map, if this were ever to be a
  262.      * performance problem, but I wrote the code a few times and it
  263.      * was never clean or easy.
  264.      */
  265.     switch (dir) {
  266.     case FORWARD:
  267.         vs_sm_fill(sp, OOBLNO, P_TOP);
  268.         break;
  269.     case BACKWARD:
  270.         vs_sm_fill(sp, OOBLNO, P_BOTTOM);
  271.         break;
  272.     default:
  273.         abort();
  274.     }
  275.  
  276.     F_SET(sp, SC_STATUS);
  277.     return (0);
  278. }
  279.  
  280. /*
  281.  * vs_fg --
  282.  *    Background the current screen, and foreground a new one.
  283.  *
  284.  * PUBLIC: int vs_fg __P((SCR *, SCR **, CHAR_T *, int));
  285.  */
  286. int
  287. vs_fg(sp, nspp, name, newscreen)
  288.     SCR *sp, **nspp;
  289.     CHAR_T *name;
  290.     int newscreen;
  291. {
  292.     GS *gp;
  293.     SCR *nsp;
  294.  
  295.     gp = sp->gp;
  296.  
  297.     if (newscreen)
  298.         /* Get the specified background screen. */
  299.         nsp = vs_getbg(sp, name);
  300.     else
  301.         /* Swap screens. */
  302.         if (vs_swap(sp, &nsp, name))
  303.             return (1);
  304.  
  305.     if ((*nspp = nsp) == NULL) {
  306.         msgq_str(sp, M_ERR, name,
  307.             name == NULL ?
  308.             "223|There are no background screens" :
  309.             "224|There's no background screen editing a file named %s");
  310.         return (1);
  311.     }
  312.  
  313.     if (newscreen) {
  314.         /* Remove the new screen from the background queue. */
  315.         CIRCLEQ_REMOVE(&gp->hq, nsp, q);
  316.  
  317.         /* Split the screen; if we fail, hook the screen back in. */
  318.         if (vs_split(sp, nsp, 0)) {
  319.             CIRCLEQ_INSERT_TAIL(&gp->hq, nsp, q);
  320.             return (1);
  321.         }
  322.     } else {
  323.         /* Move the old screen to the background queue. */
  324.         CIRCLEQ_REMOVE(&gp->dq, sp, q);
  325.         CIRCLEQ_INSERT_TAIL(&gp->hq, sp, q);
  326.     }
  327.     return (0);
  328. }
  329.  
  330. /*
  331.  * vs_bg --
  332.  *    Background the screen, and switch to the next one.
  333.  *
  334.  * PUBLIC: int vs_bg __P((SCR *));
  335.  */
  336. int
  337. vs_bg(sp)
  338.     SCR *sp;
  339. {
  340.     GS *gp;
  341.     SCR *nsp;
  342.  
  343.     gp = sp->gp;
  344.  
  345.     /* Try and join with another screen. */
  346.     if (vs_discard(sp, &nsp))
  347.         return (1);
  348.     if (nsp == NULL) {
  349.         msgq(sp, M_ERR,
  350.             "225|You may not background your only displayed screen");
  351.         return (1);
  352.     }
  353.  
  354.     /* Move the old screen to the background queue. */
  355.     CIRCLEQ_REMOVE(&gp->dq, sp, q);
  356.     CIRCLEQ_INSERT_TAIL(&gp->hq, sp, q);
  357.  
  358.     /* Toss the screen map. */
  359.     free(_HMAP(sp));
  360.     _HMAP(sp) = NULL;
  361.  
  362.     /* Switch screens. */
  363.     sp->nextdisp = nsp;
  364.     F_SET(sp, SC_SSWITCH);
  365.  
  366.     return (0);
  367. }
  368.  
  369. /*
  370.  * vs_swap --
  371.  *    Swap the current screen with a backgrounded one.
  372.  *
  373.  * PUBLIC: int vs_swap __P((SCR *, SCR **, char *));
  374.  */
  375. int
  376. vs_swap(sp, nspp, name)
  377.     SCR *sp, **nspp;
  378.     char *name;
  379. {
  380.     GS *gp;
  381.     SCR *nsp;
  382.  
  383.     gp = sp->gp;
  384.  
  385.     /* Get the specified background screen. */
  386.     if ((*nspp = nsp = vs_getbg(sp, name)) == NULL)
  387.         return (0);
  388.  
  389.     /*
  390.      * Save the old screen's cursor information.
  391.      *
  392.      * XXX
  393.      * If called after file_end(), and the underlying file was a tmp
  394.      * file, it may have gone away.
  395.      */
  396.     if (sp->frp != NULL) {
  397.         sp->frp->lno = sp->lno;
  398.         sp->frp->cno = sp->cno;
  399.         F_SET(sp->frp, FR_CURSORSET);
  400.     }
  401.  
  402.     /* Switch screens. */
  403.     sp->nextdisp = nsp;
  404.     F_SET(sp, SC_SSWITCH);
  405.  
  406.     /* Initialize terminal information. */
  407.     VIP(nsp)->srows = VIP(sp)->srows;
  408.  
  409.     /* Initialize screen information. */
  410.     nsp->cols = sp->cols;
  411.     nsp->rows = sp->rows;    /* XXX: Only place in vi that sets rows. */
  412.     nsp->woff = sp->woff;
  413.  
  414.     /*
  415.      * Small screens: see vs_refresh.c, section 6a.
  416.      *
  417.      * The new screens may have different screen options sizes than the
  418.      * old one, so use them.  Make sure that text counts aren't larger
  419.      * than the new screen sizes.
  420.      */
  421.     if (IS_SMALL(nsp)) {
  422.         nsp->t_minrows = nsp->t_rows = O_VAL(nsp, O_WINDOW);
  423.         if (nsp->t_rows > sp->t_maxrows)
  424.             nsp->t_rows = nsp->t_maxrows;
  425.         if (nsp->t_minrows > sp->t_maxrows)
  426.             nsp->t_minrows = nsp->t_maxrows;
  427.     } else
  428.         nsp->t_rows = nsp->t_maxrows = nsp->t_minrows = nsp->rows - 1;
  429.  
  430.     /* Reset the length of the default scroll. */
  431.     nsp->defscroll = nsp->t_maxrows / 2;
  432.  
  433.     /* Allocate a new screen map. */
  434.     CALLOC_RET(nsp, _HMAP(nsp), SMAP *, SIZE_HMAP(nsp), sizeof(SMAP));
  435.     _TMAP(nsp) = _HMAP(nsp) + (nsp->t_rows - 1);
  436.  
  437.     /* Fill the map. */
  438.     if (vs_sm_fill(nsp, nsp->lno, P_FILL))
  439.         return (1);
  440.  
  441.     /*
  442.      * The new screen replaces the old screen in the parent/child list.
  443.      * We insert the new screen after the old one.  If we're exiting,
  444.      * the exit will delete the old one, if we're foregrounding, the fg
  445.      * code will move the old one to the background queue.
  446.      */
  447.     CIRCLEQ_REMOVE(&gp->hq, nsp, q);
  448.     CIRCLEQ_INSERT_AFTER(&gp->dq, sp, nsp, q);
  449.  
  450.     /*
  451.      * Don't change the screen's cursor information other than to
  452.      * note that the cursor is wrong.
  453.      */
  454.     F_SET(VIP(nsp), VIP_CUR_INVALID);
  455.  
  456.     /* Draw the new screen from scratch, and add a status line. */
  457.     F_SET(nsp, SC_SCR_REDRAW | SC_STATUS);
  458.     return (0);
  459. }
  460.  
  461. /*
  462.  * vs_resize --
  463.  *    Change the absolute size of the current screen.
  464.  *
  465.  * PUBLIC: int vs_resize __P((SCR *, long, adj_t));
  466.  */
  467. int
  468. vs_resize(sp, count, adj)
  469.     SCR *sp;
  470.     long count;
  471.     adj_t adj;
  472. {
  473.     GS *gp;
  474.     SCR *g, *s;
  475.     size_t g_off, s_off;
  476.  
  477.     gp = sp->gp;
  478.  
  479.     /*
  480.      * Figure out which screens will grow, which will shrink, and
  481.      * make sure it's possible.
  482.      */
  483.     if (count == 0)
  484.         return (0);
  485.     if (adj == A_SET) {
  486.         if (sp->t_maxrows == count)
  487.             return (0);
  488.         if (sp->t_maxrows > count) {
  489.             adj = A_DECREASE;
  490.             count = sp->t_maxrows - count;
  491.         } else {
  492.             adj = A_INCREASE;
  493.             count = count - sp->t_maxrows;
  494.         }
  495.     }
  496.  
  497.     g_off = s_off = 0;
  498.     if (adj == A_DECREASE) {
  499.         if (count < 0)
  500.             count = -count;
  501.         s = sp;
  502.         if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
  503.             goto toosmall;
  504.         if ((g = sp->q.cqe_prev) == (void *)&gp->dq) {
  505.             if ((g = sp->q.cqe_next) == (void *)&gp->dq)
  506.                 goto toobig;
  507.             g_off = -count;
  508.         } else
  509.             s_off = count;
  510.     } else {
  511.         g = sp;
  512.         if ((s = sp->q.cqe_next) != (void *)&gp->dq)
  513.             if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
  514.                 s = NULL;
  515.             else
  516.                 s_off = count;
  517.         else
  518.             s = NULL;
  519.         if (s == NULL) {
  520.             if ((s = sp->q.cqe_prev) == (void *)&gp->dq) {
  521. toobig:                msgq(sp, M_BERR, adj == A_DECREASE ?
  522.                     "227|The screen cannot shrink" :
  523.                     "228|The screen cannot grow");
  524.                 return (1);
  525.             }
  526.             if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) {
  527. toosmall:            msgq(sp, M_BERR,
  528.                     "226|The screen can only shrink to %d rows",
  529.                     MINIMUM_SCREEN_ROWS);
  530.                 return (1);
  531.             }
  532.             g_off = -count;
  533.         }
  534.     }
  535.  
  536.     /*
  537.      * Fix up the screens; we could optimize the reformatting of the
  538.      * screen, but this isn't likely to be a common enough operation
  539.      * to make it worthwhile.
  540.      */
  541.     s->rows += -count;
  542.     s->woff += s_off;
  543.     g->rows += count;
  544.     g->woff += g_off;
  545.  
  546.     g->t_rows += count;
  547.     if (g->t_minrows == g->t_maxrows)
  548.         g->t_minrows += count;
  549.     g->t_maxrows += count;
  550.     _TMAP(g) += count;
  551.     F_SET(g, SC_SCR_REFORMAT | SC_STATUS);
  552.  
  553.     s->t_rows -= count;
  554.     s->t_maxrows -= count;
  555.     if (s->t_minrows > s->t_maxrows)
  556.         s->t_minrows = s->t_maxrows;
  557.     _TMAP(s) -= count;
  558.     F_SET(s, SC_SCR_REFORMAT | SC_STATUS);
  559.  
  560.     return (0);
  561. }
  562.  
  563. /*
  564.  * vs_getbg --
  565.  *    Get the specified background screen, or, if name is NULL, the first
  566.  *    background screen.
  567.  */
  568. static SCR *
  569. vs_getbg(sp, name)
  570.     SCR *sp;
  571.     char *name;
  572. {
  573.     GS *gp;
  574.     SCR *nsp;
  575.     char *p;
  576.  
  577.     gp = sp->gp;
  578.  
  579.     /* If name is NULL, return the first background screen on the list. */
  580.     if (name == NULL) {
  581.         nsp = gp->hq.cqh_first;
  582.         return (nsp == (void *)&gp->hq ? NULL : nsp);
  583.     }
  584.  
  585.     /* Search for a full match. */
  586.     for (nsp = gp->hq.cqh_first;
  587.         nsp != (void *)&gp->hq; nsp = nsp->q.cqe_next)
  588.         if (!strcmp(nsp->frp->name, name))
  589.             break;
  590.     if (nsp != (void *)&gp->hq)
  591.         return (nsp);
  592.  
  593.     /* Search for a last-component match. */
  594.     for (nsp = gp->hq.cqh_first;
  595.         nsp != (void *)&gp->hq; nsp = nsp->q.cqe_next) {
  596.         if ((p = strrchr(nsp->frp->name, '/')) == NULL)
  597.             p = nsp->frp->name;
  598.         else
  599.             ++p;
  600.         if (!strcmp(p, name))
  601.             break;
  602.     }
  603.     if (nsp != (void *)&gp->hq)
  604.         return (nsp);
  605.  
  606.     return (NULL);
  607. }
  608.