]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/abaco/page.c
ndb/dns: remove single-ip-address assuptions
[plan9front.git] / sys / src / cmd / abaco / page.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <memdraw.h>
5 #include <thread.h>
6 #include <cursor.h>
7 #include <mouse.h>
8 #include <keyboard.h>
9 #include <frame.h>
10 #include <plumb.h>
11 #include <html.h>
12 #include "dat.h"
13 #include "fns.h"
14
15 static void pageload1(Page *, Url *, int);
16
17 static
18 void
19 addchild(Page *p, Page *c)
20 {
21         Page *t;
22
23         c->parent = p;
24         c->w = p->w;
25         c->b = p->b;
26         c->col = p->col;
27         c->row = p->row;
28         if(p->child == nil)
29                 p->child = c;
30         else{
31                 for(t=p->child; t->next!=nil; t=t->next)
32                         ;
33                 t->next = c;
34         }
35 }
36
37 static
38 void
39 loadchilds(Page *p, Kidinfo *k)
40 {
41         Runestr rs;
42         Kidinfo *t;
43         Page *c;
44
45         addrefresh(p, "loading frames...");
46         p->kidinfo = k;
47         for(t=k->kidinfos; t!=nil; t=t->next){
48                 c = emalloc(sizeof(Page));
49                 addchild(p, c);
50                 if(t->isframeset){
51                         c->url = urldup(p->url);
52                         loadchilds(c, t);
53                 }else{
54                         c->kidinfo = t;
55                         /* this check shouldn't be necessary, but... */
56                         if(t->src){
57                                 rs.r = urlcombine(p->url->act.r, t->src);
58                                 rs.nr = runestrlen(rs.r);
59                                 pageload1(c, urlalloc(&rs, nil, HGet), FALSE);
60                                 closerunestr(&rs);
61                         }
62                 }
63         }
64 }
65
66 static struct {
67         char *mime;
68         char *filter;
69 }filtertab[] = {
70         "image/gif",    "gif -t9",
71         "image/jpeg",   "jpg -t9",
72         "image/jpg",    "jpg -t9",
73         "image/pjpeg",  "jpg -t9",
74         "image/png",    "png -t9",
75         "image/ppm",    "ppm -t9",
76         "image/x-icon", "ico -c",
77         nil,    nil,
78 };
79
80 char *
81 getfilter(Rune *r, int x, int y)
82 {
83         char buf[128];
84         int i;
85
86         snprint(buf, sizeof(buf), "%S", r);
87         for(i=0; filtertab[i].mime!=nil; i++)
88                 if(cistrncmp(buf, filtertab[i].mime, strlen(filtertab[i].mime)) == 0)
89                         break;
90
91         if(filtertab[i].filter == nil)
92                 return nil;
93
94         if(x==0 && y==0)
95                 return smprint("%s", filtertab[i].filter);
96         if(x!=0 && y!=0)
97                 return smprint("%s | resize -x %d -y %d", filtertab[i].filter, x, y);
98         if(x != 0)
99                 return smprint("%s | resize -x %d", filtertab[i].filter, x);
100         /* y != 0 */
101         return smprint("%s | resize -y %d", filtertab[i].filter, y);
102 }
103
104 static Cimage *cimages = nil;
105 static QLock cimagelock;
106
107 static
108 void
109 freecimage(Cimage *ci)
110 {
111         Cimage *ci1;
112
113         qlock(&cimagelock);
114         if(decref(ci) == 0){
115                 if(ci->i)
116                         freeimage(ci->i);
117                 else if(ci->mi)
118                         freememimage(ci->mi);
119                 urlfree(ci->url);
120                 ci1 = cimages;
121                 if(ci1 == ci)
122                         cimages = ci->next;
123                 else{
124                         while(ci1){
125                                 if(ci1->next == ci){
126                                         ci1->next = ci->next;
127                                         break;
128                                 }
129                                 ci1 = ci1->next;
130                         }
131                 }
132                 free(ci);
133         }
134         qunlock(&cimagelock);
135 }
136
137 static
138 void
139 closeimages(Page *p)
140 {
141         int i;
142
143         for(i=0; i<p->ncimage; i++)
144                 freecimage(p->cimage[i]);
145         free(p->cimage);
146         p->cimage =nil;
147         p->ncimage = 0;
148 }
149
150 static
151 Cimage *
152 loadimg(Rune *src, int x , int y)
153 {
154         Cimage *ci;
155         Runestr rs;
156         char *filter;
157         int fd;
158
159         ci = emalloc(sizeof(Cimage));
160         rs. r = src;
161         rs.nr = runestrlen(rs.r);
162         ci->url = urlalloc(&rs, nil, HGet);
163         fd = urlopen(ci->url);
164         if(fd < 0){
165     Err1:
166                 return ci;
167         }
168         filter = getfilter(ci->url->ctype.r, x, y);
169         if(filter == nil){
170                 werrstr("%S unsupported: %S", ci->url->ctype.r, ci->url->act.r);
171     Err2:
172                 close(fd);
173                 goto Err1;
174         }
175         fd = pipeline(fd, "%s", filter);
176         free(filter);
177         if(fd < 0)
178                 goto Err2;
179         ci->mi = readmemimage(fd);
180         close(fd);
181         if(ci->mi == nil){
182                 werrstr("can't read image");
183                 goto Err2;
184         }
185         return ci;
186 }
187
188 static
189 Cimage *
190 findimg(Rune *s)
191 {
192         Cimage *ci;
193
194         qlock(&cimagelock);
195         for(ci=cimages; ci!=nil; ci=ci->next)
196                 if(runestrcmp(ci->url->src.r, s) == 0)
197                         break;
198
199         qunlock(&cimagelock);
200         return ci;
201 }
202
203 void
204 loadimages(Page *p)
205 {
206         Cimage *ci;
207         Iimage *i;
208         Rune *src;
209
210         addrefresh(p, "loading images...");
211         reverseimages(&p->doc->images);
212         for(i=p->doc->images; i!=nil; i=i->nextimage){
213                 if(p->aborting)
214                         break;
215                 src = urlcombine(getbase(p), i->imsrc);
216                 ci = findimg(src);
217                 if(ci == nil){
218                         ci = loadimg(src, i->imwidth, i->imheight);
219                         qlock(&cimagelock);
220                         ci->next = cimages;
221                         cimages = ci;
222                         qunlock(&cimagelock);
223                 }
224                 free(src);
225                 incref(ci);
226                 i->aux = ci;
227                 p->cimage = erealloc(p->cimage, ++p->ncimage*sizeof(Cimage *));
228                 p->cimage[p->ncimage-1] = ci;
229                 p->changed = TRUE;
230                 addrefresh(p, "");
231         }
232 }
233
234 static char *mimetab[] = {
235         "text/html",
236         "application/xhtml",
237         nil,
238 };
239
240 static
241 void
242 pageloadproc(void *v)
243 {
244         Page *p;
245         char buf[BUFSIZE], cs[32], *s;
246         long n, l;
247         int fd, i, ctype;
248
249         threadsetname("pageloadproc");
250         rfork(RFFDG);
251
252         p = v;
253         addrefresh(p, "opening: %S...", p->url->src.r);
254         fd = urlopen(p->url);
255         if(fd < 0){
256                 addrefresh(p, "%S: %r", p->url->src.r);
257     Err:
258                 p->loading = FALSE;
259                 return;
260         }
261         if(runestrlen(p->url->ctype.r) == 0) /* assume .html when headers don't say anyting */
262                 goto Html;
263
264         snprint(buf, sizeof(buf), "%S", p->url->ctype.r);
265         for(i=0; mimetab[i]!=nil; i++)
266                 if(cistrncmp(buf, mimetab[i], strlen(mimetab[i])) == 0)
267                         break;
268
269         if(mimetab[i]){
270     Html:
271                 ctype = TextHtml;
272         }else if(cistrncmp(buf, "text/", 5) == 0)
273                 ctype = TextPlain;
274         else{
275                 close(fd);
276                 addrefresh(p, "%S: unsupported mime type: '%S'", p->url->act.r, p->url->ctype.r);
277                 goto Err;
278         }
279         addrefresh(p, "loading: %S...", p->url->src.r);
280
281         s = nil;
282         if(p->url->ctype.nr > 0){
283                 snprint(buf, sizeof(buf), "%.*S", p->url->ctype.nr, p->url->ctype.r);
284                 if(findctype(cs, sizeof(cs), "charset", buf) == 0)
285                         s = cs;
286         }
287         if((fd = pipeline(fd, s != nil ? "uhtml -c %q" : "uhtml", s)) < 0)
288                 goto Err;
289
290         s = nil;
291         l = 0;
292         while((n=read(fd, buf, sizeof(buf))) > 0){
293                 if(p->aborting){
294                         if(s){
295                                 free(s);
296                                 s = nil;
297                         }
298                         break;
299                 }
300                 s = erealloc(s, l+n+1);
301                 memmove(s+l, buf, n);
302                 l += n;
303                 s[l] = '\0';
304         }
305         close(fd);
306         n = l;
307         if(s){
308                 p->items = parsehtml((uchar *)s, n, p->url->act.r, ctype, UTF_8, &p->doc);
309                 free(s);
310                 fixtext(p);
311                 if(ctype==TextHtml && p->aborting==FALSE){
312                         p->changed = TRUE;
313                         addrefresh(p, "");
314                         if(p->doc->doctitle){
315                                 p->title.r = erunestrdup(p->doc->doctitle);
316                                 p->title.nr = runestrlen(p->title.r);
317                         }
318                         p->loading = XXX;
319                         if(p->doc->kidinfo)
320                                 loadchilds(p, p->doc->kidinfo);
321                         else if(p->doc->images)
322                                 loadimages(p);
323                 }
324         }
325         p->changed = TRUE;
326         p->loading = FALSE;
327         addrefresh(p, "");
328 }
329
330 static
331 void
332 pageload1(Page *p, Url *u, int dohist)
333 {
334         pageclose(p);
335         p->loading = TRUE;
336         p->url = u;
337         if(dohist)
338                 winaddhist(p->w, p->url);
339         proccreate(pageloadproc, p, STACK);
340 }
341
342 void
343 pageload(Page *p, Url *u, int dohist)
344 {
345         if(p->parent == nil)
346                 textset(&p->w->url, u->src.r, u->src.nr);
347         draw(p->b, p->all, display->white, nil, ZP);
348         pageload1(p, u, dohist);
349 }
350
351 void
352 pageget(Page *p, Runestr *src, Runestr *post,  int m, int dohist)
353 {
354         pageload(p, urlalloc(src, post, m), dohist);
355 }
356
357 void
358 pageclose(Page *p)
359 {
360         Page *c, *nc;
361
362         if(p == selpage)
363                 selpage = nil;
364         pageabort(p);
365         closeimages(p);
366         urlfree(p->url);
367         p->url = nil;
368         if(p->doc){
369                 freedocinfo(p->doc);
370                 p->doc = nil;
371         }
372         layfree(p->lay);
373         p->lay = nil;
374         freeitems(p->items);
375         p->items = nil;
376         for(c=p->child; c!=nil; c=nc){
377                 nc = c->next;
378                 pageclose(c);
379                 free(c);
380         }
381         p->child = nil;
382         closerunestr(&p->title);
383         closerunestr(&p->refresh.rs);
384         p->refresh.t = 0;
385         p->pos = ZP;
386         p->top = ZP;
387         p->bot = ZP;
388         p->loading = p->aborting = FALSE;
389 }
390
391 int
392 pageabort(Page *p)
393 {
394         Page *c;
395
396         for(c=p->child; c!=nil; c=c->next)
397                 pageabort(c);
398
399         p->aborting = TRUE;
400         while(p->loading)
401                 sleep(100);
402
403         p->aborting = FALSE;
404         return TRUE;
405 }
406
407
408 static Image *tmp;
409
410 void
411 tmpresize(void)
412 {
413         if(tmp)
414                 freeimage(tmp);
415
416         tmp = eallocimage(display, Rect(0,0,Dx(screen->r),Dy(screen->r)), screen->chan, 0, -1);
417 }
418
419 static
420 void
421 renderchilds(Page *p)
422 {
423         Rectangle r;
424         Kidinfo *k;
425         Page *c;
426         int i, j, x, y, *w, *h;
427
428         draw(p->b, p->all, display->white, nil, ZP);
429         r = p->all;
430         y = r.min.y;
431         c = p->child;
432         k = p->kidinfo;
433         frdims(k->rows, k->nrows, Dy(r), &h);
434         frdims(k->cols, k->ncols, Dx(r), &w);
435         for(i=0; i<k->nrows; i++){
436                 x = r.min.x;
437                 for(j=0; j<k->ncols; j++){
438                         if(c->aborting)
439                                 return;
440                         c->b = p->b;
441                         c->all = Rect(x,y,x+w[j],y+h[i]);
442                         c->w = p->w;
443                         pagerender(c);
444                         c = c->next;
445                         x += w[j];
446                 }
447                 y += h[i];
448         }
449         free(w);
450         free(h);
451 }
452
453 static
454 void
455 pagerender1(Page *p)
456 {
457         Rectangle r;
458
459         r = p->all;
460         p->hscrollr = r;
461         p->hscrollr.min.y = r.max.y-Scrollsize;
462         p->vscrollr = r;
463         p->vscrollr.max.x = r.min.x+Scrollsize;
464         r.max.y -= Scrollsize;
465         r.min.x += Scrollsize;
466         p->r = r;
467         p->vscrollr.max.y = r.max.y;
468         p->hscrollr.min.x = r.min.x;
469         laypage(p);
470         pageredraw(p);
471 }
472
473 void
474 pagerender(Page *p)
475 {
476         if(p->child && p->loading==FALSE)
477                 renderchilds(p);
478         else if(p->doc)
479                 pagerender1(p);
480 }
481
482 void
483 pageredraw(Page *p)
484 {
485         Rectangle r;
486
487         r = p->lay->r;
488         if(Dx(r)==0 || Dy(r)==0){
489                 draw(p->b, p->r, display->white, nil, ZP);
490                 return;
491         }
492         if(tmp == nil)
493                 tmpresize();
494
495         p->selecting = FALSE;
496         draw(tmp, tmp->r, getbg(p), nil, ZP);
497         laydraw(p, tmp, p->lay);
498         draw(p->b, p->r, tmp, nil, tmp->r.min);
499         r = p->vscrollr;
500         r.min.y = r.max.y;
501         r.max.y += Scrollsize;
502         draw(p->b, r, tagcols[HIGH], nil, ZP);
503         draw(p->b, insetrect(r, 1), tagcols[BACK], nil, ZP);
504         pagescrldraw(p);
505 }
506
507 static
508 void
509 pageselect1(Page *p)    /* when called, button 1 is down */
510 {
511         Point mp, npos, opos;
512         int b, scrled, x, y;
513
514         b = mouse->buttons;
515         mp = mousectl->xy;
516         opos = getpt(p, mp);
517         do{
518                 x = y = 0;
519                 if(mp.x < p->r.min.x)
520                         x -= p->r.min.x-mp.x;
521                 else if(mp.x > p->r.max.x)
522                         x += mp.x-p->r.max.x;
523                 if(mp.y < p->r.min.y)
524                         y -= (p->r.min.y-mp.y)*Panspeed;
525                 else if(mp.y > p->r.max.y)
526                         y += (mp.y-p->r.max.y)*Panspeed;
527
528                 scrled = pagescrollxy(p, x, y);
529                 npos = getpt(p, mp);
530                 if(opos.y <  npos.y){
531                         p->top = opos;
532                         p->bot = npos;
533                 }else{
534                         p->top = npos;
535                         p->bot = opos;
536                 }
537                 pageredraw(p);
538                 if(scrled == TRUE)
539                         scrsleep(100);
540                 else
541                         readmouse(mousectl);
542
543                 mp = mousectl->xy;
544         }while(mousectl->buttons == b);
545 }
546
547 static Rune left1[] =  { L'{', L'[', L'(', L'<', L'«', 0 };
548 static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
549 static Rune left2[] =  { L'\'', L'"', L'`', 0 };
550
551 void
552 pagedoubleclick(Page *p)
553 {
554         Point xy;
555         Line *l;
556         Box *b;
557
558         xy = getpt(p, mouse->xy);
559         l = linewhich(p->lay, xy);
560         if(l==nil || l->hastext==FALSE)
561                 return;
562
563         if(xy.x<l->boxes->r.min.x && hasbrk(l->state)){ /* beginning of line? */
564                 p->top = l->boxes->r.min;
565                 if(l->next && !hasbrk(l->next->state)){
566                         for(l=l->next; l->next!=nil; l=l->next)
567                                 if(hasbrk(l->next->state))
568                                         break;
569                 }
570                 p->bot = l->lastbox->r.max;;
571         }else if(xy.x>l->lastbox->r.max.x && hasbrk(l->next->state)){   /* end of line? */
572                 p->bot = l->lastbox->r.max;
573                 if(!hasbrk(l->state) && l->prev!=nil){
574                         for(l=l->prev; l->prev!=nil; l=l->prev)
575                                 if(hasbrk(l->state))
576                                         break;
577                 }
578                 p->top = l->boxes->r.min;
579         }else{
580                 b = pttobox(l, xy);
581                 if(b!=nil && b->i->tag==Itexttag){
582                         p->top = b->r.min;
583                         p->bot = b->r.max;
584                 }
585         }
586         p->top.y += 2;
587         p->bot.y -= 2;
588         pageredraw(p);
589 }
590
591 static uint clickmsec;
592
593 void
594 pageselect(Page *p)
595 {
596         int b, x, y;
597
598
599         selpage = p;
600         /*
601          * To have double-clicking and chording, we double-click
602          * immediately if it might make sense.
603          */
604         b = mouse->buttons;
605         if(mouse->msec-clickmsec<500){
606                 pagedoubleclick(p);
607                 x = mouse->xy.x;
608                 y = mouse->xy.y;
609                 /* stay here until something interesting happens */
610                 do
611                         readmouse(mousectl);
612                 while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3);
613                 mouse->xy.x = x;        /* in case we're calling pageselect1 */
614                 mouse->xy.y = y;
615         }
616         if(mousectl->buttons == b)
617                 pageselect1(p);
618
619         if(eqpt(p->top, p->bot)){
620                 if(mouse->msec-clickmsec<500)
621                         pagedoubleclick(p);
622                 else
623                         clickmsec = mouse->msec;
624         }
625         while(mouse->buttons){
626                 mouse->msec = 0;
627                 b = mouse->buttons;
628                 if(b & 2)       /* snarf only */
629                         cut(nil, nil, TRUE, FALSE, nil, 0);
630                 while(mouse->buttons == b)
631                         readmouse(mousectl);
632         }
633 }
634
635 Page *
636 pagewhich(Page *p, Point xy)
637 {
638         Page *c;
639
640         if(p->child == nil)
641                 return p;
642
643         for(c=p->child; c!=nil; c=c->next)
644                 if(ptinrect(xy, c->all))
645                         return pagewhich(c, xy);
646
647         return nil;
648 }
649
650 void
651 pagemouse(Page *p, Point xy, int but)
652 {
653         Box *b;
654
655         p = pagewhich(p, xy);
656         if(p == nil)
657                 return;
658
659         if(pagerefresh(p))
660                 return;
661
662         if(p->lay == nil)
663                 return;
664
665         if(ptinrect(xy, p->vscrollr)){
666                 pagescroll(p, but, FALSE);
667                 return;
668         }
669         if(ptinrect(xy, p->hscrollr)){
670                 pagescroll(p, but, TRUE);
671                 return;
672         }
673         xy = getpt(p, xy);
674         b = boxwhich(p->lay, xy);
675         if(b && b->mouse)
676                 b->mouse(b, p, but);
677         else if(but == 1)
678                 pageselect(p);
679 }
680
681 void
682 pagetype(Page *p, Rune r, Point xy)
683 {
684         Box *b;
685         int x, y;
686
687         p = pagewhich(p, xy);
688         if(p == nil)
689                 return;
690
691         if(pagerefresh(p))
692                 return;
693
694         if(p->lay == nil)
695                 return;
696
697         /* text field? */
698         xy = getpt(p, xy);
699         b = boxwhich(p->lay, xy);
700         if(b && b->key){
701                 b->key(b, p, r);
702                 return;
703         }
704
705         x = 0;
706         y = 0;
707         switch(r){
708         case Kleft:
709                 x -= Dx(p->r)/2;
710                 break;
711         case Kright:
712                 x += Dx(p->r)/2;
713                 break;
714         case Kdown:
715         case Kscrollonedown:
716                 y += Dy(p->r)/2;
717                 break;
718         case Kpgdown:
719                 y += Dy(p->r);
720                 break;
721         case Kup:
722         case Kscrolloneup:
723                 y -= Dy(p->r)/2;
724                 break;
725         case Kpgup:
726                 y -= Dy(p->r);
727                 break;
728         case Khome:
729                 y -= Dy(p->lay->r);     /* force p->pos.y = 0 */
730                 break;
731         case Kend:
732                 y = Dy(p->lay->r) - Dy(p->r);
733                 break;
734         default:
735                 return;
736         }
737         if(pagescrollxy(p, x, y))
738                 pageredraw(p);
739 }
740
741 void
742 pagesnarf(Page *p)
743 {
744         Runestr rs;
745
746         memset(&rs, 0, sizeof(Runestr));
747         laysnarf(p, p->lay, &rs);
748         putsnarf(&rs);
749         closerunestr(&rs);
750 }
751
752 void
753 pagesetrefresh(Page *p)
754 {
755         Runestr rs;
756         Rune *s, *q, *t;
757         char *v;
758         int n;
759
760         if(!p->doc || !p->doc->refresh)
761                 return;
762
763         s = p->doc->refresh;
764         q = runestrchr(s, L'=');
765         if(q == nil)
766                 return;
767         q++;
768         if(!q)
769                 return;
770         n = runestrlen(q);
771         if(*q == L'''){
772                 q++;
773                 n -= 2;
774         }
775         if(n <= 0)
776                 return;
777         t = runesmprint("%.*S", n, q);
778         rs.r = urlcombine(getbase(p), t);
779         rs.nr = runestrlen(rs.r);
780         copyrunestr(&p->refresh.rs, &rs);
781         closerunestr(&rs);
782         free(t);
783
784         /* now the time */
785         q = runestrchr(s, L';');
786         if(q){
787                 v = smprint("%.*S", (int)(q-s),  s);
788                 p->refresh.t = atoi(v);
789                 free(v);
790         }else
791                 p->refresh.t = 1;
792
793         p->refresh.t += time(0);
794 }
795
796 int
797 pagerefresh(Page *p)
798 {
799         int t;
800
801         if(!p->refresh.t)
802                 return 0;
803
804         t = p->refresh.t - time(0);
805         if(t > 0)
806                 return 0;
807
808         pageget(p, &p->refresh.rs, nil, HGet, FALSE);
809         return 1;
810 }