/**
* 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.
*/
package de.codesourcery.jasm16.ide.ui.views;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.table.AbstractTableModel;
import de.codesourcery.jasm16.emulator.Breakpoint;
import de.codesourcery.jasm16.emulator.EmulationListener;
import de.codesourcery.jasm16.emulator.IEmulationListener;
import de.codesourcery.jasm16.emulator.IEmulator;
import de.codesourcery.jasm16.exceptions.ParseException;
import de.codesourcery.jasm16.ide.DebuggerOptions;
import de.codesourcery.jasm16.ide.IAssemblyProject;
import de.codesourcery.jasm16.ide.ui.utils.UIUtils;
import de.codesourcery.jasm16.utils.Misc;
public abstract class BreakpointView extends AbstractView {
private static final String UNCONDITIONAL_BREAKPOINT = "<unconditional>";
public static final String VIEW_ID = "breakpoints-view";
private final SourceLevelDebugView sourceLevelDebugView;
private final DisassemblerView disView;
private final IEmulator emulator;
private JPanel panel;
private static final int COL_BP_ENABLED = 0;
private static final int COL_BP_ADDRESS = 1;
private static final int COL_BP_EXPRESSION = 2;
private final MyTableModel tableModel = new MyTableModel();
private final JTable table = new JTable( tableModel );
private final IEmulationListener listener = new EmulationListener()
{
public void breakpointAdded(IEmulator emulator, final Breakpoint breakpoint)
{
if ( getCurrentProject() != null ) {
getCurrentProject().getConfiguration().getDebuggerOptions().addBreakpoint( breakpoint );
}
UIUtils.invokeLater( new Runnable() {
public void run()
{
tableModel.addBreakpoint( breakpoint );
}
});
}
public void onBreakpoint(IEmulator emulator, final Breakpoint breakpoint)
{
UIUtils.invokeLater( new Runnable() {
public void run() {
int row = tableModel.getRow( breakpoint );
int viewRow = table.convertRowIndexToView( row );
table.getSelectionModel().setSelectionInterval( viewRow , viewRow );
}
});
};
public void breakpointChanged(IEmulator emulator, final Breakpoint breakpoint)
{
if ( getCurrentProject() != null ) {
getCurrentProject().getConfiguration().getDebuggerOptions().breakpointChanged(breakpoint);
}
UIUtils.invokeLater( new Runnable() {
public void run() {
tableModel.breakpointChanged( breakpoint );
}
});
}
public void breakpointDeleted(IEmulator emulator, final Breakpoint breakpoint)
{
if ( getCurrentProject() != null ) {
getCurrentProject().getConfiguration().getDebuggerOptions().deleteBreakpoint(breakpoint );
}
UIUtils.invokeLater( new Runnable() {
public void run() {
tableModel.deleteBreakpoint( breakpoint );
}
});
}
};
protected final class MyTableModel extends AbstractTableModel {
private final List<Breakpoint> breakPoints = new ArrayList<Breakpoint>();
@Override
public String getColumnName(int column)
{
switch( column )
{
case COL_BP_ENABLED:
return "Enabled?";
case COL_BP_ADDRESS:
return "Address";
case COL_BP_EXPRESSION:
return "Expression";
default:
return "<unknown column>";
}
}
public void clear()
{
if ( ! breakPoints.isEmpty() ) {
int size = breakPoints.size();
breakPoints.clear();
fireTableRowsDeleted(0, size-1 );
}
}
public int getRow(Breakpoint breakpoint)
{
for ( int index = 0 ; index < breakPoints.size() ; index++) {
if ( breakPoints.get( index ).getAddress().equals( breakpoint.getAddress() ) ) {
return index;
}
}
throw new IllegalArgumentException("Unknown breakpoint: "+breakpoint);
}
public Breakpoint getBreakpoint(int modelRow)
{
return breakPoints.get( modelRow );
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
final Breakpoint bp = getBreakpoint( rowIndex );
switch( columnIndex )
{
case COL_BP_ENABLED:
bp.setEnabled((Boolean) aValue);
if ( getCurrentProject() != null ) {
getCurrentProject().getConfiguration().getDebuggerOptions().breakpointChanged( bp );
}
break;
case COL_BP_EXPRESSION:
if ( UNCONDITIONAL_BREAKPOINT.equals( aValue ) ) {
return;
}
try {
bp.setCondition((String) aValue);
if ( getCurrentProject() != null ) {
getCurrentProject().getConfiguration().getDebuggerOptions().breakpointChanged( bp );
}
}
catch (ParseException e)
{
e.printStackTrace();
return;
}
break;
default:
throw new UnsupportedOperationException("Unhandled column edited: "+columnIndex);
}
emulator.breakpointChanged( bp );
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
switch( columnIndex ) {
case COL_BP_ENABLED:
/* fall-through */
case COL_BP_EXPRESSION:
return true;
default:
return false;
}
}
@Override
public int getRowCount() {
return breakPoints.size();
}
public void deleteBreakpoint(Breakpoint bp)
{
final int idx = breakPoints.indexOf( bp );
if ( idx != -1 ) {
breakPoints.remove( bp );
fireTableRowsDeleted( idx , idx );
}
}
public void breakpointChanged(Breakpoint bp)
{
final int idx = breakPoints.indexOf( bp );
if ( idx != -1 ) {
fireTableRowsUpdated( idx , idx );
}
}
public void addBreakpoint(Breakpoint bp)
{
int pos = 0;
for ( ; pos < breakPoints.size() ; pos++)
{
if ( breakPoints.get( pos ).getAddress().isGreaterThan( bp.getAddress() ) ) {
breakPoints.add( pos , bp );
fireTableRowsInserted( pos , pos );
return;
}
}
pos = breakPoints.size();
breakPoints.add( bp );
fireTableRowsInserted( pos , pos );
}
@Override
public int getColumnCount() {
return 3;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
switch( columnIndex )
{
case COL_BP_ENABLED:
return Boolean.class;
case COL_BP_ADDRESS:
return String.class;
case COL_BP_EXPRESSION:
return String.class;
default:
return String.class;
}
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
final Breakpoint bp = breakPoints.get( rowIndex );
switch( columnIndex )
{
case COL_BP_ENABLED:
return bp.isEnabled();
case COL_BP_ADDRESS:
return Misc.toHexString( bp.getAddress() );
case COL_BP_EXPRESSION:
return bp.hasCondition() ? bp.getCondition() : UNCONDITIONAL_BREAKPOINT;
default:
return "<unknown column>";
}
}
};
public BreakpointView(DisassemblerView disView , SourceLevelDebugView sourceLevelDebugView, IEmulator emulator) {
if ( emulator == null ) {
throw new IllegalArgumentException("emulator must not be null");
}
if ( disView == null ) {
throw new IllegalArgumentException("disView must not be null");
}
this.sourceLevelDebugView = sourceLevelDebugView; // may be NULL if we're debugging a plain object file without the source
this.disView = disView;
this.emulator = emulator;
emulator.addEmulationListener( listener );
}
protected abstract IAssemblyProject getCurrentProject();
@Override
public void disposeHook()
{
emulator.removeEmulationListener( listener );
}
@Override
public void refreshDisplay()
{
if ( getCurrentProject() != null )
{
emulator.deleteAllBreakpoints();
tableModel.clear();
for ( Breakpoint bp : getCurrentProject().getConfiguration().getDebuggerOptions().getBreakpoints() )
{
emulator.addBreakpoint( bp );
}
}
}
@Override
public String getTitle() {
return "Breakpoints";
}
@Override
public String getID() {
return VIEW_ID;
}
@Override
protected JPanel getPanel()
{
if ( panel == null ) {
panel = createPanel();
}
return panel;
}
private JPanel createPanel()
{
table.setFillsViewportHeight( true );
table.setFont( getMonospacedFont() );
table.addMouseListener( new MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent e) {
if ( e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1 ) {
final int viewRow = table.rowAtPoint( e.getPoint() );
if ( viewRow != -1 ) {
final int modelRow = table.convertRowIndexToModel( viewRow );
final Breakpoint breakPoint = tableModel.getBreakpoint( modelRow );
disView.setViewStartingAddress( breakPoint.getAddress() );
if ( sourceLevelDebugView != null ) { // may be NULL when debugging a plain object file without attached source
sourceLevelDebugView.scrollToVisible( breakPoint.getAddress() );
}
}
}
};
} );
table.getActionMap().put("deleteRow" , new AbstractAction("deleteRow") {
@Override
public void actionPerformed(ActionEvent e)
{
final int[] viewRows = table.getSelectedRows();
final List<Breakpoint> toDelete = new ArrayList<>();
for ( int viewRow : viewRows ) {
final int modelRow = table.convertRowIndexToModel( viewRow );
Breakpoint bp = tableModel.getBreakpoint( modelRow );
toDelete.add( bp );
}
if ( getCurrentProject() != null )
{
final DebuggerOptions options = getCurrentProject().getConfiguration().getDebuggerOptions();
for ( Breakpoint bp : toDelete )
{
options.deleteBreakpoint( bp );
}
}
for ( Breakpoint bp : toDelete )
{
emulator.deleteBreakpoint( bp );
}
}
} );
final KeyStroke stroke = KeyStroke.getKeyStroke( KeyEvent.VK_DELETE , 0 );
table.getInputMap().put( stroke , "deleteRow" );
setColors( table );
// setup scrollpane
final JScrollPane pane = new JScrollPane( table );
setColors( pane );
// setup result panel
final JPanel panel = new JPanel();
setColors( panel );
panel.setLayout( new GridBagLayout() );
final GridBagConstraints cnstrs = constraints( 0 , 0 , true , true , GridBagConstraints.BOTH );
panel.add( pane , cnstrs );
return panel;
}
}