use of gov.sandia.n2a.ui.eq.PanelEquations.FocusCacheEntry in project n2a by frothga.
the class ChangePart method apply.
public void apply(String nameBefore, String nameAfter) {
NodePart parent = (NodePart) NodeBase.locateNode(path);
if (parent == null)
throw new CannotRedoException();
NodeBase temp = parent.child(nameBefore);
if (!(temp instanceof NodePart))
throw new CannotRedoException();
NodePart nodeBefore = (NodePart) temp;
// Update the database
// Move the subtree
MPart mparent = parent.source;
mparent.clear(nameBefore);
mparent.set(savedTree, nameAfter);
MPart oldPart = (MPart) mparent.child(nameBefore);
MPart newPart = (MPart) mparent.child(nameAfter);
// Change connection bindings to this part.
// See ChangeVariable.apply() for a similar procedure. More detailed comments appear there.
// We make use of static functions in that class to do the heavy work of emitting code with name changes.
// TODO: This approach will probably fail on parts that contain references to themselves.
PanelEquations pe = PanelModel.instance.panelEquations;
List<List<String>> references = new ArrayList<List<String>>();
try {
MPart doc = pe.root.source;
EquationSet compiled = new EquationSet(doc);
List<String> keypath = new ArrayList<String>(path.subList(1, path.size()));
EquationSet eold;
EquationSet enew;
if (oldPart == null) {
EquationSet p = (EquationSet) compiled.getObject(keypath);
eold = new EquationSet(p, nameBefore);
p.parts.add(eold);
keypath.add(nameAfter);
} else {
keypath.add(nameBefore);
eold = (EquationSet) compiled.getObject(keypath);
keypath.set(keypath.size() - 1, nameAfter);
}
enew = (EquationSet) compiled.getObject(keypath);
try {
compiled.resolveConnectionBindings();
} catch (Exception e) {
}
try {
compiled.resolveLHS();
compiled.resolveRHS();
} catch (Exception e) {
}
ChangeVariable.prepareConnections(compiled);
// Collect variables that might have changed.
List<Variable> users = collectVariables(compiled, eold);
if (eold.dependentConnections != null) {
// The variable associated with such a connection binding could explicitly mention the part name.
for (ConnectionBinding cb : eold.dependentConnections) users.add(cb.variable);
}
eold.name = enew.name;
for (Variable v : users) {
List<String> ref = v.getKeyPath();
MNode n = doc.child(ref.toArray());
String oldKey = n.key();
String newKey = ChangeVariable.changeReferences(eold, n, v);
if (// Handle a change in variable name.
!newKey.equals(oldKey)) {
NodeBase nb = pe.root.locateNodeFromHere(ref);
n.parent().move(oldKey, newKey);
ref.set(ref.size() - 1, newKey);
nb.source = (MPart) doc.child(ref.toArray());
}
// Queue GUI updates for nodes other than the primary ones.
if (v.container != enew && v.container != eold)
references.add(ref);
}
} catch (Exception e) {
}
// Change pin links to this part.
// Scan peer parts (which is the only place a pin link can be declared) and check for "bind" keys that reference nameBefore.
// for updating GUI later
Map<NodePart, List<String>> rebind = new HashMap<NodePart, List<String>>();
Enumeration<?> siblings = parent.children();
while (siblings.hasMoreElements()) {
Object o = siblings.nextElement();
if (!(o instanceof NodePart))
continue;
NodePart sibling = (NodePart) o;
MNode pins;
if (// because the old source is no longer attached to document
sibling == nodeBefore)
// because the old source is no longer attached to document
pins = parent.source.child(nameAfter, "$metadata", "gui", "pin", "in");
else
pins = sibling.source.child("$metadata", "gui", "pin", "in");
if (pins == null)
continue;
List<String> bound = null;
for (MNode pin : pins) {
if (pin.get("bind").equals(nameBefore)) {
pin.set(nameAfter, "bind");
// Also set the new name in collated pin data.
sibling.pinIn.set(nameAfter, pin.key(), "bind");
if (bound == null)
bound = new ArrayList<String>();
bound.add(pin.key());
}
}
if (bound != null)
rebind.put(sibling, bound);
}
// Check parent for pin exports.
MNode pins = parent.source.child("$metadata", "gui", "pin", "out");
if (pins != null) {
List<String> bound = null;
for (MNode pin : pins) {
if (pin.get("bind").equals(nameBefore)) {
pin.set(nameAfter, "bind");
if (bound == null)
bound = new ArrayList<String>();
bound.add(pin.key());
}
}
if (bound != null)
rebind.put(parent, bound);
}
// Update GUI
boolean graphParent = parent == pe.part;
PanelEquationTree pet = graphParent ? null : parent.getTree();
FilteredTreeModel model = null;
if (pet != null)
model = (FilteredTreeModel) pet.tree.getModel();
// Only used if graphParent is true.
PanelEquationGraph peg = pe.panelEquationGraph;
// It's either a NodePart or it's null. Any other case should be blocked by GUI constraints.
NodePart nodeAfter = (NodePart) parent.child(nameAfter);
boolean addGraphNode = false;
if (// Only one node will remain when we are done.
oldPart == null) {
pe.renameFocus(nodeBefore.getKeyPath(), nameAfter);
if (// This is a simple rename, with no restructuring. Keep nodeBefore.
nodeAfter == null) {
nodeAfter = nodeBefore;
nodeAfter.source = newPart;
if (graphParent)
peg.updatePart(nodeAfter);
} else // Use existing nodeAfter, so get rid of nodeBefore.
{
if (model == null)
FilteredTreeModel.removeNodeFromParentStatic(nodeBefore);
else
model.removeNodeFromParent(nodeBefore);
if (graphParent)
peg.removePart(nodeBefore, true);
}
} else // Need two nodes
{
if (// Need a node to hold the new part.
nodeAfter == null) {
int index = parent.getIndex(nodeBefore);
nodeAfter = new NodePart(newPart);
nodeAfter.hide = graphParent;
if (model == null)
FilteredTreeModel.insertNodeIntoUnfilteredStatic(nodeAfter, parent, index);
else
model.insertNodeIntoUnfiltered(nodeAfter, parent, index);
addGraphNode = true;
}
nodeBefore.build();
nodeBefore.findConnections();
nodeBefore.rebuildPins();
nodeBefore.filter();
if (nodeBefore.visible()) {
if (// Need to update entire model under fake root.
graphParent) {
PanelEquationTree subpet = nodeBefore.getTree();
if (subpet != null) {
FilteredTreeModel submodel = (FilteredTreeModel) subpet.tree.getModel();
submodel.nodeStructureChanged(nodeBefore);
subpet.animate();
}
} else if (model != null) {
model.nodeStructureChanged(nodeBefore);
}
} else {
parent.hide(nodeBefore, model);
}
}
nodeAfter.build();
if (graphParent)
parent.findConnections();
else
nodeAfter.findConnections();
nodeAfter.rebuildPins();
nodeAfter.filter();
pe.resetBreadcrumbs();
TreeNode[] nodePath = nodeAfter.getPath();
Set<PanelEquationTree> needAnimate = new HashSet<PanelEquationTree>();
if (pet == null) {
PanelEquationTree.updateOrder(null, nodePath);
PanelEquationTree.updateVisibility(null, nodePath, -2, false);
} else {
pet.updateOrder(nodePath);
// Will include nodeStructureChanged(), if necessary.
pet.updateVisibility(nodePath);
needAnimate.add(pet);
}
for (List<String> ref : references) {
NodeVariable n = (NodeVariable) pe.root.locateNodeFromHere(ref);
if (n == null)
continue;
// Rebuild n, because equations and/or their conditions may have changed.
n.build();
n.findConnections();
n.filter();
if (// n's visibility won't change
n.visible()) {
PanelEquationTree subpet = n.getTree();
if (subpet == null)
continue;
JTree subtree = subpet.tree;
FilteredTreeModel submodel = (FilteredTreeModel) subtree.getModel();
NodeBase subparent = (NodeBase) n.getParent();
// Node will collapse if it was open. Don't worry about this.
submodel.nodeStructureChanged(n);
subparent.invalidateColumns(submodel);
needAnimate.add(subpet);
}
}
for (NodePart peer : rebind.keySet()) {
PanelEquationTree subpet = peer.getTree();
// also works for parent
NodeBase metadata = peer.child("$metadata");
for (String pinKey : rebind.get(peer)) {
// Retrieve GUI metadata node so it can be updated to match DB.
NodeBase nodeBind;
if (peer == parent)
nodeBind = (NodeAnnotation) AddAnnotation.findExact(metadata, false, "gui", "pin", "out", pinKey, "bind");
else
nodeBind = (NodeAnnotation) AddAnnotation.findExact(metadata, false, "gui", "pin", "in", pinKey, "bind");
nodeBind.setUserObject();
// Update display tree.
if (subpet != null) {
// For simplicity, look up subtree, submodel and subparent each time,
// even though they could be done just once per peer part.
JTree subtree = subpet.tree;
FilteredTreeModel submodel = (FilteredTreeModel) subtree.getModel();
NodeBase subparent = (NodeBase) nodeBind.getParent();
submodel.nodeChanged(nodeBind);
subparent.invalidateColumns(submodel);
needAnimate.add(subpet);
}
}
}
for (PanelEquationTree ap : needAnimate) ap.animate();
if (graphParent) {
if (addGraphNode) {
// builds tree
peg.addPart(nodeAfter);
} else {
PanelEquationTree subpet = nodeAfter.getTree();
if (subpet != null) {
FilteredTreeModel submodel = (FilteredTreeModel) subpet.tree.getModel();
submodel.nodeStructureChanged(nodeAfter);
FocusCacheEntry fce = pe.createFocus(nodeAfter);
if (fce.sp != null)
fce.sp.restore(subpet.tree, false);
subpet.animate();
}
}
nodeAfter.hide = false;
nodeAfter.graph.takeFocusOnTitle();
peg.updatePins();
peg.reconnect();
peg.repaint();
}
}
use of gov.sandia.n2a.ui.eq.PanelEquations.FocusCacheEntry in project n2a by frothga.
the class PanelEquationTree method yieldFocus.
public void yieldFocus() {
tree.stopEditing();
if (root == null)
return;
FocusCacheEntry fce = container.createFocus(root);
fce.sp = saveFocus(fce.sp);
tree.clearSelection();
if (container.view == PanelEquations.NODE) {
// Auto-close graph node when it loses focus, if it was auto-opened.
if (root.graph != null) {
boolean open = root.source.getBoolean("$metadata", "gui", "bounds", "open");
if (!open)
root.graph.setOpen(false);
} else if (root == container.part) {
boolean open = root.source.getBoolean("$metadata", "gui", "bounds", "parent");
if (!open)
container.panelParent.setOpen(false);
}
} else {
if (root == container.part)
container.setSelected(false);
}
}
use of gov.sandia.n2a.ui.eq.PanelEquations.FocusCacheEntry in project n2a by frothga.
the class GraphNode method restoreFocus.
/**
* Subroutine of takeFocus(). Called either directly by takeFocus() or indirectly by title focus listener.
*/
public void restoreFocus() {
container.setSelected(false);
if (panelEquationTree == null) {
PanelEquationTree pet = container.panelEquationTree;
container.active = pet;
if (// Only load tree if node is not blank. Usually, a blank node is about to be deleted.
pet.root != node && !node.toString().isEmpty()) {
// Save current property panel UI state.
if (// Can it ever be null?
pet.root != null) {
FocusCacheEntry fce = container.createFocus(pet.root);
if (pet.root.graph != null)
fce.titleFocused = pet.root.graph.titleFocused;
fce.sp = pet.saveFocus(fce.sp);
}
// Load new part into property panel.
pet.loadPart(node);
FocusCacheEntry fce = container.createFocus(node);
if (fce.sp != null)
fce.sp.restore(pet.tree, false);
}
} else {
container.active = panelEquationTree;
}
parent.setComponentZOrder(this, 0);
parent.scrollRectToVisible(getBounds());
repaint();
// Since parent node is always on top, we must shift the graph to avoid occlusion.
if (container.panelParent.isVisible()) {
Point me = getLocation();
Dimension d = container.panelParent.getSize();
Point p = container.panelEquationGraph.vp.getViewPosition();
int ox = d.width - me.x + p.x;
int oy = d.height - me.y + p.y;
if (ox > 0 && oy > 0) {
if (ox < oy)
p.x -= ox;
else
p.y -= oy;
parent.layout.shiftViewport(p);
parent.revalidate();
parent.repaint();
}
}
}
use of gov.sandia.n2a.ui.eq.PanelEquations.FocusCacheEntry in project n2a by frothga.
the class PanelEquationTree method updateVisibility.
/**
* Ensure that the tree down to the changed node is displayed with correct visibility and override coloring.
* @param path Every node from root to changed node, including changed node itself.
* The trailing nodes are allowed to be disconnected from root in the filtered view of the model,
* and they are allowed to be deleted nodes. Note: deleted nodes will have null parents.
* Deleted nodes should already be removed from tree by the caller, with proper notification.
* @param index Position of the last node in its parent node. Only used if the last node has been deleted.
* A value of -1 causes selection to shift up to the parent.
* A value of -2 cause index to be derived from given path.
* @param setSelection Highlights the closest tree node to the given path. Only does something if tree is non-null.
*/
public static void updateVisibility(PanelEquationTree pet, TreeNode[] path, int index, boolean setSelection) {
// Calculate index, if requested
if (index == -2) {
if (path.length < 2) {
index = -1;
} else {
NodeBase c = (NodeBase) path[path.length - 1];
NodeBase p = (NodeBase) path[path.length - 2];
index = p.getIndexFiltered(c);
}
}
// Prepare list of indices for final selection
int[] selectionIndices = new int[path.length];
for (int i = 1; i < path.length; i++) {
NodeBase p = (NodeBase) path[i - 1];
NodeBase c = (NodeBase) path[i];
// Could be -1, if c has already been deleted.
selectionIndices[i] = FilteredTreeModel.getIndexOfChildStatic(p, c);
}
// Adjust visibility
int inserted = path.length;
int removed = path.length;
int removedIndex = -1;
for (int i = path.length - 1; i > 0; i--) {
NodeBase p = (NodeBase) path[i - 1];
NodeBase c = (NodeBase) path[i];
// skip deleted nodes
if (c.getParent() == null)
continue;
int filteredIndex = FilteredTreeModel.getIndexOfChildStatic(p, c);
boolean filteredOut = filteredIndex < 0;
if (c.visible()) {
if (filteredOut) {
// silently adjust the filtering
p.unhide(c, null);
// promise to notify model
inserted = i;
}
} else {
if (!filteredOut) {
p.hide(c, null);
removed = i;
removedIndex = filteredIndex;
}
}
}
// Everything below this line has to do with updating tree view.
if (pet == null)
return;
PanelEquations pe = PanelModel.instance.panelEquations;
FilteredTreeModel model = (FilteredTreeModel) pet.tree.getModel();
// update color to indicate override state
int lastChange = Math.min(inserted, removed);
for (int i = 1; i < lastChange; i++) {
// Since it is hard to measure current color, just assume everything needs updating.
NodeBase c = (NodeBase) path[i];
if (c.getParent() == null)
continue;
model.nodeChanged(c);
}
// Reconfigures the cell renderer, which will also include color change.
if (pet.root.graph != null)
pet.root.graph.title.updateSelected();
pe.breadcrumbRenderer.updateSelected();
// Force update of overall border. Everything gets repainted, so a bit inefficient.
pe.panelEquationGraph.repaint();
if (lastChange < path.length) {
NodeBase p = (NodeBase) path[lastChange - 1];
NodeBase c = (NodeBase) path[lastChange];
int[] childIndices = new int[1];
if (inserted < removed) {
childIndices[0] = p.getIndexFiltered(c);
model.nodesWereInserted(p, childIndices);
} else {
childIndices[0] = removedIndex;
Object[] childObjects = new Object[1];
childObjects[0] = c;
model.nodesWereRemoved(p, childIndices, childObjects);
}
}
// select last visible node
int i = 1;
for (; i < path.length; i++) {
NodeBase c = (NodeBase) path[i];
if (c.getParent() == null)
break;
if (!c.visible())
break;
}
// Choose the last good node
i--;
NodeBase c = (NodeBase) path[i];
if (i == path.length - 2) {
index = Math.min(index, model.getChildCount(c) - 1);
if (index >= 0)
c = (NodeBase) model.getChild(c, index);
} else if (i < path.length - 2) {
int childIndex = Math.min(selectionIndices[i + 1], model.getChildCount(c) - 1);
if (childIndex >= 0)
c = (NodeBase) model.getChild(c, childIndex);
}
TreePath selectedPath = new TreePath(c.getPath());
GraphNode gn = pet.root.graph;
if (setSelection && c != pet.root && pe.view == PanelEquations.NODE) {
// Ensure that tree is visible.
if (gn == null)
pe.panelParent.setOpen(true);
else
gn.setOpen(true);
}
if (lastChange == path.length) {
boolean expanded = pet.tree.isExpanded(selectedPath);
// Should this be more targeted?
model.nodeStructureChanged(c);
if (c != pet.root && expanded)
pet.tree.expandPath(selectedPath);
}
if (setSelection) {
if (c == pet.root) {
if (gn == null)
pe.switchFocus(true, false);
else
gn.switchFocus(true, false);
} else {
pet.tree.setSelectionPath(selectedPath);
FocusCacheEntry fce = pe.createFocus(pet.root);
// so pet.takeFocus() does not claw focus onto title
fce.titleFocused = false;
// Ensure that current name (in case of name change) is set as desired focus.
if (fce.sp != null)
fce.sp.updateSelection(c);
pet.takeFocus();
}
}
}
Aggregations