created mirror

This commit is contained in:
whatever
2026-09-14 20:19:29 -04:00
commit 6764738d92
600 changed files with 87187 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
/*
Nile3Ops.java
*
* Copyright (c) 2000, Ted Nelson and Tuomas Lukka
*
* You may use and distribute under the terms of either the GNU Lesser
* General Public License, either version 2 of the license or,
* at your choice, any later version. Alternatively, you may use and
* distribute under the terms of the XPL.
*
* See the LICENSE.lgpl and LICENSE.xpl files for the specific terms of
* the licenses.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the README
* file for more details.
*
*/
/*
* Written by Tuomas Lukka
*/
package org.gzigzag.module;
import org.gzigzag.*;
import java.util.*;
/** A library of routines for operating on Nile streams.
* @see Nile1
*/
public abstract class Nile3Ops {
public static final String rcsid = "$Id: Nile3Ops.java,v 1.1 2001/01/07 18:59:06 bfallenstein Exp $";
public static boolean dbg = false;
static void p(String s) { if(dbg) System.out.println(s); }
static void pa(String s) { System.out.println(s); }
static public void saveCursor(ZZCell win) {
ZZCell s = win.getOrNewCell("d.nile-save", 1);
if(ZZCursorReal.get(s) == null)
ZZCursorReal.set(s, new ZZCursorReal(win));
}
static public void clearSavedCursor(ZZCell win) {
ZZCell s = win.getOrNewCell("d.nile-save", 1);
if(ZZCursorReal.get(s) != null)
ZZCursorReal.set(s, (ZZCell)null);
}
static public ZZCursor getSavedCursor(ZZCell win) {
ZZCell s = win.getOrNewCell("d.nile-save", 1);
return new ZZCursorReal(s);
}
static public void setCursor(Nile3Unit unit, ZZCell win, ZZCursor to) {
Nile3Pos pos = new Nile3Pos(to);
setPart(win, unit.extend(pos));
}
static public void moveCursor(Nile3Unit unit, ZZCell win, int dir) {
Nile3Part part = getPart(win);
if(!unit.step(part, dir)) return;
setPart(win, part);
}
static public void adjust(Nile3Unit unit, ZZCell win, int end, int dir) {
Nile3Part part = getPart(win);
if(!unit.adjust(part, end, dir)) return;
setPart(win, part);
}
static public void hop(Nile3Unit unit, ZZCell win, int dir) {
Nile3Part part = getPart(win);
Nile3Pos pos = part.side(dir);
if(!unit.insertPos(pos, dir, part, false)) return;
unit.move(part, pos, false);
setPart(win, part);
}
/** Tunnel text from one nile window to another.
* The text can be copied or cut.
*/
static public void tunnel(Nile3Unit unit, ZZCell from, ZZCell to,
boolean copy) {
Nile3Part part = getPart(from);
Nile3Pos pos = getPart(to).start;
if(!unit.insertPos(pos, -1, part, true)) return;
unit.move(part, pos, copy);
setPart(to, part);
}
static public boolean insert(ZZCell win, String key) {
if(key == null)
return false;
if(key.length() != 1)
return false;
Nile3Pos pos = getPart(win).start;
pos.insert(key, 1);
setPos(win, pos);
return true;
}
static public void backspace(Nile3Unit unit, ZZCell win) {
Nile3Part orig = getPart(win);
Nile3Part part = new Nile3Part(orig.start, orig.start);
unit.edge(part.start, -1, -1, false);
unit.edge(part.end, 1, -1, true);
unit.cut(part);
}
static public void del(Nile3Unit unit, ZZCell win) {
Nile3Pos pos = unit.cut(getPart(win));
setPos(win, pos);
}
static public void breakParagraph(Nile3Unit unit, ZZCell win) {
Nile3Part part = getPart(win);
part.start.breakParagraph();
}
/** Get a whole Nile stream as a string, properly capitalized.
* Converts paragraph breaks (==struct cells) to double newlines.<br>
* XXX a) It's no good this calls Nile1View.<br>
* XXX b) Do this for a Nile3Part (a selection), not only
* for a whole stream.<br>
* XXX c) Does this belong in this class? (It seemed to fit?)
*
* @param inStream Some cell inside the stream to be read.
*/
static public String stringify(ZZCell inStream) {
ZZCell hd = inStream.h("d.nile");
Nile1View n1v = new Nile1View();
n1v.init__zob();
String s = "";
ZZCell tail = Nile2Iter.findStruct(hd, 1, false);
while(hd != null) {
FText ft = n1v.paragraph(hd, tail, null, null, null);
ft.capitalize();
s = s + FText.Part.join(ft.parts);
hd = tail;
if(hd != null) tail = Nile2Iter.findStruct(hd, 1, false);
s = s + "\n\n";
}
return s;
}
// Shorthand...
static public Nile3Part getPart(ZZCell win) {
ZZCursorReal start = new ZZCursorReal(win);
if(start.getOffs() == start.NO_OFFSET)
start = new ZZCursorReal(win.getOrNewCell("d.nile-save"));
ZZCursorReal end = new ZZCursorReal(win.getOrNewCell("d.nile-sel"));
return new Nile3Part(start, end);
}
static public void setPart(ZZCell win, Nile3Part part) {
ZZCursor start = part.start.get(), end = part.end.get();
ZZCursorReal.set(win, start.get());
// if(ZZCursorReal.getOffs(win) != ZZCursorReal.NO_OFFSET)
ZZCursorReal.setOffs(win, start.getOffs());
ZZCursorReal.set(win.getOrNewCell("d.nile-save"), start);
ZZCursorReal.set(win.getOrNewCell("d.nile-sel"), end);
}
static public void setPos(ZZCell win, Nile3Pos pos) {
setPart(win, new Nile3Part(pos, pos));
}
}

View File

@@ -0,0 +1,132 @@
/*
Nile3Part.java
*
* Copyright (c) 2000, Ted Nelson and Tuomas Lukka
*
* You may use and distribute under the terms of either the GNU Lesser
* General Public License, either version 2 of the license or,
* at your choice, any later version. Alternatively, you may use and
* distribute under the terms of the XPL.
*
* See the LICENSE.lgpl and LICENSE.xpl files for the specific terms of
* the licenses.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the README
* file for more details.
*
*/
/*
* Written by Benjamin Fallenstein
*/
package org.gzigzag.module;
import org.gzigzag.*;
/** A Nile span, with a start and an end.
* This object explicitly assumes no modifications to the
* stream while it is working.
*/
public final class Nile3Part implements Cloneable {
public static final String rcsid = "$Id: Nile3Part.java,v 1.1 2001/01/07 18:59:06 bfallenstein Exp $";
public static boolean dbg = true;
static final void p(String s) { if(dbg) System.out.println(s); }
static final void pa(String s) { System.out.println(s); }
public final Nile3Pos start = new Nile3Pos();
public final Nile3Pos end = new Nile3Pos();
public Nile3Part(Nile3Pos start, Nile3Pos end) {
this.start.set(start); this.end.set(end);
check();
}
public Nile3Part(ZZCursor start, ZZCursor end) {
p(start + " " + end);
this.start.set(start); this.end.set(end);
check();
}
public Nile3Pos side(int dir) {
if(dir < 0) return start;
else return end;
}
public Nile3Pos cut() {
if(isStream())
return null; // XXX should this throw an error?
check();
start.split(false, 1);
end.split(false, -1);
Nile3Pos before = (Nile3Pos)start.clone();
Nile3Pos after = (Nile3Pos)end.clone();
if(!before.bias(-1) || !after.bias(1))
throw new ZZError("HELP! End of stream after split! XXX");
start.c.disconnect("d.nile", -1);
end.c.disconnect("d.nile", 1);
before.c.connect("d.nile", 1, after.c);
return before;
}
/** Make a cell's content into a span. */
static public Span spanify(ZZCell c0) {
Span sp = c0.getSpan();
if(sp != null) return sp;
StringScroll sc = c0.getSpace().getStringScroll();
String s = c0.getText();
long soffs = sc.append(s);
sp = Span.create(
Address.scrollOffs(sc, soffs),
Address.scrollOffs(sc, soffs + s.length() - 1)
);
c0.setSpan(sp);
return sp;
}
public Nile3Part copy() {
check();
start.split(false, 1);
end.split(false, -1);
ZZCell c = start.c;
ZZCell first = c.N(), last = first;
while(true) {
last.setSpan(spanify(c));
if(c.equals(end.c)) break;
c = c.s("d.nile");
last = last.N("d.nile");
}
return new Nile3Part(
new ZZCursorVirtual(first, 0),
new ZZCursorVirtual(last, last.getText().length()));
}
/** Return true if there's nothing before or after this. */
public boolean isStream() {
return start.end(-1) && end.end(1);
}
/** Check the integrity of this Nile3Part: start &lt;= end? */
public void check() {
boolean good = true;
if(start.c.equals(end.c)) {
good = start.offs <= end.offs;
p(start.offs + " " + end.offs);
} else {
good = start.c.findCell("d.nile", 1, end.c);
p(start.c + " " + end.c);
}
if(!good)
throw new ZZError("Malformed Nile3Part stream! XXX");
}
public Object clone() {
try {
return super.clone();
} catch(CloneNotSupportedException e) {
ZZLogger.exc(e);
return null;
}
}
}

View File

@@ -0,0 +1,338 @@
/*
Nile3Pos.java
*
* Copyright (c) 2000, Ted Nelson and Tuomas Lukka
*
* You may use and distribute under the terms of either the GNU Lesser
* General Public License, either version 2 of the license or,
* at your choice, any later version. Alternatively, you may use and
* distribute under the terms of the XPL.
*
* See the LICENSE.lgpl and LICENSE.xpl files for the specific terms of
* the licenses.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the README
* file for more details.
*
*/
/*
* Written by Benjamin Fallenstein
*/
package org.gzigzag.module;
import org.gzigzag.*;
import java.util.*;
/** A character-level iterator object that knows about
* paragraphs for a Nile stream.
* This object explicitly assumes no modifications to the
* stream while it is working.
* The position is between the characters, not on a character.
* Actually, it is <em>between (a char | a para boundary) and
* (a char | a para boundary)</em>, i.e., it can be at the beginning or end
* of a para or between two paras.
*/
public final class Nile3Pos implements Cloneable {
public static final String rcsid = "$Id: Nile3Pos.java,v 1.1 2001/01/07 18:59:06 bfallenstein Exp $";
public static boolean dbg = false;
static final void p(String s) { if(dbg) System.out.println(s); }
static final void pa(String s) { System.out.println(s); }
/** Returned by type() or read(), this denotes end/beginning of stream. */
public static final int END = -1;
/** Returned by type() or read(), this denotes a paragraph break. */
public static final int PARA = -2;
/** Returned by type(), this means punctuation. */
public static final int PUNCT = -3;
/** Returned by type(), this means alphanumeric character. */
public static final int ALPHA = -4;
/** Returned by type(), this means whitespace. */
public static final int WS = -5;
/** Returned by read(), this means special thing (to type, it's PUNCT). */
public static final int SPECIAL = -6;
ZZCell c;
int offs;
// Cache these for speed.
// Note: ctxt and cspan are only valid if NOT (isStruct || isSpecial)!
int clen;
String ctxt;
Span cspan;
// Is this cell a struct cell, a text object?
private boolean isStruct, isSpecial;
public Nile3Pos(Nile3Pos pos) { set(pos); }
public Nile3Pos(ZZCursor pos) { set(pos); }
Nile3Pos() {}
public ZZCursor get() { return new ZZCursorVirtual(c, offs); };
private void load(ZZCell c) {
this.c = c;
isStruct = (c.s("d.nile-struct", 1) != null);
isSpecial = (c.s("d.nile-special", 1) != null);
if(isStruct || isSpecial)
clen = 1;
else {
ctxt = c.getText();
cspan = c.getSpan();
clen = ctxt.length();
}
}
public void set(Nile3Pos pos) {
load(pos.c);
offs = pos.offs;
}
public void set(ZZCursor pos) {
load(pos.get());
offs = pos.getOffs();
if(offs == pos.NO_OFFSET) offs = 0;
}
/** Internal convenience: try to insert with bias, if can't, return */
private boolean insert(String s, int side, int bias) {
if(!bias(bias)) return false;
if(cspan != null) return false;
c.setText(ctxt.substring(0, offs) + s + ctxt.substring(offs));
ZZCursorReal.adjustCursors(c, offs, s.length());
if(side > 0) offs += s.length();
return true;
}
public void insert(String s, int side) {
if(insert(s, side, 1)) return;
if(offs == 0)
if(insert(s, side, -1)) return;
split(false, 1);
load(c.N("d.nile", -1));
c.setText(s);
offs = (side < 0) ? (0) : (clen);
}
public void insert(Nile3Part what, int side) {
if(!what.isStream())
throw new ZZError("Can't insert non-stream Nile3Part!");
split(false, side);
ZZCell other = c.s("d.nile", -side);
c.disconnect("d.nile", -side);
c.connect("d.nile", -side, what.side(side).c);
other.connect("d.nile", side, what.side(-side).c);
}
public boolean delete(int bias) {
// First, ensure we are in the same cell as the character to delete.
if(!bias(bias)) return false;
// Then, a special case check (also for isStruct and isSpecial cells)
if(clen == 1) {
ZZCell from = c;
if(c.s("d.nile") != null) {
load(c.s("d.nile"));
offs = 0;
} else if(c.s("d.nile", -1) != null) {
load(c.s("d.nile", -1));
offs = clen;
} else {
load(c.N("d.nile"));
offs = 0;
}
Enumeration e = ZZCursorReal.getPointers(from);
while(e.hasMoreElements()) {
ZZCell curs = (ZZCell)e.nextElement();
ZZCursorReal.setcursor(curs, c);
if(ZZCursorReal.getOffs(curs) != ZZCursorReal.NO_OFFSET)
ZZCursorReal.setOffs(curs, offs);
}
from.delete();
return true;
}
if(cspan == null) {
if(bias < 0) offs--;
c.setText(ctxt.substring(0, offs) + ctxt.substring(offs));
ZZCursorReal.adjustCursors(c, offs, -1);
} else {
split(false, bias);
if(bias > 0) c.setSpan(cspan.subSpan(1));
else c.setSpan(cspan.subSpan(0, clen-1));
}
load(c);
return true;
}
/** Split at the current position.
* The original cell will remain the first.
* If side is greater than zero, this pos will be set to the second cell;
* if side is less than zero, this pos will be set to the first cell.
* If offs is zero or equal to the number of characters in c, nothing is
* done and either the current cell or the next cell is returned.
* <p>
* The most special case involves doing this at the very end of the stream,
* in which case an empty new cell is created and returned.
* @param side The side of the split to make this Nile3Pos point to.
* @param always Whether to split even if the offset is at the beginning
* or end of a cell. Used e.g. for inserting a paragraph.
*/
public void split(boolean always, int side) {
if(always) {
ZZCell c1 = c.N("d.nile");
if(cspan == null) {
c.setText(ctxt.substring(0, offs));
c1.setText(ctxt.substring(offs));
} else {
c.setSpan(cspan.subSpan(0, offs));
c1.setSpan(cspan.subSpan(offs));
}
Enumeration e = ZZCursorReal.getPointers(c);
while(e.hasMoreElements()) {
ZZCell p = (ZZCell)e.nextElement();
int poffs = ZZCursorReal.getOffs(p);
if(poffs != ZZCursorReal.NO_OFFSET && poffs > offs) {
ZZCursorReal.setcursor(p, c1);
ZZCursorReal.setOffs(p, poffs-offs);
}
}
if(side > 0) { load(c1); offs = 0; }
} else {
if(offs == 0) {
if(side < 0) {
load(c.getOrNewCell("d.nile", -1));
offs = clen;
}
} else if(offs >= clen) {
if(side > 0) {
load(c.getOrNewCell("d.nile"));
offs = 0;
}
} else
split(true, side);
}
}
public int type(int dir) {
int ch = read(dir);
if(ch < 0) return ch;
if(Character.isLetterOrDigit((char)ch)) return ALPHA;
if(Character.isWhitespace((char)ch)) return WS;
return PUNCT;
}
/** Return true if type() would return <code>Nile3Pos.END</code>. */
public boolean end(int dir) {
return type(dir) == END;
}
/** Return true if type() would return <code>Nile3Pos.PARA</code>. */
public boolean para(int dir) {
return type(dir) == PARA;
}
/** Return true if type() would return <code>Nile3Pos.PUNCT</code>. */
public boolean punct(int dir) {
return type(dir) == PUNCT;
}
/** Return true if type() would return <code>Nile3Pos.ALPHA</code>. */
public boolean alpha(int dir) {
return type(dir) == ALPHA;
}
/** Return true if type() would return <code>Nile3Pos.WS</code>. */
public boolean ws(int dir) {
return type(dir) == WS;
}
/** Return true if boundary, i.e. if end() or para() would return true. */
public boolean boundary(int dir) {
int t = type(dir);
return (t == END) || (t == PARA);
}
/** Return true if boundary() or ws() would test true. */
public boolean bws(int dir) {
int t = type(dir);
return (t == WS) || (t == END) || (t == PARA);
}
/** Read character in given direction. */
public int read(int dir) {
if(!bias(dir)) return END;
if(isStruct) return PARA;
if(isSpecial) return SPECIAL;
return ctxt.charAt((dir < 0) ? (offs - 1) : (offs));
}
/** Move n characters forwards or backwards.
* If the offset given is out of bounds, moves as far as possible
* and returns false, otherwise returns true. In general, this moves as
* little as possible, i.e. if n was positive, bias is negative, and if
* n was negative, bias is positive after call.
*/
public boolean move(int n) {
n += offs;
offs = 0;
while(n > clen) {
n -= clen;
ZZCell nc = c.s("d.nile");
if(nc == null) {
// End of stream!
offs = clen;
if(n > 0) return false;
return true;
}
load(nc);
}
while(n < 0) {
ZZCell nc = c.s("d.nile", -1);
if(nc == null) {
offs = 0;
return false;
}
load(nc);
n += clen;
}
offs = n;
return true;
}
/** (Convenience:) Move if doMove is true.
* This is used by Nile3Unit to make includeThis code simpler.
*/
public boolean move(boolean doMove, int n) {
if(doMove) return move(n);
else return true;
}
/** Ensure bias in a given direction.
* This means that after bias(1), we are in the cell that contains the
* character in positive direction, and after bias(-1), same in negative
* direction.<br>
* If we bump on the end of the stream, return false, else return true.
*/
boolean bias(int dir) {
if(offs + dir >= 0 && offs + dir <= clen) return true;
do {
ZZCell nc = c.s("d.nile", dir);
if(nc == null) return false;
load(nc);
} while(clen == 0);
return true;
}
public void breakParagraph() {
split(false, 1);
c.N("d.nile", -1).N("d.nile-struct").setText("P");
}
public Object clone() {
try {
return super.clone();
} catch(CloneNotSupportedException e) {
ZZLogger.exc(e);
return null;
}
}
}

View File

@@ -0,0 +1,305 @@
/*
Nile3Unit.java
*
* Copyright (c) 2000, Ted Nelson and Tuomas Lukka
*
* You may use and distribute under the terms of either the GNU Lesser
* General Public License, either version 2 of the license or,
* at your choice, any later version. Alternatively, you may use and
* distribute under the terms of the XPL.
*
* See the LICENSE.lgpl and LICENSE.xpl files for the specific terms of
* the licenses.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the README
* file for more details.
*
*/
/*
* Written by Benjamin Fallenstein, based on Nile2Unit by Tuomas Lukka
*/
package org.gzigzag.module;
import org.gzigzag.*;
/** A nile unit of division.
* Spaces between units may be non-zero-width. Units may be zero-width, if
* neighbouring units are separated by non-zero spaces.
*/
public abstract class Nile3Unit {
public static final String rcsid = "$Id: Nile3Unit.java,v 1.1 2001/01/07 18:59:06 bfallenstein Exp $";
public static boolean dbg = false;
static final void p(String s) { if(dbg) System.out.println(s); }
static final void pa(String s) { System.out.println(s); }
public static class Char extends Nile3Unit {
public boolean isEdge(Nile3Pos pos, int dir) {
return !pos.boundary(dir);
}
}
public static class Word extends Nile3Unit {
/** An alphanumeric word. */
static final int AWORD = 1;
/** A punctuation word, e.g. a smilie. */
static final int PWORD = 2;
/** An A- or PWORD. */
static final int WORD = AWORD | PWORD;
/** A piece of left-bound punctuation, like commas. */
static final int LPUNCT = 4;
/** A piece of right-bound punctuation, like opening brackets. */
static final int RPUNCT = 8;
/** A piece of left- or right-bound punctuation. */
static final int BPUNCT = LPUNCT | RPUNCT;
/** A paragraph boundary or the end of the stream. */
static final int BOUNDARY = 16;
/** Not a correct combination of words. */
static final int BAD = 0;
/** Correct combination iff no space in between. */
static final int NSP = 1;
/** Correct combination iff space in between. */
static final int SP = 2;
/** The type of a joining position. */
public static final int jointype(int lefttype, int righttype) {
if(lefttype & WORD > 0 && righttype & WORD > 0)
return SP;
else if(lefttype == AWORD && righttype == LPUNCT)
return NSP;
else if(lefttype == AWORD && righttype == RPUNCT)
return SP;
else if(lefttype == RPUNCT && righttype == AWORD)
return NSP;
else if(lefttype == LPUNCT && righttype == AWORD)
return SP;
else if(lefttype & BPUNCT > 0 && righttype == lefttype)
return NSP;
else if(lefttype == LPUNCT && righttype == RPUNCT)
return SP;
else if(lefttype == BOUNDARY && righttype == BOUNDARY)
return NSP;
else if(lefttype == BOUNDARY && righttype & WORD > 0)
return NSP;
else if(lefttype & WORD > 0 && righttype == BOUNDARY)
return NSP;
else if(lefttype == LPUNCT && righttype == BOUNDARY)
return NSP;
else if(lefttype == BOUNDARY && righttype == RPUNCT)
return NSP;
else
return BAD;
}
/** Find out the types of the words next to a word boundary.
* This may only be called on what is known to be a word boundary.
* Else, its behavior is undefined.
*/
public static final int[] wordtype(Nile3Pos pos, int dir) {
int[] res = new int[2];
int lch = pos.type(-1), rch = pos.type(1);
boolean lp = pos.punct(-1), rp = pos.punct(1);
Nile3Pos lpos, rpos;
if(lp) {
lpos = (Nile3Pos)pos.clone();
while(lpos.punct(-1)) lpos.move(-1);
}
if(rp) {
rpos = (Nile3Pos)pos.clone();
while(rpos.punct(1)) rpos.move(1);
}
if(!lp && !rp) {
if(pos.
}
public void cleanJoin(Nile3Pos pos, int lefttype, int righttype) {
int type = jointype(lefttype, righttype);
if(jointype == NSP) {
while(pos.ws(-1)) pos.delete(-1);
while(pos.ws(1)) pos.delete(1);
} else if(jointype == SP) {
if(!pos.ws(-1) && !pos.ws(1)) pos.insert(" ", 1);
} else {
p("Nile3Unit.Word: Detected bad joining in cleanJoin");
}
}
public boolean isEdge(Nile3Pos pos, int dir) {
if(pos.punct(-dir)) return true;
if(pos.bws(dir) && pos.alpha(-dir)) return true;
return false;
}
/** Can what be inserted at pos? */
public boolean isInsertPos(Nile3Pos pos, Nile3Part what) {
if(!pos.boundary(1) && !isEdge(pos, -1)) return false;
if(what.start.punct(1) && !what.start.bws(-1)) {
}
if(what.end.punct(-1) && !what.end.bws(1)) {
if(
}
if(jointype(pos.-1, what.start.1) == BAD) return false;
if(jointype(pos.1, what.end.-1) == BAD) return false;
}
}
public static class Sentence extends Nile3Unit {
public boolean isEdge(Nile3Pos pos, int dir) {
if(pos.ws(dir)) return false;
if(pos.boundary(-dir)) return true;
if(!pos.move(-dir)) return false;
if((pos.bws(-dir)) && pos.ws(dir)) return true;
pos.move(dir);
return false;
}
}
// Ugh! Paragraph is *inefficient*!
// XXX is this a problem? in very long paragraphs maybe?
public static class Paragraph extends Nile3Unit {
public boolean isEdge(Nile3Pos pos, int dir) {
return pos.boundary(-dir);
}
}
public boolean step(Nile3Part part, int n) {
int abs = (n < 0) ? (-n) : (n);
int dir = (n < 0) ? (-1) : (1);
Nile3Pos pos = part.side(dir), other = part.side(-dir);
for(int i=0; i<abs; i++) {
other.set(pos);
if(!edge(other, -dir, dir, true)) return false;
pos.set(other);
if(!edge(pos, dir, dir, false)) return false;
}
return true;
}
public boolean adjust(Nile3Part part, int side, int n) {
int abs = (n < 0) ? (-n) : (n);
int dir = (n < 0) ? (-1) : (1);
for(int i=0; i<abs; i++) {
if(!edge(part.side(side), side, dir, false)) return false;
}
return true;
}
public Nile3Part extend(Nile3Pos pos) {
Nile3Part res = new Nile3Part(pos, pos);
if(!extend(res)) return null;
return res;
}
public boolean extend(Nile3Part part) {
if(!edge(part.start, -1, -1, true)) return false;
if(!edge(part.end, 1, 1, true)) return false;
return true;
}
public boolean delete(Nile3Part part) {
cleanUp(part.delete());
}
protected void cleanUp(Nile3Pos pos) {}
/** Move a Nile3Part to a position. */
public void move(Nile3Part what, Nile3Pos where, int moveDir,
boolean includeThis, boolean copy) {
// First, prepare the part.
if(!extend(what)) return null;
if(copy) what = what.copy();
else rmSpace(what.cut());
// Then, prepare the pos.
if(!insertPos(pos, moveDir, what, includeThis)) return null;
// Finally, move.
Nile3Pos res = paste(what, where);
rmSpace(res);
return res;
}
/** The previous or next position where this type of unit may be inserted.
* By standard, return edge(pos, -1, dir, includeThis).
*/
protected boolean insertPos(Nile3Pos pos, int dir, Nile3Part what,
boolean includeThis) {
if(!pos.move(!includeThis, moveDir)) return false;
do {
if(pos.boundary(1) || isEdge(pos, -1)) return true;
} while(pos.move(moveDir));
return false;
}
/** Is this an edge of this type of unit in the given direction?
* Positive direction means there follows such a unit after this point;
* negative direction menas this point is preceded by such a unit.
*/
protected abstract boolean isEdge(Nile3Pos pos, int dir);
public boolean edge(Nile3Pos pos, int edgeDir, int moveDir,
boolean includeThis) {
if(!pos.move(!includeThis, moveDir)) return false;
do {
if(isEdge(pos, edgeDir)) return true;
} while(pos.move(moveDir));
return false;
}
/** The previous or next beginning position of this type of unit.
* @returns false if we bump into end of string.
*/
public boolean start(Nile3Pos pos, int dir, boolean includeThis) {
return edge(pos, -1, dir, includeThis);
}
/** The previous or next ending position of this type of unit.
* @returns false if we bump into end of string.
*/
public boolean end(Nile3Pos pos, int dir, boolean includeThis) {
return edge(pos, 1, dir, includeThis);
}
/** Modify the selection so that it makes sense to the user. */
public boolean select(Nile3Part part, boolean extend) {
boolean res = start(part.start, extend ? -1 : 1, true);
res = res && end(part.end, extend ? 1 : -1, true);
return res;
}
/** Modify the selection for moving it around (cut, copy) */
public boolean prepare(Nile3Part part) {
return true;
}
/** Cut the stream.
* This routine modifies the Nile3Part to point
* to the cut stream, and returns the cut position.
* @return The pasted-together cutaway point, from which
* the insert position can be searched with !includeThis.
*/
public Nile3Pos cut(Nile3Part part) { return part.cut(); }
/** Copy a part of the stream.
* First every cell is spanified, then a new stream with the same spans
* is created.
* This routine modifies the start and to iterators to point
* to the beginning and end of the copied stream.
* XXX any special copying rules to obey, like in cut?
*/
public Nile3Part copy(Nile3Part what) {
this.start(what.start, -1, true);
this.end(what.end, 1, true);
return what.copy();
}
protected void paste(Nile3Pos at, Nile3Part what) { at.insert(what, 1); }
}