368 lines
12 KiB
Java
368 lines
12 KiB
Java
/*
|
|
ZZCursorReal.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.*;
|
|
|
|
/** A real cursor in a structure.
|
|
* This class also implements the commonly used
|
|
* static methods for convenience.
|
|
*/
|
|
|
|
public class ZZCursorReal extends ZZCursor {
|
|
String rcsid = "$Id: ZZCursorReal.java,v 1.31.2.1 2002/02/10 12:37:40 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); }
|
|
|
|
ZZCell ccell;
|
|
public ZZCursorReal(ZZCell start) {
|
|
// ccell = start.h("d.cursor-cargo", -1);
|
|
// XXX ?? Which one is right?
|
|
ccell = start;
|
|
}
|
|
public ZZCell get() { return get(ccell); }
|
|
public void set(ZZCell c) { set(ccell, c); }
|
|
public int getOffs() { return getOffs(ccell); }
|
|
public void setOffs(int i) { setOffs(ccell, i); }
|
|
|
|
/** Get the cell the given cursor cell or cargo cell is pointing to.
|
|
*/
|
|
static public final ZZCell get(ZZCell start) {
|
|
// XXX Maybe should be less allowing...
|
|
return start.h("d.cursor-cargo", -1).
|
|
h("d.cursor-list", -1).s("d.cursor", -1);
|
|
}
|
|
|
|
/** Set the given cursor cell to point to a cell.
|
|
*/
|
|
static public void setcursor(ZZCell cur, ZZCell c) {
|
|
removeFromCursorList(cur);
|
|
if (c != null) addToCursorList(cur, c);
|
|
trigger(cur);
|
|
}
|
|
|
|
/** Set the given cursor cargo to point to a cell.
|
|
* If a cursor is cargoed already, use it. If no cursor is cargoed,
|
|
* create one.
|
|
*/
|
|
static public void setcargo(ZZCell cargo, ZZCell c) {
|
|
if(cargo.s("d.cursor-cargo", -1) == null)
|
|
cargo.N("d.cursor-cargo", -1);
|
|
setcursor(cargo.h("d.cursor-cargo", -1), c);
|
|
}
|
|
|
|
/** Set the given cursor cell or cursor cargo cell to point to a cell.
|
|
* This routine has an interesting heuristic about creating
|
|
* a cell on <code>d.cursor-cargo</code>: if
|
|
* the cell is already connected negwards on <code>d.cursor</code> or
|
|
* <code>d.cursor-list</code>,
|
|
* no cursor cargo cell is created, otherwise it is.
|
|
* <p>
|
|
* XXX should this be deprecated in favor of setcursor and setcargo?
|
|
*/
|
|
static public final void set(ZZCell start, ZZCell c) {
|
|
// A rather intricate check: if there's nothing on d.cursor-cargo,
|
|
// create it but only if we're not going negwards on d.cursor or
|
|
// d.cursor-list already.
|
|
|
|
if(start.s("d.cursor-list", -1) == null &&
|
|
start.s("d.cursor", -1) == null) {
|
|
setcargo(start, c);
|
|
} else
|
|
setcursor(start, c);
|
|
}
|
|
|
|
static public void set(ZZCell c, ZZCursor curs) {
|
|
set(c, curs.get());
|
|
setOffs(c, curs.getOffs());
|
|
}
|
|
|
|
/** Create a new cursor cell pointing to another cell.
|
|
* Usually you use set to create a new cell, but sometimes you don't want
|
|
* to attach a cargo cell on d.cursor-cargo; create just creates a new
|
|
* cell on d.cursor and returns it.
|
|
*/
|
|
static public final ZZCell create(ZZCell c) {
|
|
ZZCell cur = c.N();
|
|
addToCursorList(cur, c);
|
|
return cur;
|
|
}
|
|
|
|
/** Attach a cursor to another one on d.cursor-cargo.
|
|
* The first cell is removed from its current d.cursor-cargo rank and
|
|
* inserted poswards behind the second one. Thus, the first cursor must
|
|
* be a cursor cargo cell, not a real cursor cell (connected negwards
|
|
* along d.cursor).
|
|
*/
|
|
static public final void attach(ZZCell which, ZZCell where) {
|
|
if(which.s("d.cursor", -1) != null ||
|
|
which.s("d.cursor-list", -1) != null)
|
|
throw new ZZError("which is a real cursor cell");
|
|
where.insert("d.cursor-cargo", 1, which);
|
|
trigger(which);
|
|
}
|
|
|
|
/** Get the color associated with this cursor in the structure.
|
|
*/
|
|
public java.awt.Color getColor() { return getColor(ccell); }
|
|
|
|
/** Get the color associated with the given cursor in the structure.
|
|
*/
|
|
static public final java.awt.Color getColor(ZZCell start) {
|
|
ZZCell hc = start.h("d.cursor-cargo", -1);
|
|
ZZCell col = hc.s("d.1", 1);
|
|
if(col == null)
|
|
return null;
|
|
try {
|
|
return new java.awt.Color(Integer.parseInt(col.getText()));
|
|
} catch(NumberFormatException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** Get the color associated with the given cursor, or white.
|
|
* Return Color.white if no color is associated in the structure;
|
|
* getColor would return null.
|
|
*/
|
|
static public final java.awt.Color getColorOrWhite(ZZCell start) {
|
|
java.awt.Color c = getColor(start);
|
|
if(c == null) return java.awt.Color.white;
|
|
return c;
|
|
}
|
|
|
|
/** Set the color associated with the given cursor in the
|
|
* structure.
|
|
* XXX Move cursor color to another dimension...
|
|
*/
|
|
static public final void setColor(ZZCell start, java.awt.Color c) {
|
|
ZZCell hc = start.h("d.cursor-cargo", -1);
|
|
// if(hc.s("d.1", 1) != null)
|
|
// throw new Error("Can't set color of cursor: something's there.");
|
|
hc.getOrNewCell("d.1",1).setText(String.valueOf(c.getRGB()));
|
|
}
|
|
|
|
/** Get the color of the first cursor associated with this cell.
|
|
* If there is no colored cursor accursing this cell, return null.
|
|
*/
|
|
static public final java.awt.Color getAccursedColor(ZZCell c) {
|
|
Enumeration e = ZZCursorReal.getPointers(c);
|
|
while(e.hasMoreElements()) {
|
|
java.awt.Color col = ZZCursorReal.getColor((ZZCell)e.nextElement());
|
|
if(col != null) return col;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** Set the offset of the given cursor.
|
|
*/
|
|
static public void setOffs(ZZCell c, int i) {
|
|
String s = null;
|
|
if(i == NO_OFFSET)
|
|
s = "";
|
|
else if(i < 0) {
|
|
throw new ZZError("Trying to set negative cursor offset!");
|
|
} else
|
|
s = String.valueOf(i);
|
|
c.h("d.cursor-cargo", -1).setText(s);
|
|
trigger(c);
|
|
}
|
|
|
|
/** Get the offset of the given cursor.
|
|
*/
|
|
static public int getOffs(ZZCell c) {
|
|
String s = c.h("d.cursor-cargo").getText();
|
|
if(s==null || s.equals("")) return NO_OFFSET;
|
|
try {
|
|
int res = Integer.parseInt(s);
|
|
if(res < 0) {
|
|
ZZLogger.log(
|
|
"Serious: negative cursor offset. Resetting. "+c+" "+res);
|
|
setOffs(c, NO_OFFSET);
|
|
return NO_OFFSET;
|
|
}
|
|
return res;
|
|
} catch(NumberFormatException e) {
|
|
}
|
|
return NO_OFFSET;
|
|
}
|
|
|
|
/** Delete extra cells associated only with the given cursor.
|
|
* After this, deleting the cell will remove all traces.
|
|
*/
|
|
static public void delete(ZZCell c) {
|
|
if(c.s("d.cursor-cargo", 1) != null) return;
|
|
c = c.s("d.cursor-cargo", -1);
|
|
if(c.s("d.cursor-cargo", -1) != null) return;
|
|
// Now, c is the cell on the cursor list with no other cargo,
|
|
// and is not the original cell.
|
|
removeFromCursorList(c);
|
|
c.delete();
|
|
}
|
|
|
|
static class PointerEnum implements Enumeration {
|
|
ZZCell c, cur;
|
|
{
|
|
p("PointerEnum created with " + c + ", cur = " + cur);
|
|
}
|
|
|
|
public PointerEnum(ZZCell d) {
|
|
c = d;
|
|
cur = d.s("d.cursor", 1);
|
|
}
|
|
|
|
public boolean hasMoreElements() {
|
|
boolean rv = cur != null;
|
|
p("PointeEnum: hasMoreElements:" + rv);
|
|
return rv;
|
|
}
|
|
|
|
public Object nextElement() {
|
|
if (cur == null) throw new NoSuchElementException();
|
|
Object rv = cur;
|
|
cur = cur.s("d.cursor-list", 1);
|
|
p("PointerEnum: rv = " + rv + ", cur = " + cur);
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
/** Find cursors that point to the given cell.
|
|
* This enumeration goes through the cells on the cursor list,
|
|
* note that each of those may have cursor cargo attached with
|
|
* them. There is not yet a function to go through the cursor
|
|
* cargoes.
|
|
*/
|
|
static public Enumeration getPointers(final ZZCell c) {
|
|
return new PointerEnum(c);
|
|
}
|
|
|
|
/** Adjust the cursors on a cell after text insertion or deletion.
|
|
* This in- or decrements cursors whose offsets are past a given
|
|
* division point. Cursors without offsets remain unchanged.
|
|
* @param acc The cell on which the cursors to be changed are.
|
|
* @param div The index from which on we want offsets to be changed.
|
|
* @param inc The amount by which we want offsets to be changed.
|
|
* Negative for deletions. Note that offsets won't be set
|
|
* below div, that is after we've deleted seven characters,
|
|
* a cursor which was on the third character will be set
|
|
* where the first char was, not (7-3=)four posns before that.
|
|
*/
|
|
static public void adjustCursors(ZZCell acc, int div, int inc) {
|
|
Enumeration e = getPointers(acc);
|
|
while(e.hasMoreElements()) {
|
|
ZZCell curs = (ZZCell)e.nextElement();
|
|
int offs = getOffs(curs);
|
|
if(offs != NO_OFFSET && offs >= div) {
|
|
if(offs + inc < div)
|
|
setOffs(curs, div);
|
|
else
|
|
setOffs(curs, offs + inc);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Find the visual text offset for the cursor.
|
|
* This is purely a convenience routine: if the cursor has an
|
|
* offset, it returns that, but if not, it returns half of the
|
|
* length of text in the accursed cell.
|
|
*/
|
|
static public int getVisualTextOffset(ZZCell c) {
|
|
int ret = getOffs(c);
|
|
if(ret == NO_OFFSET)
|
|
ret = get(c).getText().length() / 2;
|
|
return ret;
|
|
}
|
|
|
|
// Private stuff
|
|
|
|
static private void removeFromCursorList(ZZCell cur) {
|
|
ZZCell acc = cur.s("d.cursor", -1);
|
|
if(acc != null)
|
|
cur.disconnect("d.cursor", -1);
|
|
|
|
ZZCell nb = cur.s("d.cursor-list", 1);
|
|
if(acc != null && nb != null)
|
|
nb.connect("d.cursor", -1, acc);
|
|
|
|
cur.excise("d.cursor-list");
|
|
}
|
|
|
|
static private void addToCursorList(ZZCell cur, ZZCell c) {
|
|
ZZCell nb = c.s("d.cursor", 1);
|
|
if(nb != null)
|
|
// insert instead of connect so that when we have headcells on
|
|
// looping ranks, d.cursor-list can loop. (Dunno if we need this,
|
|
// but kinda seemed right.)
|
|
nb.h("d.cursor-list", 1)
|
|
.insert("d.cursor-list", 1, cur);
|
|
else
|
|
c.connect("d.cursor", 1, cur);
|
|
}
|
|
|
|
static private LoopDetector trigdetect = null;
|
|
static private boolean stoptrig = false;
|
|
|
|
static private void trigger(ZZCell start) {
|
|
p("Cursor trigger triggered");
|
|
boolean wasnull = false;
|
|
if(trigdetect == null) {
|
|
wasnull = true;
|
|
trigdetect = new LoopDetector();
|
|
}
|
|
|
|
synchronized(trigdetect) {
|
|
try {
|
|
ZZCell c = start.h("d.cursor-cargo", -1);
|
|
for(; c != null; c = c.s("d.cursor-cargo", 1)) {
|
|
if(c.s("d..cursor-trigger", -1) == null) continue;
|
|
p(""+c);
|
|
|
|
if(trigdetect.isLooping(c)) {
|
|
ZZLogger.log("Detected trigger loop while trying "+
|
|
"to set a cursor. Breaking triggering now.");
|
|
stoptrig = true;
|
|
break;
|
|
}
|
|
|
|
ZZCell commcell = c.h("d..cursor-trigger", -1);
|
|
ZZCommand comm = ZZCommand.getCommand(commcell);
|
|
if(comm != null) {
|
|
p("Calling trigger command: "+commcell.getText());
|
|
try {
|
|
comm.exec(c);
|
|
} catch(ZZError e) {
|
|
ZZLogger.exc(e, "Cursor trigger exception: ");
|
|
stoptrig = true;
|
|
}
|
|
if(stoptrig) break;
|
|
}
|
|
}
|
|
} finally {
|
|
if(wasnull) {
|
|
trigdetect = null;
|
|
stoptrig = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|