/* * $Id$ * * Copyright (c) 2000-2003 by Rodney Kinney * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library 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 GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ package VASSAL.configure; import java.awt.Color; import java.awt.Component; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JOptionPane; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import VASSAL.build.Buildable; import VASSAL.build.Builder; import VASSAL.build.Configurable; import VASSAL.build.module.ExtensionElement; import VASSAL.build.module.ModuleExtension; import VASSAL.build.module.documentation.HelpWindow; import VASSAL.build.widget.PieceSlot; import VASSAL.launch.EditorWindow; import VASSAL.tools.ReflectionUtils; /** * The configuration tree for editing a module extension */ public class ExtensionTree extends ConfigureTree { private static final long serialVersionUID = 1L; private ModuleExtension extension; public ExtensionTree(Configurable root, HelpWindow helpWindow, ModuleExtension extention, EditorWindow editorWindow) { super(root, helpWindow, editorWindow); this.extension = extention; setCellRenderer(new ExtensionRenderer()); } private boolean isEditable(DefaultMutableTreeNode node) { if (node != null) { for (ExtensionElement el : extension.getComponentsOf(ExtensionElement.class)) { if (el.getExtension() == node.getUserObject()) { return true; } } if (node.getParent() instanceof DefaultMutableTreeNode) { return isEditable((DefaultMutableTreeNode) node.getParent()); } } return false; } public Configurable[] getPath(DefaultMutableTreeNode node) { Object[] nodePath = node.getUserObjectPath(); Configurable[] path = new Configurable[nodePath.length - 1]; for (int i = 0; i < path.length; ++i) { path[i] = (Configurable) nodePath[i + 1]; } return path; } public void externalInsert(Configurable parent, Configurable child) { super.externalInsert(parent, child); if (!isEditable(parent)) { extension.add(new ExtensionElement(child, getPath(getTreeNode(parent)))); } } protected Action buildAddAction(final Configurable target, final Class<? extends Buildable> newConfig) { return new AbstractAction("Add " + getConfigureName(newConfig)) { private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent evt) { Configurable ch = null; try { ch = (Configurable) newConfig.getConstructor().newInstance(); } catch (Throwable t) { ReflectionUtils.handleNewInstanceFailure(t, newConfig); } if (ch != null) { final Configurable child = ch; child.build(null); if (child instanceof PieceSlot) { ((PieceSlot) child).updateGpId(extension); } if (child.getConfigurer() != null) { if (insert(target, child, getTreeNode(target).getChildCount())) { PropertiesWindow w = new PropertiesWindow((Frame) SwingUtilities.getAncestorOfClass(Frame.class, ExtensionTree.this), false, child, helpWindow) { private static final long serialVersionUID = 1L; public void save() { super.save(); if (!isEditable(target)) { ExtensionElement el = new ExtensionElement(child, getPath(getTreeNode(target))); extension.add(el); } } public void cancel() { ExtensionTree.this.remove(target, child); dispose(); } }; w.setVisible(true); } } else { boolean inserted = insert(target, child, getTreeNode(target).getChildCount()); if (inserted && !isEditable(getTreeNode(target))) { extension.add(new ExtensionElement(child, getPath(getTreeNode(target)))); } } } } }; } protected Action buildImportAction(final Configurable target) { Action a = new AbstractAction("Add Imported Class") { private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent evt) { final Configurable child = importConfigurable(); if (child != null) { try { child.build(null); final Configurable c = target; if (child.getConfigurer() != null) { PropertiesWindow w = new PropertiesWindow((Frame) SwingUtilities.getAncestorOfClass(Frame.class, ExtensionTree.this), false, child, helpWindow) { private static final long serialVersionUID = 1L; public void save() { super.save(); insert(c, child, getTreeNode(c).getChildCount()); if (!isEditable(target)) { ExtensionElement el = new ExtensionElement(child, getPath(getTreeNode(target))); extension.add(el); } } public void cancel() { dispose(); } }; w.setVisible(true); } else { insert(c, child, getTreeNode(c).getChildCount()); } } // FIXME: review error message catch (Exception ex) { JOptionPane.showMessageDialog (getTopLevelAncestor(), "Error adding " + getConfigureName(child) + " to " + getConfigureName(target) + "\n" + ex.getMessage(), "Illegal configuration", JOptionPane.ERROR_MESSAGE); } } } }; return a; } private boolean isEditable(final Configurable target) { return isEditable(getTreeNode(target)); } protected Action buildEditAction(Configurable target) { return isEditable(target) ? super.buildEditAction(target) : null; } protected Action buildCutAction(Configurable target) { return isEditable(target) ? super.buildCutAction(target) : null; } protected Action buildPasteAction(final Configurable target) { Action a = null; if (isEditable(target)) { a = super.buildPasteAction(target); } else { a = new AbstractAction("Paste") { private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent e) { if (cutData != null) { DefaultMutableTreeNode targetNode = getTreeNode(target); Configurable cutTarget = (Configurable) cutData.getUserObject(); if (remove(getParent(cutData), cutTarget)) { if (insert(target, cutTarget, targetNode.getChildCount())) { extension.add(new ExtensionElement(cutTarget, getPath(targetNode))); } } } else if (copyData != null) { final Configurable copyTarget = (Configurable) copyData.getUserObject(); Configurable clone = null; try { clone = copyTarget.getClass().getConstructor().newInstance(); } catch (Throwable t) { ReflectionUtils.handleNewInstanceFailure( t, copyTarget.getClass()); } if (clone != null) { clone.build( copyTarget.getBuildElement(Builder.createNewDocument())); if (insert(target, clone, getTreeNode(target).getChildCount())) { updateGpIds(clone); extension.add( new ExtensionElement(clone, getPath(getTreeNode(target)))); } } } cutData = null; updateEditMenu(); } }; a.setEnabled(isValidPasteTarget(target)); } return a; } protected boolean isValidPasteTarget(Configurable target) { return (cutData != null && super.isValidParent(target, (Configurable) cutData.getUserObject()) && isEditable(getParent(cutData))) || (copyData != null && super.isValidParent(target, (Configurable) copyData.getUserObject())); } /** * Allocate new PieceSlot Id's to any PieceSlot sub-components * * @param c Configurable to update */ public void updateGpIds(Configurable c) { if (c instanceof PieceSlot) { ((PieceSlot) c).updateGpId(extension); } else { for (Configurable conf : c.getConfigureComponents()) updateGpIds(conf); } } protected Action buildMoveAction(Configurable target) { return isEditable((DefaultMutableTreeNode) getTreeNode(target).getParent()) ? super.buildMoveAction(target) : null; } protected Action buildDeleteAction(final Configurable target) { Action action = null; final DefaultMutableTreeNode targetNode = getTreeNode(target); if (targetNode.getParent() != null && isEditable(targetNode)) { final Configurable parent = (Configurable) ((DefaultMutableTreeNode) targetNode.getParent()).getUserObject(); action = new AbstractAction("Delete") { private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent evt) { boolean removed = remove(parent, target); if (removed && !isEditable(parent)) { // We've removed an ExtensionElement for (ExtensionElement el : extension.getComponentsOf(ExtensionElement.class)) { if (el.getExtension() == target) { extension.remove(el); break; } } } } }; } return action; } protected Action buildCloneAction(final Configurable target) { final DefaultMutableTreeNode targetNode = getTreeNode(target); final DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) targetNode.getParent(); if (isEditable(parentNode)) { return super.buildCloneAction(target); } if (parentNode != null) { return new AbstractAction("Clone") { private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent evt) { Configurable clone = null; try { clone = target.getClass().getConstructor().newInstance(); } catch (Throwable t) { ReflectionUtils.handleNewInstanceFailure(t, target.getClass()); } if (clone != null) { clone.build(target.getBuildElement(Builder.createNewDocument())); if (insert((Configurable) parentNode.getUserObject(), clone, parentNode.getChildCount())) { extension.add(new ExtensionElement(clone, getPath(parentNode))); } } } }; } else { return null; } } protected Action buildEditPiecesAction(Configurable target) { Action a = null; if (isEditable(target)) { a = super.buildEditPiecesAction(target); } return a; } public void mousePressed(MouseEvent e) { TreePath path = getPathForLocation(e.getX(), e.getY()); if (path != null) { if (isEditable((DefaultMutableTreeNode) path.getLastPathComponent())) { super.mousePressed(e); } } } protected boolean isValidParent(Configurable parent, Configurable child) { return super.isValidParent(parent, child) && isEditable(parent); } protected void updateEditMenu() { super.updateEditMenu(); deleteAction.setEnabled(selected != null && isEditable(selected)); cutAction.setEnabled(selected != null && isEditable(selected)); // cutAction.setEnabled(selected != null && isEditable(selected)); propertiesAction.setEnabled(selected != null && isEditable(selected) && selected.getConfigurer() != null); } /** * Change color of component names based on editable status */ class ExtensionRenderer extends Renderer { private static final long serialVersionUID = 1L; public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { Component c = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); c.setForeground(isEditable((DefaultMutableTreeNode) value) ? Color.BLACK : Color.GRAY); return c; } } }