17 static Point prevmouse;
18 static Window *mousew;
37 cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
44 * Always guaranteed that n bytes may be interpreted
45 * without worrying about partial runes. This may mean
46 * reading up to UTFmax-1 more bytes than n; the caller
47 * knows this. If n is a firm limit, the caller should
57 w = chartorune(s, (char*)q);
70 bytetorunestr(char *s, Runestr *rs)
77 cvttorunes(s, nb, r, &nb, &nr, nil);
86 fprint(2, "abaco: %s: %r\n", s);
98 error("malloc failed");
99 setmalloctag(p, getcallerpc(&n));
105 erealloc(void *p, ulong n)
109 error("realloc failed");
110 setmalloctag(p, getcallerpc(&n));
123 error("runestrdup failed");
124 setmalloctag(p, getcallerpc(&r));
135 error("strdup failed");
136 setmalloctag(t, getcallerpc(&s));
141 runestreq(Runestr a, Runestr b)
143 return runeeq(a.r, a.nr, b.r, b.nr);
147 runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
151 return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
155 closerunestr(Runestr *rs)
165 copyrunestr(Runestr *a, Runestr *b)
168 a->r = runemalloc(b->nr+1);
169 runemove(a->r, b->r, b->nr);
178 * Hard to get absolutely right. Use what we know about ASCII
179 * and assume anything above the Latin control characters is
180 * potentially an alphanumeric.
184 if(0x7F<=c && c<=0xA0)
186 if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
192 skipbl(Rune *r, int n, int *np)
194 while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){
203 findbl(Rune *r, int n, int *np)
205 while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
218 ff = ((Iformfield *)i)->formfield;
219 if(ff->ftype==Ftext || ff->ftype==Ftextarea || ff->ftype==Fpassword)
228 if(i->state&IFwrap && i->tag!=Iruletag && i->tag!=Itabletag)
235 dimwidth(Dimen d, int w)
245 else if(k==Dpercent && s<100)
252 frdims(Dimen *d, int n, int t, int **ret)
254 int totpix, totpcnt, totrel;
255 double spix, spcnt, relu, vd;
256 int tt, trest, totpixrel, minrelu, i;
257 int *x, *spec, *kind;
260 *ret = x = emalloc(sizeof(int));
264 totpix = totpcnt = totrel = 0;
265 spec = emalloc(n*sizeof(int));
266 kind = emalloc(n*sizeof(int));
268 spec[i] = dimenspec(d[i]);
271 kind[i] = dimenkind(d[i]);
290 minrelu = Scrollsize+Scrollgap;
291 relu = (double)minrelu;
292 tt = totpix + t*totpcnt/100 + totrel*minrelu;
296 spcnt = (double)((t-totpix)*100)/(double)(t*totpcnt);
298 spix = (double)t/(double)totpix;
300 relu += (double)(t-tt)/(double)totrel;
302 totpixrel = totpix + totrel*minrelu;
304 spcnt = (double)((t-totpixrel)*100)/(double)(t*totpcnt);
306 trest = t - totrel*minrelu;
308 spcnt = (double)trest/(double)(totpix + (t*totpcnt/100));
310 spcnt = (double)t/(double)tt;
316 x = emalloc(n * sizeof(int));
318 for(i=0; i<n-1; i++){
319 vd = (double)spec[i];
325 vd = vd*(double)t*spcnt/100.0;
351 if(d->backgrounditem){
352 if(d->backgrounditem->aux){
353 ci = d->backgrounditem->aux;
355 getimage(ci, d->backgrounditem->altrep);
360 bg = getcolor(d->background.color);
371 return p->url->act.r;
372 return p->url->src.r;
376 eallocimage(Display *d, Rectangle r, ulong chan, int repl, int col)
380 i = allocimage(d, r, chan, repl, col);
382 error("allocimage failed");
387 rect3d(Image *im, Rectangle r, int i, Image **c, Point sp)
393 sp = addpt(sp, Pt(i,i));
396 draw(im, Rect(r.min.x+i, r.min.y+i, r.max.x-i, r.max.y-i), c[2], nil, sp);
398 p[1] = Pt(r.min.x, r.max.y);
399 p[2] = Pt(r.min.x+i, r.max.y-i);
400 p[3] = Pt(r.min.x+i, r.min.y+i);
401 p[4] = Pt(r.max.x-i, r.min.y+i);
402 p[5] = Pt(r.max.x, r.min.y);
403 fillpoly(im, p, 6, 0, c[0], sp);
405 p[1] = Pt(r.min.x, r.max.y);
406 p[2] = Pt(r.min.x+i, r.max.y-i);
407 p[3] = Pt(r.max.x-i, r.max.y-i);
408 p[4] = Pt(r.max.x-i, r.min.y+i);
409 p[5] = Pt(r.max.x, r.min.y);
410 fillpoly(im, p, 6, 0, c[1], sp);
414 ellipse3d(Image *im, Point p, int rad, int i, Image **c, Point sp)
416 fillarc(im, p, rad, rad, c[0], sp, 45, 180);
417 fillarc(im, p, rad, rad, c[1], sp, 45, -180);
418 fillellipse(im, p, rad-i, rad-i, c[2], sp);
422 colarray(Image **c, Image *c0, Image *c1, Image *c2, int checked)
434 static char *deffontpaths[] = {
438 static char *fontpaths[NumFnt];
439 static Font *fonts[NumFnt];
448 /* we don't care if getenv(2) fails */
449 snprint(buf, sizeof(buf)-1, "%s/lib/abaco.fonts", getenv("home"));
450 if((bp=Bopen(buf, OREAD)) == nil)
453 for(i=0; i<NumFnt; i++)
454 if((fontpaths[i]=Brdstr(bp, '\n', 1)) == nil)
460 fprint(2, "abaco: not enough fontpaths in '%s'\n", buf);
465 for(i=0; i<NumFnt; i++)
466 fontpaths[i] = deffontpaths[i];
473 fonts[i] = openfont(display, fontpaths[i]);
475 error("can't open font file");
480 typedef struct Color Color;
492 static Color *colortab[NHASH];
501 return display->white;
502 else if(rgb == 0x000000)
503 return display->black;
506 for(c=colortab[h]; c!=nil; c=c->next)
508 flushimage(display, 0); /* BUG? */
511 c = emalloc(sizeof(Color));
512 c->i = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, (rgb<<8)|0xFF);
514 c->next = colortab[h];
521 plumbrunestr(Runestr *rs, char *attr)
527 if(plumbsendfd >= 0){
528 m = emalloc(sizeof(Plumbmsg));
529 m->src = estrdup("abaco");
531 m->wdir = estrdup("/tmp");
532 m->type = estrdup("text");
534 m->attr = plumbunpackattr(attr);
537 m->data = smprint("%.*S", rs->nr, rs->r);
539 i = plumbsend(plumbsendfd, m);
555 inclass(char c, Rune* cl)
557 int n, ans, negate, i;
570 if(cl[i]=='-' && i>0 && i<n-1){
571 if(c>=cl[i - 1] && c<=cl[i+1]){
594 t = smprint("%S", s);
599 if(inclass(c, L"- /$_@.!*'(),a-zA-Z0-9"))
604 u = runemalloc(len+1);
609 if(inclass(c, L"-/$_@.!*'(),a-zA-Z0-9"))
615 u[j++] = hexdigit((c >> 4)&15);
616 u[j++] = hexdigit(c&15);
625 reverseimages(Iimage **head)
630 for(c=*head; c!=nil; c=n){
638 char urlexpr[] = "^(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|"
639 "prospero)://([a-zA-Z0-9_@\\-]+([.:][a-zA-Z0-9_@\\-]+)*)";
648 urlprog = regcomp(urlexpr);
652 memset(rs, 0, sizeof(rs));
653 if(rregexec(urlprog, r, rs, nelem(rs)) == 0)
666 threadsetname("execproc");
686 procexecl(sync, "/bin/rc", "rc", "-c", cmd, 0);
699 threadsetname("writeproc");
707 for(i=0; i<np; i+=n){
711 if(write(fd, s+i, n) != n)
725 /* not generated by the script */
728 "windows-874", "tis",
737 static int winchars[] = {
738 8226, /* 8226 is a bullet */
739 8226, 8226, 8218, 402, 8222, 8230, 8224, 8225,
740 710, 8240, 352, 8249, 338, 8226, 8226, 8226,
741 8226, 8216, 8217, 8220, 8221, 8226, 8211, 8212,
742 732, 8482, 353, 8250, 339, 8226, 8226, 376
746 tcs(char *cs, char *s, long *np)
754 char buf[BUFSIZE], cmd[50];
759 if(s==nil || *s=='\0' || *np==0){
760 werrstr("tcs failed: no data");
765 werrstr("tcs failed: no charset");
769 if(cistrncmp(cs, "utf-8", 5)==0 || cistrncmp(cs, "utf8", 4)==0)
772 for(i=0; tcstab[i].mime!=nil; i++)
773 if(cistrncmp(cs, tcstab[i].mime, strlen(tcstab[i].mime)) == 0)
776 if(tcstab[i].mime == nil){
777 fprint(2, "abaco: charset: %s not supported\n", cs);
780 if(cistrcmp(tcstab[i].tcs, "8859-1")==0 || cistrcmp(tcstab[i].tcs, "ascii")==0){
783 for(us=(uchar*)s; *us; us++)
787 for(us=(uchar*)s, u=t; *us; us++){
788 if(*us>=Winstart && *us<=Winend)
789 *u++ = winchars[*us-Winstart];
792 u += runetochar(u, &r);
800 if(pipe(p)<0 || pipe(q)<0)
801 error("can't create pipe");
803 sync = chancreate(sizeof(ulong), 0);
805 error("can't create channel");
807 snprint(cmd, sizeof cmd, "tcs -f %s", tcstab[i].tcs);
808 e = emalloc(sizeof(Exec));
815 proccreate(execproc, e, STACK);
821 /* in case tcs fails */
823 sync = chancreate(sizeof(ulong), 0);
825 error("can't create channel");
827 a = emalloc(4*sizeof(void *));
832 proccreate(writeproc, a, STACK);
835 while((n = read(q[0], buf, sizeof(buf))) > 0){
836 s = erealloc(s, i+n+1);
837 memmove(s+i, buf, n);
843 fprint(2, "tcs: did not write %ld; wrote %uld\n", *np, n);
850 fprint(2, "tcs failed: can't convert charset=%s to %s\n", cs, tcstab[i].tcs);
862 return c==' ' || c== '\t' || c=='\r' || c=='\n';
867 findctype(char *b, int l, char *keyword, char *s)
872 p = cistrstr(s, keyword);
875 p += strlen(keyword);
876 while(*p && isspace(*p))
881 while(*p && isspace(*p))
891 for(e = p; *e < 127 && *e > ' ' ; e++)
896 snprint(b, l, "%.*s", i, p);
902 finddocctype(char *b, int l, char *s)
906 p = cistrstr(s, "<meta");
913 snprint(b, l, "%.*s", (int)(e-p), p);
919 findxmltype(char *b, int l, char *s)
923 p = cistrstr(s, "<?xml ");
931 snprint(b, l, "%.*s", (int)(e-p), p);
937 * servers can lie about lie about the charset,
938 * so we use the charset based on the priority.
941 convert(Runestr ctype, char *s, long *np)
943 char t[25], buf[256];
947 snprint(buf, sizeof(buf), "%.*S", ctype.nr, ctype.r);
948 findctype(t, sizeof(t), "charset", buf);
950 if(findxmltype(buf, sizeof(buf), s)==0)
951 findctype(t, sizeof(t), "encoding", buf);
952 if(finddocctype(buf, sizeof(buf), s) == 0)
953 findctype(t, sizeof(t), "charset", buf);
957 return tcs(t, s, np);
961 xtofchar(Rune *s, Font *f, long p)
970 for(r=s; *r!=L'\0'; r++){
971 p -= runestringnwidth(f, r, 1);
980 istextsel(Page *p, Rectangle r, int *q0, int *q1, Rune *s, Font *f)
985 topinr= ptinrect(p->top, r);
986 if(topinr || (r.min.y>p->top.y && r.max.y<p->bot.y))
988 botinr = ptinrect(p->bot, r);
989 if(botinr || r.min.y>p->bot.y)
990 p->selecting = FALSE;
992 if(topinr || botinr){
994 *q0 = xtofchar(s, f, p->top.x-r.min.x);
996 *q1 = xtofchar(s, f, p->bot.x-r.min.x);
1000 return p->selecting;
1004 getpt(Page *p, Point xy)
1006 xy.x = xy.x-p->r.min.x+p->pos.x;
1007 xy.y = xy.y-p->r.min.y+p->pos.y;
1013 getimage(Cimage *ci, Rune *altr)
1024 snprint(buf, sizeof(buf), "[%S]", altr ? altr : L"IMG");
1026 r.max.x = 2*Space + stringwidth(font, buf);
1027 r.max.y = 2*Space + font->height;
1028 ci->i = eallocimage(display, r, GREY1, 1, DBlack);
1031 string(ci->i, r.min, display->white, ZP, font, buf);
1034 nbits = bytesperline(mi->r, mi->depth)*Dy(mi->r);
1035 bits = emalloc(nbits);
1036 unloadmemimage(mi, mi->r, bits, nbits);
1038 /* get rid of alpha channel from transparent gif * /
1040 if(mi->depth == 16){
1041 for(y=1; y<nbits; y+=2)
1042 bits[y>>1] = bits[y];
1045 i = eallocimage(display, mi->r, mi->chan, 0, DNofill);
1046 loadimage(i, i->r, bits, nbits);
1047 i2 = eallocimage(display, i->r, RGB24, 1, DNofill);
1048 draw(i2, i2->r, display->black, nil, ZP);
1049 draw(i2, i2->r, i, nil, i->r.min);
1059 fixtext1(Item **list)
1061 Itext *text, *ntext;
1070 for(it=*list; it!=nil; it=prev->next){
1071 if(it->tag!=Itexttag || forceitem(it))
1076 while(*s && isspacerune(*s))
1080 prev = *list = it->next;
1082 prev->next = it->next;
1091 while(s[n] && !isspacerune(s[n]))
1097 s1 = runemalloc(n+1);
1098 s1 = runemove(s1, s, n);
1102 while(*s && isspacerune(*s))
1107 s2 = runemalloc(n+1);
1110 ntext = emalloc(sizeof(Itext));
1112 ntext->ascent = text->ascent;
1113 ntext->anchorid = text->anchorid;
1114 ntext->state = text->state&~(IFbrk|IFbrksp|IFnobrk|IFcleft|IFcright);
1115 ntext->tag = text->tag;
1116 ntext->fnt = text->fnt;
1117 ntext->fg = text->fg;
1118 ntext->ul = text->ul;
1119 ntext->next = (Item *)text->next;
1120 text->next = (Item *)ntext;
1135 fixtext1(&p->items);
1136 for(t=p->doc->tables; t!=nil; t=t->next)
1137 for(c=t->cells; c!=nil; c=c->next)
1138 fixtext1(&c->content);
1141 typedef struct Refresh Refresh;
1149 static Refresh *refreshs = nil;
1150 static QLock refreshlock;
1153 addrefresh(Page *p, char *fmt, ...)
1163 s = runevsmprint(fmt, arg);
1166 error("runevsmprint failed");
1173 qlock(&refreshlock);
1174 for(r=refreshs; r!=nil; r=r->next)
1178 incref(p->w); /* flushrefresh will decref */
1179 r = emalloc(sizeof(Refresh));
1185 nbsendp(crefresh, nil);
1186 qunlock(&refreshlock);
1189 /* called while row is locked */
1196 qlock(&refreshlock);
1197 for(r=refreshs; r!=nil; r=next){
1199 if(p->changed==TRUE && p->aborting==FALSE){
1201 if(p->parent==nil || p->loading==FALSE)
1207 winsetstatus(p->w, p->status);
1218 qunlock(&refreshlock);
1222 savemouse(Window *w)
1224 prevmouse = mouse->xy;
1229 restoremouse(Window *w)
1231 if(mousew!=nil && mousew==w)
1232 moveto(mousectl, prevmouse);
1246 makenewwindow(Page *p)
1249 Window *w, *bigw, *emptyw;
1255 else if(selpage && selpage->col)
1257 else if(p && p->col)
1260 if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
1261 error("can't make column");
1262 c = row.col[row.ncol-1];
1265 if(p==nil || p->w==nil || c->nw==0)
1266 return coladd(c, nil, nil, -1);
1268 /* find biggest window and biggest blank spot */
1271 for(i=1; i<c->nw; i++){
1273 /* use >= to choose one near bottom of screen */
1274 if(Dy(w->page.all) >= Dy(bigw->page.all))
1276 if(w->page.lay==nil && Dy(w->page.all) >= Dy(emptyw->page.all))
1279 emptyp = &emptyw->page;
1280 el = Dy(emptyp->all);
1281 /* if empty space is big, use it */
1282 if(el>15 || (el>3 && el>(Dy(bigw->page.all)-1)/2))
1283 y = emptyp->all.max.y;
1285 /* if this window is in column and isn't much smaller, split it */
1286 if(p->col==c && Dy(p->w->r)>2*Dy(bigw->r)/3)
1288 y = (bigw->r.min.y + bigw->r.max.y)/2;
1290 w = coladd(c, nil, nil, y);
1291 colgrow(w->col, w, 1);