package de.unisiegen.gtitool.ui.style.sidebar; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.util.ArrayList; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.text.JTextComponent; import de.unisiegen.gtitool.core.entities.Entity; import de.unisiegen.gtitool.core.parser.exceptions.ParserWarningException; import de.unisiegen.gtitool.core.parser.exceptions.ScannerException; import de.unisiegen.gtitool.ui.style.document.StyledParserDocument; import de.unisiegen.gtitool.ui.style.listener.ExceptionsChangedListener; /** * This class implements the sidebar which is used in the source views. * * @author Christian Fehler * @version $Id$ * @param <E> The {@link Entity}. */ public final class SideBar < E extends Entity < E >> extends JComponent { /** * The serial version uid. */ private static final long serialVersionUID = 5819550523704175785L; /** * The parser normal error icon. */ private ImageIcon errorIcon = null; /** * The parser warning icon. */ private ImageIcon warningIcon = null; /** * The vertical positions of the errors. */ private int [] verticalPositions; /** * The vertical {@link JScrollBar}. */ private JScrollBar verticalScrollBar; /** * The horizontal {@link JScrollBar}. */ private JScrollBar horizontalScrollBar; /** * The used {@link JScrollPane}. */ private JScrollPane scrollPane; /** * The list of {@link ScannerException}. */ private ArrayList < ScannerException > exceptionList; /** * The used {@link StyledParserDocument}. */ private StyledParserDocument < E > document; /** * The used text component. */ private JTextComponent textComponent; /** * The status if something has changed. */ protected boolean proppertyChanged; /** * The left offset of the current exception. */ private int currentLeft; /** * The right offset of the current exception. */ private int currentRight; /** * Initializes the {@link SideBar}. * * @param scrollPane The used {@link JScrollPane}. * @param document The used {@link StyledParserDocument}. * @param textComponent The used text component. */ public SideBar ( JScrollPane scrollPane, StyledParserDocument < E > document, JTextComponent textComponent ) { super (); this.currentLeft = -1; this.currentRight = -1; this.scrollPane = scrollPane; this.document = document; this.textComponent = textComponent; this.errorIcon = new ImageIcon ( getClass ().getResource ( "/de/unisiegen/gtitool/ui/icon/small/error.png" ) ); //$NON-NLS-1$ this.warningIcon = new ImageIcon ( getClass ().getResource ( "/de/unisiegen/gtitool/ui/icon/small/warning.png" ) ); //$NON-NLS-1$ int imageWidth = this.errorIcon.getIconWidth (); this.proppertyChanged = false; setMinimumSize ( new Dimension ( imageWidth, imageWidth ) ); this.verticalScrollBar = this.scrollPane.getVerticalScrollBar (); this.horizontalScrollBar = this.scrollPane.getHorizontalScrollBar (); this.verticalScrollBar.addAdjustmentListener ( new AdjustmentListener () { public void adjustmentValueChanged ( @SuppressWarnings ( "unused" ) AdjustmentEvent event ) { repaint (); } } ); this.document .addExceptionsChangedListener ( new ExceptionsChangedListener () { public void exceptionsChanged () { SideBar.this.proppertyChanged = true; repaint (); } } ); this.addMouseMotionListener ( new MouseMotionAdapter () { @Override public void mouseMoved ( MouseEvent event ) { SideBar.this.mouseMoved ( event ); } } ); this.addMouseListener ( new MouseAdapter () { @Override public void mouseClicked ( MouseEvent event ) { SideBar.this.mouseClicked ( event ); } } ); } /** * Adds the given {@link SideBarListener}. * * @param listener The given {@link SideBarListener}. */ public final void addSideBarListener ( SideBarListener listener ) { this.listenerList.add ( SideBarListener.class, listener ); } /** * Builds the marks. */ private final void buildMarks () { this.exceptionList = this.document.getException (); this.verticalPositions = new int [ this.exceptionList.size () ]; for ( int i = 0 ; i < this.exceptionList.size () ; i++ ) { try { this.verticalPositions [ i ] = 10; Rectangle rect = this.textComponent.modelToView ( this.exceptionList .get ( i ).getLeft () ); if ( rect == null ) { return; } this.verticalPositions [ i ] = rect.y + rect.height / 2; } catch ( Exception exc ) { continue; } } this.proppertyChanged = false; } /** * Inserts a given text at the given index. * * @param insertText The text which should be inserted. */ private final void fireInsertText ( String insertText ) { SideBarListener listeners[] = this.listenerList .getListeners ( SideBarListener.class ); for ( SideBarListener current : listeners ) { current.insertText ( this.currentRight, insertText ); } } /** * Marks the text with the given offsets. */ private final void fireMarkText () { SideBarListener listeners[] = this.listenerList .getListeners ( SideBarListener.class ); for ( SideBarListener current : listeners ) { current.markText ( this.currentLeft, this.currentRight ); } } /** * {@inheritDoc} * * @see JComponent#getPreferredSize() */ @Override public final Dimension getPreferredSize () { return new Dimension ( this.errorIcon.getIconWidth (), getHeight () ); } /** * Handles the mouse clicked event. * * @param event The {@link MouseEvent}. */ public final void mouseClicked ( MouseEvent event ) { if ( this.currentLeft == -1 || this.currentRight == -1 ) { return; } int y = event.getY () + this.verticalScrollBar.getValue (); int hh = this.errorIcon.getIconHeight () / 2; for ( int i = 0 ; i < this.verticalPositions.length ; i++ ) { if ( y > this.verticalPositions [ i ] - hh && y <= this.verticalPositions [ i ] + hh ) { if ( this.exceptionList.get ( i ) instanceof ParserWarningException ) { ParserWarningException ecx = ( ParserWarningException ) this.exceptionList .get ( i ); this.currentLeft = this.exceptionList.get ( i ).getLeft (); this.currentRight = this.exceptionList.get ( i ).getRight (); fireInsertText ( ecx.getInsertText () ); return; } } } fireMarkText (); } /** * Handles the mouse move event. * * @param event The {@link MouseEvent}. */ protected final void mouseMoved ( MouseEvent event ) { if ( this.verticalPositions == null ) { return; } int y = event.getY () + this.verticalScrollBar.getValue (); int hh = this.errorIcon.getIconHeight () / 2; for ( int i = 0 ; i < this.verticalPositions.length ; i++ ) { if ( y > this.verticalPositions [ i ] - hh && y <= this.verticalPositions [ i ] + hh ) { this.currentLeft = this.exceptionList.get ( i ).getLeft (); this.currentRight = this.exceptionList.get ( i ).getRight (); setToolTipText ( this.exceptionList.get ( i ).getMessage () ); setCursor ( new Cursor ( Cursor.HAND_CURSOR ) ); return; } } setCursor ( new Cursor ( Cursor.DEFAULT_CURSOR ) ); setToolTipText ( null ); this.currentLeft = -1; this.currentRight = -1; } /** * {@inheritDoc} */ @Override protected final void paintComponent ( Graphics graphics ) { if ( this.proppertyChanged ) { buildMarks (); } graphics.setColor ( getBackground () ); graphics.fillRect ( 0, 0, getWidth (), getHeight () ); if ( this.verticalPositions == null ) { return; } for ( int i = 0 ; i < this.verticalPositions.length ; i++ ) { if ( this.verticalPositions [ i ] == -1 ) { continue; } int y0 = this.verticalPositions [ i ] - this.errorIcon.getIconHeight () / 2 - this.verticalScrollBar.getValue (); int y1 = y0 + this.errorIcon.getIconHeight (); if ( y1 < 0 || y0 > getHeight () ) { continue; } if ( this.exceptionList.get ( i ) instanceof ParserWarningException ) { graphics.drawImage ( this.warningIcon.getImage (), 0, y0, this ); } else { graphics.drawImage ( this.errorIcon.getImage (), 0, y0, this ); } } graphics.fillRect ( 0, getHeight () - this.horizontalScrollBar.getHeight (), getWidth (), this.horizontalScrollBar.getHeight () ); } /** * Adds the given {@link SideBarListener}. * * @param listener The given {@link SideBarListener}. */ public final void removeSideBarListener ( SideBarListener listener ) { this.listenerList.remove ( SideBarListener.class, listener ); } }