plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

cols.c (12630B)


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