package view; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.JViewport; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import animation.PseudoCodeNode; /** * JComponent used to draw line numbers. This JComponent should be added as a row header view in a JScrollPane. Based upon the * LineNumberModel provided, this component will draw the line numbers as needed. * @author Greg Cope * */ public class PseudoCodeLines extends JComponent implements MouseListener{ static final long serialVersionUID = 432143214L; //pixel padding on left and right private static final int HORIZONTAL_PADDING = 1; //pixel padding on left and right private static final int VERTICAL_PADDING = 3; private static ImageIcon currentLine = new ImageIcon( PseudoCodeNode.class.getResource( "/img/current_line.png" ) ); private JTree tree; /** * Constructs a new Component with no model */ public PseudoCodeLines( JTree t ){ super(); tree = t; this.addMouseListener( this ); } /** * Checks and adjusts the width of this component based upon the line numbers */ public void adjustWidth(){ int max = getLineNumber( (TreeNode)tree.getPathForRow( tree.getRowCount() - 1 ).getLastPathComponent() ); if ( getGraphics() == null ){ return; } int width = getGraphics().getFontMetrics().stringWidth(String.valueOf(max)) + 2 * HORIZONTAL_PADDING + 30; JComponent c = (JComponent)getParent(); if (c == null){//not within a container return; } Dimension dimension = c.getPreferredSize(); if ( c instanceof JViewport ){//do some climbing up the component tree to get the main JScrollPane view JViewport view = (JViewport)c; Component parent = view.getParent(); if ( parent != null && parent instanceof JScrollPane){ JScrollPane scroller = (JScrollPane)view.getParent(); dimension = scroller.getViewport().getView().getPreferredSize(); } } if ( width > super.getPreferredSize().width || width < super.getPreferredSize().width){ setPreferredSize(new Dimension(width + 2*HORIZONTAL_PADDING, dimension.height)); revalidate(); repaint(); } } @Override public Dimension getPreferredSize() { return super.getPreferredSize(); } private int countChildren( TreeNode treeNode ) { if( treeNode.isLeaf() ) return 0; else { int sum = 0; for( int i = 0; i < treeNode.getChildCount(); i++ ) { sum += countChildren( treeNode.getChildAt( i ) ) + 1; } return sum; } } private int getLineNumber( TreeNode treeNode ) { TreeNode parent = treeNode.getParent(); if( parent == null ) return 1; int sum = getLineNumber( parent ) + 1; for( int i = 0; i < parent.getChildCount(); i++ ) { if( i == parent.getIndex( treeNode ) ) return sum; sum += countChildren( parent.getChildAt( i ) ) + 1; } return 0; } @Override public void paintComponent(Graphics g){ adjustWidth(); super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setColor(RenderHelper.BACKGROUND_COLOR); g2d.fillRect(0, 0, getWidth(), getHeight()); g.setColor(RenderHelper.FOREGROUND_COLOR); for ( int i = 0; i < tree.getRowCount(); i++ ){ PseudoCodeNode node = (PseudoCodeNode)tree.getPathForRow( i ).getLastPathComponent(); int number = getLineNumber( node ); Rectangle rect = tree.getRowBounds( i ); String text = String.valueOf( number ); int yPosition = rect.y + rect.height / 2 + 4; if( !node.hasSelectedSubnode() && node.isSelected() ) g.drawImage( currentLine.getImage(), HORIZONTAL_PADDING, rect.y + rect.height / 2 - 10, 20, 20, null ); g2d.drawString( text, HORIZONTAL_PADDING * 2 + 20, yPosition ); if( node.hasBreakPoint() ) { Color c = g.getColor(); g.setColor( new Color (0xe7887a) ); g.fillOval( getWidth() - HORIZONTAL_PADDING - 10, rect.y + rect.height / 2 - 5, 10, 10 ); g.setColor( c ); } } } private int getRowNumber( int y ) { int curr = 0; int res = -1; for ( int i = 0; i < tree.getRowCount(); i++ ){ Rectangle rect = tree.getRowBounds( i ); if( y > curr && y < rect.y + rect.height ) { res = i; break; } curr = rect.y + rect.height; } return res; } @Override public void mouseClicked(MouseEvent e) { int rNum = getRowNumber( e.getY() ); if( rNum < 0 ) return; TreePath path = tree.getPathForRow( rNum ); if( path == null ) return; ((PseudoCodeNode)path.getLastPathComponent()).setBreakPoint( !((PseudoCodeNode)path.getLastPathComponent()).hasBreakPoint() ); tree.repaint(); } @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} }