plan9fox/sys/src/cmd/gopher/libpanel/edit.c

298 lines
6.8 KiB
C

/*
* Interface includes:
* void plescroll(Panel *p, int top);
* move the given character position onto the top line
* void plegetsel(Panel *p, int *sel0, int *sel1);
* read the selection back
* int plelen(Panel *p);
* read the length of the text back
* Rune *pleget(Panel *p);
* get a pointer to the text
* void plesel(Panel *p, int sel0, int sel1);
* set the selection -- adjusts hiliting
* void plepaste(Panel *p, Rune *text, int ntext);
* replace the selection with the given text
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
#include <keyboard.h>
typedef struct Edit Edit;
struct Edit{
Point minsize;
void (*hit)(Panel *);
int sel0, sel1;
Textwin *t;
Rune *text;
int ntext;
};
void pl_drawedit(Panel *p){
Edit *ep;
Panel *sb;
ep=p->data;
if(ep->t==0){
ep->t=twnew(p->b, font, ep->text, ep->ntext);
if(ep->t==0){
fprint(2, "pl_drawedit: can't allocate\n");
exits("no mem");
}
}
ep->t->b=p->b;
twreshape(ep->t, p->r);
twhilite(ep->t, ep->sel0, ep->sel1, 1);
sb=p->yscroller;
if(sb && sb->setscrollbar)
sb->setscrollbar(sb, ep->t->top, ep->t->bot, ep->t->etext-ep->t->text);
}
char *pl_snarfedit(Panel *p){
int s0, s1;
Rune *t;
t=pleget(p);
plegetsel(p, &s0, &s1);
if(t==0 || s0>=s1)
return nil;
return smprint("%.*S", s1-s0, t+s0);
}
void pl_pasteedit(Panel *p, char *s){
Rune *t;
if(t=runesmprint("%s", s)){
plepaste(p, t, runestrlen(t));
free(t);
}
}
/*
* Should do double-clicks:
* If ep->sel0==ep->sel1 on entry and the
* call to twselect returns the same selection, then
* expand selections (| marks possible selection points, ... is expanded selection)
* <|...|> <> must nest
* (|...|) () must nest
* [|...|] [] must nest
* {|...|} {} must nest
* '|...|' no ' in ...
* "|...|" no " in ...
* \n|...|\n either newline may be the corresponding end of text
* include the trailing newline in the selection
* ...|I... I and ... are characters satisfying pl_idchar(I)
* ...I|
*/
int pl_hitedit(Panel *p, Mouse *m){
Edit *ep;
ep=p->data;
if(ep->t && m->buttons&1){
plgrabkb(p);
ep->t->b=p->b;
twhilite(ep->t, ep->sel0, ep->sel1, 0);
twselect(ep->t, m);
ep->sel0=ep->t->sel0;
ep->sel1=ep->t->sel1;
if((m->buttons&7)==3){
plsnarf(p);
plepaste(p, 0, 0); /* cut */
}
else if((m->buttons&7)==5)
plpaste(p);
else if(ep->hit)
(*ep->hit)(p);
}
return 0;
}
void pl_scrolledit(Panel *p, int dir, int buttons, int num, int den){
Edit *ep;
Textwin *t;
Panel *sb;
int index, nline;
if(dir!=VERT) return;
ep=p->data;
t=ep->t;
if(t==0) return;
t->b=p->b;
switch(buttons){
default:
return;
case 1: /* top line moves to mouse position */
nline=(t->r.max.y-t->r.min.y)/t->hgt*num/den;
index=t->top;
while(index!=0 && nline!=0)
if(t->text[--index]=='\n') --nline;
break;
case 2: /* absolute */
index=(t->etext-t->text)*num/den;
break;
case 4: /* mouse points at new top line */
index=twpt2rune(t,
Pt(t->r.min.x, t->r.min.y+(t->r.max.y-t->r.min.y)*num/den));
break;
}
while(index!=0 && t->text[index-1]!='\n') --index;
if(index!=t->top){
twhilite(ep->t, ep->sel0, ep->sel1, 0);
twscroll(t, index);
p->scr.pos.y=t->top;
twhilite(ep->t, ep->sel0, ep->sel1, 1);
sb=p->yscroller;
if(sb && sb->setscrollbar)
sb->setscrollbar(sb, t->top, t->bot, t->etext-t->text);
}
}
void pl_typeedit(Panel *p, Rune c){
Edit *ep;
Textwin *t;
int bot, scrolled;
Panel *sb;
ep=p->data;
t=ep->t;
if(t==0) return;
t->b=p->b;
twhilite(t, ep->sel0, ep->sel1, 0);
switch(c){
case Kesc:
plsnarf(p);
plepaste(p, 0, 0); /* cut */
break;
case Kdel: /* clear */
ep->sel0=0;
ep->sel1=plelen(p);
plepaste(p, 0, 0); /* cut */
break;
case Kbs: /* ^H: erase character */
if(ep->sel0!=0) --ep->sel0;
twreplace(t, ep->sel0, ep->sel1, 0, 0);
break;
case Knack: /* ^U: erase line */
while(ep->sel0!=0 && t->text[ep->sel0-1]!='\n') --ep->sel0;
twreplace(t, ep->sel0, ep->sel1, 0, 0);
break;
case Ketb: /* ^W: erase word */
while(ep->sel0!=0 && !pl_idchar(t->text[ep->sel0-1])) --ep->sel0;
while(ep->sel0!=0 && pl_idchar(t->text[ep->sel0-1])) --ep->sel0;
twreplace(t, ep->sel0, ep->sel1, 0, 0);
break;
default:
if((c & 0xFF00) == KF || (c & 0xFF00) == Spec)
break;
twreplace(t, ep->sel0, ep->sel1, &c, 1);
++ep->sel0;
break;
}
ep->sel1=ep->sel0;
/*
* Scroll up until ep->sel0 is above t->bot.
*/
scrolled=0;
do{
bot=t->bot;
if(ep->sel0<=bot) break;
twscroll(t, twpt2rune(t, Pt(t->r.min.x, t->r.min.y+font->height)));
scrolled++;
}while(bot!=t->bot);
if(scrolled){
sb=p->yscroller;
if(sb && sb->setscrollbar)
sb->setscrollbar(sb, t->top, t->bot, t->etext-t->text);
}
twhilite(t, ep->sel0, ep->sel1, 1);
}
Point pl_getsizeedit(Panel *p, Point children){
USED(children);
return pl_boxsize(((Edit *)p->data)->minsize, p->state);
}
void pl_childspaceedit(Panel *g, Point *ul, Point *size){
USED(g, ul, size);
}
void pl_freeedit(Panel *p){
Edit *ep;
ep=p->data;
if(ep->t) twfree(ep->t);
ep->t=0;
}
void plinitedit(Panel *v, int flags, Point minsize, Rune *text, int ntext, void (*hit)(Panel *)){
Edit *ep;
ep=v->data;
v->flags=flags|LEAF;
v->state=UP;
v->draw=pl_drawedit;
v->hit=pl_hitedit;
v->type=pl_typeedit;
v->getsize=pl_getsizeedit;
v->childspace=pl_childspaceedit;
v->free=pl_freeedit;
v->snarf=pl_snarfedit;
v->paste=pl_pasteedit;
v->kind="edit";
ep->hit=hit;
ep->minsize=minsize;
ep->text=text;
ep->ntext=ntext;
if(ep->t!=0) twfree(ep->t);
ep->t=0;
ep->sel0=-1;
ep->sel1=-1;
v->scroll=pl_scrolledit;
v->scr.pos=Pt(0,0);
v->scr.size=Pt(ntext,0);
}
Panel *pledit(Panel *parent, int flags, Point minsize, Rune *text, int ntext, void (*hit)(Panel *)){
Panel *v;
v=pl_newpanel(parent, sizeof(Edit));
((Edit *)v->data)->t=0;
plinitedit(v, flags, minsize, text, ntext, hit);
return v;
}
void plescroll(Panel *p, int top){
Textwin *t;
t=((Edit*)p->data)->t;
if(t) twscroll(t, top);
}
void plegetsel(Panel *p, int *sel0, int *sel1){
Edit *ep;
ep=p->data;
*sel0=ep->sel0;
*sel1=ep->sel1;
}
int plelen(Panel *p){
Textwin *t;
t=((Edit*)p->data)->t;
if(t==0) return 0;
return t->etext-t->text;
}
Rune *pleget(Panel *p){
Textwin *t;
t=((Edit*)p->data)->t;
if(t==0) return 0;
return t->text;
}
void plesel(Panel *p, int sel0, int sel1){
Edit *ep;
ep=p->data;
if(ep->t==0) return;
ep->t->b=p->b;
twhilite(ep->t, ep->sel0, ep->sel1, 0);
ep->sel0=sel0;
ep->sel1=sel1;
twhilite(ep->t, ep->sel0, ep->sel1, 1);
}
void plepaste(Panel *p, Rune *text, int ntext){
Edit *ep;
ep=p->data;
if(ep->t==0) return;
ep->t->b=p->b;
twhilite(ep->t, ep->sel0, ep->sel1, 0);
twreplace(ep->t, ep->sel0, ep->sel1, text, ntext);
ep->sel1=ep->sel0+ntext;
twhilite(ep->t, ep->sel0, ep->sel1, 1);
p->scr.size.y=ep->t->etext-ep->t->text;
p->scr.pos.y=ep->t->top;
}
void plemove(Panel *p, Point d){
Edit *ep;
ep=p->data;
if(ep->t && !eqpt(d, Pt(0,0))) twmove(ep->t, d);
}