xref: /plan9/sys/src/libcontrol/text.c (revision da79363fb9fd5a76e4ca65f2f996542f71fcdfef)
180ee5cbfSDavid du Colombier #include <u.h>
280ee5cbfSDavid du Colombier #include <libc.h>
380ee5cbfSDavid du Colombier #include <draw.h>
480ee5cbfSDavid du Colombier #include <thread.h>
580ee5cbfSDavid du Colombier #include <mouse.h>
680ee5cbfSDavid du Colombier #include <keyboard.h>
780ee5cbfSDavid du Colombier #include <control.h>
880ee5cbfSDavid du Colombier 
96f314b92SDavid du Colombier static int debug = 0;
106f314b92SDavid du Colombier 
1180ee5cbfSDavid du Colombier typedef struct Text Text;
1280ee5cbfSDavid du Colombier 
1380ee5cbfSDavid du Colombier struct Text
1480ee5cbfSDavid du Colombier {
1580ee5cbfSDavid du Colombier 	Control;
1680ee5cbfSDavid du Colombier 	int		border;
1780ee5cbfSDavid du Colombier 	int		topline;
1880ee5cbfSDavid du Colombier 	int		scroll;
1980ee5cbfSDavid du Colombier 	int		nvis;
209a747e4fSDavid du Colombier 	int		lastbut;
2180ee5cbfSDavid du Colombier 	CFont	*font;
2280ee5cbfSDavid du Colombier 	CImage	*image;
2380ee5cbfSDavid du Colombier 	CImage	*textcolor;
2480ee5cbfSDavid du Colombier 	CImage	*bordercolor;
2580ee5cbfSDavid du Colombier 	CImage	*selectcolor;
266f314b92SDavid du Colombier 	CImage	*selectingcolor;
2780ee5cbfSDavid du Colombier 	Rune		**line;
286f314b92SDavid du Colombier 	int		selectmode;	// Selsingle, Selmulti
296f314b92SDavid du Colombier 	int		selectstyle;	// Seldown, Selup (use Selup only with Selsingle)
3080ee5cbfSDavid du Colombier 	uchar	*selected;
3180ee5cbfSDavid du Colombier 	int		nline;
326f314b92SDavid du Colombier 	int		warp;
3380ee5cbfSDavid du Colombier 	int		align;
346f314b92SDavid du Colombier 	int		sel;		// line nr of selection made by last button down
356f314b92SDavid du Colombier 	int		but;		// last button down (still being hold)
366f314b92SDavid du Colombier 	int		offsel;	// we are on selection
3780ee5cbfSDavid du Colombier };
3880ee5cbfSDavid du Colombier 
3980ee5cbfSDavid du Colombier enum
4080ee5cbfSDavid du Colombier {
4180ee5cbfSDavid du Colombier 	Selsingle,
4280ee5cbfSDavid du Colombier 	Selmulti,
436f314b92SDavid du Colombier 	Seldown,
446f314b92SDavid du Colombier 	Selup,
4580ee5cbfSDavid du Colombier };
4680ee5cbfSDavid du Colombier 
4780ee5cbfSDavid du Colombier enum{
489a747e4fSDavid du Colombier 	EAccumulate,
4980ee5cbfSDavid du Colombier 	EAdd,
5080ee5cbfSDavid du Colombier 	EAlign,
5180ee5cbfSDavid du Colombier 	EBorder,
5280ee5cbfSDavid du Colombier 	EBordercolor,
5380ee5cbfSDavid du Colombier 	EClear,
5480ee5cbfSDavid du Colombier 	EDelete,
5580ee5cbfSDavid du Colombier 	EFocus,
5680ee5cbfSDavid du Colombier 	EFont,
579a747e4fSDavid du Colombier 	EHide,
5880ee5cbfSDavid du Colombier 	EImage,
5980ee5cbfSDavid du Colombier 	ERect,
6080ee5cbfSDavid du Colombier 	EReplace,
619a747e4fSDavid du Colombier 	EReveal,
6280ee5cbfSDavid du Colombier 	EScroll,
6380ee5cbfSDavid du Colombier 	ESelect,
6480ee5cbfSDavid du Colombier 	ESelectcolor,
656f314b92SDavid du Colombier 	ESelectingcolor,
6680ee5cbfSDavid du Colombier 	ESelectmode,
676f314b92SDavid du Colombier 	ESelectstyle,
6880ee5cbfSDavid du Colombier 	EShow,
699a747e4fSDavid du Colombier 	ESize,
7080ee5cbfSDavid du Colombier 	ETextcolor,
7180ee5cbfSDavid du Colombier 	ETopline,
7280ee5cbfSDavid du Colombier 	EValue,
736f314b92SDavid du Colombier 	EWarp,
7480ee5cbfSDavid du Colombier };
7580ee5cbfSDavid du Colombier 
7680ee5cbfSDavid du Colombier static char *cmds[] = {
779a747e4fSDavid du Colombier 	[EAccumulate] =	"accumulate",
7880ee5cbfSDavid du Colombier 	[EAdd] =			"add",
7980ee5cbfSDavid du Colombier 	[EAlign] =			"align",
8080ee5cbfSDavid du Colombier 	[EBorder] =		"border",
8180ee5cbfSDavid du Colombier 	[EBordercolor] =	"bordercolor",
8280ee5cbfSDavid du Colombier 	[EClear] =			"clear",
8380ee5cbfSDavid du Colombier 	[EDelete] =		"delete",
8480ee5cbfSDavid du Colombier 	[EFocus] = 		"focus",
8580ee5cbfSDavid du Colombier 	[EFont] =			"font",
869a747e4fSDavid du Colombier 	[EHide] =			"hide",
8780ee5cbfSDavid du Colombier 	[EImage] =		"image",
8880ee5cbfSDavid du Colombier 	[ERect] =			"rect",
8980ee5cbfSDavid du Colombier 	[EReplace] =		"replace",
909a747e4fSDavid du Colombier 	[EReveal] =		"reveal",
9180ee5cbfSDavid du Colombier 	[EScroll] =			"scroll",
9280ee5cbfSDavid du Colombier 	[ESelect] =		"select",
9380ee5cbfSDavid du Colombier 	[ESelectcolor] =	"selectcolor",
946f314b92SDavid du Colombier 	[ESelectingcolor] =	"selectingcolor",
9580ee5cbfSDavid du Colombier 	[ESelectmode] =	"selectmode",
966f314b92SDavid du Colombier 	[ESelectstyle] =		"selectstyle",
9780ee5cbfSDavid du Colombier 	[EShow] =			"show",
989a747e4fSDavid du Colombier 	[ESize] =			"size",
9980ee5cbfSDavid du Colombier 	[ETextcolor] =		"textcolor",
10080ee5cbfSDavid du Colombier 	[ETopline] =		"topline",
10180ee5cbfSDavid du Colombier 	[EValue] =			"value",
1026f314b92SDavid du Colombier 	[EWarp] =			"warp",
10380ee5cbfSDavid du Colombier 	nil
10480ee5cbfSDavid du Colombier };
10580ee5cbfSDavid du Colombier 
10680ee5cbfSDavid du Colombier static void	textshow(Text*);
10780ee5cbfSDavid du Colombier static void	texttogglei(Text*, int);
1086f314b92SDavid du Colombier static int	textline(Text*, Point);
10980ee5cbfSDavid du Colombier static int	texttoggle(Text*, Point);
11080ee5cbfSDavid du Colombier 
11180ee5cbfSDavid du Colombier static void
textmouse(Control * c,Mouse * m)1129a747e4fSDavid du Colombier textmouse(Control *c, Mouse *m)
11380ee5cbfSDavid du Colombier {
11480ee5cbfSDavid du Colombier 	Text *t;
1159a747e4fSDavid du Colombier 	int sel;
11680ee5cbfSDavid du Colombier 
1179a747e4fSDavid du Colombier 	t = (Text*)c;
1186f314b92SDavid du Colombier 	if (debug) fprint(2, "textmouse %s t->lastbut %d; m->buttons %d\n", t->name, t->lastbut, m->buttons);
1196f314b92SDavid du Colombier 	if (t->warp >= 0)
1206f314b92SDavid du Colombier 		return;
1216f314b92SDavid du Colombier 	if ((t->selectstyle == Selup) && (m->buttons&7)) {
1226f314b92SDavid du Colombier 		sel = textline(t, m->xy);
1236f314b92SDavid du Colombier 		if (t->sel >= 0) {
1246f314b92SDavid du Colombier //			if (debug) fprint(2, "textmouse Selup %q sel=%d t->sel=%d t->but=%d\n",
1256f314b92SDavid du Colombier //						t->name, sel, t->sel, t->but);
1266f314b92SDavid du Colombier 			t->offsel = (sel == t->sel) ? 0 : 1;
1276f314b92SDavid du Colombier 			if ((sel == t->sel &&
1286f314b92SDavid du Colombier 				    ((t->selected[t->sel] && !t->but) ||
1296f314b92SDavid du Colombier 				     ((!t->selected[t->sel]) && t->but))) ||
1306f314b92SDavid du Colombier 			    (sel != t->sel &&
1316f314b92SDavid du Colombier 				     ((t->selected[t->sel] && t->but) ||
1326f314b92SDavid du Colombier                                          ((!t->selected[t->sel]) && (!t->but))))) {
1336f314b92SDavid du Colombier 				texttogglei(t, t->sel);
1346f314b92SDavid du Colombier 			}
1356f314b92SDavid du Colombier 		}
1366f314b92SDavid du Colombier 	}
1379a747e4fSDavid du Colombier 	if(t->lastbut != (m->buttons&7)){
1389a747e4fSDavid du Colombier 		if(m->buttons & 7){
1399a747e4fSDavid du Colombier 			sel = texttoggle(t, m->xy);
1406f314b92SDavid du Colombier 			if(sel >= 0) {
1416f314b92SDavid du Colombier 				if (t->selectstyle == Seldown) {
1429a747e4fSDavid du Colombier 					chanprint(t->event, "%q: select %d %d",
1439a747e4fSDavid du Colombier 						t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
1446f314b92SDavid du Colombier 					if (debug) fprint(2, "textmouse Seldown event %q: select %d %d\n",
1456f314b92SDavid du Colombier 						t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
1466f314b92SDavid du Colombier 				} else {
1476f314b92SDavid du Colombier 					if (debug) fprint(2, "textmouse Selup no event yet %q: select %d %d\n",
1486f314b92SDavid du Colombier 						t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
1496f314b92SDavid du Colombier 					t->sel = sel;
1506f314b92SDavid du Colombier 					t->but =  t->selected[sel] ? (m->buttons & 7) : 0;
1516f314b92SDavid du Colombier 				}
1526f314b92SDavid du Colombier 			}
1536f314b92SDavid du Colombier 		} else if (t->selectstyle == Selup) {
1546f314b92SDavid du Colombier 			sel = textline(t, m->xy);
1556f314b92SDavid du Colombier 			t->offsel = 0;
1566f314b92SDavid du Colombier 			if ((sel >= 0) && (sel == t->sel)) {
1576f314b92SDavid du Colombier 				chanprint(t->event, "%q: select %d %d",
1586f314b92SDavid du Colombier 					t->name, sel, t->but);
1596f314b92SDavid du Colombier 				if (debug) fprint(2, "textmouse Selup event %q: select %d %d\n",
1606f314b92SDavid du Colombier 					t->name, sel, t->but);
1616f314b92SDavid du Colombier 			} else if (sel != t->sel) {
1626f314b92SDavid du Colombier 				if  ((t->selected[t->sel] && t->but) ||
1636f314b92SDavid du Colombier                                          ((!t->selected[t->sel]) && (!t->but))) {
1646f314b92SDavid du Colombier 					texttogglei(t, t->sel);
1656f314b92SDavid du Colombier 				} else {
1666f314b92SDavid du Colombier 					textshow(t);
1676f314b92SDavid du Colombier 				}
1686f314b92SDavid du Colombier 				if (debug) fprint(2, "textmouse Selup cancel %q: select %d %d\n",
1696f314b92SDavid du Colombier 					t->name, sel, t->but);
1706f314b92SDavid du Colombier 			}
1716f314b92SDavid du Colombier 			t->sel = -1;
1726f314b92SDavid du Colombier 			t->but = 0;
17380ee5cbfSDavid du Colombier 		}
1749a747e4fSDavid du Colombier 		t->lastbut = m->buttons & 7;
17580ee5cbfSDavid du Colombier 	}
17680ee5cbfSDavid du Colombier }
17780ee5cbfSDavid du Colombier 
17880ee5cbfSDavid du Colombier static void
textfree(Control * c)1799a747e4fSDavid du Colombier textfree(Control *c)
18080ee5cbfSDavid du Colombier {
18180ee5cbfSDavid du Colombier 	int i;
1829a747e4fSDavid du Colombier 	Text *t;
18380ee5cbfSDavid du Colombier 
1849a747e4fSDavid du Colombier 	t = (Text*)c;
18580ee5cbfSDavid du Colombier 	_putctlfont(t->font);
18680ee5cbfSDavid du Colombier 	_putctlimage(t->image);
18780ee5cbfSDavid du Colombier 	_putctlimage(t->textcolor);
18880ee5cbfSDavid du Colombier 	_putctlimage(t->bordercolor);
18980ee5cbfSDavid du Colombier 	_putctlimage(t->selectcolor);
1906f314b92SDavid du Colombier 	_putctlimage(t->selectingcolor);
19180ee5cbfSDavid du Colombier 	for(i=0; i<t->nline; i++)
19280ee5cbfSDavid du Colombier 		free(t->line[i]);
19380ee5cbfSDavid du Colombier 	free(t->line);
19480ee5cbfSDavid du Colombier 	free(t->selected);
19580ee5cbfSDavid du Colombier }
19680ee5cbfSDavid du Colombier 
19780ee5cbfSDavid du Colombier static void
textshow(Text * t)19880ee5cbfSDavid du Colombier textshow(Text *t)
19980ee5cbfSDavid du Colombier {
20080ee5cbfSDavid du Colombier 	Rectangle r, tr;
20180ee5cbfSDavid du Colombier 	Point p;
20280ee5cbfSDavid du Colombier 	int i, ntext;
20380ee5cbfSDavid du Colombier 	Font *f;
20480ee5cbfSDavid du Colombier 	Rune *text;
20580ee5cbfSDavid du Colombier 
2069a747e4fSDavid du Colombier 	if (t->hidden)
2079a747e4fSDavid du Colombier 		return;
20880ee5cbfSDavid du Colombier 	r = t->rect;
20980ee5cbfSDavid du Colombier 	f = t->font->font;
21080ee5cbfSDavid du Colombier 	draw(t->screen, r, t->image->image, nil, t->image->image->r.min);
21180ee5cbfSDavid du Colombier 	if(t->border > 0){
21280ee5cbfSDavid du Colombier 		border(t->screen, r, t->border, t->bordercolor->image, t->bordercolor->image->r.min);
21380ee5cbfSDavid du Colombier 		r = insetrect(r, t->border);
21480ee5cbfSDavid du Colombier 	}
21580ee5cbfSDavid du Colombier 	tr = r;
2169a747e4fSDavid du Colombier 	t->nvis = Dy(r)/f->height;
21780ee5cbfSDavid du Colombier 	for(i=t->topline; i<t->nline && i<t->topline+t->nvis; i++){
21880ee5cbfSDavid du Colombier 		text = t->line[i];
21980ee5cbfSDavid du Colombier 		ntext = runestrlen(text);
22080ee5cbfSDavid du Colombier 		r.max.y = r.min.y+f->height;
2216f314b92SDavid du Colombier 		if(t->sel == i && t->offsel)
2226f314b92SDavid du Colombier 			draw(t->screen, r, t->selectingcolor->image, nil, ZP);
2236f314b92SDavid du Colombier 		else if(t->selected[i])
22480ee5cbfSDavid du Colombier 			draw(t->screen, r, t->selectcolor->image, nil, ZP);
22580ee5cbfSDavid du Colombier 		p = _ctlalignpoint(r,
22680ee5cbfSDavid du Colombier 			runestringnwidth(f, text, ntext),
22780ee5cbfSDavid du Colombier 			f->height, t->align);
2286f314b92SDavid du Colombier 		if(t->warp == i) {
2296f314b92SDavid du Colombier 			Point p2;
2306f314b92SDavid du Colombier 			 p2.x = p.x + 0.5*runestringnwidth(f, text, ntext);
2316f314b92SDavid du Colombier 			 p2.y = p.y + 0.5*f->height;
2326f314b92SDavid du Colombier 			moveto(t->controlset->mousectl, p2);
2336f314b92SDavid du Colombier 			t->warp = -1;
2346f314b92SDavid du Colombier 		}
23580ee5cbfSDavid du Colombier 		_string(t->screen, p, t->textcolor->image,
23680ee5cbfSDavid du Colombier 			ZP, f, nil, text, ntext, tr,
237ac57dd0bSDavid du Colombier 			nil, ZP, SoverD);
23880ee5cbfSDavid du Colombier 		r.min.y += f->height;
23980ee5cbfSDavid du Colombier 	}
24080ee5cbfSDavid du Colombier 	flushimage(display, 1);
24180ee5cbfSDavid du Colombier }
24280ee5cbfSDavid du Colombier 
24380ee5cbfSDavid du Colombier static void
textctl(Control * c,CParse * cp)2449a747e4fSDavid du Colombier textctl(Control *c, CParse *cp)
24580ee5cbfSDavid du Colombier {
24680ee5cbfSDavid du Colombier 	int cmd, i, n;
24780ee5cbfSDavid du Colombier 	Rectangle r;
24880ee5cbfSDavid du Colombier 	Text *t;
24980ee5cbfSDavid du Colombier 	Rune *rp;
25080ee5cbfSDavid du Colombier 
25180ee5cbfSDavid du Colombier 	t = (Text*)c;
2529a747e4fSDavid du Colombier 	cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
25380ee5cbfSDavid du Colombier 	switch(cmd){
25480ee5cbfSDavid du Colombier 	default:
2559a747e4fSDavid du Colombier 		ctlerror("%q: unrecognized message '%s'", t->name, cp->str);
25680ee5cbfSDavid du Colombier 		break;
25780ee5cbfSDavid du Colombier 	case EAlign:
2589a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
2599a747e4fSDavid du Colombier 		t->align = _ctlalignment(cp->args[1]);
26080ee5cbfSDavid du Colombier 		break;
26180ee5cbfSDavid du Colombier 	case EBorder:
2629a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
2639a747e4fSDavid du Colombier 		if(cp->iargs[1] < 0)
2649a747e4fSDavid du Colombier 			ctlerror("%q: bad border: %c", t->name, cp->str);
2659a747e4fSDavid du Colombier 		t->border = cp->iargs[1];
26680ee5cbfSDavid du Colombier 		break;
26780ee5cbfSDavid du Colombier 	case EBordercolor:
2689a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
2699a747e4fSDavid du Colombier 		_setctlimage(t, &t->bordercolor, cp->args[1]);
27080ee5cbfSDavid du Colombier 		break;
27180ee5cbfSDavid du Colombier 	case EClear:
2729a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 1);
27380ee5cbfSDavid du Colombier 		for(i=0; i<t->nline; i++)
27480ee5cbfSDavid du Colombier 			free(t->line[i]);
27580ee5cbfSDavid du Colombier 		free(t->line);
27680ee5cbfSDavid du Colombier 		free(t->selected);
27780ee5cbfSDavid du Colombier 		t->line = ctlmalloc(sizeof(Rune*));
27880ee5cbfSDavid du Colombier 		t->selected = ctlmalloc(1);
27980ee5cbfSDavid du Colombier 		t->nline = 0;
28080ee5cbfSDavid du Colombier 		textshow(t);
28180ee5cbfSDavid du Colombier 		break;
28280ee5cbfSDavid du Colombier 	case EDelete:
2839a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
2849a747e4fSDavid du Colombier 		i = cp->iargs[1];
28580ee5cbfSDavid du Colombier 		if(i<0 || i>=t->nline)
2869a747e4fSDavid du Colombier 			ctlerror("%q: line number out of range: %s", t->name, cp->str);
28780ee5cbfSDavid du Colombier 		free(t->line[i]);
28880ee5cbfSDavid du Colombier 		memmove(t->line+i, t->line+i+1, (t->nline-(i+1))*sizeof(Rune*));
28980ee5cbfSDavid du Colombier 		memmove(t->selected+i, t->selected+i+1, t->nline-(i+1));
29080ee5cbfSDavid du Colombier 		t->nline--;
29180ee5cbfSDavid du Colombier 		textshow(t);
29280ee5cbfSDavid du Colombier 		break;
29380ee5cbfSDavid du Colombier 	case EFocus:
29480ee5cbfSDavid du Colombier 		break;
29580ee5cbfSDavid du Colombier 	case EFont:
2969a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
2979a747e4fSDavid du Colombier 		_setctlfont(t, &t->font, cp->args[1]);
2989a747e4fSDavid du Colombier 		break;
2999a747e4fSDavid du Colombier 	case EHide:
3009a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 1);
3019a747e4fSDavid du Colombier 		t->hidden = 1;
30280ee5cbfSDavid du Colombier 		break;
30380ee5cbfSDavid du Colombier 	case EImage:
3049a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
3059a747e4fSDavid du Colombier 		_setctlimage(t, &t->image, cp->args[1]);
30680ee5cbfSDavid du Colombier 		break;
30780ee5cbfSDavid du Colombier 	case ERect:
3089a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 5);
3099a747e4fSDavid du Colombier 		r.min.x = cp->iargs[1];
3109a747e4fSDavid du Colombier 		r.min.y = cp->iargs[2];
3119a747e4fSDavid du Colombier 		r.max.x = cp->iargs[3];
3129a747e4fSDavid du Colombier 		r.max.y = cp->iargs[4];
31380ee5cbfSDavid du Colombier 		if(Dx(r)<=0 || Dy(r)<=0)
3149a747e4fSDavid du Colombier 			ctlerror("%q: bad rectangle: %s", t->name, cp->str);
31580ee5cbfSDavid du Colombier 		t->rect = r;
3169a747e4fSDavid du Colombier 		t->nvis = (Dy(r)-2*t->border)/t->font->font->height;
31780ee5cbfSDavid du Colombier 		break;
31880ee5cbfSDavid du Colombier 	case EReplace:
3199a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 3);
3209a747e4fSDavid du Colombier 		i = cp->iargs[1];
32180ee5cbfSDavid du Colombier 		if(i<0 || i>=t->nline)
3229a747e4fSDavid du Colombier 			ctlerror("%q: line number out of range: %s", t->name, cp->str);
32380ee5cbfSDavid du Colombier 		free(t->line[i]);
3249a747e4fSDavid du Colombier 		t->line[i] = _ctlrunestr(cp->args[2]);
3259a747e4fSDavid du Colombier 		textshow(t);
3269a747e4fSDavid du Colombier 		break;
3279a747e4fSDavid du Colombier 	case EReveal:
3289a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 1);
3299a747e4fSDavid du Colombier 		t->hidden = 0;
33080ee5cbfSDavid du Colombier 		textshow(t);
33180ee5cbfSDavid du Colombier 		break;
33280ee5cbfSDavid du Colombier 	case EScroll:
3339a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
3349a747e4fSDavid du Colombier 		t->scroll = cp->iargs[1];
33580ee5cbfSDavid du Colombier 		break;
33680ee5cbfSDavid du Colombier 	case ESelect:
3379a747e4fSDavid du Colombier 		if(cp->nargs!=2 && cp->nargs!=3)
33880ee5cbfSDavid du Colombier 	badselect:
3399a747e4fSDavid du Colombier 			ctlerror("%q: bad select message: %s", t->name, cp->str);
3409a747e4fSDavid du Colombier 		if(cp->nargs == 2){
3419a747e4fSDavid du Colombier 			if(strcmp(cp->args[1], "all") == 0){
34280ee5cbfSDavid du Colombier 				memset(t->selected, 1, t->nline);
34380ee5cbfSDavid du Colombier 				break;
34480ee5cbfSDavid du Colombier 			}
3459a747e4fSDavid du Colombier 			if(strcmp(cp->args[1], "none") == 0){
34680ee5cbfSDavid du Colombier 				memset(t->selected, 0, t->nline);
34780ee5cbfSDavid du Colombier 				break;
34880ee5cbfSDavid du Colombier 			}
3499a747e4fSDavid du Colombier 			if(cp->args[1][0]<'0' && '9'<cp->args[1][0])
35080ee5cbfSDavid du Colombier 				goto badselect;
3519a747e4fSDavid du Colombier 			texttogglei(t, cp->iargs[1]);
35280ee5cbfSDavid du Colombier 			break;
35380ee5cbfSDavid du Colombier 		}
3549a747e4fSDavid du Colombier 		if(cp->iargs[1]<0 || cp->iargs[1]>=t->nline)
3559a747e4fSDavid du Colombier 			ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str);
3569a747e4fSDavid du Colombier 		if(t->selected[cp->iargs[1]] != (cp->iargs[2]!=0))
3579a747e4fSDavid du Colombier 			texttogglei(t, cp->iargs[1]);
35880ee5cbfSDavid du Colombier 		break;
35980ee5cbfSDavid du Colombier 	case ESelectcolor:
3609a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
3619a747e4fSDavid du Colombier 		_setctlimage(t, &t->selectcolor, cp->args[1]);
36280ee5cbfSDavid du Colombier 		break;
36380ee5cbfSDavid du Colombier 	case ESelectmode:
3649a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
3659a747e4fSDavid du Colombier 		if(strcmp(cp->args[1], "single") == 0)
36680ee5cbfSDavid du Colombier 			t->selectmode = Selsingle;
3679a747e4fSDavid du Colombier 		else if(strncmp(cp->args[1], "multi", 5) == 0)
36880ee5cbfSDavid du Colombier 			t->selectmode = Selmulti;
36980ee5cbfSDavid du Colombier 		break;
3706f314b92SDavid du Colombier 	case ESelectstyle:
3716f314b92SDavid du Colombier 		_ctlargcount(t, cp, 2);
3726f314b92SDavid du Colombier 		 if(strcmp(cp->args[1], "down") == 0)
3736f314b92SDavid du Colombier 			t->selectstyle = Seldown;
3746f314b92SDavid du Colombier 		else if(strcmp(cp->args[1], "up") == 0)
3756f314b92SDavid du Colombier 			t->selectstyle = Selup;
3766f314b92SDavid du Colombier 		break;
37780ee5cbfSDavid du Colombier 	case EShow:
3789a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 1);
37980ee5cbfSDavid du Colombier 		textshow(t);
38080ee5cbfSDavid du Colombier 		break;
3819a747e4fSDavid du Colombier 	case ESize:
3829a747e4fSDavid du Colombier 		if (cp->nargs == 3)
3839a747e4fSDavid du Colombier 			r.max = Pt(10000, 10000);
3849a747e4fSDavid du Colombier 		else{
3859a747e4fSDavid du Colombier 			_ctlargcount(t, cp, 5);
3869a747e4fSDavid du Colombier 			r.max.x = cp->iargs[3];
3879a747e4fSDavid du Colombier 			r.max.y = cp->iargs[4];
3889a747e4fSDavid du Colombier 		}
3899a747e4fSDavid du Colombier 		r.min.x = cp->iargs[1];
3909a747e4fSDavid du Colombier 		r.min.y = cp->iargs[2];
3919a747e4fSDavid du Colombier 		if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y)
3929a747e4fSDavid du Colombier 			ctlerror("%q: bad sizes: %s", t->name, cp->str);
3939a747e4fSDavid du Colombier 		t->size.min = r.min;
3949a747e4fSDavid du Colombier 		t->size.max = r.max;
39580ee5cbfSDavid du Colombier 		break;
3969a747e4fSDavid du Colombier 	case ETextcolor:
3979a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
3989a747e4fSDavid du Colombier 		_setctlimage(t, &t->textcolor, cp->args[1]);
39980ee5cbfSDavid du Colombier 		break;
40080ee5cbfSDavid du Colombier 	case ETopline:
4019a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
4029a747e4fSDavid du Colombier 		i = cp->iargs[1];
40380ee5cbfSDavid du Colombier 		if(i < 0)
40480ee5cbfSDavid du Colombier 			i = 0;
40580ee5cbfSDavid du Colombier 		if(i > t->nline)
40680ee5cbfSDavid du Colombier 			i = t->nline;
40780ee5cbfSDavid du Colombier 		if(t->topline != i){
40880ee5cbfSDavid du Colombier 			t->topline = i;
40980ee5cbfSDavid du Colombier 			textshow(t);
41080ee5cbfSDavid du Colombier 		}
41180ee5cbfSDavid du Colombier 		break;
41280ee5cbfSDavid du Colombier 	case EValue:
41380ee5cbfSDavid du Colombier 		/* set contents to single line */
41480ee5cbfSDavid du Colombier 		/* free existing text and fall through to add */
41580ee5cbfSDavid du Colombier 		for(i=0; i<t->nline; i++){
41680ee5cbfSDavid du Colombier 			free(t->line[i]);
41780ee5cbfSDavid du Colombier 			t->line[i] = nil;
41880ee5cbfSDavid du Colombier 		}
41980ee5cbfSDavid du Colombier 		t->nline = 0;
42080ee5cbfSDavid du Colombier 		t->topline = 0;
42180ee5cbfSDavid du Colombier 		/* fall through */
4229a747e4fSDavid du Colombier 	case EAccumulate:
42380ee5cbfSDavid du Colombier 	case EAdd:
4249a747e4fSDavid du Colombier 		switch (cp->nargs) {
42580ee5cbfSDavid du Colombier 		default:
4269a747e4fSDavid du Colombier 			ctlerror("%q: wrong argument count in '%s'", t->name, cp->str);
42780ee5cbfSDavid du Colombier 		case 2:
42880ee5cbfSDavid du Colombier 			n = t->nline;
42980ee5cbfSDavid du Colombier 			break;
43080ee5cbfSDavid du Colombier 		case 3:
4319a747e4fSDavid du Colombier 			n = cp->iargs[1];
43280ee5cbfSDavid du Colombier 			if(n<0 || n>t->nline)
4339a747e4fSDavid du Colombier 				ctlerror("%q: line number out of range: %s", t->name, cp->str);
43480ee5cbfSDavid du Colombier 			break;
43580ee5cbfSDavid du Colombier 		}
4369a747e4fSDavid du Colombier 		rp = _ctlrunestr(cp->args[cp->nargs-1]);
43780ee5cbfSDavid du Colombier 		t->line = ctlrealloc(t->line, (t->nline+1)*sizeof(Rune*));
43880ee5cbfSDavid du Colombier 		memmove(t->line+n+1, t->line+n, (t->nline-n)*sizeof(Rune*));
43980ee5cbfSDavid du Colombier 		t->line[n] = rp;
44080ee5cbfSDavid du Colombier 		t->selected = ctlrealloc(t->selected, t->nline+1);
44180ee5cbfSDavid du Colombier 		memmove(t->selected+n+1, t->selected+n, t->nline-n);
4429a747e4fSDavid du Colombier 		t->selected[n] = (t->selectmode==Selmulti && cmd!=EAccumulate);
44380ee5cbfSDavid du Colombier 		t->nline++;
44480ee5cbfSDavid du Colombier 		if(t->scroll) {
4459a747e4fSDavid du Colombier 			if(n > t->topline + (t->nvis - 1)){
4469a747e4fSDavid du Colombier 				t->topline = n - (t->nvis - 1);
4479a747e4fSDavid du Colombier 				if(t->topline < 0)
4489a747e4fSDavid du Colombier 					t->topline = 0;
4499a747e4fSDavid du Colombier 			}
45080ee5cbfSDavid du Colombier 			if(n < t->topline)
45180ee5cbfSDavid du Colombier 				t->topline = n;
45280ee5cbfSDavid du Colombier 		}
4539a747e4fSDavid du Colombier 		if(cmd != EAccumulate)
45480ee5cbfSDavid du Colombier 			if(t->scroll || t->nline<=t->topline+t->nvis)
45580ee5cbfSDavid du Colombier 				textshow(t);
45680ee5cbfSDavid du Colombier 		break;
4576f314b92SDavid du Colombier 	case EWarp:
4586f314b92SDavid du Colombier 		_ctlargcount(t, cp, 2);
459*da79363fSDavid du Colombier 		i = cp->iargs[1];
460*da79363fSDavid du Colombier 		if(i <0 || i>=t->nline)
4616f314b92SDavid du Colombier 			ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str);
462*da79363fSDavid du Colombier 		if(i < t->topline || i >=  t->topline+t->nvis){
463*da79363fSDavid du Colombier 			t->topline = i;
464*da79363fSDavid du Colombier 		}
4656f314b92SDavid du Colombier 		t->warp = cp->iargs[1];
4666f314b92SDavid du Colombier 		textshow(t);
4676f314b92SDavid du Colombier 		t->warp = -1;
4686f314b92SDavid du Colombier 		break;
46980ee5cbfSDavid du Colombier 	}
47080ee5cbfSDavid du Colombier }
47180ee5cbfSDavid du Colombier 
47280ee5cbfSDavid du Colombier static void
texttogglei(Text * t,int i)47380ee5cbfSDavid du Colombier texttogglei(Text *t, int i)
47480ee5cbfSDavid du Colombier {
47580ee5cbfSDavid du Colombier 	int prev;
47680ee5cbfSDavid du Colombier 
47780ee5cbfSDavid du Colombier 	if(t->selectmode == Selsingle){
47880ee5cbfSDavid du Colombier 		/* clear the others */
47980ee5cbfSDavid du Colombier 		prev = t->selected[i];
48080ee5cbfSDavid du Colombier 		memset(t->selected, 0, t->nline);
48180ee5cbfSDavid du Colombier 		t->selected[i] = prev;
48280ee5cbfSDavid du Colombier 	}
48380ee5cbfSDavid du Colombier 	t->selected[i] ^= 1;
48480ee5cbfSDavid du Colombier 	textshow(t);
48580ee5cbfSDavid du Colombier }
48680ee5cbfSDavid du Colombier 
48780ee5cbfSDavid du Colombier static int
textline(Text * t,Point p)4886f314b92SDavid du Colombier textline(Text *t, Point p)
48980ee5cbfSDavid du Colombier {
49080ee5cbfSDavid du Colombier 	Rectangle r;
49180ee5cbfSDavid du Colombier 	int i;
49280ee5cbfSDavid du Colombier 
49380ee5cbfSDavid du Colombier 	r = t->rect;
49480ee5cbfSDavid du Colombier 	if(t->border > 0)
49580ee5cbfSDavid du Colombier 		r = insetrect(r, t->border);
49680ee5cbfSDavid du Colombier 	if(!ptinrect(p, r))
49780ee5cbfSDavid du Colombier 		return -1;
4989a747e4fSDavid du Colombier 	i = (p.y-r.min.y)/t->font->font->height;
49980ee5cbfSDavid du Colombier 	i += t->topline;
50080ee5cbfSDavid du Colombier 	if(i >= t->nline)
50180ee5cbfSDavid du Colombier 		return -1;
5026f314b92SDavid du Colombier 	return i;
5036f314b92SDavid du Colombier }
5046f314b92SDavid du Colombier 
5056f314b92SDavid du Colombier static int
texttoggle(Text * t,Point p)5066f314b92SDavid du Colombier texttoggle(Text *t, Point p)
5076f314b92SDavid du Colombier {
5086f314b92SDavid du Colombier 	int i;
5096f314b92SDavid du Colombier 
5106f314b92SDavid du Colombier 	i = textline(t, p);
5116f314b92SDavid du Colombier 	if (i >= 0)
51280ee5cbfSDavid du Colombier 		texttogglei(t, i);
51380ee5cbfSDavid du Colombier 	return i;
51480ee5cbfSDavid du Colombier }
5159a747e4fSDavid du Colombier 
5169a747e4fSDavid du Colombier Control*
createtext(Controlset * cs,char * name)5179a747e4fSDavid du Colombier createtext(Controlset *cs, char *name)
5189a747e4fSDavid du Colombier {
5199a747e4fSDavid du Colombier 	Text *t;
5209a747e4fSDavid du Colombier 
5219a747e4fSDavid du Colombier 	t = (Text*)_createctl(cs, "text", sizeof(Text), name);
5229a747e4fSDavid du Colombier 	t->line = ctlmalloc(sizeof(Rune*));
5239a747e4fSDavid du Colombier 	t->selected = ctlmalloc(1);
5249a747e4fSDavid du Colombier 	t->nline = 0;
5259a747e4fSDavid du Colombier 	t->image = _getctlimage("white");
5269a747e4fSDavid du Colombier 	t->textcolor = _getctlimage("black");
5279a747e4fSDavid du Colombier 	t->bordercolor = _getctlimage("black");
5289a747e4fSDavid du Colombier 	t->selectcolor = _getctlimage("yellow");
5296f314b92SDavid du Colombier 	t->selectingcolor = _getctlimage("paleyellow");
5309a747e4fSDavid du Colombier 	t->font = _getctlfont("font");
5319a747e4fSDavid du Colombier 	t->selectmode = Selsingle;
5326f314b92SDavid du Colombier 	t->selectstyle = Selup; // Seldown;
5339a747e4fSDavid du Colombier 	t->lastbut = 0;
5349a747e4fSDavid du Colombier 	t->mouse = textmouse;
5359a747e4fSDavid du Colombier 	t->ctl = textctl;
5369a747e4fSDavid du Colombier 	t->exit = textfree;
5376f314b92SDavid du Colombier 	t->warp = -1;
5386f314b92SDavid du Colombier 	t->sel = -1;
5396f314b92SDavid du Colombier 	t->offsel = 0;
5406f314b92SDavid du Colombier 	t->but = 0;
5419a747e4fSDavid du Colombier 	return (Control *)t;
5429a747e4fSDavid du Colombier }
543