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