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