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