blob: 6778f8cb07402f8ef21e8ecdbc289e9e9e1ecd53 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2016 Wind River Systems Inc. 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:
* Ted R Williams (Wind River Systems, Inc.) - initial implementation
* Randy Rohrbach (Wind River Systems, Inc.) - Copied and modified to create the floating point plugin
*******************************************************************************/
package org.eclipse.cdt.debug.ui.memory.floatingpoint;
import java.lang.reflect.Method;
import java.math.BigInteger;
import org.eclipse.cdt.debug.core.model.provisional.IMemoryRenderingViewportProvider;
import org.eclipse.cdt.debug.ui.memory.floatingpoint.FPutilities.Endian;
import org.eclipse.cdt.debug.ui.memory.floatingpoint.FPutilities.FPDataType;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IMemoryBlock;
import org.eclipse.debug.core.model.IMemoryBlockExtension;
import org.eclipse.debug.core.model.MemoryByte;
import org.eclipse.debug.internal.ui.DebugPluginImages;
import org.eclipse.debug.internal.ui.DebugUIMessages;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.IInternalDebugUIConstants;
import org.eclipse.debug.internal.ui.memory.IMemoryBlockConnection;
import org.eclipse.debug.internal.ui.memory.provisional.MemoryViewPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.memory.AbstractMemoryRendering;
import org.eclipse.debug.ui.memory.AbstractTableRendering;
import org.eclipse.debug.ui.memory.IMemoryRendering;
import org.eclipse.debug.ui.memory.IMemoryRenderingContainer;
import org.eclipse.debug.ui.memory.IMemoryRenderingSite;
import org.eclipse.debug.ui.memory.IRepositionableMemoryRendering;
import org.eclipse.debug.ui.memory.IResettableMemoryRendering;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.IBasicPropertyConstants;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.eclipse.ui.progress.UIJob;
/**
* A memory rendering displaying memory in a floating point memory view look and
* feel, optimized for minimal IO traffic.
* <p>
* requirements of the debug model implementation: - An IMemoryBlockExtension is
* required.
*
* Since it is not possible to size the memory block to match the size of the
* viewport, memory block change notification is not useful. Such events are
* ignored by this rendering.
*/
@SuppressWarnings({ "restriction" })
public class FPRendering extends AbstractMemoryRendering
implements IRepositionableMemoryRendering, IResettableMemoryRendering, IMemoryRenderingViewportProvider, IModelChangedListener
{
protected Rendering fRendering;
protected Action actionDisplayBigEndian;
protected Action actionDisplayLittleEndian;
protected Action actionDisplayFloatingPoint;
private IWorkbenchAdapter fWorkbenchAdapter;
private IMemoryBlockConnection fConnection;
private final static int MAX_MENU_COLUMN_COUNT = 8;
Action actionFloatingPoint32 = null;
Action actionFloatingPoint64 = null;
Action actionDisplay4Digits = null;
Action actionDisplay8Digits = null;
Action actionDisplay16Digits = null;
// Constructor
public FPRendering(String id)
{
super(id);
JFaceResources.getFontRegistry().addListener(new IPropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent event)
{
if (event.getProperty().equals(IInternalDebugUIConstants.FONT_NAME))
{
FPRendering.this.fRendering.handleFontPreferenceChange(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME));
}
}
});
this.addPropertyChangeListener(new IPropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent event)
{
IMemoryRendering sourceRendering = (IMemoryRendering) event.getSource();
if (!sourceRendering.getMemoryBlock().equals(getMemoryBlock()))
return;
Object address = event.getNewValue();
if (event.getProperty().equals(AbstractTableRendering.PROPERTY_SELECTED_ADDRESS) && address instanceof BigInteger)
{
FPRendering.this.fRendering.ensureVisible((BigInteger) address);
}
}
});
FPRenderingPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent event)
{
disposeColors();
allocateColors();
applyPreferences();
}
});
DebugUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent event)
{
if (event.getProperty().equals(IDebugUIConstants.PREF_PADDED_STR))
{
if (FPRendering.this.fRendering != null)
{
setRenderingPadding((String) event.getNewValue());
FPRendering.this.fRendering.redrawPanes();
}
}
}
});
}
static int paddingCounter = 0;
void setRenderingPadding(String padding)
{
if (padding == null || padding.length() == 0) padding = " "; //$NON-NLS-1$
FPRendering.this.fRendering.setPaddingString(padding);
}
protected void logError(String message, Exception e)
{
Status status = new Status(IStatus.ERROR, getRenderingId(), DebugException.INTERNAL_ERROR, message, e);
FPRenderingPlugin.getDefault().getLog().log(status);
}
BigInteger fBigBaseAddress; // Memory base address
private BigInteger fStartAddress; // Starting address
private BigInteger fEndAddress; // Ending address
private int fAddressableSize; // Memory block size
private int fAddressSize; // Size of address
/*
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener#modelChanged(org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta, org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy)
*/
@Override
public void modelChanged(IModelDelta delta, IModelProxy proxy)
{
/*
* The event model in the traditional renderer is written to expect a suspend first
* which will cause it to save its current data set away in an archive. Then when
* the state change comes through it will compare and refresh showing a difference.
*/
int flags = delta.getFlags();
if ( ( flags & IModelDelta.STATE ) != 0 ) {
fRendering.handleSuspend(false);
}
fRendering.handleChange();
}
/*
* We use the model proxy which is supplied by the TCF implementation to provide the knowledge of memory
* change notifications. The older backends ( the reference model, Wind River Systems Inc. ) are written
* to generate the Debug Model events. TCF follows the "ModelDelta/IModelProxy" implementation that the
* platform renderers use. So this implementation acts as a shim. If the older Debug Events come in then
* fine. If the newer model deltas come in fine also.
*/
IModelProxy fModel;
@Override
public void dispose()
{
/*
* We use the UI dispatch thread to protect the proxy information. Even though I believe the
* dispose routine is always called in the UI dispatch thread. I am going to make sure.
*/
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if ( fModel != null ) {
fModel.removeModelChangedListener(FPRendering.this);
fModel.dispose();
}
}});
if(this.fRendering != null)
this.fRendering.dispose();
disposeColors();
super.dispose();
}
@Override
public void init(final IMemoryRenderingContainer container, final IMemoryBlock block)
{
super.init(container, block);
/*
* Working with the model proxy must be done on the UI dispatch thread.
*/
final IModelProxyFactory factory = (IModelProxyFactory) DebugPlugin.getAdapter(block, IModelProxyFactory.class );
if ( factory != null ) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
/*
* The asynchronous model assumes we have an asynchronous viewer that has an IPresentationContext
* to represent it. The Platform memory subsystem provides a way to create one without a viewewr.
*/
IMemoryRenderingSite site = container.getMemoryRenderingSite();
MemoryViewPresentationContext context = new MemoryViewPresentationContext(site, container, FPRendering.this);
/*
* Get a new proxy and perform the initialization sequence so we are known the
* the model provider.
*/
fModel = factory.createModelProxy(block, context);
if (fModel != null) {
fModel.installed(null);
fModel.addModelChangedListener(FPRendering.this);
}
}});
}
try
{
fBigBaseAddress = ((IMemoryBlockExtension) block).getBigBaseAddress();
}
catch (DebugException de)
{
logError(FPRenderingMessages.getString("FPRendering.FAILURE_RETRIEVE_BASE_ADDRESS"), de); //$NON-NLS-1$
}
try
{
fAddressableSize = ((IMemoryBlockExtension) block).getAddressableSize();
}
catch (DebugException de)
{
fAddressableSize = 1;
}
try
{
fStartAddress = ((IMemoryBlockExtension) block).getMemoryBlockStartAddress();
}
catch (DebugException de)
{
fStartAddress = null;
logError(FPRenderingMessages.getString("FPRendering.FAILURE_RETRIEVE_START_ADDRESS"), de); //$NON-NLS-1$
}
try
{
fAddressSize = ((IMemoryBlockExtension) block).getAddressSize();
}
catch (DebugException e)
{
fAddressSize = 0;
}
BigInteger endAddress;
try
{
endAddress = ((IMemoryBlockExtension) block).getMemoryBlockEndAddress();
if (endAddress != null)
fEndAddress = endAddress;
}
catch (DebugException e)
{
fEndAddress = null;
}
if (fEndAddress == null)
{
int addressSize;
try
{
addressSize = ((IMemoryBlockExtension) block).getAddressSize();
}
catch (DebugException e)
{
addressSize = 4;
}
endAddress = BigInteger.valueOf(2);
endAddress = endAddress.pow(addressSize * 8);
endAddress = endAddress.subtract(BigInteger.ONE);
fEndAddress = endAddress;
}
// default to MAX_VALUE if we have trouble getting the end address
if (fEndAddress == null)
fEndAddress = BigInteger.valueOf(Integer.MAX_VALUE);
}
public BigInteger getBigBaseAddress()
{
return fBigBaseAddress;
}
public BigInteger getMemoryBlockStartAddress()
{
return fStartAddress;
}
public BigInteger getMemoryBlockEndAddress()
{
return fEndAddress;
}
public int getAddressableSize()
{
return fAddressableSize;
}
public int getAddressSize()
{
return fAddressSize;
}
@Override
public Control createControl(Composite parent)
{
allocateColors();
this.fRendering = new Rendering(parent, this);
applyPreferences();
createMenus();
if (actionFloatingPoint32.isChecked())
actionDisplay16Digits.setEnabled(false);
else
actionDisplay16Digits.setEnabled(true);
return this.fRendering;
}
/*
* We are duplicating the reference to the GoToAddress command because it is private in the platform.
* This is not going to change at this point so just live with it.
*/
private static final String ID_GO_TO_ADDRESS_COMMAND = "org.eclipse.debug.ui.command.gotoaddress"; //$NON-NLS-1$
private AbstractHandler fGoToAddressHandler;
@Override
public void activated()
{
super.activated();
IWorkbench workbench = PlatformUI.getWorkbench();
ICommandService commandSupport = workbench.getAdapter(ICommandService.class);
if (commandSupport != null)
{
Command gotoCommand = commandSupport.getCommand(ID_GO_TO_ADDRESS_COMMAND);
if (fGoToAddressHandler == null)
{
fGoToAddressHandler = new AbstractHandler()
{
@Override
public Object execute(ExecutionEvent event) throws ExecutionException
{
return null;
}
};
}
gotoCommand.setHandler(fGoToAddressHandler);
}
}
@Override
public void deactivated()
{
IWorkbench workbench = PlatformUI.getWorkbench();
ICommandService commandSupport = workbench.getAdapter(ICommandService.class);
if (commandSupport != null)
{
// remove handler
Command command = commandSupport.getCommand(ID_GO_TO_ADDRESS_COMMAND);
command.setHandler(null);
}
super.deactivated();
}
public void setSelection(BigInteger start, BigInteger end)
{
fRendering.getSelection().setStart(start, start);
fRendering.getSelection().setEnd(end, end);
}
public void gotoAddress(final BigInteger address)
{
this.fRendering.gotoAddress(address);
}
public void updateRenderingLabels()
{
UIJob job = new UIJob("updateLabels") { //$NON-NLS-1$
@SuppressWarnings("synthetic-access")
@Override
public IStatus runInUIThread(IProgressMonitor monitor)
{
// Update tab labels
String fLabel = getLabel();
firePropertyChangedEvent(new PropertyChangeEvent(FPRendering.this, IBasicPropertyConstants.P_TEXT, null, fLabel));
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.schedule();
}
private Color colorBackground;
private Color colorChanged;
private Color colorsChanged[] = null;
private Color colorEdit;
private Color colorSelection;
private Color colorText;
private Color colorTextAlternate;
public void allocateColors()
{
IPreferenceStore store = FPRenderingPlugin.getDefault().getPreferenceStore();
colorBackground = new Color(Display.getDefault(), PreferenceConverter.getColor(store, FPRenderingPreferenceConstants.MEM_COLOR_BACKGROUND));
colorChanged = new Color(Display.getDefault(), PreferenceConverter.getColor(store, FPRenderingPreferenceConstants.MEM_COLOR_CHANGED));
colorEdit = new Color(Display.getDefault(), PreferenceConverter.getColor(store, FPRenderingPreferenceConstants.MEM_COLOR_EDIT));
colorSelection = new Color(Display.getDefault(), PreferenceConverter.getColor(store, FPRenderingPreferenceConstants.MEM_COLOR_SELECTION));
colorText = new Color(Display.getDefault(), PreferenceConverter.getColor(store, FPRenderingPreferenceConstants.MEM_COLOR_TEXT));
// alternate cell color
Color textColor = getColorText();
int red = textColor.getRed();
int green = textColor.getGreen();
int blue = textColor.getBlue();
float scale = store.getInt(FPRenderingPreferenceConstants.MEM_LIGHTEN_DARKEN_ALTERNATE_CELLS);
red = (int) Math.min(red + ((255 - red) / 10) * scale, 255);
green = (int) Math.min(green + ((255 - green) / 10) * scale, 255);
blue = (int) Math.min(blue + ((255 - blue) / 10) * scale, 255);
colorTextAlternate = new Color(Display.getDefault(), new RGB(red, green, blue));
}
public void disposeColors()
{
if (colorBackground != null) colorBackground.dispose();
colorBackground = null;
if (colorChanged != null) colorChanged.dispose();
colorChanged = null;
if (colorEdit != null) colorEdit.dispose();
colorEdit = null;
if (colorSelection != null) colorSelection.dispose();
colorSelection = null;
if (colorText != null) colorText.dispose();
colorText = null;
if (colorTextAlternate != null) colorTextAlternate.dispose();
colorTextAlternate = null;
disposeChangedColors();
}
public void applyPreferences()
{
if (fRendering != null && !fRendering.isDisposed())
{
IPreferenceStore store = FPRenderingPlugin.getDefault().getPreferenceStore();
fRendering.setHistoryDepth(store.getInt(FPRenderingPreferenceConstants.MEM_HISTORY_TRAILS_COUNT));
fRendering.setBackground(getColorBackground());
FPAbstractPane panes[] = fRendering.getRenderingPanes();
for (int index = 0; index < panes.length; index++)
panes[index].setBackground(getColorBackground());
setRenderingPadding(FPRenderingPlugin.getDefault().getPreferenceStore().getString(IDebugUIConstants.PREF_PADDED_STR));
fRendering.redrawPanes();
}
}
public Color getColorBackground()
{
IPreferenceStore store = FPRenderingPlugin.getDefault().getPreferenceStore();
if (store.getBoolean(FPRenderingPreferenceConstants.MEM_USE_GLOBAL_BACKGROUND))
return Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
return colorBackground;
}
public Color getColorChanged()
{
return colorChanged;
}
private void disposeChangedColors()
{
if (colorsChanged != null)
for (int index = 0; index < colorsChanged.length; index++)
colorsChanged[index].dispose();
colorsChanged = null;
}
public Color[] getColorsChanged()
{
if (colorsChanged != null && colorsChanged.length != fRendering.getHistoryDepth())
{
disposeChangedColors();
}
if (colorsChanged == null)
{
colorsChanged = new Color[fRendering.getHistoryDepth()];
colorsChanged[0] = colorChanged;
int shades = fRendering.getHistoryDepth() + 4;
int red = (255 - colorChanged.getRed()) / shades;
int green = (255 - colorChanged.getGreen()) / shades;
int blue = (255 - colorChanged.getBlue()) / shades;
for (int index = 1; index < fRendering.getHistoryDepth(); index++)
{
colorsChanged[index] = new Color(colorChanged.getDevice(), colorChanged.getRed() + ((shades - index) * red), colorChanged.getGreen()
+ ((shades - index) * green), colorChanged.getBlue() + ((shades - index) * blue));
}
}
return colorsChanged;
}
public Color getColorEdit()
{
return colorEdit;
}
public Color getColorSelection()
{
IPreferenceStore store = FPRenderingPlugin.getDefault().getPreferenceStore();
if (store.getBoolean(FPRenderingPreferenceConstants.MEM_USE_GLOBAL_SELECTION))
return Display.getDefault().getSystemColor(SWT.COLOR_LIST_SELECTION);
return colorSelection;
}
public Color getColorText()
{
IPreferenceStore store = FPRenderingPlugin.getDefault().getPreferenceStore();
if (store.getBoolean(FPRenderingPreferenceConstants.MEM_USE_GLOBAL_TEXT))
return Display.getDefault().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
return colorText;
}
public Color getColorTextAlternate()
{
return colorTextAlternate;
}
// Menu creation
public void createMenus()
{
// Add the menu to each of the rendering panes
Control[] renderingControls = this.fRendering.getRenderingPanes();
for (int index = 0; index < renderingControls.length; index++)
super.createPopupMenu(renderingControls[index]);
super.createPopupMenu(this.fRendering);
// Copy
final Action copyAction = new CopyAction(this.fRendering);
// Copy address
final Action copyAddressAction = new Action(FPRenderingMessages.getString("FPRendering.COPY_ADDRESS")) //$NON-NLS-1$
{
@Override
public void run()
{
Display.getDefault().asyncExec(new Runnable()
{
@Override
public void run()
{
FPRendering.this.fRendering.copyAddressToClipboard();
}
});
}
};
// Reset to base address
final Action gotoBaseAddressAction = new Action(FPRenderingMessages.getString("FPRendering.RESET_TO_BASE_ADDRESS")) //$NON-NLS-1$
{
@Override
public void run()
{
Display.getDefault().asyncExec(new Runnable()
{
@Override
public void run()
{
FPRendering.this.fRendering.gotoAddress(FPRendering.this.fRendering.fBaseAddress);
}
});
}
};
// Refresh
final Action refreshAction = new Action(FPRenderingMessages.getString("FPRendering.REFRESH")) //$NON-NLS-1$
{
@Override
public void run()
{
Display.getDefault().asyncExec(new Runnable()
{
@Override
public void run()
{
// For compatibility with DSF update modes (hopefully this will either be replaced
// by an enhanced platform interface or the caching will move out of the data layer)
try
{
Method m = fRendering.getMemoryBlock().getClass().getMethod("clearCache", new Class[0]); //$NON-NLS-1$
if (m != null)
m.invoke(fRendering.getMemoryBlock(), new Object[0]);
}
catch (Exception e)
{
}
FPRendering.this.fRendering.refresh();
}
});
}
};
// Little Endian
actionDisplayLittleEndian = new Action(FPRenderingMessages.getString("FPRendering.ENDIAN_LITTLE"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
fRendering.setDisplayLittleEndian(true);
setRMCvalue(IFPRConstants.ENDIAN_KEY, Endian.LITTLE.getValue());
}
};
// Big Endian
actionDisplayBigEndian = new Action(FPRenderingMessages.getString("FPRendering.ENDIAN_BIG"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
fRendering.setDisplayLittleEndian(false);
setRMCvalue(IFPRConstants.ENDIAN_KEY, Endian.BIG.getValue());
}
};
// Endian settings
int endian = getRMCvalue(IFPRConstants.ENDIAN_KEY);
endian = endian != -1 ? endian : (fRendering.isDisplayLittleEndian() ? Endian.LITTLE.getValue() : Endian.BIG.getValue());
boolean le = (endian == Endian.LITTLE.getValue());
fRendering.setDisplayLittleEndian(le);
actionDisplayLittleEndian.setChecked(le);
actionDisplayBigEndian.setChecked(!le);
// Float
boolean dtFloat = getRMCvalue(IFPRConstants.DATATYPE_KEY) == FPDataType.FLOAT.getValue();
this.fRendering.setFPDataType(dtFloat ? FPDataType.FLOAT : FPDataType.DOUBLE);
actionFloatingPoint32 = new Action(FPRenderingMessages.getString("FPRendering.FLOATING_POINT_32"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
fRendering.setDisplayedPrecision(getRMCvalue(IFPRConstants.FLOAT_DISP_KEY));
fRendering.setFPDataType(FPDataType.FLOAT);
setRMCvalue(IFPRConstants.DATATYPE_KEY, FPDataType.FLOAT.getValue());
setSelections();
}
};
// Double
actionFloatingPoint64 = new Action(FPRenderingMessages.getString("FPRendering.FLOATING_POINT_64"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
fRendering.setDisplayedPrecision(getRMCvalue(IFPRConstants.DOUBLE_DISP_KEY));
fRendering.setFPDataType(FPDataType.DOUBLE);
setRMCvalue(IFPRConstants.DATATYPE_KEY, FPDataType.DOUBLE.getValue());
setSelections();
}
};
// Displayed precision: 4 digits
int savedPrecision = getDisplayedPrecision();
actionDisplay4Digits = new Action(FPRenderingMessages.getString("FPRendering.DISPLAYED_PRECISION_4"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
FPRendering.this.fRendering.setDisplayedPrecision(4);
setDisplayedPrecision(4);
}
};
if (savedPrecision == 4)
{
FPRendering.this.fRendering.setDisplayedPrecision(4);
actionDisplay4Digits.setChecked(savedPrecision == 4);
}
// Displayed precision: 8 digits
actionDisplay8Digits = new Action(FPRenderingMessages.getString("FPRendering.DISPLAYED_PRECISION_8"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
FPRendering.this.fRendering.setDisplayedPrecision(8);
setDisplayedPrecision(8);
}
};
if (savedPrecision == 8)
{
FPRendering.this.fRendering.setDisplayedPrecision(8);
actionDisplay8Digits.setChecked(savedPrecision == 8);
}
// Displayed precision: 16 digits (doubles only)
actionDisplay16Digits = new Action(FPRenderingMessages.getString("FPRendering.DISPLAYED_PRECISION_16"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
FPRendering.this.fRendering.setDisplayedPrecision(16);
setDisplayedPrecision(16);
}
};
if (savedPrecision == 16)
{
FPRendering.this.fRendering.setDisplayedPrecision(16);
actionDisplay16Digits.setChecked(savedPrecision == 16);
}
// Set RMC selections based on datatype and displayed precision settings in effect
setSelections();
// Columns
int savedColumnCount = getRMCvalue(IFPRConstants.COLUMN_COUNT_KEY);
final Action displayColumnCountAuto = new Action(FPRenderingMessages.getString("FPRendering.COLUMN_COUNT_AUTO"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
FPRendering.this.fRendering.setColumnsSetting(Rendering.COLUMNS_AUTO_SIZE_TO_FIT);
setRMCvalue(IFPRConstants.COLUMN_COUNT_KEY, Rendering.COLUMNS_AUTO_SIZE_TO_FIT);
}
};
boolean autoMode = savedColumnCount == Rendering.COLUMNS_AUTO_SIZE_TO_FIT;
displayColumnCountAuto.setChecked(autoMode);
final Action[] displayColumnCounts = new Action[MAX_MENU_COLUMN_COUNT];
for (int index = 0, j = 1; index < MAX_MENU_COLUMN_COUNT; index++, j *= 2)
{
final int finali = j;
displayColumnCounts[index] = new Action(FPRenderingMessages.getString("FPRendering.COLUMN_COUNT_" + finali), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
FPRendering.this.fRendering.setColumnsSetting(finali);
setRMCvalue(IFPRConstants.COLUMN_COUNT_KEY, finali);
}
};
displayColumnCounts[index].setChecked(fRendering.getColumnsSetting() == finali);
}
// Set/clear column count selections as appropriate
int countValue = getRMCvalue(IFPRConstants.COLUMN_COUNT_KEY);
for (int index = 0; index < MAX_MENU_COLUMN_COUNT; index++)
displayColumnCounts[index].setChecked(countValue != Rendering.COLUMNS_AUTO_SIZE_TO_FIT && (countValue == (1 << index)));
fRendering.setColumnsSetting(getRMCvalue(IFPRConstants.COLUMN_COUNT_KEY));
final Action displayColumnCountCustomValue = new Action("", IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
}
};
final Action displayColumnCountCustom = new Action(FPRenderingMessages.getString("FPRendering.COLUMN_COUNT_CUSTOM"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
InputDialog inputDialog = new InputDialog
(
fRendering.getShell(), "Set Column Count", "Please enter column count", "", new IInputValidator() //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
{
@Override
public String isValid(String input)
{
try
{
int index = Integer.parseInt(input);
if (index <= 0)
return "Please enter a positive integer"; //$NON-NLS-1$
if (index > 200)
return "Please enter a positive integer not greater than 200"; //$NON-NLS-1$
}
catch (NumberFormatException x)
{
return "Please enter a positive integer"; //$NON-NLS-1$
}
return null;
}
}
);
if (inputDialog.open() != Window.OK)
{
this.setChecked(false);
int currentColumnSetting = FPRendering.this.fRendering.getColumnsSetting();
if (currentColumnSetting == Rendering.COLUMNS_AUTO_SIZE_TO_FIT)
displayColumnCountAuto.setChecked(true);
else
{
boolean currentCountIsCustom = true;
for (int index = 0, j = 1; index < MAX_MENU_COLUMN_COUNT && currentCountIsCustom; index++, j *= 2)
{
currentCountIsCustom = (j != fRendering.getColumnsSetting());
if (j == fRendering.getColumnsSetting())
displayColumnCounts[index].setChecked(true);
}
if (currentCountIsCustom)
displayColumnCountCustomValue.setChecked(true);
}
return;
}
int newColumnCount = -1;
try
{
newColumnCount = Integer.parseInt(inputDialog.getValue());
}
catch (NumberFormatException x)
{
assert false;
}
boolean customIsOneOfStandardListChoices = false;
for (int index = 0, j = 1; index < MAX_MENU_COLUMN_COUNT; index++, j *= 2)
{
if (newColumnCount == j)
{
customIsOneOfStandardListChoices = true;
FPRendering.this.fRendering.setColumnsSetting(newColumnCount);
setRMCvalue(IFPRConstants.COLUMN_COUNT_KEY, newColumnCount);
this.setChecked(false);
displayColumnCountCustomValue.setChecked(false);
displayColumnCounts[index].setChecked(true);
break;
}
}
if (!customIsOneOfStandardListChoices)
{
FPRendering.this.fRendering.setColumnsSetting(newColumnCount);
setRMCvalue(IFPRConstants.COLUMN_COUNT_KEY, newColumnCount);
this.setChecked(false);
displayColumnCountCustomValue.setChecked(true);
displayColumnCountCustomValue.setText(Integer.valueOf(fRendering.getColumnsSetting()).toString());
}
}
};
// Check for a custom value: If we're not in "Auto Fill" mode, check for standard column sizes.
// If none of the standard sizes were entered, then it's a custom value; set it and display it.
boolean customColumnCountSet = true;
countValue = getRMCvalue(IFPRConstants.COLUMN_COUNT_KEY);
if (countValue != Rendering.COLUMNS_AUTO_SIZE_TO_FIT)
{
for (int index = 0; index < MAX_MENU_COLUMN_COUNT; index++)
{
// If the column count is one of the standard values, set flag to false and exit the loop
if (countValue == (1 << index))
{
customColumnCountSet = false;
break;
}
}
if (customColumnCountSet)
{
FPRendering.this.fRendering.setColumnsSetting(countValue);
displayColumnCountCustomValue.setChecked(true);
displayColumnCountCustomValue.setText(Integer.valueOf(fRendering.getColumnsSetting()).toString());
}
}
// Add the right-mouse-click (RMC) context menu items
getPopupMenuManager().addMenuListener(new IMenuListener()
{
@Override
public void menuAboutToShow(IMenuManager manager)
{
manager.add(new Separator());
MenuManager sub = new MenuManager(FPRenderingMessages.getString("FPRendering.PANES")); //$NON-NLS-1$
sub = new MenuManager(FPRenderingMessages.getString("FPRendering.ENDIAN")); //$NON-NLS-1$
sub.add(actionDisplayBigEndian);
sub.add(actionDisplayLittleEndian);
manager.add(sub);
sub = new MenuManager(FPRenderingMessages.getString("FPRendering.NUMBER_TYPE")); //$NON-NLS-1$
sub.add(actionFloatingPoint32);
sub.add(actionFloatingPoint64);
manager.add(sub);
sub = new MenuManager(FPRenderingMessages.getString("FPRendering.PRECISION")); //$NON-NLS-1$
sub.add(actionDisplay4Digits);
sub.add(actionDisplay8Digits);
sub.add(actionDisplay16Digits);
manager.add(sub);
// TODO: Add separator for FP group here: manager.add(new Separator());
sub = new MenuManager(FPRenderingMessages.getString("FPRendering.COLUMN_COUNT")); //$NON-NLS-1$
sub.add(displayColumnCountAuto);
for (int index = 0; index < displayColumnCounts.length; index++)
sub.add(displayColumnCounts[index]);
boolean currentCountIsCustom = fRendering.getColumnsSetting() != 0;
for (int index = 0, j = 1; index < MAX_MENU_COLUMN_COUNT && currentCountIsCustom; index++, j *= 2)
currentCountIsCustom = (j != fRendering.getColumnsSetting());
if (currentCountIsCustom)
sub.add(displayColumnCountCustomValue);
sub.add(displayColumnCountCustom);
manager.add(sub);
// Update modes
int updateMode = getRMCvalue(IFPRConstants.UPDATEMODE_KEY);
final Action updateAlwaysAction = new Action(FPRenderingMessages.getString("FPRendering.UPDATE_ALWAYS"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
fRendering.setUpdateMode(Rendering.UPDATE_ALWAYS);
setRMCvalue(IFPRConstants.UPDATEMODE_KEY, Rendering.UPDATE_ALWAYS);
}
};
updateAlwaysAction.setChecked(updateMode == Rendering.UPDATE_ALWAYS);
final Action updateOnBreakpointAction = new Action(FPRenderingMessages.getString("FPRendering.UPDATE_ON_BREAKPOINT"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
fRendering.setUpdateMode(Rendering.UPDATE_ON_BREAKPOINT);
setRMCvalue(IFPRConstants.UPDATEMODE_KEY, Rendering.UPDATE_ON_BREAKPOINT);
}
};
updateOnBreakpointAction.setChecked(updateMode == Rendering.UPDATE_ON_BREAKPOINT);
final Action updateManualAction = new Action(FPRenderingMessages.getString("FPRendering.UPDATE_MANUAL"), IAction.AS_RADIO_BUTTON) //$NON-NLS-1$
{
@Override
public void run()
{
fRendering.setUpdateMode(Rendering.UPDATE_MANUAL);
setRMCvalue(IFPRConstants.UPDATEMODE_KEY, Rendering.UPDATE_MANUAL);
}
};
updateManualAction.setChecked(updateMode == Rendering.UPDATE_MANUAL);
// Add menu
sub = new MenuManager(FPRenderingMessages.getString("FPRendering.UPDATEMODE")); //$NON-NLS-1$
sub.add(updateAlwaysAction);
sub.add(updateOnBreakpointAction);
sub.add(updateManualAction);
manager.add(sub);
manager.add(new Separator());
BigInteger start = fRendering.getSelection().getStart();
BigInteger end = fRendering.getSelection().getEnd();
copyAction.setEnabled(start != null && end != null);
manager.add(copyAction);
manager.add(copyAddressAction);
manager.add(gotoBaseAddressAction);
manager.add(refreshAction);
manager.add(new Separator());
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
});
}
// Set/clear selections as appropriate
public void setSelections()
{
if (actionDisplay4Digits == null || actionDisplay8Digits == null || actionDisplay16Digits == null) return;
// Enable/disable and set/clear menu RMC elements based on currently selected datatype
boolean dtFloat = FPRendering.this.fRendering.getFPDataType() == FPDataType.FLOAT;
actionDisplay16Digits.setEnabled(!dtFloat);
actionFloatingPoint32.setChecked(dtFloat);
actionFloatingPoint64.setChecked(!dtFloat);
// Set/clear RMC elements based on displayed precision
int displayedPrecision = getRMCvalue(dtFloat ? IFPRConstants.FLOAT_DISP_KEY : IFPRConstants.DOUBLE_DISP_KEY);
actionDisplay4Digits.setChecked (displayedPrecision == 4);
actionDisplay8Digits.setChecked (displayedPrecision == 8);
actionDisplay16Digits.setChecked(displayedPrecision == 16);
}
@Override
public Control getControl()
{
return this.fRendering;
}
// Selection is terminology for caret position
@Override
public BigInteger getSelectedAddress()
{
FPIMemorySelection selection = fRendering.getSelection();
if (selection == null || selection.getStart() == null)
return fRendering.getCaretAddress();
return selection.getStartLow();
}
@Override
public MemoryByte[] getSelectedAsBytes()
{
try
{
// default to the caret address and the cell count size
BigInteger startAddr = fRendering.getCaretAddress();
int byteCount = fRendering.getCharsPerColumn();
// Now see if there's a selection
FPIMemorySelection selection = fRendering.getSelection();
if (selection != null && selection.getStart() != null)
{
// The implementation is such that just having a caret somewhere (without multiple cells
// being selected) constitutes a selection, except for when the rendering is in its initial
// state, i.e. just because we get here doesn't mean the user has selected more than one cell.
startAddr = getSelectedAddress();
if (selection.getHigh() != null)
byteCount = selection.getHigh().subtract(selection.getLow()).intValue() * fRendering.getAddressableSize();
}
return fRendering.getViewportCache().getBytes(startAddr, byteCount);
}
catch (DebugException de)
{
return new MemoryByte[0];
}
}
@Override
public void goToAddress(final BigInteger address) throws DebugException
{
Display.getDefault().asyncExec(new Runnable()
{
@Override
public void run()
{
fRendering.gotoAddress(address);
}
});
}
protected void setTargetMemoryLittleEndian(boolean littleEndian)
{
// Once we actually read memory we can determine the endianess and need to set these actions accordingly.
actionDisplayBigEndian.setChecked(!littleEndian);
actionDisplayLittleEndian.setChecked(littleEndian);
// When target endian changes, force display endian to track. User can then change display endian if desired.
fRendering.setDisplayLittleEndian(littleEndian);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.PlatformObject#getAdapter(java.lang.Class)
*/
@Override
public Object getAdapter(Class adapter)
{
if (adapter == IWorkbenchAdapter.class)
{
if (this.fWorkbenchAdapter == null)
{
this.fWorkbenchAdapter = new IWorkbenchAdapter()
{
@Override
public Object[] getChildren(Object o)
{
return new Object[0];
}
@Override
public ImageDescriptor getImageDescriptor(Object object)
{
return null;
}
@Override
public String getLabel(Object o)
{
return FPRenderingMessages.getString("FPRendering.RENDERING_NAME"); //$NON-NLS-1$
}
@Override
public Object getParent(Object o)
{
return null;
}
};
}
return this.fWorkbenchAdapter;
}
if (adapter == IMemoryBlockConnection.class)
{
if (fConnection == null)
{
fConnection = new IMemoryBlockConnection()
{
@Override
public void update()
{
// update UI asynchronously
Display display = FPRenderingPlugin.getDefault().getWorkbench().getDisplay();
display.asyncExec(new Runnable()
{
@Override
public void run()
{
try
{
if (fBigBaseAddress != FPRendering.this.fRendering.getMemoryBlock().getBigBaseAddress())
{
fBigBaseAddress = FPRendering.this.fRendering.getMemoryBlock().getBigBaseAddress();
FPRendering.this.fRendering.gotoAddress(fBigBaseAddress);
}
FPRendering.this.fRendering.refresh();
}
catch (DebugException e)
{
}
}
});
}
};
}
return fConnection;
}
return super.getAdapter(adapter);
}
@Override
public void resetRendering() throws DebugException
{
fRendering.gotoAddress(fRendering.fBaseAddress);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.cdt.debug.internal.core.model.provisional.
* IMemoryRenderingViewportProvider#getViewportAddress()
*/
@Override
public BigInteger getViewportAddress()
{
return fRendering.getViewportStartAddress();
}
// Persistence methods
void setDisplayedPrecision(int precision)
{
// Save the appropriate displayed precision value, based on data type, in the store
if (FPRendering.this.fRendering.getFPDataType() == FPDataType.FLOAT)
setRMCvalue(IFPRConstants.FLOAT_DISP_KEY, precision);
else
setRMCvalue(IFPRConstants.DOUBLE_DISP_KEY, precision);
}
private int getDisplayedPrecision()
{
// Retrieve the persisted data value from the store
IPreferenceStore store = FPRenderingPlugin.getDefault().getPreferenceStore();
FPDataType dataType = FPRendering.this.fRendering.getFPDataType();
if (store != null)
return store.getInt(dataType == FPDataType.FLOAT ? IFPRConstants.FLOAT_DISP_KEY : IFPRConstants.DOUBLE_DISP_KEY);
// If there's nothing persisted, return the default precision for data type
return dataType == FPDataType.FLOAT ? FPDataType.FLOAT.getDisplayedPrecision() : FPDataType.DOUBLE.getDisplayedPrecision();
}
void setRMCvalue(String key, int value)
{
// Save the specified key and int value
IPreferenceStore store = FPRenderingPlugin.getDefault().getPreferenceStore();
if (store != null) store.setValue(key, value);
}
int getRMCvalue(String key)
{
// Return the value for the specified key
IPreferenceStore store = FPRenderingPlugin.getDefault().getPreferenceStore();
return (store != null) ? store.getInt(key) : 0;
}
}
// Copy class
class CopyAction extends Action
{
// TODO for the sake of large copies, this action should probably read in
// blocks on a Job.
private Rendering fRendering;
private int fType = DND.CLIPBOARD;
public CopyAction(Rendering rendering)
{
this(rendering, DND.CLIPBOARD);
}
@SuppressWarnings("restriction") // using platform's labels and images; acceptable build risk
public CopyAction(Rendering rendering, int clipboardType)
{
super();
fType = clipboardType;
fRendering = rendering;
setText(DebugUIMessages.CopyViewToClipboardAction_title);
setToolTipText(DebugUIMessages.CopyViewToClipboardAction_tooltip);
setImageDescriptor(DebugPluginImages
.getImageDescriptor(IInternalDebugUIConstants.IMG_ELCL_COPY_VIEW_TO_CLIPBOARD));
setHoverImageDescriptor(DebugPluginImages
.getImageDescriptor(IInternalDebugUIConstants.IMG_LCL_COPY_VIEW_TO_CLIPBOARD));
setDisabledImageDescriptor(DebugPluginImages
.getImageDescriptor(IInternalDebugUIConstants.IMG_DLCL_COPY_VIEW_TO_CLIPBOARD));
}
@Override
public void run()
{
Clipboard clip = null;
final String PANE_SPACING = " "; //$NON-NLS-1$
try
{
clip = new Clipboard(fRendering.getDisplay());
BigInteger start = fRendering.getSelection().getStart();
BigInteger end = fRendering.getSelection().getEnd();
if (end == null) return; // End will be null when there is nothing selected
if (start.compareTo(end) > 0)
{
// Swap start and end
BigInteger bigI = end;
end = start;
start = bigI;
}
final FPDataType numberType = fRendering.getFPDataType();
final int bytesPerColumn = numberType.getByteLength();
final boolean isLittleEndian = fRendering.isTargetLittleEndian();
final int columns = fRendering.getColumnCount();
BigInteger lengthToRead = end.subtract(start);
int rows = lengthToRead.divide(BigInteger.valueOf(columns * (fRendering.getFPDataType().getByteLength()))).intValue();
if (rows * columns * bytesPerColumn < lengthToRead.intValue()) rows++;
StringBuilder buffer = new StringBuilder();
for (int row = 0; row < rows; row++)
{
BigInteger rowAddress = start.add(BigInteger.valueOf(row * columns * bytesPerColumn));
if (fRendering.getPaneVisible(Rendering.PANE_ADDRESS))
{
buffer.append(fRendering.getAddressString(rowAddress));
buffer.append(PANE_SPACING);
}
if (fRendering.getPaneVisible(Rendering.PANE_DATA))
{
for(int col = 0; col < columns; col++)
{
BigInteger cellAddress = rowAddress.add(BigInteger.valueOf(col * bytesPerColumn));
if (cellAddress.compareTo(end) < 0)
{
try
{
FPMemoryByte bytes[] = fRendering.getBytes(cellAddress, bytesPerColumn);
buffer.append(fRendering.sciNotationString(bytes, numberType, isLittleEndian));
}
catch(DebugException de)
{
fRendering
.logError(
FPRenderingMessages
.getString("FPRendering.FAILURE_COPY_OPERATION"), de); //$NON-NLS-1$
return;
}
}
else
{
for(int i = fRendering.getCharsPerColumn(); i > 0; i--)
buffer.append(' ');
}
if (col != columns - 1)
buffer.append(' ');
}
}
if (fRendering.getPaneVisible(Rendering.PANE_DATA))
buffer.append(PANE_SPACING);
buffer.append("\n"); //$NON-NLS-1$
}
if (buffer.length() > 0)
{
TextTransfer plainTextTransfer = TextTransfer.getInstance();
clip.setContents(new Object[] { buffer.toString() }, new Transfer[] { plainTextTransfer }, fType);
}
}
finally
{
if (clip != null)
{
clip.dispose();
}
}
}
}