source: trunk/third/nvi/vi/vs_split.c @ 14302

Revision 14302, 14.0 KB checked in by ghudson, 25 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14301, which included commits to RCS files with non-trunk default branches.
Line 
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
13static 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
30static 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 */
38int
39vs_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 */
199int
200vs_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 */
286int
287vs_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 */
336int
337vs_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 */
375int
376vs_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 */
467int
468vs_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) {
521toobig:                         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) {
527toosmall:                       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 */
568static SCR *
569vs_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}
Note: See TracBrowser for help on using the repository browser.