diff --git a/.gitignore b/.gitignore index b7b7f70..42c1ab3 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,6 @@ hs_err_pid* /build/ /ConversationEditor/build/ /dist/ -/nbproject/private/ \ No newline at end of file +/nbproject/private/ +/ConversationEditor/nbproject/private/ +/ConversationCompiler/nbproject/private/ \ No newline at end of file diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationConnectProvider.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationConnectProvider.java index b45a820..08be295 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationConnectProvider.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationConnectProvider.java @@ -45,14 +45,51 @@ public class ConversationConnectProvider implements ConnectProvider { JOptionPane.showMessageDialog(null, "Cannot attach end conversation node to responses/options!", "Conversation Script Creator", JOptionPane.INFORMATION_MESSAGE); return; } + ConversationNode cSource = ((ConversationWidget) source).getAttachedNode(); + ConversationNode cTarget = ((ConversationWidget) target).getAttachedNode(); + + if (cSource.isOption()) { + if (cTarget.isOption()) + return; + + else if (cTarget.isStartNode()) + return; + + } else if (cSource.isStartNode()) { + if (cTarget.isEndNode()) + return; + } else if (cSource.isEndNode()) { + return; + + } else { // Response nodes + if (cTarget.isEndNode()) + return; + + else if (cTarget.isStartNode()) + return; + } + + /* This won't work for preventing linking to same node 2x's. Need to find another way :( + if (scene.getConnectionLayer().getChildren().size() > 0) { + for (Widget widget : scene.getConnectionLayer().getChildren()) { + System.out.println("CHILD: " + widget.toString()); + if (!(widget instanceof ConnectionWidget)) { + continue; + } + + ConnectionWidget existingConn = (ConnectionWidget) widget; + if (existingConn.getTargetAnchor().getRelatedWidget() == target) { + System.out.println("SAME"); + return; + } + } + }*/ + ConnectionWidget conn = new ConnectionWidget(scene); conn.setTargetAnchorShape(AnchorShape.TRIANGLE_FILLED); conn.setTargetAnchor(AnchorFactory.createRectangularAnchor(target)); conn.setSourceAnchor(AnchorFactory.createRectangularAnchor(source)); scene.getConnectionLayer().addChild(conn); - - ((ConversationWidget)source).getAttachedNode().setCompiled(false); - ((ConversationWidget)target).getAttachedNode().setCompiled(false); } } } diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationNode.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationNode.java index a095489..7fa9b82 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationNode.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationNode.java @@ -8,7 +8,7 @@ import org.openide.nodes.Sheet; import org.openide.util.Lookup; public class ConversationNode extends AbstractNode implements Lookup.Provider { - private final int id; + private int id; private int optionId; private String displayText; private String stf; @@ -24,10 +24,10 @@ public class ConversationNode extends AbstractNode implements Lookup.Provider { this.stf = stf; this.option = isOption; this.displayText = ""; - this.id = id; this.endNode = isEndNode; this.startNode = isStartNode; this.optionId = optionId; + this.id = id; // Properties Window if (!isStartNode) @@ -76,6 +76,10 @@ public class ConversationNode extends AbstractNode implements Lookup.Provider { return id; } + public void setId(int id) { + this.id = id; + } + public boolean isCompiled() { return compiled; } diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationWidget.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationWidget.java index 1b7d18b..8258c0e 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationWidget.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/ConversationWidget.java @@ -18,13 +18,14 @@ import org.openide.util.LookupListener; public class ConversationWidget extends IconNodeWidget implements LookupListener { private final ConversationNode attachedNode; private final ExplorerManager mgr; + private boolean selected; public ConversationWidget(SceneView scene, final ExplorerManager mgr, final ConversationNode node, boolean endConversation) { super(scene.getScene()); this.attachedNode = node; this.mgr = mgr; - + if (node.isEndNode()) { setLabel(node.getStf()); setImage(ImageUtilities.loadImage("com/projectswg/tools/csc/conversationeditor/conversation_end.png")); @@ -50,10 +51,24 @@ public class ConversationWidget extends IconNodeWidget implements LookupListener public Point locationSuggested(Widget widget, Point originalLocation, Point suggestedLocation) { if (!(widget instanceof ConversationWidget)) return suggestedLocation; + ConversationWidget cWidget = (ConversationWidget) widget; + ConversationNode node = cWidget.getAttachedNode(); - if (((ConversationWidget) widget).getAttachedNode().isLocked()) + if (node.isLocked()) return originalLocation; + if (cWidget.getExpManager().getRootContext() != node) { + cWidget.getExpManager().setRootContext(node); + + ConversationNode[] nodes = new ConversationNode[1]; + nodes[0] = node; + try { + cWidget.getExpManager().setSelectedNodes(nodes); + } catch (PropertyVetoException ex) { + Exceptions.printStackTrace(ex); + } + } + return suggestedLocation; } }, ActionFactory.createDefaultMoveProvider())); @@ -98,6 +113,14 @@ public class ConversationWidget extends IconNodeWidget implements LookupListener return mgr; } + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + @Override public void resultChanged(LookupEvent ev) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/EditorTopComponent.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/EditorTopComponent.java index bd3182a..fcf0cb9 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/EditorTopComponent.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/EditorTopComponent.java @@ -1,30 +1,26 @@ package com.projectswg.tools.csc.conversationeditor; -import com.projectswg.tools.csc.conversationeditor.actions.SaveConversation; -import java.awt.Point; -import java.io.IOException; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.TransformerException; +import com.projectswg.tools.csc.conversationeditor.actions.OpenConversation; +import java.io.File; +import javax.swing.JOptionPane; import org.netbeans.api.settings.ConvertAsProperties; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; -import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.windows.TopComponent; -import org.xml.sax.SAXException; @ConvertAsProperties( dtd = "-//com.projectswg.tools.csc.conversationeditor//Editor//EN", - autostore = false + autostore = true ) @TopComponent.Description( preferredID = "EditorTopComponent", //iconBase="SET/PATH/TO/ICON/HERE", persistenceType = TopComponent.PERSISTENCE_ALWAYS ) -@TopComponent.Registration(mode = "editor", openAtStartup = true) +@TopComponent.Registration(mode = "editor", openAtStartup = false) @ActionID(category = "Window", id = "com.projectswg.tools.csc.conversationeditor.EditorTopComponent") @ActionReference(path = "Menu/Window" /*, position = 333 */) @TopComponent.OpenActionRegistration( @@ -40,41 +36,21 @@ public final class EditorTopComponent extends TopComponent implements ExplorerMa private final ExplorerManager mgr = new ExplorerManager(); private final SceneView scene; + private String activePath = ""; // New Conversation public EditorTopComponent() { initComponents(); setToolTipText(Bundle.HINT_EditorTopComponent()); - - SceneView scene = new SceneView(mgr); - scrollPane.setViewportView(scene.createView()); - - setName(Bundle.CTL_EditorTopComponent() + " - *New Conversation*"); + setName("*New Conversation*"); - scene.addNode(new ConversationNode("Begin Conversation", false, 0, false, true, 0)); - scene.addNode(new ConversationNode("End Conversation", false, 1, true, false, 0)); - + this.scene = new SceneView(mgr); + scrollPane.setViewportView(scene.createView()); associateLookup(ExplorerUtils.createLookup(mgr, getActionMap())); - this.scene = scene; + } - // Open Conversation - public EditorTopComponent(String convName) { - initComponents(); - - setToolTipText(Bundle.HINT_EditorTopComponent()); - - SceneView scene = new SceneView(mgr); - scene.setSceneName(convName); - - scrollPane.setViewportView(scene.createView()); - - setName(Bundle.CTL_EditorTopComponent() + " - " + (scene.getSceneName().equals("") ? "*New Conversation*" : scene.getSceneName())); - - associateLookup(ExplorerUtils.createLookup(mgr, getActionMap())); - this.scene = scene; - } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -100,30 +76,40 @@ public final class EditorTopComponent extends TopComponent implements ExplorerMa // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JScrollPane scrollPane; // End of variables declaration//GEN-END:variables + @Override public void componentOpened() { - // TODO add custom code on component opening + if (activePath.equals("")) { + // blankSlate(); + // scene.validate(); + return; + } + + File file = new File(activePath); + + if (!file.exists()) { + JOptionPane.showMessageDialog(null, "Couldn't open conversation file " + file.getAbsolutePath() + + " because it no longer exists.", "Conversation Script Editor", JOptionPane.INFORMATION_MESSAGE); + blankSlate(); + scene.validate(); + } else { + OpenConversation.open(file, this); + } } @Override public void componentClosed() { - /*try { - SaveConversation.save(scene); - } catch (ParserConfigurationException | IOException | SAXException | TransformerException ex) { - Exceptions.printStackTrace(ex); - }*/ + // TODO: save msg } void writeProperties(java.util.Properties p) { - // better to version settings since initial version as advocated at - // http://wiki.apidesign.org/wiki/PropertyFiles - p.setProperty("version", "1.0"); - // TODO store your settings + //p.setProperty("version", "1.0"); + p.setProperty("activePath", scene.getScenePath()); } void readProperties(java.util.Properties p) { - String version = p.getProperty("version"); - // TODO read your settings according to their version + //String version = p.getProperty("version"); + this.activePath = p.getProperty("activePath"); } @Override @@ -134,4 +120,9 @@ public final class EditorTopComponent extends TopComponent implements ExplorerMa public SceneView getScene() { return scene; } + + public void blankSlate() { + scene.addNode(new ConversationNode("Begin Conversation", false, 0, false, true, 0)); + scene.addNode(new ConversationNode("End Conversation", false, 1, true, false, 0)); + } } diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/SceneSaver.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/SceneSaver.java index a9bda0e..9bb751a 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/SceneSaver.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/SceneSaver.java @@ -3,10 +3,11 @@ package com.projectswg.tools.csc.conversationeditor; import java.awt.Point; import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; +import java.util.HashMap; import javax.swing.SwingUtilities; -import org.netbeans.api.visual.graph.GraphScene; +import org.netbeans.api.visual.anchor.AnchorFactory; +import org.netbeans.api.visual.anchor.AnchorShape; import org.netbeans.api.visual.widget.ConnectionWidget; import org.netbeans.api.visual.widget.Widget; import org.w3c.dom.Attr; @@ -27,7 +28,7 @@ public class SceneSaver { private static final String NODEID_ATTR = "nodeID"; // NOI18N private static final String X_NODE = "posX"; // NOI18N private static final String Y_NODE = "posY"; // NOI18N - private static final String SOURCE = "source"; + private static final String TARGETS = "targets"; private static final String LOCKED = "locked"; private static final String STF = "stf"; private static final String OPTION_ID = "optionId"; @@ -41,7 +42,8 @@ public class SceneSaver { Node properties = file.createElement("Properties"); root.appendChild(properties); - setTextProperty(properties, file, "name", scene.getSceneName()); + setTextProperty(properties, file, "zoom", Double.toString(scene.getZoomFactor())); + setTextProperty(properties, file, "highestId", String.valueOf(scene.getNextId())); Node sceneXMLNode = file.createElement(NODE_ELEMENT); root.appendChild(sceneXMLNode); @@ -49,7 +51,7 @@ public class SceneSaver { setAttribute(file, sceneXMLNode, VERSION_ATTR, VERSION_VALUE_1); // NOI18N LinkedHashMap> conversationLinks = scene.getConversationLinks(); - + for (ConversationNode node : scene.getNodes ()) { Widget widget = scene.findWidget(node); if (widget != null) { @@ -66,8 +68,7 @@ public class SceneSaver { else setAttribute(file, dataXMLNode, TYPE, "begin"); - if (!node.isStartNode()) - setAttribute(file, dataXMLNode, SOURCE, String.valueOf(getSourceNode(conversationLinks, node).getId())); + setAttribute(file, dataXMLNode, TARGETS, getTargets(conversationLinks, node)); setAttribute(file, dataXMLNode, NODEID_ATTR, String.valueOf(node.getId())); setAttribute(file, dataXMLNode, X_NODE, Integer.toString(location.x)); @@ -83,24 +84,19 @@ public class SceneSaver { return root; } - private ConversationNode getSourceNode(LinkedHashMap> conversationLinks, ConversationNode lookNode) { - for (Map.Entry> entry : conversationLinks.entrySet()) { - if (entry.getValue().contains(lookNode)) - return entry.getKey(); + private String getTargets(LinkedHashMap> conversationLinks, ConversationNode source) { + String targets = ""; + if (!conversationLinks.containsKey(source)) { + return targets; } - return null; + + for (ConversationNode nodes : conversationLinks.get(source)) { + targets += (targets.equals("") ? String.valueOf(nodes.getId()) : "," + String.valueOf(nodes.getId())); + } + + return targets; } - private ConversationNode getTargetNode(LinkedHashMap> conversationLinks, ConversationNode lookNode) { - for (Map.Entry> entry : conversationLinks.entrySet()) { - if (entry.getValue().contains(lookNode)) - return entry.getValue().get(entry.getValue().indexOf(lookNode)); - } - return null; - } - - // Returns true if deserialization is successfull - // sceneXMLNode has to be found in the XML file first public boolean deserializeData(final SceneView scene, final Node sceneXMLNode) { if (!VERSION_VALUE_1.equals(getAttributeValue(sceneXMLNode, VERSION_ATTR))) return false; @@ -108,7 +104,7 @@ public class SceneSaver { SwingUtilities.invokeLater (new Runnable() { @Override public void run() { - deserializeDataVersion1(scene, sceneXMLNode); + deserializeNodeDataVersion1(scene, sceneXMLNode); scene.validate (); } }); @@ -116,19 +112,86 @@ public class SceneSaver { return true; } - private void deserializeDataVersion1(SceneView scene, Node data) { + public void deserializeProperties(SceneView scene, Node node) { + System.out.println("Got a node!" + node.getNodeType()); + Element properties = (Element) node; + scene.setId(Integer.valueOf(properties.getElementsByTagName("highestId").item(0).getTextContent())); + } + + private void deserializeNodeDataVersion1(SceneView scene, Node data) { + HashMap> widgetLinks = new HashMap<>(); // map of what this widget id targets + HashMap widgets = new HashMap<>(); // map of all loaded widget's K = id, V = widget + for (Node node : getChildNode (data)) { if(NODE_NODE.equals(node.getNodeName())) { - String nodeID = getAttributeValue(node, NODEID_ATTR); + + int nodeID = Integer.parseInt(getAttributeValue(node, NODEID_ATTR)); int x = Integer.parseInt(getAttributeValue (node, X_NODE)); int y = Integer.parseInt(getAttributeValue (node, Y_NODE)); - Widget widget = scene.findWidget(nodeID); - if (widget != null) + int optionId = Integer.parseInt(getAttributeValue(node, OPTION_ID)); + + boolean locked = Boolean.parseBoolean(getAttributeValue(node, LOCKED)); + + String targets = getAttributeValue(node, TARGETS); + String stf = getAttributeValue(node, STF); + String type = getAttributeValue(node, TYPE); + + ConversationWidget widget = null; + // ConversationNode(String stf, boolean isOption, int id, boolean isEndNode, boolean isStartNode, int optionId) + switch (type) { + case "option": + widget = (ConversationWidget) scene.addNode(new ConversationNode(stf, true, nodeID, false, false, optionId)); + break; + case "response": + widget = (ConversationWidget) scene.addNode(new ConversationNode(stf, false, nodeID, false, false, optionId)); + break; + case "begin": + widget = (ConversationWidget) scene.addNode(new ConversationNode(stf, false, nodeID, false, true, optionId)); + break; + case "end": + widget = (ConversationWidget) scene.addNode(new ConversationNode(stf, false, nodeID, true, false, optionId)); + break; + } + + if (widget != null) { + widget.getAttachedNode().setLocked(locked); widget.setPreferredLocation (new Point (x, y)); + widgets.put(nodeID, widget); + + if (!targets.equals("")) { + ArrayList targetIds = new ArrayList<>(); + if (targets.contains(",")) { + String[] split = targets.split(","); + for (String s : split) { + targetIds.add(Integer.valueOf(s)); + } + } else { + targetIds.add(Integer.valueOf(targets)); + } + widgetLinks.put(nodeID, targetIds); + } + } } } + createConnections(widgetLinks, widgets, scene); } + + private void createConnections(HashMap> links, HashMap widgets, SceneView scene) { + for (Map.Entry> entry : links.entrySet()) { + //System.out.println("Creating links for " + widgets.get(entry.getKey()).getAttachedNode().getStf()); + for (Integer targetId : entry.getValue()) { + ConnectionWidget conn = new ConnectionWidget(scene); + conn.setTargetAnchorShape(AnchorShape.TRIANGLE_FILLED); + + conn.setTargetAnchor(AnchorFactory.createRectangularAnchor(widgets.get(targetId))); + conn.setSourceAnchor(AnchorFactory.createRectangularAnchor(widgets.get(entry.getKey()))); + + scene.getConnectionLayer().addChild(conn); + } + } + } + private static String getAttributeValue(Node node, String attr) { try { if (node != null) { @@ -164,5 +227,5 @@ public class SceneSaver { for (int i = 0; i < nodes.length; i++) nodes[i] = childNodes.item (i); return nodes; - } + } } diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/SceneView.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/SceneView.java index 7268b5c..1fd2609 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/SceneView.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/SceneView.java @@ -5,7 +5,6 @@ import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import javax.swing.JOptionPane; import org.netbeans.api.visual.action.ActionFactory; import org.netbeans.api.visual.graph.GraphScene; @@ -23,6 +22,9 @@ public class SceneView extends GraphScene{ private final ExplorerManager mgr; private String name = ""; + private String scenePath = ""; + + private int id = 1; public SceneView(ExplorerManager mgr) { setLookFeel(new ConversationLookFeel()); @@ -44,6 +46,7 @@ public class SceneView extends GraphScene{ @Override protected Widget attachNodeWidget(ConversationNode n) { + final ConversationWidget widget = new ConversationWidget(this, mgr, n, n.isEndNode()); widget.getActions().addAction(createObjectHoverAction()); @@ -106,6 +109,27 @@ public class SceneView extends GraphScene{ this.name = name; } + public String getScenePath() { + return this.scenePath; + } + + public void setScenePath(String path) { + this.scenePath = path; + } + + public ExplorerManager getManager() { + return mgr; + } + + public int getNextId() { + this.id = id +1; + return id++; + } + + public void setId(int id) { + this.id = id; + } + public LinkedHashMap> getConversationLinks() { List connectedNodes = connectionLayer.getChildren(); LinkedHashMap> conversationLinks = new LinkedHashMap<>(); diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvEnd.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvEnd.java index 2f404cb..a434312 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvEnd.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvEnd.java @@ -44,7 +44,7 @@ public final class NewConvEnd implements ActionListener { if (scene == null) return; - int id = scene.getNodes().size(); + int id = scene.getNextId(); scene.addNode(new ConversationNode("New End Conversation " + String.valueOf(id), false, id, true, false, 0)); scene.validate(); diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvOption.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvOption.java index d93b06f..e23a69d 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvOption.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvOption.java @@ -39,7 +39,7 @@ public final class NewConvOption implements ActionListener { if (scene == null) return; - int id = scene.getNodes().size(); + int id = scene.getNextId(); scene.addNode(new ConversationNode("New Conversation Option " + String.valueOf(id), true, id, false, false, 0)); scene.validate(); diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvResponse.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvResponse.java index e0b5fe8..8bed03c 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvResponse.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConvResponse.java @@ -44,7 +44,7 @@ public final class NewConvResponse implements ActionListener { if (scene == null) return; - int id = scene.getNodes().size(); + int id = scene.getNextId(); scene.addNode(new ConversationNode("New Conversation Response " + String.valueOf(id), false, id, false, false, 0)); scene.validate(); } diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConversation.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConversation.java index cd39e53..120a717 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConversation.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/NewConversation.java @@ -33,6 +33,8 @@ public final class NewConversation implements ActionListener { @Override public void actionPerformed(ActionEvent e) { EditorTopComponent newEditor = new EditorTopComponent(); + newEditor.blankSlate(); + newEditor.getScene().validate(); newEditor.open(); newEditor.requestActive(); } diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/OpenConversation.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/OpenConversation.java index 1755b3a..98504d9 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/OpenConversation.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/OpenConversation.java @@ -1,22 +1,34 @@ package com.projectswg.tools.csc.conversationeditor.actions; +import com.projectswg.tools.csc.conversationeditor.EditorTopComponent; +import com.projectswg.tools.csc.conversationeditor.SceneSaver; +import com.projectswg.tools.csc.conversationeditor.SceneView; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; +import java.io.IOException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; import org.openide.filesystems.FileChooserBuilder; +import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; @ActionID(category = "File", id = "org.mycore.OpenFileAction") -@ActionRegistration(displayName = "Open Conversation") +@ActionRegistration(displayName = "Open") @ActionReferences({ @ActionReference(path = "Menu/File", position = 10, separatorAfter = 11), @ActionReference(path = "Shortcuts", name = "D-O") }) -@Messages("CTL_OpenFileAction=Open Conversation") +@Messages("CTL_OpenFileAction=Open") public final class OpenConversation implements ActionListener { @Override @@ -28,8 +40,50 @@ public final class OpenConversation implements ActionListener { setDefaultWorkingDirectory(home).setApproveText("Open").showOpenDialog(); //Result will be null if the user clicked cancel or closed the dialog w/o OK if (toAdd != null) { - // TODO: + EditorTopComponent editor = new EditorTopComponent(); + open(toAdd, editor); + editor.open(); + editor.requestActive(); } } + public static void open(File openFile, EditorTopComponent editor) { + try { + if (!openFile.getName().endsWith(".xml")) { + System.out.println("Not a valid Conversation Editor file!"); + return; + } + + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(openFile); + doc.getDocumentElement().normalize(); + + if (!doc.getDocumentElement().getNodeName().equals("Conversation")) { + System.out.println("Not a valid Conversation Editor file!"); + return; + } + + SceneSaver saver = new SceneSaver(); + + SceneView scene = editor.getScene(); + + Node propertiesNode = doc.getElementsByTagName("Properties").item(0); + saver.deserializeProperties(scene, propertiesNode); + + Node sceneNode = doc.getElementsByTagName("SceneView").item(0); + if (sceneNode == null) { + System.out.println("NULL SCENE"); + return; + } + scene.setSceneName(openFile.getName().split(".xml")[0]); + scene.setScenePath(openFile.getAbsolutePath()); + + saver.deserializeData(scene, sceneNode); + editor.setName(scene.getSceneName()); + + } catch (ParserConfigurationException | SAXException | IOException ex) { + Exceptions.printStackTrace(ex); + } + } } diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/SaveConversation.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/SaveConversation.java index 88d2e81..ce96d27 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/SaveConversation.java +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/SaveConversation.java @@ -12,6 +12,8 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; +import java.io.PrintWriter; +import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -32,7 +34,6 @@ import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.windows.TopComponent; import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXException; @@ -42,14 +43,14 @@ import org.xml.sax.SAXException; ) @ActionRegistration( iconBase = "com/projectswg/tools/csc/conversationeditor/actions/conversation_tb_save.png", - displayName = "Save Conversation" + displayName = "Save" ) @ActionReferences({ @ActionReference(path = "Menu/File", position = -90, separatorBefore = -140), @ActionReference(path = "Toolbars/File", position = 500), @ActionReference(path = "Shortcuts", name = "D-S") }) -@Messages("CTL_SaveConversation=Save Conversation") +@Messages("CTL_SaveConversation=Save") public final class SaveConversation implements ActionListener { @Override @@ -58,43 +59,55 @@ public final class SaveConversation implements ActionListener { if (component == null || !(component instanceof EditorTopComponent)) return; - EditorTopComponent editor = (EditorTopComponent) component; + final EditorTopComponent editor = (EditorTopComponent) component; final SceneView scene = editor.getScene(); if (scene == null) return; - if (scene.getSceneName().equals("")) { - File home = new File(System.getProperty("user.home")); + File existingFile = null; + + if (!scene.getScenePath().equals("")) + existingFile = new File(scene.getScenePath()); + + if (existingFile == null || !existingFile.exists()) { + saveAs(scene, editor); + } else { + final String path = existingFile.getAbsolutePath(); + SwingUtilities.invokeLater(new Runnable() { - FileChooserBuilder builder = new FileChooserBuilder("user-dir").setTitle("Save Conversation").setDefaultWorkingDirectory(home).setApproveText("Save"); - builder.setAcceptAllFileFilterUsed(true); - - final File file = builder.showSaveDialog(); - - if (file != null) { - scene.setSceneName(file.getName().split(".xml")[0]); - SwingUtilities.invokeLater(new Runnable() { - - @Override - public void run() { - try { - save(scene, file.getAbsolutePath()); - } catch (ParserConfigurationException | IOException | SAXException | TransformerException ex) { - Exceptions.printStackTrace(ex); - } + @Override + public void run() { + try { + handleSave(scene, editor, path); + } catch (ParserConfigurationException | IOException | SAXException | TransformerException ex) { + Exceptions.printStackTrace(ex); } - }); - editor.setName("Conversation Editor - " + scene.getSceneName()); - } + } + }); } } - public static void save(SceneView scene, String path) throws ParserConfigurationException, IOException, SAXException, TransformerConfigurationException, TransformerException { + public static void handleSave(final SceneView scene, final EditorTopComponent editor, String path) + throws ParserConfigurationException, IOException, SAXException, TransformerConfigurationException, TransformerException { SceneSaver saver = new SceneSaver(); - File xmlFile = new File(path + ".xml"); - if (!xmlFile.exists()) + if (!path.endsWith(".xml")) + path = path + ".xml"; + + File xmlFile = new File(path); + if (!scene.getScenePath().equals(path) && xmlFile.exists()) { + int result = JOptionPane.showConfirmDialog(null, "This file already exists. Do you wish to overwrite it?", + "Conversation Script Creator", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); + if (result == JOptionPane.YES_OPTION) { + PrintWriter writer = new PrintWriter(xmlFile); + writer.print(""); + writer.flush(); + } else { + return; + } + } + else xmlFile.createNewFile(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); @@ -109,9 +122,38 @@ public final class SaveConversation implements ActionListener { DOMSource source = new DOMSource(document); StreamResult result = new StreamResult(xmlFile); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.transform(source, result); + + if (!scene.getSceneName().equals(xmlFile.getName().split(".xml")[0])) + scene.setSceneName(xmlFile.getName().split(".xml")[0]); + + scene.setScenePath(xmlFile.getAbsolutePath()); + editor.setName(scene.getSceneName()); + } + + public static void saveAs(final SceneView scene, final EditorTopComponent editor) { + File home = new File(System.getProperty("user.home")); + + FileChooserBuilder builder = new FileChooserBuilder("user-dir").setTitle("Save Conversation").setDefaultWorkingDirectory(home).setApproveText("Save"); + builder.setAcceptAllFileFilterUsed(true); + + final File file = builder.showSaveDialog(); + + if (file != null) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + try { + handleSave(scene, editor, file.getAbsolutePath()); + } catch (ParserConfigurationException | IOException | SAXException | TransformerException ex) { + Exceptions.printStackTrace(ex); + } + } + }); + } } } diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/TrashConvNode.java b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/TrashConvNode.java new file mode 100644 index 0000000..14cb774 --- /dev/null +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/TrashConvNode.java @@ -0,0 +1,108 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.projectswg.tools.csc.conversationeditor.actions; + +import com.projectswg.tools.csc.conversationeditor.ConversationNode; +import com.projectswg.tools.csc.conversationeditor.ConversationWidget; +import com.projectswg.tools.csc.conversationeditor.EditorTopComponent; +import com.projectswg.tools.csc.conversationeditor.SceneView; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.swing.SwingUtilities; +import org.netbeans.api.visual.widget.ConnectionWidget; +import org.netbeans.api.visual.widget.Widget; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionReferences; +import org.openide.awt.ActionRegistration; +import org.openide.nodes.Node; +import org.openide.util.NbBundle.Messages; +import org.openide.windows.TopComponent; + +@ActionID( + category = "Edit", + id = "com.projectswg.tools.csc.conversationeditor.actions.TrashConvNode" +) +@ActionRegistration( + iconBase = "com/projectswg/tools/csc/conversationeditor/actions/conversation_tb_trash.png", + displayName = "Trash Node" +) +@ActionReferences({ + @ActionReference(path = "Menu/Edit", position = 1475), + @ActionReference(path = "Toolbars/Edit", position = 450), + @ActionReference(path = "Shortcuts", name = "DELETE") +}) +@Messages("CTL_TrashConvNode=Trash Node") +public final class TrashConvNode implements ActionListener { + + @Override + public void actionPerformed(ActionEvent e) { + TopComponent component = TopComponent.getRegistry().getActivated(); + if (component == null || !(component instanceof EditorTopComponent)) + return; + + final EditorTopComponent editor = (EditorTopComponent) component; + + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + trashActiveNodes(editor); + } + }); + + } + + private static void trashActiveNodes(final EditorTopComponent editor) { + SceneView scene = editor.getScene(); + + if (scene == null) + return; + + Node[] selectedNodes = scene.getManager().getSelectedNodes(); + + HashMap widgets = new HashMap<>(); + + // Get the nodes widgets & remove from scene + for (Node n : selectedNodes) { + ConversationWidget found = (ConversationWidget) scene.findWidget(n); + if (found == null) { + System.err.println("COULDNT FIND WIDGET TO REMOVE"); + return; + } + widgets.put(found, (ConversationNode) n); + found.removeFromParent(); + } + + // Remove connections for the node + ArrayList connections = new ArrayList<>(); + + for (Widget child : scene.getConnectionLayer().getChildren()) { + if (!(child instanceof ConnectionWidget)) + continue; + + ConnectionWidget connection = (ConnectionWidget) child; + if (widgets.containsKey((ConversationWidget) connection.getTargetAnchor().getRelatedWidget())) { + connections.add(child); + } + + if (widgets.containsKey((ConversationWidget) connection.getSourceAnchor().getRelatedWidget())) { + connections.add(child); + } + } + + scene.getConnectionLayer().removeChildren(connections); + + for (Node n : selectedNodes) { + scene.removeNode((ConversationNode) n); + } + scene.validate(); + } +} diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/conversation_tb_trash.png b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/conversation_tb_trash.png new file mode 100644 index 0000000..83ae75f Binary files /dev/null and b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/conversation_tb_trash.png differ diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/conversation_tb_trash24.png b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/conversation_tb_trash24.png new file mode 100644 index 0000000..3e887d2 Binary files /dev/null and b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/actions/conversation_tb_trash24.png differ diff --git a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/layer.xml b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/layer.xml index 2ceb81b..57f4107 100644 --- a/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/layer.xml +++ b/ConversationEditor/src/com/projectswg/tools/csc/conversationeditor/layer.xml @@ -19,6 +19,18 @@ + + + + + + + + + + + + @@ -66,21 +78,37 @@ + + + + + + + + + + + + + + + +