acme

thing1's custom acme build!
Log | Files | Refs | README

cols.c (12719B)


      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 <libsec.h>
     12 #include "dat.h"
     13 #include "fns.h"
     14 
     15 static Rune Lheader[] = {
     16 	'N', 'e', 'w', ' ',
     17 	'C', 'u', 't', ' ',
     18 	'P', 'a', 's', 't', 'e', ' ',
     19 	'S', 'n', 'a', 'r', 'f', ' ',
     20 	'S', 'o', 'r', 't', ' ',
     21 	'Z', 'e', 'r', 'o', 'x', ' ',
     22 	'D', 'e', 'l', 'c', 'o', 'l', ' ',
     23 	'w', 'i', 'n', ' ',
     24 	0
     25 };
     26 
     27 void
     28 colinit(Column *c, Rectangle r)
     29 {
     30 	Rectangle r1;
     31 	Text *t;
     32 
     33 	draw(screen, r, display->white, nil, ZP);
     34 	c->r = r;
     35 	c->w = nil;
     36 	c->nw = 0;
     37 	t = &c->tag;
     38 	t->w = nil;
     39 	t->col = c;
     40 	r1 = r;
     41 	r1.max.y = r1.min.y + font->height;
     42 	textinit(t, fileaddtext(nil, t), r1, &reffont, tagcols);
     43 	t->what = Columntag;
     44 	r1.min.y = r1.max.y;
     45 	r1.max.y += Border;
     46 	draw(screen, r1, display->black, nil, ZP);
     47 	//textinsert(t, 0, Lheader, 38, TRUE);
     48 	textinsert(t, 0, Lheader, sizeof(Lheader) / sizeof(Rune), TRUE);
     49 	textsetselect(t, t->file->b.nc, t->file->b.nc);
     50 	draw(screen, t->scrollr, colbutton, nil, colbutton->r.min);
     51 	c->safe = TRUE;
     52 }
     53 
     54 Window*
     55 coladd(Column *c, Window *w, Window *clone, int y)
     56 {
     57 	Rectangle r, r1;
     58 	Window *v;
     59 	int i, j, minht, ymax, buggered;
     60 
     61 	v = nil;
     62 	r = c->r;
     63 	r.min.y = c->tag.fr.r.max.y+Border;
     64 	if(y<r.min.y && c->nw>0){	/* steal half of last window by default */
     65 		v = c->w[c->nw-1];
     66 		y = v->body.fr.r.min.y+Dy(v->body.fr.r)/2;
     67 	}
     68 	/* look for window we'll land on */
     69 	for(i=0; i<c->nw; i++){
     70 		v = c->w[i];
     71 		if(y < v->r.max.y)
     72 			break;
     73 	}
     74 	buggered = 0;
     75 	if(c->nw > 0){
     76 		if(i < c->nw)
     77 			i++;	/* new window will go after v */
     78 		/*
     79 		 * if landing window (v) is too small, grow it first.
     80 		 */
     81 		minht = v->tag.fr.font->height+Border+1;
     82 		j = 0;
     83 		while(!c->safe || v->body.fr.maxlines<=3 || Dy(v->body.all) <= minht){
     84 			if(++j > 10){
     85 				buggered = 1;	/* too many windows in column */
     86 				break;
     87 			}
     88 			colgrow(c, v, 1);
     89 		}
     90 
     91 		/*
     92 		 * figure out where to split v to make room for w
     93 		 */
     94 
     95 		/* new window stops where next window begins */
     96 		if(i < c->nw)
     97 			ymax = c->w[i]->r.min.y-Border;
     98 		else
     99 			ymax = c->r.max.y;
    100 
    101 		/* new window must start after v's tag ends */
    102 		y = max(y, v->tagtop.max.y+Border);
    103 
    104 		/* new window must start early enough to end before ymax */
    105 		y = min(y, ymax - minht);
    106 
    107 		/* if y is too small, too many windows in column */
    108 		if(y < v->tagtop.max.y+Border)
    109 			buggered = 1;
    110 
    111 		/*
    112 		 * resize & redraw v
    113 		 */
    114 		r = v->r;
    115 		r.max.y = ymax;
    116 		draw(screen, r, textcols[BACK], nil, ZP);
    117 		r1 = r;
    118 		y = min(y, ymax-(v->tag.fr.font->height*v->taglines+v->body.fr.font->height+Border+1));
    119 		r1.max.y = min(y, v->body.fr.r.min.y+v->body.fr.nlines*v->body.fr.font->height);
    120 		r1.min.y = winresize(v, r1, FALSE, FALSE);
    121 		r1.max.y = r1.min.y+Border;
    122 		draw(screen, r1, display->black, nil, ZP);
    123 
    124 		/*
    125 		 * leave r with w's coordinates
    126 		 */
    127 		r.min.y = r1.max.y;
    128 	}
    129 	if(w == nil){
    130 		w = emalloc(sizeof(Window));
    131 		w->col = c;
    132 		draw(screen, r, textcols[BACK], nil, ZP);
    133 		wininit(w, clone, r);
    134 	}else{
    135 		w->col = c;
    136 		winresize(w, r, FALSE, TRUE);
    137 	}
    138 	w->tag.col = c;
    139 	w->tag.row = c->row;
    140 	w->body.col = c;
    141 	w->body.row = c->row;
    142 	c->w = realloc(c->w, (c->nw+1)*sizeof(Window*));
    143 	memmove(c->w+i+1, c->w+i, (c->nw-i)*sizeof(Window*));
    144 	c->nw++;
    145 	c->w[i] = w;
    146 	c->safe = TRUE;
    147 
    148 	/* if there were too many windows, redraw the whole column */
    149 	if(buggered)
    150 		colresize(c, c->r);
    151 
    152 	savemouse(w);
    153 	/* near the button, but in the body */
    154 	/* don't move the mouse to the new window if a mouse button is depressed */
    155 	if(!mousectl->m.buttons)
    156 		moveto(mousectl, addpt(w->tag.scrollr.max, Pt(3, 3)));
    157 
    158 	barttext = &w->body;
    159 	return w;
    160 }
    161 
    162 void
    163 colclose(Column *c, Window *w, int dofree)
    164 {
    165 	Rectangle r;
    166 	int i, didmouse, up;
    167 
    168 	/* w is locked */
    169 	if(!c->safe)
    170 		colgrow(c, w, 1);
    171 	for(i=0; i<c->nw; i++)
    172 		if(c->w[i] == w)
    173 			goto Found;
    174 	error("can't find window");
    175   Found:
    176 	r = w->r;
    177 	w->tag.col = nil;
    178 	w->body.col = nil;
    179 	w->col = nil;
    180 	didmouse = restoremouse(w);
    181 	if(dofree){
    182 		windelete(w);
    183 		winclose(w);
    184 	}
    185 	c->nw--;
    186 	memmove(c->w+i, c->w+i+1, (c->nw-i)*sizeof(Window*));
    187 	c->w = realloc(c->w, c->nw*sizeof(Window*));
    188 	if(c->nw == 0){
    189 		draw(screen, r, display->white, nil, ZP);
    190 		return;
    191 	}
    192 	up = 0;
    193 	if(i == c->nw){		/* extend last window down */
    194 		w = c->w[i-1];
    195 		r.min.y = w->r.min.y;
    196 		r.max.y = c->r.max.y;
    197 	}else{			/* extend next window up */
    198 		up = 1;
    199 		w = c->w[i];
    200 		r.max.y = w->r.max.y;
    201 	}
    202 	draw(screen, r, textcols[BACK], nil, ZP);
    203 	if(c->safe) {
    204 		if(!didmouse && up)
    205 			w->showdel = TRUE;
    206 		winresize(w, r, FALSE, TRUE);
    207 		if(!didmouse && up)
    208 			movetodel(w);
    209 	}
    210 }
    211 
    212 void
    213 colcloseall(Column *c)
    214 {
    215 	int i;
    216 	Window *w;
    217 
    218 	if(c == activecol)
    219 		activecol = nil;
    220 	textclose(&c->tag);
    221 	for(i=0; i<c->nw; i++){
    222 		w = c->w[i];
    223 		winclose(w);
    224 	}
    225 	c->nw = 0;
    226 	free(c->w);
    227 	free(c);
    228 	clearmouse();
    229 }
    230 
    231 void
    232 colmousebut(Column *c)
    233 {
    234 	moveto(mousectl, divpt(addpt(c->tag.scrollr.min, c->tag.scrollr.max), 2));
    235 }
    236 
    237 void
    238 colresize(Column *c, Rectangle r)
    239 {
    240 	int i, old, new;
    241 	Rectangle r1, r2;
    242 	Window *w;
    243 
    244 	clearmouse();
    245 	r1 = r;
    246 	r1.max.y = r1.min.y + c->tag.fr.font->height;
    247 	textresize(&c->tag, r1, TRUE);
    248 	draw(screen, c->tag.scrollr, colbutton, nil, colbutton->r.min);
    249 	r1.min.y = r1.max.y;
    250 	r1.max.y += Border;
    251 	draw(screen, r1, display->black, nil, ZP);
    252 	r1.max.y = r.max.y;
    253 	new = Dy(r) - c->nw*(Border + font->height);
    254 	old = Dy(c->r) - c->nw*(Border + font->height);
    255 	for(i=0; i<c->nw; i++){
    256 		w = c->w[i];
    257 		w->maxlines = 0;
    258 		if(i == c->nw-1)
    259 			r1.max.y = r.max.y;
    260 		else{
    261 			r1.max.y = r1.min.y;
    262 			if(new > 0 && old > 0 && Dy(w->r) > Border+font->height){
    263 				r1.max.y += (Dy(w->r)-Border-font->height)*new/old + Border + font->height;
    264 			}
    265 		}
    266 		r1.max.y = max(r1.max.y, r1.min.y + Border+font->height);
    267 		r2 = r1;
    268 		r2.max.y = r2.min.y+Border;
    269 		draw(screen, r2, display->black, nil, ZP);
    270 		r1.min.y = r2.max.y;
    271 		r1.min.y = winresize(w, r1, FALSE, i==c->nw-1);
    272 	}
    273 	c->r = r;
    274 }
    275 
    276 static
    277 int
    278 colcmp(const void *a, const void *b)
    279 {
    280 	Rune *r1, *r2;
    281 	int i, nr1, nr2;
    282 
    283 	r1 = (*(Window**)a)->body.file->name;
    284 	nr1 = (*(Window**)a)->body.file->nname;
    285 	r2 = (*(Window**)b)->body.file->name;
    286 	nr2 = (*(Window**)b)->body.file->nname;
    287 	for(i=0; i<nr1 && i<nr2; i++){
    288 		if(*r1 != *r2)
    289 			return *r1-*r2;
    290 		r1++;
    291 		r2++;
    292 	}
    293 	return nr1-nr2;
    294 }
    295 
    296 void
    297 colsort(Column *c)
    298 {
    299 	int i, y;
    300 	Rectangle r, r1, *rp;
    301 	Window **wp, *w;
    302 
    303 	if(c->nw == 0)
    304 		return;
    305 	clearmouse();
    306 	rp = emalloc(c->nw*sizeof(Rectangle));
    307 	wp = emalloc(c->nw*sizeof(Window*));
    308 	memmove(wp, c->w, c->nw*sizeof(Window*));
    309 	qsort(wp, c->nw, sizeof(Window*), colcmp);
    310 	for(i=0; i<c->nw; i++)
    311 		rp[i] = wp[i]->r;
    312 	r = c->r;
    313 	r.min.y = c->tag.fr.r.max.y;
    314 	draw(screen, r, textcols[BACK], nil, ZP);
    315 	y = r.min.y;
    316 	for(i=0; i<c->nw; i++){
    317 		w = wp[i];
    318 		r.min.y = y;
    319 		if(i == c->nw-1)
    320 			r.max.y = c->r.max.y;
    321 		else
    322 			r.max.y = r.min.y+Dy(w->r)+Border;
    323 		r1 = r;
    324 		r1.max.y = r1.min.y+Border;
    325 		draw(screen, r1, display->black, nil, ZP);
    326 		r.min.y = r1.max.y;
    327 		y = winresize(w, r, FALSE, i==c->nw-1);
    328 	}
    329 	free(rp);
    330 	free(c->w);
    331 	c->w = wp;
    332 }
    333 
    334 void
    335 colgrow(Column *c, Window *w, int but)
    336 {
    337 	Rectangle r, cr;
    338 	int i, j, k, l, y1, y2, *nl, *ny, tot, nnl, onl, dnl, h;
    339 	Window *v;
    340 
    341 	for(i=0; i<c->nw; i++)
    342 		if(c->w[i] == w)
    343 			goto Found;
    344 	error("can't find window");
    345 
    346   Found:
    347 	cr = c->r;
    348 	if(but < 0){	/* make sure window fills its own space properly */
    349 		r = w->r;
    350 		if(i==c->nw-1 || c->safe==FALSE)
    351 			r.max.y = cr.max.y;
    352 		else
    353 			r.max.y = c->w[i+1]->r.min.y - Border;
    354 		winresize(w, r, FALSE, TRUE);
    355 		return;
    356 	}
    357 	cr.min.y = c->w[0]->r.min.y;
    358 	if(but == 3){	/* full size */
    359 		if(i != 0){
    360 			v = c->w[0];
    361 			c->w[0] = w;
    362 			c->w[i] = v;
    363 		}
    364 		draw(screen, cr, textcols[BACK], nil, ZP);
    365 		winresize(w, cr, FALSE, TRUE);
    366 		for(i=1; i<c->nw; i++)
    367 			c->w[i]->body.fr.maxlines = 0;
    368 		c->safe = FALSE;
    369 		return;
    370 	}
    371 	/* store old #lines for each window */
    372 	onl = w->body.fr.maxlines;
    373 	nl = emalloc(c->nw * sizeof(int));
    374 	ny = emalloc(c->nw * sizeof(int));
    375 	tot = 0;
    376 	for(j=0; j<c->nw; j++){
    377 		l = c->w[j]->taglines-1 + c->w[j]->body.fr.maxlines;
    378 		nl[j] = l;
    379 		tot += l;
    380 	}
    381 	/* approximate new #lines for this window */
    382 	if(but == 2){	/* as big as can be */
    383 		memset(nl, 0, c->nw * sizeof(int));
    384 		goto Pack;
    385 	}
    386 	nnl = min(onl + max(min(5, w->taglines-1+w->maxlines), onl/2), tot);
    387 	if(nnl < w->taglines-1+w->maxlines)
    388 		nnl = (w->taglines-1+w->maxlines + nnl)/2;
    389 	if(nnl == 0)
    390 		nnl = 2;
    391 	dnl = nnl - onl;
    392 	/* compute new #lines for each window */
    393 	for(k=1; k<c->nw; k++){
    394 		/* prune from later window */
    395 		j = i+k;
    396 		if(j<c->nw && nl[j]){
    397 			l = min(dnl, max(1, nl[j]/2));
    398 			nl[j] -= l;
    399 			nl[i] += l;
    400 			dnl -= l;
    401 		}
    402 		/* prune from earlier window */
    403 		j = i-k;
    404 		if(j>=0 && nl[j]){
    405 			l = min(dnl, max(1, nl[j]/2));
    406 			nl[j] -= l;
    407 			nl[i] += l;
    408 			dnl -= l;
    409 		}
    410 	}
    411     Pack:
    412 	/* pack everyone above */
    413 	y1 = cr.min.y;
    414 	for(j=0; j<i; j++){
    415 		v = c->w[j];
    416 		r = v->r;
    417 		r.min.y = y1;
    418 		r.max.y = y1+Dy(v->tagtop);
    419 		if(nl[j])
    420 			r.max.y += 1 + nl[j]*v->body.fr.font->height;
    421 		r.min.y = winresize(v, r, c->safe, FALSE);
    422 		r.max.y = r.min.y + Border;
    423 		draw(screen, r, display->black, nil, ZP);
    424 		y1 = r.max.y;
    425 	}
    426 	/* scan to see new size of everyone below */
    427 	y2 = c->r.max.y;
    428 	for(j=c->nw-1; j>i; j--){
    429 		v = c->w[j];
    430 		r = v->r;
    431 		r.min.y = y2-Dy(v->tagtop);
    432 		if(nl[j])
    433 			r.min.y -= 1 + nl[j]*v->body.fr.font->height;
    434 		r.min.y -= Border;
    435 		ny[j] = r.min.y;
    436 		y2 = r.min.y;
    437 	}
    438 	/* compute new size of window */
    439 	r = w->r;
    440 	r.min.y = y1;
    441 	r.max.y = y2;
    442 	h = w->body.fr.font->height;
    443 	if(Dy(r) < Dy(w->tagtop)+1+h+Border)
    444 		r.max.y = r.min.y + Dy(w->tagtop)+1+h+Border;
    445 	/* draw window */
    446 	r.max.y = winresize(w, r, c->safe, TRUE);
    447 	if(i < c->nw-1){
    448 		r.min.y = r.max.y;
    449 		r.max.y += Border;
    450 		draw(screen, r, display->black, nil, ZP);
    451 		for(j=i+1; j<c->nw; j++)
    452 			ny[j] -= (y2-r.max.y);
    453 	}
    454 	/* pack everyone below */
    455 	y1 = r.max.y;
    456 	for(j=i+1; j<c->nw; j++){
    457 		v = c->w[j];
    458 		r = v->r;
    459 		r.min.y = y1;
    460 		r.max.y = y1+Dy(v->tagtop);
    461 		if(nl[j])
    462 			r.max.y += 1 + nl[j]*v->body.fr.font->height;
    463 		y1 = winresize(v, r, c->safe, j==c->nw-1);
    464 		if(j < c->nw-1){	/* no border on last window */
    465 			r.min.y = y1;
    466 			r.max.y += Border;
    467 			draw(screen, r, display->black, nil, ZP);
    468 			y1 = r.max.y;
    469 		}
    470 	}
    471 	free(nl);
    472 	free(ny);
    473 	c->safe = TRUE;
    474 	winmousebut(w);
    475 }
    476 
    477 void
    478 coldragwin(Column *c, Window *w, int but)
    479 {
    480 	Rectangle r;
    481 	int i, b;
    482 	Point p, op;
    483 	Window *v;
    484 	Column *nc;
    485 
    486 	clearmouse();
    487 	setcursor2(mousectl, &boxcursor, &boxcursor2);
    488 	b = mouse->buttons;
    489 	op = mouse->xy;
    490 	while(mouse->buttons == b)
    491 		readmouse(mousectl);
    492 	setcursor(mousectl, nil);
    493 	if(mouse->buttons){
    494 		while(mouse->buttons)
    495 			readmouse(mousectl);
    496 		return;
    497 	}
    498 
    499 	for(i=0; i<c->nw; i++)
    500 		if(c->w[i] == w)
    501 			goto Found;
    502 	error("can't find window");
    503 
    504   Found:
    505 	if(w->tagexpand)	/* force recomputation of window tag size */
    506 		w->taglines = 1;
    507 	p = mouse->xy;
    508 	if(abs(p.x-op.x)<5 && abs(p.y-op.y)<5){
    509 		colgrow(c, w, but);
    510 		winmousebut(w);
    511 		return;
    512 	}
    513 	/* is it a flick to the right? */
    514 	if(abs(p.y-op.y)<10 && p.x>op.x+30 && rowwhichcol(c->row, p)==c)
    515 		p.x = op.x+Dx(w->r);	/* yes: toss to next column */
    516 	nc = rowwhichcol(c->row, p);
    517 	if(nc!=nil && nc!=c){
    518 		colclose(c, w, FALSE);
    519 		coladd(nc, w, nil, p.y);
    520 		winmousebut(w);
    521 		return;
    522 	}
    523 	if(i==0 && c->nw==1)
    524 		return;			/* can't do it */
    525 	if((i>0 && p.y<c->w[i-1]->r.min.y) || (i<c->nw-1 && p.y>w->r.max.y)
    526 	|| (i==0 && p.y>w->r.max.y)){
    527 		/* shuffle */
    528 		colclose(c, w, FALSE);
    529 		coladd(c, w, nil, p.y);
    530 		winmousebut(w);
    531 		return;
    532 	}
    533 	if(i == 0)
    534 		return;
    535 	v = c->w[i-1];
    536 	if(p.y < v->tagtop.max.y)
    537 		p.y = v->tagtop.max.y;
    538 	if(p.y > w->r.max.y-Dy(w->tagtop)-Border)
    539 		p.y = w->r.max.y-Dy(w->tagtop)-Border;
    540 	r = v->r;
    541 	r.max.y = p.y;
    542 	if(r.max.y > v->body.fr.r.min.y){
    543 		r.max.y -= (r.max.y-v->body.fr.r.min.y)%v->body.fr.font->height;
    544 		if(v->body.fr.r.min.y == v->body.fr.r.max.y)
    545 			r.max.y++;
    546 	}
    547 	r.min.y = winresize(v, r, c->safe, FALSE);
    548 	r.max.y = r.min.y+Border;
    549 	draw(screen, r, display->black, nil, ZP);
    550 	r.min.y = r.max.y;
    551 	if(i == c->nw-1)
    552 		r.max.y = c->r.max.y;
    553 	else
    554 		r.max.y = c->w[i+1]->r.min.y-Border;
    555 	winresize(w, r, c->safe, TRUE);
    556 	c->safe = TRUE;
    557 	winmousebut(w);
    558 }
    559 
    560 Text*
    561 colwhich(Column *c, Point p)
    562 {
    563 	int i;
    564 	Window *w;
    565 
    566 	if(!ptinrect(p, c->r))
    567 		return nil;
    568 	if(ptinrect(p, c->tag.all))
    569 		return &c->tag;
    570 	for(i=0; i<c->nw; i++){
    571 		w = c->w[i];
    572 		if(ptinrect(p, w->r)){
    573 			if(ptinrect(p, w->tagtop) || ptinrect(p, w->tag.all))
    574 				return &w->tag;
    575 			/* exclude partial line at bottom */
    576 			if(p.x >= w->body.scrollr.max.x && p.y >= w->body.fr.r.max.y)
    577 				return nil;
    578 			return &w->body;
    579 		}
    580 	}
    581 	return nil;
    582 }
    583 
    584 int
    585 colclean(Column *c)
    586 {
    587 	int i, clean;
    588 
    589 	clean = TRUE;
    590 	for(i=0; i<c->nw; i++)
    591 		clean &= winclean(c->w[i], TRUE);
    592 	return clean;
    593 }