Java tutorial
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; } }