123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455 |
- implement Textm;
- include "common.m";
- include "keyboard.m";
- sys : Sys;
- utils : Utils;
- framem : Framem;
- drawm : Draw;
- acme : Acme;
- graph : Graph;
- gui : Gui;
- dat : Dat;
- scrl : Scroll;
- bufferm : Bufferm;
- filem : Filem;
- columnm : Columnm;
- windowm : Windowm;
- exec : Exec;
- Dir, sprint : import sys;
- frgetmouse : import acme;
- min, warning, error, stralloc, strfree, isalnum : import utils;
- Frame, frinsert, frdelete, frptofchar, frcharofpt, frselect, frdrawsel, frdrawsel0, frtick : import framem;
- BUFSIZE, Astring, SZINT, TRUE, FALSE, XXX, Reffont, Dirlist,Scrollwid, Scrollgap, seq, mouse : import dat;
- EM_NORMAL, EM_RAW, EM_MASK : import dat;
- ALPHA_LATIN, ALPHA_GREEK, ALPHA_CYRILLIC: import Dat;
- BACK, TEXT, HIGH, HTEXT : import Framem;
- Flushon, Flushoff : import Draw;
- Point, Display, Rect, Image : import drawm;
- charwidth, bflush, draw : import graph;
- black, white, mainwin, display : import gui;
- Buffer : import bufferm;
- File : import filem;
- Column : import columnm;
- Window : import windowm;
- scrdraw : import scrl;
- cvlist: adt {
- ld: int;
- nm: string;
- si: string;
- so: string;
- };
- # "@@", "'EKSTYZekstyz ", "ьЕКСТЫЗекстызъЁё",
- latintab := array[] of {
- cvlist(
- ALPHA_LATIN,
- "latin",
- nil,
- nil
- ),
- cvlist(
- ALPHA_GREEK,
- "greek",
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
- "ΑΒΞΔΕΦΓΘΙΪΚΛΜΝΟΠΨΡΣΤΥΫΩΧΗΖαβξδεφγθιϊκλμνοπψρστυϋωχηζ"
- ),
- cvlist(
- ALPHA_CYRILLIC,
- "cyrillic",
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
- "АБЧДЭФГШИЙХЛМНОПЕРЩЦУВЮХЯЖабчдэфгшийхлмноперщцувюхяж"
- ),
- cvlist(-1, nil, nil, nil)
- };
- alphabet := ALPHA_LATIN; # per window perhaps
- setalphabet(s: string)
- {
- for(a := 0; latintab[a].ld != -1; a++){
- k := latintab[a].ld;
- for(i := 0; latintab[i].ld != -1; i++){
- if(s == transs(latintab[i].nm, k)){
- alphabet = latintab[i].ld;
- return;
- }
- }
- }
- }
- transc(c: int, k: int): int
- {
- for(i := 0; latintab[i].ld != -1; i++){
- if(k == latintab[i].ld){
- si := latintab[i].si;
- so := latintab[i].so;
- ln := len si;
- for(j := 0; j < ln; j++)
- if(c == si[j])
- return so[j];
- }
- }
- return c;
- }
- transs(s: string, k: int): string
- {
- ln := len s;
- for(i := 0; i < ln; i++)
- s[i] = transc(s[i], k);
- return s;
- }
- init(mods : ref Dat->Mods)
- {
- sys = mods.sys;
- framem = mods.framem;
- dat = mods.dat;
- utils = mods.utils;
- drawm = mods.draw;
- acme = mods.acme;
- graph = mods.graph;
- gui = mods.gui;
- scrl = mods.scroll;
- bufferm = mods.bufferm;
- filem = mods.filem;
- columnm = mods.columnm;
- windowm = mods.windowm;
- exec = mods.exec;
- }
- TABDIR : con 3; # width of tabs in directory windows
- # remove eventually
- KF : con 16rF000;
- Kup : con KF | 16r0E;
- Kleft : con KF | 16r11;
- Kright : con KF | 16r12;
- Kend : con KF | 16r18;
- Kdown : con 16r80;
- nulltext : Text;
- newtext() : ref Text
- {
- t := ref nulltext;
- t.frame = framem->newframe();
- return t;
- }
- Text.init(t : self ref Text, f : ref File, r : Rect, rf : ref Dat->Reffont, cols : array of ref Image)
- {
- t.file = f;
- t.all = r;
- t.scrollr = r;
- t.scrollr.max.x = r.min.x+Scrollwid;
- t.lastsr = dat->nullrect;
- r.min.x += Scrollwid+Scrollgap;
- t.eq0 = ~0;
- t.ncache = 0;
- t.reffont = rf;
- t.tabstop = dat->maxtab;
- for(i:=0; i<Framem->NCOL; i++)
- t.frame.cols[i] = cols[i];
- t.redraw(r, rf.f, mainwin, -1);
- }
- Text.redraw(t : self ref Text, r : Rect, f : ref Draw->Font, b : ref Image, odx : int)
- {
- framem->frinit(t.frame, r, f, b, t.frame.cols);
- rr := t.frame.r;
- rr.min.x -= Scrollwid; # back fill to scroll bar
- draw(t.frame.b, rr, t.frame.cols[Framem->BACK], nil, (0, 0));
- # use no wider than 3-space tabs in a directory
- maxt := dat->maxtab;
- if(t.what == Body){
- if(t.w != nil && t.w.isdir)
- maxt = min(TABDIR, dat->maxtab);
- else
- maxt = t.tabstop;
- }
- t.frame.maxtab = maxt*charwidth(f, '0');
- # c = '0';
- # if(t.what==Body && t.w!=nil && t.w.isdir)
- # c = ' ';
- # t.frame.maxtab = Dat->Maxtab*charwidth(f, c);
- if(t.what==Body && t.w.isdir && odx!=t.all.dx()){
- if(t.frame.maxlines > 0){
- t.reset();
- t.columnate(t.w.dlp, t.w.ndl);
- t.show(0, 0, TRUE);
- }
- }else{
- t.fill();
- t.setselect(t.q0, t.q1);
- }
- }
- Text.reshape(t : self ref Text, r : Rect) : int
- {
- odx : int;
- if(r.dy() > 0)
- r.max.y -= r.dy()%t.frame.font.height;
- else
- r.max.y = r.min.y;
- odx = t.all.dx();
- t.all = r;
- t.scrollr = r;
- t.scrollr.max.x = r.min.x+Scrollwid;
- t.lastsr = dat->nullrect;
- r.min.x += Scrollwid+Scrollgap;
- framem->frclear(t.frame, 0);
- # t.redraw(r, t.frame.font, t.frame.b, odx);
- t.redraw(r, t.frame.font, mainwin, odx);
- return r.max.y;
- }
- Text.close(t : self ref Text)
- {
- t.cache = nil;
- framem->frclear(t.frame, 1);
- t.file.deltext(t);
- t.file = nil;
- t.reffont.close();
- if(dat->argtext == t)
- dat->argtext = nil;
- if(dat->typetext == t)
- dat->typetext = nil;
- if(dat->seltext == t)
- dat->seltext = nil;
- if(dat->mousetext == t)
- dat->mousetext = nil;
- if(dat->barttext == t)
- dat->barttext = nil;
- }
- dircmp(da : ref Dirlist, db : ref Dirlist) : int
- {
- if (da.r < db.r)
- return -1;
- if (da.r > db.r)
- return 1;
- return 0;
- }
- qsort(a : array of ref Dirlist, n : int)
- {
- i, j : int;
- t : ref Dirlist;
- while(n > 1) {
- i = n>>1;
- t = a[0]; a[0] = a[i]; a[i] = t;
- i = 0;
- j = n;
- for(;;) {
- do
- i++;
- while(i < n && dircmp(a[i], a[0]) < 0);
- do
- j--;
- while(j > 0 && dircmp(a[j], a[0]) > 0);
- if(j < i)
- break;
- t = a[i]; a[i] = a[j]; a[j] = t;
- }
- t = a[0]; a[0] = a[j]; a[j] = t;
- n = n-j-1;
- if(j >= n) {
- qsort(a, j);
- a = a[j+1:];
- } else {
- qsort(a[j+1:], n);
- n = j;
- }
- }
- }
- Text.columnate(t : self ref Text, dlp : array of ref Dirlist, ndl : int)
- {
- i, j, w, colw, mint, maxt, ncol, nrow : int;
- dl : ref Dirlist;
- q1 : int;
- if(t.file.ntext > 1)
- return;
- mint = charwidth(t.frame.font, '0');
- # go for narrower tabs if set more than 3 wide
- t.frame.maxtab = min(dat->maxtab, TABDIR)*mint;
- maxt = t.frame.maxtab;
- colw = 0;
- for(i=0; i<ndl; i++){
- dl = dlp[i];
- w = dl.wid;
- if(maxt-w%maxt < mint)
- w += mint;
- if(w % maxt)
- w += maxt-(w%maxt);
- if(w > colw)
- colw = w;
- }
- if(colw == 0)
- ncol = 1;
- else
- ncol = utils->max(1, t.frame.r.dx()/colw);
- nrow = (ndl+ncol-1)/ncol;
- q1 = 0;
- for(i=0; i<nrow; i++){
- for(j=i; j<ndl; j+=nrow){
- dl = dlp[j];
- t.file.insert(q1, dl.r, len dl.r);
- q1 += len dl.r;
- if(j+nrow >= ndl)
- break;
- w = dl.wid;
- if(maxt-w%maxt < mint){
- t.file.insert(q1, "\t", 1);
- q1++;
- w += mint;
- }
- do{
- t.file.insert(q1, "\t", 1);
- q1++;
- w += maxt-(w%maxt);
- }while(w < colw);
- }
- t.file.insert(q1, "\n", 1);
- q1++;
- }
- }
- Text.loadx(t : self ref Text, q0 : int, file : string, setqid : int) : int
- {
- rp : ref Astring;
- dl : ref Dirlist;
- dlp : array of ref Dirlist;
- i, n, ndl : int;
- fd : ref Sys->FD;
- q, q1 : int;
- d : Dir;
- u : ref Text;
- ok : int;
- if(t.ncache!=0 || t.file.buf.nc || t.w==nil || t!=t.w.body || (t.w.isdir && t.file.name==nil))
- error("text.load");
- {
- fd = sys->open(file, Sys->OREAD);
- if(fd == nil){
- warning(nil, sprint("can't open %s: %r\n", file));
- raise "e";
- }
- (ok, d) = sys->fstat(fd);
- if(ok){
- warning(nil, sprint("can't fstat %s: %r\n", file));
- raise "e";
- }
- if(d.qid.qtype & Sys->QTDIR){
- # this is checked in get() but it's possible the file changed underfoot
- if(t.file.ntext > 1){
- warning(nil, sprint("%s is a directory; can't read with multiple windows on it\n", file));
- raise "e";
- }
- t.w.isdir = TRUE;
- t.w.filemenu = FALSE;
- if(t.file.name[len t.file.name-1] != '/')
- t.w.setname(t.file.name + "/", len t.file.name+1);
- dlp = nil;
- ndl = 0;
- for(;;){
- (nd, dbuf) := sys->dirread(fd);
- if(nd <= 0)
- break;
- for(i=0; i<nd; i++){
- dl = ref Dirlist;
- dl.r = dbuf[i].name;
- if(dbuf[i].mode & Sys->DMDIR)
- dl.r = dl.r + "/";
- dl.wid = graph->strwidth(t.frame.font, dl.r);
- ndl++;
- odlp := dlp;
- dlp = array[ndl] of ref Dirlist;
- dlp[0:] = odlp[0:ndl-1];
- odlp = nil;
- dlp[ndl-1] = dl;
- }
- }
- qsort(dlp, ndl);
- t.w.dlp = dlp;
- t.w.ndl = ndl;
- t.columnate(dlp, ndl);
- q1 = t.file.buf.nc;
- }else{
- tmp : int;
-
- t.w.isdir = FALSE;
- t.w.filemenu = TRUE;
- tmp = t.file.loadx(q0, fd);
- q1 = q0 + tmp;
- }
- fd = nil;
- if(setqid){
- t.file.dev = d.dev;
- t.file.mtime = d.mtime;
- t.file.qidpath = d.qid.path;
- }
- rp = stralloc(BUFSIZE);
- for(q=q0; q<q1; q+=n){
- n = q1-q;
- if(n > Dat->BUFSIZE)
- n = Dat->BUFSIZE;
- t.file.buf.read(q, rp, 0, n);
- if(q < t.org)
- t.org += n;
- else if(q <= t.org+t.frame.nchars)
- frinsert(t.frame, rp.s, n, q-t.org);
- if(t.frame.lastlinefull)
- break;
- }
- strfree(rp);
- rp = nil;
- for(i=0; i<t.file.ntext; i++){
- u = t.file.text[i];
- if(u != t){
- if(u.org > u.file.buf.nc) # will be 0 because of reset(), but safety first
- u.org = 0;
- u.reshape(u.all);
- u.backnl(u.org, 0); # go to beginning of line
- }
- u.setselect(q0, q0);
- }
- return q1-q0;
- }
- exception{
- * =>
- fd = nil;
- return 0;
- }
- return 0;
- }
- Text.bsinsert(t : self ref Text, q0 : int, r : string, n : int, tofile : int) : (int, int)
- {
- tp : ref Astring;
- bp, up : int;
- i, initial : int;
- {
- if(t.what == Tag) # can't happen but safety first: mustn't backspace over file name
- raise "e";
- bp = 0;
- for(i=0; i<n; i++)
- if(r[bp++] == '\b'){
- --bp;
- initial = 0;
- tp = utils->stralloc(n);
- for (k := 0; k < i; k++)
- tp.s[k] = r[k];
- up = i;
- for(; i<n; i++){
- tp.s[up] = r[bp++];
- if(tp.s[up] == '\b')
- if(up == 0)
- initial++;
- else
- --up;
- else
- up++;
- }
- if(initial){
- if(initial > q0)
- initial = q0;
- q0 -= initial;
- t.delete(q0, q0+initial, tofile);
- }
- n = up;
- t.insert(q0, tp.s, n, tofile, 0);
- strfree(tp);
- tp = nil;
- return (q0, n);
- }
- raise "e";
- return(0, 0);
- }
- exception{
- * =>
- t.insert(q0, r, n, tofile, 0);
- return (q0, n);
- }
- return (0, 0);
- }
- Text.insert(t : self ref Text, q0 : int, r : string, n : int, tofile : int, echomode : int)
- {
- c, i : int;
- u : ref Text;
- if(tofile && t.ncache != 0)
- error("text.insert");
- if(n == 0)
- return;
- if(tofile){
- t.file.insert(q0, r, n);
- if(t.what == Body){
- t.w.dirty = TRUE;
- t.w.utflastqid = -1;
- }
- if(t.file.ntext > 1)
- for(i=0; i<t.file.ntext; i++){
- u = t.file.text[i];
- if(u != t){
- u.w.dirty = TRUE; # always a body
- u.insert(q0, r, n, FALSE, echomode);
- u.setselect(u.q0, u.q1);
- scrdraw(u);
- }
- }
- }
- if(q0 < t.q1)
- t.q1 += n;
- if(q0 < t.q0)
- t.q0 += n;
- if(q0 < t.org)
- t.org += n;
- else if(q0 <= t.org+t.frame.nchars) {
- if (echomode == EM_MASK && len r == 1 && r[0] != '\n')
- frinsert(t.frame, "*", n, q0-t.org);
- else
- frinsert(t.frame, r, n, q0-t.org);
- }
- if(t.w != nil){
- c = 'i';
- if(t.what == Body)
- c = 'I';
- if(n <= Dat->EVENTSIZE)
- t.w.event(sprint("%c%d %d 0 %d %s\n", c, q0, q0+n, n, r[0:n]));
- else
- t.w.event(sprint("%c%d %d 0 0 \n", c, q0, q0+n));
- }
- }
- Text.fill(t : self ref Text)
- {
- rp : ref Astring;
- i, n, m, nl : int;
- if(t.frame.lastlinefull || t.nofill)
- return;
- if(t.ncache > 0){
- if(t.w != nil)
- t.w.commit(t);
- else
- t.commit(TRUE);
- }
- rp = stralloc(BUFSIZE);
- do{
- n = t.file.buf.nc-(t.org+t.frame.nchars);
- if(n == 0)
- break;
- if(n > 2000) # educated guess at reasonable amount
- n = 2000;
- t.file.buf.read(t.org+t.frame.nchars, rp, 0, n);
- #
- # it's expensive to frinsert more than we need, so
- # count newlines.
- #
-
- nl = t.frame.maxlines-t.frame.nlines;
- m = 0;
- for(i=0; i<n; ){
- if(rp.s[i++] == '\n'){
- m++;
- if(m >= nl)
- break;
- }
- }
- frinsert(t.frame, rp.s, i, t.frame.nchars);
- }while(t.frame.lastlinefull == FALSE);
- strfree(rp);
- rp = nil;
- }
- Text.delete(t : self ref Text, q0 : int, q1 : int, tofile : int)
- {
- n, p0, p1 : int;
- i, c : int;
- u : ref Text;
- if(tofile && t.ncache != 0)
- error("text.delete");
- n = q1-q0;
- if(n == 0)
- return;
- if(tofile){
- t.file.delete(q0, q1);
- if(t.what == Body){
- t.w.dirty = TRUE;
- t.w.utflastqid = -1;
- }
- if(t.file.ntext > 1)
- for(i=0; i<t.file.ntext; i++){
- u = t.file.text[i];
- if(u != t){
- u.w.dirty = TRUE; # always a body
- u.delete(q0, q1, FALSE);
- u.setselect(u.q0, u.q1);
- scrdraw(u);
- }
- }
- }
- if(q0 < t.q0)
- t.q0 -= min(n, t.q0-q0);
- if(q0 < t.q1)
- t.q1 -= min(n, t.q1-q0);
- if(q1 <= t.org)
- t.org -= n;
- else if(q0 < t.org+t.frame.nchars){
- p1 = q1 - t.org;
- if(p1 > t.frame.nchars)
- p1 = t.frame.nchars;
- if(q0 < t.org){
- t.org = q0;
- p0 = 0;
- }else
- p0 = q0 - t.org;
- frdelete(t.frame, p0, p1);
- t.fill();
- }
- if(t.w != nil){
- c = 'd';
- if(t.what == Body)
- c = 'D';
- t.w.event(sprint("%c%d %d 0 0 \n", c, q0, q1));
- }
- }
- onechar : ref Astring;
- Text.readc(t : self ref Text, q : int) : int
- {
- if(t.cq0<=q && q<t.cq0+t.ncache)
- return t.cache[q-t.cq0];
- if (onechar == nil)
- onechar = stralloc(1);
- t.file.buf.read(q, onechar, 0, 1);
- return onechar.s[0];
- }
- Text.bswidth(t : self ref Text, c : int) : int
- {
- q, eq : int;
- r : int;
- skipping : int;
- # there is known to be at least one character to erase
- if(c == 16r08) # ^H: erase character
- return 1;
- q = t.q0;
- skipping = TRUE;
- while(q > 0){
- r = t.readc(q-1);
- if(r == '\n'){ # eat at most one more character
- if(q == t.q0) # eat the newline
- --q;
- break;
- }
- if(c == 16r17){
- eq = isalnum(r);
- if(eq && skipping) # found one; stop skipping
- skipping = FALSE;
- else if(!eq && !skipping)
- break;
- }
- --q;
- }
- return t.q0-q;
- }
- Text.typex(t : self ref Text, r : int, echomode : int)
- {
- q0, q1 : int;
- nnb, nb, n, i : int;
- u : ref Text;
- if(alphabet != ALPHA_LATIN)
- r = transc(r, alphabet);
- if (echomode == EM_RAW && t.what == Body) {
- if (t.w != nil) {
- s := "a";
- s[0] = r;
- t.w.event(sprint("R0 0 0 1 %s\n", s));
- }
- return;
- }
- if(t.what!=Body && r=='\n')
- return;
- case r {
- Dat->Kscrolldown=>
- if(t.what == Body){
- q0 = t.org+frcharofpt(t.frame, (t.frame.r.min.x, t.frame.r.min.y+2*t.frame.font.height));
- t.setorigin(q0, FALSE);
- }
- return;
- Dat->Kscrollup=>
- if(t.what == Body){
- q0 = t.backnl(t.org, 4);
- t.setorigin(q0, FALSE);
- }
- return;
- Kdown or Keyboard->Down =>
- n = t.frame.maxlines/3;
- q0 = t.org+frcharofpt(t.frame, (t.frame.r.min.x, t.frame.r.min.y+n*t.frame.font.height));
- t.setorigin(q0, FALSE);
- return;
- Keyboard->Pgdown =>
- n = 2*t.frame.maxlines/3;
- q0 = t.org+frcharofpt(t.frame, (t.frame.r.min.x, t.frame.r.min.y+n*t.frame.font.height));
- t.setorigin(q0, FALSE);
- return;
- Kup or Keyboard->Up =>
- n = t.frame.maxlines/3;
- q0 = t.backnl(t.org, n);
- t.setorigin(q0, FALSE);
- return;
- Keyboard->Pgup =>
- n = 2*t.frame.maxlines/3;
- q0 = t.backnl(t.org, n);
- t.setorigin(q0, FALSE);
- return;
- Keyboard->Home =>
- t.commit(TRUE);
- t.show(0, 0, FALSE);
- return;
- Kend or Keyboard->End =>
- t.commit(TRUE);
- t.show(t.file.buf.nc, t.file.buf.nc, FALSE);
- return;
- Kleft or Keyboard->Left =>
- t.commit(TRUE);
- if(t.q0 != t.q1)
- t.show(t.q0, t.q0, TRUE);
- else if(t.q0 != 0)
- t.show(t.q0-1, t.q0-1, TRUE);
- return;
- Kright or Keyboard->Right =>
- t.commit(TRUE);
- if(t.q0 != t.q1)
- t.show(t.q1, t.q1, TRUE);
- else if(t.q1 != t.file.buf.nc)
- t.show(t.q1+1, t.q1+1, TRUE);
- return;
- 1 => # ^A: beginning of line
- t.commit(TRUE);
- # go to where ^U would erase, if not already at BOL
- nnb = 0;
- if(t.q0>0 && t.readc(t.q0-1)!='\n')
- nnb = t.bswidth(16r15);
- t.show(t.q0-nnb, t.q0-nnb, TRUE);
- return;
- 5 => # ^E: end of line
- t.commit(TRUE);
- q0 = t.q0;
- while(q0<t.file.buf.nc && t.readc(q0)!='\n')
- q0++;
- t.show(q0, q0, TRUE);
- return;
- }
- if(t.what == Body){
- seq++;
- t.file.mark();
- }
- if(t.q1 > t.q0){
- if(t.ncache != 0)
- error("text.type");
- exec->cut(t, t, TRUE, TRUE);
- t.eq0 = ~0;
- if (r == 16r08 || r == 16r7f){ # erase character : odd if a char then erased
- t.show(t.q0, t.q0,TRUE);
- return;
- }
- }
- t.show(t.q0, t.q0, TRUE);
- case(r){
- 16r1B =>
- if(t.eq0 != ~0)
- t.setselect(t.eq0, t.q0);
- if(t.ncache > 0){
- if(t.w != nil)
- t.w.commit(t);
- else
- t.commit(TRUE);
- }
- return;
- 16r08 or 16r15 or 16r17 =>
- # ^H: erase character or ^U: erase line or ^W: erase word
- if(t.q0 == 0)
- return;
- if(0) # DEBUGGING
- for(i=0; i<t.file.ntext; i++){
- u = t.file.text[i];
- if(u.cq0!=t.cq0 && (u.ncache!=t.ncache || t.ncache!=0))
- error("text.type inconsistent caches");
- }
- nnb = t.bswidth(r);
- q1 = t.q0;
- q0 = q1-nnb;
- for(i=0; i<t.file.ntext; i++){
- u = t.file.text[i];
- u.nofill = TRUE;
- nb = nnb;
- n = u.ncache;
- if(n > 0){
- if(q1 != u.cq0+n)
- error("text.type backspace");
- if(n > nb)
- n = nb;
- u.ncache -= n;
- u.delete(q1-n, q1, FALSE);
- nb -= n;
- }
- if(u.eq0==q1 || u.eq0==~0)
- u.eq0 = q0;
- if(nb && u==t)
- u.delete(q0, q0+nb, TRUE);
- if(u != t)
- u.setselect(u.q0, u.q1);
- else
- t.setselect(q0, q0);
- u.nofill = FALSE;
- }
- for(i=0; i<t.file.ntext; i++)
- t.file.text[i].fill();
- return;
- 16r7f or Keyboard->Del =>
- # Delete character - forward delete
- t.commit(TRUE);
- if(t.q0 >= t.file.buf.nc)
- return;
- nnb = 1;
- q0 = t.q0;
- q1 = q0+nnb;
- for(i=0; i<t.file.ntext; i++){
- u = t.file.text[i];
- if (u!=t)
- u.commit(FALSE);
- u.nofill = TRUE;
- if(u.eq0==q1 || u.eq0==~0)
- u.eq0 = q0;
- if(u==t)
- u.delete(q0, q1, TRUE);
- if(u != t)
- u.setselect(u.q0, u.q1);
- else
- t.setselect(q0, q0);
- u.nofill = FALSE;
- }
- for(i=0; i<t.file.ntext; i++)
- t.file.text[i].fill();
- return;
- }
- # otherwise ordinary character; just insert, typically in caches of all texts
- if(0) # DEBUGGING
- for(i=0; i<t.file.ntext; i++){
- u = t.file.text[i];
- if(u.cq0!=t.cq0 && (u.ncache!=t.ncache || t.ncache!=0))
- error("text.type inconsistent caches");
- }
- for(i=0; i<t.file.ntext; i++){
- u = t.file.text[i];
- if(u.eq0 == ~0)
- u.eq0 = t.q0;
- if(u.ncache == 0)
- u.cq0 = t.q0;
- else if(t.q0 != u.cq0+u.ncache)
- error("text.type cq1");
- str := "Z";
- str[0] = r;
- u.insert(t.q0, str, 1, FALSE, echomode);
- str = nil;
- if(u != t)
- u.setselect(u.q0, u.q1);
- if(u.ncache == u.ncachealloc){
- u.ncachealloc += 10;
- u.cache += "1234567890";
- }
- u.cache[u.ncache++] = r;
- }
- t.setselect(t.q0+1, t.q0+1);
- if(r=='\n' && t.w!=nil)
- t.w.commit(t);
- }
- Text.commit(t : self ref Text, tofile : int)
- {
- if(t.ncache == 0)
- return;
- if(tofile)
- t.file.insert(t.cq0, t.cache, t.ncache);
- if(t.what == Body){
- t.w.dirty = TRUE;
- t.w.utflastqid = -1;
- }
- t.ncache = 0;
- }
- clicktext : ref Text;
- clickmsec : int = 0;
- selecttext : ref Text;
- selectq : int = 0;
- #
- # called from frame library
- #
-
- framescroll(f : ref Frame, dl : int)
- {
- if(f != selecttext.frame)
- error("frameselect not right frame");
- selecttext.framescroll(dl);
- }
- Text.framescroll(t : self ref Text, dl : int)
- {
- q0 : int;
- if(dl == 0){
- scrl->scrsleep(100);
- return;
- }
- if(dl < 0){
- q0 = t.backnl(t.org, -dl);
- if(selectq > t.org+t.frame.p0)
- t.setselect0(t.org+t.frame.p0, selectq);
- else
- t.setselect0(selectq, t.org+t.frame.p0);
- }else{
- if(t.org+t.frame.nchars == t.file.buf.nc)
- return;
- q0 = t.org+frcharofpt(t.frame, (t.frame.r.min.x, t.frame.r.min.y+dl*t.frame.font.height));
- if(selectq > t.org+t.frame.p1)
- t.setselect0(t.org+t.frame.p1, selectq);
- else
- t.setselect0(selectq, t.org+t.frame.p1);
- }
- t.setorigin(q0, TRUE);
- }
- Text.select(t : self ref Text, double : int)
- {
- q0, q1 : int;
- b, x, y : int;
- state : int;
- selecttext = t;
- #
- # To have double-clicking and chording, we double-click
- # immediately if it might make sense.
- #
-
- b = mouse.buttons;
- q0 = t.q0;
- q1 = t.q1;
- selectq = t.org+frcharofpt(t.frame, mouse.xy);
- if(double || (clicktext==t && mouse.msec-clickmsec<500))
- if(q0==q1 && selectq==q0){
- (q0, q1) = t.doubleclick(q0, q1);
- t.setselect(q0, q1);
- bflush();
- x = mouse.xy.x;
- y = mouse.xy.y;
- # stay here until something interesting happens
- do
- frgetmouse();
- while(mouse.buttons==b && utils->abs(mouse.xy.x-x)<3 && utils->abs(mouse.xy.y-y)<3);
- mouse.xy.x = x; # in case we're calling frselect
- mouse.xy.y = y;
- q0 = t.q0; # may have changed
- q1 = t.q1;
- selectq = q0;
- }
- if(mouse.buttons == b){
- t.frame.scroll = 1;
- frselect(t.frame, mouse);
- # horrible botch: while asleep, may have lost selection altogether
- if(selectq > t.file.buf.nc)
- selectq = t.org + t.frame.p0;
- t.frame.scroll = 0;
- if(selectq < t.org)
- q0 = selectq;
- else
- q0 = t.org + t.frame.p0;
- if(selectq > t.org+t.frame.nchars)
- q1 = selectq;
- else
- q1 = t.org+t.frame.p1;
- }
- if(q0 == q1){
- if(q0==t.q0 && (double || clicktext==t && mouse.msec-clickmsec<500)){
- (q0, q1) = t.doubleclick(q0, q1);
- clicktext = nil;
- }else{
- clicktext = t;
- clickmsec = mouse.msec;
- }
- }else
- clicktext = nil;
- t.setselect(q0, q1);
- bflush();
- state = 0; # undo when possible; +1 for cut, -1 for paste
- while(mouse.buttons){
- mouse.msec = 0;
- b = mouse.buttons;
- if(b & 6){
- if(state==0 && t.what==Body){
- seq++;
- t.w.body.file.mark();
- }
- if(b & 2){
- if(state==-1 && t.what==Body){
- t.w.undo(TRUE);
- t.setselect(q0, t.q0);
- state = 0;
- }else if(state != 1){
- exec->cut(t, t, TRUE, TRUE);
- state = 1;
- }
- }else{
- if(state==1 && t.what==Body){
- t.w.undo(TRUE);
- t.setselect(q0, t.q1);
- state = 0;
- }else if(state != -1){
- exec->paste(t, t, TRUE, FALSE);
- state = -1;
- }
- }
- scrdraw(t);
- utils->clearmouse();
- }
- bflush();
- while(mouse.buttons == b)
- frgetmouse();
- clicktext = nil;
- }
- }
- Text.show(t : self ref Text, q0 : int, q1 : int, doselect : int)
- {
- qe : int;
- nl : int;
- q : int;
- if(t.what != Body){
- if(doselect)
- t.setselect(q0, q1);
- return;
- }
- if(t.w!=nil && t.frame.maxlines==0)
- t.col.grow(t.w, 1, 0);
- if(doselect)
- t.setselect(q0, q1);
- qe = t.org+t.frame.nchars;
- if(t.org<=q0 && (q0<qe || (q0==qe && qe==t.file.buf.nc+t.ncache)))
- scrdraw(t);
- else{
- if(t.w.nopen[Dat->QWevent]>byte 0)
- nl = 3*t.frame.maxlines/4;
- else
- nl = t.frame.maxlines/4;
- q = t.backnl(q0, nl);
- # avoid going backwards if trying to go forwards - long lines!
- if(!(q0>t.org && q<t.org))
- t.setorigin(q, TRUE);
- while(q0 > t.org+t.frame.nchars)
- t.setorigin(t.org+1, FALSE);
- }
- }
- region(a, b : int) : int
- {
- if(a < b)
- return -1;
- if(a == b)
- return 0;
- return 1;
- }
- selrestore(f : ref Frame, pt0 : Point, p0 : int, p1 : int)
- {
- if(p1<=f.p0 || p0>=f.p1){
- # no overlap
- frdrawsel0(f, pt0, p0, p1, f.cols[BACK], f.cols[TEXT]);
- return;
- }
- if(p0>=f.p0 && p1<=f.p1){
- # entirely inside
- frdrawsel0(f, pt0, p0, p1, f.cols[HIGH], f.cols[HTEXT]);
- return;
- }
- # they now are known to overlap
- # before selection
- if(p0 < f.p0){
- frdrawsel0(f, pt0, p0, f.p0, f.cols[BACK], f.cols[TEXT]);
- p0 = f.p0;
- pt0 = frptofchar(f, p0);
- }
- # after selection
- if(p1 > f.p1){
- frdrawsel0(f, frptofchar(f, f.p1), f.p1, p1, f.cols[BACK], f.cols[TEXT]);
- p1 = f.p1;
- }
- # inside selection
- frdrawsel0(f, pt0, p0, p1, f.cols[HIGH], f.cols[HTEXT]);
- }
- Text.setselect(t : self ref Text, q0 : int, q1 : int)
- {
- p0, p1 : int;
- # t.p0 and t.p1 are always right; t.q0 and t.q1 may be off
- t.q0 = q0;
- t.q1 = q1;
- # compute desired p0,p1 from q0,q1
- p0 = q0-t.org;
- p1 = q1-t.org;
- if(p0 < 0)
- p0 = 0;
- if(p1 < 0)
- p1 = 0;
- if(p0 > t.frame.nchars)
- p0 = t.frame.nchars;
- if(p1 > t.frame.nchars)
- p1 = t.frame.nchars;
- if(p0==t.frame.p0 && p1==t.frame.p1)
- return;
- # screen disagrees with desired selection
- if(t.frame.p1<=p0 || p1<=t.frame.p0 || p0==p1 || t.frame.p1==t.frame.p0){
- # no overlap or too easy to bother trying
- frdrawsel(t.frame, frptofchar(t.frame, t.frame.p0), t.frame.p0, t.frame.p1, 0);
- frdrawsel(t.frame, frptofchar(t.frame, p0), p0, p1, 1);
- t.frame.p0 = p0;
- t.frame.p1 = p1;
- return;
- }
- # overlap; avoid unnecessary painting
- if(p0 < t.frame.p0){
- # extend selection backwards
- frdrawsel(t.frame, frptofchar(t.frame, p0), p0, t.frame.p0, 1);
- }else if(p0 > t.frame.p0){
- # trim first part of selection
- frdrawsel(t.frame, frptofchar(t.frame, t.frame.p0), t.frame.p0, p0, 0);
- }
- if(p1 > t.frame.p1){
- # extend selection forwards
- frdrawsel(t.frame, frptofchar(t.frame, t.frame.p1), t.frame.p1, p1, 1);
- }else if(p1 < t.frame.p1){
- # trim last part of selection
- frdrawsel(t.frame, frptofchar(t.frame, p1), p1, t.frame.p1, 0);
- }
- t.frame.p0 = p0;
- t.frame.p1 = p1;
- }
- Text.setselect0(t : self ref Text, q0 : int, q1 : int)
- {
- t.q0 = q0;
- t.q1 = q1;
- }
- xselect(f : ref Frame, mc : ref Draw->Pointer, col, colt : ref Image) : (int, int)
- {
- p0, p1, q, tmp : int;
- mp, pt0, pt1, qt : Point;
- reg, b : int;
- # when called button 1 is down
- mp = mc.xy;
- b = mc.buttons;
- # remove tick
- if(f.p0 == f.p1)
- frtick(f, frptofchar(f, f.p0), 0);
- p0 = p1 = frcharofpt(f, mp);
- pt0 = frptofchar(f, p0);
- pt1 = frptofchar(f, p1);
- reg = 0;
- frtick(f, pt0, 1);
- do{
- q = frcharofpt(f, mc.xy);
- if(p1 != q){
- if(p0 == p1)
- frtick(f, pt0, 0);
- if(reg != region(q, p0)){ # crossed starting point; reset
- if(reg > 0)
- selrestore(f, pt0, p0, p1);
- else if(reg < 0)
- selrestore(f, pt1, p1, p0);
- p1 = p0;
- pt1 = pt0;
- reg = region(q, p0);
- if(reg == 0)
- frdrawsel0(f, pt0, p0, p1, col, colt);
- }
- qt = frptofchar(f, q);
- if(reg > 0){
- if(q > p1)
- frdrawsel0(f, pt1, p1, q, col, colt);
- else if(q < p1)
- selrestore(f, qt, q, p1);
- }else if(reg < 0){
- if(q > p1)
- selrestore(f, pt1, p1, q);
- else
- frdrawsel0(f, qt, q, p1, col, colt);
- }
- p1 = q;
- pt1 = qt;
- }
- if(p0 == p1)
- frtick(f, pt0, 1);
- bflush();
- frgetmouse();
- }while(mc.buttons == b);
- if(p1 < p0){
- tmp = p0;
- p0 = p1;
- p1 = tmp;
- }
- pt0 = frptofchar(f, p0);
- if(p0 == p1)
- frtick(f, pt0, 0);
- selrestore(f, pt0, p0, p1);
- # restore tick
- if(f.p0 == f.p1)
- frtick(f, frptofchar(f, f.p0), 1);
- bflush();
- return (p0, p1);
- }
- Text.select23(t : self ref Text, q0 : int, q1 : int, high, low : ref Image, mask : int) : (int, int, int)
- {
- p0, p1 : int;
- buts : int;
- (p0, p1) = xselect(t.frame, mouse, high, low);
- buts = mouse.buttons;
- if((buts & mask) == 0){
- q0 = p0+t.org;
- q1 = p1+t.org;
- }
- while(mouse.buttons)
- frgetmouse();
- return (buts, q0, q1);
- }
- Text.select2(t : self ref Text, q0 : int, q1 : int) : (int, ref Text, int, int)
- {
- buts : int;
-
- (buts, q0, q1) = t.select23(q0, q1, acme->but2col, acme->but2colt, 4);
- if(buts & 4)
- return (0, nil, q0, q1);
- if(buts & 1) # pick up argument
- return (1, dat->argtext, q0, q1);
- return (1, nil, q0, q1);
- }
- Text.select3(t : self ref Text, q0 : int, q1 : int) : (int, int, int)
- {
- buts : int;
-
- (buts, q0, q1) = t.select23(q0, q1, acme->but3col, acme->but3colt, 1|2);
- return (buts == 0, q0, q1);
- }
- left := array[4] of {
- "{[(<«",
- "\n",
- "'\"`",
- nil
- };
- right := array[4] of {
- "}])>»",
- "\n",
- "'\"`",
- nil
- };
- Text.doubleclick(t : self ref Text, q0 : int, q1 : int) : (int, int)
- {
- c, i : int;
- r, l : string;
- p : int;
- q : int;
- res : int;
- for(i=0; left[i]!=nil; i++){
- q = q0;
- l = left[i];
- r = right[i];
- # try matching character to left, looking right
- if(q == 0)
- c = '\n';
- else
- c = t.readc(q-1);
- p = utils->strchr(l, c);
- if(p >= 0){
- (res, q) = t.clickmatch(c, r[p], 1, q);
- if (res)
- q1 = q-(c!='\n');
- return (q0, q1);
- }
- # try matching character to right, looking left
- if(q == t.file.buf.nc)
- c = '\n';
- else
- c = t.readc(q);
- p = utils->strchr(r, c);
- if(p >= 0){
- (res, q) = t.clickmatch(c, l[p], -1, q);
- if (res){
- q1 = q0+(q0<t.file.buf.nc && c=='\n');
- q0 = q;
- if(c!='\n' || q!=0 || t.readc(0)=='\n')
- q0++;
- }
- return (q0, q1);
- }
- }
- # try filling out word to right
- while(q1<t.file.buf.nc && isalnum(t.readc(q1)))
- q1++;
- # try filling out word to left
- while(q0>0 && isalnum(t.readc(q0-1)))
- q0--;
- return (q0, q1);
- }
- Text.clickmatch(t : self ref Text, cl : int, cr : int, dir : int, q : int) : (int, int)
- {
- c : int;
- nest : int;
- nest = 1;
- for(;;){
- if(dir > 0){
- if(q == t.file.buf.nc)
- break;
- c = t.readc(q);
- q++;
- }else{
- if(q == 0)
- break;
- q--;
- c = t.readc(q);
- }
- if(c == cr){
- if(--nest==0)
- return (1, q);
- }else if(c == cl)
- nest++;
- }
- return (cl=='\n' && nest==1, q);
- }
- Text.forwnl(t : self ref Text, p : int, n : int) : int
- {
- i, j : int;
- e := t.file.buf.nc-1;
- i = n;
- while(i-- > 0 && p<e){
- ++p;
- if(p == e)
- break;
- for(j=128; --j>0 && p<e; p++)
- if(t.readc(p)=='\n')
- break;
- }
- return p;
- }
- Text.backnl(t : self ref Text, p : int, n : int) : int
- {
- i, j : int;
- # look for start of this line if n==0
- if(n==0 && p>0 && t.readc(p-1)!='\n')
- n = 1;
- i = n;
- while(i-- > 0 && p>0){
- --p; # it's at a newline now; back over it
- if(p == 0)
- break;
- # at 128 chars, call it a line anyway
- for(j=128; --j>0 && p>0; p--)
- if(t.readc(p-1)=='\n')
- break;
- }
- return p;
- }
- Text.setorigin(t : self ref Text, org : int, exact : int)
- {
- i, a : int;
- r : ref Astring;
- n : int;
- t.frame.b.flush(Flushoff);
- if(org>0 && !exact){
- # org is an estimate of the char posn; find a newline
- # don't try harder than 256 chars
- for(i=0; i<256 && org<t.file.buf.nc; i++){
- if(t.readc(org) == '\n'){
- org++;
- break;
- }
- org++;
- }
- }
- a = org-t.org;
- fixup := 0;
- if(a>=0 && a<t.frame.nchars){
- frdelete(t.frame, 0, a);
- fixup = 1; # frdelete can leave end of last line in wrong selection mode; it doesn't know what follows
- }
- else if(a<0 && -a<t.frame.nchars){
- n = t.org - org;
- r = utils->stralloc(n);
- t.file.buf.read(org, r, 0, n);
- frinsert(t.frame, r.s, n, 0);
- utils->strfree(r);
- r = nil;
- }else
- frdelete(t.frame, 0, t.frame.nchars);
- t.org = org;
- t.fill();
- scrdraw(t);
- t.setselect(t.q0, t.q1);
- if(fixup && t.frame.p1 > t.frame.p0)
- frdrawsel(t.frame, frptofchar(t.frame, t.frame.p1-1), t.frame.p1-1, t.frame.p1, 1);
- t.frame.b.flush(Flushon);
- }
- Text.reset(t : self ref Text)
- {
- t.file.seq = 0;
- t.eq0 = ~0;
- # do t.delete(0, t.nc, TRUE) without building backup stuff
- t.setselect(t.org, t.org);
- frdelete(t.frame, 0, t.frame.nchars);
- t.org = 0;
- t.q0 = 0;
- t.q1 = 0;
- t.file.reset();
- t.file.buf.reset();
- }
|