vt: implement proper selections (thanks Ori_B)

Ori Bernstein wrote:
> I finally got around to taking another shot at this vt patch. This change
> gets rid of implicit snarfing, and instead makes selection the way you
> select text for snarfing or plumbing. Select, then use a menu entry.
>
> It would probably be nice to have double click to expand the selection,
> rio-style, along with plumbing implicitly taking the current word, but
> that can be a separate patch.
>
> This change also punts on scrolling for simplicity -- it clears the
> selection instead of trying to handle the cases where the selection
> goes offscreen.

little amendments:

- fix line selection (point min/max inversion)
- clear selection when switching linesel/blocksel
- move selection on scroll
This commit is contained in:
cinap_lenrek 2018-09-03 20:05:26 +02:00
parent 4596959f3b
commit de9aa721f6

View file

@ -11,25 +11,48 @@
#include <bio.h>
#include <mouse.h>
#include <keyboard.h>
#include <plumb.h>
enum menuact2{
Mbackup,
Mforward,
Mreset,
Mclear,
Mpaste,
Msnarf,
Mplumb,
Mpage,
};
enum menuact3{
M24x80,
Mcrnl,
Mnl,
Mraw,
Mblocksel,
Mexit,
};
char *menutext2[] = {
"backup",
"forward",
"reset",
"clear",
"paste",
"page",
0
[Mbackup] "backup",
[Mforward] "forward",
[Mreset] "reset",
[Mclear] "clear",
[Mpaste] "paste",
[Msnarf] "snarf",
[Mplumb] "plumb",
[Mpage] "page",
nil
};
char *menutext3[] = {
"24x80",
"crnl",
"nl",
"raw",
"blocksel",
"exit",
0
[M24x80] "24x80",
[Mcrnl] "crnl",
[Mnl] "nl",
[Mraw] "raw",
[Mblocksel] "blocksel",
[Mexit] "exit",
nil
};
/* variables associated with the screen */
@ -68,6 +91,8 @@ uint scrolloff;
int yscrmin, yscrmax;
int attr, defattr;
Rectangle selrect;
Image *cursorsave;
Image *bordercol;
Image *colors[8];
@ -76,6 +101,8 @@ Image *red;
Image *green;
Image *fgcolor;
Image *bgcolor;
Image *fgselected;
Image *bgselected;
Image *highlight;
uint rgbacolors[8] = {
@ -138,11 +165,15 @@ int rcvchar(void);
void bigscroll(void);
void readmenu(void);
void selection(void);
int selected(int, int);
void resize(void);
void drawcursor(void);
void send_interrupt(void);
int alnum(int);
void escapedump(int,uchar *,int);
void paste(void);
void snarfsel(void);
void plumbsel(void);
static Channel *pidchan;
@ -296,6 +327,8 @@ threadmain(int argc, char **argv)
}
bgcolor = (blkbg? display->black: display->white);
fgcolor = (blkbg? display->white: display->black);
bgselected = allocimage(display, Rect(0,0,1,1), CMAP8, 1, blkbg ? 0x333333FF : 0xCCCCCCFF);
fgselected = allocimage(display, Rect(0,0,1,1), CMAP8, 1, blkbg ? 0xCCCCCCFF : 0x333333FF);;
resize();
pidchan = chancreate(sizeof(int), 0);
@ -306,8 +339,10 @@ threadmain(int argc, char **argv)
}
Image*
bgcol(int a, int c)
bgcol(int a, int c, int sel)
{
if(sel)
return bgselected;
if(nocolor || (c & (1<<0)) == 0){
if(a & TReverse)
return fgcolor;
@ -319,8 +354,10 @@ bgcol(int a, int c)
}
Image*
fgcol(int a, int c)
fgcol(int a, int c, int sel)
{
if(sel)
return fgselected;
if(nocolor || (c & (1<<4)) == 0){
if(a & TReverse)
return bgcolor;
@ -366,8 +403,8 @@ drawscreen(void)
for(x = 0; x <= xmax; x += n){
cp = onscreenc(x, y);
ap = onscreena(x, y);
c = bgcol(*ap, *cp);
for(n = 1; x+n <= xmax && bgcol(ap[n], cp[n]) == c; n++)
c = bgcol(*ap, *cp, selected(x, y));
for(n = 1; x+n <= xmax && bgcol(ap[n], cp[n], selected(x + n, y)) == c; n++)
;
draw(screen, Rpt(pt(x, y), pt(x+n, y+1)), c, nil, ZP);
}
@ -381,8 +418,8 @@ drawscreen(void)
}
ap = onscreena(x, y);
cp = onscreenc(x, y);
c = fgcol(*ap, *cp);
for(n = 1; x+n <= xmax && rp[n] != 0 && fgcol(ap[n], cp[n]) == c
c = fgcol(*ap, *cp, selected(x, y));
for(n = 1; x+n <= xmax && rp[n] != 0 && fgcol(ap[n], cp[n], selected(x + n, y)) == c
&& ((ap[n] ^ *ap) & TUnderline) == 0; n++)
;
p = pt(x, y);
@ -451,7 +488,7 @@ newline(void)
nbacklines--;
if(y >= yscrmax) {
y = yscrmax;
if(pagemode && olines >= yscrmax) {
if(pagemode && olines >= yscrmax){
blocked = 1;
return;
}
@ -472,7 +509,7 @@ get_next_char(void)
while(c <= 0) {
if(backp) {
c = *backp;
if(c && nbacklines >= 0) {
if(c && nbacklines >= 0){
backp++;
if(backp >= &hist[HISTSIZ])
backp = hist;
@ -876,108 +913,150 @@ resize(void)
werrstr(""); /* clear spurious error messages */
}
Rune *
selrange(Rune *r, int x0, int y0, int x1, int y1)
{
Rune *p, *sr, *er;
p = r;
sr = onscreenr(x0, y0);
er = onscreenr(x1, y1);
for(; sr != er; sr++)
if(*sr)
*p++ = *sr;
*p = 0;
return p;
}
Rune*
selrunes(void)
{
Rune *r, *p;
int sz;
int y;
/* generous, but we can spare a few bytes for a few microseconds */
sz = xmax*(selrect.max.y - selrect.min.y + 2) + 1;
r = p = malloc(sizeof(Rune)*sz + 1);
if(!r)
return nil;
if(blocksel){
for(y = selrect.min.y; y <= selrect.max.y; y++){
p = selrange(p, selrect.min.x, y, selrect.max.x, y);
*p++ = '\n';
}
*p = 0;
}
else
selrange(r, selrect.min.x, selrect.min.y, selrect.max.x, selrect.max.y);
return r;
}
void
sendsnarf(void)
snarfsel(void)
{
Biobuf *b;
Rune *r;
b = Bopen("/dev/snarf", OWRITE|OTRUNC);
if(b == nil)
return;
r = selrunes();
if(!r)
return;
Bprint(b, "%S", r);
Bterm(b);
free(r);
}
void
plumbsel(void)
{
char buf[1024], wdir[512];
Rune *r;
int plumb;
print("plumb\n");
if(getwd(wdir, sizeof wdir) == 0)
return;
if((r = selrunes()) == nil)
return;
print("wdir: %s, runes: %S\n", wdir, r);
if((plumb = plumbopen("send", OWRITE)) != -1){
snprint(buf, sizeof buf, "%S", r);
print("buf: '%s'\n", buf);
plumbsendtext(plumb, "vt", nil, wdir, buf);
}
close(plumb);
free(r);
}
void
paste(void)
{
if(snarffp == nil)
snarffp = Bopen("/dev/snarf",OREAD);
}
void
seputrunes(Biobuf *b, Rune *s, Rune *e)
unselect(void)
{
int z, p;
int y;
if(s >= e)
return;
for(z = p = 0; s < e; s++){
if(*s){
if(*s == '\n')
z = p = 0;
else if(p++ == 0){
while(z-- > 0) Bputc(b, ' ');
}
Bputrune(b, *s);
} else {
z++;
}
}
}
int
snarfrect(Rectangle r)
{
Biobuf *b;
b = Bopen("/dev/snarf", OWRITE|OTRUNC);
if(b == nil)
return 0;
if(blocksel){
while(r.min.y <= r.max.y){
seputrunes(b, onscreenr(r.min.x, r.min.y), onscreenr(r.max.x, r.min.y));
Bputrune(b, L'\n');
r.min.y++;
}
} else {
seputrunes(b, onscreenr(r.min.x, r.min.y), onscreenr(r.max.x, r.max.y));
}
Bterm(b);
return 1;
}
Rectangle
drawselection(Rectangle r, Rectangle d, Image *color)
{
if(!blocksel){
while(r.min.y < r.max.y){
d = drawselection(Rect(r.min.x, r.min.y, xmax+1, r.min.y), d, color);
r.min.x = 0;
r.min.y++;
}
}
if(r.min.x >= r.max.x)
return d;
r = Rpt(pt(r.min.x, r.min.y), pt(r.max.x, r.max.y+1));
draw(screen, r, color, highlight, r.min);
combinerect(&d, r);
return d;
for(y = selrect.min.y; y <= selrect.max.y; y++)
screenchange(y) = 1;
selrect = ZR;
}
void
selection(void)
{
Point p, q;
Rectangle r, d;
Image *backup;
int y;
backup = allocimage(display, screen->r, screen->chan, 0, DNofill);
draw(backup, backup->r, screen, nil, backup->r.min);
p = pos(mc->xy);
do {
do{
/* Clear the old selection rectangle. */
unselect();
q = pos(mc->xy);
if(onscreenr(p.x, p.y) > onscreenr(q.x, q.y)){
r.min = q;
r.max = p;
selrect.min = q;
selrect.max = p;
} else {
r.min = p;
r.max = q;
selrect.min = p;
selrect.max = q;
}
if(r.max.y > ymax)
r.max.x = 0;
d = drawselection(r, ZR, red);
flushimage(display, 1);
/* And mark the new one as changed. */
for(y = selrect.min.y; y <= selrect.max.y; y++)
screenchange(y) = 1;
readmouse(mc);
draw(screen, d, backup, nil, d.min);
drawscreen();
} while(button1());
if((mc->buttons & 07) == 5)
sendsnarf();
else if(snarfrect(r)){
d = drawselection(r, ZR, green);
flushimage(display, 1);
sleep(200);
draw(screen, d, backup, nil, d.min);
switch(mc->buttons & 0x7){
case 3: snarfsel(); break;
case 5: paste(); break;
}
freeimage(backup);
}
int
selected(int x, int y)
{
int s;
s = y >= selrect.min.y && y <= selrect.max.y;
if (blocksel)
s = s && x >= selrect.min.x && x < selrect.max.x;
else{
if(y == selrect.min.y)
s = s && x >= selrect.min.x;
if(y == selrect.max.y)
s = s && x < selrect.max.x;
if(y > selrect.min.y && y < selrect.max.y)
s = 1;
}
return s;
}
void
@ -990,39 +1069,39 @@ readmenu(void)
menu3.item[4] = blocksel ? "linesel" : "blocksel";
switch(menuhit(3, mc, &menu3, nil)) {
case 0: /* 24x80 */
case M24x80: /* 24x80 */
setdim(24, 80);
return;
case 1: /* newline after cr? */
case Mcrnl: /* newline after cr? */
ttystate[cs->raw].crnl = !ttystate[cs->raw].crnl;
return;
case 2: /* cr after newline? */
case Mnl: /* cr after newline? */
ttystate[cs->raw].nlcr = !ttystate[cs->raw].nlcr;
return;
case 3: /* switch raw mode */
case Mraw: /* switch raw mode */
cs->raw = !cs->raw;
return;
case 4:
case Mblocksel:
unselect();
blocksel = !blocksel;
return;
case 5:
case Mexit:
exits(0);
}
return;
}
menu2.item[5] = pagemode? "scroll": "page";
menu2.item[Mpage] = pagemode? "scroll": "page";
switch(menuhit(2, mc, &menu2, nil)) {
case 0: /* back up */
if(atend == 0) {
case Mbackup: /* back up */
if(atend == 0){
backc++;
backup(backc);
}
return;
case 1: /* move forward */
case Mforward: /* move forward */
backc--;
if(backc >= 0)
backup(backc);
@ -1030,20 +1109,28 @@ readmenu(void)
backc = 0;
return;
case 2: /* reset */
case Mreset: /* reset */
backc = 0;
backup(0);
return;
case 3: /* clear screen */
case Mclear: /* clear screen */
resize_flag = 1;
return;
case 4: /* send the snarf buffer */
sendsnarf();
case Mpaste: /* paste the snarf buffer */
paste();
return;
case 5: /* pause and clear at end of screen */
case Msnarf: /* send the snarf buffer */
snarfsel();
return;
case Mplumb:
plumbsel();
return;
case Mpage: /* pause and clear at end of screen */
pagemode = 1-pagemode;
if(blocked && !pagemode) {
resize_flag = 1;
@ -1059,6 +1146,8 @@ backup(int count)
Rune *cp;
int n;
unselect();
resize_flag = 1;
if(count == 0 && !pagemode) {
n = ymax;
@ -1154,6 +1243,22 @@ scroll(int sy, int ly, int dy, int cy) /* source, limit, dest, which line to cle
memmove(onscreenc(0, dy), onscreenc(0, sy), n*(xmax+2));
}
/* move selection */
selrect.min.y -= d;
selrect.max.y -= d;
if(selrect.max.y < 0 || selrect.min.y > ymax)
selrect = ZR;
else {
if(selrect.min.y < 0){
selrect.min.y = 0;
if(!blocksel) selrect.min.x = 0;
}
if(selrect.max.y > ymax){
selrect.max.y = ymax;
if(!blocksel) selrect.max.x = xmax+1;
}
}
clear(0, cy, xmax+1, cy+1);
}