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