xref: /plan9/sys/src/cmd/acme/rows.c (revision 9b943567965ba040fd275927fbe088656eb8ce4f)
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 <bio.h>
11 #include <plumb.h>
12 #include "dat.h"
13 #include "fns.h"
14 
15 void
16 rowinit(Row *row, Rectangle r)
17 {
18 	Rectangle r1;
19 	Text *t;
20 
21 	draw(screen, r, display->white, nil, ZP);
22 	row->r = r;
23 	row->col = nil;
24 	row->ncol = 0;
25 	r1 = r;
26 	r1.max.y = r1.min.y + font->height;
27 	t = &row->tag;
28 	textinit(t, fileaddtext(nil, t), r1, rfget(FALSE, FALSE, FALSE, nil), tagcols);
29 	t->what = Rowtag;
30 	t->row = row;
31 	t->w = nil;
32 	t->col = nil;
33 	r1.min.y = r1.max.y;
34 	r1.max.y += Border;
35 	draw(screen, r1, display->black, nil, ZP);
36 	textinsert(t, 0, L"Newcol Kill Putall Dump Exit ", 29, TRUE);
37 	textsetselect(t, t->file->nc, t->file->nc);
38 }
39 
40 Column*
41 rowadd(Row *row, Column *c, int x)
42 {
43 	Rectangle r, r1;
44 	Column *d;
45 	int i;
46 
47 	d = nil;
48 	r = row->r;
49 	r.min.y = row->tag.r.max.y+Border;
50 	if(x<r.min.x && row->ncol>0){	/*steal 40% of last column by default */
51 		d = row->col[row->ncol-1];
52 		x = d->r.min.x + 3*Dx(d->r)/5;
53 	}
54 	/* look for column we'll land on */
55 	for(i=0; i<row->ncol; i++){
56 		d = row->col[i];
57 		if(x < d->r.max.x)
58 			break;
59 	}
60 	if(row->ncol > 0){
61 		if(i < row->ncol)
62 			i++;	/* new column will go after d */
63 		r = d->r;
64 		if(Dx(r) < 100)
65 			return nil;
66 		draw(screen, r, display->white, nil, ZP);
67 		r1 = r;
68 		r1.max.x = min(x, r.max.x-50);
69 		if(Dx(r1) < 50)
70 			r1.max.x = r1.min.x+50;
71 		colresize(d, r1);
72 		r1.min.x = r1.max.x;
73 		r1.max.x = r1.min.x+Border;
74 		draw(screen, r1, display->black, nil, ZP);
75 		r.min.x = r1.max.x;
76 	}
77 	if(c == nil){
78 		c = emalloc(sizeof(Column));
79 		colinit(c, r);
80 		incref(&reffont);
81 	}else
82 		colresize(c, r);
83 	c->row = row;
84 	c->tag.row = row;
85 	row->col = realloc(row->col, (row->ncol+1)*sizeof(Column*));
86 	memmove(row->col+i+1, row->col+i, (row->ncol-i)*sizeof(Column*));
87 	row->col[i] = c;
88 	row->ncol++;
89 	clearmouse();
90 	return c;
91 }
92 
93 void
94 rowresize(Row *row, Rectangle r)
95 {
96 	int i, dx, odx;
97 	Rectangle r1, r2;
98 	Column *c;
99 
100 	dx = Dx(r);
101 	odx = Dx(row->r);
102 	row->r = r;
103 	r1 = r;
104 	r1.max.y = r1.min.y + font->height;
105 	textresize(&row->tag, r1);
106 	r1.min.y = r1.max.y;
107 	r1.max.y += Border;
108 	draw(screen, r1, display->black, nil, ZP);
109 	r.min.y = r1.max.y;
110 	r1 = r;
111 	r1.max.x = r1.min.x;
112 	for(i=0; i<row->ncol; i++){
113 		c = row->col[i];
114 		r1.min.x = r1.max.x;
115 		if(i == row->ncol-1)
116 			r1.max.x = r.max.x;
117 		else
118 			r1.max.x = r1.min.x+Dx(c->r)*dx/odx;
119 		if(i > 0){
120 			r2 = r1;
121 			r2.max.x = r2.min.x+Border;
122 			draw(screen, r2, display->black, nil, ZP);
123 			r1.min.x = r2.max.x;
124 		}
125 		colresize(c, r1);
126 	}
127 }
128 
129 void
130 rowdragcol(Row *row, Column *c, int)
131 {
132 	Rectangle r;
133 	int i, b, x;
134 	Point p, op;
135 	Column *d;
136 
137 	clearmouse();
138 	setcursor(mousectl, &boxcursor);
139 	b = mouse->buttons;
140 	op = mouse->xy;
141 	while(mouse->buttons == b)
142 		readmouse(mousectl);
143 	setcursor(mousectl, nil);
144 	if(mouse->buttons){
145 		while(mouse->buttons)
146 			readmouse(mousectl);
147 		return;
148 	}
149 
150 	for(i=0; i<row->ncol; i++)
151 		if(row->col[i] == c)
152 			goto Found;
153 	error("can't find column");
154 
155   Found:
156 	if(i == 0)
157 		return;
158 	p = mouse->xy;
159 	if((abs(p.x-op.x)<5 && abs(p.y-op.y)<5))
160 		return;
161 	if((i>0 && p.x<row->col[i-1]->r.min.x) || (i<row->ncol-1 && p.x>c->r.max.x)){
162 		/* shuffle */
163 		x = c->r.min.x;
164 		rowclose(row, c, FALSE);
165 		if(rowadd(row, c, p.x) == nil)	/* whoops! */
166 		if(rowadd(row, c, x) == nil)		/* WHOOPS! */
167 		if(rowadd(row, c, -1)==nil){		/* shit! */
168 			rowclose(row, c, TRUE);
169 			return;
170 		}
171 		colmousebut(c);
172 		return;
173 	}
174 	d = row->col[i-1];
175 	if(p.x < d->r.min.x+80+Scrollwid)
176 		p.x = d->r.min.x+80+Scrollwid;
177 	if(p.x > c->r.max.x-80-Scrollwid)
178 		p.x = c->r.max.x-80-Scrollwid;
179 	r = d->r;
180 	r.max.x = c->r.max.x;
181 	draw(screen, r, display->white, nil, ZP);
182 	r.max.x = p.x;
183 	colresize(d, r);
184 	r = c->r;
185 	r.min.x = p.x;
186 	r.max.x = r.min.x;
187 	r.max.x += Border;
188 	draw(screen, r, display->black, nil, ZP);
189 	r.min.x = r.max.x;
190 	r.max.x = c->r.max.x;
191 	colresize(c, r);
192 	colmousebut(c);
193 }
194 
195 void
196 rowclose(Row *row, Column *c, int dofree)
197 {
198 	Rectangle r;
199 	int i;
200 
201 	for(i=0; i<row->ncol; i++)
202 		if(row->col[i] == c)
203 			goto Found;
204 	error("can't find column");
205   Found:
206 	r = c->r;
207 	if(dofree)
208 		colcloseall(c);
209 	memmove(row->col+i, row->col+i+1, (row->ncol-i)*sizeof(Column*));
210 	row->ncol--;
211 	row->col = realloc(row->col, row->ncol*sizeof(Column*));
212 	if(row->ncol == 0){
213 		draw(screen, r, display->white, nil, ZP);
214 		return;
215 	}
216 	if(i == row->ncol){		/* extend last column right */
217 		c = row->col[i-1];
218 		r.min.x = c->r.min.x;
219 		r.max.x = row->r.max.x;
220 	}else{			/* extend next window left */
221 		c = row->col[i];
222 		r.max.x = c->r.max.x;
223 	}
224 	draw(screen, r, display->white, nil, ZP);
225 	colresize(c, r);
226 }
227 
228 Column*
229 rowwhichcol(Row *row, Point p)
230 {
231 	int i;
232 	Column *c;
233 
234 	for(i=0; i<row->ncol; i++){
235 		c = row->col[i];
236 		if(ptinrect(p, c->r))
237 			return c;
238 	}
239 	return nil;
240 }
241 
242 Text*
243 rowwhich(Row *row, Point p)
244 {
245 	Column *c;
246 
247 	if(ptinrect(p, row->tag.all))
248 		return &row->tag;
249 	c = rowwhichcol(row, p);
250 	if(c)
251 		return colwhich(c, p);
252 	return nil;
253 }
254 
255 Text*
256 rowtype(Row *row, Rune r, Point p)
257 {
258 	Window *w;
259 	Text *t;
260 
261 	clearmouse();
262 	qlock(row);
263 	if(bartflag)
264 		t = barttext;
265 	else
266 		t = rowwhich(row, p);
267 	if(t!=nil && !(t->what==Tag && ptinrect(p, t->scrollr))){
268 		w = t->w;
269 		if(w == nil)
270 			texttype(t, r);
271 		else{
272 			winlock(w, 'K');
273 			wintype(w, t, r);
274 			winunlock(w);
275 		}
276 	}
277 	qunlock(row);
278 	return t;
279 }
280 
281 int
282 rowclean(Row *row)
283 {
284 	int clean;
285 	int i;
286 
287 	clean = TRUE;
288 	for(i=0; i<row->ncol; i++)
289 		clean &= colclean(row->col[i]);
290 	return clean;
291 }
292 
293 void
294 rowdump(Row *row, char *file)
295 {
296 	int i, j, fd, m, n, dumped;
297 	uint q0, q1;
298 	Biobuf *b;
299 	char *buf, *a, *fontname;
300 	Rune *r;
301 	Column *c;
302 	Window *w, *w1;
303 	Text *t;
304 
305 	if(row->ncol == 0)
306 		return;
307 	buf = fbufalloc();
308 	if(file == nil){
309 		if(home == nil){
310 			warning(nil, "can't find file for dump: $home not defined\n");
311 			goto Rescue;
312 		}
313 		sprint(buf, "%s/acme.dump", home);
314 		file = buf;
315 	}
316 	fd = create(file, OWRITE, 0600);
317 	if(fd < 0){
318 		warning(nil, "can't open %s: %r\n", file);
319 		goto Rescue;
320 	}
321 	b = emalloc(sizeof(Biobuf));
322 	Binit(b, fd, OWRITE);
323 	r = fbufalloc();
324 	Bprint(b, "%s\n", wdir);
325 	Bprint(b, "%s\n", fontnames[0]);
326 	Bprint(b, "%s\n", fontnames[1]);
327 	for(i=0; i<row->ncol; i++){
328 		c = row->col[i];
329 		Bprint(b, "%11d", 100*(c->r.min.x-row->r.min.x)/Dx(row->r));
330 		if(i == row->ncol-1)
331 			Bputc(b, '\n');
332 		else
333 			Bputc(b, ' ');
334 	}
335 	for(i=0; i<row->ncol; i++){
336 		c = row->col[i];
337 		for(j=0; j<c->nw; j++)
338 			c->w[j]->body.file->dumpid = 0;
339 	}
340 	for(i=0; i<row->ncol; i++){
341 		c = row->col[i];
342 		for(j=0; j<c->nw; j++){
343 			w = c->w[j];
344 			wincommit(w, &w->tag);
345 			t = &w->body;
346 			/* windows owned by others get special treatment */
347 			if(w->nopen[QWevent] > 0)
348 				if(w->dumpstr == nil)
349 					continue;
350 			/* zeroxes of external windows are tossed */
351 			if(t->file->ntext > 1)
352 				for(n=0; n<t->file->ntext; n++){
353 					w1 = t->file->text[n]->w;
354 					if(w == w1)
355 						continue;
356 					if(w1->nopen[QWevent])
357 						goto Continue2;
358 				}
359 			fontname = "";
360 			if(t->reffont->f != font)
361 				fontname = t->reffont->f->name;
362 			if(t->file->nname)
363 				a = runetobyte(t->file->name, t->file->nname);
364 			else
365 				a = emalloc(1);
366 			if(t->file->dumpid){
367 				dumped = FALSE;
368 				Bprint(b, "x%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid,
369 					w->body.q0, w->body.q1,
370 					100*(w->r.min.y-c->r.min.y)/Dy(c->r),
371 					fontname);
372 			}else if(w->dumpstr){
373 				dumped = FALSE;
374 				Bprint(b, "e%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid,
375 					0, 0,
376 					100*(w->r.min.y-c->r.min.y)/Dy(c->r),
377 					fontname);
378 			}else if((w->dirty==FALSE && access(a, 0)==0) || w->isdir){
379 				dumped = FALSE;
380 				t->file->dumpid = w->id;
381 				Bprint(b, "f%11d %11d %11d %11d %11d %s\n", i, w->id,
382 					w->body.q0, w->body.q1,
383 					100*(w->r.min.y-c->r.min.y)/Dy(c->r),
384 					fontname);
385 			}else{
386 				dumped = TRUE;
387 				t->file->dumpid = w->id;
388 				Bprint(b, "F%11d %11d %11d %11d %11d %11d %s\n", i, j,
389 					w->body.q0, w->body.q1,
390 					100*(w->r.min.y-c->r.min.y)/Dy(c->r),
391 					w->body.file->nc, fontname);
392 			}
393 			free(a);
394 			winctlprint(w, buf, 0);
395 			Bwrite(b, buf, strlen(buf));
396 			m = min(RBUFSIZE, w->tag.file->nc);
397 			bufread(w->tag.file, 0, r, m);
398 			n = 0;
399 			while(n<m && r[n]!='\n')
400 				n++;
401 			r[n++] = '\n';
402 			Bprint(b, "%.*S", n, r);
403 			if(dumped){
404 				q0 = 0;
405 				q1 = t->file->nc;
406 				while(q0 < q1){
407 					n = q1 - q0;
408 					if(n > BUFSIZE/UTFmax)
409 						n = BUFSIZE/UTFmax;
410 					bufread(t->file, q0, r, n);
411 					Bprint(b, "%.*S", n, r);
412 					q0 += n;
413 				}
414 			}
415 			if(w->dumpstr){
416 				if(w->dumpdir)
417 					Bprint(b, "%s\n%s\n", w->dumpdir, w->dumpstr);
418 				else
419 					Bprint(b, "\n%s\n", w->dumpstr);
420 			}
421     Continue2:;
422 		}
423 	}
424 	Bterm(b);
425 	close(fd);
426 	free(b);
427 	fbuffree(r);
428 
429    Rescue:
430 	fbuffree(buf);
431 }
432 
433 static
434 char*
435 rdline(Biobuf *b, int *linep)
436 {
437 	char *l;
438 
439 	l = Brdline(b, '\n');
440 	if(l)
441 		(*linep)++;
442 	return l;
443 }
444 
445 /*
446  * Get font names from load file so we don't load fonts we won't use
447  */
448 void
449 rowloadfonts(char *file)
450 {
451 	int i;
452 	Biobuf *b;
453 	char *l;
454 
455 	b = Bopen(file, OREAD);
456 	if(b == nil)
457 		return;
458 	/* current directory */
459 	l = Brdline(b, '\n');
460 	if(l == nil)
461 		goto Return;
462 	/* global fonts */
463 	for(i=0; i<2; i++){
464 		l = Brdline(b, '\n');
465 		if(l == nil)
466 			goto Return;
467 		l[Blinelen(b)-1] = 0;
468 		if(*l && strcmp(l, fontnames[i])!=0)
469 			fontnames[i] = estrdup(l);
470 	}
471     Return:
472 	Bterm(b);
473 }
474 
475 void
476 rowload(Row *row, char *file, int initing)
477 {
478 	int i, j, line, percent, y, nr, nfontr, n, ns, ndumped, dumpid, x, fd;
479 	Biobuf *b, *bout;
480 	char *buf, *l, *t, *fontname;
481 	Rune *r, rune, *fontr;
482 	Column *c, *c1, *c2;
483 	uint q0, q1;
484 	Rectangle r1, r2;
485 	Window *w;
486 
487 	buf = fbufalloc();
488 	if(file == nil){
489 		if(home == nil){
490 			warning(nil, "can't find file for load: $home not defined\n");
491 			goto Rescue1;
492 		}
493 		sprint(buf, "%s/acme.dump", home);
494 		file = buf;
495 	}
496 	b = Bopen(file, OREAD);
497 	if(b == nil){
498 		warning(nil, "can't open load file %s: %r\n", file);
499 		goto Rescue1;
500 	}
501 	/* current directory */
502 	line = 0;
503 	l = rdline(b, &line);
504 	if(l == nil)
505 		goto Rescue2;
506 	l[Blinelen(b)-1] = 0;
507 	if(chdir(l) < 0){
508 		warning(nil, "can't chdir %s\n", l);
509 		goto Rescue2;
510 	}
511 	/* global fonts */
512 	for(i=0; i<2; i++){
513 		l = rdline(b, &line);
514 		if(l == nil)
515 			goto Rescue2;
516 		l[Blinelen(b)-1] = 0;
517 		if(*l && strcmp(l, fontnames[i])!=0)
518 			rfget(i, TRUE, i==0 && initing, estrdup(l));
519 	}
520 	if(initing && row->ncol==0)
521 		rowinit(row, screen->clipr);
522 	l = rdline(b, &line);
523 	if(l == nil)
524 		goto Rescue2;
525 	j = Blinelen(b)/12;
526 	if(j<=0 || j>10)
527 		goto Rescue2;
528 	for(i=0; i<j; i++){
529 		percent = atoi(l+i*12);
530 		if(percent<0 || percent>=100)
531 			goto Rescue2;
532 		x = row->r.min.x+percent*Dx(row->r)/100;
533 		if(i < row->ncol){
534 			if(i == 0)
535 				continue;
536 			c1 = row->col[i-1];
537 			c2 = row->col[i];
538 			r1 = c1->r;
539 			r2 = c2->r;
540 			r1.max.x = x;
541 			r2.min.x = x+Border;
542 			if(Dx(r1) < 50 || Dx(r2) < 50)
543 				continue;
544 			draw(screen, Rpt(r1.min, r2.max), display->white, nil, ZP);
545 			colresize(c1, r1);
546 			colresize(c2, r2);
547 			r2.min.x = x;
548 			r2.max.x = x+Border;
549 			draw(screen, r2, display->black, nil, ZP);
550 		}
551 		if(i >= row->ncol)
552 			rowadd(row, nil, x);
553 	}
554 	for(;;){
555 		l = rdline(b, &line);
556 		if(l == nil)
557 			break;
558 		dumpid = 0;
559 		switch(l[0]){
560 		case 'e':
561 			if(Blinelen(b) < 1+5*12+1)
562 				goto Rescue2;
563 			l = rdline(b, &line);	/* ctl line; ignored */
564 			if(l == nil)
565 				goto Rescue2;
566 			l = rdline(b, &line);	/* directory */
567 			if(l == nil)
568 				goto Rescue2;
569 			l[Blinelen(b)-1] = 0;
570 			if(*l == '\0'){
571 				if(home == nil)
572 					r = bytetorune("./", &nr);
573 				else{
574 					t = emalloc(strlen(home)+1+1);
575 					sprint(t, "%s/", home);
576 					r = bytetorune(t, &nr);
577 					free(t);
578 				}
579 			}else
580 				r = bytetorune(l, &nr);
581 			l = rdline(b, &line);	/* command */
582 			if(l == nil)
583 				goto Rescue2;
584 			t = emalloc(Blinelen(b)+1);
585 			memmove(t, l, Blinelen(b));
586 			run(nil, t, r, nr, TRUE, nil, nil, FALSE);
587 			/* r is freed in run() */
588 			continue;
589 		case 'f':
590 			if(Blinelen(b) < 1+5*12+1)
591 				goto Rescue2;
592 			fontname = l+1+5*12;
593 			ndumped = -1;
594 			break;
595 		case 'F':
596 			if(Blinelen(b) < 1+6*12+1)
597 				goto Rescue2;
598 			fontname = l+1+6*12;
599 			ndumped = atoi(l+1+5*12+1);
600 			break;
601 		case 'x':
602 			if(Blinelen(b) < 1+5*12+1)
603 				goto Rescue2;
604 			fontname = l+1+5*12;
605 			ndumped = -1;
606 			dumpid = atoi(l+1+1*12);
607 			break;
608 		default:
609 			goto Rescue2;
610 		}
611 		l[Blinelen(b)-1] = 0;
612 		fontr = nil;
613 		nfontr = 0;
614 		if(*fontname)
615 			fontr = bytetorune(fontname, &nfontr);
616 		i = atoi(l+1+0*12);
617 		j = atoi(l+1+1*12);
618 		q0 = atoi(l+1+2*12);
619 		q1 = atoi(l+1+3*12);
620 		percent = atoi(l+1+4*12);
621 		if(i<0 || i>10)
622 			goto Rescue2;
623 		if(i > row->ncol)
624 			i = row->ncol;
625 		c = row->col[i];
626 		y = c->r.min.y+(percent*Dy(c->r))/100;
627 		if(y<c->r.min.y || y>=c->r.max.y)
628 			y = -1;
629 		if(dumpid == 0)
630 			w = coladd(c, nil, nil, y);
631 		else
632 			w = coladd(c, nil, lookid(dumpid, TRUE), y);
633 		if(w == nil)
634 			continue;
635 		w->dumpid = j;
636 		l = rdline(b, &line);
637 		if(l == nil)
638 			goto Rescue2;
639 		l[Blinelen(b)-1] = 0;
640 		r = bytetorune(l+5*12, &nr);
641 		ns = -1;
642 		for(n=0; n<nr; n++){
643 			if(r[n] == '/')
644 				ns = n;
645 			if(r[n] == ' ')
646 				break;
647 		}
648 		if(dumpid == 0)
649 			winsetname(w, r, n);
650 		for(; n<nr; n++)
651 			if(r[n] == '|')
652 				break;
653 		wincleartag(w);
654 		textinsert(&w->tag, w->tag.file->nc, r+n+1, nr-(n+1), TRUE);
655 		free(r);
656 		if(ndumped >= 0){
657 			/* simplest thing is to put it in a file and load that */
658 			sprint(buf, "/tmp/d%d.%.4sacme", getpid(), getuser());
659 			fd = create(buf, OWRITE|ORCLOSE, 0600);
660 			if(fd < 0){
661 				warning(nil, "can't create temp file: %r\n");
662 				goto Rescue2;
663 			}
664 			bout = emalloc(sizeof(Biobuf));
665 			Binit(bout, fd, OWRITE);
666 			for(n=0; n<ndumped; n++){
667 				rune = Bgetrune(b);
668 				if(rune == '\n')
669 					line++;
670 				if(rune == Beof){
671 					Bterm(bout);
672 					free(bout);
673 					close(fd);
674 					goto Rescue2;
675 				}
676 				Bputrune(bout, rune);
677 			}
678 			Bterm(bout);
679 			free(bout);
680 			textload(&w->body, 0, buf, 1);
681 			close(fd);
682 			w->body.file->mod = TRUE;
683 			for(n=0; n<w->body.file->ntext; n++)
684 				w->body.file->text[n]->w->dirty = TRUE;
685 			winsettag(w);
686 		}else if(dumpid==0 && r[ns+1]!='+' && r[ns+1]!='-')
687 			get(&w->body, nil, nil, FALSE, XXX, nil, 0);
688 		if(fontr){
689 			fontx(&w->body, nil, nil, 0, 0, fontr, nfontr);
690 			free(fontr);
691 		}
692 		if(q0>w->body.file->nc || q1>w->body.file->nc || q0>q1)
693 			q0 = q1 = 0;
694 		textshow(&w->body, q0, q1, 1);
695 		w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines));
696 	}
697 	Bterm(b);
698 
699 Rescue1:
700 	fbuffree(buf);
701 	return;
702 
703 Rescue2:
704 	warning(nil, "bad load file %s:%d\n", file, line);
705 	Bterm(b);
706 	goto Rescue1;
707 }
708 
709 void
710 allwindows(void (*f)(Window*, void*), void *arg)
711 {
712 	int i, j;
713 	Column *c;
714 
715 	for(i=0; i<row.ncol; i++){
716 		c = row.col[i];
717 		for(j=0; j<c->nw; j++)
718 			(*f)(c->w[j], arg);
719 	}
720 }
721