Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <mouse.h>
6 #include <cursor.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include "flayer.h"
10 #include "samterm.h"
12 #define DELTA 10
14 static Flayer **llist; /* front to back */
15 static int nllist;
16 static int nlalloc;
17 static Rectangle lDrect;
19 Vis visibility(Flayer *);
20 void newvisibilities(int);
21 void llinsert(Flayer*);
22 void lldelete(Flayer*);
24 Image *maincols[NCOL];
25 Image *cmdcols[NCOL];
27 void
28 flstart(Rectangle r)
29 {
30 lDrect = r;
32 /* Main text is yellowish */
33 maincols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
34 maincols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
35 maincols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DYellowgreen);
36 maincols[TEXT] = display->black;
37 maincols[HTEXT] = display->black;
39 /* Command text is blueish */
40 cmdcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
41 cmdcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
42 cmdcols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DPurpleblue);
43 cmdcols[TEXT] = display->black;
44 cmdcols[HTEXT] = display->black;
45 }
47 void
48 flnew(Flayer *l, Rune *(*fn)(Flayer*, long, ulong*), int u0, void *u1)
49 {
50 if(nllist == nlalloc){
51 nlalloc += DELTA;
52 llist = realloc(llist, nlalloc*sizeof(Flayer**));
53 if(llist == 0)
54 panic("flnew");
55 }
56 l->textfn = fn;
57 l->user0 = u0;
58 l->user1 = u1;
59 l->lastsr = ZR;
60 llinsert(l);
61 }
63 Rectangle
64 flrect(Flayer *l, Rectangle r)
65 {
66 rectclip(&r, lDrect);
67 l->entire = r;
68 l->scroll = insetrect(r, FLMARGIN(l));
69 r.min.x =
70 l->scroll.max.x = r.min.x+FLMARGIN(l)+FLSCROLLWID(l)+(FLGAP(l)-FLMARGIN(l));
71 return r;
72 }
74 void
75 flinit(Flayer *l, Rectangle r, Font *ft, Image **cols)
76 {
77 lldelete(l);
78 llinsert(l);
79 l->visible = All;
80 l->origin = l->p0 = l->p1 = 0;
81 l->f.display = display; // for FLMARGIN
82 frinit(&l->f, insetrect(flrect(l, r), FLMARGIN(l)), ft, screen, cols);
83 l->f.maxtab = maxtab*stringwidth(ft, "0");
84 newvisibilities(1);
85 draw(screen, l->entire, l->f.cols[BACK], nil, ZP);
86 scrdraw(l, 0L);
87 flborder(l, 0);
88 }
90 void
91 flclose(Flayer *l)
92 {
93 if(l->visible == All)
94 draw(screen, l->entire, display->white, nil, ZP);
95 else if(l->visible == Some){
96 if(l->f.b == 0)
97 l->f.b = allocimage(display, l->entire, screen->chan, 0, DNofill);
98 if(l->f.b){
99 draw(l->f.b, l->entire, display->white, nil, ZP);
100 flrefresh(l, l->entire, 0);
103 frclear(&l->f, 1);
104 lldelete(l);
105 if(l->f.b && l->visible!=All)
106 freeimage(l->f.b);
107 l->textfn = 0;
108 newvisibilities(1);
111 void
112 flborder(Flayer *l, int wide)
114 if(flprepare(l)){
115 border(l->f.b, l->entire, FLMARGIN(l), l->f.cols[BACK], ZP);
116 border(l->f.b, l->entire, wide? FLMARGIN(l) : 1, l->f.cols[BORD], ZP);
117 if(l->visible==Some)
118 flrefresh(l, l->entire, 0);
122 Flayer *
123 flwhich(Point p)
125 int i;
127 if(p.x==0 && p.y==0)
128 return nllist? llist[0] : 0;
129 for(i=0; i<nllist; i++)
130 if(ptinrect(p, llist[i]->entire))
131 return llist[i];
132 return 0;
135 void
136 flupfront(Flayer *l)
138 int v = l->visible;
140 lldelete(l);
141 llinsert(l);
142 if(v!=All)
143 newvisibilities(0);
146 void
147 newvisibilities(int redraw)
148 /* if redraw false, we know it's a flupfront, and needn't
149 * redraw anyone becoming partially covered */
151 int i;
152 Vis ov;
153 Flayer *l;
155 for(i = 0; i<nllist; i++){
156 l = llist[i];
157 l->lastsr = ZR; /* make sure scroll bar gets redrawn */
158 ov = l->visible;
159 l->visible = visibility(l);
160 #define V(a, b) (((a)<<2)|((b)))
161 switch(V(ov, l->visible)){
162 case V(Some, None):
163 if(l->f.b)
164 freeimage(l->f.b);
165 case V(All, None):
166 case V(All, Some):
167 l->f.b = 0;
168 frclear(&l->f, 0);
169 break;
171 case V(Some, Some):
172 case V(None, Some):
173 if(ov == None || (l->f.b==0 && redraw))
174 flprepare(l);
175 if(l->f.b && redraw){
176 flrefresh(l, l->entire, 0);
177 freeimage(l->f.b);
178 l->f.b = 0;
179 frclear(&l->f, 0);
181 case V(None, None):
182 case V(All, All):
183 break;
185 case V(Some, All):
186 if(l->f.b){
187 draw(screen, l->entire, l->f.b, nil, l->entire.min);
188 freeimage(l->f.b);
189 l->f.b = screen;
190 break;
192 case V(None, All):
193 flprepare(l);
194 break;
196 if(ov==None && l->visible!=None)
197 flnewlyvisible(l);
201 void
202 llinsert(Flayer *l)
204 int i;
205 for(i=nllist; i>0; --i)
206 llist[i]=llist[i-1];
207 llist[0]=l;
208 nllist++;
211 void
212 lldelete(Flayer *l)
214 int i;
216 for(i=0; i<nllist; i++)
217 if(llist[i]==l){
218 --nllist;
219 for(; i<nllist; i++)
220 llist[i] = llist[i+1];
221 return;
223 panic("lldelete");
226 void
227 flinsert(Flayer *l, Rune *sp, Rune *ep, long p0)
229 if(flprepare(l)){
230 frinsert(&l->f, sp, ep, p0-l->origin);
231 scrdraw(l, scrtotal(l));
232 if(l->visible==Some)
233 flrefresh(l, l->entire, 0);
237 void
238 fldelete(Flayer *l, long p0, long p1)
240 if(flprepare(l)){
241 p0 -= l->origin;
242 if(p0 < 0)
243 p0 = 0;
244 p1 -= l->origin;
245 if(p1<0)
246 p1 = 0;
247 frdelete(&l->f, p0, p1);
248 scrdraw(l, scrtotal(l));
249 if(l->visible==Some)
250 flrefresh(l, l->entire, 0);
254 int
255 flselect(Flayer *l)
257 int ret;
258 if(l->visible!=All)
259 flupfront(l);
260 frselect(&l->f, mousectl);
261 ret = 0;
262 if(l->f.p0==l->f.p1){
263 if(mousep->msec-l->click<Clicktime && l->f.p0+l->origin==l->p0){
264 ret = 1;
265 l->click = 0;
266 }else
267 l->click = mousep->msec;
268 }else
269 l->click = 0;
270 l->p0 = l->f.p0+l->origin, l->p1 = l->f.p1+l->origin;
271 return ret;
274 void
275 flsetselect(Flayer *l, long p0, long p1)
277 ulong fp0, fp1;
278 int ticked;
280 l->click = 0;
281 if(l->visible==None || !flprepare(l)){
282 l->p0 = p0, l->p1 = p1;
283 return;
285 l->p0 = p0, l->p1 = p1;
286 flfp0p1(l, &fp0, &fp1, &ticked);
287 if(fp0==l->f.p0 && fp1==l->f.p1){
288 if(l->f.ticked != ticked)
289 frtick(&l->f, frptofchar(&l->f, fp0), ticked);
290 return;
293 if(fp1<=l->f.p0 || fp0>=l->f.p1 || l->f.p0==l->f.p1 || fp0==fp1){
294 /* no overlap or trivial repainting */
295 frdrawsel(&l->f, frptofchar(&l->f, l->f.p0), l->f.p0, l->f.p1, 0);
296 if(fp0 != fp1 || ticked)
297 frdrawsel(&l->f, frptofchar(&l->f, fp0), fp0, fp1, 1);
298 goto Refresh;
300 /* the current selection and the desired selection overlap and are both non-empty */
301 if(fp0 < l->f.p0){
302 /* extend selection backwards */
303 frdrawsel(&l->f, frptofchar(&l->f, fp0), fp0, l->f.p0, 1);
304 }else if(fp0 > l->f.p0){
305 /* trim first part of selection */
306 frdrawsel(&l->f, frptofchar(&l->f, l->f.p0), l->f.p0, fp0, 0);
308 if(fp1 > l->f.p1){
309 /* extend selection forwards */
310 frdrawsel(&l->f, frptofchar(&l->f, l->f.p1), l->f.p1, fp1, 1);
311 }else if(fp1 < l->f.p1){
312 /* trim last part of selection */
313 frdrawsel(&l->f, frptofchar(&l->f, fp1), fp1, l->f.p1, 0);
316 Refresh:
317 l->f.p0 = fp0;
318 l->f.p1 = fp1;
319 if(l->visible==Some)
320 flrefresh(l, l->entire, 0);
323 void
324 flfp0p1(Flayer *l, ulong *pp0, ulong *pp1, int *ticked)
326 long p0 = l->p0-l->origin, p1 = l->p1-l->origin;
328 *ticked = p0 == p1;
329 if(p0 < 0){
330 *ticked = 0;
331 p0 = 0;
333 if(p1 < 0)
334 p1 = 0;
335 if(p0 > l->f.nchars)
336 p0 = l->f.nchars;
337 if(p1 > l->f.nchars){
338 *ticked = 0;
339 p1 = l->f.nchars;
341 *pp0 = p0;
342 *pp1 = p1;
345 Rectangle
346 rscale(Rectangle r, Point old, Point new)
348 r.min.x = r.min.x*new.x/old.x;
349 r.min.y = r.min.y*new.y/old.y;
350 r.max.x = r.max.x*new.x/old.x;
351 r.max.y = r.max.y*new.y/old.y;
352 return r;
355 void
356 flresize(Rectangle dr)
358 int i;
359 Flayer *l;
360 Frame *f;
361 Rectangle r, olDrect;
362 int move;
364 olDrect = lDrect;
365 lDrect = dr;
366 move = 0;
367 /* no moving on rio; must repaint */
368 if(0 && Dx(dr)==Dx(olDrect) && Dy(dr)==Dy(olDrect))
369 move = 1;
370 else
371 draw(screen, lDrect, display->white, nil, ZP);
372 for(i=0; i<nllist; i++){
373 l = llist[i];
374 l->lastsr = ZR;
375 f = &l->f;
376 if(move)
377 r = rectaddpt(rectsubpt(l->entire, olDrect.min), dr.min);
378 else{
379 r = rectaddpt(rscale(rectsubpt(l->entire, olDrect.min),
380 subpt(olDrect.max, olDrect.min),
381 subpt(dr.max, dr.min)), dr.min);
382 if(l->visible==Some && f->b){
383 freeimage(f->b);
384 frclear(f, 0);
386 f->b = 0;
387 if(l->visible!=None)
388 frclear(f, 0);
390 if(!rectclip(&r, dr))
391 panic("flresize");
392 if(r.max.x-r.min.x<100)
393 r.min.x = dr.min.x;
394 if(r.max.x-r.min.x<100)
395 r.max.x = dr.max.x;
396 if(r.max.y-r.min.y<2*FLMARGIN(l)+f->font->height)
397 r.min.y = dr.min.y;
398 if(r.max.y-r.min.y<2*FLMARGIN(l)+f->font->height)
399 r.max.y = dr.max.y;
400 if(!move)
401 l->visible = None;
402 frsetrects(f, insetrect(flrect(l, r), FLMARGIN(l)), f->b);
403 if(!move && f->b)
404 scrdraw(l, scrtotal(l));
406 newvisibilities(1);
409 int
410 flprepare(Flayer *l)
412 Frame *f;
413 ulong n;
414 Rune *r;
415 int ticked;
417 if(l->visible == None)
418 return 0;
419 f = &l->f;
420 if(f->b == 0){
421 if(l->visible == All)
422 f->b = screen;
423 else if((f->b = allocimage(display, l->entire, screen->chan, 0, 0))==0)
424 return 0;
425 draw(f->b, l->entire, f->cols[BACK], nil, ZP);
426 border(f->b, l->entire, l==llist[0]? FLMARGIN(l) : 1, f->cols[BORD], ZP);
427 n = f->nchars;
428 frinit(f, f->entire, f->font, f->b, 0);
429 f->maxtab = maxtab*stringwidth(f->font, "0");
430 r = (*l->textfn)(l, n, &n);
431 frinsert(f, r, r+n, (ulong)0);
432 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
433 flfp0p1(l, &f->p0, &f->p1, &ticked);
434 if(f->p0 != f->p1 || ticked)
435 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
436 l->lastsr = ZR;
437 scrdraw(l, scrtotal(l));
439 return 1;
442 static int somevis, someinvis, justvis;
444 Vis
445 visibility(Flayer *l)
447 somevis = someinvis = 0;
448 justvis = 1;
449 flrefresh(l, l->entire, 0);
450 justvis = 0;
451 if(somevis==0)
452 return None;
453 if(someinvis==0)
454 return All;
455 return Some;
458 void
459 flrefresh(Flayer *l, Rectangle r, int i)
461 Flayer *t;
462 Rectangle s;
464 Top:
465 if((t=llist[i++]) == l){
466 if(!justvis)
467 draw(screen, r, l->f.b, nil, r.min);
468 somevis = 1;
469 }else{
470 if(!rectXrect(t->entire, r))
471 goto Top; /* avoid stacking unnecessarily */
472 if(t->entire.min.x>r.min.x){
473 s = r;
474 s.max.x = t->entire.min.x;
475 flrefresh(l, s, i);
476 r.min.x = t->entire.min.x;
478 if(t->entire.min.y>r.min.y){
479 s = r;
480 s.max.y = t->entire.min.y;
481 flrefresh(l, s, i);
482 r.min.y = t->entire.min.y;
484 if(t->entire.max.x<r.max.x){
485 s = r;
486 s.min.x = t->entire.max.x;
487 flrefresh(l, s, i);
488 r.max.x = t->entire.max.x;
490 if(t->entire.max.y<r.max.y){
491 s = r;
492 s.min.y = t->entire.max.y;
493 flrefresh(l, s, i);
494 r.max.y = t->entire.max.y;
496 /* remaining piece of r is blocked by t; forget about it */
497 someinvis = 1;
501 int
502 flscale(Flayer *l, int n)
504 if(l == nil)
505 return n;
506 return scalesize(l->f.display, n);