Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/sys/src/cmd/omero/frame.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#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;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.