/** * */ package de.unisiegen.tpml.graphics ; 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.beans.PropertyChangeEvent ; import java.beans.PropertyChangeListener ; 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.tpml.core.exceptions.LanguageParserReplaceException ; import de.unisiegen.tpml.core.exceptions.LanguageParserWarningException ; import de.unisiegen.tpml.core.languages.LanguageScannerException ; /** * This class implements the sidebar which is used in the source views. * * @author Marcell Fischbach * @author Christian Fehler */ public class SideBar extends JComponent { /** * The serial version UID. */ private static final long serialVersionUID = - 4668570581006435967L ; /** * The parser normal error icon. */ private ImageIcon errorIcon = null ; /** * The parser warning icon. */ private ImageIcon warningIcon = null ; /** * The parser replace icon. */ private ImageIcon replaceIcon = 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 array of {@link LanguageScannerException}. */ private LanguageScannerException [ ] exceptions ; /** * The used {@link StyledLanguageDocument}. */ private StyledLanguageDocument document ; /** * The used text component. */ private JTextComponent textComponent ; /** * The status if something has changed. */ private 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 pScrollPane The used {@link JScrollPane}. * @param pDocument The used {@link StyledLanguageDocument}. * @param pTextComponent The used text component. */ public SideBar ( JScrollPane pScrollPane , StyledLanguageDocument pDocument , JTextComponent pTextComponent ) { super ( ) ; this.currentLeft = - 1 ; this.currentRight = - 1 ; this.scrollPane = pScrollPane ; this.document = pDocument ; this.textComponent = pTextComponent ; this.errorIcon = new ImageIcon ( getClass ( ).getResource ( "/de/unisiegen/tpml/ui/icons/error.gif" ) ) ; //$NON-NLS-1$ this.warningIcon = new ImageIcon ( getClass ( ).getResource ( "/de/unisiegen/tpml/ui/icons/warning.gif" ) ) ; //$NON-NLS-1$ this.replaceIcon = new ImageIcon ( getClass ( ).getResource ( "/de/unisiegen/tpml/ui/icons/error_blue.gif" ) ) ; //$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.addPropertyChangeListener ( "exceptions" , //$NON-NLS-1$ new PropertyChangeListener ( ) { @ SuppressWarnings ( "synthetic-access" ) public void propertyChange ( @ SuppressWarnings ( "unused" ) PropertyChangeEvent event ) { SideBar.this.proppertyChanged = true ; repaint ( ) ; } } ) ; this.addMouseMotionListener ( new MouseMotionAdapter ( ) { @ SuppressWarnings ( "synthetic-access" ) @ 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 pSideBarListener The given {@link SideBarListener}. */ public void addSideBarListener ( SideBarListener pSideBarListener ) { this.listenerList.add ( SideBarListener.class , pSideBarListener ) ; } /** * Builds the marks. */ private void buildMarks ( ) { this.exceptions = this.document.getExceptions ( ) ; this.verticalPositions = new int [ this.exceptions.length ] ; for ( int i = 0 ; i < this.exceptions.length ; i ++ ) { try { this.verticalPositions [ i ] = - 1 ; Rectangle rect = this.textComponent.modelToView ( this.exceptions [ i ] .getLeft ( ) ) ; if ( rect == null ) { return ; } this.verticalPositions [ i ] = rect.y + rect.height / 2 ; } catch ( Exception e ) { continue ; } } this.proppertyChanged = false ; } /** * Inserts a given text at the given index. * * @param pInsertText The text which should be inserted. */ private void fireInsertText ( String pInsertText ) { Object listeners[] = this.listenerList.getListenerList ( ) ; for ( int i = 0 ; i < listeners.length ; i ++ ) { if ( listeners [ i ] == SideBarListener.class ) { ( ( SideBarListener ) listeners [ i + 1 ] ).insertText ( this.currentRight , pInsertText ) ; } } } /** * Marks the text with the given offsets. */ private void fireMarkText ( ) { Object listeners[] = this.listenerList.getListenerList ( ) ; for ( int i = 0 ; i < listeners.length ; i ++ ) { if ( listeners [ i ] == SideBarListener.class ) { ( ( SideBarListener ) listeners [ i + 1 ] ).markText ( this.currentLeft , this.currentRight ) ; } } } /** * Replaces the texts with the given start and end offsets with the replace * text. * * @param pStart The start offsets of the texts which should be renamed. * @param pEnd The end offsets of the texts which should be renamed. * @param pReplaceText The replace text. */ private void fireReplaceText ( int [ ] pStart , int [ ] pEnd , String pReplaceText ) { Object listeners[] = this.listenerList.getListenerList ( ) ; for ( int i = 0 ; i < listeners.length ; i ++ ) { if ( listeners [ i ] == SideBarListener.class ) { ( ( SideBarListener ) listeners [ i + 1 ] ).replaceText ( pStart , pEnd , pReplaceText ) ; } } } /** * {@inheritDoc} */ @ Override public Dimension getPreferredSize ( ) { return new Dimension ( this.errorIcon.getIconWidth ( ) , getHeight ( ) ) ; } /** * Handles the mouse clicked event. * * @param pMouseEvent The {@link MouseEvent}. */ public void mouseClicked ( MouseEvent pMouseEvent ) { if ( this.currentLeft == - 1 || this.currentRight == - 1 ) { return ; } int y = pMouseEvent.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.exceptions [ i ] instanceof LanguageParserWarningException ) { LanguageParserWarningException e = ( LanguageParserWarningException ) this.exceptions [ i ] ; this.currentLeft = this.exceptions [ i ].getLeft ( ) ; this.currentRight = this.exceptions [ i ].getRight ( ) ; fireInsertText ( e.getInsertText ( ) ) ; return ; } if ( this.exceptions [ i ] instanceof LanguageParserReplaceException ) { LanguageParserReplaceException e = ( LanguageParserReplaceException ) this.exceptions [ i ] ; this.currentLeft = this.exceptions [ i ].getLeft ( ) ; this.currentRight = this.exceptions [ i ].getRight ( ) ; ArrayList < LanguageParserReplaceException > list = new ArrayList < LanguageParserReplaceException > ( ) ; for ( int j = 0 ; j < this.exceptions.length ; j ++ ) { if ( this.exceptions [ j ] instanceof LanguageParserReplaceException ) { list .add ( ( LanguageParserReplaceException ) this.exceptions [ j ] ) ; } } int [ ] start = new int [ list.size ( ) ] ; int [ ] end = new int [ list.size ( ) ] ; for ( int j = 0 ; j < list.size ( ) ; j ++ ) { start [ j ] = list.get ( j ).getParserStartOffsetReplace ( ) [ 0 ] ; end [ j ] = list.get ( j ).getParserEndOffsetReplace ( ) [ 0 ] ; } fireReplaceText ( start , end , e.getReplaceText ( ) ) ; return ; } } } fireMarkText ( ) ; } /** * Handles the mouse move event. * * @param pMouseEvent The {@link MouseEvent}. */ private void mouseMoved ( MouseEvent pMouseEvent ) { if ( this.verticalPositions == null ) { return ; } int y = pMouseEvent.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.exceptions [ i ].getLeft ( ) ; this.currentRight = this.exceptions [ i ].getRight ( ) ; setToolTipText ( this.exceptions [ 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 void paintComponent ( Graphics gc ) { if ( this.proppertyChanged ) { buildMarks ( ) ; } gc.setColor ( getBackground ( ) ) ; gc.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.exceptions [ i ] instanceof LanguageParserWarningException ) { gc.drawImage ( this.warningIcon.getImage ( ) , 0 , y0 , this ) ; } else if ( this.exceptions [ i ] instanceof LanguageParserReplaceException ) { gc.drawImage ( this.replaceIcon.getImage ( ) , 0 , y0 , this ) ; } else { gc.drawImage ( this.errorIcon.getImage ( ) , 0 , y0 , this ) ; } } gc.fillRect ( 0 , getHeight ( ) - this.horizontalScrollBar.getHeight ( ) , getWidth ( ) , this.horizontalScrollBar.getHeight ( ) ) ; } /** * Adds the given {@link SideBarListener}. * * @param pSideBarListener The given {@link SideBarListener}. */ public void removeSideBarListener ( SideBarListener pSideBarListener ) { this.listenerList.remove ( SideBarListener.class , pSideBarListener ) ; } }