package gradeapp; import com.jgraph.layout.JGraphFacade; import com.jgraph.layout.tree.JGraphTreeLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Map; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingConstants; import org.jgraph.JGraph; import org.jgraph.graph.DefaultCellViewFactory; import org.jgraph.graph.DefaultEdge; import org.jgraph.graph.DefaultGraphCell; import org.jgraph.graph.DefaultGraphModel; import org.jgraph.graph.DefaultPort; import org.jgraph.graph.GraphConstants; import org.jgraph.graph.GraphLayoutCache; /** * A Panel that conains a JScrollPane that contains a JGraph. * This holds all of the details of how the Graph is rendered. * In the context of this program it is mostly used by * {@link GradeApp} * @author sandro */ public class GRender extends JPanel{ private static final String GRAPH = "Graph"; private static final String LOAD = "Load"; //lets us swap between showing the "click on the load button!" text //and actually showing the graph private CardLayout swingLayout = new CardLayout(); private JGraph graph; private DefaultGraphModel model; private GraphLayoutCache view; private DefaultGraphCell root; private JGraphFacade facade; private JGraphTreeLayout graphLayout; /** * Returns an instance where the JGraph is blank. */ public GRender(){ super(); //Make a graph model = new DefaultGraphModel(); view = new GraphLayoutCache(model, new DefaultCellViewFactory()); graph = new JGraph(model, view); graph.setAntiAliased(true); graph.setDisconnectOnMove(true); graph.setConnectable(true); graph.setDisconnectOnMove(false); graph.setDisconnectable(false); graph.setEdgeLabelsMovable(false); //Configure our layout graphLayout = new JGraphTreeLayout(); graphLayout.setAlignment(SwingConstants.TOP); graphLayout.setOrientation(SwingConstants.NORTH); graphLayout.setCombineLevelNodes(false); //Add it to our selves setLayout(swingLayout); JLabel loadText = new JLabel("Click on the \"Load *.xls\" button above"+ " to load a spreadsheet", JLabel.CENTER); add(loadText, LOAD); add(new JScrollPane(graph), GRAPH); swingLayout.show(this, LOAD); } /** * Same as {@link #render(gradeapp.MinedTree) } except it does the following: * <code>render(Graph.getGraph());</code> This method does nothing if * the graph or tree is null. */ public void render(){ Graph g = Graph.getGraph(); if (g == null) return; MinedTree tree = g.getTree(); if (tree == null) return; render(tree); } /** * Does the hard work of deleting the current tree, creating a graph * from the given tree and displayig it on the jGraph. * @param tree the tree to render. */ public void render(MinedTree tree){ swingLayout.show(this, GRAPH); graph.setModel(new DefaultGraphModel());//Clear the tree by replacing //the model with a blank new one ArrayList<DefaultGraphCell> cells = buildCellsFromTree(tree, true); root = cells.get(0); //Add the new tree DefaultGraphCell[] acells = cells.toArray(new DefaultGraphCell[]{}); graph.getGraphLayoutCache().insert(acells); //Run the layout facade = new JGraphFacade(graph, new Object[]{root}); graphLayout.run(facade); Map nested = facade.createNestedMap(true, true); graph.getGraphLayoutCache().edit(nested);//Apply the layout's measurements repaint();//Repaint! } /** * Takes care of all of the hardwork that goes into builing a JGraph from * a MinedTree. This method takes care of traversing the tree, and creating * Cells using {@link #getCell(gradeapp.MinedTree, boolean) } and putting edges * between them using {@link #getEdge(org.jgraph.graph.DefaultGraphCell, org.jgraph.graph.DefaultGraphCell) }. * This method only has the algoritm for conversion and the structure between * cells. To change how things look, go to getEdge or getCell. * @param tree the Tree to turn into a graph. * @return the list of cells that make up the graph to be rendered. Note: * the first element of this list is the root of the tree. */ private ArrayList<DefaultGraphCell> buildCellsFromTree(MinedTree tree, boolean gotItCorrect){ ArrayList<DefaultGraphCell> cells = new ArrayList<DefaultGraphCell>(); DefaultGraphCell cell = getCell(tree, gotItCorrect); DefaultPort port = new DefaultPort(); cell.add(port); cells.add(cell); if (tree.right != null && tree.wrong != null) { ArrayList<DefaultGraphCell> wrongCells = buildCellsFromTree(tree.wrong, false); ArrayList<DefaultGraphCell> rightCells = buildCellsFromTree(tree.right, true); cells.addAll(wrongCells); cells.addAll(rightCells); cells.add(getEdge(cell, wrongCells.get(0))); cells.add(getEdge(cell, rightCells.get(0))); } return cells; } /** * Given 2 cells it creates an edge between them. Edit this to change what * edges look like. * @param source The cell from which a cell begins * @param target The cell in which a cell ends * @return The edge that is created */ protected DefaultEdge getEdge(DefaultGraphCell source, DefaultGraphCell target){ DefaultEdge edge = new DefaultEdge(); edge.setSource(source.getChildAt(0)); edge.setTarget(target.getChildAt(0)); GraphConstants.setLineEnd(edge.getAttributes(), GraphConstants.ARROW_CLASSIC); GraphConstants.setEndFill(edge.getAttributes(), true); GraphConstants.setEditable(edge.getAttributes(), false); GraphConstants.setSelectable(edge.getAttributes(), false); return edge; } /** * Transforms a MinedTree Node from boothe into a DefaultGraphCell. * This basically converts from boothe -> JGraph. Edit this to change how * cells look. Since cell looks are dependent on whether the cell is correct * or not, we need to know if they got it correct or not. * @param tree the tree from the boothe algorithm to render * @param gotItCorrect whether this cell got the question correct or not. * @return the graphcell that represents the MinedTree passed. */ protected DefaultGraphCell getCell(MinedTree tree, boolean gotItCorrect){ String s = "<HTML>"; //You make new lines in JGraph by rendering the //With HTML. Crazy? yes. See jgraphmanual.pdf //Whenever there is a <BR> that's a new line. if (tree.question != -1) { s+= "If they got question " + tree.question + (gotItCorrect ? " right" : " wrong") + " then<BR>"; } int good = tree.count(true, tree.students); int total = tree.students.size(); int bad = total-good; int goodPercent = (int)(((float)good/total)*100); int badPercent = 100 - goodPercent; s += bad + " out of " + total+ " did poorly (" + badPercent + "%)"; s += "<BR>"; s += good + " out of " + total + " did well (" + goodPercent +"%)"; s += "</HTML>"; DefaultGraphCell cell = new DefaultGraphCell(s); //Just need to pick an arbitrary size, the layout goes and overrides this GraphConstants.setBounds(cell.getAttributes(), new Rectangle2D.Double(140,140,40,20)); //I like orange. GraphConstants.setGradientColor(cell.getAttributes(), Color.ORANGE); //Apparently we need to make the cell Opaque? GraphConstants.setOpaque(cell.getAttributes(), true); //Resize depending on the size of the text that this cell contains GraphConstants.setAutoSize(cell.getAttributes(), true); //No editing!!!! GraphConstants.setEditable(cell.getAttributes(), false); return cell; } /** * Gives access to the JGraph object that is rendering the graph. * @return the JGraph object showing the graph */ public JGraph getGraph(){ return graph; } }