import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; import java.text.DecimalFormat; /** This class defines a custom JPanel that will draw vertices, edges, and the shortest path of a Graph object. This custom JPanel is used because it is much easier to intercept x and y coordinates in this panel than in the frame and then having to compensate for the other graphical components. */ public class GraphScreen extends JPanel implements MouseListener { /** defines the radius of the vertices in pixels */ private final int radius = 20; /** the path of vertices for the currently displayed shortest path */ private ArrayList path; /** the graph to add vertices/edges to */ private Graph mygraph; /** the status bar in the parent jframe */ private JLabel information; /* a flag to check if you are calculating the shortest path */ private boolean shortestpath; /* a flag used to check if the mouseclick intercepted was the second in the series (add edge, shortest path) */ private boolean issecondvertex; /* the data stored in the first vertex clicked in the series */ private Comparable firstvertex; /** * Default constructor for the GraphScreen panel.
* Preconditions: A JLabel status bar in the parent JFrame.
* Postconditions: Initializes all variables.
*/ public GraphScreen ( JLabel information ) { super ( true ); this.information = information; addMouseListener ( this ); mygraph = new Graph(); mygraph.setShortestPathDisplay ( new ArrayList() ); information.setText ( " Click to add vertex" ); issecondvertex = false; firstvertex = null; shortestpath = false; } /** mousePressed and mouseReleased could be used to move vertices around the graph, but as per explicitly stated in the program requirements, the GraphNodes have no setX ( int x ) and setY ( int y ) methods. */ public void mousePressed ( MouseEvent event ) {} public void mouseReleased ( MouseEvent event ) {} /** not used, just here to fully implement the MouseListener interface */ public void mouseEntered ( MouseEvent event ) {} public void mouseExited ( MouseEvent event ) {} /** * Intercepts a mouse click in the drawing window.
* Preconditions: A mouse click.
* Postconditions: This method could add a vertex, add an edge, or calculate where the shortest path needs to be based upon flags and user interaction.
*/ public void mouseClicked ( MouseEvent event ) { /* If it is a right mouse click, then we must add an edge */ if ( SwingUtilities.isRightMouseButton ( event ) ) { /* the node that is contained within radius pixels of the MouseEvent */ GraphNode temp = findVertex ( event.getX(), event.getY() ); /* if temp is null then the user clicked in blank space */ if ( temp != null ) { /* if this is the second right mouse click, and it is not the same vertex */ if ( issecondvertex && firstvertex.compareTo ( temp.getKey() ) != 0 ) { /* lets add an edge, and hopefully it will work */ try { addEdge ( firstvertex, temp.getKey() ); information.setText ( " Edge added between " + firstvertex.toString() + " and " + temp.getKey().toString() ); } /* error exists between keyboard and chair */ catch ( GraphException exception ) { information.setText ( " Could not add the edge" ); JOptionPane.showMessageDialog ( null, exception.toString(), "Exception caught", JOptionPane.WARNING_MESSAGE ); } /* we will set all the flags to normal */ issecondvertex = false; firstvertex = null; } /* this must be the first right click intercepted */ else { /* save the data for use later */ firstvertex = temp.getKey(); issecondvertex = true; information.setText ( " Right click on another vertex to create an edge" ); } } /* we don't care and set everything to normal since the user clicked in blank space */ else { firstvertex = null; issecondvertex = false; information.setText ( " Right click on vertices to add and edge" ); } } /* not the right mouse button, so this could be shortest path or add a vertex */ /* if the shortest path flag is set, we must find two different vertices */ else if ( shortestpath ) { /* if there are less than 2 vertices in the graph, it is impossible to find the shortest path */ if ( mygraph.numVertices() > 2 ) { GraphNode temp = findVertex ( event.getX(), event.getY() ); /* if this is the second click and the user clicked on a node and the node is not the same node */ if ( issecondvertex && temp != null && firstvertex.compareTo ( temp.getKey() ) != 0 ) { /* set the shortest path arraylist to the path in the graph */ try { mygraph.setShortestPathDisplay ( mygraph.shortestPath ( firstvertex, temp.getKey() ) ); information.setText ( " Shortest path is displayed in green" ); repaint(); } catch ( GraphException exception ) { JOptionPane.showMessageDialog ( null, exception.toString(), "Exception caught", JOptionPane.WARNING_MESSAGE ); mygraph.setShortestPathDisplay ( new ArrayList() ); information.setText ( " Could not create the shortest path" ); } shortestpath = false; issecondvertex = false; firstvertex = null; } /* the user is clicking on blank space */ else if ( temp == null ) { information.setText ( " You must first click on vertices" ); shortestpath = false; issecondvertex = false; firstvertex = null; } /* must be the first click in the series then */ else { firstvertex = temp.getKey(); issecondvertex = true; information.setText ( " Click on the ending vertex of the path" ); } } /* not enough vertices to make a shortest path */ else { information.setText ( " There must be at least 2 vertices in the graph before calculating the shortest path" ); shortestpath = false; firstvertex = null; issecondvertex = false; } } /* well, the only thing left is to add a new vertex */ else { /* if the user clicked in blank space for once */ if ( findVertex ( event.getX(), event.getY() ) == null ) { information.setText ( " Click to add vertex" ); shortestpath = false; issecondvertex = false; firstvertex = null; /* ask the user what they want to call it */ String name = JOptionPane.showInputDialog ( null, "Enter name: ","Add new graph node", JOptionPane.QUESTION_MESSAGE ); /* as long as the name appears somewhat valid anyways */ if ( name != null && !name.equals ( "" ) ) { int x = event.getX(), y = event.getY(); /* these if's check to make sure the user didn't click too close to the edge */ if ( x + radius > getWidth() ) x = getWidth() - radius; if ( x - radius < 0 ) x = radius; if ( y - radius > getHeight() ) y = getHeight() - radius; if ( y - radius < 0 ) y = radius; try { mygraph.addVertex ( new GraphNode ( name, x, y ) ); information.setText ( " Vertex added at (" + x + ", " + y + ")" ); repaint(); } catch ( Exception exception ) { JOptionPane.showMessageDialog ( null, exception.toString(), "Exception caught!", JOptionPane.WARNING_MESSAGE ); information.setText ( " Vertex could not be added" ); } } } /* the user clicked on a vertex */ else { information.setText ( " Vertex already added at location" ); JOptionPane.showMessageDialog ( null, "Vertex is already at that point!", "Waitaminit!", JOptionPane.WARNING_MESSAGE ); } } } /** * Empties the graph and sets all flags to default values.
* Preconditions: None.
* Postconditions: Resets all internally used variables to default values.
*/ public void clearGraph() { mygraph.makeEmpty(); information.setText ( " Click to add vertex" ); mygraph.setShortestPathDisplay ( new ArrayList() ); issecondvertex = false; firstvertex = null; shortestpath = false; repaint(); } /** * Used to set the flag to find the shortest path.
* Preconditions: None.
* Postconditions: Sets the shortestpath flag to true.
*/ public void findShortestPath() { shortestpath = true; information.setText ( " Click on the starting vertex" ); } /** * Attempts to locate a GraphNode within x +/- radius pixels and y +/- radius pixels of the given coordinate.
* Preconditions: Two integers representing coordinates inside the GraphScreen.
* Postconditions: Returns null if no GraphNode is close to the coord. or the GraphNode closest to that point.
*/ private GraphNode findVertex ( int x, int y ) { /* look through all of the nodes one by one */ for ( int i = 0; i < mygraph.numVertices(); i++ ) { GraphNode result = mygraph.getVertex ( i ); int tempx = result.getX(); int tempy = result.getY(); if ( tempx + radius >= x && tempx - radius <= x && tempy - radius <= y && tempy + radius >= y ) return result; } return null; } /** * Adds an edge between two vertices.
* Preconditions: Two comparable objects that are in the graph.
* Postconditions: Adds an edge between the two vertices containing the comparable objects.
*/ public void addEdge ( Comparable first, Comparable second ) { String temp = JOptionPane.showInputDialog ( null, "Enter a weight", "Add an edge", JOptionPane.QUESTION_MESSAGE ); double result = -1.0; if ( temp != null ) { try { result = Double.parseDouble ( temp ); if ( result < 0 ) throw new GraphException ( "Invalid weight for edge." ); mygraph.addEdge ( first, second, result ); information.setText ( " New edge added between vertices {" + (String) first + "} and {" + (String) second + "}" ); repaint(); } catch ( Exception exception ) { information.setText ( " Edge could not be added" ); JOptionPane.showMessageDialog ( null, exception.toString(), "Exception Caught", JOptionPane.WARNING_MESSAGE ); } } } /** * Draws the background for the custom JPanel.
*/ public void paint ( Graphics g ) { /* draw a black background */ g.setColor ( Color.black ); g.fillRect ( 0, 0, this.getWidth(), this.getHeight() ); mygraph.draw ( g, radius ); } }