/*******************************************************************************
* Copyright (c) 2004, 2008, 2008 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX Software Systems - Initial API and implementation
* Freescale Semiconductor - Address watchpoints, https://bugs.eclipse.org/bugs/show_bug.cgi?id=118299
* IBM Corporation
*******************************************************************************/
package org.eclipse.cdt.debug.internal.ui.actions;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.debug.core.cdi.model.ICDIMemorySpaceManagement;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
/**
* The "Add Watchpoint" dialog of the "Toggle watchpoint" action.
*/
public class AddWatchpointDialog extends Dialog implements ModifyListener, SelectionListener {
private Combo fExpressionInput;
private String fExpression;
private static List<String> sExpressionHistory = new ArrayList<String>();
private boolean fHasMemorySpaceControls;
private Button fMemorySpaceEnableButton;
private Combo fMemorySpaceInput;
private String fMemorySpace;
private boolean fRangeInitialEnable;
private Button fRangeEnableButton;
private Text fRangeField;
private String fRange = ""; //$NON-NLS-1$
private Button fChkBtnWrite;
private Button fChkBtnRead;
private boolean fRead;
private boolean fWrite;
private ICDIMemorySpaceManagement fMemManagement;
/**
* Constructor for AddWatchpointDialog.
*
* @param parentShell
*/
public AddWatchpointDialog( Shell parentShell, ICDIMemorySpaceManagement memMgmt ) {
super( parentShell );
setShellStyle( getShellStyle() | SWT.RESIZE );
fMemManagement = memMgmt;
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
*/
protected Control createDialogArea( Composite parent ) {
// The button bar will work better if we make the parent composite
// a single column grid layout. For the widgets we add, we want a
// a two-column grid, so we just create a sub composite for that.
GridLayout gridLayout = new GridLayout();
parent.setLayout( gridLayout );
GridData gridData = new GridData( GridData.FILL_BOTH );
parent.setLayoutData( gridData );
Composite composite = new Composite( parent, SWT.None );
gridLayout = new GridLayout();
gridLayout.numColumns = 2;
composite.setLayout( gridLayout );
parent = composite;
// Create the controls
createExpressionControl( parent );
boolean hasDebugContext = DebugUITools.getDebugContext() != null;
boolean hasMemorySpaces = hasDebugContext && fMemManagement != null && fMemManagement.getMemorySpaces() != null && fMemManagement.getMemorySpaces().length > 0;
fHasMemorySpaceControls = !hasDebugContext || hasMemorySpaces;
if ( fHasMemorySpaceControls ) {
createMemorySpaceControl( parent, hasMemorySpaces );
}
createCountField( parent );
createAccessWidgets( parent );
// Initialize the inter-control state
if ( fExpression != null && fExpression.length() > 0 ) {
fExpressionInput.add( fExpression, 0 );
fExpressionInput.select( 0 );
}
fExpressionInput.setFocus();
if ( fHasMemorySpaceControls ) {
fMemorySpaceInput.setEnabled( fMemorySpaceEnableButton.getEnabled() );
}
fRangeField.setEnabled( fRangeEnableButton.getEnabled() );
updateUI();
return parent;
}
private void createExpressionControl(Composite parent ) {
Label l = new Label( parent, GridData.FILL_HORIZONTAL );
l.setText( ActionMessages.getString( "AddWatchpointDialog.1" ) ); //$NON-NLS-1$
GridData gridData = new GridData( GridData.FILL_HORIZONTAL );
gridData.horizontalSpan = 2;
l.setLayoutData( gridData );
fExpressionInput = new Combo( parent, SWT.BORDER );
gridData = new GridData( GridData.FILL_HORIZONTAL );
gridData.horizontalSpan = 2;
fExpressionInput.setLayoutData( gridData );
fExpressionInput.addModifyListener( this );
for (String expression : sExpressionHistory) {
fExpressionInput.add( expression );
}
}
private void createMemorySpaceControl( Composite parent, boolean hasMemorySpaces ) {
fMemorySpaceEnableButton = new Button( parent, SWT.CHECK );
GridData gridData = new GridData( GridData.FILL_HORIZONTAL );
gridData.horizontalSpan = 1;
fMemorySpaceEnableButton.setLayoutData( gridData );
fMemorySpaceEnableButton.setText( ActionMessages.getString( "AddWatchpointDialog.5" ) ); //$NON-NLS-1$
fMemorySpaceEnableButton.setSelection( false );
fMemorySpaceEnableButton.addSelectionListener( this );
if ( hasMemorySpaces ) {
fMemorySpaceInput = new Combo( parent, SWT.BORDER | SWT.READ_ONLY );
} else {
fMemorySpaceInput = new Combo( parent, SWT.BORDER );
}
gridData = new GridData( GridData.FILL_HORIZONTAL );
gridData.horizontalSpan = 1;
fMemorySpaceInput.setLayoutData( gridData );
fMemorySpaceInput.addSelectionListener( this );
if ( fMemManagement != null ) {
String [] memorySpaces = fMemManagement.getMemorySpaces();
for ( int i = 0; i < memorySpaces.length; i++ ) {
fMemorySpaceInput.add( memorySpaces[i] );
}
}
if ( fMemorySpace != null && fMemorySpace.length() > 0 ) {
int i = fMemorySpaceInput.indexOf( fMemorySpace );
if ( i >= 0 ) {
fMemorySpaceInput.select( i );
fMemorySpaceEnableButton.setSelection( true );
} else {
fMemorySpaceInput.add( fMemorySpace );
}
}
fMemorySpaceInput.addModifyListener( this );
//234909 - for accessibility
fMemorySpaceInput.getAccessible().addAccessibleListener(
new AccessibleAdapter() {
@Override
public void getName(AccessibleEvent e) {
e.result = ActionMessages.getString( "AddWatchpointDialog.5" ); //$NON-NLS-1$
}
});
}
/**
* @param text
* @param c
* @return true if the concatenation of text + c results
* in a valid string representation of an integer
* @see verifyIntegerText()
*/
private static boolean verifyIntegerTextAddition( String text, char c ) {
// pass through all control characters
if ( Character.isISOControl( c ) ) {
return true;
}
// case-insensitive
c = Character.toLowerCase( c );
text = text.toLowerCase();
// first character has to be 0-9
if ( text.length() == 0 ) {
return Character.isDigit( c );
}
// second character must be x if preceded by a 0, otherwise 0-9 will do
if ( text.length() == 1 ) {
if ( text.equals( "0" ) ) { //$NON-NLS-1$
return c == 'x';
}
return Character.isDigit( c );
}
// all subsequent characters must be 0-9 or a-f if started with 0x
return Character.isDigit( c )
|| text.startsWith( "0x" ) && 'a' <= c && c <= 'f'; //$NON-NLS-1$
}
/**
* @param text integer string built up using verifyIntegerTextAddition()
* @return true if text represents a valid string representation of
* an integer
*/
private static boolean verifyIntegerText( String text ) {
if ( text.length() == 0 ) {
return false;
}
if ( text.length() == 1 ) {
return true;
}
if ( text.length() == 2 ) {
return !text.equals("0x"); //$NON-NLS-1$
}
return true;
}
private void createCountField( Composite parent ) {
fRangeEnableButton = new Button( parent, SWT.CHECK );
GridData gridData = new GridData( GridData.FILL_HORIZONTAL );
gridData.horizontalSpan = 1;
fRangeEnableButton.setLayoutData( gridData );
fRangeEnableButton.setText( ActionMessages.getString( "AddWatchpointDialog.6" ) ); //$NON-NLS-1$
fRangeEnableButton.setSelection( fRangeInitialEnable && fRange.length() > 0 );
fRangeEnableButton.addSelectionListener( this );
fRangeField = new Text( parent, SWT.BORDER );
gridData = new GridData( GridData.FILL_HORIZONTAL );
gridData.horizontalSpan = 1;
GC gc = new GC( fRangeField );
FontMetrics fm = gc.getFontMetrics();
gridData.minimumWidth = 8 * fm.getAverageCharWidth();
fRangeField.setLayoutData( gridData );
fRangeField.setText( fRange );
fRangeField.addVerifyListener( new VerifyListener() {
public void verifyText( VerifyEvent e ) {
e.doit = verifyIntegerTextAddition( fRangeField.getText(), e.character );
}
});
fRangeField.addModifyListener( this );
//234909 - for accessibility
fRangeField.getAccessible().addAccessibleListener(
new AccessibleAdapter() {
@Override
public void getName(AccessibleEvent e) {
e.result = ActionMessages.getString( "AddWatchpointDialog.6" ); //$NON-NLS-1$
}
});
}
private void createAccessWidgets( Composite parent ) {
GridData gridData = new GridData( GridData.FILL_HORIZONTAL );
gridData.horizontalSpan = 3;
Group group = new Group( parent, SWT.NONE );
group.setLayout( new GridLayout() );
group.setLayoutData( gridData );
group.setText( ActionMessages.getString( "AddWatchpointDialog.2" ) ); //$NON-NLS-1$
fChkBtnWrite = new Button( group, SWT.CHECK );
fChkBtnWrite.setText( ActionMessages.getString( "AddWatchpointDialog.3" ) ); //$NON-NLS-1$
fChkBtnWrite.setSelection( true );
fChkBtnWrite.addSelectionListener( this );
fChkBtnRead = new Button( group, SWT.CHECK );
fChkBtnRead.setText( ActionMessages.getString( "AddWatchpointDialog.4" ) ); //$NON-NLS-1$
fChkBtnRead.setSelection( false );
fChkBtnRead.addSelectionListener( this );
}
/* (non-Javadoc)
* @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
*/
protected void configureShell( Shell newShell ) {
super.configureShell( newShell );
// use the same title used by the platform dialog
newShell.setText( ActionMessages.getString( "AddWatchpointDialog.0" ) ); //$NON-NLS-1$
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.Dialog#okPressed()
*/
protected void okPressed() {
fExpression = fExpressionInput.getText().trim();
if ( fExpression.length() > 0 ) {
addHistory( fExpression );
}
if ( fHasMemorySpaceControls ) {
fMemorySpace = fMemorySpaceEnableButton.getSelection() ? fMemorySpaceInput.getText().trim() : ""; //$NON-NLS-1$
}
fRange = fRangeEnableButton.getSelection() ? fRangeField.getText().trim() : "0"; //$NON-NLS-1$
fRead = fChkBtnRead.getSelection();
fWrite = fChkBtnWrite.getSelection();
super.okPressed();
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
*/
public void modifyText( ModifyEvent e ) {
updateUI();
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.TrayDialog#createButtonBar(org.eclipse.swt.widgets.Composite)
*/
protected Control createButtonBar( Composite parent ) {
return super.createButtonBar( parent );
}
public String getExpression() {
return fExpression;
}
public String getMemorySpace() {
return fMemorySpace;
}
private static void addHistory( String item ) {
if ( !sExpressionHistory.contains( item ) ) {
sExpressionHistory.add( 0, item );
if ( sExpressionHistory.size() > 5 )
sExpressionHistory.remove( sExpressionHistory.size() - 1 );
}
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetDefaultSelected( SelectionEvent e ) {
// ignore
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected( SelectionEvent e ) {
updateUI();
}
private void updateUI() {
if ( fHasMemorySpaceControls ) {
fMemorySpaceInput.setEnabled( fMemorySpaceEnableButton.getSelection() );
}
fRangeField.setEnabled( fRangeEnableButton.getSelection() );
Button b = getButton( IDialogConstants.OK_ID );
if ( b == null ) {
return;
}
b.setEnabled( okayEnabled() );
}
private boolean okayEnabled() {
if ( !fChkBtnRead.getSelection() && !fChkBtnWrite.getSelection() ) {
return false ;
}
if ( fExpressionInput.getText().length() == 0 ) {
return false;
}
if ( fHasMemorySpaceControls && fMemorySpaceInput.getEnabled() && fMemorySpaceInput.getText().length() == 0 ) {
return false;
}
if ( fRangeField.getEnabled()
&& ( fRangeField.getText().length() == 0 || !verifyIntegerText( fRangeField.getText() ) ) ) {
return false;
}
return true;
}
public boolean getWriteAccess() {
return fWrite;
}
public boolean getReadAccess() {
return fRead;
}
public void setExpression(String expressionString ) {
fExpression = expressionString;
}
public BigInteger getRange() {
return BigInteger.valueOf( Long.decode(fRange).longValue() );
}
public void initializeRange( boolean enable, String range ) {
fRangeInitialEnable = enable;
fRange = range;
}
public void initializeMemorySpace( String memorySpace ) {
fMemorySpace = memorySpace;
}
protected void createButtonsForButtonBar(Composite parent) {
// override so we can change the initial okay enabled state
createButton( parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
true ).setEnabled( okayEnabled() );
createButton( parent, IDialogConstants.CANCEL_ID,
IDialogConstants.CANCEL_LABEL, false );
}
}