xref: /plan9/sys/src/cmd/acme/cols.c (revision 588d0145e19f8596f2f4442d05dd8a9eda147983)
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