/******************************************************************************* * Mission Control Technologies, Copyright (c) 2009-2012, United States Government * as represented by the Administrator of the National Aeronautics and Space * Administration. All rights reserved. * * The MCT platform is licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * MCT includes source code licensed under additional open source licenses. See * the MCT Open Source Licenses file included with this distribution or the About * MCT Licenses dialog available at runtime from the MCT Help menu for additional * information. *******************************************************************************/ package gov.nasa.arc.mct.defaults.view; import gov.nasa.arc.mct.components.AbstractComponent; import gov.nasa.arc.mct.gui.AbstractViewListener; import gov.nasa.arc.mct.gui.MCTMutableTreeNode; import gov.nasa.arc.mct.gui.View; import gov.nasa.arc.mct.gui.util.GUIUtil; import gov.nasa.arc.mct.platform.spi.PlatformAccess; import gov.nasa.arc.mct.policy.PolicyContext; import gov.nasa.arc.mct.policy.PolicyInfo; import gov.nasa.arc.mct.roles.events.AddChildEvent; import gov.nasa.arc.mct.roles.events.FocusEvent; import gov.nasa.arc.mct.roles.events.PropertyChangeEvent; import gov.nasa.arc.mct.roles.events.ReloadEvent; import gov.nasa.arc.mct.roles.events.RemoveChildEvent; import gov.nasa.arc.mct.services.component.PolicyManager; import gov.nasa.arc.mct.services.component.ViewInfo; import gov.nasa.arc.mct.services.component.ViewType; import gov.nasa.arc.mct.util.MCTIcons; import gov.nasa.arc.mct.util.internal.ElapsedTimer; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JTree; import javax.swing.event.TreeExpansionEvent; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SuppressWarnings("serial") public class NodeViewManifestation extends View { private static final Logger PERF_LOGGER = LoggerFactory.getLogger("gov.nasa.arc.mct.performance.node"); private final JLabel spacebar = new JLabel(" "); private JLabel label; private MCTMutableTreeNode node; private PropertyChangeListener objectStaleListener = new PropertyChangeListener() { @Override public void propertyChange(java.beans.PropertyChangeEvent evt) { if ((Boolean) evt.getNewValue()) { if (getManifestedComponent().getComponentId() != null) { AbstractComponent committedComponent = PlatformAccess.getPlatform().getPersistenceProvider().getComponent(getManifestedComponent().getComponentId()); setManifestedComponent(committedComponent); updateMonitoredGUI(); } } } }; public static final String DEFAULT_NODE_VIEW_ROLE_NAME = "DefaultNodeView"; /** * For Internal Use only */ public NodeViewManifestation() { super(); } public NodeViewManifestation(AbstractComponent component, ViewInfo viewinfo) { super(component,viewinfo); label = new JLabel(component.getExtendedDisplayName()); label.putClientProperty("TITLE", true); setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); add(new JLabel(getIcon(component))); add(spacebar); add(label); doLockRendering(label,component); setViewListener(new NodeViewManifestationListener()); addPropertyChangeListener(VIEW_STALE_PROPERTY, objectStaleListener); } private ImageIcon getIcon(AbstractComponent ac) { ImageIcon baseIcon = ac.getAsset(ImageIcon.class); return MCTIcons.processIcon(baseIcon, new Color(129, 154, 204), new Color(101, 131, 192), false); } private void doLockRendering(JLabel widget, AbstractComponent comp) { widget.getFont(); if (comp != null) { comp.getId(); } } @Override public Dimension getPreferredSize() { Dimension defaultDim = super.getPreferredSize(); Dimension newSize = new Dimension(defaultDim.width, defaultDim.height+5); return newSize; } @Override public void viewPersisted() { updateMonitoredGUI(); } @Override public void updateMonitoredGUI() { // Sets display name if changed boolean labelChanged = false; if (!label.getText().equals(getManifestedComponent().getExtendedDisplayName())) { label.setText(getManifestedComponent().getExtendedDisplayName()); labelChanged = true; } if (node != null) { JTree parentTree = node.getParentTree(); if (parentTree == null) return; DefaultTreeModel treeModel = (DefaultTreeModel) parentTree.getModel(); if (labelChanged) treeModel.nodeChanged(node); if (node.isProxy()) return; // First, remove any placeholder nodes for (int i = 0; i < node.getChildCount(); i++) { while (i < node.getChildCount() && !(node.getChildAt(i) instanceof MCTMutableTreeNode)) { node.remove(i); } } // Check if a node structure refresh is necessary List<AbstractComponent> visibleChildComponents = new ArrayList<AbstractComponent>(); for (AbstractComponent childComponent : getManifestedComponent().getComponents()) { visibleChildComponents.add(childComponent); } if (visibleChildComponents.size() == node.getChildCount()) { boolean changed = false; // Same number of children - but have any changed or moved? for (int index = 0; index < visibleChildComponents.size(); index++) { MCTMutableTreeNode treeNode = (MCTMutableTreeNode) node.getChildAt(index); AbstractComponent nodeComponent = ((View) treeNode.getUserObject()).getManifestedComponent(); if (!nodeComponent.getComponentId().equals(visibleChildComponents.get(index).getComponentId())) { changed = true; break; } } if (!changed) return; // Don't continue with refresh if children are unchanged. } // Note currently expanded nodes to restore state after re-ordering Set<String> expanded = new HashSet<String>(); for (int index = 0; index < node.getChildCount(); index++) { MCTMutableTreeNode childNode = (MCTMutableTreeNode) node.getChildAt(index); View childView = (View) childNode.getUserObject(); if (parentTree.isExpanded(childNode.getTreePath())) expanded.add(childView.getManifestedComponent().getComponentId()); } // Insert nodes at the bottom which reflect current structure... for (AbstractComponent childComponent : visibleChildComponents) { Set<ViewInfo> viewInfos = childComponent.getViewInfos(ViewType.NODE); if (!node.isProxy()) { MCTMutableTreeNode childNode = GUIUtil.cloneTreeNode(childComponent,viewInfos.iterator() .next()); node.addChild(node.getChildCount(), childNode, objectStaleListener); } } // ...and then remove the old nodes from the top. (Removing first would cause node to collapse.) while (node.getChildCount() > visibleChildComponents.size()) { node.removeChild((MCTMutableTreeNode) node.getChildAt(0), objectStaleListener); } // Finally, restore selection paths. for (int index = 0; index < node.getChildCount(); index++) { MCTMutableTreeNode childNode = (MCTMutableTreeNode) node.getChildAt(index); View childView = (View) childNode.getUserObject(); if (expanded.contains(childView.getManifestedComponent().getComponentId())) { parentTree.expandPath(childNode.getTreePath()); childNode.setProxy(false); // If expanded node is mislabeled as proxy, it will lose updates } } treeModel.nodeChanged(node); } } @Override public void updateMonitoredGUI(AddChildEvent event) { if (node != null) { AbstractComponent parentComponent = ((View) node.getUserObject()).getManifestedComponent(); PolicyContext context = new PolicyContext(); context.setProperty(PolicyContext.PropertyName.TARGET_COMPONENT.getName(), parentComponent); context.setProperty(PolicyContext.PropertyName.ACTION.getName(), 'w'); PolicyManager policyManager = PlatformAccess.getPlatform().getPolicyManager(); if (!policyManager.execute(PolicyInfo.CategoryType.OBJECT_INSPECTION_POLICY_CATEGORY.getKey(), context).getStatus()) return; AbstractComponent childComponent = event.getChildComponent(); Set<ViewInfo> viewInfos = childComponent.getViewInfos(ViewType.NODE); if (!node.isProxy()) { MCTMutableTreeNode childNode = GUIUtil.cloneTreeNode(childComponent, viewInfos.iterator() .next()); node.addChild(event.getChildIndex(), childNode, objectStaleListener); } } } @Override public void updateMonitoredGUI(RemoveChildEvent event) { if (node != null && !node.isProxy()) { AbstractComponent targetChildComponent = event.getChildComponent(); for (int i = 0; i < node.getChildCount(); i++) { MCTMutableTreeNode childNode = (MCTMutableTreeNode) node.getChildAt(i); AbstractComponent childComponent = ((View) childNode.getUserObject()).getManifestedComponent(); if (targetChildComponent.equals(childComponent)) { node.removeChild(childNode, objectStaleListener); } } } } @Override public void updateMonitoredGUI(FocusEvent event) { JTree tree = node.getParentTree(); DefaultTreeModel treeModel = (DefaultTreeModel) tree.getModel(); List<TreePath> treePaths = new ArrayList<TreePath>(); for (int i = 0; i < node.getChildCount(); i++) { MCTMutableTreeNode childNode = (MCTMutableTreeNode) node.getChildAt(i); View childGUIComponent = (View) childNode.getUserObject(); AbstractComponent childComponent = childGUIComponent.getManifestedComponent(); for (AbstractComponent focusComponent : event.getFocusComponents()) { if (focusComponent.getComponentId().equals(childComponent.getComponentId())) { treePaths.add(new TreePath(treeModel.getPathToRoot(childNode))); } } } if (treePaths.size() > 0) { tree.setSelectionPaths(treePaths.toArray(new TreePath[treePaths.size()])); } } @Override public void updateMonitoredGUI(PropertyChangeEvent event) { Object property = event.getProperty(PropertyChangeEvent.DISPLAY_NAME); if (property != null) { label.setText(getManifestedComponent().getExtendedDisplayName()); if (node != null) ((DefaultTreeModel) this.node.getParentTree().getModel()).nodeChanged(node); } } @Override public void updateMonitoredGUI(ReloadEvent event) { updateMonitoredGUI(); if (node != null && node.getChildCount() == 0) { node.setProxy(true); node.add(new MCTMutableTreeNode(View.NULL_VIEW_MANIFESTATION)); } } @Override public <T> void addMonitoredGUI(T gui) { this.node = (MCTMutableTreeNode) gui; } public MCTMutableTreeNode getMCTMutableTreeNode() { return node; } @Override public void enterLockedState() { label.setFont(label.getFont().deriveFont(Font.BOLD)); repaintIfTree(node); } @Override public void exitLockedState() { StringBuilder text = new StringBuilder(label.getText()); if (text.charAt(0) == '*') { label.setText(text.substring(1)); } label.setFont(label.getFont().deriveFont(Font.PLAIN)); repaintIfTree(node); } // This view role may be used outside a tree. protected void repaintIfTree(MCTMutableTreeNode treeNode) { if (treeNode != null && treeNode.getParentTree() != null) { treeNode.getParentTree().repaint(); } } @Override public View getParentView() { if (node != null) { TreeNode parentNode = node.getParent(); if (parentNode instanceof MCTMutableTreeNode) { return ((View) ((MCTMutableTreeNode) parentNode).getUserObject()); } } // If the parent can't be found, return default (null) return super.getParentView(); } protected class NodeViewManifestationListener extends AbstractViewListener { @Override public void actionPerformed(TreeExpansionEvent event) { final ElapsedTimer timer = new ElapsedTimer(); timer.startInterval(); JTree tree = (JTree) event.getSource(); final DefaultTreeModel treeModel = (DefaultTreeModel) tree.getModel(); final MCTMutableTreeNode selectedNode = node; AbstractComponent component = getManifestedComponent(); for (AbstractComponent childComponent : component.getComponents()) { Set<ViewInfo> nodeViews = childComponent.getViewInfos(ViewType.NODE); if (!nodeViews.isEmpty()) { ViewInfo nextViewInfo = nodeViews.iterator().next(); // Children are only allowed if the component is not a leaf or the component is another users drop box boolean allowsChildren =!childComponent.isLeaf(); View childNodeView = nextViewInfo.createView(childComponent); MCTMutableTreeNode childNode = new MCTMutableTreeNode(childNodeView, tree, allowsChildren); if (allowsChildren){ MCTMutableTreeNode grandChildNode = new MCTMutableTreeNode(View.NULL_VIEW_MANIFESTATION, tree); childNode.add(grandChildNode); childNode.setProxy(true); } selectedNode.add(childNode); childNodeView.addPropertyChangeListener(VIEW_STALE_PROPERTY, objectStaleListener); } } treeModel.reload(selectedNode); timer.stopInterval(); PERF_LOGGER.debug("Time to expand node {}: {}", component.getId(), timer.getIntervalInMillis()); } } }