/******************************************************************************* * Copyright (c) 2005, 2010 Freescale Semiconductor, Inc. * 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: * Freescale Semiconductor, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.debug.internal.ui.views.memory; import java.util.ArrayList; import java.util.List; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.jface.dialogs.TrayDialog; import org.eclipse.swt.SWT; 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.Shell; import org.eclipse.ui.PlatformUI; /** * Dialog CDT puts up when adding a memory monitor to the memory view for a * debug target that supports memory spaces. * <p> * It differs from the platform one in that you can enter an expression or * an address + memory space pair. * * @since 3.2 */ public class AddMemoryBlockDialog extends TrayDialog implements ModifyListener, SelectionListener { private Combo fAddressInput; private Button fAddressRadio; private Combo fMemorySpaceInput; private Combo fExpressionInput; private String fExpression; private Button fExpressionRadio; private String fAddress; private String fMemorySpace; private boolean fEnteredExpression; // basically, which of the two radio buttons was selected when OK was hit /** The memory spaces to expose. Given to use at instantiation time. */ final private String[] fMemorySpaces; /** * For improved usability, we persist the memory space selection from one * invocation of the dialog to another, but we need not worry about * persisting it from one instantiation of Eclipse to the next */ private static String fPreviousMemorySpaceSelection; private static List<String> sAddressHistory = new ArrayList<String>(); private static List<String> sExpressionHistory = new ArrayList<String>(); private static boolean sDefaultToExpression = true; public AddMemoryBlockDialog(Shell parentShell, String[] memorySpaces) { super(parentShell); setShellStyle(getShellStyle() | SWT.RESIZE); fMemorySpaces = memorySpaces; // We shouldn't be using this custom dialog if there are none or only // one memory spaces available. // https://bugs.eclipse.org/bugs/show_bug.cgi?id=309032#c50 assert memorySpaces != null && memorySpaces.length >= 2; } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) */ @Override protected Control createDialogArea(Composite parent) { PlatformUI.getWorkbench().getHelpSystem().setHelp( parent, IDebugUIConstants.PLUGIN_ID + ".MonitorMemoryBlockDialog_context"); //$NON-NLS-1$ // 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); gridData = new GridData(GridData.FILL_BOTH); composite.setLayoutData(gridData); parent = composite; // for all our widgets, the two-column composite is the real parent fExpressionRadio = new Button(parent, SWT.RADIO); final int radioButtonWidth = fExpressionRadio.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; fExpressionRadio.setText(Messages.AddMemBlockDlg_enterExpression); gridData = new GridData(); gridData.horizontalSpan = 2; fExpressionRadio.setLayoutData(gridData); fExpressionRadio.addSelectionListener(this); fExpressionInput = new Combo(parent, SWT.BORDER); gridData = new GridData(GridData.FILL_HORIZONTAL); gridData.horizontalSpan = 2; gridData.horizontalIndent = radioButtonWidth; fExpressionInput.setLayoutData(gridData); fAddressRadio = new Button(parent, SWT.RADIO); fAddressRadio.setText(Messages.AddMemBlockDlg_enterAddrAndMemSpace); gridData = new GridData(GridData.FILL_HORIZONTAL); gridData.horizontalSpan = 2; fAddressRadio.setLayoutData(gridData); fAddressRadio.addSelectionListener(this); fMemorySpaceInput = new Combo(parent, SWT.BORDER | SWT.READ_ONLY); gridData = new GridData(); gridData.horizontalIndent = radioButtonWidth; fMemorySpaceInput.setLayoutData(gridData); fMemorySpaceInput.addSelectionListener(this); fMemorySpaceInput.setItems(fMemorySpaces); // Try to persist the mem space selection from one invocation of the // dialog to the next String memSpaceSelection = null; if (fPreviousMemorySpaceSelection != null) { String[] items = fMemorySpaceInput.getItems(); for (String item : items) { if (item.equals(fPreviousMemorySpaceSelection)) { memSpaceSelection = fPreviousMemorySpaceSelection; } } } if (memSpaceSelection != null) { fMemorySpaceInput.setText(memSpaceSelection); } else { fMemorySpaceInput.select(0); // the n/a entry } fAddressInput = new Combo(parent, SWT.BORDER); gridData = new GridData(GridData.FILL_HORIZONTAL); GC gc = new GC(fAddressInput); FontMetrics fm = gc.getFontMetrics(); // Give enough room for a 64 bit hex address (25 is a guess at the combobox selector) gridData.minimumWidth = 18 * fm.getAverageCharWidth() + 25; gc.dispose(); fAddressInput.setLayoutData(gridData); fAddressInput.addModifyListener(this); fAddressInput.addVerifyListener(new VerifyListener() { // limit entry to hex or decimal public void verifyText(VerifyEvent e) { e.doit = false; final char c = e.character; if (Character.isDigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F') || c == 'x' || Character.isISOControl(e.character)) { e.doit = true; } } }); // add the history into the combo boxes String[] history = getHistory(sExpressionHistory); for (int i = 0; i < history.length; i++) fExpressionInput.add(history[i]); history = getHistory(sAddressHistory); for (int i = 0; i < history.length; i++) fAddressInput.add(history[i]); fExpressionInput.addModifyListener(this); if (sDefaultToExpression) { fExpressionRadio.setSelection(true); fAddressRadio.setSelection(false); fExpressionInput.setFocus(); } else { fAddressRadio.setSelection(false); fAddressRadio.setSelection(true); fAddressInput.setFocus(); } return parent; } /* (non-Javadoc) * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell) */ @Override protected void configureShell(Shell newShell) { super.configureShell(newShell); // use the same title used by the platform dialog newShell.setText(Messages.AddMemBlockDlg_MonitorMemory); } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.Dialog#okPressed() */ @Override protected void okPressed() { fExpression = fExpressionInput.getText(); fAddress = fAddressInput.getText(); fMemorySpace = fMemorySpaceInput.getText(); // add to HISTORY list; add to the platform dialog's for the expression if (fExpression.length() > 0) addHistory(sExpressionHistory, fExpression); if (fAddress.length() > 0) addHistory(sAddressHistory, fAddress); fEnteredExpression = fExpressionRadio.getSelection(); fPreviousMemorySpaceSelection = fMemorySpace; super.okPressed(); } /* (non-Javadoc) * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent) */ public void modifyText(ModifyEvent e) { // if user enters text into either the address field or the expression one, automatically // select its associated radio button (and deselect the other, these are mutually exclusive) if (e.widget == fAddressInput || e.widget == fExpressionInput) { fAddressRadio.setSelection(e.widget != fExpressionInput); fExpressionRadio.setSelection(e.widget == fExpressionInput); sDefaultToExpression = (e.widget == fExpressionInput); } } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.TrayDialog#createButtonBar(org.eclipse.swt.widgets.Composite) */ @Override protected Control createButtonBar(Composite parent) { return super.createButtonBar(parent); } public String getExpression() { return fExpression; } public String getAddress() { return fAddress; } public String getMemorySpace() { return fMemorySpace; } public boolean enteredExpression() { return fEnteredExpression; } private static void addHistory(List<String> list, String item) { if (!list.contains(item)) list.add(0, item); if (list.size() > 5) list.remove(list.size()-1); } private static String[] getHistory(List<String> list) { return list.toArray(new String[list.size()]); } /* (non-Javadoc) * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent) */ public void widgetDefaultSelected(SelectionEvent e) { // TODO Auto-generated method stub } /* (non-Javadoc) * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ public void widgetSelected(SelectionEvent e) { // if user selects a memory space, select its associated radio button (and deselect the // other, these are mutually exclusive) if (e.widget == fExpressionRadio) { fExpressionRadio.setSelection(true); fAddressRadio.setSelection(false); fExpressionInput.setFocus(); } else { fExpressionRadio.setSelection(false); fAddressRadio.setSelection(true); fAddressInput.setFocus(); } sDefaultToExpression = (e.widget == fExpressionInput); } }