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