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

327
Modules/nile/Nile1.java Normal file
View File

@@ -0,0 +1,327 @@
/*
Nile1.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.*;
import java.awt.*;
import java.io.*;
/** A stream with parallel markup.
* <p>
* Character-level formatting is currently done using B and I (as in HTML,
* for bold and italics). These are placed in cells whose headcell
* on <code>d.nile-fmt-start</code> is the cell in the stream that the
* markup starts from. Likewise, their headcell on <code>d.nile-fmt-end</code>
* is the last cell that does have this markup.
* This is a bit fragile with reordering but most effects can be limited by
* knowing that all formats are allowed to touch only things inside a paragraph
* and we can run a fixer script every once in a while.
* <p>
* Paragraph-, section- and chapter -level markup is done by cells
* that say the level: P, H1..H6 on the dimension
* <code>d.nile-struct</code>. These are interpreted by the space part
* so that the HOP operation can be used to move things around easily.
* For higher-than-paragraph level sectioning, the part starting at
* the section, up to the next paragraph or section is considered the
* heading.
* So, in <pre>H1 foo H2 bar P ab H2 zzz P cd</pre>,
* the headings would be foo, bar and zzz.
*/
public class Nile1 {
public static final String rcsid = "$Id: Nile1.java,v 1.10 2001/01/26 13:53:27 ajk 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 ZZModule module = new ZZModule() {
public void action(String id,
ZZCell code,
ZZCell target,
ZZView view, ZZView cview, String key, Point pt, ZZScene xi) {
ZZUpdateManager.setFast(view);
ZZCell viewCell = view.getViewcell();
ZZCell otherCell = viewCell.s("d.nile-wins");
boolean split = false;
ZZCell viewAcc = ZZCursorReal.get(viewCell);
ZZCell leftCell = viewAcc.s("d.nile-pair", -1);
ZZCell rightCell = viewAcc.s("d.nile-pair");
if(leftCell != null || rightCell != null) {
split = true;
if(leftCell != null) {
rightCell = viewAcc;
viewCell = rightCell;
otherCell = leftCell;
} else {
leftCell = viewAcc;
viewCell = leftCell;
otherCell = rightCell;
}
}
p("Nile action: id: '"+id+"' key: '"+key+"' code: "+code);
StringTokenizer st = new StringTokenizer(id);
String[] toks = new String[st.countTokens()];
for(int i=0; i<toks.length; i++)
toks[i] = st.nextToken();
int dir = 0;
Nile2Unit unit = null;
if(toks.length > 1) {
if(toks[1].equals("+")) dir = 1;
else if(toks[1].equals("-")) dir = -1;
if(toks.length > 2) {
if(toks[2].equals("CHAR"))
unit = new Nile2Unit.Char();
else if(toks[2].equals("WORD"))
unit = new Nile2Unit.Word();
else if(toks[2].equals("SENT"))
unit = new Nile2Unit.Sentence();
else if(toks[2].equals("PARA"))
unit = new Nile2Unit.Paragraph();
}
}
id = toks[0];
if(id.equals("Cursor")) {
p("Cursor: "+dir);
Nile2Ops.moveCursor(unit, viewCell, dir);
ZZUpdateManager.setFast(null);
}
else if(id.equals("AdjLeft")) {
Nile2Ops.adjust(unit, viewCell, -1, dir);
ZZUpdateManager.setFast(null);
}
else if(id.equals("AdjRight")) {
Nile2Ops.adjust(unit, viewCell, 1, dir);
ZZUpdateManager.setFast(null);
}
else if(id.equals("Hop")) {
Nile2Ops.hop(unit, viewCell, dir);
}
else if(id.equals("Mode")) {
Nile2Ops.setCursor(unit, viewCell, null);
if(split)
Nile2Ops.setCursor(unit, otherCell, null);
}
else if(id.equals("Insert")) {
Nile2Ops.insert(viewCell, key);
// Set the selection of other half to char mode, too
if(split)
Nile2Ops.setCursor(new Nile2Unit.Char(), otherCell, null);
}
else if(id.equals("BackSpace")) {
Nile2Ops.backspace(unit, viewCell);
}
else if(id.equals("Delete")) {
Nile2Ops.del(unit, viewCell);
}
else if(id.equals("BreakParagraph")) {
Nile2Ops.breakParagraph(unit, viewCell);
}
else if(id.equals("Tunnel")) {
if(otherCell != null) {
Nile2Ops.tunnel(unit, otherCell, viewCell, dir, false);
}
}
else if(id.equals("Transcopy")) {
if(otherCell != null) {
Nile2Ops.tunnel(unit, otherCell, viewCell, dir, true);
}
}
else if(id.equals("PUICopy")) {
ZZCell c = ZZCursorReal.get(viewCell);
String s = Nile2Ops.stringify(c);
ZZUtil.puiCopy(s);
}
/*
else if(id.equals("TraverseTranscopies")) {
Nile2Ops.traverseTranscopies(viewCell, dir);
}
*/
Object ob = null;
if(pt != null)
ob = xi.getObjectAt(pt.x, pt.y);
if(id.equals("MouseSetCursor")) {
p("Setting cursor from mouse! Unit: "+unit);
if(split) {
// Brute force: was the click in left window half?
if(pt.x < ((FlobSet)xi).getSize().width/2) {
viewCell = leftCell; otherCell = rightCell;
} else {
viewCell = rightCell; otherCell = leftCell;
}
ZZCursorReal.set(view.getViewcell(), viewCell);
}
Nile2Ops.setCursor(unit, viewCell, (ZZCursor)ob);
}
}
public ZOb newZOb(String id) {
if(id.equals("Normal")) {
return new Nile1View();
} else if(id.equals("Parallel")) {
return new SplitNileView();
}
return null;
}
};
// This is really hacky part...
static ZZCell curAct;
static void a(String key, String act) { a(key, act, null); }
static void a(String key, String act, ZZCell next) {
curAct = curAct.N("d.2", 1);
curAct.setText(key);
ZZCell n = curAct.N("d.1", 1);
n.setText("Nile1." + act);
if(next != null)
next.insert("d.3", 1, n);
}
static void actions(ZZCell start) {
start.setText("Nile char");
ZZCell nchar = start;
ZZCell nword = nchar.N("d.1", 1); nword.setText("Nile word");
ZZCell nsent = nword.N("d.1", 1); nsent.setText("Nile sent");
ZZCell npara = nsent.N("d.1", 1); npara.setText("Nile para");
ZZCell ncommon = npara.N("d.1", 1); ncommon.setText("Nile common");
curAct = nword;
// Annoying - these should be structurally
// describable!!!
a("MouseClicked1", "MouseSetCursor 0 WORD");
a("Backspace", "BackSpace 0 WORD");
a("Delete", "Delete 0 WORD");
a("Alt-Backspace", "Delete 0 WORD");
a("Ctrl-P", "Tunnel - WORD"); // P for pull
a("Ctrl-T", "Transcopy - WORD");
a("Left", "Cursor - WORD");
a("Right", "Cursor + WORD");
a("Shift-Left", "AdjLeft - WORD");
a("Shift-Right", "AdjRight + WORD");
a("Ctrl-Left", "AdjRight - WORD");
a("Ctrl-Right", "AdjLeft + WORD");
a("Alt-Left", "Hop - WORD");
a("Alt-Right", "Hop + WORD");
a("Enter", "BreakParagraph 0 WORD");
a("Up", "Cursor - SENT");
a("Down", "Cursor + SENT");
a("Alt-Up", "Hop - SENT");
a("Alt-Down", "Hop + SENT");
a("INSERT", "Insert", nchar);
a("Tab", "Mode 0 SENT", nsent);
curAct = nsent;
a("MouseClicked1", "MouseSetCursor 0 SENT");
a("Backspace", "BackSpace 0 SENT");
a("Delete", "Delete 0 SENT");
a("Alt-Backspace", "Delete 0 SENT");
a("Ctrl-P", "Tunnel - SENT"); // P for push
a("Ctrl-T", "Transcopy - SENT");
a("Left", "Cursor - SENT");
a("Right", "Cursor + SENT");
a("Shift-Left", "AdjLeft - SENT");
a("Shift-Right", "AdjRight + SENT");
a("Ctrl-Left", "AdjRight - SENT");
a("Ctrl-Right", "AdjLeft + SENT");
a("Alt-Left", "Hop - SENT");
a("Alt-Right", "Hop + SENT");
a("Enter", "BreakParagraph 0 SENT");
a("Up", "Cursor - PARA");
a("Down", "Cursor + PARA");
a("Alt-Up", "Hop - PARA");
a("Alt-Down", "Hop + PARA");
a("INSERT", "Insert", nchar);
a("Tab", "Mode 0 CHAR", nchar);
/*
curAct = npara;
a("MouseClicked1", "MouseSetCursor 0 PARA");
a("Backspace", "BackSpace 0 PARA");
a("Delete", "Delete 0 PARA");
a("Alt-Backspace", "Delete 0 PARA");
a("Left", "Cursor - PARA");
a("Right", "Cursor + PARA");
a("Shift-Left", "AdjLeft - PARA");
a("Shift-Right", "AdjRight + PARA");
a("Ctrl-Left", "AdjRight - PARA");
a("Ctrl-Right", "AdjLeft + PARA");
a("Alt-Left", "Hop - PARA");
a("Alt-Right", "Hop + PARA");
a("INSERT", "Insert");
a("Tab", "Mode 0 CHAR", nchar);
*/
curAct = nchar;
a("MouseClicked1", "MouseSetCursor 0 CHAR");
a("Left", "Cursor - CHAR");
a("Right", "Cursor + CHAR");
a("Backspace", "BackSpace 0 CHAR");
a("Delete", "Delete 0 CHAR");
a("Alt-Backspace", "Delete 0 CHAR");
a("INSERT", "Insert");
a("Tab", "Mode 0 WORD", nword);
a("Enter", "BreakParagraph 0 CHAR");
a("Ctrl-C", "PUICopy 0 CHAR");
a("Up", "Cursor - WORD");
a("Down", "Cursor + WORD");
}
static String[][] actions = {
{ "MouseClicked1", "Nile1.MouseSetCursor" },
{ "Left", "Nile1.Cursor -" },
{ "Right", "Nile1.Cursor +" },
{ "Shift-Left", "Nile1.AdjLeft -" },
{ "Shift-Right", "Nile1.AdjRight +" },
{ "Ctrl-Left", "Nile1.AdjRight -" },
{ "Ctrl-Right", "Nile1.AdjLeft +" },
{ "Alt-Left", "Nile1.Hop -" },
{ "Alt-Right", "Nile1.Hop +" },
{ "INSERT", "Nile1.Insert" }
};
}
// vim: set syntax=java :

View File

@@ -0,0 +1,71 @@
/*
Nile1Div.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 representation of a particular way of dividing up the nile stream.
* For instance, words, sentences, clauses, paragraphs or user-specified
* split points could be represented this way.
* It is allowed to have an non-zero-width break between the divisions.
* <p>
* It is assumed that no elements will be zero-width.
*/
public interface Nile1Div {
String rcsid = "$Id: Nile1Div.java,v 1.1 2000/11/20 17:36:13 tjl Exp $";
/** Find the next break.
* To iterate, over breaks, pass the start2 returned
* from the previous iteration
* as start to the next and set includeThis to false.
* <p>
* An important property here is that the stream may be cut
* at any location returned in end2.
*
* @param start The location to start from
* @param dir The direction to move into, 1 or -1.
* @param includeThis Whether to exclude the break where
* @param start2 The cursor to store the start of the next break in.
* @param end2 The cursor to store the end of the next break in.
* @return true if next break found.
*/
boolean findBreak(ZZCursor start,
int dir, boolean includeThis,
ZZCursor start2, ZZCursor end2);
/** Cut the stream at a boundary specified
* by this division.
* @param start An end of a break. This MUST have been returned by
* findBreak as start2 or end2.
*/
/** Splice two pieces of the stream, so that between them a division
* of this level is maintained.
*/
ZZCell splice(ZZCell p1, ZZCell p2);
}

View File

@@ -0,0 +1,81 @@
/*
Nile1GenTreeModel.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.*;
/** The nile1 structural model for GenTreePart.
* This implements the nile1 structural model from H1 to P levels as a
* tree using GenTreePart.
* @see GenTreePart
*/
public class Nile1GenTreeModel implements GenTreePartModel {
public static final String rcsid = "$Id: Nile1GenTreeModel.java,v 1.3 2000/11/22 00:35:41 tjl Exp $";
public static boolean dbg = true;
final static void p(String s) { if(dbg) System.out.println(s); }
final static void pa(String s) { System.out.println(s); }
public int depth() {
return 7;
}
public int depth(ZZCell c) {
if(c.s("d.nile-struct", -1) == null) return -1;
return Nile1Ops.getLevel(c);
}
public ZZCell getNext(int mindepth, ZZCell c, int steps) {
if(steps == 0) return c;
c = c.s("d.nile-struct", -1);
int dir = (steps > 0 ? 1 : -1);
ZZCell lasthdr = null;
while(steps != 0 && c != null) {
c = c.s("d.nile", dir);
if(c == null) return null;
lasthdr = c.s("d.nile-struct", 1);
if(lasthdr == null) continue;
if(depth(lasthdr) < mindepth) continue;
steps -= dir;
}
if(c == null) return null;
return lasthdr;
}
public void disconnectNeg(ZZCell c) {
p("Model: disconnectneg "+c);
c.s("d.nile-struct", -1).disconnect("d.nile", -1);
}
public void connect(ZZCell c, ZZCell d) {
p("Model: connect "+c+" "+d);
c.s("d.nile-struct", -1).h("d.nile", 1).connect("d.nile",
d.s("d.nile-struct", -1));
}
public void setDepth(ZZCell c, int d) {
if(depth(c) < 0) return;
Nile1Ops.setLevel(c, d);
}
}

479
Modules/nile/Nile1Ops.java Normal file
View File

@@ -0,0 +1,479 @@
/*
Nile1Ops.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 Nile1 streams.
* @see Nile1
* @deprecated Use Nile2Ops.
*/
public abstract class Nile1Ops {
public static final String rcsid = "$Id: Nile1Ops.java,v 1.16 2000/12/07 00:37:19 tjl Exp $";
public static boolean dbg = true;
static void p(String s) { if(dbg) System.out.println(s); }
static void pa(String s) { System.out.println(s); }
/** Split the given cell into two at the offset offset.
* The original cell will remain the first.
* Returns the cell that is after the split.
* If offset is zero or greater than 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 c The cursor to split at
* @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.
* @return Never null.
*/
static public ZZCell split(ZZCursor c, boolean always) {
// XXX Adjust cursors!!!
ZZCell c0 = c.get();
int offs = c.getOffs();
if(always) {
ZZCell c1 = c0.N("d.nile");
String s = c0.getText();
c0.setText(s.substring(0, offs));
c1.setText(s.substring(offs));
return c1;
} else {
if(offs == 0) return c0;
if(offs == c0.getText().length())
return c0.getOrNewCell("d.nile");
return split(c, true);
}
}
/** Break a paragraph at the given position.
* Returns the first cell of the (possibly empty) new paragraph.
*/
static public ZZCell breakParagraph(ZZCursor c) {
ZZCell n = split(c, true);
if(n.s("d.nile-struct") != null)
throw new ZZError("split structure already!");
ZZCell str = n.N("d.nile-struct");
setStrLevel(str, 0);
return n;
}
/** Change the structural level of a paragraph.
* @param c Any cell in the paragraph - does not need to be the first.
* @param level The new level.
*/
static public void setLevel(ZZCell c, int level) {
ZZCell para = findStruct(c, -1, true);
ZZCell str = para.s("d.nile-struct");
if(str == null) return;
setStrLevel(str, level);
}
/** Get the level of a paragraph.
* @param c Any cell in the paragraph - does not need to be the first.
*/
static public int getLevel(ZZCell c) {
ZZCell para = findStruct(c, -1, true);
ZZCell str = para.s("d.nile-struct");
if(str == null) return 6;
return getStrLevel(str);
}
/** Insert the given string into the stream at cursor.
*/
static public void insert(ZZCursor c, String txt) {
throw new ZZError("Not implemented");
}
/** The level of structure the given structure cell is.
* Note that the levels are a bit interesting: it returns
* zero for paragraphs, 1 for H6, 2 for H5 and so on.
* -1 is returned for cells that are not at a structural border.
*/
static int getStrLevel(ZZCell str) {
String txt = str.getText();
if(txt.equals("MP")) // Middle of paragraph - special level.
return -1;
if(txt.equals("P"))
return 0;
if(txt.equals("H1"))
return 6;
if(txt.equals("H2"))
return 5;
if(txt.equals("H3"))
return 4;
if(txt.equals("H4"))
return 3;
if(txt.equals("H5"))
return 2;
if(txt.equals("H6"))
return 1;
throw new ZZError("Invalid nile-struct "+txt);
}
static String[] levels = new String[] {
"P", "H6", "H5", "H4", "H3", "H2", "H1"
};
/** Set the level of a structural cell.
*/
static void setStrLevel(ZZCell str, int level) {
if(level < 0 || level > levels.length)
throw new ZZError("Request to set invalid level! "+level);
str.setText(levels[level]);
}
/** Find an angle in the rank, i.e.the next cell on a rank with
* a connection into a certain direction.
*/
static ZZCell findAngle(ZZCell c, String dim, int dir,
String adim, int adir, boolean includeThis) {
LoopDetector ld = new LoopDetector();
if(includeThis && c.s(adim, adir) != null) return c;
c = c.s(dim, dir);
while(c != null) {
if(c.s(adim, adir) != null) return c;
ld.detect(c);
c = c.s(dim, dir);
}
return null;
}
/** Find the next or previous cell that is a structural level element.
*/
static ZZCell findStruct(ZZCell c, int dir, boolean includeThis) {
ZZCell ret = findAngle(c, "d.nile", dir, "d.nile-struct", 1, includeThis);
if(ret == null && dir == -1) {
if(!includeThis && c.s("d.nile", -1) == null)
return null;
return c.h("d.nile", -1);
}
return ret;
}
/** An editing mode, i.e.the definition of what are the breaks
* that are being operated on in this mode.
*/
public interface Mode {
/** Set the mode cursor, and possibly selection cursor, to the
* given cursor.
* This is called in response to e.g. mouse click, so the position
* may be adjusted to the beginning of the previous unit.
*/
void setCursor(ZZCell view, ZZCursor c);
/** Move the cursor nsteps units.
*/
void moveCursor(ZZCell view, int nsteps);
/** Extend/shrink the selection by n units in given direction.
*/
void adjust(ZZCell view, int end, int nsteps);
/** Hop the selection.
*/
void hop(ZZCell view, int nsteps);
/** Delete the current selection, move cursor backwards or forwerds.
*/
// void delete(ZZCell view, int movedir);
}
public static class EdgeMode implements Mode {
Nile1Text.Edger edger;
EdgeMode(Nile1Text.Edger edger) { this.edger = edger; }
public void setCursor(ZZCell view, ZZCursor c) {
Stream.Iterator iter = giter(c);
edger.edge(iter, true, -1, Nile1Text.START);
curs(view).set(iter.get());
edger.edge(iter, false, 1, Nile1Text.END);
endcurs(view).set(iter.get());
}
// Move from end of area if forwards, from start if backwards.
public void moveCursor(ZZCell view, int nsteps) {
if(nsteps <= 0) {
Stream.Iterator iter = giter(curs(view));
if(!edger.edge(iter, false, nsteps, Nile1Text.START))
return;
curs(view).set(iter.get());
edger.edge(iter, false, 1, Nile1Text.END);
endcurs(view).set(iter.get());
} else {
Stream.Iterator iter = giter(endcurs(view));
if(!edger.edge(iter, false, nsteps, Nile1Text.END))
return;
endcurs(view).set(iter.get());
edger.edge(iter, false, -1, Nile1Text.START);
curs(view).set(iter.get());
}
}
public void adjust(ZZCell view, int end, int nsteps) {
ZZCursor c1 = curs(view);
ZZCursor c2 = endcurs(view);
if(end < 0) {
Stream.Iterator iter = Stream.getIterator(c1, "d.nile");
if(!edger.edge(iter, false, nsteps, Nile1Text.START))
return;
ZZCursor curs = iter.get();
if(!isOrdered(curs, c2)) return;
c1.set(curs);
} else {
Stream.Iterator iter = Stream.getIterator(c2, "d.nile");
if(!edger.edge(iter, false, nsteps, Nile1Text.END))
return;
ZZCursor curs = iter.get();
if(!isOrdered(c1, curs)) return;
c2.set(curs);
}
// XXX Check that they do not cross...
}
public void hop(ZZCell view, int nsteps) {
p("Cut out the selection for hop");
ZZCell selec = cutSelection(view);
if(selec == null) return;
ZZCursor c1 = curs(view);
p("Now, find place to paste at hopped location");
Stream.Iterator iter = Stream.getIterator(c1, "d.nile");
edger.edge(iter, false, nsteps, Nile1Text.START);
c1.set(iter.get());
p("And paste");
paste(view, selec, true);
}
}
static Mode wordmode = new EdgeMode(new Nile1Text.WordEdger());
static Mode sentencemode = new EdgeMode(new Nile1Text.SentenceEdger());
static public Mode getMode(ZZCell view) {
// return wordmode;
return sentencemode;
}
// XXX
static ZZCell adjStart(ZZCell c, int adj) {
if(adj > 0) {
// XXX Easily screws up font change!
ZZCursor c1 = new ZZCursorVirtual(c, 0);
if(!Stream.move(c1, "d.nile", adj))
throw new ZZError("ARRRRGH!");
if(c1.get().equals(c)) {
c.setText(c.getText().substring(adj));
} else {
c = split(c1, false);
c.disconnect("d.nile", -1);
}
} else if(adj < 0) {
// XXX Spans
String s = "";
while(adj++ < 0) s = s + " "; // XXX
ZZCell n = c.N("d.nile", -1);
n.setText(s);
}
return c;
}
static ZZCell adjEnd(ZZCell c, int adj) {
if(adj > 0) {
ZZCursor c1 = new ZZCursorVirtual(c, c.getText().length());
if(!Stream.move(c1, "d.nile", -adj))
throw new ZZError("ARRRRGH!");
// XXX
c = c1.get();
c.setText(c.getText().substring(0, c1.getOffs()));
c.disconnect("d.nile", 1);
} else if(adj < 0) {
c.setText(c.getText()+" ");
}
return c;
}
static void adjustSelection(Stream.Iterator start, Stream.Iterator end) {
Nile1Text.wordEdge(start, true, 1, Nile1Text.START);
Nile1Text.wordEdge(end, true, -1, Nile1Text.END);
}
static ZZCell cutSelection(ZZCell view) {
ZZCursor curs1 = curs(view);
p("Cut");
dump(curs1.get());
ZZCursor curs2 = endcurs(view);
if(!isOrdered(curs1, curs2)) return null;
Stream.Iterator iter1 = Stream.getIterator(curs1, "d.nile");
Stream.Iterator iter2 = Stream.getIterator(curs2, "d.nile");
int[] adj = new int[2];
Nile1Text.adjustCut(iter1, iter2, adj);
ZZCursor c1 = iter1.get();
ZZCursor c2 = iter2.get();
ZZCell after = split(c2, false);
p("\nAFTER SPLIT:");
dump(after);
ZZCell first = split(c1, false);
p("\nFIRST SPLIT:");
dump(first);
ZZCell prev = first.s("d.nile", -1);
ZZCell last = after.s("d.nile", -1);
after.disconnect("d.nile", -1);
first.disconnect("d.nile", -1);
if(prev != null) prev.connect("d.nile", after);
p("AFTER RECONNECT");
dump(prev);
curs(view).set(after);
curs(view).setOffs(0);
// Now, if more characters need to be left out, do it.
first = adjStart(first, adj[0]);
last = adjEnd(last, adj[1]);
if(adj[1] != 0) throw new ZZError("End adj not implemented");
p("AFTER CUTADJ");
dump(first);
return first;
}
/** Paste the given stream into the given cursor.
* The range and the text are adjusted for word-by-word
* insertion.
* @param view The viewcell that gives the location to
* paste into. Used later to set the new range,
* if desired.
* @param what The first cell of the stream to paste in.
* @param setRange Whether to set the selection of the given
* view to the pasted range afterwards.
*/
static void paste(ZZCell view, ZZCell what, boolean setRange) {
ZZCursor v = curs(view);
p("PASTE");
dump(v.get());
p("PASTING WHAT");
dump(what);
int[] adj = new int[2];
Stream.Iterator to = Stream.getIterator(v, "d.nile");
Stream.Iterator start = giter(new ZZCursorVirtual(what, 0));
ZZCell last = what.h("d.nile", 1);
Stream.Iterator end = giter(new ZZCursorVirtual(last,
last.getText().length()));
Nile1Text.adjustPaste(to, start, end, adj);
what = adjStart(start.get().get(), adj[0]);
adjEnd(end.get().get(), adj[1]);
p("WHAT ADJUSTED");
dump(what);
ZZCell after = split(v, false);
ZZCell before = after.s("d.nile", -1);
after.disconnect("d.nile", -1);
after.connect("d.nile", -1, what.h("d.nile", 1));
if(before != null)
before.connect("d.nile", 1, what);
if(setRange) {
v.set(what);
v.setOffs(0);
start = giter(v);
ZZCursor ev = endcurs(view);
ev.set(after);
ev.setOffs(0);
end = giter(ev);
adjustSelection(start, end);
v.set(start.get());
ev.set(end.get());
}
p("AFTER EVERYTHING");
dump(what);
}
// XXX Should use the tree for help...
static public boolean isOrdered(ZZCursor cu1, ZZCursor cu2) {
ZZCell c1 = cu1.get();
ZZCell c2 = cu2.get();
if(c1 == null || c2 == null) return false;
if(c1.equals(c2))
return cu1.getOffs() <= cu2.getOffs();
for(; c1 != null && !c1.equals(c2); c1 = c1.s("d.nile"));
if(c1 == null) return false;
return (c1.equals(c2));
}
// Shorthand...
static public ZZCursorReal curs(ZZCell view) {
return new ZZCursorReal(view);
}
static public Stream.Iterator giter(ZZCursor c) {
return Stream.getIterator(c, "d.nile");
}
static public ZZCursorReal endcurs(ZZCell view) {
ZZCell end = view.getOrNewCell("d.nile-sel");
return new ZZCursorReal(end);
}
static public void dump(ZZCell start) {
if(start == null) {
pa("Nile stream Null start!");
return;
}
ZZCell init = start.h("d.nile", -1);
pa("Nile stream! "+start);
boolean first = true;
for(ZZCell c = init; c != null; c=c.s("d.nile")) {
pa((c.s("d.nile-struct") != null ? "***S*** " : " ") +
" Cell "+c+" "+
(c.equals(start) ? " <----------------START" : ""));
if(!first && c.equals(init)) {
pa(" WAS A LOOOOOOOP!!!!!!!!!!");
throw new ZZFatalError("LOOPING NILE STREAM! BAD BAD TROUBLE!");
}
first = false;
}
}
}

356
Modules/nile/Nile1Text.java Normal file
View File

@@ -0,0 +1,356 @@
/*
Nile1Text.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 streams with Nile1 text model.
* @see Nile1
* @deprecated Use Nile2Unit
*/
public abstract class Nile1Text {
public static final String rcsid = "$Id: Nile1Text.java,v 1.9 2000/12/07 00:37:19 tjl Exp $";
public static boolean dbg = true;
static void p(String s) { if(dbg) System.out.println(s); }
static void pa(String s) { System.out.println(s); }
// Stream.Iterator iter = Stream.getIterator(start, "d.nile");
static public int START = 1;
static public int END = -1;
static public int ANY = 0;
static boolean isWordBreak(Stream.Iterator iter, int edge) {
char cur = iter.getChar();
if(cur == -1) return true;
if(!iter.move(-1)) return true;
char prev = iter.getChar();
iter.move(1);
return isWordBreak(prev, cur, edge);
}
static boolean isWordBreak(char prev, char cur, int edge) {
boolean ret = isWordBreak0(prev, cur, edge);
p("IsWordBreak: '"+prev+"' '"+cur+"' "+edge+ " RETURNING: "+ret);
return ret;
}
static boolean isWordBreak0(char prev, char cur, int edge) {
boolean curlet = Character.isLetterOrDigit(cur);
boolean curws = Character.isWhitespace(cur);
boolean prevlet = Character.isLetterOrDigit(prev);
boolean prevws = Character.isWhitespace(prev);
if(!curlet && !curws) {
if(edge != END) return true; // punctuation -> break start
return prevlet || !prevws; // punct -> br end if prev ltr or punct
}
if(prevlet && curlet) return false;
if(prevlet && curws) return edge != START;
// --> prevlet == false
if(!prevws) { // previous was punctuation ->
if(edge != START) return true;
return curlet; // Starting edge only if this is a letter.
}
if(prevws && curws) return false;
if(prevws && curlet) return edge != END;
// shouldn't get here
throw new ZZError("isWordBreak logic error!");
}
/** Find the nsteps:th next wordbreak.
* Always alters iter.
* <p>
* Characters are split into three categories:
* letter-or-digit, whitespace and other.
* whitespace is always a word boundary (but there is no
* word boundary between two whitespace characters).
* The characters in the other category are words by themselves.
* So basically there is a word break at character c, if
* <ul>
* <li> c is the first character in the stream.
* <li> c is one beyond the last character in the stream
* <li> c is punctuation
* <li> c is of a different category (see above) than
* the character just before it.
* </ul>
* @param nsteps The number of steps, a positive or negative number.
* @param edge Which edge of the word do we want: START, END or ANY.
*/
static public boolean wordEdge(Stream.Iterator iter,
boolean includeThis, int nsteps,
int edge) {
if(nsteps == 0) throw new ZZError("Can't step 0 words");
int dir = (nsteps > 0 ? 1 : -1);
for(; nsteps != 0; nsteps -= dir) {
if(!wordEdge0(iter, includeThis, dir, edge))
return false;
includeThis = false;
}
return true;
}
static private boolean wordEdge0(Stream.Iterator iter,
boolean includeThis, int dir,
int edge) {
char prev = 0;
char cur = iter.getChar();
if(dir > 0) {
if(includeThis) {
if(!iter.move(-dir)) return true;
prev = iter.getChar();
iter.move(dir);
if(isWordBreak(prev, cur, edge)) return true;
}
while(iter.move(dir)) {
prev = cur;
cur = iter.getChar();
if(cur == -1) return true; // Ran into end of text.
if(isWordBreak(prev, cur, edge)) return true;
}
return (edge != START);
} else {
if(!includeThis) {
if(!iter.move(dir)) return false;
cur = iter.getChar();
}
while(iter.move(dir)) {
prev = cur;
cur = iter.getChar();
if(isWordBreak(cur, prev, edge)) {
iter.move(-dir);
return true;
}
}
return (edge != END);
}
}
static public boolean sentenceEdge(Stream.Iterator iter,
boolean includeThis, int nsteps, int edge) {
if(nsteps == 0) throw new ZZError("Can't step 0 words");
int dir = (nsteps > 0 ? 1 : -1);
for(; nsteps != 0; nsteps -= dir) {
if(!sentenceEdge0(iter, includeThis, dir, edge))
return false;
includeThis = false;
}
return true;
}
static public boolean sentenceEdge0(Stream.Iterator iter,
boolean includeThis, int dir, int edge) {
if(edge == 0)
throw new ZZError("Looking for either sentence edge not implemented");
if(edge == START) {
// If we cannot move, we're at the beginning.
if(!iter.move(-1))
if(dir < 0 || includeThis) return includeThis;
// Back out
iter.move(1);
// Back up two, one by one, and then search non-inclusive.
iter.move(-1);
iter.move(-1);
if(iter.find(dir, includeThis, " ")) {
iter.move(2);
return true;
} else {
// There is a start in the beginning, but not at the end.
iter.end(dir);
return (dir < 0);
}
} else {
if(!iter.move(1)) return includeThis;
iter.move(-1);
// Here we just find because end is at the found text
if(iter.find(dir, includeThis, " "))
return true;
else {
iter.end(dir);
// There is an end in the end, but not at the beginning.
return (dir > 0);
}
}
}
static boolean ws(char c) { return Character.isWhitespace(c); }
static boolean punct(char c) {
return (!Character.isWhitespace(c)) && (!Character.isLetterOrDigit(c));
}
static boolean lett(char c) { return Character.isLetterOrDigit(c); }
/** Adjust the locations to cut text from.
* Basically, cut one or two spaces after the latter location,
* and if none, and there is a single space preceding,
* cut it along.
* <p>
* @param leaveout An output parameter: the values in the two-member
* array are set to the number of characters
* to leave out of the cut text.
* An important meaning is given to negative
* values: a negative value means "add this many
* spaces".
*/
static public void adjustCut(Stream.Iterator iter1,
Stream.Iterator iter2,
int[] leaveout) {
leaveout[0] = leaveout[1] = 0;
// First off, do not adjust if one of them is not word end.
if(!isWordBreak(iter1, START) || !(isWordBreak(iter2, END))) return;
char c1 = iter2.getChar(0);
char c2 = iter2.getChar(1);
// If there are one or two spaces afterwards, cut them along.
if(ws(c1)) {
iter2.move(1);
if(ws(c2))
iter2.move(1);
} else {
// If there is one character before...
char b1 = iter1.getChar(-1);
char b2 = iter1.getChar(-2);
if(ws(b1) && !ws(b2)) {
// This is a tricky situation. What we have here is
// cutting off in
// word WORD, word
// without the comma.
// The space before WORD needs to be removed.
// This is what the leaveout means.
//
// Additionally, for pasting at the correct boundary
// to work right, we need to add a space *after* this
// word when pasting. See adjustPaste.
iter1.move(-1);
leaveout[0] = 1;
}
}
}
/** The adjustment to a paste operation.
* @param adjust Analogour to the leaveout parameter of
* adjustCut. Positive values mean
* leaving this many characters out
* and negative values mean
* adding that many spaces.
*/
static public void adjustPaste(Stream.Iterator to,
Stream.Iterator start,
Stream.Iterator end,
int[] adjust) {
adjust[0] = adjust[1] = 0;
if(!isWordBreak(to, START)) return;
// If to is at a letter-punct edge, make sure
// pasted text starts with space and does not end with one.
// Otherwise, make sure it ends with space but doesn't start
// with it.
char c1 = to.getChar(-1);
char c2 = to.getChar(0);
if(lett(c1) && punct(c2)) {
// Now, start but don't end with space.
if(!ws(start.getChar(0))) adjust[0] = -1;
if(ws(end.getChar(-1))) adjust[1] = 1;
} else {
if(ws(start.getChar(0))) adjust[0] = 1;
if(!ws(end.getChar(-1))) adjust[1] = -1;
}
}
static public boolean paragraphEdge(Stream.Iterator iter,
boolean includeThis, int nsteps, int edge) {
if(nsteps == 0) throw new ZZError("Can't step 0 paragraphs");
int dir = (nsteps > 0 ? 1 : -1);
for(; nsteps != 0; nsteps -= dir) {
if(!paragraphEdge0(iter, includeThis, dir, edge))
return false;
includeThis = false;
}
return true;
}
static public boolean paragraphEdge0(Stream.Iterator iter,
boolean includeThis, int dir, int edge) {
ZZCell c = iter.get().get();
int offs = iter.get().getOffs();
boolean beg = (offs == 0);
boolean end = (offs == c.getText().length());
if(edge == 0)
throw new ZZError("Can't look for paragraph end / begin");
// XXX Should we set the end edge to point to negative
// direction?
// if(edge == START) {
if(dir > 0) {
if(!beg || !includeThis) c = c.s("d.nile");
} else {
if(beg && !includeThis) c = c.s("d.nile", -1);
}
// Now we can go on, using includeThis always.
if(c == null) return false;
c = Nile1Ops.findStruct(c, dir, true);
if(c != null) {
iter.set(new ZZCursorVirtual(c, 0));
if(edge == START)
iter.move(0, 1);
else
iter.move(0, -1);
return true;
} else {
if(edge == END && dir > 0) {
iter.end(); return true;
}
return false;
}
// }
}
public interface Edger {
boolean edge(Stream.Iterator iter, boolean includeThis, int nsteps,
int whichEdge);
}
static public class WordEdger implements Edger {
public boolean edge(Stream.Iterator iter, boolean includeThis,
int nsteps, int whichEdge) {
return wordEdge(iter, includeThis, nsteps, whichEdge);
}
}
static public class SentenceEdger implements Edger {
public boolean edge(Stream.Iterator iter, boolean includeThis,
int nsteps, int whichEdge) {
return sentenceEdge(iter, includeThis, nsteps, whichEdge);
}
}
static public class ParagraphEdger implements Edger {
public boolean edge(Stream.Iterator iter, boolean includeThis,
int nsteps, int whichEdge) {
return paragraphEdge(iter, includeThis, nsteps, whichEdge);
}
}
}

311
Modules/nile/Nile1View.zob Normal file
View File

@@ -0,0 +1,311 @@
/*
Nile1View.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.*;
import java.awt.*;
import java.io.*;
/** A view for nile streams.
*/
public class Nile1View implements FlobView, ZOb {
public static final String rcsid = "$Id: Nile1View.zob,v 1.4 2001/01/26 13:53:27 ajk 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); }
STRUCTPARAMS {
/** The default font to use.
*/
Font font
= new Font("SansSerif", Font.PLAIN, 11);
/** The distance between paragraphs.
*/
int paraskip
= 8;
/** The left-side margin for normal text.
* Headings hang left of this margin.
*/
int lmargin
= 50;
ZOb ftextlayouter
= new FTextLayouter();
/** Whether to use a special color for span (transcluded) cells. */
boolean showcopy
= false;
}
/** Bold and italic derivatives of the font given.
* The first index is the level of the text i.e.
* from paragraph to H1.
*/
Font[][][] bifonts;
FontMetrics[][][] bifontms;
INITIALIZE {
bifonts = new Font[7][2][2];
bifontms = new FontMetrics[7][2][2];
for(int l = 0; l < bifonts.length; l++) {
int size = font.getSize();
if(l > 0 && l < 5) size += l-1;
if(l == 5) size += 6;
if(l == 6) size += 9;
bifonts[l][1][0] = new Font(font.getName(),
Font.BOLD, size);
bifonts[l][1][1] = new Font(font.getName(),
Font.BOLD | Font.ITALIC, size);
if(l == 0) {
bifonts[l][0][0] = new Font(font.getName(), 0, size);
bifonts[l][0][1] = new Font(font.getName(),
Font.ITALIC, size);
} else { // All bold for headers
bifonts[l][0][0] = bifonts[l][1][0];
bifonts[l][0][1] = bifonts[l][1][1];
}
for(int b = 0; b<2; b++)
for(int i = 0; i<2; i++)
bifontms[l][b][i] = FONTMETRICS(bifonts[l][b][i]);
}
}
Color selcolor = Color.white;
Color copycolor = Color.yellow.darker().darker().darker().darker();
/** Return the formatted text object for one paragraph.
*/
FText paragraph(ZZCell start, ZZCell endBefore, ZZCursor offsetOfC,
int[]into,
ZZCursor selectEnd)
{
p("Paragraph! "+start+" "+endBefore+" ");
ZZCell selsc = null;
int selso = 0;
if(offsetOfC != null) {
selsc = offsetOfC.get();
selso = offsetOfC.getOffs();
}
ZZCell offsetOf = selsc;
ZZCell selec = null;
int seleo = 0;
if(selectEnd != null) {
selec = selectEnd.get();
seleo = selectEnd.getOffs();
}
boolean selected = (selsc == null && selec != null);
if(selso == ZZCursor.NO_OFFSET || selec == null) {
selsc = null;
selec = null;
selected = false;
}
FText.Part part = null;
int level = Nile2Iter.getLevel(start);
int curOffs = 0;
int b = 0, i = 0;
Vector parts = new Vector();
LoopDetector ld = new LoopDetector();
LoopDetector ld2 = new LoopDetector();
for(ZZCell c = start; c != null && c != endBefore; c = c.s("d.nile")) {
ld.detect(c);
for(ZZCell fs = c.s("d.nile-fmt-start"); fs != null;
fs = fs.s("d.nile-fmt-start")) {
ld.detect(fs);
if(fs.getText().equals("B")) b++;
else if(fs.getText().equals("I")) i++;
else p("Invalid format: "+fs.getText());
}
int b0 = (b > 0 ? 1 : 0);
int i0 = (i > 0 ? 1 : 0);
int len = c.getText().length();
if(c == offsetOf && into != null) into[0] = curOffs;
int internoffs = 0;
if(c == selsc) {
if(selso > len) {
ZZLogger.log("Warning: start over length "+selso+" "+len);
selso = len;
}
part = new FText.CellPart( c, 0, selso,
bifonts[level][b0][i0], bifontms[level][b0][i0],
null, c.getSpan() == null ? null : copycolor);
parts.addElement(part);
internoffs = selso;
selected = true;
p("Start selection: "+selso+" "+selsc);
}
if(c == selec && seleo >= internoffs) {
if(seleo > len) {
ZZLogger.log("Warning: end over length "+seleo+" "+len);
seleo = len;
}
part = new FText.CellPart( c, internoffs, seleo-internoffs,
bifonts[level][b0][i0], bifontms[level][b0][i0],
selcolor, c.getSpan() == null ? null : copycolor);
parts.addElement(part);
internoffs = seleo;
selected = false;
selectEnd.set((ZZCell)null);
p("End selection: "+seleo+" "+selec);
}
part = new FText.CellPart( c, internoffs, len-internoffs,
bifonts[level][b0][i0], bifontms[level][b0][i0],
selected ? selcolor : null,
c.getSpan() == null ? null : copycolor);
parts.addElement(part);
curOffs += len;
for(ZZCell fe = c.s("d.nile-fmt-end"); fe != null;
fe = fe.s("d.nile-fmt-start")) {
ld2.detect(fe); // Can't use same: same cell is start&end
if(fe.getText().equals("B")) b--;
else if(fe.getText().equals("I")) i--;
else p("Invalid format: "+fe.getText());
}
}
return new FText(parts);
}
Rectangle pmarg(Rectangle r, ZZCell para) {
int level = Nile2Iter.getLevel(para);
Rectangle ret = new Rectangle(r);
int w = (6 - level) * lmargin / 6;
ret.x += w; ret.width -= w;
return ret;
}
boolean[] beginningSentence = new boolean[2];
public Rectangle placeParagraph(FlobSet into,
FText ft, ZZCell para,Rectangle origrect,
int direction, int initY, int middleOffs) {
beginningSentence[0] = true;
beginningSentence[1] = false;
// Margins.
Rectangle rect = pmarg(origrect, para);
Rectangle ret = ((FTextLayouter)ftextlayouter).place(into, ft, rect,
1,
direction, initY, middleOffs, false,
beginningSentence);
// Now, do the cool thing with the section headings: include
// previous and next section headings of the same level here.
if(Nile2Iter.getLevel(para) > 0) {
ZZCell strcell = para.s("d.nile-struct");
if(strcell == null) return ret;
for(int dir = -1; dir <= 1; dir += 2) {
ZZCell prevsec = strcell.s("nile1str:breadth",dir);
if(prevsec == null) continue;
prevsec = prevsec.h("nile1str:depth").s("d.nile-struct", -1);
if(prevsec == null) continue;
beginningSentence[0] = true;
beginningSentence[1] = false;
FText nft = paragraph(prevsec, Nile2Iter.findStruct(prevsec, 1, false),
null, null, null);
((FTextLayouter)ftextlayouter).place(into, nft, rect, 200,
dir, ret.y + (dir < 0 ? 0 : ret.height),
0, false, beginningSentence);
}
}
return ret;
}
public void raster(FlobSet into, FlobFactory fact,
ZZCell view, String[] dims, ZZCell accursed) {
int[] offs = new int[1];
LoopDetector ld = new LoopDetector();
ZZCursor selectEnd = null;
ZZCell sele = view.s("d.nile-sel");
if(sele != null) {
selectEnd = ZZCursorVirtual.createFromReal(sele);
}
// Do the center paragraph first.
ZZCell firstpara = Nile2Iter.findStruct(accursed, -1, true);
ZZCell nextpara = Nile2Iter.findStruct(accursed, 1, false);
FText ft = paragraph(firstpara, nextpara, new ZZCursorReal(view),
offs, selectEnd);
Dimension size = into.getSize();
Rectangle rect = new Rectangle(0, 0, size.width, size.height);
Rectangle ctr = placeParagraph(into, ft, firstpara, rect,
0, rect.y+rect.height/2,
ZZCursorReal.getVisualTextOffset(view));
// Go into positive direction.
Rectangle r = ctr;
while(nextpara != null) {
ld.detect(nextpara);
ZZCell next = Nile2Iter.findStruct(nextpara, 1, false);
ft = paragraph(nextpara, next, null, null, selectEnd);
r = placeParagraph(into, ft, nextpara, rect,
1, r.y+r.height + paraskip, 0);
nextpara = next;
if(r.y + r.height > size.height) break;
}
// And negative.
r = ctr;
while(true) {
ZZCell prev = firstpara;
firstpara = Nile2Iter.findStruct(firstpara, -1, false);
if(firstpara == null) break;
ld.detect(firstpara);
ft = paragraph(firstpara, prev, null, null, null);
beginningSentence[0] = true;
r = placeParagraph(into, ft, firstpara, rect,
-1, r.y - paraskip, 0);
if(r.y < 0) break;
}
// into.dump();
Flob cursf = into.findFlob(null, accursed);
// p("Cursf: "+cursf);
if(cursf != null)
((SplitCellFlob1)cursf).addCurs(into, ZZCursorReal.getOffs(view),
Color.red);
}
}
// vim: set syntax=java :

589
Modules/nile/Nile2Iter.java Normal file
View File

@@ -0,0 +1,589 @@
/*
Nile2Iter.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.*;
/** 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.
*/
public final class Nile2Iter implements Cloneable {
public static final String rcsid = "$Id: Nile2Iter.java,v 1.8 2000/12/20 22:26:35 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); }
private ZZCell c;
private int offs;
// Cache these for speed.
private String ctext;
private int clen;
private void load(ZZCell c) {
this.c = c;
ctext = c.getText();
clen = ctext.length();
}
private void load(ZZCell c, int offs) {
load(c);
if(offs == -1) this.offs = clen;
else this.offs = offs;
move(0);
}
private void reload() {
load(c);
move(0);
}
static private boolean isStruct(ZZCell c) {
if(c == null)
throw new ZZError("Null isStruct?");
return c.s("d.nile-struct") != null;
}
Nile2Iter() { }
public Nile2Iter(ZZCursor curs) { set(curs); }
public void set(ZZCursor curs) {
load(curs.get());
this.offs = curs.getOffs();
// Ensure positive bias.
move(0);
}
public void set(Nile2Iter iter) {
load(iter.c);
offs = iter.offs;
}
/** Move n characters forwards or backwards within the current
* paragraph.
* If the offset given is out of bounds, moves as far as possible
* and returns false, otherwise returns true.
*/
public boolean move(int n) {
n += offs;
offs = 0;
// Internally, we like to maintain positive bias always.
// Thus, the greater-OR-EQUALS sign.
while(n >= clen) {
n -= clen;
ZZCell nc = c.s("d.nile");
if(nc == null || isStruct(nc)) {
// Only situation for negative bias: end of paragraph.
offs = clen;
if(n > 0) return false;
return true;
}
load(nc);
}
while(n < 0) {
ZZCell nc = c.s("d.nile", -1);
if(nc == null || isStruct(nc)) {
offs = 0;
return false;
}
load(nc);
n += clen;
}
offs = n;
return true;
}
/** Get the character at current offset.
* Returns -1 if out of bounds.
*/
public int cur() {
if(offs >= clen || offs < 0) return -1;
return ctext.charAt(offs);
}
public int get() { return cur(); }
public int next() {
if(!move(1)) return -1;
return cur();
}
public int prev() {
if(!move(-1)) return -1;
return cur();
}
public int get(int at) {
ZZCell cache = c;
int offscache = offs;
int ret = -1;
if(move(at)) {
ret = cur();
}
if(c != cache)
load(cache);
offs = offscache;
return ret;
}
/** Move n paragraphs, moving as little as possible.
* This means that if we move backwards, we will be at the end
* of the paragraph and vice versa.
*/
public boolean shiftPara(int n) {
// XXX What about empty paragraphs? A LOT of trouble...
if(n == 0) return true;
int dir = (n > 0 ? 1 : -1);
ZZCell nc = c;
while(n != 0) {
nc = nc.s("d.nile", dir);
if(nc == null) return false;
if(isStruct(nc)) n -= dir;
}
// Now we are at the paragraph cell.
// Need to take one step.
nc = nc.s("d.nile", dir);
if(nc == null) {
return false; // XXX Should set position!
// throw new ZZError("AUGH! Para shifting null next");
}
load(nc);
offs = (dir < 0 ? clen : 0);
return true;
}
/** Shift to paragraph beginning or end.
*/
public boolean paraEnd(int dir) {
ZZCell nc = c;
ZZCell prev = nc;
while(nc != null && !isStruct(nc)) {
prev = nc;
nc = nc.s("d.nile", dir);
}
if(nc == null) {
nc = prev;
} else {
nc = nc.s("d.nile", -dir);
}
load(nc);
offs = (dir < 0 ? 0 : clen);
return true;
}
public void streamEnd(int dir) {
// XXX When going to beginning, may set pos to struct!
ZZCell h = c.h("d.nile", dir);
paraEnd(dir);
}
/** Get the current position with given bias.
* @param bias Whether we are looking into positive or negative
* direction. If the cursor is at an edge,
* this affects which cell is returned.
*/
public ZZCursor getCursor(int bias) {
// XXX Check para?
if(bias < 0 && offs == 0) {
ZZCell nc = c.s("d.nile", -1);
return new ZZCursorVirtual(nc, nc.getText().length());
}
return new ZZCursorVirtual(c, offs);
}
public ZZCursor getCursor() { return getCursor(1); }
/** Cut the stream at this location.
* This pointer remains pointing to the prior space,
* and it returns a iter at the beginning of the new stream.
*/
public Nile2Iter cut() {
move(0);
Nile2Iter ni = new Nile2Iter();
if(offs == 0) {
ZZCell prev = c.getOrNewCell("d.nile", -1);
prev.disconnect("d.nile", 1);
ni.load(c, 0);
load(prev);
offs = clen;
return ni;
}
if(offs == clen) { // must be very end.
ZZCell next = c.getOrNewCell("d.nile", 1);
ni.load(next, 0);
c.disconnect("d.nile", 1);
return ni;
}
ZZCell spl = split(c, offs, true);
c.disconnect("d.nile", 1);
reload();
ni.load(spl, 0);
return ni;
}
// XXX Cursors easily cut out!
public void join(Nile2Iter to) {
c.h("d.nile", 1).connect("d.nile", to.c.h("d.nile", -1));
reload();
}
public void adjustSpaceStart(int nstart) {
if(dbg) dumpStream();
Nile2Iter ctr = (Nile2Iter)clone();
int ncur = 0;
while(Nile2Unit.ws(ctr.get())) {
ncur ++; ctr.move(1);
}
if(nstart < ncur) {
move(ncur-nstart);
// XXX Cursors could go wild.
set(cut());
} else if(nstart > ncur) {
StringBuffer buf = new StringBuffer();
while(nstart-- > ncur) buf.append(' ');
ZZCell n = c.N("d.nile", -1);
n.setText(buf.toString());
load(n, 0);
}
}
public void adjustSpaceEnd(int nend) {
Nile2Iter ctr = (Nile2Iter)clone();
ctr.streamEnd(1);
int ncur = 0;
while(Nile2Unit.ws(ctr.get(-1))) {
ncur ++; ctr.move(-1);
}
if(nend < ncur) {
ctr.move(nend); // Move back forwards.
// XXX Cursors could go wild.
ctr.cut();
} else if(nend > ncur) {
StringBuffer buf = new StringBuffer();
while(nend-- > ncur) buf.append(' ');
ZZCell n = ctr.c.h("d.nile", 1).N("d.nile", 1);
n.setText(buf.toString());
}
if(dbg) dumpStream();
}
public void adjustSpaces(int nstart, int nend) {
p("Adjustspaces: "+nstart+" "+nend);
adjustSpaceStart(nstart);
adjustSpaceEnd(nend);
}
public Object clone() {
try {
return super.clone();
} catch(CloneNotSupportedException e) {
ZZLogger.exc(e);
return null;
}
}
public void dumpStream() {
pa("Dumping Nile2Iter stream: at offs: "+offs);
dump(c);
}
/** Returns true if this &lt;= it.
* Throws an error if not in same stream.
*/
public boolean isOrdered(Nile2Iter it) {
if(c.equals(it.c)) {
return offs <= it.offs;
} else {
if(c.findCell("d.nile", 1, it.c)) return true;
if(!c.findCell("d.nile", -1, it.c))
throw new ZZError("IsOrdered: not on same stream!");
return false;
}
}
public void insertInCell(String s) {
ZZCell spl = split(c, offs, true);
spl = spl.N("d.nile", -1);
spl.setText(s);
load(spl, -1);
}
public void insert(String str) {
ZZCell n = c;
if(isStruct(c)) {
load(c.s("d.nile"), -1);
ZZCell d = n.N("d.nile");
d.setText(str);
ZZLogger.log("AUGH! Insert at struct cell!");
// throw new ZZError("Inserting");
} else if((offs == 0 || offs == clen) && c.getSpan() == null) {
if(offs == clen) {
c.setText(c.getText() + str);
load(c, -1);
} else { // In this case, this iter doesn't move ;)
ZZCell prev = c.s("d.nile", -1);
if(isStruct(prev) || prev == null)
c.N("d.nile", -1).setText(str);
else
prev.setText(prev.getText() + str);
}
} else {
if(offs > 0 && offs < clen) split(c, offs, false);
ZZCell d = c.N("d.nile", offs > 0 ? 1 : -1);
d.setText(str);
load(d, -1);
}
}
/** 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;
}
/** Split the given cell into two at the offset offset.
* The original cell will remain the first.
* Returns the cell that is after the split.
* If offset is zero or greater than 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 c The cursor to split at
* @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.
* @return Never null.
*/
static public ZZCell split(ZZCell c0, int offs, boolean always) {
// XXX Adjust cursors!!!
if(always) {
ZZCell c1 = c0.N("d.nile");
if(c0.s("d.nile-transcluded") != null) {
c0.insert("d.nile-transcluded", 1, c1);
c0.insert("d.nile-transcluded-split", 1, c1);
}
Span sp = c0.getSpan();
if(sp == null) {
String s = c0.getText();
c0.setText(s.substring(0, offs));
c1.setText(s.substring(offs));
} else {
c0.setSpan(sp.subSpan(0, offs));
c1.setSpan(sp.subSpan(offs));
}
return c1;
} else {
if(offs == 0) return c0;
if(offs == c0.getText().length())
return c0.getOrNewCell("d.nile");
return split(c0, offs, true);
}
}
/** Internal convenience: make a connection and a ring if it isn't yet
* This is part of a cheat to make jumping between transclusions work:
* we connect all transclusions of a cell on d.nile-transcluded, even
* if the transcluded spans do not overlap, and jump between them.
* Works because currently, a cell can contain only one span.
* XXX what to do when splitting ???
* XXX if we cheat anyway, is it more appropriate to use zz clones?
*/
static private void transcluded(ZZCell from, ZZCell to) {
from.insert("d.nile-transcluded", 1, to);
if(from.s("d.nile-transcluded", -1) == null)
from.connect("d.nile-transcluded", -1, to);
}
/** Transcopy the part of the stream between two iters.
* Sets the iters to point to the beginning and the end of the transcopied
* stream.
*/
static public void copy(Nile2Iter start, Nile2Iter to) {
if(!start.isOrdered(to)) {
Nile2Iter tmp = to;
to = start; start = tmp;
}
// First, spanify all cells.
ZZCell d = start.c;
while(true) {
if(!isStruct(d)) spanify(d);
p("Spanified "+d+" is span "+d.getSpan());
if(d == to.c) break;
d = d.s("d.nile");
}
// Next, create the new stream.
ZZCell first, last;
if(start.c.equals(to.c)) {
first = start.c.N();
last = first;
first.setSpan(start.c.getSpan().subSpan(start.offs, to.offs));
transcluded(start.c, first);
} else {
first = start.c.N();
first.setSpan(start.c.getSpan().subSpan(start.offs));
transcluded(start.c, first);
ZZCell prev = first;
ZZCell here = start.c.s("d.nile");
while(here != to.c) {
prev = prev.N("d.nile");
prev.setSpan(here.getSpan());
transcluded(prev, here);
here = here.s("d.nile");
}
last = prev.N("d.nile");
last.setSpan(to.c.getSpan().subSpan(0, to.offs));
transcluded(to.c, last);
}
// Finally, set the iters.
start.set(new ZZCursorVirtual(first, 0));
to.set(new ZZCursorVirtual(last, last.getText().length() - 1));
}
static public void dump(ZZCell start) {
if(start == null) {
pa("Nile stream Null start!");
return;
}
ZZCell init = start.h("d.nile", -1);
pa("Nile stream! "+start);
boolean first = true;
for(ZZCell c = init; c != null; c=c.s("d.nile")) {
pa((c.s("d.nile-struct") != null ? "***S*** " : " ") +
" Cell "+c+" "+
(c.equals(start) ? " <----------------START" : ""));
if(!first && c.equals(init)) {
pa(" WAS A LOOOOOOOP!!!!!!!!!!");
throw new ZZFatalError("LOOPING NILE STREAM! BAD BAD TROUBLE!");
}
first = false;
}
}
static String[] levels = new String[] {
"P", "H3", "H2", "H1"
};
static int getMaxLevel() { return levels.length - 1; }
// XXX Should use clones.
/** Set the level of a structural cell.
*/
static void setStrLevel(ZZCell str, int level) {
if(level < 0 || level > levels.length)
throw new ZZError("Request to set invalid level! "+level);
str.setText(levels[level]);
}
/** The level of structure the given structure cell is.
* Note that the levels are a bit interesting: it returns
* zero for paragraphs, 1 for H6, 2 for H5 and so on.
* -1 is returned for cells that are not at a structural border.
*/
static int getStrLevel(ZZCell str) {
String txt = str.getText();
for(int i=0; i<levels.length; i++)
if(txt.equals(levels[i])) return i;
throw new ZZError("Invalid nile-struct "+txt);
}
/** Find an angle in the rank, i.e.the next cell on a rank with
* a connection into a certain direction.
*/
static ZZCell findAngle(ZZCell c, String dim, int dir,
String adim, int adir, boolean includeThis) {
LoopDetector ld = new LoopDetector();
if(includeThis && c.s(adim, adir) != null) return c;
c = c.s(dim, dir);
while(c != null) {
if(c.s(adim, adir) != null) return c;
ld.detect(c);
c = c.s(dim, dir);
}
return null;
}
/** Find the next or previous cell that is a structural level element.
*/
static ZZCell findStruct(ZZCell c, int dir, boolean includeThis) {
ZZCell ret = findAngle(c, "d.nile", dir, "d.nile-struct", 1, includeThis);
if(ret == null && dir == -1) {
if(!includeThis && c.s("d.nile", -1) == null)
return null;
return c.h("d.nile", -1);
}
return ret;
}
/** Break a paragraph at the given position.
* Sets this position to the first position in the possibly
* empty new paragraph.
*/
public void breakParagraph() {
ZZCell n = split(c, offs, true);
load(n, 0);
// INsert the real structural cell between.
n = n.N("d.nile", -1);
ZZCell str = n.N("d.nile-struct");
setStrLevel(str, 0); // XXX Lists...
}
/** Get the level of a paragraph.
* @param c Any cell in the paragraph - does not need to be the first.
*/
static public int getLevel(ZZCell c) {
ZZCell para = findStruct(c, -1, true);
ZZCell str = para.s("d.nile-struct");
if(str == null) return 6;
return getStrLevel(str);
}
}

254
Modules/nile/Nile2Ops.java Normal file
View File

@@ -0,0 +1,254 @@
/*
Nile2Ops.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 Nile2Ops {
public static final String rcsid = "$Id: Nile2Ops.java,v 1.9 2001/01/26 13:53:27 ajk 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 Nile2Unit unit = new Nile2Unit.Sentence();
static public void saveCursor(ZZCell view) {
ZZCell s = view.getOrNewCell("d.nile-save", 1);
if(ZZCursorReal.get(s) == null)
ZZCursorReal.set(s, new ZZCursorReal(view));
}
static public void clearSavedCursor(ZZCell view) {
ZZCell s = view.getOrNewCell("d.nile-save", 1);
if(ZZCursorReal.get(s) != null)
ZZCursorReal.set(s, (ZZCell)null);
}
static public ZZCursor getSavedCursor(ZZCell view) {
ZZCell s = view.getOrNewCell("d.nile-save", 1);
return new ZZCursorReal(s);
}
static public void setCursor(Nile2Unit unit, ZZCell view, ZZCursor to) {
p("Set cursor: "+unit+" "+view+" "+to);
if(to == null) {
// Saves only when there is nothing there before.
saveCursor(view);
to = getSavedCursor(view);
} else {
clearSavedCursor(view);
}
Nile2Iter iter = giter(to);
unit.start(iter, -1, true);
curs(view).set(iter.getCursor());
unit.end(iter, 1, true);
endcurs(view).set(iter.getCursor());
}
static public void moveCursor(Nile2Unit unit, ZZCell view, int dir) {
clearSavedCursor(view);
Nile2Iter iter;
if(dir > 0) {
iter = giter(endcurs(view));
if(!unit.insertPos(iter, 1, true)) return;
} else {
iter = giter(curs(view));
if(!unit.insertPos(iter, -1, false)) return;
}
curs(view).set(iter.getCursor());
unit.end(iter, 1, false);
endcurs(view).set(iter.getCursor());
}
static public void adjust(Nile2Unit unit, ZZCell view, int end, int dir) {
clearSavedCursor(view);
Nile2Iter iter = giter(curs(view));
Nile2Iter enditer = giter(endcurs(view));
if(end > 0) {
if(!unit.end(enditer, dir, false)) return;
} else {
if(!unit.start(iter, dir, false)) return;
}
if(!iter.isOrdered(enditer))
return;
curs(view).set(iter.getCursor());
endcurs(view).set(enditer.getCursor());
}
static public void hop(Nile2Unit unit, ZZCell view, int dir) {
clearSavedCursor(view);
Nile2Iter iter = giter(curs(view));
Nile2Iter enditer = giter(endcurs(view));
// XXX Checkpoint for undo!
Nile2Iter pt = unit.cut(iter, enditer);
unit.insertPos(pt, dir, false);
unit.paste(pt, iter, enditer);
curs(view).set(iter.getCursor());
endcurs(view).set(enditer.getCursor());
}
/** Tunnel text from one nile window to another.
* The text can be copied or cut.
*/
static public void tunnel(Nile2Unit unit, ZZCell from, ZZCell to,
int dir, boolean copy) {
clearSavedCursor(from); clearSavedCursor(to);
Nile2Iter fiter = giter(curs(from));
Nile2Iter fenditer = giter(endcurs(from));
Nile2Iter titer = giter(curs(to));
Nile2Iter pt = null;
// XXX Checkpoint for undo!
if(copy)
unit.copy(fiter, fenditer);
else
pt = unit.cut(fiter, fenditer);
// Insert exactly where we are -- is this o.k.?
// unit.insertPos(titer, dir, true);
unit.paste(titer, fiter, fenditer);
curs(to).set(fiter.getCursor());
endcurs(to).set(fenditer.getCursor());
if(!copy) {
unit.start(pt, -1, false);
curs(from).set(pt.getCursor());
unit.end(pt, 1, true);
endcurs(from).set(pt.getCursor());
}
}
static public boolean insert(ZZCell view, String key) {
clearSavedCursor(view);
if(key == null)
return false;
if(key.length() != 1)
return false;
Nile2Iter it = giter(curs(view));
it.insert(key);
ZZCursor c = it.getCursor();
curs(view).set(c);
endcurs(view).set(c);
return true;
}
static public void backspace(Nile2Unit unit, ZZCell view) {
clearSavedCursor(view);
Nile2Iter iter = giter(curs(view));
if(!unit.insertPos(iter, -1, false)) return;
Nile2Iter enditer = (Nile2Iter)iter.clone();
if(!unit.end(enditer, 1, false)) return;
Nile2Iter cut = unit.cut(iter, enditer);
if(cut == null)
throw new ZZError("Couldn't cut!");
setCursor(unit, view, cut.getCursor());
}
static public void del(Nile2Unit unit, ZZCell view) {
// XXX if nothing is selected -- delete unit right from ins cursor
unit.cut(giter(curs(view)), giter(endcurs(view)));
}
static public void breakParagraph(Nile2Unit unit, ZZCell view) {
clearSavedCursor(view);
Nile2Iter it = giter(curs(view));
it.breakParagraph();
setCursor(unit, view, it.getCursor());
}
static public void traverseTranscopies(ZZCell view, int dir) {
clearSavedCursor(view);
ZZCell c0 = ZZCursorReal.get(view);
ZZCell c = c0.s("d.nile-transcluded");
if(c == null) {
ZZLogger.log("Could not traverse transcopies because accused cell "
+ "IS not transcopied");
return;
}
ZZCell h0 = c0.h("d.nile-transcluded-split", true);
if(h0 != null) { // jump over splits
while(h0.equals(c.h("d.nile-transcluded-split"))) {
if(c.equals(c0))
throw new ZZError("XXX! All transcluded cells are "
+ "splits of the same cell! HELP!");
c = c.s("d.nile-transcluded");
}
}
ZZCursorReal.setOffs(view, 0);
ZZCursorReal.set(view, c);
}
/** 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 Nile2View, and it's even worse it does
* *line-breaking* to capitalize! I.e. move capitalization elsewhere
* (ZZUtil)<br>
* XXX b) Do this for the text between two iters (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 ZZCursorReal curs(ZZCell view) {
return new ZZCursorReal(view);
}
static public Nile2Iter giter(ZZCursor c) {
return new Nile2Iter(c);
}
static public ZZCursorReal endcurs(ZZCell view) {
ZZCell end = view.getOrNewCell("d.nile-sel");
return new ZZCursorReal(end);
}
}

152
Modules/nile/Nile2Test.java Normal file
View File

@@ -0,0 +1,152 @@
/*
Nile2Test.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.*;
/** Some simple tests for Nile.
*/
public final class Nile2Test {
public static final String rcsid = "$Id: Nile2Test.java,v 1.1 2000/12/07 00:37:19 tjl 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); }
static final void pok(String id, boolean ok) {
if(ok) p("ok "+id);
else p("not ok "+id);
}
public static void main(String[] argv) {
ZZSpace z = new ZZDimSpace();
ZZCell home = z.getHomeCell();
ZZCell stream = home.N("d.1", 1);
stream.N("d.nile-struct", 1); // Beginning structural marker
stream = stream.N("d.nile", 1); // Can't be at same cell.
stream.setText("Hello world a! How's it going?");
Nile2Iter it = new Nile2Iter(new ZZCursorVirtual(stream, 4));
p("Got: "+it.get());
Nile2Iter nextpart = it.cut();
it.dumpStream();
nextpart.dumpStream();
p("Got: "+it.get());
pok("Atend", it.get() == -1);
pok("Prev", it.get(-1) == 'l');
pok("foll", nextpart.get() == 'o');
pok("atstart", nextpart.get(-1) == -1);
it.dumpStream();
p("Joining");
it.join(nextpart);
it.dumpStream();
nextpart.dumpStream();
pok("GET1", it.get() == 'o');
pok("NEXT1", it.next() == ' ');
pok("NEXT2", it.next() == 'w');
pok("PREV1", it.prev() == ' ');
// Next, test word boundaries.
Nile2Unit un = new Nile2Unit.Word();
pok("PREVWORD1", un.start(it, -1, false));
pok("PREVWORD2", !un.start(it, -1, false));
pok("Wordplace", it.get() == 'H' && it.get(1) == 'e');
pok("NEXTWORD1", un.start(it, 1, true));
pok("Wordplace2", it.get() == 'H' && it.get(1) == 'e');
pok("NEXTWORD2", un.start(it, 1, false));
pok("Wordplace3", it.get() == 'w' && it.get(1) == 'o');
pok("NEXTWORD2", un.start(it, 1, false));
pok("Wordplace2.5", it.get() == 'a' && it.get(1) == '!');
pok("NEXTWORD2", un.start(it, 1, false));
pok("Wordplace3", it.get() == '!' && it.get(1) == ' ');
pok("NEXTWORD3", un.start(it, 1, false));
pok("Wordplace4", it.get() == 'H' && it.get(1) == 'o');
Nile2Unit un2 = new Nile2Unit.Sentence();
pok("senst1", un2.start(it, -1, false));
pok("senst2", !un2.start(it, -1, false));
pok("Sen", it.get() == 'H' && it.get(1) == 'e');
pok("senst3", un2.start(it, 1, false));
pok("Sen", it.get() == 'H' && it.get(1) == 'o');
it.dumpStream();
pok("senst4", !un2.start(it, 1, false));
it.dumpStream();
pok("senst5", un2.start(it, -1, false));
pok("senst6", un2.start(it, -1, false));
pok("senst7", !un2.start(it, -1, false));
// Now at beginning.
Nile2Iter end = (Nile2Iter)it.clone();
pok("sene1", un2.end(end, 1, true));
p("At end: "+(end.get(-1))+"||"+end.get());
end.dumpStream();
pok("sene2", end.get() == ' ' && end.get(-1) == '!');
Nile2Iter result = un2.cut(it, end);
p("Start:");
it.dumpStream();
p("End:");
end.dumpStream();
p("Res:");
result.dumpStream();
// Next, test adjustspaces.
ZZCell n = stream.N("d.1", 1);
n.setText("d");
it.set(new ZZCursorVirtual(n, 0));
pok("as1", it.get(-1) == -1 && it.get() == 'd' && it.get(1) == -1);
it.adjustSpaces(0, 1);
p("Adjusted: ");
it.dumpStream();
pok("as2", it.get(-1) == -1 && it.get() == 'd' && it.get(1) == ' ');
it.adjustSpaces(0, 0);
p("Adjusted3: ");
it.dumpStream();
pok("as3", it.get(-1) == -1 && it.get() == 'd' && it.get(1) == -1);
it.adjustSpaces(1, 0);
pok("as4", it.get(-1) == -1 && it.get() == ' ' && it.get(1) == 'd'
&& it.get(2) == -1);
it.adjustSpaces(0, 0);
p("Adjusted5: ");
it.dumpStream();
pok("as5", it.get(-1) == -1 && it.get() == 'd' && it.get(1) == -1);
pok("END", true);
System.exit(0);
}
}

432
Modules/nile/Nile2Unit.java Normal file
View File

@@ -0,0 +1,432 @@
/*
Nile2Unit.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.*;
/** A nile unit of division.
*/
public abstract class Nile2Unit {
public static final String rcsid = "$Id: Nile2Unit.java,v 1.5 2000/12/17 16:32:24 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); }
/** The previous or next position where
* this type of unit may be inserted.
*/
public abstract boolean insertPos(Nile2Iter iter, int dir,
boolean includeThis);
/** The previous or next beginning position of this type of
* unit.
*/
public abstract boolean start(Nile2Iter iter, int dir, boolean includeThis);
/** The previous or next ending position of this type of unit.
*/
public abstract boolean end(Nile2Iter iter, int dir, boolean includeThis);
/** Cut the stream.
* This routine modifies the start and to iterators to point
* to the beginning and end of the modified stream.
* @return The pasted-together cutaway point, from which
* the insert position can be searched with !includeThis.
*
*/
public abstract Nile2Iter cut(Nile2Iter start, Nile2Iter to);
/** 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 void copy(Nile2Iter start, Nile2Iter to) {
this.start(start, -1, true);
this.end(to, 1, true);
Nile2Iter.copy(start, to);
}
public abstract void paste(Nile2Iter at, Nile2Iter what, Nile2Iter whatEnd);
static public class Char extends Nile2Unit {
public boolean insertPos(Nile2Iter iter, int dir,
boolean includeThis) {
if(!includeThis)
if(!iter.move(dir))
if(!iter.shiftPara(dir))
return false;
return true;
}
public boolean start(Nile2Iter iter, int dir,
boolean includeThis) {
if(!includeThis)
if(!iter.move(dir))
if(!iter.shiftPara(dir))
return false;
if(iter.get() == -1) return false;
return true;
}
public boolean end(Nile2Iter iter, int dir,
boolean includeThis) {
if(!includeThis)
if(!iter.move(dir))
if(!iter.shiftPara(dir))
return false;
if(iter.get(-1) == -1) return false;
return true;
}
public Nile2Iter cut(Nile2Iter start, Nile2Iter to) {
Nile2Iter after = to.cut();
start.cut();
start.join(after);
return start;
}
public void paste(Nile2Iter at, Nile2Iter what, Nile2Iter whatEnd) {
}
}
/** The word and sentence base class.
* Words and sentences obey the same rules for the insert point.
*/
static public abstract class WordOrsentence extends Nile2Unit {
public abstract boolean isStart(Nile2Iter iter);
public abstract boolean isEnd(Nile2Iter iter);
public abstract int nspacesAtEnd();
// As documented,
// word insert points are word starting points, as well as
// paragraph's ending points.
public boolean insertPos(Nile2Iter iter, int dir,
boolean includeThis) {
return startOrIns(iter, dir, includeThis, true);
}
public boolean start(Nile2Iter iter, int dir, boolean includeThis) {
return startOrIns(iter, dir, includeThis, false);
}
public boolean startOrIns(Nile2Iter iter, int dir, boolean includeThis,
boolean insert) {
// This way we can always assume includeThis later.
if(!includeThis)
if(!iter.move(dir)) {
if(!iter.shiftPara(dir))
return false;
// If we were looking for an insert point, we just
// found it...
if(dir < 0 && insert) return true;
}
// Can optimize if necessary...
while(true) {
if(isStart(iter)) return true;
if(!iter.move(dir)) {
if(dir > 0 && insert) return true;
if(!iter.shiftPara(dir)) return false;
if(dir < 0 && insert) return true;
}
}
}
public boolean end(Nile2Iter iter, int dir, boolean includeThis) {
if(!includeThis)
if(!iter.move(dir)) {
if(!iter.shiftPara(dir)) return false;
// In forwards dir, just go to the beginning
// of the next paragraph.
// This way, cutting from the last insertpos until
// end() results in just the paragraph break being
// removed, which is just right.
return true;
}
while(true) {
if(isEnd(iter)) return true;
if(!iter.move(dir))
if(!iter.shiftPara(dir))
return false;
}
}
public Nile2Iter cut(Nile2Iter start, Nile2Iter end) {
// Find first non-space character.
int nws = this.firstNonWs(start);
this.p("Cut Firstnonws: "+nws + " '"+(char)nws+"'");
boolean startPunct = this.punct(nws);
// cut all the space after end.
int ncut = 0;
Nile2Iter endclone = (Nile2Iter)end.clone();
while(this.ws(end.get())) { end.move(1); ncut ++; }
// If we happen to have space before us, count that too!
int nspace = 0;
while(this.ws(endclone.prev())) nspace++;
Nile2Iter rstart = null;
if(ncut + nspace == 0 || startPunct) {
// If no space at end,
// we must cut space from before this, if it
// is a single space.
if(this.ws(start.get(-1)) && !this.ws(start.get(-2)))
(rstart = (Nile2Iter)start.clone()). move(-1);
else
if(this.ws(start.get(-1)) && startPunct) {
(rstart = (Nile2Iter)start.clone()). move(-1);
// For the if down later
ncut = 1;
}
}
// Now, here's the cut. We take away the stuff from
// start to end and then remove from rstart
// We must cut starting from the end so as not to disturb
// things.
Nile2Iter after = end.cut();
Nile2Iter first = start.cut();
Nile2Iter before = (Nile2Iter)start.clone();
start.set(first); // We want to return this in start.
// Finally, cut the other stuff.
if(rstart != null) {
rstart.cut();
before = rstart;
}
before.join(after);
// This is a bit special rule.
if(startPunct && ncut + nspace > 0)
before.insertInCell(" ");
return before;
}
public void paste(Nile2Iter at, Nile2Iter what, Nile2Iter whatEnd) {
// Check whether what has punctuation in the start...
int nws = this.firstNonWs(what);
this.p("Paste Firstnonws: "+nws + " '"+(char)nws+"'");
boolean startPunct = this.punct(nws);
Nile2Iter after = null;
if(dbg) {
this.p("PASTE!!!");
at.dumpStream();
what.dumpStream();
}
if(startPunct) {
// If there is punctuation, we must start it with
// exactly the spaces that were there, UNLESS
// we are at the beginning of the sentence, in which
// case we shall have two spaces.
if(this.ws(at.get(-1)) && this.ws(at.get(-2))) {
// Ok, now it's difficult.
// Leave these spaces, but remove one in the
// beginning of
what.adjustSpaceStart(0);
after = at.cut();
} else {
if(this.ws(at.get(-1))) {
at.move(-1);
after = at.cut();
after.move(1);
after = after.cut();
} else {
after = at.cut();
}
}
} else {
// pasting words is a bit tricky.
int prev = at.get(-1);
int cur = at.get();
// Where do we want to ensure we have space.
boolean spaceEnd = false, spaceStart = false;
// If we are at start of paragraph, we ensure that
// a space is in the end.
if(prev == -1) spaceEnd = true;
else if(this.punct(cur)) {
// If we are at punctuation, we put a space after
// inserted text is there was a space before.
if(this.ws(prev)) spaceEnd = true;
else spaceStart = true;
}
else spaceEnd = true;
what.adjustSpaces(spaceStart ? 1 : 0,
spaceEnd ? nspacesAtEnd() : 0);
after = at.cut();
}
at.join(what);
whatEnd.set(what);
whatEnd.streamEnd(1);
whatEnd.join(after);
whatEnd.set(after);
}
}
static public class Word extends WordOrsentence {
public boolean isStart(Nile2Iter iter) { return this.isWordStart(iter); }
public boolean isEnd(Nile2Iter iter) { return this.isWordEnd(iter); }
public int nspacesAtEnd() { return 1; }
}
static public class Sentence extends WordOrsentence {
public boolean isStart(Nile2Iter iter) { return this.isSentenceStart(iter); }
public boolean isEnd(Nile2Iter iter) { return this.isSentenceEnd(iter); }
public int nspacesAtEnd() { return 2; }
}
static public class Paragraph extends Nile2Unit {
// A paragraph may be inserted at the end of any paragraph,
// or in the very beginning of the stream.
// That requires special handling because
// Nile2Iter can't get there.
public boolean insertPos(Nile2Iter iter, int dir,
boolean includeThis) {
// XXX First, check if we *are* there already.
if(!end(iter, dir, includeThis)) {
if(dir > 0) return false;
ZZCursor curs = iter.getCursor(1);
curs.set(curs.get().h("d.nile"));
curs.setOffs(0);
iter.set(curs);
}
return true;
}
public boolean start(Nile2Iter iter, int dir, boolean includeThis) {
if(!includeThis) {
if(!iter.move(dir)) {
// We were at the beginning
if(!iter.shiftPara(dir))
return false;
}
}
return iter.paraEnd(-1);
}
public boolean end(Nile2Iter iter, int dir, boolean includeThis) {
if(!includeThis) {
if(!iter.move(dir)) {
// We were at the beginning
if(!iter.shiftPara(dir))
return false;
}
}
return iter.paraEnd(1);
}
public Nile2Iter cut(Nile2Iter start, Nile2Iter end) {
return null;
}
public void paste(Nile2Iter at, Nile2Iter what, Nile2Iter whatEnd) {
}
}
// Something non-ws following to ws's.
static public boolean isSentenceStart(Nile2Iter iter) {
int cur = iter.get();
if(cur == -1 || ws(cur)) return false;
int prev = iter.get(-1);
if(prev == -1) return true;
if(!ws(prev)) return false;
int prev2 = iter.get(-2);
if(prev2 == -1) return true;
if(!ws(prev2)) return false;
return true;
}
// Something non-ws following to ws's.
static public boolean isSentenceEnd(Nile2Iter iter) {
int prev = iter.get(-1);
if(prev == -1 || ws(prev)) return false;
int cur = iter.get();
if(cur == -1) return true;
if(!ws(cur)) return false;
int next = iter.get(1);
if(next == -1) return true;
if(!ws(next)) return false;
return true;
}
static public boolean isWordStart(Nile2Iter iter) {
int cur = iter.get();
if(punct(cur)) {
/* if(lett(punct.get(-1)) && lett(punct.get(1))) {
// Punctuation surrounded by two letters is NOT
// a word boundary.
return false;
} else XXX Must test in other cases!!! */
return true;
}
int prev = iter.get(-1);
// This should also cover buffer start.
if(lett(cur) && !lett(prev)) return true;
return false;
}
static public boolean isWordEnd(Nile2Iter iter) {
int prev = iter.get(-1);
if(punct(prev))
/*
if(lett(punct.get(-2)) && lett(punct.get(0))) {
// Punctuation surrounded by two letters is NOT
// a word boundary.
return false;
} else XXX Must test!!!! */
return true;
int cur = iter.get();
if(lett(prev) && !lett(cur)) return true;
return false;
}
// Shorthand.
// Exactly one of these must return true for every character.
// except -1, when all are false.
static boolean ws(int c) { return Character.isWhitespace((char)c); }
static boolean punct(int c) {
if(c == -1) return false;
return (!Character.isWhitespace((char)c)) &&
(!Character.isLetterOrDigit((char)c));
}
static boolean lett(int c) { return Character.isLetterOrDigit((char)c); }
static int firstNonWs(Nile2Iter iter) {
int ch = 0;
// Use index, don't move.
for(int i=0; (ch=iter.get(i)) != -1; i++) {
if(!ws(ch)) return ch;
}
return -1;
}
}

View File

@@ -0,0 +1,63 @@
/*
NileApplet.java
*
* Copyright (c) 1999, 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;
import java.awt.*;
import java.applet.*;
import java.io.*;
import java.util.*;
/** An applet showing the split Nile view.
* This just takes a lot of code from ZZApplet -- bad style. Well, this is
* just an intermediate: When we start making real demos using the Dump
* stuff, ZZApplet probably needs to be rewritten, and NileApplet will be
* totally obsolete.
*/
public class NileApplet extends Applet {
public static final String rcsid = "$Id: NileApplet.java,v 1.4 2001/01/03 16:47:50 raulir Exp $";
public void init() {
ZZSpace space = new ZZCacheDimSpace(new DummyStreamSet());
ZZCell home=space.getHomeCell();
home.N("d.1").setText("First text");
home.N("d.1").setText("Second text");
ZZDefaultSpace.create(home);
setLayout(new BorderLayout());
ZZWindows.init(space,this);
ZZCell topl = ZZWindows.startCell().s("d.1", 1)
.s("d.2", 1);
ZZCell a = topl.s("d.1", 2);
ZZCell b = topl.s("d.2", 1).s("d.1", 1);
ZZCursorReal.set(a, home.s("d.1", 1));
ZZCursorReal.set(b, home.s("d.1", 2));
ZZCursorReal.setColor(a,Color.red);
ZZCursorReal.setColor(b,Color.yellow);
org.gzigzag.module.SplitNileDemo.updateSpace(a, a, b);
ZZObsTrigger.runObsQueue();
}
}

143
Modules/nile/NileDemo.java Normal file
View File

@@ -0,0 +1,143 @@
/*
NileDemo.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.*;
import java.awt.*;
import java.io.*;
public class NileDemo {
public static final String rcsid = "$Id: NileDemo.java,v 1.3 2000/12/28 01:19:07 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); }
/** Update the bindings etc.in the space.
* This is temporary and should go away once we have better
* space management.
*/
static void updateSpace(ZZCell code, ZZCell viewCell, ZZCell ctrlCell) {
// First, make sure the system list cell is ok
// and recreate actions.
ZZSpace sp = code.getSpace();
ZZCell t0 = ZZDefaultSpace.findOnSystemlist(sp, "Nile1", true);
ZZCell bind = t0.getOrNewCell("d.1", 1);
ZZCell rast = t0.getOrNewCell("d.2", 1);
bind.disconnect("d.1", 1);
bind = bind.N("d.1", 1);
Nile1.actions(bind);
rast.disconnect("d.1",1);
rast = rast.N("d.1");
rast.setText("Nile1View");
rast = rast.N("d.1");
rast.setText("Nile1.Normal");
rast = rast.s("d.1", -1); // go to the cell we need
// Then, update windows.
ZZCell datac = updateWin(sp, viewCell, rast, bind, false);
ZZCell ctrlc = updateWin(sp, ctrlCell, rast, bind, true);
if(datac != null && ctrlc != null) {
datac.excise("d.nile-wins"); ctrlc.excise("d.nile-wins");
ctrlc.connect("d.nile-wins", 1, datac);
datac.connect("d.nile-wins", 1, ctrlc);
}
}
static ZZCell updateWin(ZZSpace sp, ZZCell viewCell, ZZCell rast,
ZZCell bind, boolean ctrl) {
if(viewCell == null) return null;
// Make sure we have a window and update its
// binding and raster.
ZZCell win = null;
String title = ctrl ? "Nile1-Left" : "Nile1W";
int round = 0;
while(win == null) {
if(round > 1)
throw new ZZError("Couldn't create window");
round++;
win = ZZDefaultSpace.findOnSystemlist(sp, "Windows", false);
win = win.s("d.1");
win = win.findText("d.2", 1, title);
if(win == null)
ZZDefaultSpace.newToplevelView(sp,
title, ctrl ? 0 : 400, 20, 400, 400,
null, null, null, viewCell,
null, new Color(0x819fff));
}
// Then, set window bindings
win = win.s("d.1", 2);
ZZCell bcurs = win.getOrNewCell("d.bind", 1);
ZZCursorReal.set(bcurs, bind);
ZZCell viewsel = win.findText("d.2", 1, "View");
ZZCursorReal.set(viewsel, rast);
ZZCursorReal.setColor(viewsel, ZZUtil.perturb(Color.red));
return win;
}
static public ZZModule module = new ZZModule() {
public void action(String id,
ZZCell code,
ZZCell target,
ZZView view, ZZView cview, String key, Point pt, ZZScene xi) {
ZZUpdateManager.setFast(view);
ZZCell viewCell = view.getViewcell();
ZZCell otherCell = viewCell.s("d.nile-wins");
p("Nile action: "+id+" "+key);
StringTokenizer st = new StringTokenizer(id);
String[] toks = new String[st.countTokens()];
for(int i=0; i<toks.length; i++)
toks[i] = st.nextToken();
id = toks[0];
// Update the Nile stuff to the latest bindings (defined
// unfortunately in this file -- oh how I long for the day
// when they are in a ZZ space as the definitive version)
if(id.equals("UPDATE")) {
updateSpace(code, viewCell, null);
} else if(id.equals("UPDATEBOTH")) {
updateSpace(code, viewCell, cview.getViewcell());
} else if(id.equals("REGISTER")) {
ZZCell binds = code.N();
Nile1.actions(binds);
ApplitudeMgr.register(code.getSpace(), "Nile",
new String[] { "d.nile" },
"Nile1.Normal", binds);
}
}
};
}

49
Modules/nile/README Normal file
View File

@@ -0,0 +1,49 @@
Nile Is a Literary Editor ;)
=========================
Hierarchical text structure
Key-based simple selections *and* motions of the selected text
word-by-word, sentence-by-sentence.
Parallel markup stream, indicating the paragraphs and sectioning.
Additionally, spaceparts that use this markup to imply various
kinds of trees.
With firstcap after any two spaces.
Operations: (not yet implemented - this is a list of ideas)
Fundamental (required to create text that uses all features)
- insertion
- backspace, going over paragraph boundaries
- enter, split / start new paragraph
- cursor motion
- adjust paragraph level (P .. H1)
(XXX mustn't take too much keyspace if we want e.g. ULs later)
Rearrangement (the real thing)
- selecting groups by word / sentence / paragraph using keyboard,
hopping those bits over the corresponding bits
- easily moving to outline view, outline hop
- splitting into implied fragments and hopping those as cells
- cutting into textcloud -style bits that float around paragraphs
Hyperconnections
- paragraph comments for outline
- sideways connected documents, using nile-anchor dimension between
cells very simply. Like paralleltextview
Versioning & transcluding
- need cellular versioning mechanisms
- side-by-side view with beams
(the nile delta view ;)
- see all current documents that transclude given area
-----
Refs:
http://devworld.apple.com/techpubs/mac/HIGuidelines/HIGuidelines-224.html
http://www.windowatch.com/firstwords/fw-cutpa.html
http://www.windowatch.com/firstwords/first3.html
http://www.oasis-open.org/cover/coombs.html
Word's selection by words by default needs to be mentioned.

View File

@@ -0,0 +1,113 @@
/*
SplitNileDemo.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.*;
import java.awt.*;
import java.io.*;
public class SplitNileDemo {
public static final String rcsid = "$Id: SplitNileDemo.java,v 1.2 2000/12/26 21:59:22 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); }
/** Update the bindings etc.in the space.
* This is temporary and should go away once we have better
* space management.
*/
static public void updateSpace(ZZCell code, ZZCell viewCell,
ZZCell ctrlCell) {
// First, make sure the system list cell is ok
// and recreate actions.
ZZSpace sp = code.getSpace();
ZZCell t0 = ZZDefaultSpace.findOnSystemlist(sp, "SplitNile", true);
ZZCell bind = t0.getOrNewCell("d.1", 1);
ZZCell rast = t0.getOrNewCell("d.2", 1);
bind.disconnect("d.1", 1);
bind = bind.N("d.1", 1);
Nile1.actions(bind);
rast.disconnect("d.1",1);
rast = rast.N("d.1");
rast.setText("Nile1View");
rast = rast.N("d.1");
rast.setText("Nile1.Parallel");
rast = rast.s("d.1", -1); // go to the cell we need
// Next, make sure we have a window
ZZCell win = null;
String title = "Nile";
int round = 0;
while(win == null) {
if(round > 1)
throw new ZZError("Couldn't create window");
round++;
win = ZZDefaultSpace.findOnSystemlist(sp, "Windows", false);
win = win.s("d.1");
win = win.findText("d.2", 1, title);
if(win == null)
ZZDefaultSpace.newToplevelView(sp,
title, 20, 20, 600, 400,
null, null, null, null,
null, new Color(0x819fff));
}
// Then, create the cursor pair and set the window cursor
win = win.s("d.1", 2);
ZZCell leftcurs = win.N();
ZZCursorReal.attach(leftcurs, ctrlCell);
ZZCell rightcurs = leftcurs.N("d.nile-pair");
ZZCursorReal.attach(rightcurs, viewCell);
ZZCursorReal.set(win, leftcurs);
// Finally, set window bindings
ZZCell bcurs = win.getOrNewCell("d.bind", 1);
ZZCursorReal.set(bcurs, bind);
ZZCell viewsel = win.findText("d.2", 1, "View");
ZZCursorReal.set(viewsel, rast);
ZZCursorReal.setColor(viewsel, ZZUtil.perturb(Color.red));
}
static public ZZModule module = new ZZModule() {
public void action(String id,
ZZCell code,
ZZCell target,
ZZView view, ZZView cview, String key, Point pt, ZZScene xi) {
ZZUpdateManager.setFast(view);
// Update the Nile stuff to the latest bindings (defined
// unfortunately in this file -- oh how I long for the day
// when they are in a ZZ space as the definitive version)
if(id.equals("UPDATE")) {
updateSpace(code, view.getViewcell(), cview.getViewcell());
}
}
};
}

View File

@@ -0,0 +1,71 @@
/*
SplitNileView.zob
*
* 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.awt.*;
/** A view that shows two Nile streams side-by-side. */
public class SplitNileView implements FlobView, ZOb {
public static final String rcsid = "$Id: SplitNileView.zob,v 1.1 2000/12/26 20:15:50 bfallenstein Exp $";
/** Should we print out debugging info? */
public static boolean dbg = false;
static final void p(String s) { if(dbg) pa(s); }
static final void pa(String s) { System.out.println(s); }
STRUCTPARAMS {
/** The space to leave between the two Nile streams. */
int margin
= 20;
}
public void raster(FlobSet into, FlobFactory fact,
ZZCell win, String[] dims, ZZCell accursed) {
ZZCell leftc = accursed.s("d.nile-pair", -1);
ZZCell rightc = accursed.s("d.nile-pair");
if(leftc != null && rightc == null)
rightc = accursed;
else if(leftc == null && rightc != null)
leftc = accursed;
else
throw new ZZError("HELP! Strange d.nile-pair arrangement XXX");
ZZCell leftacc = ZZCursorReal.get(leftc);
ZZCell rightacc = ZZCursorReal.get(rightc);
Dimension size = new Dimension(into.getSize());
size.width = (size.width - margin) / 2;
FlobSet left = into.create(size), right = into.create(size);
Nile1View view = new Nile1View();
view.init__zob();
SimpleBeamer bmr = new SimpleBeamer();
view.raster(left, fact, leftc, dims, leftacc);
view.raster(right, fact, rightc, dims, rightacc);
into.fetch(left, 0, 0, 0);
into.fetch(right, size.width + margin, 0, 0);
bmr.decorate(into, "", null);
}
}

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