shithub: mez

ref: a4d0372d0f1916d1d9df34c4fe4ae7a98c8de020
dir: /text.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <mouse.h>
#include <frame.h>
#include "mez.h"

enum {
	Scrollwid = 12,		/* width of scroll bar */
	Scrollgap = 4,		/* gap right of scroll bar */
	Margin = 4,	/* margin around text */
};

#define max(a, b) ((a)>(b)?(a):(b))
#define min(a, b) ((a)<(b)?(a):(b))

void
textdraw(Text *t)
{
	Rectangle scrpos;
	Image *screen;

	screen = *t->screen;
	draw(screen, screen->r, t->cols[BACK], nil, ZP);
	draw(screen, t->scrollr, t->cols[BORD], nil, ZP);

	scrpos = t->scrollr;
	if(t->nlines > 0){
		scrpos.min.y = scrpos.min.y+max(0, Dy(scrpos))*t->topline/t->nlines;
		scrpos.max.y = scrpos.min.y+Dy(t->scrollr)*t->Frame.maxlines/t->nlines;
	}
	scrpos = insetrect(scrpos, 1);
	draw(screen, scrpos, t->cols[BACK], nil, ZP);

	frinsert(t, t->text+t->lines[t->topline], t->text+t->textlen, 0);
	flushimage(display, 1);
}

void
textinit(Text *t)
{
	Image *screen;

	screen = *t->screen;
	t->scrollr = screen->r;
	t->scrollr.max.x = screen->r.min.x + Scrollwid + 1;

	t->bodyr.min.x = screen->r.min.x + Scrollwid + Scrollgap + Margin;
	t->bodyr.min.y = screen->r.min.y;
	t->bodyr.max = screen->r.max;

	if(t->text == nil){
		t->textcap = 8192;
		t->text = emallocz(t->textcap*sizeof(*t->text), 1);
		t->textlen = 0;
	}

	if(t->lines == nil){
		t->linescap = 1024;
		t->lines = emallocz(t->linescap*sizeof(*t->lines), 1);
		t->nlines = 0;
		t->topline = 0;
	}

	frclear(t, 0);
	frinit(t, t->bodyr, display->defaultfont, screen, t->cols);
}

void
textaddline(Text *t, uint sol)
{
	if(t->nlines == t->linescap){
		t->linescap *= 2;
		t->lines = erealloc(t->lines, t->linescap*sizeof(*t->lines));
	}
	t->lines[t->nlines++] = sol;
}

void
textaddrunes(Text *t, Rune *s)
{
	Rune *rp;
	int len;

	len = runestrlen(s);
	while(len > t->textcap-t->textlen+1){
		t->textcap *= 2;
		t->text = erealloc(t->text, t->textcap*sizeof(*t->text));
	}
	runestrecpy(t->text+t->textlen, t->text+t->textcap, s);
	for(rp = t->text+t->textlen; *rp != L'\0'; rp++)
		if(*rp == L'\n') textaddline(t, rp+1-t->text);
	t->textlen += len;
}

void
textresize(Text *t)
{
	static char s[512];

	snprint(s, sizeof(s), "%s/winname", t->wsys);
	if(gengetwindow(display, s, t->screen, t->_screen, Refnone) < 0)
		sysfatal("%s: %r", argv0);
	textinit(t);
	textdraw(t);
}

void
textscroll(Text *t, Mousectl *mc)
{
	uint pos, i;

	while(mc->buttons == 2){
		t->topline = min(max(0, mc->xy.y-t->scrollr.min.y)*t->nlines/Dy(t->scrollr), t->nlines-1);
		textdraw(t);
		readmouse(mc);
	}
	while(mc->buttons == 1){
		pos = frcharofpt(t, (Point){t->bodyr.min.x+1, mc->xy.y});
		for(i = t->topline; t->lines[t->topline]+pos > t->lines[i]; i++){
			if(i == t->nlines) break;
		}
		i -= t->topline;
		if(t->topline < i) t->topline = 0;
		else t->topline -= i;
		textdraw(t);
		readmouse(mc);
	}
	while(mc->buttons == 4){
		pos = frcharofpt(t, (Point){t->bodyr.min.x+1, mc->xy.y});
		for(i = t->topline; t->lines[t->topline]+pos > t->lines[i]; i++);
		t->topline = min(i, t->nlines-1);
		textdraw(t);
		readmouse(mc);
	}
}

void
textmouse(Text *t, Mousectl *mc)
{
	if(ptinrect(mc->xy, t->scrollr)){
		textscroll(t, mc);
		return;
	}
}