]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/abaco/cols.c
cwfs: fix listen filedescriptor leaks
[plan9front.git] / sys / src / cmd / abaco / cols.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 void
16 colinit(Column *c, Rectangle r)
17 {
18         Rectangle r1;
19         Text *t;
20
21         draw(screen, r, display->white, nil, ZP);
22         c->r = r;
23         c->w = nil;
24         c->nw = 0;
25         t = &c->tag;
26         t->w = nil;
27         t->col = c;
28         r1 = r;
29         r1.max.y = r1.min.y + font->height;
30         textinit(t, screen, r1, font, tagcols);
31         t->what = Columntag;
32         r1.min.y = r1.max.y;
33         r1.max.y += Border;
34         draw(screen, r1, display->black, nil, ZP);
35         textinsert(t, 0, L"New Cut Paste Snarf Sort Delcol ", 32);
36         textsetselect(t, t->rs.nr, t->rs.nr);
37         draw(screen, t->scrollr, colbutton, nil, colbutton->r.min);
38         c->safe = TRUE;
39 }
40
41 Window*
42 coladd(Column *c, Window *w, Window *clone, int y)
43 {
44         Rectangle r, r1;
45         Window *v;
46         int i, t;
47
48         v = nil;
49         r = c->r;
50         r.min.y = c->tag.r.max.y+Border;
51         if(y<r.min.y && c->nw>0){       /* steal half of last window by default */
52                 v = c->w[c->nw-1];
53                 y = v->page.all.min.y+Dy(v->page.all)/2;
54         }
55         /* look for window we'll land on */
56         for(i=0; i<c->nw; i++){
57                 v = c->w[i];
58                 if(y < v->r.max.y)
59                         break;
60         }
61         if(c->nw > 0){
62                 if(i < c->nw)
63                         i++;    /* new window will go after v */
64                 /*
65                  * if v's too small, grow it first.
66                  */
67                 if(!c->safe || Dy(v->page.all)<=3 ){
68                         colgrow(c, v, 1);
69                         y = v->page.all.min.y+Dy(v->page.all)/2;
70                 }
71                 r = v->r;
72                 if(i == c->nw)
73                         t = c->r.max.y;
74                 else
75                         t = c->w[i]->r.min.y-Border;
76                 r.max.y = t;
77                 r1 = r;
78                 y = min(y, t-(Dy(v->r)-Dy(v->page.all)));
79                 r1.max.y = min(y, v->page.all.min.y+Dy(v->page.all));
80                 r1.min.y = winresize(v, r1, FALSE);
81                 r1.max.y = r1.min.y+Border;
82                 draw(screen, r1, display->black, nil, ZP);
83                 r.min.y = r1.max.y;
84         }
85         if(w == nil){
86                 w = emalloc(sizeof(Window));
87                 w->col = c;
88                 wininit(w, clone, r);
89         }else{
90                 w->col = c;
91                 winresize(w, r, FALSE);
92         }
93         w->tag.col = c;
94         w->tag.row = c->row;
95         w->url.col = c;
96         w->url.row = c->row;
97         w->page.col = c;
98         w->page.row = c->row;
99         w->status.col = c;
100         w->status.row = c->row;
101         c->w = realloc(c->w, (c->nw+1)*sizeof(Window*));
102         memmove(c->w+i+1, c->w+i, (c->nw-i)*sizeof(Window*));
103         c->nw++;
104         c->w[i] = w;
105         savemouse(w);
106         /* near but not on the button */
107         moveto(mousectl, addpt(w->tag.scrollr.max, Pt(3, 3)));
108         c->safe = TRUE;
109         return w;
110 }
111
112 void
113 colclose(Column *c, Window *w, int dofree)
114 {
115         Rectangle r;
116         int i;
117
118         /* w is locked */
119         if(!c->safe)
120                 colgrow(c, w, 1);
121         for(i=0; i<c->nw; i++)
122                 if(c->w[i] == w)
123                         goto Found;
124         error("can't find window");
125   Found:
126         r = w->r;
127         w->tag.col = nil;
128         w->url.col = nil;
129         w->page.col = nil;
130         w->col = nil;
131         restoremouse(w);
132         if(dofree)
133                 winclose(w);
134         memmove(c->w+i, c->w+i+1, (c->nw-i)*sizeof(Window*));
135         c->nw--;
136         c->w = realloc(c->w, c->nw*sizeof(Window*));
137         if(c->nw == 0){
138                 draw(screen, r, display->white, nil, ZP);
139                 return;
140         }
141         if(i == c->nw){         /* extend last window down */
142                 w = c->w[i-1];
143                 r.min.y = w->r.min.y;
144                 r.max.y = c->r.max.y;
145         }else{                  /* extend next window up */
146                 w = c->w[i];
147                 r.max.y = w->r.max.y;
148         }
149         if(c->safe)
150                 winresize(w, r, FALSE);
151 }
152
153 void
154 colcloseall(Column *c)
155 {
156         int i;
157         Window *w;
158
159         if(c == activecol)
160                 activecol = nil;
161         if(seltext && c==seltext->col)
162                 seltext = nil;
163         textclose(&c->tag);
164         for(i=0; i<c->nw; i++){
165                 w = c->w[i];
166                 w->tag.col = nil;
167                 w->url.col = nil;
168                 w->page.col = nil;
169                 w->col = nil;
170                 winclose(w);
171         }
172         c->nw = 0;
173         free(c->w);
174         free(c);
175         clearmouse();
176 }
177
178 void
179 colmousebut(Column *c)
180 {
181         moveto(mousectl, divpt(addpt(c->tag.scrollr.min, c->tag.scrollr.max), 2));
182 }
183
184 void
185 colresize(Column *c, Rectangle r)
186 {
187         int i;
188         Rectangle r1, r2;
189         Window *w;
190
191         clearmouse();
192         r1 = r;
193         r1.max.y = r1.min.y + c->tag.font->height;
194         textresize(&c->tag, screen, r1);
195         draw(screen, c->tag.scrollr, colbutton, nil, colbutton->r.min);
196         r1.min.y = r1.max.y;
197         r1.max.y += Border;
198         draw(screen, r1, display->black, nil, ZP);
199         r1.max.y = r.max.y;
200         for(i=0; i<c->nw; i++){
201                 w = c->w[i];
202                 if(i == c->nw-1)
203                         r1.max.y = r.max.y;
204                 else
205                         r1.max.y = r1.min.y+(Dy(w->r)+Border)*Dy(r)/Dy(c->r);
206                 r2 = r1;
207                 r2.max.y = r2.min.y+Border;
208                 draw(screen, r2, display->black, nil, ZP);
209                 r1.min.y = r2.max.y;
210                 r1.min.y = winresize(w, r1, FALSE);
211         }
212         c->r = r;
213 }
214
215 static
216 int
217 colcmp(void *a, void *b)
218 {
219         Rune *r1, *r2;
220         int i, nr1, nr2;
221
222         r1 = (*(Window**)a)->page.title.r;
223         nr1 = (*(Window**)a)->page.title.nr;
224         r2 = (*(Window**)b)->page.title.r;
225         nr2 = (*(Window**)b)->page.title.nr;
226         for(i=0; i<nr1 && i<nr2; i++){
227                 if(*r1 != *r2)
228                         return *r1-*r2;
229                 r1++;
230                 r2++;
231         }
232         return nr1-nr2;
233 }
234
235 void
236 colsort(Column *c)
237 {
238         int i, y;
239         Rectangle r, r1, *rp;
240         Window **wp, *w;
241
242         if(c->nw == 0)
243                 return;
244         clearmouse();
245         rp = emalloc(c->nw*sizeof(Rectangle));
246         wp = emalloc(c->nw*sizeof(Window*));
247         memmove(wp, c->w, c->nw*sizeof(Window*));
248         qsort(wp, c->nw, sizeof(Window*), colcmp);
249         for(i=0; i<c->nw; i++)
250                 rp[i] = wp[i]->r;
251         r = c->r;
252         y = c->tag.r.max.y;
253         for(i=0; i<c->nw; i++){
254                 w = wp[i];
255                 r.min.y = y;
256                 if(i == c->nw-1)
257                         r.max.y = c->r.max.y;
258                 else
259                         r.max.y = r.min.y+Dy(w->r)+Border;
260                 r1 = r;
261                 r1.max.y = r1.min.y+Border;
262                 draw(screen, r1, display->black, nil, ZP);
263                 r.min.y = r1.max.y;
264                 y = winresize(w, r, FALSE);
265         }
266         free(rp);
267         free(c->w);
268         c->w = wp;
269 }
270
271 void
272 colgrow(Column *c, Window *w, int but)
273 {
274         Rectangle r, cr;
275         int i, j, k, l, y1, y2, *nl, *ny, tot, nnl, onl, dnl, h, wnl;
276         Window *v;
277
278         for(i=0; i<c->nw; i++)
279                 if(c->w[i] == w)
280                         goto Found;
281         error("can't find window");
282
283   Found:
284         cr = c->r;
285         if(but < 0){    /* make sure window fills its own space properly */
286                 r = w->r;
287                 if(i==c->nw-1 || c->safe==FALSE)
288                         r.max.y = cr.max.y;
289                 else
290                         r.max.y = c->w[i+1]->r.min.y;
291                 winresize(w, r, FALSE);
292                 return;
293         }
294         cr.min.y = c->w[0]->r.min.y;
295         if(but == 3){   /* full size */
296                 if(i != 0){
297                         v = c->w[0];
298                         c->w[0] = w;
299                         c->w[i] = v;
300                 }
301                 for(j=1; j<c->nw; j++)
302                         c->w[j]->page.all = ZR;
303                 winresize(w, cr, FALSE);
304                 c->safe = FALSE;
305                 return;
306         }
307         /* store old #lines for each window */
308         wnl = Dy(w->page.all);
309         onl = wnl;
310         nl = emalloc(c->nw * sizeof(int));
311         ny = emalloc(c->nw * sizeof(int));
312         tot = 0;
313         for(j=0; j<c->nw; j++){
314                 l = Dy(c->w[j]->page.all);
315                 nl[j] = l;
316                 tot += l;
317         }
318         /* approximate new #lines for this window */
319         if(but == 2){   /* as big as can be */
320                 memset(nl, 0, c->nw * sizeof(int));
321                 goto Pack;
322         }
323         nnl = min(onl + max(min(5, wnl), onl/2), tot);
324         if(nnl < wnl)
325                 nnl = (wnl+nnl)/2;
326         if(nnl == 0)
327                 nnl = 2;
328         dnl = nnl - onl;
329         /* compute new #lines for each window */
330         for(k=1; k<c->nw; k++){
331                 /* prune from later window */
332                 j = i+k;
333                 if(j<c->nw && nl[j]){
334                         l = min(dnl, max(1, nl[j]/2));
335                         nl[j] -= l;
336                         nl[i] += l;
337                         dnl -= l;
338                 }
339                 /* prune from earlier window */
340                 j = i-k;
341                 if(j>=0 && nl[j]){
342                         l = min(dnl, max(1, nl[j]/2));
343                         nl[j] -= l;
344                         nl[i] += l;
345                         dnl -= l;
346                 }
347         }
348     Pack:
349         /* pack everyone above */
350         y1 = cr.min.y;
351         for(j=0; j<i; j++){
352                 v = c->w[j];
353                 r = v->r;
354                 r.min.y = y1;
355                 r.max.y = y1+Dy(v->tag.all)+Border+Dy(v->url.all);
356                 if(nl[j])
357                         r.max.y += 1 + nl[j];
358                 if(!c->safe || !eqrect(v->r, r))
359                         winresize(v, r, c->safe);
360
361                 r.min.y = v->r.max.y;
362                 r.max.y += Border;
363                 draw(screen, r, display->black, nil, ZP);
364                 y1 = r.max.y;
365         }
366         /* scan to see new size of everyone below */
367         y2 = c->r.max.y;
368         for(j=c->nw-1; j>i; j--){
369                 v = c->w[j];
370                 r = v->r;
371                 r.min.y = y2-Dy(v->tag.all)-Border-Dy(v->url.all);
372                 if(nl[j])
373                         r.min.y -= 1 + nl[j];
374                 r.min.y -= Border;
375                 ny[j] = r.min.y;
376                 y2 = r.min.y;
377         }
378         /* compute new size of window */
379         r = w->r;
380         r.min.y = y1;
381         if(i == c->nw-1)
382                 r.max.y = c->r.max.y;
383         else{
384                 r.max.y = r.min.y+Dy(w->tag.all)+Border+Dy(w->url.all);
385                 h = font->height;
386                 if(y2-r.max.y >= 2*(1+h+Border)){
387                         r.max.y += 1;
388                         r.max.y += h*((y2-r.max.y)/h);
389                 }
390         }
391         /* draw window */
392         if(!c->safe || !eqrect(w->r, r))
393                 winresize(w, r, c->safe);
394
395         if(i < c->nw-1){
396                 r.min.y = r.max.y;
397                 r.max.y += Border;
398                 draw(screen, r, display->black, nil, ZP);
399                 for(j=i+1; j<c->nw; j++)
400                         ny[j] -= (y2-r.max.y);
401         }
402         /* pack everyone below */
403         y1 = r.max.y;
404         for(j=i+1; j<c->nw; j++){
405                 v = c->w[j];
406                 r = v->r;
407                 r.min.y = y1;
408                 if(j == c->nw-1)
409                         r.max.y = c->r.max.y;
410                 else{
411                         r.max.y = y1+Dy(v->tag.all)+Border+Dy(v->url.all);
412                         if(nl[j])
413                                 r.max.y += 1 + nl[j];
414                 }
415                 if(!c->safe || !eqrect(v->r, r))
416                         winresize(v, r, c->safe);
417                 if(j < c->nw-1){        /* no border on last window */
418                         r.min.y = v->r.max.y;
419                         r.max.y += Border;
420                         draw(screen, r, display->black, nil, ZP);
421                 }
422                 y1 = r.max.y;
423         }
424         free(nl);
425         free(ny);
426         c->safe = TRUE;
427         winmousebut(w);
428 }
429
430 void
431 coldragwin(Column *c, Window *w, int but)
432 {
433         Rectangle r;
434         int i, b;
435         Point p, op;
436         Window *v;
437         Column *nc;
438
439         clearmouse();
440         setcursor(mousectl, &boxcursor);
441         b = mouse->buttons;
442         op = mouse->xy;
443         while(mouse->buttons == b)
444                 readmouse(mousectl);
445         setcursor(mousectl, nil);
446         if(mouse->buttons){
447                 while(mouse->buttons)
448                         readmouse(mousectl);
449                 return;
450         }
451
452         for(i=0; i<c->nw; i++)
453                 if(c->w[i] == w)
454                         goto Found;
455         error("can't find window");
456
457   Found:
458         p = mouse->xy;
459         if(abs(p.x-op.x)<5 && abs(p.y-op.y)<5){
460                 colgrow(c, w, but);
461                 winmousebut(w);
462                 return;
463         }
464         /* is it a flick to the right? */
465         if(abs(p.y-op.y)<10 && p.x>op.x+30 && rowwhichcol(c->row, p)==c)
466                 p.x = op.x+Dx(w->r);    /* yes: toss to next column */
467         nc = rowwhichcol(c->row, p);
468         if(nc!=nil && nc!=c){
469                 colclose(c, w, FALSE);
470                 coladd(nc, w, nil, p.y);
471                 winmousebut(w);
472                 return;
473         }
474         if(i==0 && c->nw==1)
475                 return;                 /* can't do it */
476         if((i>0 && p.y<c->w[i-1]->r.min.y) || (i<c->nw-1 && p.y>w->r.max.y)
477         || (i==0 && p.y>w->r.max.y)){
478                 /* shuffle */
479                 colclose(c, w, FALSE);
480                 coladd(c, w, nil, p.y);
481                 winmousebut(w);
482                 return;
483         }
484         if(i == 0)
485                 return;
486         v = c->w[i-1];
487         if(p.y < v->url.all.max.y)
488                 p.y = v->url.all.max.y;
489         if(p.y > w->r.max.y-Dy(w->tag.all)-Border-Dy(w->url.all))
490                 p.y = w->r.max.y-Dy(w->tag.all)-Border-Dy(w->url.all);
491         r = v->r;
492         r.max.y = p.y;
493         if(r.max.y > v->page.all.min.y){
494                 r.max.y -= (r.max.y-v->page.all.min.y)%font->height;
495                 if(v->page.all.min.y == v->page.all.max.y)
496                         r.max.y++;
497         }
498         if(!eqrect(v->r, r))
499                 winresize(v, r, c->safe);
500         r.min.y = v->r.max.y;
501         r.max.y = r.min.y+Border;
502         draw(screen, r, display->black, nil, ZP);
503         r.min.y = r.max.y;
504         if(i == c->nw-1)
505                 r.max.y = c->r.max.y;
506         else
507                 r.max.y = c->w[i+1]->r.min.y-Border;
508         if(!eqrect(w->r, r))
509                 winresize(w, r, c->safe);
510         c->safe = TRUE;
511         winmousebut(w);
512 }
513
514 Text*
515 colwhich(Column *c, Point p, Rune r, int key)
516 {
517         Window *w;
518         Text *t;
519         int i;
520
521         if(!ptinrect(p, c->r))
522                 return nil;
523         if(ptinrect(p, c->tag.all))
524                 return &c->tag;
525         for(i=0; i<c->nw; i++){
526                 w = c->w[i];
527                 if(ptinrect(p, w->r)){
528                         winlock(w, key ? 'K' : 'M');
529                         if(key)
530                                 t = wintype(w, p, r);
531                         else
532                                 t = winmouse(w, p, r);
533                         winunlock(w);
534                         return t;
535                 }
536         }
537         return nil;
538 }
539
540 int
541 colclean(Column *c)
542 {
543         int i, clean;
544
545         clean = TRUE;
546         for(i=0; i<c->nw; i++)
547                 clean &= winclean(c->w[i], TRUE);
548         return clean;
549 }