diff --git a/src/com/mxgraph/analysis/mxGraphStructure.java b/src/com/mxgraph/analysis/mxGraphStructure.java index 626322c..2b3e832 100644 --- a/src/com/mxgraph/analysis/mxGraphStructure.java +++ b/src/com/mxgraph/analysis/mxGraphStructure.java @@ -43,6 +43,9 @@ public static boolean isConnected(mxAnalysisGraph aGraph) //data preparation int connectedVertices = 1; + if(vertexNum < 1) { + return true; + } int[] visited = new int[vertexNum]; visited[0] = 1; diff --git a/src/com/mxgraph/app/designer/properties/editor.properties b/src/com/mxgraph/app/designer/properties/editor.properties index f16e5ae..b49ff65 100644 --- a/src/com/mxgraph/app/designer/properties/editor.properties +++ b/src/com/mxgraph/app/designer/properties/editor.properties @@ -33,6 +33,9 @@ pageLayout=Page Layout antialias=Antialias exitText=Are you sure you want to exit this program? analyze=Analyze +tools=Tools +makeComplementary=Invert connections +makeConnections=Make missing connections measureText=Measure distance between nodes notConnected=Some vertices are not connected! emptyValue=Entered {1} value is not valid! @@ -50,6 +53,10 @@ type=Type: cancel=Cancel calculate=Calculate totalDistance=Total minimal distance is: +isConnected=Check if all nodes are connected +diagramStatus=All nodes are {1} +connected=connected. +notconnected=not connected. grid=Grid rulers=Rulers zoom=Zoom @@ -116,6 +123,7 @@ west=West rotateLabel=Rotate Label rotation=Rotation image=Image +more=More style=Style shape=Shape home=Home @@ -217,3 +225,4 @@ backupRecommended=Backup in ETD Format Recommended loseDetails=Saving in Plain Topology Format removes all cosmetic information. Hence, it cannot be opened with Topology Editor again.\nConsider saving a copy as an ETD File. noLayout=Layout unavailable error=Error +information=Information diff --git a/src/com/mxgraph/examples/swing/editor/BasicGraphEditor.java b/src/com/mxgraph/examples/swing/editor/BasicGraphEditor.java index 514c5bb..17f01d8 100644 --- a/src/com/mxgraph/examples/swing/editor/BasicGraphEditor.java +++ b/src/com/mxgraph/examples/swing/editor/BasicGraphEditor.java @@ -1,6 +1,7 @@ package com.mxgraph.examples.swing.editor; import java.awt.BorderLayout; +import java.awt.Image; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -642,7 +643,7 @@ public Action bind(String name, final Action action) public Action bind(String name, final Action action, String iconUrl) { AbstractAction newAction = new AbstractAction(name, (iconUrl != null) ? new ImageIcon( - BasicGraphEditor.class.getResource(iconUrl)) : null) + new ImageIcon(BasicGraphEditor.class.getResource(iconUrl)).getImage().getScaledInstance(16, 16, Image.SCALE_DEFAULT)) : null) { public void actionPerformed(ActionEvent e) { diff --git a/src/com/mxgraph/examples/swing/editor/EditorMenuBar.java b/src/com/mxgraph/examples/swing/editor/EditorMenuBar.java index 33823aa..268ac25 100644 --- a/src/com/mxgraph/examples/swing/editor/EditorMenuBar.java +++ b/src/com/mxgraph/examples/swing/editor/EditorMenuBar.java @@ -4,6 +4,12 @@ import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + import javax.swing.AbstractAction; import javax.swing.JMenu; import javax.swing.JMenuBar; @@ -13,9 +19,13 @@ import javax.swing.TransferHandler; import javax.swing.UIManager; -import com.mxgraph.analysis.mxGraphProperties.GraphType; +import com.mxgraph.analysis.StructuralException; import com.mxgraph.analysis.mxAnalysisGraph; +import com.mxgraph.analysis.mxGraphProperties; +import com.mxgraph.analysis.mxGraphProperties.GraphType; import com.mxgraph.analysis.mxGraphStructure; +import com.mxgraph.analysis.mxTraversal; +import com.mxgraph.costfunction.mxCostFunction; import com.mxgraph.examples.swing.editor.EditorActions.AlignCellsAction; import com.mxgraph.examples.swing.editor.EditorActions.AutosizeAction; import com.mxgraph.examples.swing.editor.EditorActions.BackgroundAction; @@ -52,12 +62,14 @@ import com.mxgraph.examples.swing.editor.EditorActions.TogglePropertyItem; import com.mxgraph.examples.swing.editor.EditorActions.ToggleRulersItem; import com.mxgraph.examples.swing.editor.EditorActions.ZoomPolicyAction; +import com.mxgraph.model.mxIGraphModel; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.swing.util.mxGraphActions; import com.mxgraph.util.mxConstants; import com.mxgraph.util.mxPoint; import com.mxgraph.util.mxResources; import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; public class EditorMenuBar extends JMenuBar { @@ -67,7 +79,8 @@ public class EditorMenuBar extends JMenuBar { private static final long serialVersionUID = 4060203894740766714L; public enum AnalyzeType { - MAKE_CONNECTED + IS_CONNECTED, IS_SIMPLE, IS_CYCLIC_DIRECTED, IS_CYCLIC_UNDIRECTED, COMPLEMENTARY, REGULARITY, COMPONENTS, MAKE_CONNECTED, MAKE_SIMPLE, IS_TREE, ONE_SPANNING_TREE, IS_DIRECTED, GET_CUT_VERTEXES, GET_CUT_EDGES, GET_SOURCES, GET_SINKS, PLANARITY, IS_BICONNECTED, GET_BICONNECTED, SPANNING_TREE, FLOYD_ROY_WARSHALL + } public EditorMenuBar(final BasicGraphEditor editor) { @@ -75,6 +88,8 @@ public EditorMenuBar(final BasicGraphEditor editor) { final mxGraph graph = graphComponent.getGraph(); mxAnalysisGraph aGraph = new mxAnalysisGraph(); + + JMenu menu = null; JMenu submenu = null; @@ -438,9 +453,15 @@ public void actionPerformed(ActionEvent e) { }); } - // Creates a developer menu + // Creates a analyze menu menu = add(new JMenu(mxResources.get("analyze"))); menu.add(editor.bind(mxResources.get("measureText"), new InsertGraph(GraphType.DIJKSTRA, aGraph))); + menu.add(editor.bind(mxResources.get("isConnected"), new AnalyzeGraph(AnalyzeType.IS_CONNECTED, aGraph))); + + // Creates tools menu + menu = add(new JMenu(mxResources.get("tools"))); + menu.add(editor.bind(mxResources.get("makeComplementary"), new AnalyzeGraph(AnalyzeType.COMPLEMENTARY, aGraph))); + menu.add(editor.bind(mxResources.get("makeConnections"), new AnalyzeGraph(AnalyzeType.MAKE_CONNECTED, aGraph))); // Creates the help menu menu = add(new JMenu(mxResources.get("help"))); @@ -525,6 +546,85 @@ public static void populateShapeMenu(JMenu menu, BasicGraphEditor editor) { menu.add(editor.bind(mxResources.get("autosize"), new AutosizeAction())); + } + + /** + * Makes it possible to choose the image to be used for nodes. Add PNG images to add more images. The sizes + * are automatically resized to 16x16. + * @param menu + * @param editor + * @param startFrom + */ + public static void populateImageMenu(JMenu menu, BasicGraphEditor editor, int startFrom) { + try { + String menuText; + + if(startFrom == 0) { + menuText = mxResources.get("image"); + } else { + menuText = mxResources.get("more"); + } + + JMenu submenu = (JMenu) menu.add(new JMenu(menuText)); + + String relativeImagePath = "/com/mxgraph/examples/swing/images/"; + + String path = EditorMenuBar.class.getProtectionDomain().getCodeSource().getLocation().getPath() + relativeImagePath; + + final File jarFile = new File(EditorMenuBar.class.getProtectionDomain().getCodeSource().getLocation().getPath()); + + if(jarFile.isFile()) { // Run with JAR file + final JarFile jar = new JarFile(jarFile); + final Enumeration entries = jar.entries(); //gives ALL entries in jar + ArrayList images = new ArrayList(); + while(entries.hasMoreElements()) { + final String name = entries.nextElement().getName(); + if (name.startsWith(relativeImagePath.substring(1)) && name.endsWith(".png")) { //filter according to the path + images.add("/" + name); + } + else { + + } + } + for(int i = startFrom; i < images.size() && i < startFrom + 10; i++) { + submenu.add(editor.bind(images.get(i).substring(images.get(i).lastIndexOf("/") + 1), + new KeyValueAction(mxConstants.STYLE_IMAGE, + images.get(i)), + images.get(i))); + } + + if(startFrom < images.size()) { + populateImageMenu(submenu, editor, startFrom+10); + } + jar.close(); + } else { // Run with IDE + File f = new File(path); + File[] fileArray = f.listFiles(); + for(int i = startFrom; i < fileArray.length && i < startFrom + 10; i++) { + if(fileArray[i].getName().contains(".png")) { + submenu.add(editor.bind(fileArray[i].getName(), new KeyValueAction(mxConstants.STYLE_IMAGE, + relativeImagePath + fileArray[i].getName()), + relativeImagePath + fileArray[i].getName())); + } + else { + startFrom += 1; + } + } + + if(startFrom < fileArray.length) { + populateImageMenu(submenu, editor, startFrom+10); + } + } + + + + + } + catch(Exception e) { + e.printStackTrace(); + } + + } /** @@ -541,7 +641,8 @@ public static void populateFormatMenu(JMenu menu, BasicGraphEditor editor) { submenu.addSeparator(); - submenu.add(editor.bind(mxResources.get("image"), new PromptValueAction(mxConstants.STYLE_IMAGE, "Image"))); + populateImageMenu(submenu, editor, 0); + submenu.add(editor.bind(mxResources.get("shadow"), new ToggleAction(mxConstants.STYLE_SHADOW))); submenu.addSeparator(); @@ -835,5 +936,359 @@ else if(aGraph.getChildVertices(aGraph.getGraph().getDefaultParent()).length < 2 } } } + + public static class AnalyzeGraph extends AbstractAction + { + /** + * + */ + private static final long serialVersionUID = 6926170745240507985L; + + mxAnalysisGraph aGraph; + + /** + * + */ + protected AnalyzeType analyzeType; + + /** + * Examples for calling analysis methods from mxGraphStructure + */ + public AnalyzeGraph(AnalyzeType analyzeType, mxAnalysisGraph aGraph) + { + this.analyzeType = analyzeType; + this.aGraph = aGraph; + } + + public void actionPerformed(ActionEvent e) + { + if (e.getSource() instanceof mxGraphComponent) + { + mxGraphComponent graphComponent = (mxGraphComponent) e.getSource(); + mxGraph graph = graphComponent.getGraph(); + aGraph.setGraph(graph); + + if (analyzeType == AnalyzeType.IS_CONNECTED) + { + boolean isConnected = mxGraphStructure.isConnected(aGraph); + + String status = ""; + if (isConnected) + { + + status = mxResources.get("connected"); + } + else + { + status = mxResources.get("notconnected"); + } + JOptionPane.showMessageDialog(null, mxResources.get("diagramStatus", new String[] {status}), mxResources.get("information"), + JOptionPane.INFORMATION_MESSAGE); + } + else if (analyzeType == AnalyzeType.IS_SIMPLE) + { + boolean isSimple = mxGraphStructure.isSimple(aGraph); + + if (isSimple) + { + System.out.println("The graph is simple"); + } + else + { + System.out.println("The graph is not simple"); + } + } + else if (analyzeType == AnalyzeType.IS_CYCLIC_DIRECTED) + { + boolean isCyclicDirected = mxGraphStructure.isCyclicDirected(aGraph); + + if (isCyclicDirected) + { + System.out.println("The graph is cyclic directed"); + } + else + { + System.out.println("The graph is acyclic directed"); + } + } + else if (analyzeType == AnalyzeType.IS_CYCLIC_UNDIRECTED) + { + boolean isCyclicUndirected = mxGraphStructure.isCyclicUndirected(aGraph); + + if (isCyclicUndirected) + { + System.out.println("The graph is cyclic undirected"); + } + else + { + System.out.println("The graph is acyclic undirected"); + } + } + else if (analyzeType == AnalyzeType.COMPLEMENTARY) + { + graph.getModel().beginUpdate(); + + mxGraphStructure.complementaryGraph(aGraph); + + graph.getModel().endUpdate(); + } + else if (analyzeType == AnalyzeType.REGULARITY) + { + try + { + int regularity = mxGraphStructure.regularity(aGraph); + System.out.println("Graph regularity is: " + regularity); + } + catch (StructuralException e1) + { + System.out.println("The graph is irregular"); + } + } + else if (analyzeType == AnalyzeType.COMPONENTS) + { + Object[][] components = mxGraphStructure.getGraphComponents(aGraph); + mxIGraphModel model = aGraph.getGraph().getModel(); + + for (int i = 0; i < components.length; i++) + { + System.out.print("Component " + i + " :"); + + for (int j = 0; j < components[i].length; j++) + { + System.out.print(" " + model.getValue(components[i][j])); + } + + System.out.println("."); + } + + System.out.println("Number of components: " + components.length); + + } + else if (analyzeType == AnalyzeType.MAKE_CONNECTED) + { + graph.getModel().beginUpdate(); + + if (!mxGraphStructure.isConnected(aGraph)) + { + mxGraphStructure.makeConnected(aGraph); + } + + graph.getModel().endUpdate(); + } + else if (analyzeType == AnalyzeType.MAKE_SIMPLE) + { + mxGraphStructure.makeSimple(aGraph); + } + else if (analyzeType == AnalyzeType.IS_TREE) + { + boolean isTree = mxGraphStructure.isTree(aGraph); + + if (isTree) + { + System.out.println("The graph is a tree"); + } + else + { + System.out.println("The graph is not a tree"); + } + } + else if (analyzeType == AnalyzeType.ONE_SPANNING_TREE) + { + try + { + graph.getModel().beginUpdate(); + aGraph.getGenerator().oneSpanningTree(aGraph, true, true); + mxGraphStructure.setDefaultGraphStyle(aGraph, false); + graph.getModel().endUpdate(); + } + catch (StructuralException e1) + { + System.out.println("The graph must be simple and connected"); + } + } + else if (analyzeType == AnalyzeType.IS_DIRECTED) + { + boolean isDirected = mxGraphProperties.isDirected(aGraph.getProperties(), mxGraphProperties.DEFAULT_DIRECTED); + + if (isDirected) + { + System.out.println("The graph is directed."); + } + else + { + System.out.println("The graph is undirected."); + } + } + else if (analyzeType == AnalyzeType.GET_CUT_VERTEXES) + { + Object[] cutVertices = mxGraphStructure.getCutVertices(aGraph); + + System.out.print("Cut vertices of the graph are: ["); + mxIGraphModel model = aGraph.getGraph().getModel(); + + for (int i = 0; i < cutVertices.length; i++) + { + System.out.print(" " + model.getValue(cutVertices[i])); + } + + System.out.println(" ]"); + } + else if (analyzeType == AnalyzeType.GET_CUT_EDGES) + { + Object[] cutEdges = mxGraphStructure.getCutEdges(aGraph); + + System.out.print("Cut edges of the graph are: ["); + mxIGraphModel model = aGraph.getGraph().getModel(); + + for (int i = 0; i < cutEdges.length; i++) + { + System.out.print(" " + Integer.parseInt((String) model.getValue(aGraph.getTerminal(cutEdges[i], true))) + "-" + + Integer.parseInt((String) model.getValue(aGraph.getTerminal(cutEdges[i], false)))); + } + + System.out.println(" ]"); + } + else if (analyzeType == AnalyzeType.GET_SOURCES) + { + try + { + Object[] sourceVertices = mxGraphStructure.getSourceVertices(aGraph); + System.out.print("Source vertices of the graph are: ["); + mxIGraphModel model = aGraph.getGraph().getModel(); + + for (int i = 0; i < sourceVertices.length; i++) + { + System.out.print(" " + model.getValue(sourceVertices[i])); + } + + System.out.println(" ]"); + } + catch (StructuralException e1) + { + System.out.println(e1); + } + } + else if (analyzeType == AnalyzeType.GET_SINKS) + { + try + { + Object[] sinkVertices = mxGraphStructure.getSinkVertices(aGraph); + System.out.print("Sink vertices of the graph are: ["); + mxIGraphModel model = aGraph.getGraph().getModel(); + + for (int i = 0; i < sinkVertices.length; i++) + { + System.out.print(" " + model.getValue(sinkVertices[i])); + } + + System.out.println(" ]"); + } + catch (StructuralException e1) + { + System.out.println(e1); + } + } + else if (analyzeType == AnalyzeType.PLANARITY) + { + //TODO implement + } + else if (analyzeType == AnalyzeType.IS_BICONNECTED) + { + boolean isBiconnected = mxGraphStructure.isBiconnected(aGraph); + + if (isBiconnected) + { + System.out.println("The graph is biconnected."); + } + else + { + System.out.println("The graph is not biconnected."); + } + } + else if (analyzeType == AnalyzeType.GET_BICONNECTED) + { + //TODO implement + } + else if (analyzeType == AnalyzeType.SPANNING_TREE) + { + //TODO implement + } + else if (analyzeType == AnalyzeType.FLOYD_ROY_WARSHALL) + { + + ArrayList FWIresult = new ArrayList(); + try + { + //only this line is needed to get the result from Floyd-Roy-Warshall, the rest is code for displaying the result + FWIresult = mxTraversal.floydRoyWarshall(aGraph); + + Object[][] dist = FWIresult.get(0); + Object[][] paths = FWIresult.get(1); + Object[] vertices = aGraph.getChildVertices(aGraph.getGraph().getDefaultParent()); + int vertexNum = vertices.length; + System.out.println("Distances are:"); + + for (int i = 0; i < vertexNum; i++) + { + System.out.print("["); + + for (int j = 0; j < vertexNum; j++) + { + System.out.print(" " + Math.round((Double) dist[i][j] * 100.0) / 100.0); + } + + System.out.println("] "); + } + + System.out.println("Path info:"); + + mxCostFunction costFunction = aGraph.getGenerator().getCostFunction(); + mxGraphView view = aGraph.getGraph().getView(); + + for (int i = 0; i < vertexNum; i++) + { + System.out.print("["); + + for (int j = 0; j < vertexNum; j++) + { + if (paths[i][j] != null) + { + System.out.print(" " + costFunction.getCost(view.getState(paths[i][j]))); + } + else + { + System.out.print(" -"); + } + } + + System.out.println(" ]"); + } + + try + { + Object[] path = mxTraversal.getWFIPath(aGraph, FWIresult, vertices[0], vertices[vertexNum - 1]); + System.out.print("The path from " + costFunction.getCost(view.getState(vertices[0])) + " to " + + costFunction.getCost((view.getState(vertices[vertexNum - 1]))) + " is:"); + + for (int i = 0; i < path.length; i++) + { + System.out.print(" " + costFunction.getCost(view.getState(path[i]))); + } + + System.out.println(); + } + catch (StructuralException e1) + { + System.out.println(e1); + } + } + catch (StructuralException e2) + { + System.out.println(e2); + } + } + } + } + }; }; \ No newline at end of file