1 #include <lib9.h>
2 #include <kernel.h>
3 #include "interp.h"
4 #include "isa.h"
5 #include "runt.h"
6 #include "prefabmod.h"
7 #include "draw.h"
8 #include "drawif.h"
9 #include "prefab.h"
10 #include "raise.h"
11
12 uchar elementmap[] = Prefab_Element_map;
13 uchar compoundmap[] = Prefab_Compound_map;
14 uchar layoutmap[] = Prefab_Layout_map;
15
16 void freeprefabcompound(Heap*, int);
17
18 Type* TCompound;
19 Type* TElement;
20 Type* TLayout;
21
22 /* Infrared remote buttons known to Compound_select */
23 enum
24 {
25 IRFF = 14,
26 IRRew = 15,
27 IRUp = 16,
28 IRDn = 17,
29 IRSelect = 18,
30 IREnter = 20,
31 };
32
33 void
prefabmodinit(void)34 prefabmodinit(void)
35 {
36 TElement = dtype(freeheap, sizeof(PElement), elementmap, sizeof(elementmap));
37 TLayout = dtype(freeheap, Prefab_Layout_size, layoutmap, sizeof(layoutmap));
38 TCompound = dtype(freeprefabcompound, sizeof(PCompound), compoundmap, sizeof(compoundmap));
39 builtinmod("$Prefab", Prefabmodtab, Prefabmodlen);
40 }
41
42 PElement*
checkelement(Prefab_Element * de)43 checkelement(Prefab_Element *de)
44 {
45 PElement *pe;
46
47 pe = lookupelement(de);
48 if(pe == H)
49 error(exType);
50 return pe;
51 }
52
53 PCompound*
checkcompound(Prefab_Compound * de)54 checkcompound(Prefab_Compound *de)
55 {
56 PCompound *pe;
57
58 pe = lookupcompound(de);
59 if(pe == H)
60 error(exType);
61 return pe;
62 }
63
64 PElement*
lookupelement(Prefab_Element * de)65 lookupelement(Prefab_Element *de)
66 {
67 PElement *pe;
68 if(de == H)
69 return H;
70 if(D2H(de)->t != TElement)
71 return H;
72 pe = (PElement*)de;
73 if(de->kind!=pe->pkind || de->kids!=pe->first)
74 return H;
75 return pe;
76 }
77
78 PCompound*
lookupcompound(Prefab_Compound * dc)79 lookupcompound(Prefab_Compound *dc)
80 {
81 if(dc == H)
82 return H;
83 if(D2H(dc)->t != TCompound)
84 return H;
85 return (PCompound*)dc;
86 }
87
88 void
freeprefabcompound(Heap * h,int swept)89 freeprefabcompound(Heap *h, int swept)
90 {
91 Image *i;
92 Prefab_Compound *d;
93 PCompound *pc;
94
95 d = H2D(Prefab_Compound*, h);
96 pc = lookupcompound(d);
97 /* disconnect compound from image refresh daemon */
98 i = lookupimage(pc->c.image);
99 if(i != nil)
100 delrefresh(i);
101 if(!swept && TCompound->np)
102 freeptrs(d, TCompound);
103 /* header will be freed by caller */
104 }
105
106 static
107 PElement*
findtag(PElement * pelem,char * tag)108 findtag(PElement *pelem, char *tag)
109 {
110 PElement *pe, *t;
111 List *l;
112
113 if(pelem==H || tag[0]==0)
114 return pelem;
115 for(l=pelem->first; l!=H; l=l->tail){
116 pe = *(PElement**)l->data;
117 if(strcmp(tag, string2c(pe->e.tag)) == 0)
118 return pe;
119 else if(pe->pkind==EHorizontal || pe->pkind==EVertical){
120 t = findtag(pe, tag);
121 if(t != H)
122 return t;
123 }
124 }
125 return H;
126 }
127
128 int
badenviron(Prefab_Environ * env,int err)129 badenviron(Prefab_Environ *env, int err)
130 {
131 Prefab_Style *s;
132
133 if(env == H)
134 goto bad;
135 s = env->style;
136 if(s == H)
137 goto bad;
138 if(s->titlefont==H || s->textfont==H)
139 goto bad;
140 if(s->elemcolor==H || s->edgecolor==H)
141 goto bad;
142 if(s->titlecolor==H || s->textcolor==H || s->highlightcolor==H)
143 goto bad;
144 return 0;
145 bad:
146 if(err)
147 error(exType);
148 return 1;
149 }
150
151 void
Element_iconseparator(void * fp,int kind)152 Element_iconseparator(void *fp, int kind)
153 {
154 F_Element_icon *f;
155 PElement *e;
156 Image *icon;
157 int locked;
158
159 f = fp;
160 badenviron(f->env, 1);
161 checkimage(f->mask);
162 icon = checkimage(f->icon);
163 locked = lockdisplay(icon->display);
164 destroy(*f->ret);
165 *f->ret = H;
166 if(kind == ESeparator)
167 e = separatorelement(f->env, f->r, f->icon, f->mask);
168 else
169 e = iconelement(f->env, f->r, f->icon, f->mask);
170 *f->ret = (Prefab_Element*)e;
171 if(locked)
172 unlockdisplay(icon->display);
173 }
174
175 void
Element_icon(void * fp)176 Element_icon(void *fp)
177 {
178 Element_iconseparator(fp, EIcon);
179 }
180
181 void
Element_separator(void * fp)182 Element_separator(void *fp)
183 {
184 Element_iconseparator(fp, ESeparator);
185 }
186
187 void
Element_text(void * fp)188 Element_text(void *fp)
189 {
190 F_Element_text *f;
191 PElement *pelem;
192 Display *disp;
193 int locked;
194
195 f = fp;
196 badenviron(f->env, 1);
197 if(f->kind!=EText && f->kind!=ETitle)
198 return;
199
200 disp = checkscreen(f->env->screen)->display;
201 locked = lockdisplay(disp);
202 destroy(*f->ret);
203 *f->ret = H;
204 pelem = textelement(f->env, f->text, f->r, f->kind);
205 *f->ret = (Prefab_Element*)pelem;
206 if(locked)
207 unlockdisplay(disp);
208 }
209
210 void
Element_layout(void * fp)211 Element_layout(void *fp)
212 {
213 F_Element_layout *f;
214 PElement *pelem;
215 Display *disp;
216 int locked;
217
218 f = fp;
219 badenviron(f->env, 1);
220 if(f->kind!=EText && f->kind!=ETitle)
221 return;
222
223 disp = checkscreen(f->env->screen)->display;
224 locked = lockdisplay(disp);
225 destroy(*f->ret);
226 *f->ret = H;
227 pelem = layoutelement(f->env, f->lay, f->r, f->kind);
228 *f->ret = (Prefab_Element*)pelem;
229 if(locked)
230 unlockdisplay(disp);
231 }
232
233 void
Element_elist(void * fp)234 Element_elist(void *fp)
235 {
236 F_Element_elist *f;
237 PElement *pelist;
238 Display *disp;
239 int locked;
240
241 f = fp;
242 if(f->elem != H)
243 checkelement(f->elem);
244 badenviron(f->env, 1);
245 if(f->kind!=EHorizontal && f->kind!=EVertical)
246 return;
247
248 disp = checkscreen(f->env->screen)->display;
249 locked = lockdisplay(disp);
250 destroy(*f->ret);
251 *f->ret = H;
252 pelist = elistelement(f->env, f->elem, f->kind);
253 *f->ret = (Prefab_Element*)pelist;
254 if(locked)
255 unlockdisplay(disp);
256 }
257
258 void
Element_append(void * fp)259 Element_append(void *fp)
260 {
261 F_Element_append *f;
262
263 f = fp;
264 *f->ret = 0;
265 if(f->elist==H || f->elem==H)
266 return;
267
268 badenviron(f->elist->environ, 1);
269 checkelement(f->elist);
270 checkelement(f->elem);
271
272 if(f->elist->kind!=EHorizontal && f->elist->kind!=EVertical)
273 return;
274
275 if(appendelist(f->elist, f->elem) != H)
276 *f->ret = 1;
277 }
278
279 void
Element_adjust(void * fp)280 Element_adjust(void *fp)
281 {
282 F_Element_adjust *f;
283 Display *disp;
284 int locked;
285
286 f = fp;
287 checkelement(f->elem);
288 badenviron(f->elem->environ, 1);
289 disp = checkscreen(f->elem->environ->screen)->display;
290 locked = lockdisplay(disp);
291 adjustelement(f->elem, f->equal, f->dir);
292 if(locked)
293 unlockdisplay(disp);
294 }
295
296 void
Element_show(void * fp)297 Element_show(void *fp)
298 {
299 F_Element_show *f;
300 Display *disp;
301 int locked;
302
303 f = fp;
304 checkelement(f->elem);
305 checkelement(f->elist);
306 badenviron(f->elem->environ, 1);
307 disp = checkscreen(f->elem->environ->screen)->display;
308 locked = lockdisplay(disp);
309 *f->ret = showelement(f->elist, f->elem);
310 if(locked)
311 unlockdisplay(disp);
312 }
313
314 void
Element_clip(void * fp)315 Element_clip(void *fp)
316 {
317 F_Element_clip *f;
318 Rectangle r;
319 Display *disp;
320 int locked;
321
322 f = fp;
323 checkelement(f->elem);
324 badenviron(f->elem->environ, 1);
325 R2R(r, f->r);
326 disp = checkscreen(f->elem->environ->screen)->display;
327 locked = lockdisplay(disp);
328 clipelement(f->elem, r);
329 if(locked)
330 unlockdisplay(disp);
331 }
332
333 void
Element_translatescroll(void * fp,int trans)334 Element_translatescroll(void *fp, int trans)
335 {
336 F_Element_scroll *f;
337 Point d;
338 Display *disp;
339 int locked, moved;
340
341 f = fp;
342 checkelement(f->elem);
343 badenviron(f->elem->environ, 1);
344 P2P(d, f->d);
345 disp = checkscreen(f->elem->environ->screen)->display;
346 locked = lockdisplay(disp);
347 if(trans)
348 translateelement(f->elem, d);
349 else{
350 moved = 0;
351 scrollelement(f->elem, d, &moved);
352 }
353 if(locked)
354 unlockdisplay(disp);
355 }
356
357 void
Element_scroll(void * fp)358 Element_scroll(void *fp)
359 {
360 Element_translatescroll(fp, 0);
361 }
362
363 void
Element_translate(void * fp)364 Element_translate(void *fp)
365 {
366 Element_translatescroll(fp, 1);
367 }
368
369 void
Compound_iconbox(void * fp)370 Compound_iconbox(void *fp)
371 {
372 F_Compound_iconbox *f;
373 Image *icon;
374 int locked;
375 PCompound *pc;
376
377 f = fp;
378 badenviron(f->env, 1);
379 checkimage(f->mask);
380 icon = checkimage(f->icon);
381 locked = lockdisplay(icon->display);
382 destroy(*f->ret);
383 *f->ret = H;
384 pc = iconbox(f->env, f->p, f->title, f->icon, f->mask);
385 *f->ret = &pc->c;
386 if(locked)
387 unlockdisplay(icon->display);
388 }
389
390 void
Compound_textbox(void * fp)391 Compound_textbox(void *fp)
392 {
393 F_Compound_textbox *f;
394 Display *disp;
395 int locked;
396 PCompound *pc;
397
398 f = fp;
399 badenviron(f->env, 1);
400 disp = checkscreen(f->env->screen)->display;
401 locked = lockdisplay(disp);
402 destroy(*f->ret);
403 *f->ret = H;
404 pc = textbox(f->env, f->r, f->title, f->text);
405 *f->ret = &pc->c;
406 if(locked)
407 unlockdisplay(disp);
408 }
409
410 void
Compound_layoutbox(void * fp)411 Compound_layoutbox(void *fp)
412 {
413 F_Compound_layoutbox *f;
414 Display *disp;
415 int locked;
416 PCompound *pc;
417
418 f = fp;
419 badenviron(f->env, 1);
420 disp = checkscreen(f->env->screen)->display;
421 locked = lockdisplay(disp);
422 destroy(*f->ret);
423 *f->ret = H;
424 pc = layoutbox(f->env, f->r, f->title, f->lay);
425 *f->ret = &pc->c;
426 if(locked)
427 unlockdisplay(disp);
428 }
429
430 void
Compound_box(void * fp)431 Compound_box(void *fp)
432 {
433 F_Compound_box *f;
434 Display *disp;
435 int locked;
436 PCompound *pc;
437
438 f = fp;
439 badenviron(f->env, 1);
440 if(f->title != H)
441 checkelement(f->title);
442 checkelement(f->elist);
443 disp = checkscreen(f->env->screen)->display;
444 locked = lockdisplay(disp);
445 destroy(*f->ret);
446 *f->ret = H;
447 pc = box(f->env, f->p, f->title, f->elist);
448 *f->ret = &pc->c;
449 if(locked)
450 unlockdisplay(disp);
451 }
452
453 void
Compound_draw(void * fp)454 Compound_draw(void *fp)
455 {
456 F_Compound_draw *f;
457 PCompound *pc;
458 int locked;
459
460 f = fp;
461 if(f->comp == H)
462 return;
463 pc = checkcompound(f->comp);
464 badenviron(pc->c.environ, 1);
465 locked = lockdisplay(pc->display);
466 drawcompound(&pc->c);
467 flushimage(pc->display, 1);
468 if(locked)
469 unlockdisplay(pc->display);
470 }
471
472 void
Compound_redraw(void * fp)473 Compound_redraw(void *fp)
474 {
475 F_Compound_redraw *f;
476 PCompound *pc;
477 Image *i;
478 int locked;
479
480 f = fp;
481 if(f->comp == H)
482 return;
483 pc = checkcompound(f->comp);
484 badenviron(pc->c.environ, 1);
485 i = checkimage(pc->c.image);
486 locked = lockdisplay(pc->display);
487 redrawcompound(i, IRECT(f->r), &pc->c);
488 flushimage(pc->display, 1);
489 if(locked)
490 unlockdisplay(pc->display);
491 }
492
493 static
494 PElement*
pelement(Prefab_Compound * comp,Prefab_Element * elem)495 pelement(Prefab_Compound *comp, Prefab_Element *elem)
496 {
497 PElement *pe;
498
499 if(comp == H)
500 return H;
501 checkcompound(comp);
502 badenviron(comp->environ, 1);
503 pe = lookupelement(elem);
504 return pe;
505 }
506
507 void
Compound_highlight(void * fp)508 Compound_highlight(void *fp)
509 {
510 F_Compound_highlight *f;
511 PCompound *pc;
512 PElement *pe;
513 Image *i;
514 int locked;
515
516 f = fp;
517 pe = pelement(f->comp, f->elem);
518 if(pe == H)
519 return;
520 pc = (PCompound*)f->comp;
521 i = checkimage(pc->c.image);
522 locked = lockdisplay(pc->display);
523 highlightelement(&pe->e, i, &pc->c, f->on);
524 flushimage(pc->display, 1);
525 if(locked)
526 unlockdisplay(pc->display);
527 }
528
529 void
Compound_scroll(void * fp)530 Compound_scroll(void *fp)
531 {
532 F_Compound_scroll *f;
533 PCompound *pc;
534 PElement *pe;
535 int locked;
536 Image *i;
537 int moved;
538
539 f = fp;
540 pe = pelement(f->comp, f->elem);
541 if(pe == H)
542 return;
543 pc = (PCompound*)f->comp;
544 i = checkimage(pc->c.image);
545 locked = lockdisplay(pc->display);
546 moved = 0;
547 scrollelement(&pe->e, IPOINT(f->d), &moved);
548 if(moved){
549 drawelement(&pe->e, i, IRECT(pe->e.r), 0, 0);
550 flushimage(pc->display, 1);
551 }
552 if(locked)
553 unlockdisplay(pc->display);
554 }
555
556 void
Compound_show(void * fp)557 Compound_show(void *fp)
558 {
559 F_Compound_show *f;
560 PCompound *pc;
561 PElement *pe;
562 int locked;
563
564 f = fp;
565 pe = pelement(f->comp, f->elem);
566 if(pe == H)
567 return;
568 pc = (PCompound*)f->comp;
569 locked = lockdisplay(pc->display);
570 *f->ret = showelement(pc->c.contents, &pe->e);
571 flushimage(pc->display, 1);
572 if(locked)
573 unlockdisplay(pc->display);
574 }
575
576 static
577 PElement*
element(PElement * plist,int index,int * ip)578 element(PElement *plist, int index, int *ip)
579 {
580 int i;
581 PElement *pe;
582 List *l;
583
584 i = 0;
585 pe = H;
586 for(l=plist->first; l!=H; l=l->tail){
587 pe = *(PElement**)l->data;
588 if(pe->pkind == ESeparator)
589 continue;
590 if(i == index)
591 break;
592 i++;
593 }
594 if(ip)
595 *ip = i;
596 if(l == H)
597 return H;
598 return pe;
599 }
600
601 static
602 int
wrapelement(PElement * plist,int index,int ntag)603 wrapelement(PElement *plist, int index, int ntag)
604 {
605 int i, wrap;
606
607 if(ntag > 0){
608 if(index < 0)
609 return ntag-1;
610 if(index >= ntag)
611 return 0;
612 return index;
613 }
614 wrap = 1;
615 if(index < 0){
616 index = 1000000; /* will seek to end */
617 wrap = 0;
618 }
619 if(element(plist, index, &i)==H && index!=0){
620 if(wrap) /* went off end; wrap to beginning */
621 return wrapelement(plist, 0, 0);
622 if(i > 0)
623 --i;
624 }
625 return i;
626 }
627
628 void
dohighlight(PCompound * pc,PElement * list,PElement * pe,int on)629 dohighlight(PCompound *pc, PElement *list, PElement *pe, int on)
630 {
631 Image *i;
632
633 /* see if we need to scroll */
634 i = lookupimage(pc->c.image);
635 if(i == nil)
636 return;
637 if(on && showelement(&list->e, &pe->e))
638 redrawcompound(i, IRECT(pc->c.contents->r), &pc->c);
639 highlightelement(&pe->e, i, &pc->c, on);
640 }
641
642 void
highlight(PCompound * pc,PElement * list,int index,int on)643 highlight(PCompound *pc, PElement *list, int index, int on)
644 {
645 dohighlight(pc, list, element(list, index, nil), on);
646 }
647
648 static
649 PElement**
tags(PElement * pelem,int * ntag)650 tags(PElement *pelem, int *ntag)
651 {
652 int n, nalloc, nn;
653 List *l;
654 PElement *pe, **tagged, **ntagged;
655
656 n = 0;
657 nalloc = 0;
658 tagged = nil;
659 *ntag = 0;
660 for(l=pelem->first; l!=H; l=l->tail){
661 pe = *(PElement**)l->data;
662 if(pe->e.tag != H){
663 if(nalloc == n){
664 nalloc += 10;
665 tagged = realloc(tagged, nalloc*sizeof(PElement*));
666 if(tagged == nil)
667 return nil;
668 }
669 tagged[n++] = pe;
670 }else if(pe->pkind==EHorizontal || pe->pkind==EVertical){
671 ntagged = tags(pe, &nn);
672 if(nn > 0){
673 if(nalloc < n+nn){
674 nalloc = n+nn+10;
675 tagged = realloc(tagged, nalloc*sizeof(PElement*));
676 if(tagged == nil){
677 free(ntagged);
678 return nil;
679 }
680 }
681 memmove(tagged+n, ntagged, nn*sizeof(PElement*));
682 free(ntagged);
683 n += nn;
684 }
685 }
686 }
687 *ntag = n;
688 return tagged;
689 }
690
691 void
doselect(void * fp,int dotags)692 doselect(void *fp, int dotags)
693 {
694 F_Compound_select *f;
695 PCompound *pc;
696 PElement *pe;
697 WORD *val;
698 List *l;
699 Prefab_Element *t;
700 int i, lasti, ntag;
701 PElement **tagged;
702 int locked;
703
704 f = fp;
705 pc = checkcompound(f->comp);
706 pe = lookupelement(f->elem);
707 if(pe->pkind!=EHorizontal && pe->pkind!=EVertical || pe->nkids == 0){
708 Bad:
709 destroy(f->ret->t2);
710 f->ret->t0 = 9999;
711 f->ret->t1 = 0;
712 f->ret->t2 = H;
713 return;
714 }
715 ntag = 0;
716 tagged = 0;
717 /* check at least one selectable item */
718 if(dotags){
719 tagged = tags(pe, &ntag);
720 if(ntag > 0)
721 goto OK;
722 }else
723 for(l=pe->first; l!=H; l=l->tail){
724 t = *(Prefab_Element**)l->data;
725 if(t->kind != ESeparator)
726 goto OK;
727 }
728 goto Bad;
729
730 OK:
731 i = f->i;
732 i = wrapelement(pe, i, ntag);
733 lasti = i;
734 locked = lockdisplay(pc->display);
735 if(dotags)
736 dohighlight(pc, pe, tagged[i], 1);
737 else
738 highlight(pc, pe, i, 1);
739 /* val must be in shared memory, but stacks not shared */
740 val = malloc(sizeof(WORD));
741 if(val == nil)
742 goto Bad;
743 for(;;){
744 if(lasti != i){
745 if(dotags){
746 dohighlight(pc, pe, tagged[lasti], 0);
747 dohighlight(pc, pe, tagged[i], 1);
748 }else{
749 highlight(pc, pe, lasti, 0);
750 highlight(pc, pe, i, 1);
751 }
752 lasti = i;
753 }
754 flushimage(pc->display, 1);
755 if(locked)
756 unlockdisplay(pc->display);
757 crecv(f->c, val);
758 locked = lockdisplay(pc->display);
759 switch(*val){
760 case IRUp:
761 if(pe->pkind != EVertical)
762 goto Default;
763 goto Up;
764 case IRRew:
765 if(pe->pkind != EHorizontal)
766 goto Default;
767 Up:
768 i = wrapelement(pe, i-1, ntag);
769 break;
770 case IRSelect:
771 if(dotags)
772 dohighlight(pc, pe, tagged[i], 0);
773 else
774 highlight(pc, pe, i, 0);
775 f->ret->t0 = *val;
776 f->ret->t1 = i;
777 Return:
778 flushimage(pc->display, 1);
779 if(dotags)
780 pe = tagged[i];
781 else
782 pe = element(pe, i, nil);
783 destroy(f->ret->t2);
784 D2H(pe)->ref++;
785 f->ret->t2 = &pe->e;
786 if(locked)
787 unlockdisplay(pc->display);
788 free(val);
789 free(tagged);
790 return;
791 case IRDn:
792 if(pe->pkind != EVertical)
793 goto Default;
794 goto Down;
795 case IRFF:
796 if(pe->pkind != EHorizontal)
797 goto Default;
798 Down:
799 i = wrapelement(pe, i+1, ntag);
800 break;
801 default:
802 Default:
803 if(dotags)
804 dohighlight(pc, pe, tagged[lasti], 0);
805 else
806 highlight(pc, pe, lasti, 0);
807 f->ret->t0 = *val;
808 f->ret->t1 = i;
809 goto Return;
810 }
811 }
812 }
813
814 void
Compound_tagselect(void * fp)815 Compound_tagselect(void *fp)
816 {
817 doselect(fp, 1);
818 }
819
820 void
Compound_select(void * fp)821 Compound_select(void *fp)
822 {
823 doselect(fp, 0);
824 }
825