package de.unisiegen.gtitool.ui.history; import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JTable; import de.unisiegen.gtitool.core.entities.State; import de.unisiegen.gtitool.core.entities.Transition; import de.unisiegen.gtitool.core.parser.style.PrettyPrintable; import de.unisiegen.gtitool.core.parser.style.PrettyToken; /** * This class implements the {@link PrettyPrintable} {@link HistoryPath} * {@link Component}. * * @author Christian Fehler */ public final class HistoryPathComponent extends JLabel { /** * The serial version uid. */ private static final long serialVersionUID = 88702245703588279L; /** * The used font. */ private static final Font FONT = new Font ( "Dialog", Font.PLAIN, 12 ); //$NON-NLS-1$ /** * The row height. */ private static final int ROW_HEIGHT = 24; /** * The space width. */ private static final int SPACE_WIDTH = 10; /** * The state offset. */ private static final int STATE_OFFSET = 3; /** * The transition offset. */ private static final int TRANSITION_OFFSET = 10; /** * The transition arrow offset. */ private static final int TRANSITION_ARROW_OFFSET = 6; /** * The {@link HistoryPath}. */ private HistoryPath historyPath; /** * The x position. */ private int xPosition = 0; /** * The y position. */ private int yPosition = 0; /** * The row count. */ private int rowCount = 1; /** * The used {@link JTable}. */ private JTable table; /** * The {@link JTable} row. */ private int tableRow; /** * Initializes the {@link HistoryPathComponent}. * * @param historyPath The {@link HistoryPath}. * @param table The used {@link JTable}. * @param tableRow The {@link JTable} row. */ public HistoryPathComponent ( HistoryPath historyPath, JTable table, int tableRow ) { super (); setBorder ( null ); this.historyPath = historyPath; this.table = table; this.tableRow = tableRow; } /** * Returns the row count. * * @param g The {@link Graphics}. * @return The row count. */ private final int calculateRowCount ( Graphics g ) { int width = getWidth (); int count = 1; this.xPosition = 0; this.yPosition = ROW_HEIGHT; for ( int i = 0 ; i < this.historyPath.getTransitionList ().size () ; i++ ) { Transition transition = this.historyPath.getTransitionList ().get ( i ); // Line break int stateWidth = calculateStateWidth ( transition.getStateBegin (), g ); if ( ( this.xPosition + stateWidth ) > width ) { count++ ; this.xPosition = 0; this.yPosition += ROW_HEIGHT; } // State this.xPosition += stateWidth; // Line break int transitionWidth = calculateTransitionWidth ( transition, g ); if ( ( this.xPosition + transitionWidth ) > width ) { count++ ; this.xPosition = 0; this.yPosition += ROW_HEIGHT; } // Transition this.xPosition += transitionWidth; } // Last state if ( this.historyPath.getTransitionList ().size () > 0 ) { State state = this.historyPath.getTransitionList ().get ( this.historyPath.getTransitionList ().size () - 1 ).getStateEnd (); // Line break int stateWidth = calculateStateWidth ( state, g ); if ( this.xPosition + stateWidth > width ) { count++ ; this.xPosition = 0; this.yPosition += ROW_HEIGHT; } // State this.xPosition += stateWidth; } // Start state if ( this.historyPath.getStartState () != null ) { State state = this.historyPath.getStartState (); // Line break int stateWidth = calculateStateWidth ( state, g ); if ( this.xPosition + stateWidth > width ) { count++ ; this.xPosition = 0; this.yPosition += ROW_HEIGHT; } // State this.xPosition += stateWidth; } return count; } /** * Calculates the {@link State} width. * * @param state The {@link State}. * @param g The {@link Graphics}. * @return The {@link State} width. */ private final int calculateStateWidth ( State state, Graphics g ) { FontMetrics metrics = g.getFontMetrics (); char [] prettyString = state.toPrettyString ().toString ().toCharArray (); int result = 0; for ( char element : prettyString ) { result += metrics.charWidth ( element ); } return result; } /** * Calculates the {@link Transition} width. * * @param transition The {@link Transition}. * @param g The {@link Graphics}. * @return The {@link Transition} width. */ private final int calculateTransitionWidth ( Transition transition, Graphics g ) { FontMetrics metrics = g.getFontMetrics (); int result = 0; for ( PrettyToken currentToken : transition.toPrettyString () .getPrettyToken () ) { Font font = null; if ( !currentToken.isBold () && !currentToken.isItalic () ) { font = FONT; } else if ( currentToken.isBold () && currentToken.isItalic () ) { font = FONT.deriveFont ( Font.BOLD | Font.ITALIC ); } else if ( currentToken.isBold () ) { font = FONT.deriveFont ( Font.BOLD ); } else if ( currentToken.isItalic () ) { font = FONT.deriveFont ( Font.ITALIC ); } g.setFont ( font ); g.setColor ( currentToken.getColor () ); char [] chars = currentToken.getChar (); for ( char c : chars ) { result += metrics.charWidth ( c ); } } return result + ( 2 * SPACE_WIDTH ); } /** * {@inheritDoc} * * @see JComponent#paintComponent(Graphics) */ @Override protected final void paintComponent ( Graphics g ) { g.setFont ( FONT ); int width = getWidth (); this.rowCount = calculateRowCount ( g ); g.setColor ( getBackground () ); g.fillRect ( 0, 0, getWidth (), ROW_HEIGHT * this.rowCount ); this.xPosition = 0; this.yPosition = ROW_HEIGHT; for ( int i = 0 ; i < this.historyPath.getTransitionList ().size () ; i++ ) { Transition transition = this.historyPath.getTransitionList ().get ( i ); State state = transition.getStateBegin (); // Line break if ( this.xPosition + calculateStateWidth ( state, g ) > width ) { this.xPosition = 0; this.yPosition += ROW_HEIGHT; } paintState ( state, g ); // Line break if ( this.xPosition + calculateTransitionWidth ( transition, g ) > width ) { this.xPosition = 0; this.yPosition += ROW_HEIGHT; } paintTransition ( transition, g ); } // Last state if ( this.historyPath.getTransitionList ().size () > 0 ) { State state = this.historyPath.getTransitionList ().get ( this.historyPath.getTransitionList ().size () - 1 ).getStateEnd (); // Line break if ( this.xPosition + calculateStateWidth ( state, g ) > width ) { this.xPosition = 0; this.yPosition += ROW_HEIGHT; } paintState ( state, g ); } // Start state if ( this.historyPath.getStartState () != null ) { State state = this.historyPath.getStartState (); // Line break if ( this.xPosition + calculateStateWidth ( state, g ) > width ) { this.xPosition = 0; this.yPosition += ROW_HEIGHT; } paintState ( state, g ); } // Update the table row height if ( this.table.getRowHeight ( this.tableRow ) != ROW_HEIGHT * this.rowCount ) { this.table.setRowHeight ( this.tableRow, ROW_HEIGHT * this.rowCount ); } } /** * Paints the given {@link State}. * * @param state The {@link State} to paint. * @param g The {@link Graphics}. */ private final void paintState ( State state, Graphics g ) { FontMetrics metrics = g.getFontMetrics (); for ( PrettyToken currentToken : state.toPrettyString ().getPrettyToken () ) { Font font = null; if ( !currentToken.isBold () && !currentToken.isItalic () ) { font = FONT; } else if ( currentToken.isBold () && currentToken.isItalic () ) { font = FONT.deriveFont ( Font.BOLD | Font.ITALIC ); } else if ( currentToken.isBold () ) { font = FONT.deriveFont ( Font.BOLD ); } else if ( currentToken.isItalic () ) { font = FONT.deriveFont ( Font.ITALIC ); } g.setFont ( font ); g.setColor ( currentToken.getColor () ); char [] chars = currentToken.getChar (); for ( int i = 0 ; i < chars.length ; i++ ) { g.drawChars ( chars, i, 1, this.xPosition, this.yPosition - STATE_OFFSET ); this.xPosition += metrics.charWidth ( chars [ i ] ); } } } /** * Paints the given {@link Transition}. * * @param transition The {@link Transition} to paint. * @param g The {@link Graphics}. */ private final void paintTransition ( Transition transition, Graphics g ) { FontMetrics metrics = g.getFontMetrics (); int oldX = this.xPosition; this.xPosition += SPACE_WIDTH; for ( PrettyToken currentToken : transition.toPrettyString () .getPrettyToken () ) { Font font = null; if ( !currentToken.isBold () && !currentToken.isItalic () ) { font = FONT; } else if ( currentToken.isBold () && currentToken.isItalic () ) { font = FONT.deriveFont ( Font.BOLD | Font.ITALIC ); } else if ( currentToken.isBold () ) { font = FONT.deriveFont ( Font.BOLD ); } else if ( currentToken.isItalic () ) { font = FONT.deriveFont ( Font.ITALIC ); } g.setFont ( font ); g.setColor ( currentToken.getColor () ); char [] chars = currentToken.getChar (); for ( int i = 0 ; i < chars.length ; i++ ) { g.drawChars ( chars, i, 1, this.xPosition, this.yPosition - TRANSITION_OFFSET ); this.xPosition += metrics.charWidth ( chars [ i ] ); } } g.setColor ( Color.BLACK ); g.drawLine ( oldX + ( SPACE_WIDTH / 2 ), this.yPosition - TRANSITION_ARROW_OFFSET, this.xPosition + ( SPACE_WIDTH / 2 ), this.yPosition - TRANSITION_ARROW_OFFSET ); g.drawLine ( this.xPosition + ( SPACE_WIDTH / 2 ), this.yPosition - TRANSITION_ARROW_OFFSET, this.xPosition + ( SPACE_WIDTH / 2 ) - 4, this.yPosition - TRANSITION_ARROW_OFFSET - 2 ); g.drawLine ( this.xPosition + ( SPACE_WIDTH / 2 ), this.yPosition - TRANSITION_ARROW_OFFSET, this.xPosition + ( SPACE_WIDTH / 2 ) - 4, this.yPosition - TRANSITION_ARROW_OFFSET + 2 ); this.xPosition += SPACE_WIDTH; } }