we’ll need to distinguish these for sarge/etch as well
[alioth/jupp.git] / w.c
1 /*
2  *      Window system
3  *      Copyright
4  *              (C) 1992 Joseph H. Allen
5  *
6  *      This file is part of JOE (Joe's Own Editor)
7  */
8 #include "config.h"
9 #include "types.h"
10
11 __RCSID("$MirOS: contrib/code/jupp/w.c,v 1.8 2017/12/02 02:07:38 tg Exp $");
12
13 #ifdef HAVE_STDLIB_H
14 #include <stdlib.h>
15 #endif
16
17 #include "blocks.h"
18 #include "kbd.h"
19 #include "poshist.h"
20 #include "queue.h"
21 #include "rc.h"
22 #include "scrn.h"
23 #include "utils.h"
24 #include "utf8.h"
25 #include "w.h"
26
27 extern int dspasis;             /* Set to display chars above 127 as-is */
28 extern int staen;               /* 0 if top-most status line not displayed */
29
30 /* Count no. of main windows */
31
32 int countmain(SCREEN *t)
33 {
34         int nmain = 1;
35         W *m = t->curwin->main;
36         W *q;
37
38         for (q = t->curwin->link.next; q != t->curwin; q = q->link.next)
39                 if (q->main != m) {
40                         ++nmain;
41                         m = q->main;
42                 }
43         return nmain;
44 }
45
46 /* Redraw a window */
47
48 void wredraw(W *w)
49 {
50         msetI(w->t->t->updtab + w->y, 1, w->h);
51         msetI(w->t->t->syntab + w->y, -1, w->h);
52 }
53
54 /* Find first window in a group */
55
56 W *findtopw(W *w)
57 {
58         W *x;
59
60         for (x = w; x->link.prev->main == w->main && x->link.prev != w; x = x->link.prev) ;
61         return x;
62 }
63
64 /* Determine height of a window.  Returns reqh if it is set, otherwise
65  * used fixed or hh scaled to the current screen size */
66
67 static int geth(W *w)
68 {
69         if (w->reqh)
70                 return w->reqh;
71         else if (w->fixed)
72                 return w->fixed;
73         else
74                 return (((long) w->t->h - w->t->wind) * w->hh) / 1000;
75 }
76
77 /* Set the height of a window */
78
79 static void seth(W *w, int h)
80 {
81         long tmp;
82         int tmpb;
83
84         w->reqh = h;
85         tmp = 1000L * h;
86         tmpb = w->t->h - w->t->wind;
87         w->hh = (tmp + (tmpb - 1)) / tmpb;
88 }
89
90 /* Determine height of a family of windows.  Uses 'reqh' if it's set */
91
92 int getgrouph(W *w)
93 {
94         W *x;
95         int h;
96
97         /* Find first window in family */
98         x = findtopw(w);
99
100         /* Add heights of all windows in family */
101         for (w = x, h = geth(w); w->link.next != x && w->link.next->main == x->main; w = w->link.next, h += geth(w)) ;
102
103         return h;
104 }
105
106 /* Determine minimum height of a family */
107
108 static int getminh(W *w)
109 {
110         W *x;
111         int h;
112
113         x = findtopw(w);
114         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)) ;
115
116         return h;
117 }
118
119 /* Find last window in a group */
120
121 W *findbotw(W *w)
122 {
123         W *x;
124
125         for (x = w; x->link.next->main == w->main && x->link.next != w; x = x->link.next) ;
126         return x;
127 }
128
129 /* Demote group of window to end of window list.  Returns true if top window
130    was demoted */
131
132 int demotegroup(W *w)
133 {
134         W *top = findtopw(w);
135         W *bot = findbotw(w);
136         W *next;
137         int flg = 0;
138
139         for (w = top; w != bot; w = next) {
140                 next = w->link.next;
141                 if (w == w->t->topwin) {
142                         flg = 1;
143                         w->t->topwin = next;
144                 } else
145                         demote(W, link, w->t->topwin, w);
146                 w->y = -1;
147         }
148         if (w == w->t->topwin)
149                 flg = 1;
150         else
151                 demote(W, link, w->t->topwin, w);
152         w->y = -1;
153         return flg;
154 }
155
156 /* Find last window on the screen */
157
158 W *lastw(SCREEN *t)
159 {
160         W *x;
161
162         for (x = t->topwin; x->link.next != t->topwin && x->link.next->y >= 0; x = x->link.next) ;
163         return x;
164 }
165
166 /* Create a screen object */
167
168 SCREEN *scr;
169
170 SCREEN *screate(SCRN *scrn)
171 {
172         SCREEN *t = (SCREEN *) joe_malloc(sizeof(SCREEN));
173
174         t->t = scrn;
175         t->w = scrn->co;
176         t->h = scrn->li;
177         t->topwin = NULL;
178         t->curwin = NULL;
179         t->wind = skiptop;
180         scr = t;
181         return t;
182 }
183
184 void sresize(SCREEN *t)
185 {
186         SCRN *scrn = t->t;
187         W *w;
188
189         t->w = scrn->co;
190         t->h = scrn->li;
191         if (t->h - t->wind < FITHEIGHT)
192                 t->wind = t->h - FITHEIGHT;
193         if (t->wind < 0)
194                 t->wind = 0;
195         w = t->topwin;
196         do {
197                 w->y = -1;
198                 w->w = t->w - 1;
199                 w = w->link.next;
200         } while (w != t->topwin);
201         wfit(t);
202         updall();
203 }
204
205 void updall(void)
206 {
207         int y;
208
209         for (y = 0; y != scr->h; ++y) {
210                 scr->t->updtab[y] = 1;
211                 scr->t->syntab[y] = -1;
212                 }
213 }
214
215 void scrins(B *b, long l, long n, int flg)
216 {
217         W *w;
218
219         if ((w = scr->topwin) != NULL) {
220                 do {
221                         if (w->y >= 0) {
222                                 if (w->object && w->watom->ins)
223                                         w->watom->ins(w->object, b, l, n, flg);
224                         }
225                 w = w->link.next;
226                 } while (w != scr->topwin);
227         }
228 }
229
230 void scrdel(B *b, long l, long n, int flg)
231 {
232         W *w;
233
234         if ((w = scr->topwin) != NULL) {
235                 do {
236                         if (w->y >= 0) {
237                                 if (w->object && w->watom->del)
238                                         w->watom->del(w->object, b, l, n, flg);
239                         }
240                 w = w->link.next;
241                 } while (w != scr->topwin);
242         }
243 }
244
245 /* Fit as many windows on the screen as is possible beginning with the window
246  * at topwin.  Give any extra space which couldn't be used to fit in another
247  * window to the last text window on the screen.  This function guarentees
248  * to fit on the window with the cursor in it (moves topwin to next group
249  * of windows until window with cursor fits on screen).
250  */
251
252 static int doabort(W *w, int *ret);
253 extern volatile int dostaupd;
254
255 void wfit(SCREEN *t)
256 {
257         int y;                  /* Where next window goes */
258         int left;               /* Lines left on screen */
259         W *w;                   /* Current window we're fitting */
260         W *pw;                  /* Main window of previous family */
261         int req;                /* Amount this family needs */
262         int adj;                /* Amount family needs to be adjusted */
263         int flg = 0;            /* Set if cursor window was placed on screen */
264         int ret;
265
266         dostaupd = 1;
267
268       tryagain:
269         y = t->wind;
270         left = t->h - y;
271         pw = NULL;
272
273         w = t->topwin;
274         do {
275                 w->ny = -1;
276                 w->nh = geth(w);
277                 w = w->link.next;
278         } while (w != t->topwin);
279
280         /* Fit a group of windows on the screen */
281         w = t->topwin;
282         do {
283                 req = getgrouph(w);
284                 if (req > left) /* If group is taller than lines left */
285                         adj = req - left;       /* then family gets shorter */
286                 else
287                         adj = 0;
288
289                 /* Fit a family of windows on the screen */
290                 do {
291                         w->ny = y;      /* Set window's y position */
292                         if (!w->win) {
293                                 pw = w;
294                                 w->nh -= adj;   /* Adjust main window of the group */
295                         }
296                         if (!w->win && w->nh < 2)
297                                 while (w->nh < 2)
298                                         w->nh += doabort(w->link.next, &ret);
299                         if (w == t->curwin)
300                                 flg = 1;        /* Set if we got window with cursor */
301                         y += w->nh;
302                         left -= w->nh;  /* Increment y value by height of window */
303                         w = w->link.next;       /* Next window */
304                 } while (w != t->topwin && w->main == w->link.prev->main);
305         } while (w != t->topwin && left >= FITHEIGHT);
306
307         if (!pw)
308                 return;
309
310         /* We can't use extra space to fit a new family on, so give space to parent of
311          * previous family */
312         pw->nh += left;
313
314         /* Adjust that family's children which are below the parent */
315         while ((pw = pw->link.next) != w)
316                 pw->ny += left;
317
318         /* Make sure the cursor window got on the screen */
319         if (!flg) {
320                 t->topwin = findbotw(t->topwin)->link.next;
321                 goto tryagain;
322         }
323
324         /* All of the windows are now on the screen.  Scroll the screen to reflect what
325          * happened
326          */
327         w = t->topwin;
328         do {
329                 if (w->y >= 0 && w->ny >= 0)
330                         if (w->ny > w->y) {
331                                 W *l = pw = w;
332
333                                 while (pw->link.next != t->topwin && (pw->link.next->y < 0 || pw->link.next->ny < 0 || pw->link.next->ny > pw->link.next->y)) {
334                                         pw = pw->link.next;
335                                         if (pw->ny >= 0 && pw->y >= 0)
336                                                 l = pw;
337                                 }
338                                 /* Scroll windows between l and w */
339                               loop1:
340                                 if (l->ny >= 0 && l->y >= 0)
341                                         nscrldn(t->t, l->y, l->ny + uns_min(l->h, l->nh), l->ny - l->y);
342                                 if (w != l) {
343                                         l = l->link.prev;
344                                         goto loop1;
345                                 }
346                                 w = pw->link.next;
347                         } else if (w->ny < w->y) {
348                                 W *l = pw = w;
349
350                                 while (pw->link.next != t->topwin && (pw->link.next->y < 0 || pw->link.next->ny < 0 || pw->link.next->ny < pw->link.next->y)) {
351                                         pw = pw->link.next;
352                                         if (pw->ny >= 0 && pw->y >= 0)
353                                                 l = pw;
354                                 }
355                                 /* Scroll windows between l and w */
356                               loop0:
357                                 if (w->ny >= 0 && w->y >= 0)
358                                         nscrlup(t->t, w->ny, w->y + uns_min(w->h, w->nh), w->y - w->ny);
359                                 if (w != l) {
360                                         w = w->link.next;
361                                         goto loop0;
362                                 }
363                                 w = pw->link.next;
364                         } else
365                                 w = w->link.next;
366                 else
367                         w = w->link.next;
368         } while (w != t->topwin);
369
370         /* Update current height and position values */
371         w = t->topwin;
372         do {
373                 if (w->ny >= 0) {
374                         if (w->object) {
375                                 if (w->watom->move)
376                                         w->watom->move(w->object, w->x, w->ny);
377                                 if (w->watom->resize)
378                                         w->watom->resize(w->object, w->w, w->nh);
379                         }
380                         if (w->y == -1) {
381                                 msetI(t->t->updtab + w->ny, 1, w->nh);
382                                 msetI(t->t->syntab + w->ny, -1, w->nh);
383                                 }
384                         w->y = w->ny;
385                 } else
386                         w->y = -1;
387                 w->h = w->nh;
388                 w->reqh = 0;
389                 w = w->link.next;
390         } while (w != t->topwin);
391 }
392
393 /* Goto next window */
394
395 int wnext(SCREEN *t)
396 {
397         if (t->curwin->link.next != t->curwin) {
398                 t->curwin = t->curwin->link.next;
399                 if (t->curwin->y == -1)
400                         wfit(t);
401                 return 0;
402         } else
403                 return -1;
404 }
405
406 /* Goto previous window */
407
408 int wprev(SCREEN *t)
409 {
410         if (t->curwin->link.prev != t->curwin) {
411                 t->curwin = t->curwin->link.prev;
412                 if (t->curwin->y == -1) {
413                         t->topwin = findtopw(t->curwin);
414                         wfit(t);
415                 }
416                 return 0;
417         } else
418                 return -1;
419 }
420
421 /* Grow window */
422
423 int wgrow(W *w)
424 {
425         W *nextw;
426
427         /* If we're the last window on the screen, shrink the previous window */
428         if ((w->link.next == w->t->topwin || w->link.next->y == -1) && w != w->t->topwin)
429                 return wshrink(w->link.prev->main);
430
431         /* Get to next variable size window */
432         for (nextw = w->link.next; nextw->fixed && nextw != w->t->topwin; nextw = nextw->link.next) ;
433
434         /* Is it below us, on screen and big enough to take space from? */
435         if (nextw == w->t->topwin || nextw->y == -1 || nextw->h <= FITHEIGHT)
436                 return -1;
437
438         /* Increase this window's height */
439         seth(w, w->h + 1);
440
441         /* Decrease next window's height */
442         seth(nextw, nextw->h - 1);
443
444         /* Do it */
445         wfit(w->t);
446
447         return 0;
448 }
449
450 /* Shrink window */
451
452 int wshrink(W *w)
453 {
454         W *nextw;
455
456         /* If we're the last window on the screen, grow the previous window */
457         if ((w->link.next == w->t->topwin || w->link.next->y == -1) && w != w->t->topwin)
458                 return wgrow(w->link.prev->main);
459
460         /* Is this window too small already? */
461         if (w->h <= FITHEIGHT)
462                 return -1;
463
464         /* Get to window below us */
465         for (nextw = w->link.next; nextw != w->t->topwin && nextw->fixed; nextw = nextw->link.next) ;
466         if (nextw == w->t->topwin)
467                 return -1;
468
469         /* Decrease the size of this window */
470         seth(w, w->h - 1);
471
472         /* Increase the size of next window */
473         seth(nextw, nextw->h + 1);
474
475         /* Do it */
476         wfit(w->t);
477
478         return 0;
479 }
480
481 /* Show all windows */
482
483 void wshowall(SCREEN *t)
484 {
485         int n = 0;
486         int set;
487         W *w;
488
489         /* Count no. of main windows */
490         w = t->topwin;
491         do {
492                 if (!w->win)
493                         ++n;
494                 w = w->link.next;
495         } while (w != t->topwin);
496
497         /* Compute size to set each window */
498         if ((set = (t->h - t->wind) / (n ? n : 1)) < FITHEIGHT)
499                 set = FITHEIGHT;
500
501         /* Set size of each variable size window */
502         w = t->topwin;
503         do {
504                 if (!w->win) {
505                         int h = getminh(w);
506
507                         if (h >= set)
508                                 seth(w, 2);
509                         else
510                                 seth(w, set - (h - 2));
511                         w->orgwin = NULL;
512                 }
513                 w = w->link.next;
514         } while (w != t->topwin);
515
516         /* Do it */
517         wfit(t);
518 }
519
520 static void wspread(SCREEN *t)
521 {
522         int n = 0;
523         W *w = t->topwin;
524
525         do {
526                 if (w->y >= 0 && !w->win)
527                         ++n;
528                 w = w->link.next;
529         } while (w != t->topwin);
530         if (!n) {
531                 wfit(t);
532                 return;
533         }
534         if ((t->h - t->wind) / n >= FITHEIGHT)
535                 n = (t->h - t->wind) / n;
536         else
537                 n = FITHEIGHT;
538         w = t->topwin;
539         do {
540                 if (!w->win) {
541                         int h = getminh(w);
542
543                         if (h >= n)
544                                 seth(w, 2);
545                         else
546                                 seth(w, n - (h - 2));
547                         w->orgwin = NULL;
548                 }
549                 w = w->link.next;
550         } while (w != t->topwin);
551         wfit(t);
552 }
553
554 /* Show just one family of windows */
555
556 void wshowone(W *w)
557 {
558         W *q = w->t->topwin;
559
560         do {
561                 if (!q->win) {
562                         seth(q, w->t->h - w->t->wind - (getminh(q) - 2));
563                         q->orgwin = NULL;
564                 }
565                 q = q->link.next;
566         } while (q != w->t->topwin);
567         wfit(w->t);
568 }
569
570 /* Create a window */
571
572 W *wcreate(SCREEN *t, WATOM *watom, W *where, W *target, W *original, int height, const unsigned char *huh, int *notify)
573 {
574         W *new;
575
576         if (height < 1)
577                 return NULL;
578
579         /* Create the window */
580         new = (W *) joe_malloc(sizeof(W));
581         new->notify = notify;
582         new->t = t;
583         new->w = t->w - 1;
584         seth(new, height);
585         new->h = new->reqh;
586         new->y = -1;
587         new->ny = 0;
588         new->nh = 0;
589         new->x = 0;
590         new->huh = huh;
591         new->orgwin = original;
592         new->watom = watom;
593         new->object = NULL;
594         new->msgb = NULL;
595         new->msgt = NULL;
596         /* Set window's target and family */
597 /* was: if (new->win = target) {        which may be mistyped == */
598         if ((new->win = target) != NULL) {      /* A subwindow */
599                 new->main = target->main;
600                 new->fixed = height;
601         } else {                /* A parent window */
602                 new->main = new;
603                 new->fixed = 0;
604         }
605
606         /* Get space for window */
607         if (original) {
608                 if (original->h - height <= 2) {
609                         /* Not enough space for window */
610                         joe_free(new);
611                         return NULL;
612                 } else
613                         seth(original, original->h - height);
614         }
615
616         /* Create new keyboard handler for window */
617         if (watom->context)
618                 new->kbd = mkkbd(kmap_getcontext(watom->context, 1));
619         else
620                 new->kbd = NULL;
621
622         /* Put window on the screen */
623         if (where)
624                 enquef(W, link, where, new);
625         else {
626                 if (t->topwin)
627                         enqueb(W, link, t->topwin, new);
628                 else {
629                         izque(W, link, new);
630                         t->curwin = t->topwin = new;
631                 }
632         }
633
634         return new;
635 }
636
637 /* Abort group of windows */
638
639 static int doabort(W *w, int *ret)
640 {
641         int amnt = geth(w);
642         W *z;
643
644         w->y = -2;
645         if (w->t->topwin == w)
646                 w->t->topwin = w->link.next;
647       loop:
648         z = w->t->topwin;
649         do {
650                 if (z->orgwin == w)
651                         z->orgwin = NULL;
652                 if ((z->win == w || z->main == w) && z->y != -2) {
653                         amnt += doabort(z, ret);
654                         goto loop;
655                 }
656         } while (z = z->link.next, z != w->t->topwin);
657         if (w->orgwin)
658                 seth(w->orgwin, geth(w->orgwin) + geth(w));
659         if (w->t->curwin == w) {
660                 if (w->t->curwin->win)
661                         w->t->curwin = w->t->curwin->win;
662                 else if (w->orgwin)
663                         w->t->curwin = w->orgwin;
664                 else
665                         w->t->curwin = w->link.next;
666         }
667         if (qempty(W, link, w)) {
668                 leave = 1;
669                 amnt = 0;
670         }
671         deque(W, link, w);
672         if (w->watom->abort && w->object) {
673                 *ret = w->watom->abort(w->object);
674                 if (w->notify)
675                         *w->notify = -1;
676         } else {
677                 *ret = -1;
678                 if (w->notify)
679                         *w->notify = 1;
680         }
681         rmkbd(w->kbd);
682         joe_free(w);
683         windie(w);
684         return amnt;
685 }
686
687 /* Abort a window and its children */
688
689 int wabort(W *w)
690 {
691         SCREEN *t = w->t;
692         int ret;
693
694         if (w != w->main) {
695                 doabort(w, &ret);
696                 if (!leave)
697                         wfit(t);
698         } else {
699                 doabort(w, &ret);
700                 if (!leave) {
701                         if (lastw(t)->link.next != t->topwin)
702                                 wfit(t);
703                         else
704                                 wspread(t);
705                 }
706         }
707         return ret;
708 }
709
710 /* Display a message and skip the next key */
711
712 static void mdisp(SCRN *t, int y, const unsigned char *s)
713 {
714         int ofst;
715         int len;
716
717         len = fmtlen(s);
718         if (len <= (t->co - 1))
719                 ofst = 0;
720         else
721                 ofst = len - (t->co - 1);
722         genfmt(t, 0, y, ofst, s, 1);
723         t->updtab[y] = 1;
724 }
725
726 void msgout(W *w)
727 {
728         SCRN *t = w->t->t;
729
730         if (w->msgb) {
731                 mdisp(t, w->y + w->h - 1, w->msgb);
732                 w->msgb = NULL;
733         }
734         if (w->msgt) {
735                 mdisp(t, w->y + ((w->h > 1 && (w->y || !staen)) ? 1 : 0), w->msgt);
736                 w->msgt = NULL;
737         }
738 }
739
740 /* Set temporary message */
741
742 unsigned char msgbuf[JOE_MSGBUFSIZE];
743
744 /* display message on bottom line of window */
745 void msgnw(W *w, const unsigned char *s)
746 {
747         w->msgb = s;
748 }
749
750 void msgnwt(W *w, const unsigned char *s)
751 {
752         w->msgt = s;
753 }
754
755 int urtn(BASE *b, int k)
756 {
757         if (b->parent->watom->rtn)
758                 return b->parent->watom->rtn(b, k);
759         else
760                 return -1;
761 }
762
763 int utype(BASE *b, int k)
764 {
765         if (b->parent->watom->type)
766                 return b->parent->watom->type(b, k);
767         else
768                 return -1;
769 }
770
771 /* Window user commands */
772
773 int uprevw(BASE *bw)
774 {
775         return wprev(bw->parent->t);
776 }
777
778 int unextw(BASE *bw)
779 {
780         return wnext(bw->parent->t);
781 }
782
783 int ugroww(BASE *bw)
784 {
785         return wgrow(bw->parent);
786 }
787
788 int ushrnk(BASE *bw)
789 {
790         return wshrink(bw->parent);
791 }
792
793 int uexpld(BASE *bw)
794 {
795         if (bw->parent->t->h - bw->parent->t->wind == getgrouph(bw->parent))
796                 wshowall(bw->parent->t);
797         else
798                 wshowone(bw->parent);
799         return 0;
800 }
801
802 int uretyp(BASE *bw)
803 {
804         nredraw(bw->parent->t->t);
805         return 0;
806 }