Search in sources :

Example 1 with FocusCacheEntry

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();
    }
}
Also used : MPart(gov.sandia.n2a.eqset.MPart) AccessVariable(gov.sandia.n2a.language.AccessVariable) NodeVariable(gov.sandia.n2a.ui.eq.tree.NodeVariable) Variable(gov.sandia.n2a.eqset.Variable) HashMap(java.util.HashMap) PanelEquationGraph(gov.sandia.n2a.ui.eq.PanelEquationGraph) ConnectionBinding(gov.sandia.n2a.eqset.EquationSet.ConnectionBinding) ArrayList(java.util.ArrayList) PanelEquations(gov.sandia.n2a.ui.eq.PanelEquations) MNode(gov.sandia.n2a.db.MNode) NodeBase(gov.sandia.n2a.ui.eq.tree.NodeBase) NodeAnnotation(gov.sandia.n2a.ui.eq.tree.NodeAnnotation) TreeNode(javax.swing.tree.TreeNode) ArrayList(java.util.ArrayList) List(java.util.List) PanelEquationTree(gov.sandia.n2a.ui.eq.PanelEquationTree) FilteredTreeModel(gov.sandia.n2a.ui.eq.FilteredTreeModel) HashSet(java.util.HashSet) EquationSet(gov.sandia.n2a.eqset.EquationSet) CannotRedoException(javax.swing.undo.CannotRedoException) NodeVariable(gov.sandia.n2a.ui.eq.tree.NodeVariable) CannotRedoException(javax.swing.undo.CannotRedoException) JTree(javax.swing.JTree) FocusCacheEntry(gov.sandia.n2a.ui.eq.PanelEquations.FocusCacheEntry) NodePart(gov.sandia.n2a.ui.eq.tree.NodePart)

Example 2 with FocusCacheEntry

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);
    }
}
Also used : FocusCacheEntry(gov.sandia.n2a.ui.eq.PanelEquations.FocusCacheEntry)

Example 3 with FocusCacheEntry

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();
        }
    }
}
Also used : FocusCacheEntry(gov.sandia.n2a.ui.eq.PanelEquations.FocusCacheEntry) Point(java.awt.Point) Dimension(java.awt.Dimension) Point(java.awt.Point)

Example 4 with FocusCacheEntry

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();
        }
    }
}
Also used : NodeBase(gov.sandia.n2a.ui.eq.tree.NodeBase) TreePath(javax.swing.tree.TreePath) FocusCacheEntry(gov.sandia.n2a.ui.eq.PanelEquations.FocusCacheEntry)

Aggregations

FocusCacheEntry (gov.sandia.n2a.ui.eq.PanelEquations.FocusCacheEntry)4 NodeBase (gov.sandia.n2a.ui.eq.tree.NodeBase)2 MNode (gov.sandia.n2a.db.MNode)1 EquationSet (gov.sandia.n2a.eqset.EquationSet)1 ConnectionBinding (gov.sandia.n2a.eqset.EquationSet.ConnectionBinding)1 MPart (gov.sandia.n2a.eqset.MPart)1 Variable (gov.sandia.n2a.eqset.Variable)1 AccessVariable (gov.sandia.n2a.language.AccessVariable)1 FilteredTreeModel (gov.sandia.n2a.ui.eq.FilteredTreeModel)1 PanelEquationGraph (gov.sandia.n2a.ui.eq.PanelEquationGraph)1 PanelEquationTree (gov.sandia.n2a.ui.eq.PanelEquationTree)1 PanelEquations (gov.sandia.n2a.ui.eq.PanelEquations)1 NodeAnnotation (gov.sandia.n2a.ui.eq.tree.NodeAnnotation)1 NodePart (gov.sandia.n2a.ui.eq.tree.NodePart)1 NodeVariable (gov.sandia.n2a.ui.eq.tree.NodeVariable)1 Dimension (java.awt.Dimension)1 Point (java.awt.Point)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1