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