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->taglines+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, 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, TRUE);
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, TRUE);
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, TRUE);
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, i==c->nw-1);
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, i==c->nw-1);
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, TRUE);
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, TRUE);
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]->taglines-1 + 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->taglines-1+w->maxlines), onl/2), tot);
327 if(nnl < w->taglines-1+w->maxlines)
328 nnl = (w->taglines-1+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->tagtop);
359 if(nl[j])
360 r.max.y += 1 + nl[j]*v->body.font->height;
361 r.min.y = winresize(v, r, c->safe, FALSE);
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->tagtop);
372 if(nl[j])
373 r.min.y -= 1 + nl[j]*v->body.font->height;
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 r.max.y = y2;
382 h = w->body.font->height;
383 if(Dy(r) < Dy(w->tagtop)+1+h+Border)
384 r.max.y = r.min.y + Dy(w->tagtop)+1+h+Border;
385 /* draw window */
386 r.max.y = winresize(w, r, c->safe, TRUE);
387 if(i < c->nw-1){
388 r.min.y = r.max.y;
389 r.max.y += Border;
390 draw(screen, r, display->black, nil, ZP);
391 for(j=i+1; j<c->nw; j++)
392 ny[j] -= (y2-r.max.y);
393 }
394 /* pack everyone below */
395 y1 = r.max.y;
396 for(j=i+1; j<c->nw; j++){
397 v = c->w[j];
398 r = v->r;
399 r.min.y = y1;
400 r.max.y = y1+Dy(v->tagtop);
401 if(nl[j])
402 r.max.y += 1 + nl[j]*v->body.font->height;
403 winresize(v, r, c->safe, j==c->nw-1);
404 if(j < c->nw-1){ /* no border on last window */
405 r.min.y = v->r.max.y;
406 r.max.y += Border;
407 draw(screen, r, display->black, nil, ZP);
408 }
409 y1 = r.max.y;
410 }
411 r = w->r;
412 r.min.y = y1;
413 r.max.y = c->r.max.y;
414 draw(screen, r, textcols[BACK], nil, ZP);
415 free(nl);
416 free(ny);
417 c->safe = TRUE;
418 winmousebut(w);
419 }
420
421 void
coldragwin(Column * c,Window * w,int but)422 coldragwin(Column *c, Window *w, int but)
423 {
424 Rectangle r;
425 int i, b;
426 Point p, op;
427 Window *v;
428 Column *nc;
429
430 clearmouse();
431 setcursor(mousectl, &boxcursor);
432 b = mouse->buttons;
433 op = mouse->xy;
434 while(mouse->buttons == b)
435 readmouse(mousectl);
436 setcursor(mousectl, nil);
437 if(mouse->buttons){
438 while(mouse->buttons)
439 readmouse(mousectl);
440 return;
441 }
442
443 for(i=0; i<c->nw; i++)
444 if(c->w[i] == w)
445 goto Found;
446 error("can't find window");
447
448 Found:
449 if(w->tagexpand) /* force recomputation of window tag size */
450 w->taglines = 1;
451 p = mouse->xy;
452 if(abs(p.x-op.x)<5 && abs(p.y-op.y)<5){
453 colgrow(c, w, but);
454 winmousebut(w);
455 return;
456 }
457 /* is it a flick to the right? */
458 if(abs(p.y-op.y)<10 && p.x>op.x+30 && rowwhichcol(c->row, p)==c)
459 p.x = op.x+Dx(w->r); /* yes: toss to next column */
460 nc = rowwhichcol(c->row, p);
461 if(nc!=nil && nc!=c){
462 colclose(c, w, FALSE);
463 coladd(nc, w, nil, p.y);
464 winmousebut(w);
465 return;
466 }
467 if(i==0 && c->nw==1)
468 return; /* can't do it */
469 if((i>0 && p.y<c->w[i-1]->r.min.y) || (i<c->nw-1 && p.y>w->r.max.y)
470 || (i==0 && p.y>w->r.max.y)){
471 /* shuffle */
472 colclose(c, w, FALSE);
473 coladd(c, w, nil, p.y);
474 winmousebut(w);
475 return;
476 }
477 if(i == 0)
478 return;
479 v = c->w[i-1];
480 if(p.y < v->tagtop.max.y)
481 p.y = v->tagtop.max.y;
482 if(p.y > w->r.max.y-Dy(w->tagtop)-Border)
483 p.y = w->r.max.y-Dy(w->tagtop)-Border;
484 r = v->r;
485 r.max.y = p.y;
486 if(r.max.y > v->body.r.min.y){
487 r.max.y -= (r.max.y-v->body.r.min.y)%v->body.font->height;
488 if(v->body.r.min.y == v->body.r.max.y)
489 r.max.y++;
490 }
491 r.min.y = winresize(v, r, c->safe, FALSE);
492 r.max.y = r.min.y+Border;
493 draw(screen, r, display->black, nil, ZP);
494 r.min.y = r.max.y;
495 if(i == c->nw-1)
496 r.max.y = c->r.max.y;
497 else
498 r.max.y = c->w[i+1]->r.min.y-Border;
499 winresize(w, r, c->safe, TRUE);
500 c->safe = TRUE;
501 winmousebut(w);
502 }
503
504 Text*
colwhich(Column * c,Point p)505 colwhich(Column *c, Point p)
506 {
507 int i;
508 Window *w;
509
510 if(!ptinrect(p, c->r))
511 return nil;
512 if(ptinrect(p, c->tag.all))
513 return &c->tag;
514 for(i=0; i<c->nw; i++){
515 w = c->w[i];
516 if(ptinrect(p, w->r)){
517 if(ptinrect(p, w->tagtop) || ptinrect(p, w->tag.all))
518 return &w->tag;
519 return &w->body;
520 }
521 /* scrollr drops below w->r on low windows */
522 if(ptinrect(p, w->body.scrollr))
523 return &w->body;
524 }
525 return nil;
526 }
527
528 int
colclean(Column * c)529 colclean(Column *c)
530 {
531 int i, clean;
532
533 clean = TRUE;
534 for(i=0; i<c->nw; i++)
535 clean &= winclean(c->w[i], TRUE);
536 return clean;
537 }
538