package de.codesourcery.jasm16.ide.ui.views;
/**
* Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.NoSuchElementException;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.CaretEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreePath;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import de.codesourcery.jasm16.Address;
import de.codesourcery.jasm16.ast.AST;
import de.codesourcery.jasm16.ast.ASTNode;
import de.codesourcery.jasm16.ast.ASTUtils;
import de.codesourcery.jasm16.ast.ISimpleASTNodeVisitor;
import de.codesourcery.jasm16.ast.InstructionNode;
import de.codesourcery.jasm16.ast.LabelNode;
import de.codesourcery.jasm16.ast.NumberNode;
import de.codesourcery.jasm16.ast.OperatorNode;
import de.codesourcery.jasm16.ast.StatementNode;
import de.codesourcery.jasm16.ast.SymbolReferenceNode;
import de.codesourcery.jasm16.compiler.Equation;
import de.codesourcery.jasm16.compiler.ICompilationError;
import de.codesourcery.jasm16.compiler.ICompilationUnit;
import de.codesourcery.jasm16.compiler.ISymbol;
import de.codesourcery.jasm16.compiler.ISymbolTable;
import de.codesourcery.jasm16.compiler.Label;
import de.codesourcery.jasm16.compiler.Severity;
import de.codesourcery.jasm16.compiler.SourceLocation;
import de.codesourcery.jasm16.compiler.io.DefaultResourceMatcher;
import de.codesourcery.jasm16.compiler.io.IResource;
import de.codesourcery.jasm16.compiler.io.IResourceResolver;
import de.codesourcery.jasm16.exceptions.ParseException;
import de.codesourcery.jasm16.ide.IAssemblyProject;
import de.codesourcery.jasm16.ide.IWorkspace;
import de.codesourcery.jasm16.ide.IWorkspaceListener;
import de.codesourcery.jasm16.ide.NavigationHistory;
import de.codesourcery.jasm16.ide.WorkspaceListener;
import de.codesourcery.jasm16.ide.ui.utils.ASTTableModelWrapper;
import de.codesourcery.jasm16.ide.ui.utils.UIUtils;
import de.codesourcery.jasm16.ide.ui.viewcontainers.EditorContainer;
import de.codesourcery.jasm16.ide.ui.viewcontainers.ViewContainerManager;
import de.codesourcery.jasm16.parser.Identifier;
import de.codesourcery.jasm16.utils.ITextRegion;
import de.codesourcery.jasm16.utils.Line;
import de.codesourcery.jasm16.utils.Misc;
import de.codesourcery.jasm16.utils.TextRegion;
/**
* Crude editor to test the compiler's inner workings.
*
* @author tobias.gierke@code-sourcery.de
*/
public class SourceEditorView extends SourceCodeView {
private static final Logger LOG = Logger.getLogger(SourceEditorView.class);
// UI widgets
private volatile JPanel panel;
private final ViewContainerManager viewContainerManager;
private JFrame astInspector;
private final JTree astTree = new JTree();
private final JTable statusArea = new JTable();
private final StatusModel statusModel = new StatusModel();
private final JButton navigationHistoryBack = new JButton("Previous");
private final JButton navigationHistoryForward = new JButton("Next");
private final SymbolTableModel symbolTableModel = new SymbolTableModel();
private final JTable symbolTable = new JTable( symbolTableModel );
// compiler
private final IWorkspaceListener workspaceListener = new WorkspaceListener() {
public void projectDeleted(IAssemblyProject deletedProject)
{
if ( deletedProject.isSame( getCurrentProject() ) )
{
dispose();
}
}
private void dispose() {
if ( getViewContainer() != null ) {
getViewContainer().disposeView( SourceEditorView.this );
} else {
SourceEditorView.this.dispose();
}
}
public void resourceDeleted(IAssemblyProject project, IResource deletedResource)
{
if ( DefaultResourceMatcher.INSTANCE.isSame( deletedResource , getCurrentResource() ) )
{
dispose();
}
}
public void buildFinished(IAssemblyProject project, boolean success) {
if ( isASTInspectorVisible() ) {
symbolTableModel.refresh();
}
}
};
protected static final class StatusMessage
{
private final Severity severity;
private final ITextRegion location;
private final String message;
@SuppressWarnings("unused")
private final Throwable cause;
private final ICompilationError error;
public StatusMessage(Severity severity, String message)
{
this( severity , null , message , null ,null );
}
public StatusMessage(Severity severity, ITextRegion location, String message)
{
this( severity , location , message , null ,null);
}
public StatusMessage(Severity severity, ICompilationError error)
{
this(severity,error.getLocation(),error.getMessage(),error,error.getCause());
}
public StatusMessage(Severity severity, ITextRegion location, String message, ICompilationError error,Throwable cause)
{
if ( severity == null ) {
throw new IllegalArgumentException("severity must not be NULL.");
}
if (StringUtils.isBlank(message) ) {
throw new IllegalArgumentException("message must not be NULL/blank.");
}
this.severity = severity;
this.location = location;
this.message = message;
if ( cause == null ) {
this.cause = error != null ? error.getCause() : null;
} else {
this.cause = cause;
}
this.error = error;
}
public StatusMessage(Severity severity, String message, Throwable e)
{
this(severity, null , message , null , e );
}
public Severity getSeverity()
{
return severity;
}
public ITextRegion getLocation()
{
return location;
}
public String getMessage()
{
return message;
}
public ICompilationError getError()
{
return error;
}
}
protected class StatusModel extends AbstractTableModel
{
private final List<StatusMessage> messages = new ArrayList<StatusMessage>();
private final int COL_SEVERITY = 0;
private final int COL_LOCATION = 1;
private final int COL_MESSAGE = 2;
public StatusModel() {
}
@Override
public int getRowCount()
{
return messages.size();
}
public StatusMessage getMessage(int modelRow) {
return messages.get(modelRow);
}
public void addMessage(StatusMessage msg) {
if ( msg == null ) {
throw new IllegalArgumentException("msg must not be NULL.");
}
int index = messages.size();
messages.add( msg );
fireTableRowsInserted( index, index );
}
public void setMessage(StatusMessage msg)
{
if ( msg == null ) {
throw new IllegalArgumentException("msg must not be NULL.");
}
messages.clear();
messages.add( msg );
fireTableDataChanged();
}
@Override
public int getColumnCount()
{
return 3;
}
@Override
public String getColumnName(int columnIndex)
{
switch(columnIndex) {
case COL_SEVERITY:
return "Severity";
case COL_LOCATION:
return "Location";
case COL_MESSAGE:
return "Message";
default:
return "no column name?";
}
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
return String.class;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return false;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
final StatusMessage msg = messages.get( rowIndex );
switch(columnIndex) {
case COL_SEVERITY:
return msg.getSeverity().toString();
case COL_LOCATION:
if ( msg.getLocation() != null ) {
SourceLocation location;
try {
location = getSourceLocation(msg.getLocation());
return "Line "+location.getLineNumber()+" , column "+location.getColumnNumber();
} catch (NoSuchElementException e) {
// ok, can't help it
}
}
return "<unknown>";
case COL_MESSAGE:
return msg.getMessage();
default:
return "no column name?";
}
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
throw new UnsupportedOperationException("");
}
public void addError(String message, IOException e1)
{
addMessage( new StatusMessage(Severity.ERROR , message , e1 ) );
}
public void addInfo(String message)
{
addMessage( new StatusMessage(Severity.INFO , message ) );
}
public void addWarning(String message)
{
addMessage( new StatusMessage(Severity.WARNING , message ) );
}
public void clearMessages()
{
messages.clear();
fireTableDataChanged();
}
}
protected void onCaretUpdate(CaretEvent e)
{
// if AST inspector is visible, make sure the current AST node is visible
// (scroll there if it isn't)
if ( ! isASTInspectorVisible() ) {
return;
}
final AST ast = getCurrentCompilationUnit() != null ? getCurrentCompilationUnit().getAST() : null;
if ( ast == null ) {
return;
}
final ASTNode n = ast.getNodeInRange( e.getDot() );
if ( n != null ) {
TreePath path = new TreePath( n.getPathToRoot() );
astTree.setSelectionPath( path );
astTree.scrollPathToVisible( path );
}
}
private boolean isASTInspectorVisible() {
return astInspector != null && astInspector.isVisible();
}
public SourceEditorView(IResourceResolver resourceResolver,IWorkspace workspace,
ViewContainerManager viewContainerManager,NavigationHistory navigationHistory)
{
super( resourceResolver,workspace , navigationHistory , true );
workspace.addWorkspaceListener( workspaceListener );
this.viewContainerManager = viewContainerManager;
}
protected void setStatusMessage(String message) {
}
@Override
protected final void setupKeyBindingsHook(final JTextPane editor)
{
// 'Rename' action
addKeyBinding( editor ,
KeyStroke.getKeyStroke(KeyEvent.VK_R,Event.ALT_MASK|Event.SHIFT_MASK),
new AbstractAction()
{
@Override
public void actionPerformed(ActionEvent e) {
maybeRenameLabel( editor.getCaretPosition() );
}
});
}
private void maybeRenameLabel(int caretPosition)
{
final ICompilationUnit compilationUnit = getCurrentCompilationUnit();
if ( compilationUnit == null || compilationUnit.getAST().hasErrors() ) {
System.out.println("*** Renaming not supported for erronous compilation units");
return;
}
final ASTNode node = compilationUnit.getAST().getNodeInRange( caretPosition );
final ISymbol scopeOfSymbolToRename;
final Identifier symbolToRename;
if ( node instanceof LabelNode)
{
symbolToRename = ((LabelNode) node).getIdentifier();
scopeOfSymbolToRename = ((LabelNode) node).getScope();
}
else if ( node instanceof SymbolReferenceNode)
{
ISymbol resolved = ((SymbolReferenceNode) node).resolve( compilationUnit.getSymbolTable() );
symbolToRename = resolved.getName();
scopeOfSymbolToRename = resolved.getScope();
} else {
return;
}
final ISymbol oldSymbol = compilationUnit.getSymbolTable().getSymbol( symbolToRename , scopeOfSymbolToRename );
if ( oldSymbol == null ) {
System.out.println("*** Renaming symbols defined in other source files is currently not implemented, sorry. ***");
return;
}
// TODO: Add support for renaming .equ definitions ...
if ( !( oldSymbol instanceof Label ) ) {
System.out.println("*** Only labels can currently be renamed, sorry. ***");
return;
}
final ISymbol oldScope = oldSymbol.getScope();
final Identifier oldIdentifier = oldSymbol.getName();
String result = UIUtils.showInputDialog(null, "Please choose a new identifier","Enter a new identifier for '"+oldIdentifier.getRawValue()+"'");
if ( StringUtils.isBlank(result) || ! Identifier.isValidIdentifier( result ) ) {
return;
}
final Identifier newIdentifier;
try {
newIdentifier = new Identifier(result);
} catch(ParseException e) {
throw new RuntimeException(e);
}
// rename symbol (this will ALSO update the symbol's ITextRegion !)
final ISymbol newSymbol = compilationUnit.getSymbolTable().renameSymbol( oldSymbol , newIdentifier );
// gather all AST nodes that need to be updated
final List<ASTNode> nodesRequiringUpdate = new ArrayList<>();
final ISimpleASTNodeVisitor<ASTNode> simpleAstVisitor = new ISimpleASTNodeVisitor<ASTNode>() {
@Override
public boolean visit(ASTNode node)
{
if ( node instanceof LabelNode ) {
final LabelNode label = (LabelNode) node;
if ( ObjectUtils.equals( oldIdentifier , label.getIdentifier() ) &&
ObjectUtils.equals( oldScope, label.getScope() ) )
{
nodesRequiringUpdate.add( node );
}
}
else if ( node instanceof SymbolReferenceNode)
{
final SymbolReferenceNode ref = (SymbolReferenceNode) node;
final ISymbol symbol = ref.resolve( compilationUnit.getSymbolTable() );
if ( symbol != null &&
ObjectUtils.equals( oldIdentifier , symbol.getName() ) &&
ObjectUtils.equals( oldScope , symbol.getScope() ) )
{
nodesRequiringUpdate.add( node );
}
}
return true;
}
};
ASTUtils.visitInOrder( compilationUnit.getAST() , simpleAstVisitor );
Collections.sort( nodesRequiringUpdate , new Comparator<ASTNode>() {
@Override
public int compare(ASTNode n1, ASTNode n2)
{
final ITextRegion r1 = n1.getTextRegion();
final ITextRegion r2 = n2.getTextRegion();
if ( r1 != null && r2 != null ) {
if ( r1.getStartingOffset() < r2.getStartingOffset() ) {
return -1;
}
if ( r1.getStartingOffset() > r2.getStartingOffset() ) {
return 1;
}
return 0;
}
if ( r1 != null ) {
return -1;
}
if ( r2 != null ) {
return 1;
}
return 0;
}
});
// we now need to offset the location of EVERY AST node
// that has a location > oldSymbol.getLocation()
final int lengthDelta;
if ( newIdentifier.getRawValue().length() >= oldIdentifier.getRawValue().length() ) {
lengthDelta = newIdentifier.getRawValue().length() - oldIdentifier.getRawValue().length();
} else {
// new identifier is shorter than the old one, need to adjust by a NEGATIVE offset
lengthDelta = -( oldIdentifier.getRawValue().length() - newIdentifier.getRawValue().length() );
}
try {
int currentOffsetAdjustment = 0;
for ( ASTNode n : nodesRequiringUpdate )
{
if ( n instanceof LabelNode )
{
// update symbol in document
final ITextRegion oldRegion = oldSymbol.getLocation();
final TextRegion newRegion = new TextRegion( oldRegion.getStartingOffset()+currentOffsetAdjustment,
oldRegion.getLength() );
replaceText( newRegion , newIdentifier.getRawValue() );
((LabelNode) n).setLabel( (Label) newSymbol );
n.adjustTextRegion( currentOffsetAdjustment , lengthDelta );
currentOffsetAdjustment += lengthDelta;
}
else if ( n instanceof SymbolReferenceNode)
{
((SymbolReferenceNode) n).setIdentifier( newIdentifier );
final ITextRegion oldRegion = n.getTextRegion();
final TextRegion newRegion = new TextRegion( oldRegion.getStartingOffset()+currentOffsetAdjustment,
oldRegion.getLength() );
replaceText( newRegion , newIdentifier.getRawValue() );
n.adjustTextRegion( currentOffsetAdjustment , lengthDelta );
currentOffsetAdjustment += lengthDelta;
} else {
throw new RuntimeException("Internal error,unhandled node type "+n);
}
}
} finally {
notifyDocumentChanged();
}
}
private void showASTInspector()
{
if ( astInspector == null ) {
setupASTInspector();
}
if ( ! astInspector.isVisible() )
{
symbolTableModel.refresh();
astInspector.setVisible( true );
}
}
private static final int COL_SYMBOL_NAME = 0;
private static final int COL_SYMBOL_VALUE = 1;
private static final int COL_SYMBOL_RESOURCE = 2;
protected final class SymbolTableModel extends AbstractTableModel {
private ISymbolTable getSymbolTable()
{
ISymbolTable result = getCurrentCompilationUnit().getSymbolTable();
if ( result != null && result.getParent() != null ) {
return result.getParent();
}
return result;
}
public void refresh() {
fireTableStructureChanged();
}
@Override
public int getRowCount()
{
ISymbolTable table = getSymbolTable();
return table == null ? 0 : getSymbolTable().getSize();
}
@Override
public int getColumnCount()
{
return 3;
}
private List<ISymbol> getSortedSymbols() {
ISymbolTable table = getSymbolTable();
List<ISymbol> all = table == null ? Collections.<ISymbol>emptyList() : table.getSymbols();
Collections.sort( all , new Comparator<ISymbol>() {
@Override
public int compare(ISymbol o1, ISymbol o2)
{
return o1.getFullyQualifiedName().compareTo( o2.getFullyQualifiedName() );
}
} );
return all;
}
private ISymbol getSymbolForRow(int modelRowIndex) {
return getSortedSymbols().get( modelRowIndex );
}
@Override
public String getColumnName(int column)
{
if ( column == COL_SYMBOL_NAME ) {
return "Symbol name";
} else if ( column == COL_SYMBOL_VALUE ) {
return "Symbol value";
} else if ( column == COL_SYMBOL_RESOURCE ) {
return "Compilation unit";
}
throw new IllegalArgumentException("Internal error, unhandled column "+column);
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
return String.class;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
final ISymbol symbol = getSymbolForRow( rowIndex );
if ( columnIndex == COL_SYMBOL_NAME ) {
return symbol.getFullyQualifiedName();
}
if ( columnIndex == COL_SYMBOL_VALUE )
{
if ( symbol instanceof Label ) {
final Address address = ((Label) symbol).getAddress();
return address == null ? "<no address assigned>" : address.toString();
} else if ( symbol instanceof Equation ) {
Equation eq = (Equation) symbol;
Long value = eq.getValue( getSymbolTable() );
return value == null ? "<failed to evaluate>" : value.toString();
}
return "<unknown symbol type: "+symbol+">";
}
if ( columnIndex == COL_SYMBOL_RESOURCE ) {
return symbol.getCompilationUnit();
}
throw new IllegalArgumentException("Internal error, unhandled column "+columnIndex);
}
}
private void setupASTInspector()
{
astInspector = new JFrame("AST");
final MouseAdapter treeMouseListener = new MouseAdapter()
{
@Override
public void mouseMoved(MouseEvent e)
{
String text = null;
TreePath path= astTree.getClosestPathForLocation( e.getX() , e.getY() );
if ( path != null )
{
ASTNode node = (ASTNode) path.getLastPathComponent();
if ( node instanceof InstructionNode) { // TODO: debug code, remove when done
text = null;
}
try {
text = getCurrentCompilationUnit().getSource( node.getTextRegion() );
} catch (Exception ex) {
text = "Node "+node.getClass().getSimpleName()+" has invalid text region "+node.getTextRegion();
}
text = "<HTML>"+text.replace("\n", "<BR>" )+"</HTML>";
}
if ( ! ObjectUtils.equals( astTree.getToolTipText() , text ) ) {
astTree.setToolTipText( text );
}
}
};
astTree.addMouseMotionListener( treeMouseListener );
astTree.setCellRenderer( new ASTTreeCellRenderer() );
final JScrollPane pane = new JScrollPane( astTree );
setColors( pane );
pane.setPreferredSize( new Dimension(400,600) );
GridBagConstraints cnstrs = constraints( 0, 0 , true , false , GridBagConstraints.REMAINDER );
cnstrs.weighty = 0.9;
panel.add( pane , cnstrs );
// add symbol table
symbolTable.setFillsViewportHeight( true );
MouseAdapter mouseListener = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e)
{
if ( e.getButton() == MouseEvent.BUTTON1 )
{
int viewRow = symbolTable.rowAtPoint( e.getPoint() );
if ( viewRow != -1 ) {
final int modelRow = symbolTable.convertRowIndexToModel( viewRow );
final ISymbol symbol = symbolTableModel.getSymbolForRow( modelRow );
final int caretPosition = symbol.getLocation().getStartingOffset();
IEditorView editor = null;
if ( DefaultResourceMatcher.INSTANCE.isSame( symbol.getCompilationUnit().getResource() , getSourceFromMemory() ) ) {
editor = SourceEditorView.this;
}
else if ( getViewContainer() instanceof EditorContainer)
{
final EditorContainer parent = (EditorContainer) getViewContainer();
try {
editor = parent.openResource(
workspace , getCurrentProject() ,
symbol.getCompilationUnit().getResource() , caretPosition );
}
catch (IOException e1) {
LOG.error("mouseClicked(): Failed top open "+symbol.getCompilationUnit().getResource(),e1);
return;
}
}
if ( editor instanceof SourceCodeView) {
((SourceCodeView) editor).moveCursorTo( caretPosition , true );
}
}
}
}
};
symbolTable.addMouseListener( mouseListener);
final JScrollPane tablePane = new JScrollPane( symbolTable );
setColors( tablePane );
tablePane.setPreferredSize( new Dimension(400,200) );
cnstrs = constraints( 0, 1 , true , true , GridBagConstraints.REMAINDER );
cnstrs.weighty = 0.1;
panel.add( pane , cnstrs );
final JSplitPane split = new JSplitPane( JSplitPane.VERTICAL_SPLIT , pane , tablePane );
setColors( split );
// setup content pane
astInspector.getContentPane().add( split );
setColors( astInspector.getContentPane() );
astInspector.setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE );
astInspector.pack();
}
private class ASTTreeCellRenderer extends DefaultTreeCellRenderer {
public ASTTreeCellRenderer() {
}
@Override
public Component getTreeCellRendererComponent(JTree tree,
Object value, boolean selected, boolean expanded,
boolean leaf, int row, boolean hasFocus)
{
final Component result = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
if ( ! (value instanceof ASTNode)) {
return result;
}
final ASTNode n = (ASTNode) value;
String txt;
try {
txt = getLabelFor( n );
} catch (IOException e) {
txt = e.getMessage();
}
setText( txt );
return result;
}
private String getSourceFor(ASTNode node) throws IOException {
final ITextRegion range = node.getTextRegion();
try {
return range == null ? "<no source location>" : getCurrentCompilationUnit().getSource( range );
} catch(StringIndexOutOfBoundsException e) {
return "<node has invalid text range "+range+">";
}
}
private String getLabelFor(ASTNode n) throws IOException
{
String name = n.getClass().getSimpleName();
ITextRegion range = n.getTextRegion();
String source = getSourceFor( n );
String txt = name+" "+source+" ( "+range+" )";
final Address address = ASTUtils.getEarliestMemoryLocation( n );
final String sAddress = address == null ? "" : "0x"+Misc.toHexString( address.toWordAddress() );
if ( n instanceof StatementNode )
{
final List<Line> linesForRange = getCurrentCompilationUnit().getLinesForRange( n.getTextRegion() );
return sAddress+": Statement "+StringUtils.join( linesForRange , ",");
}
if ( n instanceof AST ) {
return "AST";
}
if ( n instanceof OperatorNode ) {
return "Operator "+((OperatorNode) n).getOperator();
}
if ( n instanceof NumberNode ) {
return "Number ("+((NumberNode) n).getValue()+")";
}
if ( n instanceof LabelNode )
{
final Label label = ((LabelNode) n).getLabel();
return ( label.isLocalSymbol() ? "." : "" )+ label.getFullyQualifiedName();
}
return txt;
}
}
@Override
protected void onSourceCodeValidation()
{
statusModel.clearMessages();
}
@Override
protected void onHighlightingStart()
{
final ASTTableModelWrapper astModel = new ASTTableModelWrapper( getCurrentCompilationUnit().getAST() ) ;
astTree.setModel( astModel );
}
@Override
protected void onCompilationError(ICompilationError error)
{
statusModel.addMessage( new StatusMessage( Severity.ERROR , error ) );
}
@Override
protected void onCompilationWarning(ICompilationError error)
{
statusModel.addMessage( new StatusMessage( Severity.WARNING , error ) );
}
// ============= view creation ===================
@Override
public JPanel getPanel()
{
if ( panel == null ) {
panel = createPanel();
}
return panel;
}
@Override
protected void onNavigationHistoryChange()
{
navigationHistoryBack.setEnabled( canNavigationHistoryBack() );
navigationHistoryForward.setEnabled( canNavigationHistoryForward() );
}
protected JPanel createPanel()
{
// button panel
final JToolBar toolbar = new JToolBar();
toolbar.setLayout( new GridBagLayout() );
setColors( toolbar );
final JButton showASTButton = new JButton("Show AST" );
setColors( showASTButton );
showASTButton.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
final boolean currentlyVisible = isASTInspectorVisible();
if ( currentlyVisible ) {
showASTButton.setText("Show AST");
} else {
showASTButton.setText("Hide AST");
}
if ( currentlyVisible ) {
astInspector.setVisible( false );
} else {
showASTInspector();
}
}
} );
GridBagConstraints cnstrs = constraints( 0, 0 , false , true , GridBagConstraints.NONE );
toolbar.add( showASTButton , cnstrs );
// navigation history back button
cnstrs = constraints( 1, 0 , false , true , GridBagConstraints.NONE );
toolbar.add( navigationHistoryBack, cnstrs );
navigationHistoryBack.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
navigationHistoryBack();
}
});
navigationHistoryBack.setEnabled( false );
// navigation history forward button
cnstrs = constraints( 2, 0 , true , true , GridBagConstraints.NONE );
toolbar.add( navigationHistoryForward , cnstrs );
navigationHistoryForward.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
navigationHistoryForward();
}
});
navigationHistoryForward.setEnabled( false );
// create status area
statusArea.setPreferredSize( new Dimension(400, 100 ) );
statusArea.setModel( statusModel );
setColors( statusArea );
/**
* TOOLBAR
* SOURCE
* cursor position
* status area
*/
final JPanel topPanel = new JPanel();
topPanel.setLayout( new GridBagLayout() );
cnstrs = constraints( 0, 0 , GridBagConstraints.HORIZONTAL );
cnstrs.gridwidth = GridBagConstraints.REMAINDER;
cnstrs.weighty = 0;
topPanel.add( toolbar , cnstrs );
cnstrs = constraints( 0, 1 , GridBagConstraints.BOTH );
cnstrs.gridwidth = GridBagConstraints.REMAINDER;
cnstrs.weighty=1.0;
topPanel.add( super.getPanel() , cnstrs );
final JPanel bottomPanel = new JPanel();
bottomPanel.setLayout( new GridBagLayout() );
statusArea.setAutoResizeMode( JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS );
statusArea.addMouseListener( new MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent e)
{
if ( e.getButton() == MouseEvent.BUTTON1 ) {
final int viewRow = statusArea.rowAtPoint( e.getPoint() );
if ( viewRow != -1 ) {
final int modelRow = statusArea.convertRowIndexToModel( viewRow );
StatusMessage message = statusModel.getMessage( modelRow );
if ( message.getLocation() != null )
{
moveCursorTo( message.getLocation() , true );
}
}
}
};
} );
EditorContainer.addEditorCloseKeyListener( statusArea , this );
statusArea.setFillsViewportHeight( true );
statusArea.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
final JScrollPane statusPane = new JScrollPane( statusArea );
setColors( statusPane );
statusPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
statusPane.setPreferredSize( new Dimension(400,100 ) );
statusPane.setMinimumSize(new Dimension(100, 20));
cnstrs = constraints( 0, 0 , GridBagConstraints.BOTH );
cnstrs.weightx=1;
cnstrs.weighty=1;
cnstrs.gridwidth = GridBagConstraints.REMAINDER;
cnstrs.gridheight = GridBagConstraints.REMAINDER;
bottomPanel.add( statusPane , cnstrs );
// setup result panel
final JSplitPane splitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT , topPanel , bottomPanel );
setColors( splitPane );
final JPanel panel = new JPanel();
panel.setLayout( new GridBagLayout() );
setColors( panel );
cnstrs = constraints( 0 , 0 , true , true , GridBagConstraints.BOTH );
panel.add( splitPane , cnstrs );
final AncestorListener l = new AncestorListener() {
@Override
public void ancestorRemoved(AncestorEvent event) { }
@Override
public void ancestorMoved(AncestorEvent event) { }
@Override
public void ancestorAdded(AncestorEvent event) {
splitPane.setDividerLocation( 0.8d );
}
};
panel.addAncestorListener( l );
return panel;
}
@Override
public void disposeHook2()
{
workspace.removeWorkspaceListener( workspaceListener );
if ( astInspector != null )
{
astInspector.setVisible( false );
astInspector.dispose();
}
}
@Override
public String getID() {
return "source-editor";
}
@Override
protected JPopupMenu createPopupMenu(ASTNode node, int caretPosition,
String currentSelection)
{
final JPopupMenu popup = new JPopupMenu();
boolean gotEntries = false;
final ICompilationUnit unit = getCurrentCompilationUnit();
if ( getCurrentProject() != null && unit != null &&
unit.getAST() != null && ! unit.hasErrors() )
{
try
{
if ( WorkspaceExplorer.canOpenInDebugPerspective( getCurrentProject() ) )
{
final IResource executable = getCurrentProject().getProjectBuilder().getExecutable();
addMenuEntry( popup , "Open in debugger", new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
WorkspaceExplorer.openDebugPerspective( getCurrentProject() , executable ,
viewContainerManager );
} catch (IOException e1) {
LOG.error("actionPerformed(): ",e1);
}
}
});
gotEntries = true;
}
} catch (IOException e) {
LOG.error("createPopupMenu(): ",e);
}
}
return gotEntries ? popup : null;
}
}