shithub: eesp

ref: f94021cad309874252753f9ac7b49b9efe9b04be
dir: /libeenter.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <keyboard.h>

static void
sorttick(int *t1, int *t2)
{
	int i;
	if(*t1 <= *t2)
		return;
	i = *t1;
	*t1 = *t2;
	*t2 = i;
}

static Point
drawstring(Image *b, char *buf, Point p, int s, int e, Image *bg, int h)
{
	sorttick(&s, &e);
	p = stringn(b, p, display->black, ZP, font, buf, utfnlen(buf, s));
	if(s == e){
		draw(b, Rect(p.x-1, p.y, p.x+2, p.y+3), display->black, nil, ZP);
		draw(b, Rect(p.x, p.y, p.x+1, p.y+h), display->black, nil, ZP);
		draw(b, Rect(p.x-1, p.y+h-3, p.x+2, p.y+h), display->black, nil, ZP);
	} else {
		p = stringnbg(b, p, display->black, ZP, font, buf+s, utfnlen(buf+s, e-s), bg, ZP);
	}
	p = string(b, p, display->black, ZP, font, buf+e);
	return p;
}

static void
enterselection(char *buf, int len, int s, int e, int *sp, int *ep)
{
	int l, i;
	Rune k;
	sorttick(&s, &e);
	*sp = *ep = -1;
	for(i = 0; i < len; i += l){
		l = chartorune(&k, buf+i);
		if(*sp >= 0 && i >= e){
			*ep = i;
			break;
		}
		if(*sp < 0 && i >= s)
			*sp = i;
	}
}

static int
delsubstring(char *buf, int len, int s, int e, int *nlen)
{
	int l, i, sp, ep;
	Rune k;
	if(s == e)
		return s;
	if (s < 0)
		s = 0;
	enterselection(buf, len, s, e, &sp, &ep);
	memmove(buf+sp, buf+ep, len - ep);
	buf[len-ep+sp] = 0;
	if (nlen)
		*nlen = sp+len-ep;
	return sp;
}

static int
entersnarf(char *buf, int len, int s, int e, int *nlen)
{
	int fd, sp, ep;
	fd = open("/dev/snarf", OWRITE|OTRUNC);
	if(fd < 0){
		fprint(2, "error: %r\n");
		return s;
	}
	enterselection(buf, len, s, e, &sp, &ep);
	if(sp < 0 || ep < 0){
		close(fd);
		return sp < 0 ? ep : sp;
	}
	pwrite(fd, &buf[sp], ep-sp, 0);
	close(fd);
	return delsubstring(buf, len, s, e, nlen);
}

static void
enterpaste(char *buf, int len, int max, int s, int e, int *nlen, int *ns, int *ne)
{
	char *str;
	int fd, sp, ep, n, newlen;
	
	fd = open("/dev/snarf", OREAD);
	if(fd < 0){
		fprint(2, "error: %r\n");
		return;
	}
	str = mallocz(max*sizeof(char), 1);
	n = pread(fd, str, max-1, 0);
	str[n] = 0;
	close(fd);

	newlen = len;
	enterselection(buf, len, s, e, &sp, &ep);
	s = entersnarf(buf, len, s, e, &newlen);

	memmove(buf+n+s, buf+s, newlen-s);
	memcpy(buf+s, str, n);

	free(str);
	if (nlen)
		*nlen = newlen+n;
	if (ns)
		*ns = s;
	if (ne)
		*ne = s+n;
}

int
eenter(char *ask, char *buf, int len, Mouse *m)
{
	int done, down, tick, n, h, w, l, i;
	int tick2, mb, ti;
	Image *b, *save, *backcol, *bordcol;
	Point p, o, t;
	Rectangle r, sc;
	Event ev;
	Rune k;

	mb = 0;
	o = screen->r.min;
	backcol = allocimagemix(display, DPurpleblue, DWhite);
	bordcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
	if(backcol == nil || bordcol == nil)
		return -1;

	while(ecankbd())
		ekbd();

	if(m) o = m->xy;

	if(buf && len > 0)
		n = strlen(buf);
	else {
		buf = nil;
		len = 0;
		n = 0;
	}

	k = -1;
	tick2 = tick = n;
	save = nil;
	done = down = 0;

	p = stringsize(font, " ");
	h = p.y;
	w = p.x;

	b = screen;
	sc = b->clipr;
	replclipr(b, 0, b->r);

	while(!done){
		p = stringsize(font, buf ? buf : "");
		if(ask && ask[0]){
			if(buf) p.x += w;
			p.x += stringwidth(font, ask);
		}
		r = rectaddpt(insetrect(Rpt(ZP, p), -4), o);
		p.x = 0;
		r = rectsubpt(r, p);

		p = ZP;
		if(r.min.x < screen->r.min.x)
			p.x = screen->r.min.x - r.min.x;
		if(r.min.y < screen->r.min.y)
			p.y = screen->r.min.y - r.min.y;
		r = rectaddpt(r, p);
		p = ZP;
		if(r.max.x > screen->r.max.x)
			p.x = r.max.x - screen->r.max.x;
		if(r.max.y > screen->r.max.y)
			p.y = r.max.y - screen->r.max.y;
		r = rectsubpt(r, p);

		r = insetrect(r, -2);
		if(save == nil){
			save = allocimage(display, r, b->chan, 0, DNofill);
			if(save == nil){
				n = -1;
				break;
			}
			draw(save, r, b, nil, r.min);
		}
		draw(b, r, backcol, nil, ZP);
		border(b, r, 2, bordcol, ZP);
		p = addpt(r.min, Pt(6, 6));
		if(ask && ask[0]){
			p = string(b, p, bordcol, ZP, font, ask);
			if(buf) p.x += w;
		}
		if(buf){
			t = p;
			p = drawstring(b, buf, p, tick, tick2, bordcol, h);
		}
		flushimage(display, 1);

nodraw:
		i = Ekeyboard;
		if(m != nil)
			i |= Emouse;

		replclipr(b, 0, sc);
		i = eread(i, &ev);

		/* screen might have been resized */
		if(b != screen || !eqrect(screen->clipr, sc)){
			freeimage(save);
			save = nil;
		}
		b = screen;
		sc = b->clipr;
		replclipr(b, 0, b->r);

		switch(i){
		default:
			done = 1;
			n = -1;
			break;
		case Ekeyboard:
			k = ev.kbdc;
			if(buf == nil || k == Keof || k == '\n'){
				done = 1;
				break;
			}
			if(k == Knack || k == Kesc){
				done = !n;
				n = tick2 = tick = 0;
				buf[n] = 0;
				break;
			}
			if(k == Ksoh || k == Khome){
				tick2 = tick = 0;
				continue;
			}
			if(k == Kenq || k == Kend){
				tick2 = tick = n;
				continue;
			}
			if(k == Kright){
				if(tick2 < n)
					tick2 = tick += chartorune(&k, buf+tick);
				continue;
			}
			if(k == Kleft){
				for(i = 0; i < n; i += l){
					l = chartorune(&k, buf+i);
					if(i+l >= tick){
						tick2 = tick = i;
						break;
					}
				}
				continue;
			}
			if(k == Ketb){
				l = tick;
				while(tick > 0){
					tick--;
					if(tick == 0 ||
						strchr(" !\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", buf[tick-1]))
						break;
				}
				memmove(buf+tick, buf+l, n-l);
				buf[n -= l-tick] = 0;
				break;
			}
			if(k == Kbs){
				if(tick <= 0 && tick2 <= 0)
					continue;
				if (tick == tick2)
					for(i = 0; i < n; i += l){
						l = chartorune(&k, buf+i);
						if(i+l >= tick){
							memmove(buf+i, buf+i+l, n - (i+l));
							buf[n -= l] = 0;
							tick2 = tick -= l;
							break;
						}
					}
				else
					tick = tick2 = delsubstring(buf, n, tick-1, tick2, &n);
				break;
			}
			if(k < 0x20 || k == Kdel || (k & 0xFF00) == KF || (k & 0xFF00) == Spec)
				continue;
			if((len-n) <= (l = runelen(k)))
				continue;
			tick = delsubstring(buf, n, tick, tick2, &n);
			memmove(buf+tick+l, buf+tick, n - tick);
			runetochar(buf+tick, &k);
			buf[n += l] = 0;
			tick2 = tick += l;
			break;
		case Emouse:
			*m = ev.mouse;
			if(mb&1 && !m->buttons&1) {
				sorttick(&tick, &tick2);
				mb = m->buttons;
			}
			if(m->buttons&1 && mb&6){
				if (m->buttons != mb)
					goto Mchecks;
				continue;
			}
			if(m->buttons&1 && mb&6){
				continue;
			}
			if(!ptinrect(m->xy, r)){
				down = 0;
				goto nodraw;
			}
			if(m->buttons&1){
				down = 1;
				if(buf && m->xy.x >= (t.x - w)){
					down = 0;
					for(i = 0; i < n; i += l){
						l = chartorune(&k, buf+i);
						t.x += stringnwidth(font, buf+i, 1);
						if(t.x > m->xy.x)
							break;
					}
					if(mb & 1){
						tick2 = i;
					}else
						tick = tick2 = i;
				}
				if(!((m->buttons&2) || (m->buttons&4))){
					mb = m->buttons;
					continue;
				}
			}
Mchecks:
			if(!(mb&2) && (m->buttons&3) == 3){
				tick = tick2 = entersnarf(buf, n, tick, tick2, &n);
				mb = m->buttons;
			}
			if(!(mb&4) && (m->buttons&5) == 5){
				enterpaste(buf, n, len, tick, tick2, &n, &tick, &tick2);
				mb = m->buttons;
			}

			done = down;
			break;
		}
		if(save){
			draw(b, save->r, save, nil, save->r.min);
			freeimage(save);
			save = nil;
		}
	}

	replclipr(b, 0, sc);

	freeimage(backcol);
	freeimage(bordcol);
	flushimage(display, 1);

	return n;
}