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

95
Java/media/Address.java Normal file
View File

@@ -0,0 +1,95 @@
/*
Address.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 Tuomas Lukka
*/
package org.gzigzag;
import java.util.*;
/** An address in a stable media stream.
* Currently just a silly pointer but will be later extended
* to include full tumblers.
* Format: streamid.offset, where streamid is an alphanumeric string
* identifying a stream.
*/
public class Address {
public static final String rcsid = "$Id: Address.java,v 1.3 2000/09/19 10:32:00 ajk Exp $";
public static final boolean dbg = false;
static final void p(String s) { if(dbg) System.out.println(s); }
Scroll scroll;
String stream;
long offset;
public static Address parse(String s) {
StringTokenizer st = new StringTokenizer(s, ".");
if(st.countTokens() != 2) {
throw new ZZError("Wrong number of toks in '"+s+"'");
}
Address a = new Address();
a.stream = st.nextToken();
a.offset = Long.parseLong(st.nextToken());
return a;
}
public static Address streamOffs(String s, long offs) {
Address a = new Address();
a.stream = s;
a.offset = offs;
return a;
}
public static Address scrollOffs(Scroll s, long offs) {
// return s.getId() + "." + offs;
Address a = new Address();
a.stream = s.getId();
a.offset = offs;
return a;
}
public String getStream() { return stream; }
public Scroll getScroll(ZZSpace s) {
if(scroll==null) scroll = Scroll.obtain(s, stream);
return scroll;
}
public long getOffs() { return offset; }
public String toString() {
return stream.toString() + "." + offset;
}
public boolean lessThan(Address a) {
int c = stream.compareTo(a.stream);
if(c < 0) return true;
if(c > 0) return false;
if(offset < a.offset) return true;
return false;
}
public Address addOffs(int o) {
Address a = new Address();
a.scroll = scroll;
a.stream = stream;
a.offset = offset + o;
return a;
}
}

View File

@@ -0,0 +1,99 @@
/*
ByteScroll.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 Tuomas Lukka
*/
package org.gzigzag;
import java.util.*;
import java.io.*;
/** A scroll of bytes. May later have holes etc. from expunging,
* but basically the point is that the address to content mapping
* is stable and will not change whatever is done.
*/
public class ByteScroll extends Scroll{
public static final String rcsid = "$Id: ByteScroll.java,v 1.4 2000/09/19 10:32:00 ajk Exp $";
// Cheat: it's just a file
private RandomAccessFile f;
private String encoding;
private boolean readonly;
public ByteScroll(String id, File fn, String mode, String encoding,
boolean ro) {
super(id);
this.encoding = encoding;
this.readonly = ro;
try {
f = new RandomAccessFile(fn, mode);
} catch(Exception e) {
ZZLogger.exc(e);
throw new NullPointerException("Can't get");
}
}
public synchronized byte[] get(long start, int n) {
try {
f.seek(start);
byte[] ret = new byte[n];
int r = f.read(ret);
if(r!=n)
throw new NullPointerException("NOT ENOUGH!");
return ret;
} catch(Exception e) {
ZZLogger.exc(e);
throw new NullPointerException("Can't get");
}
}
// XXX ENCODING
public String getString(long start, int n) {
byte[] b = get(start,n);
try {
return new String(b, "ISO8859_1");
} catch(Exception e) {
ZZLogger.exc(e);
throw new NullPointerException("Can't get");
}
}
public long curEnd() { try { return f.length();
} catch(Exception e) {
ZZLogger.exc(e);
throw new NullPointerException("Can't get");
}
}
public synchronized long append(byte[] bytes) {
if(readonly) throw new ZZError("Can't append to readonly scroll");
try {
long offs = curEnd();
f.seek(offs);
f.write(bytes);
return offs;
} catch(Exception e) {
ZZLogger.exc(e);
throw new NullPointerException("Can't get");
}
}
}

100
Java/media/Capitalizer.java Normal file
View File

@@ -0,0 +1,100 @@
/*
Capitalizer.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.
*
*/
/*
* Capitalizing code written by Tuomas Lukka
*/
package org.gzigzag;
import java.awt.*;
import java.util.*;
/** A class to capitalize first letters of sentences.
* A sentence is separated by two or more spaces. If you just need to
* capitalize one string, use the static method single(String). If you need
* to capitalize a number of concurrent strings, instantiate Capitalizer and
* run cap(String) over them, in order.
* <p>
* The algorithm is tricky and probably needs a lot of adjustment.
* The first letters of sentences are currently identified by
* two spaces next to each other.
*/
public class Capitalizer {
public static final String rcsid = "$Id: Capitalizer.java,v 1.1 2000/12/25 19:37:09 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); }
/** Have we read a single space? */
boolean hadSpace = false;
/** Are we past a sentence boundary (two spaces)? */
boolean boundary = true;
public static String single(String s) {
return new Capitalizer().cap(s);
}
public String cap(String s) {
int len = s.length();
// Look for the first non-space character the hard way.
int i;
for(i=0; i<s.length(); i++) {
char c = s.charAt(i);
if(Character.isLetter(c)) {
if(boundary)
s = s.substring(0,i) + Character.toUpperCase(c)
+ s.substring(i+1);
boundary = false;
hadSpace = false;
break;
} else
if(c == ' ') {
if(hadSpace) {
boundary = true;
hadSpace = false;
} else {
hadSpace = true;
}
}
}
if(i == s.length()) return s;
boundary = false;
hadSpace = false;
if(len > 1) {
char c1 = s.charAt(len-1);
if(c1 == ' ') {
char c2 = s.charAt(len-2);
boundary = (c2 == ' ');
hadSpace = (c2 != ' ');
}
}
// Then, find the other occurrences.
int ind = i-1;
while((ind = s.indexOf(" ", ind+1)) >= 0) {
if(ind == s.length() - 2) break;
if(Character.isLetter(s.charAt(ind+2)))
s = s.substring(0, ind+2) +
Character.toUpperCase( s.charAt(ind+2))
+ s.substring(ind+3);
}
return s;
}
}

109
Java/media/CharScroll.java Normal file
View File

@@ -0,0 +1,109 @@
/*
CharScroll.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 Tuomas Lukka
*/
package org.gzigzag;
import java.util.*;
import java.io.*;
/** DEPRECATED FOR STRINGSCROLL
* A scroll of chars. May later have holes etc. from expunging,
* but basically the point is that the address to content mapping
* is stable and will not change whatever is done.
* XXX copied from ByteScroll - sad.
*/
public class CharScroll extends Scroll{
public static final String rcsid = "$Id: CharScroll.java,v 1.5 2000/09/19 10:32:00 ajk Exp $";
// Cheat: it's just a file
private RandomAccessFile f;
// public static final boolean dbg = true;
// static final void p(String s) { if(dbg) System.out.println(s); }
private boolean readonly;
public CharScroll(String id, File fn, String mode,
boolean ro) {
super(id);
this.readonly = ro;
try {
f = new RandomAccessFile(fn, mode);
} catch(Exception e) {
ZZLogger.exc(e);
throw new NullPointerException("Can't get");
}
}
public synchronized char[] get(long start, int n) {
try {
f.seek(start*2);
byte[] ret = new byte[n*2];
int r = f.read(ret);
if(r!=n*2)
throw new NullPointerException("NOT ENOUGH!");
char[] c = new char[n];
for(int i=0; i<n; i++)
c[i] = (char)((ret[i*2]<<8) | (ret[i*2+1]<<0));
return c;
} catch(Exception e) {
ZZLogger.exc(e);
throw new NullPointerException("Can't get");
}
}
// XXX ENCODING
public String getString(long start, int n) {
char[] b = get(start,n);
String s = new String(b);
p("GETS: "+start+" "+n+" "+b+" '"+s+"'");
return s;
}
public long curEnd() { try { return f.length()/2;
} catch(Exception e) {
ZZLogger.exc(e);
throw new NullPointerException("Can't get");
}
}
public synchronized long append(String b) {
return append(b.toCharArray());
}
public synchronized long append(char[] bytes) {
if(readonly) throw new ZZError("Can't append to readonly scroll");
try {
long offs = curEnd();
f.seek(offs*2);
// f.write(bytes);
for(int i=0; i<bytes.length; i++) {
f.writeChar(bytes[i]);
}
p("WRITE: R "+offs);
return offs;
} catch(Exception e) {
ZZLogger.exc(e);
throw new NullPointerException("Can't get");
}
}
}

226
Java/media/FText.java Normal file
View File

@@ -0,0 +1,226 @@
/*
FText.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;
import java.awt.*;
import java.util.*;
/** A class representing some formatted text.
* The text is represented as FText.Part objects, which
* contain a single string of text in a given font, from a given cell
* (possibly with an offset and length).
* These parts may be created by any view.
* @see FTextLayouter
*/
public class FText {
public static final String rcsid = "$Id: FText.java,v 1.21 2001/04/02 20:02:51 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 abstract class Part {
public Font f; public FontMetrics fm;
public Color bg, fg;
public String s; public int n;
/** Make a flob showing this part on the screen. */
public abstract Renderable rend(int x, int y, int d, Renderable prev);
/** Create another instance of the same class. */
public abstract Part subpart(int first, int last);
public Part[] split(int at) { return new Part[] { to(at), from(at) }; }
public Part to(int at) { return subpart(0, at); }
public Part from(int at) { return subpart(at, n); }
public int getStrX(int at) {
return fm.stringWidth(s.substring(0, at));
}
public int getHit(int x) {
return ZZUtil.findStringHit(s, x, fm, true);
}
public int getHitX(int x) { return getStrX(getHit(x)); }
private int wd = -1;
public int width() {
if(wd == -1) wd = fm.stringWidth(s);
return wd;
}
private int ht = -1;
public int height() {
if(ht == -1) ht = fm.getHeight();
return ht;
}
public static String join(Part[] ps) {
StringBuffer sb = new StringBuffer();
for(int i=0; i<ps.length; i++) sb.append(ps[i].s);
return sb.toString();
}
public static int width(Part[] ps) {
int res = 0;
for(int i=0; i<ps.length; i++)
res += ps[i].width();
return res;
}
public static int height(Part[] ps) {
int res = 0;
for(int i=0; i<ps.length; i++) {
int h = ps[i].height();
if(h > res) res = h;
}
return res;
}
public static int len(Part[] ps) {
int res = 0;
for(int i=0; i<ps.length; i++) res += ps[i].n;
return res;
}
}
public static class CellPart extends Part {
public ZZCell c, handleCell;
public int offs;
public CellPart(ZZCell c, ZZCell handleCell, int offs, int n, Font f,
FontMetrics fm, Color bg, Color fg) {
this.c = c; this.offs = offs; this.f = f; this.fm = fm;
this.handleCell = handleCell;
this.bg = bg; this.fg = fg;
if(n < 0) {
s = c.t().substring(offs);
this.n = s.length();
} else {
s = c.t().substring(offs, offs+n);
this.n = n;
}
}
public CellPart(ZZCell c, int offs, int n, Font f,
FontMetrics fm, Color bg, Color fg) {
this(c,c,offs,n,f,fm,bg,fg);
}
public Part subpart(int first, int last) {
CellPart p = new CellPart(c, handleCell, offs+first, last-first, f, fm, bg, fg);
p.s = s.substring(first, last);
return p;
}
public Renderable rend(int x, int y, int d, Renderable prev0) {
SplitCellFlob1 prev = null, parent = null, res;
if(prev0 instanceof SplitCellFlob1) {
prev = (SplitCellFlob1)prev0;
parent = prev.parent != null ? prev.parent : prev;
if(!parent.c.equals(c))
parent = null;
}
res = new SplitCellFlob1(x, y, d, width(), height(),
handleCell, s, f, fm, parent, offs, n, bg);
if(prev != null) res.setPrev(prev);
return res;
}
}
/** A cell part showing cursor colors in the background. */
public static class CursorCellPart extends CellPart {
public CursorCellPart(ZZCell c, ZZCell handleCell, int offs, int n, Font f,
FontMetrics fm, Color bg, Color fg) {
super(c, handleCell, offs, n, f, fm, bg, fg);
}
public CursorCellPart(ZZCell c, int offs, int n, Font f,
FontMetrics fm, Color bg, Color fg) {
super(c, offs, n, f, fm, bg, fg);
}
public Renderable rend(int x, int y, int d, Renderable prev) {
SplitCellFlob1 sf = (SplitCellFlob1)super.rend(x, y, d, prev);
sf.showCursor();
return sf;
}
}
public static class StringPart extends Part {
public StringPart(String s, Font f, FontMetrics fm,
Color bg, Color fg) {
this.s = s; this.f = f; this.fm = fm; this.bg = bg; this.fg = fg;
n = s.length();
}
public Part subpart(int first, int last) {
return new StringPart(s.substring(first, last), f, fm, bg, fg);
}
public Renderable rend(int x, int y, int d, Renderable prev) {
int my = y - fm.getDescent() + height();
TextDecor res = new TextDecor(x, my, s, fg, f);
res.d = d;
return res;
}
}
public Part[] parts;
public FText(Part[] ps) { parts = ps; }
public FText(Vector v) {
parts = new Part[v.size()];
for(int i=0; i<parts.length; i++) parts[i] = (Part)v.elementAt(i);
}
public String get() { return Part.join(parts); }
public String get(int at, int n) { return get().substring(at, at+n); }
// return { part, offs }
public int[] pos(int at) {
int here = 0;
for(int i=0; i<parts.length; i++) {
Part p = parts[i];
here += p.n;
if(here >= at) {
return new int[] { i, p.n-(here-at) };
}
}
return new int[] { parts.length-1, parts[parts.length-1].n };
}
/** Return lines laid out at a given width.
* The dimensions of the array are lines[line][part]
*/
public Part[][] getLines(int width) {
return FirstMatchBreaker.stupidBreak(this, width);
}
public static int findLine(Part[][] lines, int offs) {
int here = 0;
for(int i=0; i<lines.length; i++) {
here += Part.len(lines[i]);
if(here >= offs) return i;
}
return lines.length-1;
}
/** Capitalize the sentences in the whole FText. */
public void capitalize() {
Capitalizer c = new Capitalizer();
for(int i=0; i<parts.length; i++)
parts[i].s = c.cap(parts[i].s);
}
}

View File

@@ -0,0 +1,143 @@
/*
FTextBreaker.java
*
* Copyright (c) 2001, 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.util.*;
/** A class doing linebreaking of FTexts with the stupid first match algorithm.
* @see FText
*/
public class FirstMatchBreaker {
public static final String rcsid = "$Id$";
public static boolean dbg = false;
private static final void p(String s) { if(dbg) System.out.println(s); }
private static final void pa(String s) { System.out.println(s); }
static class Word {
FText.Part p; int start, end;
/** Is a line break enforced after this word? */
boolean forced;
FText.Part get() { return p.subpart(start, end); }
int width() { return p.fm.stringWidth(p.s.substring(start, end)); }
Word(FText.Part p, int s, int e, boolean f) {
this.p=p; start=s; end=e; forced=f;
}
}
/** Internal convenience: concatenate all adjacent words in a vector. */
private static void concat(Vector v) {
int i=0;
while(i+1 < v.size()) {
Word w1 = (Word)v.elementAt(i), w2 = (Word)v.elementAt(i+1);
if(w1.p == w2.p && w1.end == w2.start) {
w1.end = w2.end;
v.removeElement(w2);
} else
i++;
}
}
/** Internal convenience: search a number of tokens in a string
* Returns the next occurence of any of the tokens after the given
* position.
*/
static int indOf(String s, int pos, char[] toks) {
for(int i=pos+1; i<s.length(); i++) {
char c = s.charAt(i);
for(int j=0; j<toks.length; j++)
if(toks[j] == c) return i;
}
return -1;
}
/** Prepare an FText for the linebreaker (find possible breaks).
* Returns a Vector of Word objects.
*/
public static Vector prepare(FText txt) {
final char[] delims = new char[] { ' ', '\n' };
if(txt.parts.length == 0)
throw new ZZError("FText has length 0");
Vector v = new Vector();
p("prepare: Old FText (len "+txt.parts.length+"): "+txt.get());
for(int i=0; i<txt.parts.length; i++) {
FText.Part p = txt.parts[i];
String s = p.s;
int here, last = -1;
while((here = indOf(s, last, delims)) >= 0) {
char c = s.charAt(here);
v.addElement(new Word(p, last+1, here+1, c == '\n'));
last = here;
}
v.addElement(new Word(p, last+1, s.length(), false));
}
return v;
}
/** Internal convenience: Vector to array. */
private static FText.Part[] v2a(Vector v) {
concat(v);
FText.Part[] res = new FText.Part[v.size()];
for(int i=0; i<res.length; i++)
res[i] = ((Word)v.elementAt(i)).get();
return res;
}
public static FText.Part[][] stupidBreak(FText ftxt, int width) {
Vector prep = prepare(ftxt); int psize = prep.size();
Vector lineset = new Vector(), line = new Vector();
int x = 0, linestart = 0;
p("FTextBreaker.stupidBreak...");
for(int i=0; i<psize; i++) {
Word w = (Word)prep.elementAt(i);
x += w.width();
if(!w.forced && x <= width) {
line.addElement(w);
continue;
}
if(i > linestart && x > width) {
lineset.addElement(v2a(line));
line.removeAllElements();
x = w.width(); linestart = i;
}
line.addElement(w);
if(x > width || w.forced) {
/* w alone doesn't fit on our line, or forced break after w */
lineset.addElement(v2a(line));
line.removeAllElements();
x = 0; linestart = i+1;
}
}
if(line.size() > 0) lineset.addElement(v2a(line));
FText.Part[][] res = new FText.Part[lineset.size()][];
for(int j=0; j<res.length; j++) {
res[j] = (FText.Part[])lineset.elementAt(j);
if(dbg) pa(" "+(j+1)+". "+new FText(res[j]).get());
}
return res;
}
}

112
Java/media/LineBreaker.java Normal file
View File

@@ -0,0 +1,112 @@
/*
LineBreaker.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;
import java.awt.FontMetrics;
import java.lang.reflect.*;
import java.util.*;
/** An interface to line breaking algorithms
* This class knows absolutely nothing about characters, words, or so on.
* It handles abstract tokens given by integers and asks the related
* class LineInfo about the goodness of fitting a particular line
* at a particular point.
* <p>
* Specific attention is paid to the billowing problem where
* we have to render a paragraph so that the cursor is at the line with
* the first line of the paragraph starts from the beginning of a line.
* This is achieved by specifying the cursor position (from where the lines
* should be counted) and the desired line number for that position.
* This can also be used to render the paragraph starting from the end.
*/
public abstract class LineBreaker {
String rcsid = "$Id: LineBreaker.java,v 1.8.10.1 2001/08/16 13:50:01 bfallenstein Exp $";
static private void pa(String s) { System.out.println(s); }
/** Break the lines between the given addresses.
* @return An array: the first element is the line index the rendering
* starts on, and from the second element onwards the end points of the
* lines are given.
*/
public abstract int[] breakLines(LineInfo li);
static private void test(LineBreaker b, String str, int cursor,
SimpleLineInfo.Layout l) {
SimpleLineInfo inf = new SimpleLineInfo(str, cursor, l);
int[] foo = b.breakLines(inf);
pa("Tested '"+str+"'");
for(int i=0; i<foo.length; i++)
pa(""+foo[i]);
}
/** Doesn't compile with Kaffe's classes.
static private class FixFM extends FontMetrics {
FixFM() { super(null); }
public int getAscent() { return 0; }
public int getDescent() { return 0; }
public int getLeading() { return 0; }
public int getMaxAdvance() { return 10; }
public int charWidth(char ch) { return 10; }
public int charsWidth(char data[], int off, int len) {
return 10 * len;
}
}
**/
/** A test routine. Provide name of the class to test first.
* The class to test must have a public constructor.
*/
/** FixFM doesn't compile with Kaffe's classes.
static public void main(String[] argv) {
try {
Class cls = Class.forName("org.gzigzag."+argv[0]);
Constructor ctr = cls.getConstructor(new Class[] {});
LineBreaker b = (LineBreaker)ctr.newInstance(new Object[] {});
Vector db = new Vector();
for(int i=0; i<argv.length; i++)
Main.debugClass(argv[i], db);
final FontMetrics fixwd = new FixFM();
// Test 1: fixed-width lines, 10 chars each.
SimpleLineInfo.Layout lay = new SimpleLineInfo.Layout() {
public FontMetrics getFontMetrics(int line) {
return fixwd;
}
public int getWidth(int line) {
return 100;
}
public int getCenterLine() { return 0; }
};
test(b, "", 0, lay);
test(b, "A", 0, lay);
test(b, "A B C D E F G H I J K L M N O P Q", 0, lay);
} catch(Exception e) {
e.printStackTrace();
pa(""+e);
}
}
**/
}

55
Java/media/LineInfo.java Normal file
View File

@@ -0,0 +1,55 @@
/*
LineInfo.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;
/** An interface for line breaking algorithms to query about line widths.
* The LineBreaker interface allows many different line breaking algorithms.
* This class allows those algorithms to ask about various possible breaks
* within the line.
*/
public interface LineInfo {
String rcsid = "$Id: LineInfo.java,v 1.5 2000/09/19 10:32:00 ajk Exp $";
/** The maximum token index.
*/
int getMax();
int getCursor();
int getCenterLine();
/** Give the best token between x0 and x1 that splits the interval
* by approximately fract.
* @return An array: first element is index, second penalty
*/
void split(int x0, int n, int sug, int[] ret);
/** Give the penalty of fitting the tokens from x0 to x1 on the
* given line.
* @return An array: first element 1000*width fraction, second element
* penalty
*/
void widthPenalty(int x0, int n, int line, int[] ret);
}

2
Java/media/README Normal file
View File

@@ -0,0 +1,2 @@
This directory contains files related to handling various fluid media
(text, sound, video, pictures) in ZZ.

97
Java/media/Scroll.java Normal file
View File

@@ -0,0 +1,97 @@
/*
Scroll.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;
import java.util.*;
import java.io.*;
/** A pseudo-permanent stable media storage.
* If you put data in a scroll, it is guaranteed (well, in the development
* stage only theoretically) that the same address will be eternal for it.
* If you look for that particular address, you will get the data you stored
* at it or you will get an error (e.g. if you have expunged some region
* of a scroll) from it not being accessible.
*/
public abstract class Scroll {
public static final String rcsid = "$Id: Scroll.java,v 1.5 2000/11/03 08:01:05 ajk Exp $";
public static boolean dbg = true;
static public final void p(String s) { if(dbg) System.out.println(s); }
static public final void pa(String s) { if(dbg) System.out.println(s); }
protected Scroll(String i) { id = i; }
String id;
public String getId() { return id; }
static Hashtable scrolls = new Hashtable();
public interface Creator {
Scroll create(String id, File f);
}
static Hashtable scrolltypes = new Hashtable(); // full of creators
static public void register(String id, Creator c) {
scrolltypes.put(id, c);
}
static public void register(String id, Scroll s) {
scrolls.put(id, s);
}
/** Get a scroll for the given identifier.
* This method encapsulates the way the system finds
* scrolls and the implementation will change in the future.
* Currently it looks in the given ZZSpace for a file name.
*/
static public Scroll obtain(ZZSpace e, String id) {
Scroll scr;
if((scr=(Scroll)scrolls.get(id))!=null)
return scr;
ZZCell c = e.getHomeCell();
c = ZZDefaultSpace.findScrollCell(c, id);
if(c==null) return null;
ZZCell cfn = c.s("d.2",1);
File f = new File(cfn.getText());
ZZCell typ = cfn.s("d.2",1);
String t = typ.getText();
if(t.equals("Byte")) {
scr = new ByteScroll(id, f, "r", "ISO8859_1", true);
} else if(t.equals("Sound")) {
ZZCell dur = typ.s("d.2", 1);
scr = new SoundScroll(id, f, Long.parseLong(dur.getText()));
} else {
Creator crea = (Creator)scrolltypes.get(t);
if(crea==null)
throw new ZZError("No such scroll type: "+t);
scr = crea.create(id, f);
}
scrolls.put(id, scr);
return scr;
}
}

View File

@@ -0,0 +1,141 @@
/*
SimpleLineBreaker.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;
import java.util.*;
public class SimpleLineBreaker extends LineBreaker {
public static final String rcsid = "$Id: SimpleLineBreaker.java,v 1.11 2000/09/19 10:32:00 ajk Exp $";
public static boolean dbg = false;
final static void p(String s) { if(dbg) System.out.println(s); }
final static void pa(String s) { System.out.println(s); }
public int[] breakLines(LineInfo li) {
int n = li.getMax();
// Can't we do without this?
if(n==0) return new int[] {0, 0};
int xc = li.getCursor();
int l = li.getCenterLine();
int startmin = 0, startmax = 0;
boolean gotmin = false, gotmax = false;
boolean ret = false;
int curstart = l;
while(true) {
Vector res = new Vector();;
int lenn = 0;
int line = curstart;
for(int cur = 0; cur < n; cur += lenn) {
lenn = oneGoodLine(cur, n-cur, n-cur, line, li, 6);
p("OGL RET: "+cur+" "+n+" "+line+" "+li+" : "+lenn);
p("CURS: "+xc+" "+line+" "+l);
if(xc >= cur && xc <= cur+lenn) {
p("Cursor was on this line");
// We hit the cursor here.
if(line > l) {
// line greater -> move upwards. This is a maximum.
startmax = curstart; gotmax = true;
curstart -= (line - l);
} else if(line < l) {
// line smaller -> move down. This is a min.
startmin = curstart; gotmin = true;
curstart += (l - line);
}
if(line == l) ret = true;
}
res.addElement(new Integer(cur + lenn));
line ++;
}
if(gotmin && gotmax && startmax - startmin < 2) {
ret = true;
curstart = startmin;
} else {
if(gotmin && curstart <= startmin) {
curstart = startmin + 1;
}
if(gotmax && curstart >= startmax) {
curstart = startmax - 1;
}
}
p("Ret: curstart = "+curstart);
if(ret) {
int[] r = new int[res.size() + 1];
r[0] = curstart;
for(int i=1; i<r.length; i++) {
r[i] = ((Integer)res.elementAt(i-1)).intValue();
p("Line "+(i-1)+": "+r[i]);
}
return r;
}
}
}
/** Starting at x0, find a good amount of text to include in line l.
*/
int oneGoodLine(int x0, int guessn, int maxn, int l, LineInfo li,
int accept) {
int[] res = new int[2];
int minn = 0;
int splitpen = 0;
int best = 0;
int bestpen = 1000000;
while(true) {
li.widthPenalty(x0, guessn, l, res);
p("OGL iter: "+x0+" "+minn+" "+guessn+" "+maxn+" R: "+res[0]);
int widpen = res[1];
int pen = splitpen + widpen;
if(pen < bestpen) {
bestpen = pen; best = guessn;
}
// if(widpen <= accept) return best;
if(res[0] > 1000) {
p("Too wide");
maxn = guessn;
li.split(x0, guessn, x0+(guessn*1000/res[0]), res);
p("Split res: "+guessn+" "+res[0]+" "+minn);
if(guessn == res[0] || minn == guessn) return best;
guessn = res[0];
splitpen = res[1];
} else {
p("Too narrow");
minn = guessn;
li.split(x0+guessn, maxn-guessn,
x0+guessn+
((1000000/(res[0]+1) - 1000) * (maxn-guessn))/1000,
res);
res[0] += guessn;
if(guessn == res[0] || maxn == guessn) return best;
guessn = res[0];
splitpen = res[1];
}
if(minn >= maxn || guessn <= minn || guessn >= maxn) return best;;
}
}
}

View File

@@ -0,0 +1,91 @@
/*
SimpleLineInfo.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;
import java.util.*;
import java.io.*;
import java.awt.*;
/** A simple implementation of LineInfo, for plain strings in a list
* of fonts.
*/
public class SimpleLineInfo implements LineInfo {
public static final String rcsid = "$Id: SimpleLineInfo.java,v 1.8 2000/09/19 10:32:00 ajk Exp $";
public static boolean dbg = false;
static final void p(String s) { if(dbg) System.out.println(s); }
public interface Layout {
FontMetrics getFontMetrics(int line);
int getWidth(int line);
int getCenterLine();
}
String s;
Layout l;
int cursor;
SimpleLineInfo(String s, int cursor, Layout l) { this.s = s; this.l = l;
this.cursor = cursor;}
public int getMax() { return s.length(); }
public int getCursor() { return cursor; }
public int getCenterLine() { return l.getCenterLine(); }
public void split(int x0, int n, int sug, int[] ret) {
int ind = s.indexOf(' ', x0+1);
if(ind >= x0+n || ind < 0) {
// split word
ind = 1;
ret[0] = 1;
ret[1] = 500;
} else {
while(true) {
int ind2 = s.indexOf(' ', ind+1);
if(ind2 < 0) break;
if(ind2 >= x0+n) break;
if(ind2 >= sug) break;
ind = ind2;
}
ret[0] = ind - x0;
ret[1] = 0;
}
p("Split: "+x0+" "+n+" "+sug+" "+ret[0]+" "+ret[1]);
}
public void widthPenalty(int x0, int n, int line, int[] ret) {
p("WP: "+x0+" "+n+" "+line);
String subs = s.substring(x0, x0+n).trim();
int w = l.getFontMetrics(line).stringWidth(subs);
int want = l.getWidth(line);
ret[0] = (1000*w) / want;
if(w > want) {
ret[1] = (w-want) * (w-want) / 3;
} else {
ret[1] = (want - w) / 2;
}
p("WP: "+x0+" "+n+" "+line+" "+want+" "+w+" Ret: "+ret[0]+" "+ret[1]);
}
}

View File

@@ -0,0 +1,63 @@
/*
SoundScroll.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;
import java.io.*;
import java.util.*;
/** A scroll of sound.
* The fact that you need to specify the duraction when creating is not good but
* we can fix that later somehow.
*/
public class SoundScroll extends Scroll {
public static final String rcsid = "$Id: SoundScroll.java,v 1.3 2000/09/19 10:32:00 ajk Exp $";
// public abstract void preparePlayer();
/** XXX change to play(Span) and get the next at runtime?
*/
protected SoundScroll(String i) { super(i); }
Vector obses = new Vector();
public void addObs(SoundObs o) {
obses.addElement(o);
}
protected void inform(int event, long l) {
for(int i = 0; i<obses.size(); i++)
((SoundObs)obses.elementAt(i)).playingAt(event,l);
}
// Cheat: it's currently just a file.
File f;
public File getFile() { return f; }
long d;
public long getDurationNanoseconds() { return d; }
public SoundScroll(String id, File fn, long dur) {
super(id);
f = fn;
d = dur;
}
public SoundScroll(String id, String fn, long dur) {
super(id);
f = new File(fn);
d = dur;
}
}

178
Java/media/Span.java Normal file
View File

@@ -0,0 +1,178 @@
/*
Span.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 Tuomas Lukka
*/
package org.gzigzag;
import java.util.*;
/** An address span in the stable media streams.
* Currently just a silly pointer but will be later extended
* to include full tumblers.
* <p>
* Spans are immutable, just like Strings: all the verb-like method
* like join and split return new Span objects.
*/
public class Span {
public static final String rcsid = "$Id: Span.java,v 1.9 2001/03/11 19:14:38 veparkki Exp $";
public static final boolean dbg = false;
static final void p(String s) { if(dbg) System.out.println(s); }
Address a1;
Address a2;
public Address getStart() { return a1; }
public Address getEnd() { return a2; }
public long getStartOffs() { return a1.offset; }
public long getEndOffs() { return a2.offset; }
public static Span parse(String str) {
StringTokenizer st = new StringTokenizer(str, ":");
if(st.countTokens() != 2)
throw new ZZError("Wrong number of ':' toks in '"+str+"'");
Span s = new Span();
s.a1 = Address.parse(st.nextToken());
s.a2 = Address.parse(st.nextToken());
if(!s.a1.getStream().equals(s.a2.getStream()))
throw new ZZError("Must be same stream for now '" + str
+"' '"+s.a1.getStream()+"' '"+s.a2.getStream()+"'");
return s;
}
public static Span create(Address i1, Address i2) {
Span s = new Span();
s.a1 = i1; s.a2 = i2;
return s;
}
public String toString() {
return a1+":"+a2;
}
public boolean overlaps(Span s) {
if(a2.lessThan(s.a1) || s.a2.lessThan(a1)) return false;
return true;
}
/** The string pointed to by the span.
*/
public String getString(ZZCell c) {
Scroll scr //= c.getSpace().getStringScroll(a1.getStream());
= Scroll.obtain((c==null?null:c.getSpace()),
a1.getStream());
p("Scroll: "+scr);
if(scr instanceof StringScroll) {
StringScroll s = (StringScroll) scr;
int o1 = (int)a1.getOffs();
int o2 = (int)a2.getOffs();
if(o1 > o2) {
// This is allowed
if(o1-o2 == 1) return "";
// This is not
throw new ZZError("Too negative span "+ this);
}
return s.getString(a1.getOffs(),
(int)(o2-o1)+1);
} if(scr instanceof ByteScroll) {
ByteScroll s = (ByteScroll)scr;
return s.getString(a1.getOffs(),
(int)(a2.getOffs()-a1.getOffs())+1);
}
if(scr instanceof CharScroll) {
CharScroll s = (CharScroll)scr;
return s.getString(a1.getOffs(),
(int)(a2.getOffs()-a1.getOffs())+1);
}
if(scr instanceof SoundScroll) {
return "SND " + toString();
}
return "???";
}
public Span adjustEdge(int edge, long units) {
Address adj = (edge==0?a1:a2);
adj = Address.streamOffs(adj.getStream(), adj.getOffs()+units);
Address x = (edge==0?adj:a1);
Address y = (edge==1?adj:a2);
return create(x,y);
}
public Span[] adjustEdgeLocked(long units, Span other, long allowfudge) {
Address adj = a2;
adj = Address.streamOffs(adj.getStream(), adj.getOffs()+units);
long d = adj.getOffs() - other.a1.getOffs();
if(d<0) d = -d;
if(d>allowfudge)
return new Span[] {
create(a1, adj), other
};
return new Span[] {
create(a1, adj),
create(Address.streamOffs(adj.getStream(), adj.getOffs()+1),
other.a2)
};
}
public Span[] splitInHalf() {
long off = (a1.getOffs() + a2.getOffs()) / 2;
return new Span[] {
create(a1, Address.streamOffs(a1.getStream(), off-1)),
create(Address.streamOffs(a1.getStream(), off), a2),
};
}
public Span join(Span next, long allowfudge) {
if(! a2.getStream().equals(next.a1.getStream()))
return null;
long diff = a2.getOffs() - next.a1.getOffs();
if(diff<0) diff = -diff;
if(diff > allowfudge)
return null;
return create(a1, next.a2);
}
/** Does exactly what substring does to String.
* This is in opposition to the other span routines
* in which the last address is given, not one beyond!
*/
public Span subSpan(int o1, int o2) {
return create(a1.addOffs(o1), a1.addOffs(o2-1));
}
public Span subSpan(int o1) {
return create(a1.addOffs(o1), a2);
}
/** Whether this span can be joined with the other span.
*/
public boolean isAppendable(Span s) {
return a2.getStream().equals(s.a1.getStream())
&& a2.getOffs() == s.a1.getOffs()-1;
}
// XXX Check?
public Span append(Span s) {
return create(a1, s.a2);
}
public long length() {
return a2.getOffs() - a1.getOffs() + 1;
}
}

45
Java/media/SpanSet.java Normal file
View File

@@ -0,0 +1,45 @@
/*
SpanSet.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;
import java.util.*;
import java.awt.*;
/** An interface to Flobs that contain spans. This is more a Map than
* a Set...
* XXX Extend with links, i.e. one-level indirection between
* overlaps.
*/
public interface SpanSet {
String rcsid = "$Id: SpanSet.java,v 1.4 2000/09/19 10:32:00 ajk Exp $";
/** Add a span.
*/
void addSpan(Span s, Object obj);
/** Get the objects corresponding to the spans entered that overlap
* the given span. XXX Should be enumeration?
*/
Object[] overlaps(Span s);
}

54
Java/media/SpanTree.java Normal file
View File

@@ -0,0 +1,54 @@
/*
SpanTree.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 Tuomas Lukka
*/
package org.gzigzag;
import java.util.*;
/** A structure for putting spans in and obtaining the
* matching spans. Not actually a tree yet XXX!
*/
public class SpanTree implements SpanSet {
public static final String rcsid = "$Id: SpanTree.java,v 1.4 2000/09/19 10:32:00 ajk Exp $";
Vector v = new Vector();
Vector vo = new Vector();
public void addSpan(Span s, Object uo) {
v.addElement(s);
vo.addElement(uo);
}
public Object[] overlaps(Span s) {
Vector res = new Vector();
int ind = 0;
for(Enumeration e = v.elements(); e.hasMoreElements();) {
Span s0 = (Span)e.nextElement();
if(s.overlaps(s0)) {
res.addElement(vo.elementAt(ind));
}
ind++;
}
Object[] r = new Object[res.size()];
for(int i=0; i<r.length; i++)
r[i] = res.elementAt(i);
return r;
}
}

289
Java/media/Stream.java Normal file
View File

@@ -0,0 +1,289 @@
/*
Stream.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;
import java.util.*;
/** Some routines to handle plain text streams.
*/
public class Stream {
public static final String rcsid = "$Id: Stream.java,v 1.9 2000/12/04 13:01:10 tjl 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); }
static public boolean move(ZZCursor curs, String dim, int nchars) {
return move(curs, dim, nchars, 1);
}
/** Move by an offset in a stream string.
* The last position of the stream is actually one beyond
* the last character.
* @param curs The cursor to move
* @param dim The dimension the stream is along
* @param nchars The number of characters to move. May be
* positive or negative.
* @param lookingDir The direction you want to look from
* the cursor. Matters when the cursor
* is at the edge of two cells and
* positive here means to put it into the
* beginning of the next cell and negative
* to the end of the preceding cell.
* @return false if operation goes past beginning or end.
*/
static public boolean move(ZZCursor curs, String dim, int nchars,
int lookingDir) {
int o = curs.getOffs() + nchars;
ZZCell nc = curs.get();
if(nc == null)
throw new ZZError("Moving stream cursor!");
while(nc != null && o < 0) {
nc = nc.s(dim, -1);
if(nc != null)
o += nc.getText().length();
}
ZZCell prev = null;
while(nc != null && o > nc.getText().length() -
(lookingDir > 0 ? 1 : 0)) {
o -= nc.getText().length();
prev = nc;
nc = nc.s(dim);
}
if(nc == null) {
if(o != 0)
return false;
else {
nc = prev;
o = nc.getText().length();
}
}
if(o < 0)
throw new ZZError("WHAT? WHAT? WHAT? Neg offset in move");
curs.set(nc); curs.setOffs(o);
return true;
}
/** Find a position along a stream with the given string.
* Currently relatively inefficient, unless the cells are relatively
* long.
*/
static public boolean find(ZZCursor from, String dim, int dir,
boolean includeThis, String text) {
// First, see if there is a match inside the current cell,
// done carefully.
ZZCell c = from.get();
int offs = from.getOffs();
p("Find: "+c+" "+offs+" "+dim+" "+dir+" "+includeThis+" '"+text+"'");
String txt = c.getText();
char first = text.charAt(0);
if(!includeThis) offs += dir;
if(dir > 0) {
while(c != null) {
while((offs = txt.indexOf(first, offs)) >= 0) {
if(isMatch(c, offs, text, dim, from))
return true;
offs ++;
}
c = c.s(dim, dir);
if(c != null) {
txt = c.getText();
offs = 0;
}
}
} else {
while(c != null) {
p("Findnegloop: "+c);
while((offs = txt.lastIndexOf(first, offs)) >= 0) {
p("Findnegloop: try "+ offs);
if(isMatch(c, offs, text, dim, from))
return true;
offs --;
}
c = c.s(dim, dir);
if(c != null) {
txt = c.getText();
offs = txt.length()-1;
}
}
}
p("Not found!");
// Then, we're looking at whole cells the rest of the way.
return false;
}
/** Check to see whether the text in the stream is equal to this text.
*/
static public boolean isMatch(ZZCell c, int offs, String text, String dim) {
return isMatch(c, offs, text, dim, null);
}
/** Check whether the match is correct and set the cursor if it is.
*/
static boolean isMatch(ZZCell c, int offs, String text,
String dim, ZZCursor from)
{
p("Match: "+c+" "+offs+" '"+text+"' "+dim);
p("Matching csubstr: '"+c.getText().substring(offs)+"'");
ZZCell origc = c;
int origoffs = offs;
while(c != null) {
String ct = c.getText().substring(offs);
if(ct.length() >= text.length()) {
if(ct.substring(0, text.length()).equals(text)) {
if(from != null) {
from.set(origc);
from.setOffs(origoffs);
}
p("TRUE!");
return true;
} else return false;
}
if(!text.substring(0, ct.length()).equals(ct)) return false;
text = text.substring(ct.length());
c = c.s(dim);
offs = 0;
}
return false;
}
static public String getString(ZZCursor curs, String dim, int n) {
ZZCell c = curs.get();
int offs = curs.getOffs();
StringBuffer res = new StringBuffer();
while(n > 0) {
String txt = c.getText();
if(offs > 0) txt = txt.substring(offs);
if(n <= txt.length()) {
res.append(txt.substring(0, n));
return res.toString();
}
res.append(txt);
n -= txt.length();
offs = 0;
c = c.s(dim);
if(c == null) return null;
}
return null;
}
/** A class for moving along the stream, collecting characters.
* Part of the interface might be abstracted to an interface like
* java.text.CharacterIterator, but in a slightly more restricted subset.
* <p>
* This class considers the end of the stream the location when it
* can't return any characters any more + 1.
*/
static public class Iterator {
final ZZCursorVirtual c;
final String dim;
Iterator(ZZCursorVirtual c, String dim) { this.c = c; this.dim = dim; }
public void set(ZZCursor c) { this.c.set(c); }
/** Get the current iterator position.
* @return The current position. The position should not be modified.
*/
public ZZCursor get() { return c; }
public boolean move(int nsteps) {
if(!Stream.move(c, dim, nsteps)) return false;
if(c.get().s(dim) == null)
// XXX Is this right?
if(c.get().getText().length() <= c.getOffs())
return false;
return true;
}
public boolean move(int nsteps, int lookingDir) {
if(!Stream.move(c, dim, nsteps, lookingDir)) return false;
if(c.get().s(dim) == null)
if(c.get().getText().length() <= c.getOffs())
return false;
return true;
}
public void start() {
c.set(c.get().h(dim));
c.setOffs(0);
}
public void end() {
ZZCell cl = c.get().h(dim, 1);
c.set(cl);
c.setOffs(cl.getText().length());
}
public void end(int dir) {
if(dir < 0) start(); else end();
}
public boolean find(int dir, boolean includeThis, String text) {
return Stream.find(c, dim, dir, includeThis, text);
}
/** Get the current character.
* Returns -1 for the end-of-stream.
*/
public char getChar() {
return getChar(0);
}
public char getChar(int offs) {
ZZCursorVirtual c1 = new ZZCursorVirtual(c);
// Move even if zero, to get the right
// directional position.
if(!Stream.move(c1, dim, offs, 1)) return (char)-1;
String str = c1.get().getText();
if(c1.getOffs() >= str.length()) return (char)-1;
return str.charAt(c1.getOffs());
}
public String getString(int n, int dir) {
// SLOW!!!!
ZZCursorVirtual c1 = new ZZCursorVirtual(c);
if(dir < 0) {
if(!Stream.move(c1, dim, -n)) return null;
}
return Stream.getString(c1, dim, n);
}
}
static public Iterator getIterator(ZZCursor c, String dim) {
return new Iterator(new ZZCursorVirtual(c), dim);
}
}

View File

@@ -0,0 +1,310 @@
/*
StringScroll.java
*
* Copyright (c) 1999-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,
* multiformat and UTF-8 format support by Antti-Juhani Kaijanaho
*/
package org.gzigzag;
import java.util.*;
import java.io.*;
/** A scroll of chars. May later have holes etc. from expunging,
* but basically the point is that the address to content mapping
* is stable and will not change whatever is done.
*
* The new StringScroll format is based on UTF-8. After the initial
* signature described shortly, the scroll writable is a plain UTF-8
* sequence. The whole StringScroll should be
*
* The signature of the new StringSroll format is the octet sequence
* given in the "signature" attribute.
*/
public class StringScroll extends Scroll{
public static final String rcsid = "$Id: StringScroll.java,v 1.8 2001/02/09 08:30:54 ajk Exp $";
/** Number of characters in a new-format "record". Records are
* implicit in the actual file; they are explicit only in the
* in-memory index. */
static int recordsize = 1000;
private Writable f;
private boolean readonly;
private interface SSImpl {
char[] get(long start, int n);
long curEnd();
long append(char[] bytes);
}
private SSImpl ss;
private byte[] signature = new byte[]{ (byte) 0xef, (byte) 0xbb, (byte) 0xbf,
(byte) 0x20, (byte) 0x20, (byte) 0x5a,
(byte) 0x5a, (byte) 0x53, (byte) 0x53,
(byte) 0x2d, (byte) 0x30, (byte) 0x0a };
private synchronized void determineFormat() {
boolean newformat;
if (f.length() == 0) {
if (readonly) {
newformat = false;
} else {
f.write(0, signature);
newformat = true;
}
} else if (f.length() < signature.length) {
newformat = false;
} else {
byte[] sig = f.read(0, signature.length);
// assume it's new format; check signature and note it's
// old format if signature does not match
newformat = true;
for (int i = 0; i < sig.length; i++) {
if (sig[i] != signature[i]) {
newformat = false;
break;
}
}
}
if (newformat) ss = new UTF8Format(); else ss = new OldFormat();
}
public StringScroll(String id, Writable w, boolean ro) {
super(id);
this.readonly = ro;
f = w;
determineFormat();
}
/** Get an array of characters of length n starting from the
* startth (or so) character. */
public synchronized char[] get(long start, int n) {
return ss.get(start, n);
}
// XXX ENCODING - currently 2 bytes / char :(
public String getString(long start, int n) {
char[] b = get(start,n);
String s = new String(b);
// p("GETS: "+start+" "+n+" "+b+" '"+s+"'");
return s;
}
public long curEnd() {
return ss.curEnd();
}
public synchronized long append(String b) {
p("Append - length: "+b.length());
return append(b.toCharArray());
}
public synchronized long append(char[] bytes) {
return ss.append(bytes);
}
private class UTF8Format implements SSImpl {
/** A private copy of recordsize in order to avoid changing
the parent recordsize from messing up the index. */
private int recordsize;
/** We store in the index the offset - counting from the
writable's start - of each recordsize character so that
index[n] contains the offset of the character at the
address n*recordsize. */
private Vector index;
/** Cache the address to be given to the next appended character. */
private long nextAddr;
/** Put an offset for an address (which must be a multiple of
* recordsize) into the index. */
private void putOffs(long addr, long offs) {
if (addr % recordsize != 0) {
throw new ZZError("Address is not a record boundary");
}
// FIXME: Vector can index only using int's. This is bad.
long idx = addr / recordsize;
if (idx > Integer.MAX_VALUE) {
throw new ZZError("Internal error: idx too big");
}
if (idx >= index.size()) index.setSize((int) idx + 1);
index.setElementAt(new Long(offs), (int)idx);
// sanity check
if (lookupOffs(addr) != offs) {
throw new ZZError("Internal error: index add failure");
}
}
/** Look offset up by address (must be a multiple of
* recordsize). */
private long lookupOffs(long addr) {
if (addr % recordsize != 0) {
throw new ZZError("Address is not in index");
}
// FIXME: Vector can index only using int's. This is bad.
long idx = addr / recordsize;
if (idx > Integer.MAX_VALUE) {
throw new ZZError("Internal error: idx too big");
}
return ((Long) index.elementAt((int) idx)).longValue();
}
public UTF8Format() {
index = new Vector();
recordsize = StringScroll.this.recordsize;
// Build the index by scanning the whole scroll
long offs = signature.length;
long addr = 0;
while (offs < f.length()) {
UTF8Char uc = new UTF8Char(f, offs);
if (addr % recordsize == 0) {
putOffs(addr, offs);
}
++addr;
offs += uc.b.length;
}
nextAddr = addr;
}
// Implementation of SSImpl
public char[] get(long start, int n) {
char[] rv = new char[n];
int acc = 0;
// The loop body will read at most n characters starting
// from address start+acc by slurping the whole record and
// scanning there. The body is run repeatedly until we
// have exactly n characters in rv.
while (acc < n) {
// Step 1: Read the current record completely into memory
long recstart = (start + acc) - (start + acc) % recordsize;
long recoffs = lookupOffs(recstart);
byte[] record = f.read(recoffs, recordsize);
// Step 2: Scan the record for the start address
long sad = recstart;
long sof = 0;
while (sof < record.length) {
if (sad == start + acc) break;
UTF8Char uc = new UTF8Char(record, (int) sof);
sof += uc.b.length;
sad++;
}
// Step 3: Convert at most n bytes and at most to the
// end of the record from record[sad] into rv, using acc as
// the rv index.
for (int i = (int) sof; i < record.length;) {
if (acc == n) break;
UTF8Char uc = new UTF8Char(record, i);
rv[acc++] = uc.c;
i += uc.b.length;
}
}
return rv;
}
public long curEnd() {
return nextAddr;
}
public long append(char[] chs) {
// We want to avoid doing unnecessary writes. Therefore,
// we first build an array of UTF-8 characters. Then we
// flatten it into an onedimensional array containing the
// UTF-8 representations in a row and write that.
UTF8Char[] ucs = new UTF8Char[chs.length];
long rv = nextAddr;
int len = 0;
for (int i = 0; i < chs.length; i++) {
ucs[i] = new UTF8Char(chs[i]);
len += ucs[i].b.length;
}
byte[] block = new byte[len];
int bi = 0;
for (int i = 0; i < ucs.length; i++) {
// Update index
if ((nextAddr + i) % recordsize == 0) {
putOffs(nextAddr + i, f.length() + bi);
}
// Copy UTF-8 octets to block.
for (int j = 0; j < ucs[i].b.length; j++) {
block[bi++] = ucs[i].b[j];
}
}
f.write(f.length(), block);
nextAddr += ucs.length;
if (nextAddr - nextAddr % recordsize
!= (index.size() - 1) * recordsize) {
throw new ZZError("Internal error: index not consistent");
}
return rv;
}
}
private class OldFormat implements SSImpl {
public synchronized char[] get(long start, int n) {
byte[] b = f.read(start*2, n*2);
char[] c = new char[n];
for(int i=0; i<n; i++)
c[i] = (char)((b[i*2]<<8) | (b[i*2+1]<<0));
return c;
}
public long curEnd() {
return f.length() / 2;
}
public synchronized long append(char[] bytes) {
if(readonly) throw new ZZError("Can't append to readonly scroll");
byte[] byt = new byte[bytes.length * 2];
for(int i=0; i<bytes.length; i++) {
byt[i*2] = (byte)((bytes[i] >> 8) & 0xff);
byt[i*2+1] = (byte)((bytes[i] >> 0) & 0xff);
}
// XXX race?
long c = f.length();
if(c%1 != 0) throw new Error("Argh! Odd number of bytes");
f.write(c, byt);
return c / 2;
}
}
}

189
Java/media/TestLines.java Normal file
View File

@@ -0,0 +1,189 @@
/*
TestLines.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;
import java.util.*;
import java.io.*;
import java.awt.*;
/** Some tests for line breaking.
*/
public class TestLines {
public static final String rcsid = "$Id: TestLines.java,v 1.10 2001/02/23 15:01:10 tjl Exp $";
static final boolean dbg = false;
static final void p(String s) { if(dbg) System.out.println(s); }
Frame f = new Frame();
/* Jikes won't eat this any more in new versions due to äö.
static final String str = "Pikku stringi testataksemme rivinpilkkomista. "+
"Tällä kertaa kirjoitammekin sen suomeksi - englanninkielisiä lukijoitamme "+
"varmaankin hämmästyttää. Etenkin ääkköset. Tämä merkkijono piirretään "+
"näytölle monta kertaa erilaisena, siis \"kursori\" eri kohdalle laitettuna. "+
"Tästä voidaan sitten nähdä, toimiiko rivien pilkkominen palasiksi oikein. " +
"Itse asiassa merkkijonoa tulee vielä jonkin verran pidentää koska muuten " +
"emme ehdi nähdä kaikkia efektejä. Hauskaahan tästä tulee vasta sitten " +
"kun voimme oikeasti muokata näin näytettyä tekstiä mutta tämä testi on " +
"tärkeä askel siihen suuntaan mentäessä."
;
*/
static final String str = "lsjifs lefjis elfj sef jsle fjslf ";
class MyLayout implements SimpleLineInfo.Layout {
int c;
MyLayout(int c) { this.c = c; }
public FontMetrics getFontMetrics(int line) {
return fm[find(c, line)];
}
public int getWidth(int line) {
return width(c, line);
}
public int getCenterLine() { return 0; }
}
public static void main(String[] argv) {
TestLines t = new TestLines();
t.br();
}
int NTESTS = 1;
int[][] lines = new int[NTESTS][];
Font[] fonts = new Font[64];
FontMetrics[] fm = new FontMetrics[fonts.length];
{ for(int i=0; i<fonts.length; i++) {
fonts[i] = new Font("SansSerif", Font.PLAIN, i+1);
// fm[i] = Toolkit.getDefaultToolkit().getFontMetrics(fonts[i]);
fm[i] = f.getFontMetrics(fonts[i]);
}
}
/** Give the index to the fonts[] array for the given line of
* the given case.
*/
int find(int cas, int line) {
if(cas == 0)
return 10 + line;
else if(cas == 1) {
int w = 30 - line;
if(w < 3) return 3;
return w;
} else if(cas == 2) {
return (int)(8 + 10 / (1 + (line * line)/30.0));
}
return -1;
}
int width(int cas, int line) {
if(cas == 0) {
int w = 100 + 20 * line;
if(w > 400) w = 400;
return w;
} else if(cas == 1) {
int w =300 - 20 * line;
if(w < 50) return 50;
return w;
} else if(cas == 2) {
return (int)(100 + 200.0 / (1 + (line * line)/40.0));
}
return -1;
}
int curs = 0;
int dir = 1;
public TestLines() {
}
public void br() {
Component cp;
f.add(
cp= new Component() {
Image cache;
public void paint(Graphics g0) {
Thread.dumpStack();
if(lines == null) return;
if(cache==null) {
cache = createImage(800,800);
}
Graphics g = cache.getGraphics();
g.setColor(getBackground());
g.fillRect(0, 0, 800, 800);
for(int set = 0; set < lines.length; set++) {
if(lines[set] == null) return;
int cur = 0;
int x = 20 + 400 * set;
int y = 100;
int line = lines[set][0];
for(int i=0; i<lines[set].length-1; i++) {
int cas = 2;
FontMetrics metr = fm[find(cas, line)];
y += metr.getLeading() + metr.getAscent();
g.setColor(Color.black);
g.setFont(fonts[find(cas, line)]);
g.drawString(str.substring(cur, lines[set][i+1]).trim(), x, y);
if(curs >= cur && curs < lines[set][i+1]) {
// Trim is wrong :(
int cw = metr.stringWidth(str.substring(cur, curs).trim());
g.setColor(Color.blue);
g.drawLine(x+cw, y-metr.getAscent(),
x+cw, y+metr.getDescent());
}
g.setColor(Color.red);
g.drawLine(x+width(cas, line), y-metr.getAscent(),
x+width(cas,line), y+metr.getDescent());
cur = lines[set][i+1];
y += metr.getDescent();
line ++;
}
}
g0.drawImage(cache, 0, 0, null);
}
});
f.setSize(800,800);
SimpleLineBreaker slb = new SimpleLineBreaker();
f.show();
while(true) {
for(int i=0; i<lines.length; i++) {
int[] brk = slb.breakLines(
new SimpleLineInfo(str, curs, new MyLayout(2)));
lines[i] = brk;
}
cp.paint(cp.getGraphics());
for(int k = 0; k<5; k++) {
curs += dir;
if(curs <= 0 || curs >= str.length()-5) dir = -dir;
}
}
}
}

245
Java/media/UTF8Char.java Normal file
View File

@@ -0,0 +1,245 @@
/*
UTF8Char.java
*
* 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 Antti-Juhani Kaijanaho
*/
package org.gzigzag;
import java.io.*;
public class UTF8Char {
public static final String rcsid = "$Id: UTF8Char.java,v 1.3 2001/03/21 10:01:21 ajk Exp $";
public char c;
public byte[] b;
/* UTF-8 <-> Java char conversion is table-driven. This code is
* adapted from CatDVI. */
private static class ConvOctet {
/** The number of variable bits, 1-8 */
public int numbits;
/** The template for this octet */
public byte octet;
public ConvOctet(int n, byte o) {
numbits = n;
octet = o;
}
public ConvOctet(int n, int o) {
this(n, (byte) o);
}
}
private static class ConvEntry {
/** Maximum UCS-4 value encodable using this entry. */
public char maxval;
/** Info on each octet. */
ConvOctet[] template;
public ConvEntry(char maxval, ConvOctet[] template) {
this.maxval = maxval;
this.template = template;
}
public ConvEntry(int maxval, ConvOctet[] template) {
this((char) maxval, template);
}
public final int len() { return template.length; }
}
private static final ConvEntry[] convtbl =
new ConvEntry[] {
new ConvEntry (0x7f,
new ConvOctet[] {
new ConvOctet(7, 0)
}),
new ConvEntry (0x7ff,
new ConvOctet[] {
new ConvOctet(5, 0xc0),
new ConvOctet(6, 0x80)
}),
new ConvEntry (0xffff,
new ConvOctet[] {
new ConvOctet(4, 0xe0),
new ConvOctet(6, 0x80),
new ConvOctet(6, 0x80)
}),
new ConvEntry (0x1fffff,
new ConvOctet[] {
new ConvOctet(3, 0xf0),
new ConvOctet(6, 0x80),
new ConvOctet(6, 0x80),
new ConvOctet(6, 0x80)
}),
new ConvEntry (0x3ffffff,
new ConvOctet[] {
new ConvOctet(2, 0xf4),
new ConvOctet(6, 0x80),
new ConvOctet(6, 0x80),
new ConvOctet(6, 0x80),
new ConvOctet(6, 0x80)
}),
new ConvEntry (0x7fffffff,
new ConvOctet[] {
new ConvOctet(1, 0xfc),
new ConvOctet(6, 0x80),
new ConvOctet(6, 0x80),
new ConvOctet(6, 0x80),
new ConvOctet(6, 0x80),
new ConvOctet(6, 0x80),
})
};
/** Convert a Java char into an UTF-8 octet stream. */
public static byte[] charToUTF8(char c) {
// Stage 1: Determine which ConvEntry to use. We use the
// maxval attribute for this: the ConvEntry with smallest
// maxval where the char fits is used.
ConvEntry e = null;
for (int i = 0; i < convtbl.length; i++) {
if (c <= convtbl[i].maxval) {
e = convtbl[i];
break;
}
}
if (e == null) throw new ZZError("character has no UTF-8 representation");
// Stage 2: Do the conversion from right to left.
byte[] rv = new byte[e.len()];
for (int i = rv.length - 1; i >= 0; --i) {
/* At each iteration we first apply the initial bit
pattern and then patch the rest with the indicated
number of lower-order bits in code. When we are done,
we shift these out. */
int numbits = e.template[i].numbits;
rv[i] = e.template[i].octet;
rv[i] |= ((1 << numbits) - 1) & c;
c = (char) (c >> numbits);
}
return rv;
}
private static class UTF8ToCharRV {
public char c;
public int len;
public UTF8ToCharRV(char c, int len) {
this.c = c;
this.len = len;
}
}
public static class InvalidChar extends ZZError {
public InvalidChar(String s) { super(s); }
}
/** Determine the ConvEntry to be used. We determine this
using the first octet's template. */
private static ConvEntry UTF8ConvEntry(byte o) {
for (int i = 0; i < convtbl.length; i++) {
byte b = (byte) (o & ~((1 << convtbl[i].template[0].numbits) - 1));
if (convtbl[i].template[0].octet == b) {
return convtbl[i];
}
}
return null;
}
/** Find out the length of an UTF-8 sequence based on the first
* octet. */
public static int UTF8Len(byte o) {
ConvEntry e = UTF8ConvEntry(o);
if (e == null) throw new InvalidChar("this is no UTF-8 character");
return e.len();
}
private static UTF8ToCharRV UTF8ToChar(byte[] array, int start) {
ConvEntry e = UTF8ConvEntry(array[start]);
if (e == null) return null;
// Decode the octet stram.
char rv = 0;
for (int i = 0; i < e.template.length; i++) {
byte mask = (byte) ((1 << e.template[i].numbits) - 1);
byte fixed = (byte) (array[start + i] & ~mask);
byte bits = (byte) (array[start + i] & mask);
if (fixed != e.template[i].octet) throw new ZZError("Invalid UTF-8 character");
rv = (char) ((rv << e.template[i].numbits) | bits);
}
return new UTF8ToCharRV(rv, e.template.length);
}
private static void init(UTF8Char c, UTF8ToCharRV r, byte[] a, int start) {
if (r == null) throw new InvalidChar("Invalid UTF-8 character");
c.c = r.c;
c.b = new byte[r.len];
for (int i = 0; i < c.b.length; i++) {
byte b = a[start + i];
c.b[i] = b;
}
}
private UTF8Char(UTF8ToCharRV r, byte[] a, int start) {
init(this, r, a, start);
}
public UTF8Char tryFromUTF8(byte[] a, int start) {
UTF8ToCharRV r = UTF8ToChar(a, start);
if (r == null) return null;
return new UTF8Char(r, a, start);
}
/** Create an UTF-8 char from the byte stream (bytes after the
* character are ignored). You can determine how many bytes was
* used by invoking UTF8Len on the created object. */
public UTF8Char(byte[] a) { this(a, 0); }
public UTF8Char(byte[] a, int start) {
init(this, UTF8ToChar(a, start), a, start);
}
public UTF8Char(char c) {
this.c = c;
b = charToUTF8(c);
}
public UTF8Char(Writable w, long offs) {
long len = (w.length() - offs < 6) ? w.length() - offs : 6;
byte[] a = w.read(offs, (int)len);
init(this, UTF8ToChar(a, 0), a, 0);
}
static public void main(String argv[]) {
FileWritable fw;
try {
RandomAccessFile raf = new RandomAccessFile("test.utf8", "rw");
fw = new FileWritable(raf);
} catch (IOException e) {
throw new ZZError("" + e);
}
long offs = 0;
while (offs < fw.length()) {
UTF8Char uc = new UTF8Char(fw, offs);
System.out.println("H: " + (long) uc.c);
offs += uc.b.length;
}
}
}

655
Java/media/VStream.java Normal file
View File

@@ -0,0 +1,655 @@
/*
VStream.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;
import java.util.*;
import java.awt.*;
/** Utility functions for vstreams.
* NOTE! USE THE VirtualCursor OBJECTS ONLY SHORT-TERM, put stuff
* in the structure
* for longer term.
* ANY call to VStream.* routines invalidates all VirtualCursor objects pointing
* to that vstream.
* <p>
* TODO: d.markup, active references to parts of another vstream
* (via including complete vstream objects!)
* <p>
* The vstream format is simple: the headcell on d.2 is a dummy cell
* that is never moved (so that it remains easy to refer to the vstream).
* Note that cells with the span length of 0 (i.e. end less than start)
* are explicitly ALLOWED.
* <p>
* Important policy decision pending: what to do about non-span cells
* in vstreams?
* <p>
* In addition to pointers, we'll probably need spans (of vstream) to complement
* them, for formatting etc.
* @deprecated This is a bit old now - see e.g. Nile and Stream.java.
*/
public class VStream {
public static final String rcsid = "$Id: VStream.java,v 1.28 2000/11/24 16:24:21 tjl Exp $";
public static final boolean dbg = false;
static final void p(String s) { if(dbg) System.out.println(s); }
static String dim = "d.2";
static public class Rend {
public Span[] spans;
public String[] strs;
public ZZCell[] cells;
public int[][] marks;
public ZZCell[][] markcells;
}
static public final void assertOffs(ZZCursor c) {
if(c.getOffs() == c.NO_OFFSET)
throw new ZZError("Need cursor offset");
}
static public ZZCursorVirtual add(ZZCursor vc, int o) {
assertOffs(vc);
ZZCell c = vc.get();
o += vc.getOffs();
// XXX Do we need to enforce spans?
while(o < 0) {
c = c.s(dim, -1);
if(c == null || c.getSpan() == null) return null;
o += (int)c.getSpan().length();
}
while(o > c.getSpan().length()) {
o -= (int)c.getSpan().length();
c = c.s(dim, 1);
if(c == null || c.getSpan() == null) return null;
}
return new ZZCursorVirtual(c, o);
}
static public String error(ZZCell c) {
ZZCell s = c.h(dim, -1);
if(s.getSpan() != null) return "First cell has span";
while((s=s.s(dim, 1))!=null) {
if(s.getSpan() == null)
return "No span in cell!";
}
return null;
}
static public void check(ZZCell c) {
if(!dbg) return;
String e = error(c);
System.out.println("VSTREAM DUMP");
for(ZZCell s = c.h(dim, -1); s != null;
s = s.s(dim, 1)) {
System.out.println("'"+s.getText()+"' from span: "+s.getSpan());
}
if(e == null) return;
throw new ZZError("VStream error in stream! '"+e+"' dumped");
}
// The first cell is the second of the rank.
static public ZZCursor start(ZZCell c) {
c = c.h(dim, -1).s(dim, 1);
if(c==null) return null;
return new ZZCursorVirtual(c, 0);
}
static public ZZCursor end(ZZCell c) {
c = c.h(dim, 1);
return new ZZCursorVirtual(c, c.getText().length());
}
static public void create(ZZCell c, String dim, int dir, Span s) {
ZZCell n = c.N(dim, dir).N("d.2", 1);
if(s != null)
n.setSpan(s);
}
/** Find the first instance of the given character
* that occurs at position p or after.
*/
static public ZZCursor findChar(char c, ZZCursor p) {
assertOffs(p);
String s = p.get().getText();
int io = s.indexOf(c, p.getOffs());
if(io != -1) return new ZZCursorVirtual(p.get(), io);
ZZCell cur = p.get();
while((cur = cur.s(dim, 1))!=null) {
io = cur.getText().indexOf(c);
if(io != -1) return new ZZCursorVirtual(cur, io);
}
return null;
}
/** Return the string between two positions, inclusive.
*/
static public String getStr(ZZCursor p1, ZZCursor p2) {
p("getStr: "+p1+" "+p2);
assertOffs(p1); assertOffs(p2);
StringBuffer s = new StringBuffer();
if(p1.get() == p2.get()) {
return p1.get().getText().substring(p1.getOffs(), p2.getOffs()+1);
}
s.append(p1.get().getText().substring(p1.getOffs()));
ZZCell c = p1.get();
while((c=c.s(dim,1)) != null && c != p2.get()) {
s.append(c.getText());
}
if(c!=null) {
String str = c.getText();
// Have to check whether one past end or end
if(str.length() > p2.getOffs())
s.append(str.substring(0, p2.getOffs()+1));
else
s.append(str);
}
return s.toString();
}
// XXX Join the next two methods
static public Span[] getSpans(ZZCursor p1, ZZCursor p2) {
Vector sp = new Vector();
p("getspans: "+p1+" "+p2+" "+p1.get()+" "+p2.get());
p("getspans1: "+p1.get().getText());
p("getspans2: "+p1.get().getSpan());
p("getspans3: "+p1.get().getSpan().subSpan(p1.getOffs()));
if(p1.get() == p2.get()) {
sp.addElement(
p1.get().getSpan().subSpan(p1.getOffs(), p2.getOffs()+1)
);
} else {
sp.addElement(
p1.get().getSpan().subSpan(p1.getOffs())
);
ZZCell c = p1.get();
while((c=c.s(dim,1)) != null && c != p2.get()) {
sp.addElement(c.getSpan());
}
if(c!=null)
sp.addElement(c.getSpan().subSpan(0, p2.getOffs()+1));
}
Span[] ret = new Span[sp.size()];
for(int i=0; i<ret.length; i++) ret[i] = (Span)sp.elementAt(i);
return ret;
}
static public String[] getStrings(ZZCursor p1, ZZCursor p2) {
Vector sp = new Vector();
if(p1.get() == p2.get()) {
p("Gs==: "+p1.getOffs()+" "+p2.getOffs());
sp.addElement(
p1.get().getText().substring(p1.getOffs(), p2.getOffs()+1)
);
} else {
sp.addElement(
p1.get().getText().substring(p1.getOffs())
);
ZZCell c = p1.get();
while((c=c.s(dim,1)) != null && c != p2.get()) {
sp.addElement(c.getText());
}
if(c!=null)
sp.addElement(c.getText().substring(0, p2.getOffs()+1));
}
String[] ret = new String[sp.size()];
for(int i=0; i<ret.length; i++) ret[i] = (String)sp.elementAt(i);
return ret;
}
static public void findMarks(ZZCell c, int o1, int o2,
Vector mkindto, Vector mkcellto) {
Vector res = null;
if(o1 <= 0 && c.s("d.cut", 1) != null) {
res = new Vector();
// A cut == null cell
res.addElement(new Integer(0));
res.addElement(null);
}
while((c = c.s("d.cursor", 1))!=null) {
try {
int i = Integer.parseInt(c.getText());
if((o1 < 0 || i >= o1) && (o2<0 || i <= o2)) {
if(res==null) res = new Vector();
res.addElement(new Integer(i-o1));
res.addElement(c);
}
} catch(NumberFormatException e) {
// Nothin'
}
}
if(res!=null) {
int[] r = new int[res.size()/2];
ZZCell[] rc = new ZZCell[res.size()];
for(int i=0 ;i<res.size(); i+=2) {
r[i/2] = ((Integer)res.elementAt(i)).intValue();
rc[i/2] = (ZZCell)res.elementAt(i+1);
}
mkindto.addElement(r);
mkcellto.addElement(rc);
} else {
mkindto.addElement(null);
mkcellto.addElement(null);
}
}
static public Rend getRend(ZZCursor p1, ZZCursor p2) {
Vector sp = new Vector();
Vector cells = new Vector();
Vector strs = new Vector();
Vector mk = new Vector();
Vector mkc = new Vector();
Rend r = new Rend();
if(p1.get() == p2.get()) {
r.spans = new Span[] {p1.get().getSpan().subSpan(p1.getOffs(), p2.getOffs()+1)};
r.strs = new String[] {
p1.get().getText().substring(p1.getOffs(), p2.getOffs()+1)
};
findMarks(p1.get(), p1.getOffs(), p2.getOffs()+1, mk, mkc);
r.marks = new int[][] { (int[])mk.elementAt(0) };
r.markcells = new ZZCell[][] { (ZZCell[])mkc.elementAt(0) };
r.cells = new ZZCell[] { p1.get() };
return r;
}
sp.addElement(p1.get().getSpan().subSpan(p1.getOffs()));
strs.addElement(p1.get().getText().substring(p1.getOffs()));
cells.addElement(p1.get());
findMarks(p1.get(), p1.getOffs(), -1, mk, mkc);
ZZCell c = p1.get();
while((c=c.s(dim, 1)) != null && c != p2.get()) {
sp.addElement(c.getSpan());
strs.addElement(c.getText());
cells.addElement(c);
findMarks(c, 0, -1, mk, mkc);
}
if(c!=null) {
int n = p2.getOffs() + 1;
p("RENDEND: "+n+" '"+c.getText()+"' ");
if(c.getText().length() < n) n = c.getText().length();
sp.addElement(c.getSpan().subSpan(0, n));
strs.addElement(c.getText().substring(0, n));
findMarks(c, 0, p2.getOffs(), mk, mkc);
cells.addElement(c);
}
r.spans = new Span[sp.size()];
r.strs = new String[sp.size()];
r.marks = new int[sp.size()][];
r.markcells = new ZZCell[sp.size()][];
r.cells = new ZZCell[sp.size()];
for(int i=0; i<r.spans.length; i++) {
r.spans[i] = (Span)sp.elementAt(i);
r.strs[i] = (String)strs.elementAt(i);
r.marks[i] = (int[])mk.elementAt(i);
r.markcells[i] = (ZZCell[])mkc.elementAt(i);
r.cells[i] = (ZZCell)cells.elementAt(i);
}
return r;
}
// XXX The following two are ridiculous now that Pos
// has been changed to Cursor.
static public ZZCursor posFromCursor(ZZCell curscell) {
// XXX Go to cursor-cargo, then get offset, then go to cell.
int of = 0;
curscell = curscell.h("d.cursor-cargo", -1);
try {
of = Integer.parseInt(curscell.getText());
} catch(NumberFormatException e) {
// Nothin'
}
curscell = curscell.h("d.cursor", -1);
if(curscell.getSpan() == null) return null;
return new ZZCursorVirtual(curscell, of);
}
static public void setCursorFromPos(ZZCell c, ZZCursor p) {
ZZCursorReal.set(c, p.get());
c.h("d.cursor-cargo", -1).setText(""+p.getOffs());
}
static public void insert(ZZCursor p, String s) {
p("Insert: "+p+" "+p.get());
StringScroll sc = p.get().getSpace().getStringScroll();
long offs = sc.append(s);
p("Insertsc: "+sc+" "+offs);
Span sp = Span.create(
Address.scrollOffs(sc, offs),
Address.scrollOffs(sc, offs + s.length() - 1)
);
insert(p, sp);
}
static public void moveCurs(ZZCell c0, int offs0, int offs1, ZZCell r,
int noffs) {
ZZCell[] rnk = c0.readRank("d.cursor", 1, false);
for(int i=0; i<rnk.length; i++) {
try {
int of = Integer.parseInt(rnk[i].getText());
if((offs0 < -1 || of >= offs0) &&
(offs1 < -1 || of <= offs1)) {
rnk[i].setText(""+(of-offs0+noffs));
r.insert("d.cursor", 1, rnk[i]);
}
} catch(NumberFormatException e) {
// Nothin'
}
}
}
/** Split a VStream at position, return the cell just BEFORE the
* split. XXX MAYBE ALL VSTREAMS SHOULD HAVE EMPTY CELL AT BEGINNING AND
* END.
*/
static private ZZCell splitAt(ZZCursor p) {
// If offs == 0, need to do nothing.
if(p.getOffs() == 0) return p.get().s(dim, -1);
Span cur = p.get().getSpan();
if(p.getOffs() == cur.length()) return p.get();
// In the middle -> split and reposition cursors.
ZZCell n1 = p.get().N(dim, 1);
n1.setSpan(cur.subSpan(p.getOffs()));
p.get().setSpan(cur.subSpan(0, p.getOffs()));
ZZCell[] rnk = p.get().readRank("d.cursor", 1, false);
for(int i=0; i<rnk.length; i++) {
try {
int of = Integer.parseInt(rnk[i].getText());
if(of >= p.getOffs()) {
int nof = of-p.getOffs();
rnk[i].setText(""+nof);
n1.insert("d.cursor", 1, rnk[i]);
}
} catch(NumberFormatException e) {
// Nothin'
}
}
return p.get();
}
/** Insert a cut mark at the given position.
*/
static public void cut(ZZCursor p) {
ZZCell prev = splitAt(p);
ZZCell n = prev.s(dim, 1);
n.disconnect("d.cut", 1);
n.connect("d.cut", 1, n);
}
/** Clear the cuts in the given vstream.
*/
static public void clearcuts(ZZCursor p) {
if ( p == null ) return ;
for(ZZCell c = p.get().h(dim, -1); c!=null;
c = c.s(dim, 1))
if(c.s("d.cut", 1) != null)
c.disconnect("d.cut", 1);
}
static ZZCell findPrevCut(ZZCursor p) {
ZZCell prev;
for(prev=p.get(); prev != null && prev.s("d.cut", 1) == null;
prev = prev.s(dim, -1));
return prev;
}
static ZZCell findNextCut(ZZCursor p) {
ZZCell next;
for(next=p.get().s(dim, 1); next != null &&
next.s("d.cut", 1) == null;
next = next.s(dim, 1));
return next;
}
/** Create a new vstream between the given cut marks.
* XXX This is difficult since Pos doesn't have the forwards-backwards
* info - Swing positions do. This should probably be added...
* Imagine cutting apart at single character edges...
*/
static public ZZCell separate(ZZCursor p) {
ZZCell prev = findPrevCut(p);
ZZCell next = findNextCut(p);
ZZCell tmp;
// based on their nullness, find the section to cut.
if(prev == null && next == null) return null; // nothing.
if(prev == null) {
next.disconnect("d.cut", 1);
next.disconnect(dim, -1);
tmp = p.get().h(dim, -1);
tmp.disconnect(dim, 1);
tmp.connect(dim, 1, next);
return p.get().h(dim, -1).N(dim, -1);
} else {
if(next != null){
//prev.s(dim, -1).connect(dim, 1, next);
next.disconnect(dim, -1);
tmp = prev.s(dim, -1);
tmp.disconnect(dim, 1);
tmp.connect(dim, 1, next);
}
prev.disconnect(dim, -1);
prev.disconnect("d.cut", 1);
return prev.N(dim, -1);
}
// return null;
}
/** Copy the part of the vstream between the nearest cut marks
* into a new vstream.
*/
static public ZZCell separateCopy(ZZCursor p) {
ZZCell nhead = p.get().N("d.tmp",1);
nhead.excise("d.tmp");
ZZCell prev = findPrevCut(p);
ZZCell next = findNextCut(p);
if(prev == null) prev = p.get().h(dim, -1).s(dim, 1);
ZZCell n = nhead;
for(ZZCell c = prev; c != null && c != next; c=c.s(dim, 1)) {
n = n.N(dim, 1);
n.setSpan(c.getSpan());
}
return nhead;
}
/** Insert the given span.
* Cursors at the insertion point will be moved to AFTER the
* insertion. XXX Here's something where more flexibility
* is needed: does a cursor point forwards or backwards?
*/
static public void insert(ZZCursor p, Span s) {
ZZCell prev = splitAt(p);
Span cur = prev.getSpan();
if(prev.getSpan() != null && prev.getSpan().isAppendable(s)) {
int len = (int)prev.getSpan().length();
moveCurs(prev, len, len, prev, (int)(len+s.length()));
prev.setSpan(prev.getSpan().append(s));
return;
}
ZZCell n = prev.N(dim, 1);
n.setSpan(s);
moveCurs(p.get(), p.getOffs(), p.getOffs(), n, 1);
}
/** Assimilate a whole vstream in insertion.
* Also places cuts at the beginning and the end of the result...
* XXX should it just return the POSes?
* XXX Delete the empty cells at ends...
*/
static public void assimilate(ZZCursor p, ZZCell vstream) {
ZZCell prev = splitAt(p);
ZZCell next = prev.s(dim, 1);
ZZCell firstof = vstream.h(dim, -1).s(dim, 1);
ZZCell tail = firstof.h(dim, 1);
prev.disconnect(dim, 1);
firstof.disconnect(dim, -1);
prev.connect(dim, 1, firstof);
firstof.connect("d.cut", 1, firstof);
if(next != null) {
next.disconnect("d.cut", 1);
next.connect("d.cut", 1, next);
next.connect(dim, -1, tail);
}
}
/** A convenience function: delete one character prior to p0.
*/
static public void backspace(ZZCursor p0) {
// while and not if: there might be null-length
// spans in there...
while(p0.getOffs() == 0) {
ZZCell c = p0.get().s(dim, -1);
if(c == null || c.getSpan() == null || c.getSpan().length() == 0)
return;
p0 = new ZZCursorVirtual(c, (int)c.getSpan().length());
}
// Split if needed
if(p0.getOffs() != p0.get().getSpan().length()) {
ZZCell prev = splitAt(p0);
p0.set(prev);
}
// Now we know p0.offs == p0.get().getSpan().length()...
int l = (int)(p0.get().getSpan().length());
if(l==1) {
// move cursors and delete cell
// XXX AUGH! Must move EITHER forwards or backwards,
// depending on which one is there...
ZZCell to = p0.get().s(dim, 1); int offs = 0;
if(to==null) {
to = p0.get().s(dim, -1);
offs = (int)to.getSpan().length();
}
// Ouch...
moveCurs(p0.get(), 0, 0, to, offs);
moveCurs(p0.get(), 1, 1, to, offs);
p0.get().delete();
return;
}
p0.get().setSpan(p0.get().getSpan().subSpan(0, l-1));
moveCurs(p0.get(), l, l, p0.get(), l-1);
}
// XXX Maybe too heavy - always creates new cells.
// untested.
static public void delete(ZZCursor p0, ZZCursor p1) {
ZZCell[] rnk;
if(p0.get() == p1.get()) {
ZZCell n = p0.get().N(dim, 1);
p0.get().setSpan(p0.get().getSpan().subSpan(0, p0.getOffs()));
n.setSpan(p0.get().getSpan().subSpan(p1.getOffs()+1));
// Then, redo cursors
rnk = p0.get().readRank("d.cursor", 1, false);
for(int i=0; i<rnk.length; i++) {
try {
int of = Integer.parseInt(rnk[i].getText());
if(of > p1.getOffs()) {
int nof = of-p1.getOffs()-1;
rnk[i].setText(""+nof);
n.insert("d.cursor", 1, rnk[i]);
} else if(of >= p0.getOffs()) {
rnk[i].setText(""+p0.getOffs());
}
} catch(NumberFormatException e) {
// Nothin'
}
}
} else {
// 1. first cell's span & cursors
p0.get().setSpan(p0.get().getSpan().subSpan(0, p0.getOffs()));
rnk = p0.get().readRank("d.cursor", 1, false);
for(int i=0; i<rnk.length; i++) {
try {
int of = Integer.parseInt(rnk[i].getText());
if(of >= p0.getOffs()) {
rnk[i].setText(""+p0.getOffs());
}
} catch(NumberFormatException e) {
// Nothin'
}
}
// Do the cells until we get to the second cell.
ZZCell c = p0.get();
while((c = c.s(dim, 1)) != null && c != p1.get()) {
rnk = c.readRank("d.cursor", 1, false);
for(int i=0; i<rnk.length; i++) {
// Insert first - exception mustn't cancel insert.
p0.get().insert("d.cursor", 1, rnk[i]);
try {
int of = Integer.parseInt(rnk[i].getText());
// If there was an integer
rnk[i].setText(""+p0.getOffs());
} catch(NumberFormatException e) {
// Nothin'
}
}
c.delete();
}
// Then, the final cell.
if(c == null) return; // No cell - oops - deleted to end.
c.setSpan(c.getSpan().subSpan(p1.getOffs()+1));
rnk = p0.get().readRank("d.cursor", 1, false);
for(int i=0; i<rnk.length; i++) {
try {
int of = Integer.parseInt(rnk[i].getText());
if(of > p1.getOffs()) {
int nof = of-p1.getOffs()-1;
rnk[i].setText(""+nof);
} else {
p0.get().insert("d.cursor", 1, rnk[i]);
rnk[i].setText(""+p0.getOffs());
}
} catch(NumberFormatException e) {
// Nothin'
}
}
}
}
/** Given a blank cell, set that cell's span and add extra
* cells as necessary to copy the span between p0 and p1.
*/
static public void copyNew(ZZCursor p0, ZZCursor p1, ZZCell blank) {
if(p0.get()==p1.get()) {
blank.setSpan(p0.get().getSpan().subSpan(p0.getOffs(), p1.getOffs()+1));
} else {
// First cell
blank.setSpan(p0.get().getSpan().subSpan(p0.getOffs()));
// middle cells
ZZCell c = p0.get();
ZZCell n = blank;
while((c = c.s(dim, 1)) != null && c != p1.get()) {
n = n.N("dim", 1);
n.setSpan(c.getSpan());
}
// Last cell
if(c==null) return;
n = n.N("dim", 1);
n.setSpan(p1.get().getSpan().subSpan(0, p1.getOffs()+1));
}
}
}

View File

@@ -0,0 +1,100 @@
/*
ZZDefaultText.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;
import java.awt.*;
/** A simple implementation of ZZText.
*/
public class ZZDefaultText extends ZZText {
String rcsid = "$Id: ZZDefaultText.java,v 1.2 2000/10/19 17:04:26 tjl Exp $";
public String getText(ZZCell c) {
return c.getText();
}
public void setText(ZZCell c, String s) {
c.disconnect("d.reftext", 1);
// XXX Delete all cells there too?
c.setText(s);
}
public boolean isRef(ZZCell c) {
return c.s("d.reftext") != null;
}
public void delete(ZZCell c, int start) {
// XXX faster
delete(c, start, getText(c).length());
}
public void delete(ZZCell c, int start, int end) {
if(isRef(c)) {
} else {
}
}
public void insertNewRef(ZZCell c, String txt, int offs) {
ensureRef(c);
}
public void insertRef(ZZCell c, Span sp, int offs) {
ensureRef(c);
}
public Span[] getSpans(ZZCell c) {
LoopDetector ld = new LoopDetector();
ZZCell v = c.s("d.reftext");
return null;
}
public Span[] getSpans(ZZCell c, int start) {
LoopDetector ld = new LoopDetector();
ZZCell v = c.s("d.reftext");
return null;
}
public Span[] getSpans(ZZCell c, int start, int end) {
LoopDetector ld = new LoopDetector();
ZZCell v = c.s("d.reftext");
return null;
}
public void setSpans(ZZCell c, Span[] spans) {
}
Span makeSpan(ZZCell c, String s) {
StringScroll sc = c.getSpace().getStringScroll();
long offs = sc.append(s);
return Span.create(Address.scrollOffs(sc, offs),
Address.scrollOffs(sc, offs+s.length()-1));
}
void ensureRef(ZZCell c) {
if(c.s("d.reftext") == null) {
Span sp = makeSpan(c, getText(c));
ZZCell n = c.N("d.reftext", 1);
n.setSpan(sp);
}
}
}

57
Java/media/ZZHumber.java Normal file
View File

@@ -0,0 +1,57 @@
/*
ZZCellScroll.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 Tuomas Lukka
*/
package org.gzigzag;
import java.io.*;
/** A package for representing, reading and writing huge numbers.
* Humbers are described in Ted Nelson's Literary Machines.
* The ZZHumber objects are immutable, like the Java String class.
* XXX Not yet working
*/
// Immutable
public class ZZHumber {
public static final String rcsid = "$Id: ZZHumber.java,v 1.3 2000/09/19 10:32:00 ajk Exp $";
byte[] bytes; // The raw humber bytes.
public boolean equals(Object o) {
ZZHumber h = (ZZHumber) o;
if(h.bytes.length != bytes.length) return false;
for(int i=0; i<bytes.length; i++) {
if(h.bytes[i] != bytes[i]) return false;
}
return true;
}
byte[] getBytes() { return bytes; }
public static ZZHumber read(DataInput d) {
// XXX
return null;
}
/** Returns a humber one greater than the current one */
ZZHumber next() {
// XXX
return null;
}
}

115
Java/media/ZZSoundView.java Normal file
View File

@@ -0,0 +1,115 @@
/*
ZZSoundView.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;
import java.awt.*;
/** A view showing a sound vstream on a sound scroll.
* Assumes that all cells in the d.2 rank come from the same scroll.
*/
public class ZZSoundView extends ZZViewComponent {
public static final String rcsid = "$Id: ZZSoundView.java,v 1.8 2000/11/01 08:12:49 tjl Exp $";
public static boolean dbg = true;
void p(String s) { if(dbg) System.out.println(s); }
void pa(String s) { System.out.println(s); }
public ZZSoundView(ZZCell viewCell0) {
super(viewCell0);
setBackground(new Color(150,150,255));
}
ZZCell[] rank;
int[] t0; int[] t1; // in pixels
int top = 10; int bottom = 10;
int right = 10;
public boolean reraster() { return false; }
public void paintInto(Graphics gr) {
synchronized(viewCell.getSpace()) {
Dimension size = getSize();
Insets ins = getInsets();
gr.setColor(getBackground());
gr.fillRect(ins.left, ins.top,
size.width-ins.left-ins.right, size.height-ins.top-ins.bottom);
gr.setColor(Color.black);
// assume vertical display - that way putting in transcription text is easy
ZZCell cursor = viewCell.h("d.cursor-cargo",-1).
h("d.cursor", -1);
Span s = cursor.getSpan();
if(s==null) {
gr.drawString("Not a span", 0, size.height/2);
return;
}
Address start = s.getStart();
Address end = s.getEnd();
Scroll scr0 = start.getScroll(viewCell.getSpace());
if(!(scr0 instanceof SoundScroll)) {
gr.drawString("Wrong type:"+scr0, 0, size.height/2);
return;
}
SoundScroll scr = (SoundScroll)scr0;
long len = scr.getDurationNanoseconds();
ZZCell h = cursor.h("d.2", -1);
rank = h.readRank("d.2", 1, true, null);
t0 = new int[rank.length];
t1 = new int[rank.length];
int l = size.height - top - bottom;
gr.drawLine(right, top, right, size.height-bottom);
for(int i=0; i<rank.length; i++) {
Span sp = rank[i].getSpan();
if(sp==null) return;
Address curstart = sp.getStart();
Address curend = sp.getEnd();
t0[i] = (int)((l*curstart.getOffs())/len);
t1[i] = (int)((l*curend.getOffs())/len);
if(rank[i] == cursor)
gr.setColor(Color.white);
gr.drawLine(right, t0[i], right+5, t0[i]);
gr.drawLine(right, t1[i], right+5, t1[i]);
gr.drawLine(right+1, t0[i], right+1, t1[i]);
gr.drawLine(right+2, t0[i], right+2, t1[i]);
if(rank[i] == cursor)
gr.setColor(Color.black);
}
}
}
}

117
Java/media/ZZText.java Normal file
View File

@@ -0,0 +1,117 @@
/*
ZZText.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;
import java.awt.*;
/** An interface for getting or putting text in cells.
* Used e.g. to implement multilingual text.
* Handles referential text as well.
*/
public abstract class ZZText {
public static final String rcsid = "$Id: ZZText.java,v 1.2 2000/10/19 17:04:26 tjl Exp $";
/** Get the string in a given cell, whether referential or non-referential.
*/
public abstract String getText(ZZCell c);
/** Set the string in the given cell non-referentially.
*/
public abstract void setText(ZZCell c, String s);
/** Whether a given cell is referential.
*/
public abstract boolean isRef(ZZCell c);
/** Insert the given text into the cell.
* If the cell is referential, insert referentially by creating
* a new Span for this text. Else just handle characters.
*/
public void insert(ZZCell c, String txt, int offs) {
if(isRef(c)) {
insertNewRef(c, txt, offs);
} else {
String s = getText(c);
setText(c, s.substring(0, offs) + txt + s.substring(offs));
}
}
/** Delete a span of text from this cell.
*/
public abstract void delete(ZZCell c, int start);
/** Delete a span of text from this cell.
*/
public abstract void delete(ZZCell c, int start, int end);
/** Split a cell into two along a dimension.
* 0..offs-1 goes into first, offs..end goes to next.
* All connections remain in the first cell.
*/
public void split(ZZCell c, int offs, String dim) {
ZZCell n = c.N(dim);
if(isRef(c)) {
Span[] sps = getSpans(c, offs);
setSpans(n, sps);
delete(c, offs);
} else {
String s = getText(c);
setText(c, s.substring(0, offs));
setText(n, s.substring(offs));
}
}
/** Insert a string of typed-in text referentially.
* A Span in the current cell's space is automatically constructed, etc.
* This is at this level because we *may* want to fudge things later if
* the user corrects typing errors before saving. However, that's a
* bit tricky
* so we won't try it yet.
* <p>
* Calling this function on a cell that only has text content
* makes the whole
* current text content referential as well.
*/
public abstract void insertNewRef(ZZCell c, String txt, int offs);
/** Insert a referential span of text.
* <p>
* Calling this function on a cell that only has text content
* makes the whole
* current text content referential as well.
*/
public abstract void insertRef(ZZCell c, Span sp, int offs);
/** Obtain the referential contents of this cell.
* @return List of spans, or null if not referential.
*/
public abstract Span[] getSpans(ZZCell c);
public abstract Span[] getSpans(ZZCell c, int start);
public abstract Span[] getSpans(ZZCell c, int start, int end);
/** Set the spans of the cell to the given spans.
*/
public abstract void setSpans(ZZCell c, Span[] spans);
}