]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/mothra/libpanel/rtext.c
exec(2): fix prototypes
[plan9front.git] / sys / src / cmd / mothra / libpanel / rtext.c
1 /*
2  * Rich text with images.
3  * Should there be an offset field, to do subscripts & kerning?
4  */
5 #include <u.h>
6 #include <libc.h>
7 #include <draw.h>
8 #include <event.h>
9 #include <panel.h>
10 #include "pldefs.h"
11 #include "rtext.h"
12
13 #define LEAD    4       /* extra space between lines */
14 #define BORD    2       /* extra border for images */
15
16 Rtext *pl_rtnew(Rtext **t, int space, int indent, Image *b, Panel *p, Font *f, char *s, int flags, void *user){
17         Rtext *new;
18         new=pl_emalloc(sizeof(Rtext));
19         new->flags=flags;
20         new->user=user;
21         new->space=space;
22         new->indent=indent;
23         new->b=b;
24         new->p=p;
25         new->font=f;
26         new->text=s;
27         new->next=0;
28         new->nextline=0;
29         new->r=Rect(0,0,0,0);
30         if(*t)
31                 (*t)->last->next=new;
32         else
33                 *t=new;
34         (*t)->last=new;
35         return new;
36 }
37 Rtext *plrtpanel(Rtext **t, int space, int indent, Panel *p, void *user){
38         return pl_rtnew(t, space, indent, 0, p, 0, 0, 1, user);
39 }
40 Rtext *plrtstr(Rtext **t, int space, int indent, Font *f, char *s, int flags, void *user){
41         return pl_rtnew(t, space, indent, 0, 0, f, s, flags, user);
42 }
43 Rtext *plrtbitmap(Rtext **t, int space, int indent, Image *b, int flags, void *user){
44         return pl_rtnew(t, space, indent, b, 0, 0, 0, flags, user);
45 }
46 void plrtfree(Rtext *t){
47         Rtext *next;
48         while(t){
49                 next=t->next;
50                 free(t);
51                 t=next;
52         }
53 }
54 int pl_tabmin, pl_tabsize;
55 void pltabsize(int min, int size){
56         pl_tabmin=min;
57         pl_tabsize=size;
58 }
59 int pl_space(int space, int pos, int indent){
60         if(space>=0) return space;
61         switch(PL_OP(space)){
62         default:
63                 return 0;
64         case PL_TAB:
65                 return ((pos-indent+pl_tabmin)/pl_tabsize+PL_ARG(space))*pl_tabsize+indent-pos;
66         }
67 }
68 /*
69  * initialize rectangles & nextlines of text starting at t,
70  * galley width is wid.  Returns the total width/height of the text
71  */
72 Point pl_rtfmt(Rtext *t, int wid){
73         Rtext *tp, *eline;
74         int ascent, descent, x, space, a, d, w, topy, indent, maxwid;
75         Point p;
76
77         p=Pt(0,0);
78         eline=t;
79         maxwid=0;
80         while(t){
81                 ascent=0;
82                 descent=0;
83                 indent=space=pl_space(t->indent, 0, 0);
84                 x=0;
85                 tp=t;
86                 for(;;){
87                         if(tp->b){
88                                 a=tp->b->r.max.y-tp->b->r.min.y+BORD;
89                                 d=BORD;
90                                 w=tp->b->r.max.x-tp->b->r.min.x+BORD*2;
91                         }
92                         else if(tp->p){
93                                 /* what if plpack fails? */
94                                 plpack(tp->p, Rect(0,0,wid,wid));
95                                 plmove(tp->p, subpt(Pt(0,0), tp->p->r.min));
96                                 a=tp->p->r.max.y-tp->p->r.min.y;
97                                 d=0;
98                                 w=tp->p->r.max.x-tp->p->r.min.x;
99                         }
100                         else{
101                                 a=tp->font->ascent;
102                                 d=tp->font->height-a;
103                                 w=tp->wid=stringwidth(tp->font, tp->text);
104                         }
105                         if(x+w+space>wid) break;
106                         if(a>ascent) ascent=a;
107                         if(d>descent) descent=d;
108                         x+=w+space;
109                         tp=tp->next;
110                         if(tp==0){
111                                 eline=0;
112                                 break;
113                         }
114                         space=pl_space(tp->space, x, indent);
115                         if(space) eline=tp;
116                 }
117                 if(eline==t){   /* No progress!  Force fit the first block! */
118                         if(tp==t){
119                                 if(a>ascent) ascent=a;
120                                 if(d>descent) descent=d;
121                                 eline=tp->next;
122                         }else
123                                 eline=tp;
124                 }
125                 topy=p.y;
126                 p.y+=ascent;
127                 p.x=indent=pl_space(t->indent, 0, 0);
128                 for(;;){
129                         t->topy=topy;
130                         t->r.min.x=p.x;
131                         if(t->b){
132                                 t->r.max.y=p.y+BORD;
133                                 t->r.min.y=p.y-(t->b->r.max.y-t->b->r.min.y)-BORD;
134                                 p.x+=(t->b->r.max.x-t->b->r.min.x)+BORD*2;
135                         }
136                         else if(t->p){
137                                 t->r.max.y=p.y;
138                                 t->r.min.y=p.y-t->p->r.max.y;
139                                 p.x+=t->p->r.max.x;
140                         }
141                         else{
142                                 t->r.min.y=p.y-t->font->ascent;
143                                 t->r.max.y=t->r.min.y+t->font->height;
144                                 p.x+=t->wid;
145                         }
146                         t->r.max.x=p.x;
147                         t->nextline=eline;
148                         t=t->next;
149                         if(t==eline) break;
150                         p.x+=pl_space(t->space, p.x, indent);
151                 }
152                 if(p.x>maxwid) maxwid=p.x;
153                 p.y+=descent+LEAD;
154         }
155         return Pt(maxwid, p.y);
156 }
157
158 /*
159  * If we draw the text in a backup bitmap and copy it onto the screen,
160  * the bitmap pointers in all the subpanels point to the wrong bitmap.
161  * This code fixes them.
162  */
163 void pl_stuffbitmap(Panel *p, Image *b){
164         p->b=b;
165         for(p=p->child;p;p=p->next)
166                 pl_stuffbitmap(p, b);
167 }
168
169 void pl_rtdraw(Image *b, Rectangle r, Rtext *t, Point offs){
170         static Image *backup;
171         Point lp, sp;
172         Rectangle dr;
173         Image *bb;
174
175         bb = b;
176         if(backup==0 || backup->chan!=b->chan || rectinrect(r, backup->r)==0){
177                 freeimage(backup);
178                 backup=allocimage(display, bb->r, bb->chan, 0, DNofill);
179         }
180         if(backup)
181                 b=backup;
182         pl_clr(b, r);
183         lp=ZP;
184         sp=ZP;
185         offs=subpt(r.min, offs);
186         for(;t;t=t->next) if(!eqrect(t->r, Rect(0,0,0,0))){
187                 dr=rectaddpt(t->r, offs);
188                 if(dr.max.y>r.min.y
189                 && dr.min.y<r.max.y
190                 && dr.max.x>r.min.x
191                 && dr.min.x<r.max.x){
192                         if(t->b){
193                                 draw(b, insetrect(dr, BORD), t->b, 0, t->b->r.min);
194                                 if(t->flags&PL_HOT) border(b, dr, 1, display->black, ZP);
195                                 if(t->flags&PL_STR) {
196                                         line(b, Pt(dr.min.x, dr.min.y), Pt(dr.max.x, dr.max.y),
197                                                 Endsquare, Endsquare, 0,
198                                                 display->black, ZP);
199                                         line(b, Pt(dr.min.x, dr.max.y), Pt(dr.max.x, dr.min.y),
200                                                 Endsquare, Endsquare, 0,
201                                                 display->black, ZP);
202                                 }
203                                 if(t->flags&PL_SEL)
204                                         pl_highlight(b, dr);
205                         }
206                         else if(t->p){
207                                 plmove(t->p, subpt(dr.min, t->p->r.min));
208                                 pldraw(t->p, b);
209                                 if(b!=bb)
210                                         pl_stuffbitmap(t->p, bb);
211                         }
212                         else{
213                                 string(b, dr.min, display->black, ZP, t->font, t->text);
214                                 if(t->flags&PL_SEL)
215                                         pl_highlight(b, dr);
216                                 if(t->flags&PL_STR){
217                                         int y = dr.max.y - t->font->height/2;
218                                         if(sp.y != y)
219                                                 sp = Pt(dr.min.x, y);
220                                         line(b, sp, Pt(dr.max.x, y),
221                                                 Endsquare, Endsquare, 0,
222                                                 display->black, ZP);
223                                         sp = Pt(dr.max.x, y);
224                                 } else
225                                         sp = ZP;
226                                 if(t->flags&PL_HOT){
227                                         int y = dr.max.y - 1;
228                                         if(lp.y != y)
229                                                 lp = Pt(dr.min.x, y);
230                                         line(b, lp, Pt(dr.max.x, y),
231                                                 Endsquare, Endsquare, 0,
232                                                 display->black, ZP);
233                                         lp = Pt(dr.max.x, y);
234                                 } else
235                                         lp = ZP;
236                                 continue;
237                         }
238                         lp = ZP;
239                         sp = ZP;
240                 }
241         }
242         if(b!=bb)
243                 draw(bb, r, b, 0, r.min);
244 }
245 /*
246  * Reposition text already drawn in the window.
247  * We just move the pixels and update the positions of any
248  * enclosed panels
249  */
250 void pl_reposition(Rtext *t, Image *b, Point p, Rectangle r){
251         Point offs;
252         pl_cpy(b, p, r);
253         offs=subpt(p, r.min);
254         for(;t;t=t->next)
255                 if(!eqrect(t->r, Rect(0,0,0,0)) && !t->b && t->p)
256                         plmove(t->p, offs);
257 }
258 /*
259  * Rectangle r of Image b contains an image of Rtext t, offset by oldoffs.
260  * Redraw the text to have offset yoffs.
261  */
262 void pl_rtredraw(Image *b, Rectangle r, Rtext *t, Point offs, Point oldoffs, int dir){
263         int d, size;
264
265         if(dir==VERT){
266                 d=oldoffs.y-offs.y;
267                 size=r.max.y-r.min.y;
268                 if(d>=size || -d>=size) /* move more than screenful */
269                         pl_rtdraw(b, r, t, offs);
270                 else if(d<0){ /* down */
271                         pl_reposition(t, b, r.min,
272                                 Rect(r.min.x, r.min.y-d, r.max.x, r.max.y));
273                         pl_rtdraw(b, Rect(r.min.x, r.max.y+d, r.max.x, r.max.y),
274                                 t, Pt(offs.x, offs.y+size+d));
275                 }
276                 else if(d>0){ /* up */
277                         pl_reposition(t, b, Pt(r.min.x, r.min.y+d),
278                                 Rect(r.min.x, r.min.y, r.max.x, r.max.y-d));
279                         pl_rtdraw(b, Rect(r.min.x, r.min.y, r.max.x, r.min.y+d),
280                                 t, offs);
281                 }
282         }else{ /* dir==HORIZ */
283                 d=oldoffs.x-offs.x;
284                 size=r.max.x-r.min.x;
285                 if(d>=size || -d>=size) /* move more than screenful */
286                         pl_rtdraw(b, r, t, offs);
287                 else if(d<0){ /* right */
288                         pl_reposition(t, b, r.min,
289                                 Rect(r.min.x-d, r.min.y, r.max.x, r.max.y));
290                         pl_rtdraw(b, Rect(r.max.x+d, r.min.y, r.max.x, r.max.y),
291                                 t, Pt(offs.x+size+d, offs.y));
292                 }
293                 else if(d>0){ /* left */
294                         pl_reposition(t, b, Pt(r.min.x+d, r.min.y),
295                                 Rect(r.min.x, r.min.y, r.max.x-d, r.max.y));
296                         pl_rtdraw(b, Rect(r.min.x, r.min.y, r.min.x+d, r.max.y),
297                                 t, offs);
298                 }               
299         }
300 }
301 Rtext *pl_rthit(Rtext *t, Point offs, Point p, Point ul){
302         Rectangle r;
303         Point lp;
304         if(t==0) return 0;
305         p.x+=offs.x-ul.x;
306         p.y+=offs.y-ul.y;
307         while(t->nextline && t->nextline->topy<=p.y) t=t->nextline;
308         lp=ZP;
309         for(;t!=0;t=t->next){
310                 if(t->topy>p.y) return 0;
311                 r = t->r;
312                 if((t->flags&PL_HOT) != 0 && t->b == nil && t->p == nil){
313                         if(lp.y == r.max.y && lp.x < r.min.x)
314                                 r.min.x=lp.x;
315                         lp=r.max;
316                 } else
317                         lp=ZP;
318                 if(ptinrect(p, r)) return t;
319         }
320         return 0;
321 }
322
323 void plrtseltext(Rtext *t, Rtext *s, Rtext *e){
324         while(t){
325                 t->flags &= ~PL_SEL;
326                 t = t->next;
327         }
328         if(s==0 || e==0)
329                 return;
330         for(t=s; t!=0 && t!=e; t=t->next)
331                 ;
332         if(t==e){
333                 for(t=s; t!=e; t=t->next)
334                         t->flags |= PL_SEL;
335         }else{
336                 for(t=e; t!=s; t=t->next)
337                         t->flags |= PL_SEL;
338         }
339         t->flags |= PL_SEL;
340 }
341
342 char *plrtsnarftext(Rtext *w){
343         char *b, *p, *e, *t;
344         int n;
345
346         b=p=e=0;
347         for(; w; w = w->next){
348                 if((w->flags&PL_SEL)==0 || w->text==0)
349                         continue;
350                 n = strlen(w->text)+64;
351                 if(p+n >= e){
352                         n = (p+n+64)-b;
353                         t = pl_erealloc(b, n);
354                         p = t+(p-b);
355                         e = t+n;
356                         b = t;
357                 }
358                 if(w->space == 0)
359                         p += sprint(p, "%s", w->text);
360                 else if(w->space > 0)
361                         p += sprint(p, " %s", w->text);
362                 else if(PL_OP(w->space) == PL_TAB)
363                         p += sprint(p, "\t%s", w->text);
364                 if(w->nextline == w->next)
365                         p += sprint(p, "\n");
366         }
367         return b;
368 }