use of gov.sandia.n2a.ui.eq.FilteredTreeModel in project n2a by frothga.
the class NodeReference method applyEdit.
@Override
public void applyEdit(JTree tree) {
String input = (String) getUserObject();
if (input.isEmpty()) {
boolean canceled = MainFrame.instance.undoManager.getPresentationName().equals("AddReference");
delete(canceled);
return;
}
String[] parts = input.split("=", 2);
String name = parts[0].trim().replaceAll("[ \\n\\t]", "");
String value;
if (parts.length > 1)
value = parts[1].trim();
else
value = "";
NodeBase parent = (NodeBase) getParent();
String oldName = source.key();
String oldValue = source.get();
if (!name.equals(oldName)) {
// Check if name change is forbidden
if (name.isEmpty()) {
name = oldName;
} else {
MPart mparent = source.parent();
MPart partAfter = (MPart) mparent.child(name);
if (partAfter != null && partAfter.isFromTopDocument())
name = oldName;
}
}
if (name.equals(oldName) && value.equals(oldValue)) {
setUserObject();
FilteredTreeModel model = (FilteredTreeModel) tree.getModel();
// Our siblings should not change, because we did not really change. Just repaint in non-edit mode.
model.nodeChanged(this);
return;
}
MainFrame.instance.undoManager.apply(new ChangeReference(parent, oldName, oldValue, name, value));
}
use of gov.sandia.n2a.ui.eq.FilteredTreeModel in project n2a by frothga.
the class NodeVariable method makeAdd.
@Override
public Undoable makeAdd(String type, JTree tree, MNode data, Point location) {
if (type.isEmpty()) {
FilteredTreeModel model = (FilteredTreeModel) tree.getModel();
if (model.getChildCount(this) == 0 || tree.isCollapsed(new TreePath(getPath())))
return ((NodeBase) parent).makeAdd("Variable", tree, data, location);
type = "Equation";
}
if (isBinding && !type.equals("Annotation"))
return ((NodeBase) parent).makeAdd(type, tree, data, location);
if (type.equals("Equation")) {
if (data != null) {
// includes @
String key = data.key();
// Determine if pasting over empty variable (no equations of any type except a naked combiner)
Variable.ParsedValue existing = new Variable.ParsedValue(source.get());
boolean hasEquations = !existing.condition.isEmpty() || !existing.expression.isEmpty();
if (!hasEquations) {
// unfiltered
Enumeration<?> children = children();
while (children.hasMoreElements()) {
Object c = children.nextElement();
if (c instanceof NodeEquation) {
hasEquations = true;
break;
}
}
}
if (// no equations, or possibly a naked combiner
!hasEquations) {
String value = existing.combiner + data.get() + key;
if (value.endsWith("@"))
value = value.substring(0, value.length() - 1);
return new ChangeVariable(this, source.key(), value);
}
// Determine if pasting over an existing equation
NodeBase existingEquation = child(key);
if (existingEquation != null) {
// remove the @, since ChangeEquation expects strings from ParsedValue
key = key.substring(1);
String combiner = existing.combiner;
String newValue = data.get();
String existingValue = existingEquation.source.get();
if (!newValue.equals(existingValue))
return new ChangeEquation(this, key, combiner, existingValue, key, combiner, newValue);
// else the user intent is to duplicate the equation for convenience before editing it.
// In this case, we need to create a new equation with alternate key.
data = new MVolatile(existingValue, "@" + key + "&&");
}
}
// Determine index for new equation
int index = 0;
NodeBase child = null;
TreePath path = tree.getLeadSelectionPath();
if (path != null)
child = (NodeBase) path.getLastPathComponent();
if (child != null && child.getParent() == this)
index = getIndex(child);
while (index > 0 && !(getChildAt(index) instanceof NodeEquation)) index--;
if (index < getChildCount() && getChildAt(index) instanceof NodeEquation)
index++;
// Create an AddEquation action
return new AddEquation(this, index, data);
} else if (type.equals("Annotation")) {
// Determine index at which to insert new annotation
int index = 0;
int count = getChildCount();
while (index < count && !(children.get(index) instanceof NodeReference)) index++;
return new AddAnnotation(this, index, data);
} else if (type.equals("Annotations")) {
// In this case, everything under this node will be rebuilt, so no need to worry about insertion index.
return new ChangeAnnotations(this, data);
} else if (type.equals("Reference")) {
return new AddReference(this, getChildCount(), data);
} else if (type.equals("References")) {
return new ChangeReferences(this, data);
}
// refer all other requests up the tree
return ((NodeBase) parent).makeAdd(type, tree, data, location);
}
use of gov.sandia.n2a.ui.eq.FilteredTreeModel in project n2a by frothga.
the class NodeVariable method applyEdit.
/**
* Enforces all the different use cases associated with editing of variables.
* This is the most complex node class, and does the most work. Some of the use cases include:
* Create a new variable.
* Move an existing variable tree, perhaps overriding an inherited one, perhaps also with a change of value.
* Insert an equation under ourselves.
* Insert an equation under another variable.
*/
@Override
public void applyEdit(JTree tree) {
String input = toString();
UndoManager um = MainFrame.instance.undoManager;
boolean canceled = um.getPresentationName().equals("AddVariable");
if (input.isEmpty()) {
delete(canceled);
return;
}
String[] parts = input.split("=", 2);
String nameAfter = parts[0].trim().replaceAll("[ \\n\\t]", "");
String valueAfter;
if (// Explicit assignment
parts.length > 1) {
valueAfter = parts[1].trim();
if (valueAfter.startsWith("$kill"))
valueAfter = valueAfter.substring(5).trim();
} else {
// Input was a variable name with no assignment.
valueAfter = "";
}
// What follows is a series of analyses, most having to do with enforcing constraints
// on name change (which implies moving the variable tree or otherwise modifying another variable).
// Handle a naked expression.
String nameBefore = source.key();
String valueBefore = getValue();
if (// Not a proper variable name. The user actually passed a naked expression, so resurrect the old (probably auto-assigned) variable name.
!isValidIdentifier(nameAfter)) {
nameAfter = nameBefore;
valueAfter = input;
}
// Handle creation of $inherit node.
FilteredTreeModel model = (FilteredTreeModel) tree.getModel();
boolean canInject = getChildCount() == 0 && source.isFromTopDocument();
// Only a heuristic. Could also be an existing variable with no equation.
boolean newlyCreated = canInject && valueBefore.isEmpty();
NodeBase parent = (NodeBase) getParent();
if (nameAfter.equals("$inherit")) {
if (parent.child(nameAfter) == null) {
if (newlyCreated) {
parent.source.clear(nameBefore);
// No need to update GUI, because AddInherit rebuilds parent.
um.apply(new AddInherit((NodePart) parent, valueAfter));
} else {
um.apply(new ChangeVariableToInherit(this, valueAfter));
}
return;
}
// Reject name change, because $inherit already exists. User should edit it directly.
nameAfter = nameBefore;
}
// Prevent illegal name change. (Don't override another top-level node. Don't overwrite a non-variable node.)
NodeBase nodeAfter = parent.child(nameAfter);
if (nodeAfter != null) {
boolean isVariable = nodeAfter instanceof NodeVariable;
boolean different = nodeAfter != this;
boolean topdoc = nodeAfter.source.isFromTopDocument();
boolean revoked = nodeAfter.source.get().equals("$kill");
if (!isVariable || (different && topdoc && !revoked && !canInject)) {
nameAfter = nameBefore;
nodeAfter = this;
}
}
// If there's nothing to do, then repaint the node and quit.
if (nameBefore.equals(nameAfter) && valueBefore.equals(valueAfter)) {
setUserObject();
model.nodeChanged(this);
return;
}
// Detect and handle special cases
if (// There exists a variable in the target location, so we may end up injecting an equation into a multiconditional expression.
nodeAfter != null) {
// In this section, "dest" refers to state of target node before it is overwritten, while "after" refers to newly input values from user.
Variable.ParsedValue piecesDest = new Variable.ParsedValue(((NodeVariable) nodeAfter).getValue());
Variable.ParsedValue piecesAfter = new Variable.ParsedValue(valueAfter);
boolean expressionAfter = !piecesAfter.expression.isEmpty() || !piecesAfter.condition.isEmpty();
// If the user doesn't specify a combiner, absorb it from our destination.
if (piecesAfter.combiner.isEmpty())
piecesAfter.combiner = piecesDest.combiner;
int equationCount = 0;
NodeEquation equationMatch = null;
Enumeration<?> childrenAfter = nodeAfter.children();
while (childrenAfter.hasMoreElements()) {
Object c = childrenAfter.nextElement();
if (c instanceof NodeEquation) {
equationCount++;
NodeEquation e = (NodeEquation) c;
if (e.source.key().substring(1).equals(piecesAfter.condition))
equationMatch = e;
}
}
if (nodeAfter == this) {
if (// Inject an equation into ourselves.
equationCount > 0 && expressionAfter) {
if (// New equation
equationMatch == null) {
// It is possible to add an equation revocation here without there being an existing equation to revoke.
um.apply(new AddEquation(this, piecesAfter.condition, piecesAfter.combiner, piecesAfter.expression));
} else // Overwrite an existing equation
{
Variable.ParsedValue piecesMatch = new Variable.ParsedValue(piecesDest.combiner + equationMatch.source.get() + equationMatch.source.key());
um.apply(new ChangeEquation(this, piecesMatch.condition, piecesMatch.combiner, piecesMatch.expression, piecesAfter.condition, piecesAfter.combiner, piecesAfter.expression));
}
return;
}
} else // Node has been renamed.
{
if (// Inject into/over an existing variable.
canInject) {
// Remove this variable, regardless of what we do to nodeAfter.
um.addEdit(new CompoundEdit());
um.apply(new DeleteVariable(this, canceled));
// Decide what change (if any) to apply to nodeAfter.
if (expressionAfter) {
NodeVariable nva = (NodeVariable) nodeAfter;
if (equationCount == 0) {
if (// Directly overwrite the target, since they share the say name and condition.
piecesAfter.condition.equals(piecesDest.condition)) {
um.apply(new ChangeVariable(nva, nameAfter, valueAfter, getKeyPath()));
} else // Inject new equation and change target into a multiconditional variable.
{
// Possible to revoke non-existent equation
um.apply(new AddEquation(nva, piecesAfter.condition, piecesAfter.combiner, piecesAfter.expression, getKeyPath()));
}
} else {
if (// Add new equation to an existing multiconditional.
equationMatch == null) {
// Possible to revoke non-existent equation
um.apply(new AddEquation(nva, piecesAfter.condition, piecesAfter.combiner, piecesAfter.expression, getKeyPath()));
} else // Overwrite an existing equation in a multiconditional
{
Variable.ParsedValue piecesMatch = new Variable.ParsedValue(piecesDest.combiner + equationMatch.source.get() + equationMatch.source.key());
um.apply(new ChangeEquation(nva, piecesMatch.condition, piecesMatch.combiner, piecesMatch.expression, piecesAfter.condition, piecesAfter.combiner, piecesAfter.expression, getKeyPath()));
}
}
}
um.endCompoundEdit();
return;
}
}
}
// The @ will be hidden most of the time, but it will distinguish a variable from a part.
if (valueAfter.isEmpty() && !hasEquations())
valueAfter = "@";
um.apply(new ChangeVariable(this, nameAfter, valueAfter));
}
use of gov.sandia.n2a.ui.eq.FilteredTreeModel in project n2a by frothga.
the class AddEquation method destroy.
public static void destroy(List<String> path, int equationCount, boolean canceled, String name, String combinerBefore, boolean setSelection) {
// Retrieve created node
NodeBase parent = NodeBase.locateNode(path);
if (parent == null)
throw new CannotUndoException();
NodeBase createdNode = parent.child(name);
PanelEquationTree pet = parent.getTree();
FilteredTreeModel model = (FilteredTreeModel) pet.tree.getModel();
TreeNode[] createdPath = createdNode.getPath();
int index = parent.getIndexFiltered(createdNode);
if (canceled)
index--;
// Update database
MPart mparent = parent.source;
mparent.clear(name);
boolean parentChanged = false;
if (!mparent.get().equals(combinerBefore)) {
// This value may be replaced below if we switch back to single-line.
mparent.set(combinerBefore);
parentChanged = true;
}
if (// There is no overridden value, so this node goes away completely.
mparent.child(name) == null) {
model.removeNodeFromParent(createdNode);
if (// The node used to be single-line, so fold the last equation back into it.
equationCount == 0) {
// The one remaining equation.
NodeEquation lastEquation = null;
// unfiltered
Enumeration<?> i = parent.children();
while (i.hasMoreElements()) {
Object o = i.nextElement();
if (o instanceof NodeEquation) {
lastEquation = (NodeEquation) o;
break;
}
}
String lastCondition = lastEquation.source.key();
String lastExpression = lastEquation.source.get();
mparent.clear(lastCondition);
if (lastCondition.equals("@"))
mparent.set(combinerBefore + lastExpression);
else
mparent.set(combinerBefore + lastExpression + lastCondition);
parentChanged = true;
model.removeNodeFromParent(lastEquation);
}
} else // Just exposed an overridden value, so update.
{
createdNode.setUserObject();
}
if (// Update tabs among this variable's siblings
parentChanged) {
parent.setUserObject();
NodeBase grandparent = (NodeBase) parent.getParent();
grandparent.invalidateColumns(model);
}
parent.invalidateColumns(null);
pet.updateOrder(createdPath);
pet.updateVisibility(createdPath, index, setSelection);
parent.allNodesChanged(model);
pet.animate();
}
use of gov.sandia.n2a.ui.eq.FilteredTreeModel in project n2a by frothga.
the class NodePart method applyEdit.
@Override
public void applyEdit(JTree tree) {
FilteredTreeModel model = (FilteredTreeModel) tree.getModel();
String input = (String) getUserObject();
String[] pieces = input.split("=", 2);
String name = pieces[0].trim();
String oldKey = source.key();
if (name.equals(oldKey)) {
revert(model);
return;
}
UndoManager um = MainFrame.instance.undoManager;
if (// Edits to root cause a rename of the document on disk
isTrueRoot()) {
if (name.isEmpty()) {
revert(model);
return;
}
name = MDir.validFilenameFrom(name);
// In addition to filename constraints, we also forbid comma, because these names are used in list expressions.
name = name.replace(",", "-");
String stem = name;
int suffix = 0;
MNode models = AppData.models;
MNode existingDocument = models.child(name);
while (existingDocument != null) {
suffix++;
name = stem + " " + suffix;
existingDocument = models.child(name);
}
um.apply(new ChangeDoc(oldKey, name));
// MDir promises to maintain object identity during the move, so "source" is still valid.
return;
}
if (input.isEmpty()) {
// A part may appear in the form of a graph node, rather than an entry in an equation tree.
// In that case, delete during the current event can mess up focus control (due to sequence of other code).
// Therefore, put the delete action onto the event queue. This is OK to do, even for tree editing.
EventQueue.invokeLater(new Runnable() {
public void run() {
boolean canceled = um.getPresentationName().equals("AddPart");
delete(canceled);
}
});
return;
}
name = validIdentifierFrom(name);
NodeBase parent = getTrueParent();
NodeBase sibling = parent.child(name);
if (// the name already exists in top document, so reject rename
sibling != null && (sibling.source.isFromTopDocument() || !(sibling instanceof NodePart))) {
revert(model);
return;
}
um.apply(new ChangePart(this, oldKey, name));
}
Aggregations