543 lines
12 KiB
C
543 lines
12 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <draw.h>
|
|
#include <thread.h>
|
|
#include <mouse.h>
|
|
#include <keyboard.h>
|
|
#include <control.h>
|
|
|
|
static int debug = 0;
|
|
|
|
typedef struct Text Text;
|
|
|
|
struct Text
|
|
{
|
|
Control;
|
|
int border;
|
|
int topline;
|
|
int scroll;
|
|
int nvis;
|
|
int lastbut;
|
|
CFont *font;
|
|
CImage *image;
|
|
CImage *textcolor;
|
|
CImage *bordercolor;
|
|
CImage *selectcolor;
|
|
CImage *selectingcolor;
|
|
Rune **line;
|
|
int selectmode; // Selsingle, Selmulti
|
|
int selectstyle; // Seldown, Selup (use Selup only with Selsingle)
|
|
uchar *selected;
|
|
int nline;
|
|
int warp;
|
|
int align;
|
|
int sel; // line nr of selection made by last button down
|
|
int but; // last button down (still being hold)
|
|
int offsel; // we are on selection
|
|
};
|
|
|
|
enum
|
|
{
|
|
Selsingle,
|
|
Selmulti,
|
|
Seldown,
|
|
Selup,
|
|
};
|
|
|
|
enum{
|
|
EAccumulate,
|
|
EAdd,
|
|
EAlign,
|
|
EBorder,
|
|
EBordercolor,
|
|
EClear,
|
|
EDelete,
|
|
EFocus,
|
|
EFont,
|
|
EHide,
|
|
EImage,
|
|
ERect,
|
|
EReplace,
|
|
EReveal,
|
|
EScroll,
|
|
ESelect,
|
|
ESelectcolor,
|
|
ESelectingcolor,
|
|
ESelectmode,
|
|
ESelectstyle,
|
|
EShow,
|
|
ESize,
|
|
ETextcolor,
|
|
ETopline,
|
|
EValue,
|
|
EWarp,
|
|
};
|
|
|
|
static char *cmds[] = {
|
|
[EAccumulate] = "accumulate",
|
|
[EAdd] = "add",
|
|
[EAlign] = "align",
|
|
[EBorder] = "border",
|
|
[EBordercolor] = "bordercolor",
|
|
[EClear] = "clear",
|
|
[EDelete] = "delete",
|
|
[EFocus] = "focus",
|
|
[EFont] = "font",
|
|
[EHide] = "hide",
|
|
[EImage] = "image",
|
|
[ERect] = "rect",
|
|
[EReplace] = "replace",
|
|
[EReveal] = "reveal",
|
|
[EScroll] = "scroll",
|
|
[ESelect] = "select",
|
|
[ESelectcolor] = "selectcolor",
|
|
[ESelectingcolor] = "selectingcolor",
|
|
[ESelectmode] = "selectmode",
|
|
[ESelectstyle] = "selectstyle",
|
|
[EShow] = "show",
|
|
[ESize] = "size",
|
|
[ETextcolor] = "textcolor",
|
|
[ETopline] = "topline",
|
|
[EValue] = "value",
|
|
[EWarp] = "warp",
|
|
nil
|
|
};
|
|
|
|
static void textshow(Text*);
|
|
static void texttogglei(Text*, int);
|
|
static int textline(Text*, Point);
|
|
static int texttoggle(Text*, Point);
|
|
|
|
static void
|
|
textmouse(Control *c, Mouse *m)
|
|
{
|
|
Text *t;
|
|
int sel;
|
|
|
|
t = (Text*)c;
|
|
if (debug) fprint(2, "textmouse %s t->lastbut %d; m->buttons %d\n", t->name, t->lastbut, m->buttons);
|
|
if (t->warp >= 0)
|
|
return;
|
|
if ((t->selectstyle == Selup) && (m->buttons&7)) {
|
|
sel = textline(t, m->xy);
|
|
if (t->sel >= 0) {
|
|
// if (debug) fprint(2, "textmouse Selup %q sel=%d t->sel=%d t->but=%d\n",
|
|
// t->name, sel, t->sel, t->but);
|
|
t->offsel = (sel == t->sel) ? 0 : 1;
|
|
if ((sel == t->sel &&
|
|
((t->selected[t->sel] && !t->but) ||
|
|
((!t->selected[t->sel]) && t->but))) ||
|
|
(sel != t->sel &&
|
|
((t->selected[t->sel] && t->but) ||
|
|
((!t->selected[t->sel]) && (!t->but))))) {
|
|
texttogglei(t, t->sel);
|
|
}
|
|
}
|
|
}
|
|
if(t->lastbut != (m->buttons&7)){
|
|
if(m->buttons & 7){
|
|
sel = texttoggle(t, m->xy);
|
|
if(sel >= 0) {
|
|
if (t->selectstyle == Seldown) {
|
|
chanprint(t->event, "%q: select %d %d",
|
|
t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
|
|
if (debug) fprint(2, "textmouse Seldown event %q: select %d %d\n",
|
|
t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
|
|
} else {
|
|
if (debug) fprint(2, "textmouse Selup no event yet %q: select %d %d\n",
|
|
t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
|
|
t->sel = sel;
|
|
t->but = t->selected[sel] ? (m->buttons & 7) : 0;
|
|
}
|
|
}
|
|
} else if (t->selectstyle == Selup) {
|
|
sel = textline(t, m->xy);
|
|
t->offsel = 0;
|
|
if ((sel >= 0) && (sel == t->sel)) {
|
|
chanprint(t->event, "%q: select %d %d",
|
|
t->name, sel, t->but);
|
|
if (debug) fprint(2, "textmouse Selup event %q: select %d %d\n",
|
|
t->name, sel, t->but);
|
|
} else if (sel != t->sel) {
|
|
if ((t->selected[t->sel] && t->but) ||
|
|
((!t->selected[t->sel]) && (!t->but))) {
|
|
texttogglei(t, t->sel);
|
|
} else {
|
|
textshow(t);
|
|
}
|
|
if (debug) fprint(2, "textmouse Selup cancel %q: select %d %d\n",
|
|
t->name, sel, t->but);
|
|
}
|
|
t->sel = -1;
|
|
t->but = 0;
|
|
}
|
|
t->lastbut = m->buttons & 7;
|
|
}
|
|
}
|
|
|
|
static void
|
|
textfree(Control *c)
|
|
{
|
|
int i;
|
|
Text *t;
|
|
|
|
t = (Text*)c;
|
|
_putctlfont(t->font);
|
|
_putctlimage(t->image);
|
|
_putctlimage(t->textcolor);
|
|
_putctlimage(t->bordercolor);
|
|
_putctlimage(t->selectcolor);
|
|
_putctlimage(t->selectingcolor);
|
|
for(i=0; i<t->nline; i++)
|
|
free(t->line[i]);
|
|
free(t->line);
|
|
free(t->selected);
|
|
}
|
|
|
|
static void
|
|
textshow(Text *t)
|
|
{
|
|
Rectangle r, tr;
|
|
Point p;
|
|
int i, ntext;
|
|
Font *f;
|
|
Rune *text;
|
|
|
|
if (t->hidden)
|
|
return;
|
|
r = t->rect;
|
|
f = t->font->font;
|
|
draw(t->screen, r, t->image->image, nil, t->image->image->r.min);
|
|
if(t->border > 0){
|
|
border(t->screen, r, t->border, t->bordercolor->image, t->bordercolor->image->r.min);
|
|
r = insetrect(r, t->border);
|
|
}
|
|
tr = r;
|
|
t->nvis = Dy(r)/f->height;
|
|
for(i=t->topline; i<t->nline && i<t->topline+t->nvis; i++){
|
|
text = t->line[i];
|
|
ntext = runestrlen(text);
|
|
r.max.y = r.min.y+f->height;
|
|
if(t->sel == i && t->offsel)
|
|
draw(t->screen, r, t->selectingcolor->image, nil, ZP);
|
|
else if(t->selected[i])
|
|
draw(t->screen, r, t->selectcolor->image, nil, ZP);
|
|
p = _ctlalignpoint(r,
|
|
runestringnwidth(f, text, ntext),
|
|
f->height, t->align);
|
|
if(t->warp == i) {
|
|
Point p2;
|
|
p2.x = p.x + 0.5*runestringnwidth(f, text, ntext);
|
|
p2.y = p.y + 0.5*f->height;
|
|
moveto(t->controlset->mousectl, p2);
|
|
t->warp = -1;
|
|
}
|
|
_string(t->screen, p, t->textcolor->image,
|
|
ZP, f, nil, text, ntext, tr,
|
|
nil, ZP, SoverD);
|
|
r.min.y += f->height;
|
|
}
|
|
flushimage(display, 1);
|
|
}
|
|
|
|
static void
|
|
textctl(Control *c, CParse *cp)
|
|
{
|
|
int cmd, i, n;
|
|
Rectangle r;
|
|
Text *t;
|
|
Rune *rp;
|
|
|
|
t = (Text*)c;
|
|
cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
|
|
switch(cmd){
|
|
default:
|
|
ctlerror("%q: unrecognized message '%s'", t->name, cp->str);
|
|
break;
|
|
case EAlign:
|
|
_ctlargcount(t, cp, 2);
|
|
t->align = _ctlalignment(cp->args[1]);
|
|
break;
|
|
case EBorder:
|
|
_ctlargcount(t, cp, 2);
|
|
if(cp->iargs[1] < 0)
|
|
ctlerror("%q: bad border: %c", t->name, cp->str);
|
|
t->border = cp->iargs[1];
|
|
break;
|
|
case EBordercolor:
|
|
_ctlargcount(t, cp, 2);
|
|
_setctlimage(t, &t->bordercolor, cp->args[1]);
|
|
break;
|
|
case EClear:
|
|
_ctlargcount(t, cp, 1);
|
|
for(i=0; i<t->nline; i++)
|
|
free(t->line[i]);
|
|
free(t->line);
|
|
free(t->selected);
|
|
t->line = ctlmalloc(sizeof(Rune*));
|
|
t->selected = ctlmalloc(1);
|
|
t->nline = 0;
|
|
textshow(t);
|
|
break;
|
|
case EDelete:
|
|
_ctlargcount(t, cp, 2);
|
|
i = cp->iargs[1];
|
|
if(i<0 || i>=t->nline)
|
|
ctlerror("%q: line number out of range: %s", t->name, cp->str);
|
|
free(t->line[i]);
|
|
memmove(t->line+i, t->line+i+1, (t->nline-(i+1))*sizeof(Rune*));
|
|
memmove(t->selected+i, t->selected+i+1, t->nline-(i+1));
|
|
t->nline--;
|
|
textshow(t);
|
|
break;
|
|
case EFocus:
|
|
break;
|
|
case EFont:
|
|
_ctlargcount(t, cp, 2);
|
|
_setctlfont(t, &t->font, cp->args[1]);
|
|
break;
|
|
case EHide:
|
|
_ctlargcount(t, cp, 1);
|
|
t->hidden = 1;
|
|
break;
|
|
case EImage:
|
|
_ctlargcount(t, cp, 2);
|
|
_setctlimage(t, &t->image, cp->args[1]);
|
|
break;
|
|
case ERect:
|
|
_ctlargcount(t, cp, 5);
|
|
r.min.x = cp->iargs[1];
|
|
r.min.y = cp->iargs[2];
|
|
r.max.x = cp->iargs[3];
|
|
r.max.y = cp->iargs[4];
|
|
if(Dx(r)<=0 || Dy(r)<=0)
|
|
ctlerror("%q: bad rectangle: %s", t->name, cp->str);
|
|
t->rect = r;
|
|
t->nvis = (Dy(r)-2*t->border)/t->font->font->height;
|
|
break;
|
|
case EReplace:
|
|
_ctlargcount(t, cp, 3);
|
|
i = cp->iargs[1];
|
|
if(i<0 || i>=t->nline)
|
|
ctlerror("%q: line number out of range: %s", t->name, cp->str);
|
|
free(t->line[i]);
|
|
t->line[i] = _ctlrunestr(cp->args[2]);
|
|
textshow(t);
|
|
break;
|
|
case EReveal:
|
|
_ctlargcount(t, cp, 1);
|
|
t->hidden = 0;
|
|
textshow(t);
|
|
break;
|
|
case EScroll:
|
|
_ctlargcount(t, cp, 2);
|
|
t->scroll = cp->iargs[1];
|
|
break;
|
|
case ESelect:
|
|
if(cp->nargs!=2 && cp->nargs!=3)
|
|
badselect:
|
|
ctlerror("%q: bad select message: %s", t->name, cp->str);
|
|
if(cp->nargs == 2){
|
|
if(strcmp(cp->args[1], "all") == 0){
|
|
memset(t->selected, 1, t->nline);
|
|
break;
|
|
}
|
|
if(strcmp(cp->args[1], "none") == 0){
|
|
memset(t->selected, 0, t->nline);
|
|
break;
|
|
}
|
|
if(cp->args[1][0]<'0' && '9'<cp->args[1][0])
|
|
goto badselect;
|
|
texttogglei(t, cp->iargs[1]);
|
|
break;
|
|
}
|
|
if(cp->iargs[1]<0 || cp->iargs[1]>=t->nline)
|
|
ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str);
|
|
if(t->selected[cp->iargs[1]] != (cp->iargs[2]!=0))
|
|
texttogglei(t, cp->iargs[1]);
|
|
break;
|
|
case ESelectcolor:
|
|
_ctlargcount(t, cp, 2);
|
|
_setctlimage(t, &t->selectcolor, cp->args[1]);
|
|
break;
|
|
case ESelectmode:
|
|
_ctlargcount(t, cp, 2);
|
|
if(strcmp(cp->args[1], "single") == 0)
|
|
t->selectmode = Selsingle;
|
|
else if(strncmp(cp->args[1], "multi", 5) == 0)
|
|
t->selectmode = Selmulti;
|
|
break;
|
|
case ESelectstyle:
|
|
_ctlargcount(t, cp, 2);
|
|
if(strcmp(cp->args[1], "down") == 0)
|
|
t->selectstyle = Seldown;
|
|
else if(strcmp(cp->args[1], "up") == 0)
|
|
t->selectstyle = Selup;
|
|
break;
|
|
case EShow:
|
|
_ctlargcount(t, cp, 1);
|
|
textshow(t);
|
|
break;
|
|
case ESize:
|
|
if (cp->nargs == 3)
|
|
r.max = Pt(10000, 10000);
|
|
else{
|
|
_ctlargcount(t, cp, 5);
|
|
r.max.x = cp->iargs[3];
|
|
r.max.y = cp->iargs[4];
|
|
}
|
|
r.min.x = cp->iargs[1];
|
|
r.min.y = cp->iargs[2];
|
|
if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y)
|
|
ctlerror("%q: bad sizes: %s", t->name, cp->str);
|
|
t->size.min = r.min;
|
|
t->size.max = r.max;
|
|
break;
|
|
case ETextcolor:
|
|
_ctlargcount(t, cp, 2);
|
|
_setctlimage(t, &t->textcolor, cp->args[1]);
|
|
break;
|
|
case ETopline:
|
|
_ctlargcount(t, cp, 2);
|
|
i = cp->iargs[1];
|
|
if(i < 0)
|
|
i = 0;
|
|
if(i > t->nline)
|
|
i = t->nline;
|
|
if(t->topline != i){
|
|
t->topline = i;
|
|
textshow(t);
|
|
}
|
|
break;
|
|
case EValue:
|
|
/* set contents to single line */
|
|
/* free existing text and fall through to add */
|
|
for(i=0; i<t->nline; i++){
|
|
free(t->line[i]);
|
|
t->line[i] = nil;
|
|
}
|
|
t->nline = 0;
|
|
t->topline = 0;
|
|
/* fall through */
|
|
case EAccumulate:
|
|
case EAdd:
|
|
switch (cp->nargs) {
|
|
default:
|
|
ctlerror("%q: wrong argument count in '%s'", t->name, cp->str);
|
|
case 2:
|
|
n = t->nline;
|
|
break;
|
|
case 3:
|
|
n = cp->iargs[1];
|
|
if(n<0 || n>t->nline)
|
|
ctlerror("%q: line number out of range: %s", t->name, cp->str);
|
|
break;
|
|
}
|
|
rp = _ctlrunestr(cp->args[cp->nargs-1]);
|
|
t->line = ctlrealloc(t->line, (t->nline+1)*sizeof(Rune*));
|
|
memmove(t->line+n+1, t->line+n, (t->nline-n)*sizeof(Rune*));
|
|
t->line[n] = rp;
|
|
t->selected = ctlrealloc(t->selected, t->nline+1);
|
|
memmove(t->selected+n+1, t->selected+n, t->nline-n);
|
|
t->selected[n] = (t->selectmode==Selmulti && cmd!=EAccumulate);
|
|
t->nline++;
|
|
if(t->scroll) {
|
|
if(n > t->topline + (t->nvis - 1)){
|
|
t->topline = n - (t->nvis - 1);
|
|
if(t->topline < 0)
|
|
t->topline = 0;
|
|
}
|
|
if(n < t->topline)
|
|
t->topline = n;
|
|
}
|
|
if(cmd != EAccumulate)
|
|
if(t->scroll || t->nline<=t->topline+t->nvis)
|
|
textshow(t);
|
|
break;
|
|
case EWarp:
|
|
_ctlargcount(t, cp, 2);
|
|
i = cp->iargs[1];
|
|
if(i <0 || i>=t->nline)
|
|
ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str);
|
|
if(i < t->topline || i >= t->topline+t->nvis){
|
|
t->topline = i;
|
|
}
|
|
t->warp = cp->iargs[1];
|
|
textshow(t);
|
|
t->warp = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
texttogglei(Text *t, int i)
|
|
{
|
|
int prev;
|
|
|
|
if(t->selectmode == Selsingle){
|
|
/* clear the others */
|
|
prev = t->selected[i];
|
|
memset(t->selected, 0, t->nline);
|
|
t->selected[i] = prev;
|
|
}
|
|
t->selected[i] ^= 1;
|
|
textshow(t);
|
|
}
|
|
|
|
static int
|
|
textline(Text *t, Point p)
|
|
{
|
|
Rectangle r;
|
|
int i;
|
|
|
|
r = t->rect;
|
|
if(t->border > 0)
|
|
r = insetrect(r, t->border);
|
|
if(!ptinrect(p, r))
|
|
return -1;
|
|
i = (p.y-r.min.y)/t->font->font->height;
|
|
i += t->topline;
|
|
if(i >= t->nline)
|
|
return -1;
|
|
return i;
|
|
}
|
|
|
|
static int
|
|
texttoggle(Text *t, Point p)
|
|
{
|
|
int i;
|
|
|
|
i = textline(t, p);
|
|
if (i >= 0)
|
|
texttogglei(t, i);
|
|
return i;
|
|
}
|
|
|
|
Control*
|
|
createtext(Controlset *cs, char *name)
|
|
{
|
|
Text *t;
|
|
|
|
t = (Text*)_createctl(cs, "text", sizeof(Text), name);
|
|
t->line = ctlmalloc(sizeof(Rune*));
|
|
t->selected = ctlmalloc(1);
|
|
t->nline = 0;
|
|
t->image = _getctlimage("white");
|
|
t->textcolor = _getctlimage("black");
|
|
t->bordercolor = _getctlimage("black");
|
|
t->selectcolor = _getctlimage("yellow");
|
|
t->selectingcolor = _getctlimage("paleyellow");
|
|
t->font = _getctlfont("font");
|
|
t->selectmode = Selsingle;
|
|
t->selectstyle = Selup; // Seldown;
|
|
t->lastbut = 0;
|
|
t->mouse = textmouse;
|
|
t->ctl = textctl;
|
|
t->exit = textfree;
|
|
t->warp = -1;
|
|
t->sel = -1;
|
|
t->offsel = 0;
|
|
t->but = 0;
|
|
return (Control *)t;
|
|
}
|