package view; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JLayeredPane; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import org.eclipse.elk.graph.ElkNode; import animation.Action; import animation.AnimationController; import animation.PseudoCodeNode; import bk.BKNodePlacement; import bk.ExtremalLayoutCalc.LayoutType; import graph.InitializeNodePositions; import graph.LayeredGraphEdge; import graph.LayeredGraphNode; import graph.LayeredNode; import graph.RandomGraphGenerator; import graph.io.Reader; import graph.io.Writer; import lib.SweepCrossingMinimizer; import lib.TextLayoutHelper; /** * The main window of the application. * There should only be one instance of this class at the same time. * The JFrame of that single instance can be accessed by the static field {code MainView.frame}. * @author kolja * */ public class MainView { /** * The 'frame' of the main window. * The reason why there can only be one instance of this class. */ private static int frameCounter = 0; private JFrame frame; private AnimationController controller; private JButton stepForward; private JButton stepForwardInto; private JButton stepForwardOut; private JButton stepBackward; private JButton stepBackwardInto; private JButton stepBackwardOut; private JButton runForward; private JButton runBackward; private JButton pause; private JButton load; private JButton save; private JButton debug; private JButton randomGraph; private JLabel delayText; private JTextField delay; private JTree pseudoTree; private LayeredGraphNode graph; private String debugInfo() { String info = "Debug Information Table: \n"; info += "_______________________________________________________________________________________________________________________________________________________________________________________________________________________\n"; info += "|" + TextLayoutHelper.strToLen( "Top -> Bottom :> Left", 51 ) + "| |" + TextLayoutHelper.strToLen( "Top -> Bottom :> Right", 51 ) + "| |" + TextLayoutHelper.strToLen( "Bottom -> Top :> Left", 51 ) + "| |" + TextLayoutHelper.strToLen( "Bottom -> Top :> Right", 51 ) + "|\n"; info += "|___________________________________________________| |___________________________________________________| |___________________________________________________| |___________________________________________________|\n"; info += "| Node | Shift | Sink | Root | Align | x | xDef | | Node | Shift | Sink | Root | Align | x | xDef | | Node | Shift | Sink | Root | Align | x | xDef | | Node | Shift | Sink | Root | Align | x | xDef |\n"; for( LayeredGraphNode n : graph.getContainedNodes() ) { info += "|" + TextLayoutHelper.strToLen( n.getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getShift( LayoutType.TOP_BOTTOM_LEFT ) + "", 7 ) + "|" + TextLayoutHelper.strToLen( n.getSink( LayoutType.TOP_BOTTOM_LEFT ).getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getRoot( LayoutType.TOP_BOTTOM_LEFT ).getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getAlign( LayoutType.TOP_BOTTOM_LEFT ).getName(), 7 ) + "|" + TextLayoutHelper.strToLen( n.getX( LayoutType.TOP_BOTTOM_LEFT ) + "", 5 ) + "|" + TextLayoutHelper.strToLen( !n.isXUndefined( LayoutType.TOP_BOTTOM_LEFT ) + "", 8 ) + "| " + "|" + TextLayoutHelper.strToLen( n.getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getShift( LayoutType.TOP_BOTTOM_RIGHT ) + "", 7 ) + "|" + TextLayoutHelper.strToLen( n.getSink( LayoutType.TOP_BOTTOM_RIGHT ).getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getRoot( LayoutType.TOP_BOTTOM_RIGHT ).getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getAlign( LayoutType.TOP_BOTTOM_RIGHT ).getName(), 7 ) + "|" + TextLayoutHelper.strToLen( n.getX( LayoutType.TOP_BOTTOM_RIGHT ) + "", 5 ) + "|" + TextLayoutHelper.strToLen( !n.isXUndefined( LayoutType.TOP_BOTTOM_RIGHT ) + "", 8 ) + "| " + "|" + TextLayoutHelper.strToLen( n.getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getShift( LayoutType.BOTTOM_TOP_LEFT ) + "", 7 ) + "|" + TextLayoutHelper.strToLen( n.getSink( LayoutType.BOTTOM_TOP_LEFT ).getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getRoot( LayoutType.BOTTOM_TOP_LEFT ).getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getAlign( LayoutType.BOTTOM_TOP_LEFT ).getName(), 7 ) + "|" + TextLayoutHelper.strToLen( n.getX( LayoutType.BOTTOM_TOP_LEFT ) + "", 5 ) + "|" + TextLayoutHelper.strToLen( !n.isXUndefined( LayoutType.BOTTOM_TOP_LEFT ) + "", 8 ) + "| " + "|" + TextLayoutHelper.strToLen( n.getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getShift( LayoutType.BOTTOM_TOP_RIGHT ) + "", 7 ) + "|" + TextLayoutHelper.strToLen( n.getSink( LayoutType.BOTTOM_TOP_RIGHT ).getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getRoot( LayoutType.BOTTOM_TOP_RIGHT ).getName(), 6 ) + "|" + TextLayoutHelper.strToLen( n.getAlign( LayoutType.BOTTOM_TOP_RIGHT ).getName(), 7 ) + "|" + TextLayoutHelper.strToLen( n.getX( LayoutType.BOTTOM_TOP_RIGHT ) + "", 5 ) + "|" + TextLayoutHelper.strToLen( !n.isXUndefined( LayoutType.BOTTOM_TOP_RIGHT ) + "", 8 ) + "|\n"; } info += "-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; return info; } private void showDebugInfo() { JFrame debugFrame = new JFrame(); JTextArea info = new JTextArea(); info.setEditable( false ); info.setFont( new Font( Font.MONOSPACED, Font.PLAIN, 11 ) ); String infoS = debugInfo(); info.setText( infoS ); JScrollPane view = new JScrollPane( info ); debugFrame.add( view ); debugFrame.setSize( frame.getWidth(), frame.getHeight() ); debugFrame.setVisible( true ); System.out.println( infoS ); } public MainView( ElkNode graph ) { this( LayeredNode.convertToLayeredGraph( graph ) ); } /** * Initialize the window and its contents. * There is good reason not to split up this method to smaller methods: * Imagine a tree with a fixed number of nodes, but limited degree of branching. * The the height of the tree is at least inversely proportional to the degree of branching. * This means halving the maximum method size by splitting methods would make the call stack twice as high * and this way debugging twice as time-consuming. * @param graph the graph that is displayed in this window. */ public MainView( LayeredGraphNode graph ) { frameCounter++; this.graph = graph; controller = new AnimationController(); frame = new JFrame( "NodeShuffler" ); frame.addWindowListener(new java.awt.event.WindowAdapter() { @Override public void windowClosing(java.awt.event.WindowEvent windowEvent) { frameCounter--; if( frameCounter == 0 ) System.exit( 0 ); } }); BKNodePlacement algorithm = new BKNodePlacement( controller, graph, frame ); // Create Menu GUI stepForward = new NiceButton( "stepForward" ); stepForward.setLocation( 10, 10 ); stepForward.setMnemonic( KeyEvent.VK_DOWN ); stepForward.setToolTipText( "Forward step over (alt + down arrow key)" ); stepForward.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.setContinuous( false ); controller.setNextAction( Action.FORWARD_OVER ); } }); stepForwardInto = new NiceButton( "stepForwardInto" ); stepForwardInto.setLocation( 60, 10 ); stepForwardInto.setMnemonic( KeyEvent.VK_RIGHT ); stepForwardInto.setToolTipText( "Forward step into (alt + right arrow key)" ); stepForwardInto.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.setContinuous( false ); controller.setNextAction( Action.FORWARD ); } }); stepForwardOut = new NiceButton( "stepForwardOut" ); stepForwardOut.setLocation( 110, 10 ); stepForwardOut.setMnemonic( KeyEvent.VK_PAGE_DOWN ); stepForwardOut.setToolTipText( "Forward step out (alt + page down key)" ); stepForwardOut.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.setContinuous( false ); controller.setNextAction( Action.FORWARD_OUT ); } }); runForward = new NiceButton( "runForward" ); runForward.setLocation( 160, 10 ); runForward.setMnemonic( KeyEvent.VK_P ); runForward.setToolTipText( "Run forwards (alt + p)" ); runForward.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.setContinuous( true ); controller.setNextAction( Action.FORWARD ); } }); runBackward = new NiceButton( "runBackward" ); runBackward.setLocation( 160, 60 ); runBackward.setMnemonic( KeyEvent.VK_R ); runBackward.setToolTipText( "Run backwards (alt + r)" ); runBackward.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.setContinuous( true ); controller.setNextAction( Action.BACKWARD ); } }); stepBackward = new NiceButton( "stepBackward" ); stepBackward.setLocation( 10, 60 ); stepBackward.setMnemonic( KeyEvent.VK_UP ); stepBackward.setToolTipText( "Backward step over (alt + up arrow key)" ); stepBackward.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.setContinuous( false ); controller.setNextAction( Action.BACKWARD_OVER ); } }); stepBackwardInto = new NiceButton( "stepBackwardInto" ); stepBackwardInto.setLocation( 60, 60 ); stepBackwardInto.setMnemonic( KeyEvent.VK_LEFT ); stepBackwardInto.setToolTipText( "Backward step into (alt + left arrow key)" ); stepBackwardInto.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.setContinuous( false ); controller.setNextAction( Action.BACKWARD ); } }); stepBackwardOut = new NiceButton( "stepBackwardOut" ); stepBackwardOut.setLocation( 110, 60 ); stepBackwardOut.setMnemonic( KeyEvent.VK_PAGE_UP ); stepBackwardOut.setToolTipText( "Backward step out (alt + page up)" ); stepBackwardOut.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.setContinuous( false ); controller.setNextAction( Action.BACKWARD_OUT ); } }); pause = new NiceButton( "pause" ); pause.setLocation( 210, 10 ); pause.setMnemonic( KeyEvent.VK_PAUSE ); pause.setToolTipText( "Pause (alt + pause)" ); pause.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { controller.setContinuous( false ); } }); debug = new NiceButton( "debug" ); debug.setLocation( 360, 10 ); debug.setMnemonic( KeyEvent.VK_D ); debug.setToolTipText( "Show debug info (alt + d)" ); debug.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { showDebugInfo(); } }); randomGraph = new NiceButton( "random" ); randomGraph.setLocation( 360, 60 ); randomGraph.setMnemonic( KeyEvent.VK_G ); randomGraph.setToolTipText( "Generate random graph (alt + g)" ); randomGraph.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JDialog diag = new JDialog( frame, "Generate random graph" ); diag.setLayout( new GridBagLayout() ); GridBagConstraints c = new GridBagConstraints(); c.gridx = 0; c.gridy = 0; diag.add( new JLabel( "P(subgraph exists)"), c ); c = new GridBagConstraints(); c.gridx = 1; c.gridy = 0; JTextField pSubgraph = new JTextField( "0.1" ); pSubgraph.setPreferredSize( new Dimension( 100, 20 ) ); pSubgraph.addFocusListener( new FocusListener() { @Override public void focusGained(FocusEvent e) { pSubgraph.setBackground( Color.WHITE ); } @Override public void focusLost(FocusEvent e) { try { double d = Double.parseDouble( pSubgraph.getText() ); if( d > 1 || d < 0 ) pSubgraph.setBackground( Color.RED ); } catch( Exception e1 ) { pSubgraph.setBackground( Color.RED ); } } }); diag.add( pSubgraph, c ); c = new GridBagConstraints(); c.gridx = 0; c.gridy = 1; diag.add( new JLabel( "P(edge exists)"), c ); c = new GridBagConstraints(); c.gridx = 1; c.gridy = 1; JTextField pEdge = new JTextField( "0.3" ); pEdge.setPreferredSize( new Dimension( 100, 20 ) ); pEdge.addFocusListener( new FocusListener() { @Override public void focusGained(FocusEvent e) { pEdge.setBackground( Color.WHITE ); } @Override public void focusLost(FocusEvent e) { try { double d = Double.parseDouble( pEdge.getText() ); if( d > 1 || d < 0 ) pEdge.setBackground( Color.RED ); } catch( Exception e1 ) { pEdge.setBackground( Color.RED ); } } }); diag.add( pEdge, c ); c = new GridBagConstraints(); c.gridx = 0; c.gridy = 2; diag.add( new JLabel( "min. num. layers"), c ); c = new GridBagConstraints(); c.gridx = 1; c.gridy = 2; JTextField minLayers = new JTextField( "5" ); JTextField maxLayers = new JTextField( "5" ); minLayers.setPreferredSize( new Dimension( 100, 20 ) ); minLayers.addFocusListener( new FocusListener() { @Override public void focusGained(FocusEvent e) { minLayers.setBackground( Color.WHITE ); } @Override public void focusLost(FocusEvent e) { try { int i = Integer.parseInt( minLayers.getText() ); int max = Integer.parseInt( maxLayers.getText() ); if( i < 1 || i > max ) minLayers.setBackground( Color.RED ); else maxLayers.setBackground( Color.WHITE ); } catch( Exception e1 ) { minLayers.setBackground( Color.RED ); } } }); diag.add( minLayers, c ); c = new GridBagConstraints(); c.gridx = 0; c.gridy = 3; diag.add( new JLabel( "max. num. layers"), c ); c = new GridBagConstraints(); c.gridx = 1; c.gridy = 3; maxLayers.setPreferredSize( new Dimension( 100, 20 ) ); maxLayers.addFocusListener( new FocusListener() { @Override public void focusGained(FocusEvent e) { maxLayers.setBackground( Color.WHITE ); } @Override public void focusLost(FocusEvent e) { try { int i = Integer.parseInt( maxLayers.getText() ); int min = Integer.parseInt( minLayers.getText() ); if( i < min ) maxLayers.setBackground( Color.RED ); else if( min > 0 ) minLayers.setBackground( Color.WHITE ); } catch( Exception e1 ) { maxLayers.setBackground( Color.RED ); } } }); diag.add( maxLayers, c ); c = new GridBagConstraints(); c.gridx = 0; c.gridy = 4; diag.add( new JLabel( "min. num. nodes"), c ); c = new GridBagConstraints(); c.gridx = 1; c.gridy = 4; JTextField minNodes = new JTextField( "5" ); JTextField maxNodes = new JTextField( "5" ); minNodes.setPreferredSize( new Dimension( 100, 20 ) ); minNodes.setToolTipText( "between 1 and 'min. num. nodes'" ); minNodes.addFocusListener( new FocusListener() { @Override public void focusGained(FocusEvent e) { minNodes.setBackground( Color.WHITE ); } @Override public void focusLost(FocusEvent e) { try { int i = Integer.parseInt( minNodes.getText() ); int max = Integer.parseInt( maxNodes.getText() ); if( i < 1 || i > max ) minNodes.setBackground( Color.RED ); else minNodes.setBackground( Color.WHITE ); } catch( Exception e1 ) { minNodes.setBackground( Color.RED ); } } }); diag.add( minNodes, c ); c = new GridBagConstraints(); c.gridx = 0; c.gridy = 5; diag.add( new JLabel( "max. num. nodes"), c ); c = new GridBagConstraints(); c.gridx = 1; c.gridy = 5; maxNodes.setPreferredSize( new Dimension( 100, 20 ) ); maxNodes.setToolTipText( "between 'min. num. nodes' and +Inf" ); maxNodes.addFocusListener( new FocusListener() { @Override public void focusGained(FocusEvent e) { maxNodes.setBackground( Color.WHITE ); } @Override public void focusLost(FocusEvent e) { try { int i = Integer.parseInt( maxNodes.getText() ); int min = Integer.parseInt( minNodes.getText() ); if( i < min ) maxNodes.setBackground( Color.RED ); else if( min > 0 ) minNodes.setBackground( Color.WHITE ); } catch( Exception e1 ) { maxNodes.setBackground( Color.RED ); } } }); diag.add( maxNodes, c ); c = new GridBagConstraints(); c.gridx = 0; c.gridy = 6; diag.add( new JLabel( "max. hier. depth"), c ); c = new GridBagConstraints(); c.gridx = 1; c.gridy = 6; JTextField maxDepth = new JTextField( "1" ); maxDepth.setPreferredSize( new Dimension( 100, 20 ) ); maxDepth.setToolTipText( "between 1 and +Inf" ); maxDepth.addFocusListener( new FocusListener() { @Override public void focusGained(FocusEvent e) { maxDepth.setBackground( Color.WHITE ); } @Override public void focusLost(FocusEvent e) { try { int i = Integer.parseInt( maxDepth.getText() ); if( i < 1 ) maxDepth.setBackground( Color.RED ); } catch( Exception e1 ) { maxDepth.setBackground( Color.RED ); } } }); diag.add( maxDepth, c ); c = new GridBagConstraints(); c.gridx = 0; c.gridy = 7; c.gridwidth = 2; JButton gen = new JButton( "generate"); gen.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { double pSubGraphD = Double.parseDouble( pSubgraph.getText() ); double pEdgeD = Double.parseDouble( pEdge.getText() ); int minLayerI = Integer.parseInt( minLayers.getText() ); int maxLayerI = Integer.parseInt( maxLayers.getText() ); int minNodeI = Integer.parseInt( minNodes.getText() ); int maxNodeI = Integer.parseInt( maxNodes.getText() ); int maxDepthI = Integer.parseInt( maxDepth.getText() ); boolean ok = true; if( pSubGraphD < 0 || pSubGraphD > 1 ) { pSubgraph.setBackground( Color.RED ); ok = false; } if( pEdgeD < 0 || pEdgeD > 1 ) { pEdge.setBackground( Color.RED ); ok = false; } if( minLayerI < 1 ) { minLayers.setBackground( Color.RED ); ok = false; } if( maxLayerI < minLayerI ) { maxLayers.setBackground( Color.RED ); ok = false; } if( minNodeI < 1 ) { minNodes.setBackground( Color.RED ); ok = false; } if( maxNodeI < minNodeI ) { maxNodes.setBackground( Color.RED ); ok = false; } if( maxDepthI < 1 ) { maxDepth.setBackground( Color.RED ); ok = false; } if( ok ) { RandomGraphGenerator r = new RandomGraphGenerator( pSubGraphD, pEdgeD, minLayerI, maxLayerI, minNodeI, maxNodeI, maxDepthI ); try { LayeredGraphNode graph = r.createRandomNode( null, 0, true ); SweepCrossingMinimizer cminzer = new SweepCrossingMinimizer(); for( int i = 0; i < 10; i++ ) cminzer.minimizeCrossings( graph ); InitializeNodePositions.placeNodes( graph ); new MainView( graph ); diag.setVisible( false ); } catch( Exception e1 ) { JOptionPane.showMessageDialog(frame, e1.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } } }); diag.add( gen, c ); diag.setSize( 270, 220 ); diag.setLocation( frame.getX() + frame.getWidth() / 2 - diag.getWidth() / 2, frame.getY() + frame.getHeight() / 2 - diag.getHeight() / 2 ); diag.setVisible( true ); } }); delayText = new JLabel( "Delay (ms)" ); delayText.setBounds( 260, 10, 80, 20 ); delay = new JTextField( String.valueOf(AnimationController.DEFAULT_DELAY) ); delay.setBounds( 260, 30, 90, 20 ); delay.getDocument().addDocumentListener( new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { try { controller.setDelay( Integer.parseInt( delay.getText() ) ); delay.setBackground( Color.WHITE ); } catch( Exception e1 ) { delay.setBackground( Color.RED ); } } @Override public void removeUpdate(DocumentEvent e) { try { controller.setDelay( Integer.parseInt( delay.getText() ) ); delay.setBackground( Color.WHITE ); } catch( Exception e1 ) { delay.setBackground( Color.RED ); } } @Override public void changedUpdate(DocumentEvent e) { try { controller.setDelay( Integer.parseInt( delay.getText() ) ); delay.setBackground( Color.WHITE ); } catch( Exception e1 ) { delay.setBackground( Color.RED ); } } }); load = new NiceButton( "load" ); load.setLocation( 230, 60 ); load.setMnemonic( KeyEvent.VK_L ); load.setToolTipText( "Load a graph (alt + l)" ); load.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser(); chooser.setFileFilter( new FileNameExtensionFilter("Json Graph", "json") ); chooser.showOpenDialog( frame ); if( chooser.getSelectedFile() != null ) { Reader r = new Reader( chooser.getSelectedFile().getAbsolutePath() ); LayeredGraphNode graph = r.readInputGraph(); InitializeNodePositions.placeNodes( graph ); new MainView( graph ); } } }); save = new NiceButton( "save" ); save.setLocation( 295, 60 ); save.setMnemonic( KeyEvent.VK_S ); save.setToolTipText( "Save graph (alt + s)" ); save.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser(); chooser.setFileFilter( new FileNameExtensionFilter("Json Graph", "json") ); chooser.showSaveDialog( frame ); if( chooser.getSelectedFile() != null ) { Writer w = new Writer( chooser.getSelectedFile().getAbsolutePath() ); w.writeOutputGraph( graph ); } } }); pseudoTree = new JTree(); pseudoTree.setBackground(RenderHelper.BACKGROUND_COLOR); PseudoCodeNode tree = algorithm.createPseudocodeTree( pseudoTree ); tree.setController( controller ); pseudoTree.setModel( new DefaultTreeModel( tree ) ); pseudoTree.setCellRenderer( new PseudoCodeRenderer() ); pseudoTree.setSelectionModel( null ); pseudoTree.addMouseListener( new MouseAdapter() { public void mousePressed(MouseEvent e) { TreePath selPath = pseudoTree.getPathForLocation(e.getX(), e.getY()); if( selPath != null && e.getClickCount() == 3 ) { ((PseudoCodeNode)selPath.getLastPathComponent()).setBreakPoint( !((PseudoCodeNode)selPath.getLastPathComponent()).hasBreakPoint() ); if( !pseudoTree.isExpanded( selPath ) ) { pseudoTree.collapsePath( selPath ); pseudoTree.expandPath( selPath ); } else { pseudoTree.expandPath( selPath ); pseudoTree.collapsePath( selPath ); } pseudoTree.repaint(); frame.repaint(); } } } ); pseudoTree.setRowHeight(15); JScrollPane treeView = new JScrollPane( pseudoTree ); treeView.setBounds( 10, 110, 390, 380 ); JTextArea debugText = new JTextArea(); debugText.setFont( new Font( "Monospaced", Font.PLAIN, 12 ) ); debugText.setEditable( false ); debugText.setBackground( RenderHelper.BACKGROUND_COLOR ); debugText.setForeground( RenderHelper.FOREGROUND_COLOR ); JScrollPane debugView = new JScrollPane( debugText ); debugView.setBounds( treeView.getX(), treeView.getY() + 500, treeView.getWidth(), 250 ); frame.setSize( (int)graph.getWidth( LayoutType.TOP_BOTTOM_LEFT ) * 2 + 575, (int)graph.getHeight( LayoutType.TOP_BOTTOM_LEFT ) * 2 + 200 ); frame.setLocation( 100, 100 ); frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); frame.setVisible( true ); JLayeredPane layne = new JLayeredPane(); JPanel pl = new JPanel(); GridLayout grout = new GridLayout( 2, 2, 10, 10 ); pl.setLayout( grout ); pl.setLocation( 0, 0 ); pl.setSize( frame.getSize() ); NodeView topLeft = createNodeView( graph, LayoutType.TOP_BOTTOM_LEFT ); NodeView topRight = createNodeView( graph, LayoutType.TOP_BOTTOM_RIGHT ); NodeView bottomLeft = createNodeView( graph, LayoutType.BOTTOM_TOP_LEFT ); NodeView bottomRight = createNodeView( graph, LayoutType.BOTTOM_TOP_RIGHT ); pl.add( topLeft ); pl.add( topRight ); pl.add( bottomLeft ); pl.add( bottomRight ); layne.add( pl, 1 ); NodeView combined = createNodeView( graph, LayoutType.COMBINED ); combined.setSize( 500, 500 ); layne.add( combined, 0 ); frame.add( layne ); JPanel menue = new JPanel(); menue.setLayout( null ); menue.setPreferredSize( new Dimension( 410, 500 ) ); menue.add( stepForward ); menue.add( stepForwardInto ); menue.add( stepForwardOut ); menue.add( runForward ); menue.add( pause ); menue.add( debug ); menue.add( stepBackward ); menue.add( delayText ); menue.add( delay ); menue.add( treeView ); menue.add( stepBackwardInto ); menue.add( stepBackwardOut ); menue.add( runBackward ); menue.add( randomGraph ); menue.add( save ); menue.add( load ); menue.add( debugView ); frame.add( menue, BorderLayout.EAST ); frame.setSize( frame.getWidth() + 1, frame.getHeight() ); frame.setSize( frame.getWidth() - 1, frame.getHeight() ); frame.validate(); frame.repaint(); frame.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent evt) { pl.setSize( layne.getSize() ); menue.setSize( menue.getWidth(), layne.getHeight() ); treeView.setSize( treeView.getWidth(), layne.getHeight() - 370 ); debugView.setBounds( treeView.getX(), treeView.getY() + treeView.getHeight() + 10, treeView.getWidth(), 240 ); if( graph.getColor( LayoutType.COMBINED ) == null ) { grout.setHgap( 10 ); grout.setVgap( 10 ); } else { grout.setHgap( layne.getWidth() / 3 ); grout.setVgap( layne.getHeight() / 3 ); } combined.setSize( layne.getWidth() / 3, layne.getHeight() / 3 ); combined.setLocation( layne.getWidth() / 3, layne.getHeight() / 3 ); debugText.setText( algorithm.getDebugString().trim() ); layne.remove( pl ); layne.add( pl, 1 ); frame.repaint(); } }); algorithm.start(); } private NodeView createNodeView( LayeredGraphNode gNode, LayoutType lt ) { NodeView graphView = new NodeView( gNode, lt ); ((LayeredNode)gNode).setView( graphView, lt ); graphView.setLayout( null ); graphView.setOpaque( true ); for( LayeredGraphNode n : gNode.getContainedNodes() ) { NodeView nv = createNodeView( n, lt ); nv.setBounds( nv.getX(), nv.getY(), nv.getWidth(), nv.getHeight() ); graphView.add( nv ); } for( LayeredGraphEdge e : gNode.getContainedEdges() ) { EdgeView ev = new EdgeView( e, lt ); ev.setOpaque( true ); graphView.add( ev ); } return graphView; } }