#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <draw.h>
#include <mouse.h>
#include <cursor.h>
#include <keyboard.h>
#include <frame.h>
#include <9p.h>
#include <ctype.h>
#include "gui.h"
int framedebug;
static void
framedump(Panel* p)
{
if (framedebug)
print("froff %d chr %d ln %d max %d, llf %d full %d\n",
p->froff, p->f.nchars, p->f.nlines,
p->f.maxlines, p->f.lastlinefull, p->f.lastlinefull);
}
/*
* The routines below update both the Tblock and the Frame,
* they do nothing else. No events, no resizes, no flushimages.
*/
void
filltext(Panel* p)
{
Tblock* b;
int n;
int pos;
Rune nl[1];
Rectangle wr;
Point pt;
nl[0] = L'\n';
if (!p->loaded || p->f.lastlinefull)
return;
b = blockseek(p->blks, &n, p->froff + p->f.nchars);
for(; b && !p->f.lastlinefull; b = b->next){
pos = p->f.nchars;
if (b->nr){
fdprint("fill at %d+%d with ", p->froff, b->nr);
fdprint("%d runes: [%.*S]\n", b->nr,
(b->nr < 100 ? b->nr : 100), b->r + n);
frinsert(&p->f, b->r+n, b->r+b->nr, pos);
}
n = 0;
}
/* Libframe may not clear the right of the last line. BUG?
* we force that by inserting a fake \n
*/
if (!p->f.lastlinefull){
frinsert(&p->f, nl, nl+1, p->f.nchars);
frdelete(&p->f, p->f.nchars-1, p->f.nchars);
}
/* Clear what's left unused */
pt = p->rect.min;
pt.y += p->f.nlines * p->font->height;
wr = Rpt(pt, p->rect.max);
draw(screen, wr, cols[BACK], nil, ZP);
}
static int
canonpos(Panel* p, int pos)
{
if (pos < 0)
return 0;
else if (pos > p->f.nchars)
return p->f.nchars;
else
return pos;
}
void
hidesel(Panel* p)
{
Frame* f;
if (!p->loaded)
return;
f = &p->f;
if (f->p0 != f->p1 || (p->flags&Pedit))
frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
}
void
drawsel(Panel* p)
{
int ss, se;
Frame* f;
if (!p->loaded)
return;
f = &p->f;
ss = canonpos(p, p->ss - p->froff);
se = canonpos(p, p->se - p->froff);
if(ss == f->p0 && se == f->p1) // it's already there.
return;
if (f->p0 != f->p1 || (p->flags&Pedit))
frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
f->p0 = ss;
f->p1 = se;
if (f->p0 != f->p1 || (p->flags&Pedit))
frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
}
/* Returns true if we want a resize
*/
int
settextsize(Panel* p)
{
int oncols, onrows;
int res;
Point omin;
int n;
res = p->maxsz.y = 0;
if (p->flags&Pline){
omin = p->minsz;
packblock(p->blks);
p->minsz.y = fontheight(p->font);
if (!(p->flags&Pedit) && p->blks->nr > 0){
n = runestringnwidth(p->font, p->blks->r, p->blks->nr);
p->minsz.x = n;
}
res = !eqpt(omin, p->minsz);
} else {
p->minsz = Pt(p->font->height*2,p->font->height*2);
if (p->nlines == 0)
p->maxsz.y = 0;
else {
n = p->nlines + 1;
if (n < 3)
n = 3;
p->maxsz.y = p->font->height * n;
}
}
oncols = p->ncols;
onrows = p->nrows;
p->ncols = gettextwid(p);
p->nrows = gettextht(p);
fdprint("setframesizes: %s: sz %P min %P max [0 %d] txt %dx%d wx %d wy %d\n",
p->name, p->size, p->minsz, p->maxsz.y, p->ncols, p->nrows,
p->wants.x, p->wants.y);
if (!res)
res = oncols != p->ncols || onrows != p->nrows;
return res;
}
static int
posselcmp(Panel* p, int pos)
{
if (pos < p->ss)
return -1;
if (pos >= p->se)
return 1;
return 0;
}
int
findln(Tblock* b, int* pp)
{
int i;
int pos;
pos = *pp;
for(i = 0; i < 128 && pos < b->nr; i++, pos++)
if (b->r[pos] == '\n')
break;
if (i == 128 || b->r[pos] == '\n'){
*pp = pos;
return 1;
}
return 0;
}
int
findrln(Tblock* b, int* pp)
{
int i;
int pos;
pos = *pp;
for(i = 0; i < 128 && pos > 0; i++, pos--)
if (b->r[pos] == '\n')
break;
if (i == 128 || b->r[pos] == '\n' || pos == 0){
*pp = pos;
return 1;
}
return 0;
}
int
scrollframe(Panel* p, int nscroll)
{
Tblock* b;
int n;
int pos;
int nlines;
int l;
fdprint("scroll %d\n", nscroll);
nlines = abs(nscroll);
assert(p->froff >= 0);
if (nscroll == 0)
return 0;
packblock(p->blks);
b = p->blks;
if (nscroll > 0) {
l = blocklen(b);
if (p->froff + p->f.nchars >= l)
return 0;
n = p->froff;
while(n < b->nr && nlines-- && findln(b, &n))
if (n < b->nr)
n++;
} else {
nlines++; // adjust to skip the \n before this line
if (p->froff <= 0){
if (blockdebug)
blockdump(b);
return 0;
}
n = p->froff;
while(n > 0 && nlines-- && findrln(b, &n))
if (n > 0)
n--;
if (n > 0 && n < b->nr) // advance to skip last \n
n++;
}
pos = n;
if (b->r[pos] == '\n')
pos++;
p->froff = pos;
framedump(p);
return 1;
}
int
frameins(Panel* p, Rune* r, int nr, int pos)
{
Tblock* b;
int n;
assert(pos <= blocklen(p->blks));
fdprint("ins %d (p0 %uld) %d runes [%.*S]\n", pos, p->f.p0, nr, nr, r);
b = blockseek(p->blks, &n, pos);
blockins(b, n, r, nr);
p->dfile->length += runenlen(r, nr);
if (pos >= p->froff){
if (p->loaded)
frinsert(&p->f, r, r +nr, pos - p->froff);
} else
p->froff += nr;
if (pos < p->ss)
p->ss += nr;
if (pos < p->se)
p->se += nr;
if (pos < p->s0)
p->s0 += nr;
if (pos < p->mark)
p->mark += nr;
return p->loaded ? p->f.lastlinefull : 0;
}
static int
fixpos(int pos, int x, int n)
{
if (x < pos){
if (x + n > pos)
n = pos - x;
pos -= n;
}
return pos;
}
int
framedel(Panel* p, Rune* r, int nr, int pos)
{
Tblock* b;
int n;
Rune nl[1];
Frame* f;
nl[0] = L'\n';
assert(r);
b = blockseek(p->blks, &n, pos);
if (b == nil)
return 0;
blockdel(b, n, nr, r);
p->dfile->length -= runenlen(r, nr);
if (pos >= p->froff && p->loaded){
f = &p->f;
frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
frdelete(f, pos - p->froff, pos - p->froff + nr);
/* Libframe may not clear the right of the last line. BUG?
* we force that by inserting a fake \n
*/
if (!p->f.lastlinefull){
frinsert(f, nl, nl+1, f->nchars);
frdelete(f, f->nchars-1, f->nchars);
}
frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
} else
p->froff = fixpos(p->froff, pos, nr);
p->ss = fixpos(p->ss, pos, nr);
p->se = fixpos(p->se, pos, nr);
p->s0 = fixpos(p->s0, pos, nr);
p->mark = fixpos(p->mark, pos, nr);
return nr;
}
static int
iswordchar(Rune r)
{
return isalpharune(r) || runestrchr(L"0123456789|&?=._-+/:", r);
}
static Rune lparen[] = L"{[(«<“";
static Rune rparen[] = L"}])»>”";
static Rune paren[] = L"\"'`";
static int
isparen(Rune* set, Rune r)
{
Rune* p;
p = runestrchr(set, r);
if (p)
return p - set + 1;
else
return 0;
}
static int
findexpsep(void* a, int i, Rune r)
{
int* inword = a;
//fprint(2, "[%d %C] ", i, r);
if (*inword)
return !iswordchar(r);
else
return (i > 128 || (int)r == '\n');
}
/* Returns the word at pos.
* If we are looking at the end of line, we pretend we look right
* before it. In any case:
* The word is the selection when it exists.
* It is the longest set of <wordchar>s if pos at <wordchar>
* It is the text between {} [] '' "" () if pos is at delim.
* It is the current line otherwise (if pos at blank)
*/
Rune*
gettextword(Panel* p, int pos, int* ss, int* se)
{
Rune* r;
Tblock* b;
int spos, epos;
int nr;
int pi;
int nparen;
b = p->blks;
packblock(b);
assert(pos <= b->nr);
if (pos == b->nr && pos > 0)
pos--;
spos = epos = pos;
if (b->nr == 0)
goto Done;
if (!posselcmp(p, pos) && p->ss != p->se){
spos = p->ss;
epos = p->se;
} else if (iswordchar(b->r[pos])){
while(spos > 0 && iswordchar(b->r[spos]))
spos--;
if (spos > 0)
spos++;
while(epos < b->nr && iswordchar(b->r[epos]))
epos++;
} else if (pi = isparen(paren, b->r[pos])){
spos++;
for(epos = spos; epos < b->nr; epos++)
if (isparen(paren, b->r[epos]) == pi)
break;
} else if (pi = isparen(lparen, b->r[pos])){
nparen = 1;
spos++;
for(epos = spos; epos < b->nr; epos++){
if (isparen(lparen, b->r[epos]) == pi)
nparen++;
if (isparen(rparen, b->r[epos]) == pi)
nparen--;
if (nparen <= 0){
break;
}
}
} else if (pi = isparen(rparen, b->r[pos])){
nparen = 1;
if (spos > 0)
for(spos--; spos > 0; spos--){
if (isparen(rparen, b->r[spos]) == pi)
nparen++;
if (isparen(lparen, b->r[spos]) == pi)
nparen--;
if (nparen <= 0){
spos++;
break;
}
}
} else { // pos at blank
if (b->r[spos] == '\n' && spos > 0 && b->r[spos-1] != '\n'){
// click at right part of line; step back
// so that expanding leads to previous line
spos--;
}
while(spos > 0 && b->r[spos-1] != '\n')
spos--;
while(epos < b->nr && b->r[epos] != '\n')
epos++;
if (epos < b->nr)
epos++; // include \n
}
Done:
if (spos < 0 || epos < 0 || epos < spos || epos > b->nr){
fprint(2, "spos/epos bug: %d %d %d\n", spos, epos, b->nr);
abort();
}
if (ss){
*ss = spos;
*se = epos;
}
nr = epos - spos;
r = emalloc9p((nr + 1) * sizeof(Rune));
if (nr > 0)
blockget(b, spos, nr, r);
r[nr] = 0;
edprint("gettextword %.*S %d %d\n", nr, r, spos, epos);
return r;
}
|