#include <u.h>#include <libc.h>#include <draw.h>#include <mouse.h>#include <keyboard.h>#include <thread.h>#include <plumb.h>#include <regexp.h>#include "w.h" extern void mesgmenuhit(int, Mouse); typedef struct Highlight Highlight; struct Highlight{ char *pattern; ulong color; Reprog *re; Image *i;}; Highlight hilights[] = { /* diff */ { "^--- a.*$", 0x458588ff, nil }, { "^\\+\\+\\+ b.*$", 0x458588ff, nil }, { "^-[^\\-].*$", 0xcc241dFF, nil }, { "^\\+.*$", 0x98971AFF, nil }, /* quote */ { "^>.*$", 0x928374FF, nil },}; enum{ Scrollwidth = 12, Padding = 4,}; enum{ Msnarf, Mplumb,};char *menu2str[] = { "snarf", "plumb", nil,};Menu menu2 = { menu2str }; Image*findhilight(Text *t, int e){ Image *h; char c, *s; int i; h = nil; s = t->data + t->lines[t->nlines - 1]; c = t->data[e]; t->data[e] = 0; for(i = 0; i < nelem(hilights); i++) if(regexec(hilights[i].re, s, nil, 0)){ h = hilights[i].i; break; } t->data[e] = c; return h;} voidcomputelines(Text *t){ int i, x, w, l, c, s, wrap; Rune r; Image *h; t->lines[0] = 0; t->nlines = 1; w = Dx(t->textr); x = 0; wrap = 0; h = nil; for(i = 0; i < t->ndata; ){ s = i; c = chartorune(&r, &t->data[i]); if(r == '\n'){ if(i + c == t->ndata){ t->high[t->nlines - 1] = findhilight(t, i+c); break; } if(wrap) t->high[t->nlines - 1] = h; else t->high[t->nlines - 1] = findhilight(t, i+c); t->lines[t->nlines++] = i + c; x = 0; wrap = 0; h = nil; }else{ l = 0; if(r == '\t'){ x += stringwidth(t->font, " "); }else{ l = runestringnwidth(t->font, &r, 1); x += l; if(r == ' ') s = i + 1; } if(x > w){ i = s; h = findhilight(t, i); t->high[t->nlines - 1] = h; t->lines[t->nlines++] = i; x = l; wrap = 1; } } i += c; }} intindexat(Text *t, Point p){ int line, i, s, e, x, c, l; Rune r; if(!ptinrect(p, t->textr)) return -1; line = t->offset + ((p.y - t->textr.min.y) / font->height); s = t->lines[line]; if(line+1 >= t->nlines) e = t->ndata; else e = t->lines[line+1] - 2; /* make sure we exclude the newline */ c = 0; x = t->textr.min.x; for(i = s; i < e; ){ c = chartorune(&r, &t->data[i]); if(r == '\t') l = stringwidth(t->font, " "); else l = runestringnwidth(t->font, &r, 1); if(x <= p.x && p.x <= x+l) break; i += c; x += l; } if(r == '\n') i -= c; return i;} voidtextinit(Text *t, Image *b, Rectangle r, Font *f, Image *cols[NCOLS]){ int i; memset(t, 0, sizeof *t); t->b = b; t->font = f; t->s0 = -1; t->s1 = -1; t->offset = 0; textresize(t, r); memmove(t->cols, cols, sizeof t->cols); for(i = 0; i < nelem(hilights); i++){ hilights[i].re = regcomp(hilights[i].pattern); hilights[i].i = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, hilights[i].color); }} voidtextset(Text *t, char *data, usize ndata){ t->s0 = -1; t->s1 = -1; t->offset = 0; t->data = data; t->ndata = ndata; memset(t->high, 0, Maxlines*sizeof(Image*)); computelines(t);} voidtextresize(Text *t, Rectangle r){ t->r = r; t->vlines = Dy(t->r) / t->font->height; t->scrollr = rectaddpt(Rect(0, 0, Scrollwidth, Dy(r)), r.min); t->textr = r; t->textr.min.x = t->scrollr.max.x + Padding; if(t->nlines > 0) computelines(t);} intselected(Text *t, int index){ int s0, s1; if(t->s0 < 0 || t->s1 < 0) return 0; s0 = t->s0 < t->s1 ? t->s0 : t->s1; s1 = t->s0 > t->s1 ? t->s0 : t->s1; return s0 <= index && index <= s1;} voiddrawline(Text *t, int index){ int i, s, e, sel; Point p; Rune r; Image *fg, *bg; s = t->lines[t->offset+index]; if(t->offset+index+1 >= t->nlines) e = t->ndata; else e = t->lines[t->offset+index+1]; p = addpt(t->textr.min, Pt(0, index*font->height)); for(i = s; i < e; ){ sel = selected(t, i); fg = sel ? t->cols[HTEXT] : t->cols[TEXT]; bg = sel ? t->cols[HIGH] : t->cols[BACK]; if(!sel && t->high[t->offset+index] != nil) fg = t->high[t->offset+index]; i += chartorune(&r, &t->data[i]); if(r == '\n') if(s + 1 == e) /* empty line */ r = L' '; else continue; if(r == '\t') p = stringbg(t->b, p, fg, ZP, t->font, " ", bg, ZP); else p = runestringnbg(t->b, p, fg, ZP, t->font, &r, 1, bg, ZP); }} voidtextdraw(Text *t){ int i, h, y; Rectangle sr; draw(t->b, t->r, t->cols[BACK], nil, ZP); draw(t->b, t->scrollr, t->cols[BORD], nil, ZP); border(t->b, t->scrollr, 0, t->cols[TEXT], ZP); if(t->nlines > 0){ h = ((double)t->vlines / t->nlines) * Dy(t->scrollr); y = ((double)t->offset / t->nlines) * Dy(t->scrollr); sr = Rect(t->scrollr.min.x + 1, t->scrollr.min.y + y + 1, t->scrollr.max.x - 1, t->scrollr.min.y + y + h - 1); }else sr = insetrect(t->scrollr, -1); draw(t->b, sr, t->cols[BACK], nil, ZP); for(i = 0; i < t->vlines; i++){ if(t->offset+i >= t->nlines) break; drawline(t, i); } flushimage(display, 1);} staticvoidscroll(Text *t, int lines){ if(t->nlines <= t->vlines) return; if(lines < 0 && t->offset == 0) return; if(lines > 0 && t->offset + t->vlines >= t->nlines) return; t->offset += lines; if(t->offset < 0) t->offset = 0; if(t->offset + t->nlines%t->vlines >= t->nlines) t->offset = t->nlines - t->nlines%t->vlines; textdraw(t);} voidtextkeyboard(Text *t, Rune k){ switch(k){ case Kup: scroll(t, -1); break; case Kdown: scroll(t, 1); break; case Kpgup: scroll(t, -t->vlines); break; case Kpgdown: scroll(t, t->vlines); break; case Khome: scroll(t, -t->nlines); break; case Kend: scroll(t, t->nlines); break; }} voidsnarfsel(Text *t){ int fd, s0, s1; if(t->s0 < 0 || t->s1 < 0) return; fd = open("/dev/snarf", OWRITE); if(fd < 0) return; s0 = t->s0 < t->s1 ? t->s0 : t->s1; s1 = t->s0 > t->s1 ? t->s0 : t->s1; write(fd, &t->data[s0], s1 - s0 + 1); close(fd);} voidplumbsel(Text *t){ int fd, s0, s1; char *s; if(t->s0 < 0 || t->s1 < 0) return; fd = plumbopen("send", OWRITE); if(fd < 0) return; s0 = t->s0 < t->s1 ? t->s0 : t->s1; s1 = t->s0 > t->s1 ? t->s0 : t->s1; s = smprint("%.*s", s1 - s0 + 1, &t->data[s0]); plumbsendtext(fd, argv0, nil, nil, s); free(s); close(fd);} voidmenu2hit(Text *t, Mousectl *mc){ int n; n = menuhit(2, mc, &menu2, nil); switch(n){ case Msnarf: snarfsel(t); break; case Mplumb: plumbsel(t); break; }} voidtextmouse(Text *t, Mousectl *mc){ static selecting = 0; Point p; int n; if(ptinrect(mc->xy, t->scrollr)){ if(mc->buttons == 1){ n = (mc->xy.y - t->scrollr.min.y) / font->height; scroll(t, -n); }else if(mc->buttons == 2){ t->offset = (mc->xy.y - t->scrollr.min.y) * t->nlines / Dy(t->scrollr); textdraw(t); }else if(mc->buttons == 4){ n = (mc->xy.y - t->scrollr.min.y) / font->height; scroll(t, n); } }else if(ptinrect(mc->xy, t->textr)){ if(mc->buttons == 0) selecting = 0; if(mc->buttons == 1){ if(!selecting){ selecting = 1; t->s0 = t->s1 = -1; n = indexat(t, mc->xy); if(n < 0) return; t->s0 = n; t->s1 = -1; textdraw(t); }else{ n = indexat(t, mc->xy); if(n < 0) return; t->s1 = n; } for(readmouse(mc); mc->buttons == 1; readmouse(mc)){ p = mc->xy; if(p.y <= t->textr.min.y){ scroll(t, -1); p.y = t->textr.min.y + 1; }else if(p.y >= t->textr.max.y){ scroll(t, 1); p.y = t->textr.max.y - 1; } n = indexat(t, p); if(n < 0) break; t->s1 = n; textdraw(t); } }else if(mc->buttons == 2){ menu2hit(t, mc); }else if(mc->buttons == 4){ mesgmenuhit(3, *mc); }else if(mc->buttons == 8){ n = mousescrollsize(t->vlines); scroll(t, -n); }else if(mc->buttons == 16){ n = mousescrollsize(t->vlines); scroll(t, n); } }}
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <thread.h>
#include <plumb.h>
#include <regexp.h>
#include "w.h"
extern void mesgmenuhit(int, Mouse);
typedef struct Highlight Highlight;
struct Highlight
{
char *pattern;
ulong color;
Reprog *re;
Image *i;
};
Highlight hilights[] = {
/* diff */
{ "^--- a.*$", 0x458588ff, nil },
{ "^\\+\\+\\+ b.*$", 0x458588ff, nil },
{ "^-[^\\-].*$", 0xcc241dFF, nil },
{ "^\\+.*$", 0x98971AFF, nil },
/* quote */
{ "^>.*$", 0x928374FF, nil },
};
enum
{
Scrollwidth = 12,
Padding = 4,
};
enum
{
Msnarf,
Mplumb,
};
char *menu2str[] = {
"snarf",
"plumb",
nil,
};
Menu menu2 = { menu2str };
Image*
findhilight(Text *t, int e)
{
Image *h;
char c, *s;
int i;
h = nil;
s = t->data + t->lines[t->nlines - 1];
c = t->data[e];
t->data[e] = 0;
for(i = 0; i < nelem(hilights); i++)
if(regexec(hilights[i].re, s, nil, 0)){
h = hilights[i].i;
break;
}
t->data[e] = c;
return h;
}
void
computelines(Text *t)
{
int i, x, w, l, c, s, wrap;
Rune r;
Image *h;
t->lines[0] = 0;
t->nlines = 1;
w = Dx(t->textr);
x = 0;
wrap = 0;
h = nil;
for(i = 0; i < t->ndata; ){
s = i;
c = chartorune(&r, &t->data[i]);
if(r == '\n'){
if(i + c == t->ndata){
t->high[t->nlines - 1] = findhilight(t, i+c);
break;
}
if(wrap)
t->high[t->nlines - 1] = h;
else
t->high[t->nlines - 1] = findhilight(t, i+c);
t->lines[t->nlines++] = i + c;
x = 0;
wrap = 0;
h = nil;
}else{
l = 0;
if(r == '\t'){
x += stringwidth(t->font, " ");
}else{
l = runestringnwidth(t->font, &r, 1);
x += l;
if(r == ' ')
s = i + 1;
}
if(x > w){
i = s;
h = findhilight(t, i);
t->high[t->nlines - 1] = h;
t->lines[t->nlines++] = i;
x = l;
wrap = 1;
}
}
i += c;
}
}
int
indexat(Text *t, Point p)
{
int line, i, s, e, x, c, l;
Rune r;
if(!ptinrect(p, t->textr))
return -1;
line = t->offset + ((p.y - t->textr.min.y) / font->height);
s = t->lines[line];
if(line+1 >= t->nlines)
e = t->ndata;
else
e = t->lines[line+1] - 2; /* make sure we exclude the newline */
c = 0;
x = t->textr.min.x;
for(i = s; i < e; ){
c = chartorune(&r, &t->data[i]);
if(r == '\t')
l = stringwidth(t->font, " ");
else
l = runestringnwidth(t->font, &r, 1);
if(x <= p.x && p.x <= x+l)
break;
i += c;
x += l;
}
if(r == '\n')
i -= c;
return i;
}
void
textinit(Text *t, Image *b, Rectangle r, Font *f, Image *cols[NCOLS])
{
int i;
memset(t, 0, sizeof *t);
t->b = b;
t->font = f;
t->s0 = -1;
t->s1 = -1;
t->offset = 0;
textresize(t, r);
memmove(t->cols, cols, sizeof t->cols);
for(i = 0; i < nelem(hilights); i++){
hilights[i].re = regcomp(hilights[i].pattern);
hilights[i].i = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, hilights[i].color);
}
}
void
textset(Text *t, char *data, usize ndata)
{
t->s0 = -1;
t->s1 = -1;
t->offset = 0;
t->data = data;
t->ndata = ndata;
memset(t->high, 0, Maxlines*sizeof(Image*));
computelines(t);
}
void
textresize(Text *t, Rectangle r)
{
t->r = r;
t->vlines = Dy(t->r) / t->font->height;
t->scrollr = rectaddpt(Rect(0, 0, Scrollwidth, Dy(r)), r.min);
t->textr = r;
t->textr.min.x = t->scrollr.max.x + Padding;
if(t->nlines > 0)
computelines(t);
}
int
selected(Text *t, int index)
{
int s0, s1;
if(t->s0 < 0 || t->s1 < 0)
return 0;
s0 = t->s0 < t->s1 ? t->s0 : t->s1;
s1 = t->s0 > t->s1 ? t->s0 : t->s1;
return s0 <= index && index <= s1;
}
void
drawline(Text *t, int index)
{
int i, s, e, sel;
Point p;
Rune r;
Image *fg, *bg;
s = t->lines[t->offset+index];
if(t->offset+index+1 >= t->nlines)
e = t->ndata;
else
e = t->lines[t->offset+index+1];
p = addpt(t->textr.min, Pt(0, index*font->height));
for(i = s; i < e; ){
sel = selected(t, i);
fg = sel ? t->cols[HTEXT] : t->cols[TEXT];
bg = sel ? t->cols[HIGH] : t->cols[BACK];
if(!sel && t->high[t->offset+index] != nil)
fg = t->high[t->offset+index];
i += chartorune(&r, &t->data[i]);
if(r == '\n')
if(s + 1 == e) /* empty line */
r = L' ';
else
continue;
if(r == '\t')
p = stringbg(t->b, p, fg, ZP, t->font, " ", bg, ZP);
else
p = runestringnbg(t->b, p, fg, ZP, t->font, &r, 1, bg, ZP);
}
}
void
textdraw(Text *t)
{
int i, h, y;
Rectangle sr;
draw(t->b, t->r, t->cols[BACK], nil, ZP);
draw(t->b, t->scrollr, t->cols[BORD], nil, ZP);
border(t->b, t->scrollr, 0, t->cols[TEXT], ZP);
if(t->nlines > 0){
h = ((double)t->vlines / t->nlines) * Dy(t->scrollr);
y = ((double)t->offset / t->nlines) * Dy(t->scrollr);
sr = Rect(t->scrollr.min.x + 1, t->scrollr.min.y + y + 1, t->scrollr.max.x - 1, t->scrollr.min.y + y + h - 1);
}else
sr = insetrect(t->scrollr, -1);
draw(t->b, sr, t->cols[BACK], nil, ZP);
for(i = 0; i < t->vlines; i++){
if(t->offset+i >= t->nlines)
break;
drawline(t, i);
}
flushimage(display, 1);
}
static
void
scroll(Text *t, int lines)
{
if(t->nlines <= t->vlines)
return;
if(lines < 0 && t->offset == 0)
return;
if(lines > 0 && t->offset + t->vlines >= t->nlines)
return;
t->offset += lines;
if(t->offset < 0)
t->offset = 0;
if(t->offset + t->nlines%t->vlines >= t->nlines)
t->offset = t->nlines - t->nlines%t->vlines;
textdraw(t);
}
void
textkeyboard(Text *t, Rune k)
{
switch(k){
case Kup:
scroll(t, -1);
break;
case Kdown:
scroll(t, 1);
break;
case Kpgup:
scroll(t, -t->vlines);
break;
case Kpgdown:
scroll(t, t->vlines);
break;
case Khome:
scroll(t, -t->nlines);
break;
case Kend:
scroll(t, t->nlines);
break;
}
}
void
snarfsel(Text *t)
{
int fd, s0, s1;
if(t->s0 < 0 || t->s1 < 0)
return;
fd = open("/dev/snarf", OWRITE);
if(fd < 0)
return;
s0 = t->s0 < t->s1 ? t->s0 : t->s1;
s1 = t->s0 > t->s1 ? t->s0 : t->s1;
write(fd, &t->data[s0], s1 - s0 + 1);
close(fd);
}
void
plumbsel(Text *t)
{
int fd, s0, s1;
char *s;
if(t->s0 < 0 || t->s1 < 0)
return;
fd = plumbopen("send", OWRITE);
if(fd < 0)
return;
s0 = t->s0 < t->s1 ? t->s0 : t->s1;
s1 = t->s0 > t->s1 ? t->s0 : t->s1;
s = smprint("%.*s", s1 - s0 + 1, &t->data[s0]);
plumbsendtext(fd, argv0, nil, nil, s);
free(s);
close(fd);
}
void
menu2hit(Text *t, Mousectl *mc)
{
int n;
n = menuhit(2, mc, &menu2, nil);
switch(n){
case Msnarf:
snarfsel(t);
break;
case Mplumb:
plumbsel(t);
break;
}
}
void
textmouse(Text *t, Mousectl *mc)
{
static selecting = 0;
Point p;
int n;
if(ptinrect(mc->xy, t->scrollr)){
if(mc->buttons == 1){
n = (mc->xy.y - t->scrollr.min.y) / font->height;
scroll(t, -n);
}else if(mc->buttons == 2){
t->offset = (mc->xy.y - t->scrollr.min.y) * t->nlines / Dy(t->scrollr);
textdraw(t);
}else if(mc->buttons == 4){
n = (mc->xy.y - t->scrollr.min.y) / font->height;
scroll(t, n);
}
}else if(ptinrect(mc->xy, t->textr)){
if(mc->buttons == 0)
selecting = 0;
if(mc->buttons == 1){
if(!selecting){
selecting = 1;
t->s0 = t->s1 = -1;
n = indexat(t, mc->xy);
if(n < 0)
return;
t->s0 = n;
t->s1 = -1;
textdraw(t);
}else{
n = indexat(t, mc->xy);
if(n < 0)
return;
t->s1 = n;
}
for(readmouse(mc); mc->buttons == 1; readmouse(mc)){
p = mc->xy;
if(p.y <= t->textr.min.y){
scroll(t, -1);
p.y = t->textr.min.y + 1;
}else if(p.y >= t->textr.max.y){
scroll(t, 1);
p.y = t->textr.max.y - 1;
}
n = indexat(t, p);
if(n < 0)
break;
t->s1 = n;
textdraw(t);
}
}else if(mc->buttons == 2){
menu2hit(t, mc);
}else if(mc->buttons == 4){
mesgmenuhit(3, *mc);
}else if(mc->buttons == 8){
n = mousescrollsize(t->vlines);
scroll(t, -n);
}else if(mc->buttons == 16){
n = mousescrollsize(t->vlines);
scroll(t, n);
}
}
}