Merge remote-tracking branch 'origin/GP-3049_ghidragon_fixing_improving_register_manager--SQUASHED'
@@ -24,6 +24,7 @@ import javax.swing.*;
|
|||||||
import javax.swing.table.TableCellEditor;
|
import javax.swing.table.TableCellEditor;
|
||||||
|
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
|
|
||||||
public class HexBigIntegerTableCellEditor extends AbstractCellEditor implements TableCellEditor {
|
public class HexBigIntegerTableCellEditor extends AbstractCellEditor implements TableCellEditor {
|
||||||
protected IntegerTextField input;
|
protected IntegerTextField input;
|
||||||
@@ -48,11 +49,11 @@ public class HexBigIntegerTableCellEditor extends AbstractCellEditor implements
|
|||||||
int row, int column) {
|
int row, int column) {
|
||||||
input = new IntegerTextField();
|
input = new IntegerTextField();
|
||||||
input.getComponent().setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
|
input.getComponent().setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
|
||||||
input.setAllowNegativeValues(true);
|
input.setMinValue(null); // allow negative numbers
|
||||||
input.setHexMode();
|
input.setFormat(IntegerFormat.HEX);
|
||||||
input.setAllowsHexPrefix(false);
|
input.setUseNumberPrefix(false);
|
||||||
input.setShowNumberMode(true);
|
input.setShowNumberMode(true);
|
||||||
input.setHorizontalAlignment(JTextField.RIGHT);
|
input.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||||
|
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
input.setValue((BigInteger) value);
|
input.setValue((BigInteger) value);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package ghidra.machinelearning.functionfinding;
|
package ghidra.machinelearning.functionfinding;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.LongStream;
|
import java.util.stream.LongStream;
|
||||||
@@ -438,7 +439,7 @@ public class FunctionStartRFParamsDialog extends ReusableDialogComponentProvider
|
|||||||
minUndefRangeLabel.setToolTipText(MIN_UNDEFINED_RANGE_SIZE_TIP);
|
minUndefRangeLabel.setToolTipText(MIN_UNDEFINED_RANGE_SIZE_TIP);
|
||||||
funcDataPanel.add(minUndefRangeLabel);
|
funcDataPanel.add(minUndefRangeLabel);
|
||||||
minUndefRangeField = new IntegerTextField();
|
minUndefRangeField = new IntegerTextField();
|
||||||
minUndefRangeField.setAllowNegativeValues(false);
|
minUndefRangeField.setMinValue(BigInteger.ZERO);
|
||||||
minUndefRangeField.setValue(plugin.getMinUndefinedRangeSize());
|
minUndefRangeField.setValue(plugin.getMinUndefinedRangeSize());
|
||||||
funcDataPanel.add(minUndefRangeField.getComponent());
|
funcDataPanel.add(minUndefRangeField.getComponent());
|
||||||
|
|
||||||
|
|||||||
@@ -127,8 +127,7 @@ public class BSimSearchDialog extends AbstractBSimSearchDialog {
|
|||||||
maxResultsField = new IntegerTextField(10);
|
maxResultsField = new IntegerTextField(10);
|
||||||
maxResultsField.setValue(100);
|
maxResultsField.setValue(100);
|
||||||
maxResultsField.setMinValue(BigInteger.ONE);
|
maxResultsField.setMinValue(BigInteger.ONE);
|
||||||
maxResultsField.setAllowNegativeValues(false);
|
maxResultsField.setUseNumberPrefix(false);
|
||||||
maxResultsField.setAllowsHexPrefix(false);
|
|
||||||
maxResultsField.setShowNumberMode(false);
|
maxResultsField.setShowNumberMode(false);
|
||||||
|
|
||||||
JComponent maxResultsComponent = maxResultsField.getComponent();
|
JComponent maxResultsComponent = maxResultsField.getComponent();
|
||||||
|
|||||||
@@ -71,24 +71,27 @@
|
|||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<H3><A name="tool_buttons"></A>Tool Buttons</H3>
|
<H3><A name="tool_buttons"></A>Toolbar Buttons</H3>
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<BLOCKQUOTE>
|
<P><IMG src="images/locationIn.gif"> Toggles whether or not to select the row in the
|
||||||
<P><IMG src="images/locationIn.gif">Toggles whether or not to select the row in the
|
|
||||||
currently selected register value table whose address range contains the current address
|
currently selected register value table whose address range contains the current address
|
||||||
of the cursor in the listing view. For example, in the Register Manager image show above,
|
of the cursor in the listing view. For example, in the Register Manager image show above,
|
||||||
if the user clicks on any address between 804c12 and 804c24, then the first row of the
|
if the user clicks on any address between 804c12 and 804c24, then the first row of the
|
||||||
table will be selected if this action toggle is on.</P>
|
table will be selected if this action toggle is on.</P>
|
||||||
|
|
||||||
<P><IMG src="images/edit-delete.png">Deletes the register value associations for all the
|
<P><IMG src="Icons.ADD_ICON"> Brings up the <I>Add Register Value Range</I> dialog for setting a
|
||||||
|
value over an address range for the selected register. See the
|
||||||
|
<A href="">Edit Address Range</A> section below for dialog details as this action shares the
|
||||||
|
same dialog as the edit range action.</P>
|
||||||
|
|
||||||
|
<P><IMG src="images/edit-delete.png"> Deletes the register value associations for all the
|
||||||
selected ranges in the table.</P>
|
selected ranges in the table.</P>
|
||||||
|
|
||||||
<P><IMG src="Icons.MAKE_SELECTION_ICON">Creates a selection in the browser for all the address
|
<P><IMG src="Icons.MAKE_SELECTION_ICON"> Creates a selection in the browser for all the address
|
||||||
ranges selected in the register values table.</P>
|
ranges selected in the register values table.</P>
|
||||||
|
|
||||||
<P><IMG src="images/view-filter.png">Filters out all registers in the register tree that
|
<P><IMG src="images/view-filter.png"> Filters out all registers in the register tree that
|
||||||
don't have any associated values (default or otherwise).</P>
|
don't have any associated values (default or otherwise).</P>
|
||||||
|
|
||||||
<H3><BR>
|
<H3><BR>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.2 KiB |
@@ -33,6 +33,7 @@ import docking.widgets.combobox.GhidraComboBox;
|
|||||||
import docking.widgets.dialogs.StringChoices;
|
import docking.widgets.dialogs.StringChoices;
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.*;
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
import ghidra.docking.settings.*;
|
import ghidra.docking.settings.*;
|
||||||
import ghidra.framework.preferences.Preferences;
|
import ghidra.framework.preferences.Preferences;
|
||||||
import ghidra.util.BigEndianDataConverter;
|
import ghidra.util.BigEndianDataConverter;
|
||||||
@@ -783,7 +784,8 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateHexMode() {
|
private void updateHexMode() {
|
||||||
intHexModeMap.put(rowobject.definition.getName(), intTextField.isHexMode());
|
intHexModeMap.put(rowobject.definition.getName(),
|
||||||
|
intTextField.getFormat() == IntegerFormat.HEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Number getNumber() {
|
private Number getNumber() {
|
||||||
@@ -854,14 +856,14 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
|
|||||||
mode = NUMBER;
|
mode = NUMBER;
|
||||||
NumberSettingsDefinition def = (NumberSettingsDefinition) rowobject.definition;
|
NumberSettingsDefinition def = (NumberSettingsDefinition) rowobject.definition;
|
||||||
if (def.isHexModePreferred() || isHexModeEnabled(def)) {
|
if (def.isHexModePreferred() || isHexModeEnabled(def)) {
|
||||||
intTextField.setHexMode();
|
intTextField.setFormat(IntegerFormat.HEX);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
intTextField.setDecimalMode();
|
intTextField.setFormat(IntegerFormat.DEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
intTextField.setMaxValue(def.getMaxValue());
|
intTextField.setMaxValue(def.getMaxValue());
|
||||||
intTextField.setAllowNegativeValues(def.allowNegativeValue());
|
intTextField.setMinValue(def.allowNegativeValue() ? null : BigInteger.ZERO);
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
intTextField.setValue(null);
|
intTextField.setValue(null);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import docking.widgets.DropDownSelectionTextField;
|
import docking.widgets.DropDownSelectionTextField;
|
||||||
import docking.widgets.table.FocusableEditor;
|
import docking.widgets.table.FocusableEditor;
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||||
import ghidra.app.util.AddressInput;
|
import ghidra.app.util.AddressInput;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
@@ -163,7 +164,7 @@ class VarnodeLocationCellEditor extends AbstractCellEditor
|
|||||||
|
|
||||||
private Component createStackOffsetEditor(VarnodeInfo varnode) {
|
private Component createStackOffsetEditor(VarnodeInfo varnode) {
|
||||||
offsetInput = new IntegerTextField();
|
offsetInput = new IntegerTextField();
|
||||||
offsetInput.setHexMode();
|
offsetInput.setFormat(IntegerFormat.HEX);
|
||||||
Address address = varnode.getAddress();
|
Address address = varnode.getAddress();
|
||||||
if (address != null) {
|
if (address != null) {
|
||||||
offsetInput.setValue(address.getOffset());
|
offsetInput.setValue(address.getOffset());
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import javax.swing.*;
|
|||||||
import javax.swing.table.TableCellEditor;
|
import javax.swing.table.TableCellEditor;
|
||||||
|
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||||
|
|
||||||
class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEditor {
|
class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEditor {
|
||||||
@@ -56,8 +57,8 @@ class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEdito
|
|||||||
int row, int column) {
|
int row, int column) {
|
||||||
|
|
||||||
input = new IntegerTextField();
|
input = new IntegerTextField();
|
||||||
input.setAllowNegativeValues(false);
|
input.setMinValue(BigInteger.ZERO);
|
||||||
input.setDecimalMode();
|
input.setFormat(IntegerFormat.DEC);
|
||||||
Integer size = (Integer) value;
|
Integer size = (Integer) value;
|
||||||
if (size != null) {
|
if (size != null) {
|
||||||
input.setValue(size.longValue());
|
input.setValue(size.longValue());
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package ghidra.app.plugin.core.memory;
|
package ghidra.app.plugin.core.memory;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@@ -28,6 +29,7 @@ import docking.widgets.combobox.GhidraComboBox;
|
|||||||
import docking.widgets.label.GDLabel;
|
import docking.widgets.label.GDLabel;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
import ghidra.app.plugin.core.memory.AddBlockModel.InitializedType;
|
import ghidra.app.plugin.core.memory.AddBlockModel.InitializedType;
|
||||||
import ghidra.app.plugin.core.misc.RegisterField;
|
import ghidra.app.plugin.core.misc.RegisterField;
|
||||||
import ghidra.app.util.AddressInput;
|
import ghidra.app.util.AddressInput;
|
||||||
@@ -474,16 +476,16 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||||||
JPanel schemePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
JPanel schemePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||||
|
|
||||||
schemeDestByteCountField = new IntegerTextField(4, 1);
|
schemeDestByteCountField = new IntegerTextField(4, 1);
|
||||||
schemeDestByteCountField.setAllowNegativeValues(false);
|
schemeDestByteCountField.setMinValue(BigInteger.ZERO);
|
||||||
schemeDestByteCountField.setAllowsHexPrefix(false);
|
schemeDestByteCountField.setUseNumberPrefix(false);
|
||||||
schemeDestByteCountField.setDecimalMode();
|
schemeDestByteCountField.setFormat(IntegerFormat.DEC);
|
||||||
schemeDestByteCountField.addChangeListener(ev -> schemeDestByteCountChanged());
|
schemeDestByteCountField.addChangeListener(ev -> schemeDestByteCountChanged());
|
||||||
schemeDestByteCountField.setAccessibleName("Mapping Ratio: Destination Size");
|
schemeDestByteCountField.setAccessibleName("Mapping Ratio: Destination Size");
|
||||||
|
|
||||||
schemeSrcByteCountField = new IntegerTextField(4, 1);
|
schemeSrcByteCountField = new IntegerTextField(4, 1);
|
||||||
schemeSrcByteCountField.setAllowNegativeValues(false);
|
schemeSrcByteCountField.setMinValue(BigInteger.ZERO);
|
||||||
schemeSrcByteCountField.setAllowsHexPrefix(false);
|
schemeSrcByteCountField.setUseNumberPrefix(false);
|
||||||
schemeSrcByteCountField.setDecimalMode();
|
schemeSrcByteCountField.setFormat(IntegerFormat.DEC);
|
||||||
schemeSrcByteCountField.addChangeListener(ev -> schemeSrcByteCountChanged());
|
schemeSrcByteCountField.addChangeListener(ev -> schemeSrcByteCountChanged());
|
||||||
schemeSrcByteCountField.setAccessibleName("Mapping Ratio: Source Size");
|
schemeSrcByteCountField.setAccessibleName("Mapping Ratio: Source Size");
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import javax.swing.*;
|
|||||||
|
|
||||||
import docking.DialogComponentProvider;
|
import docking.DialogComponentProvider;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
|
import docking.widgets.textfield.FixedSizeIntegerTextField;
|
||||||
import ghidra.app.util.AddressInput;
|
import ghidra.app.util.AddressInput;
|
||||||
import ghidra.app.util.bean.FixedBitSizeValueField;
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSpace;
|
import ghidra.program.model.address.AddressSpace;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
@@ -36,7 +36,7 @@ class EditRegisterValueDialog extends DialogComponentProvider {
|
|||||||
|
|
||||||
private AddressInput startAddrField;
|
private AddressInput startAddrField;
|
||||||
private AddressInput endAddrField;
|
private AddressInput endAddrField;
|
||||||
private FixedBitSizeValueField registerValueField;
|
private FixedSizeIntegerTextField registerValueField;
|
||||||
private boolean wasCancelled = true;
|
private boolean wasCancelled = true;
|
||||||
|
|
||||||
EditRegisterValueDialog(Register register, Address start, Address end, BigInteger value,
|
EditRegisterValueDialog(Register register, Address start, Address end, BigInteger value,
|
||||||
@@ -61,10 +61,16 @@ class EditRegisterValueDialog extends DialogComponentProvider {
|
|||||||
startAddrField = new AddressInput(program, addressChangeListener);
|
startAddrField = new AddressInput(program, addressChangeListener);
|
||||||
endAddrField = new AddressInput(program, addressChangeListener);
|
endAddrField = new AddressInput(program, addressChangeListener);
|
||||||
|
|
||||||
registerValueField = new FixedBitSizeValueField(register.getBitLength(), true, false);
|
registerValueField = new FixedSizeIntegerTextField(16, register.getBitLength());
|
||||||
registerValueField.getAccessibleContext().setAccessibleName("Register Value");
|
registerValueField.getComponent()
|
||||||
|
.getAccessibleContext()
|
||||||
|
.setAccessibleName("Register Value");
|
||||||
|
if (start != null) {
|
||||||
startAddrField.setAddress(start);
|
startAddrField.setAddress(start);
|
||||||
|
}
|
||||||
|
if (end != null) {
|
||||||
endAddrField.setAddress(end);
|
endAddrField.setAddress(end);
|
||||||
|
}
|
||||||
registerValueField.setValue(value);
|
registerValueField.setValue(value);
|
||||||
|
|
||||||
JPanel panel = new JPanel(new PairLayout(5, 1));
|
JPanel panel = new JPanel(new PairLayout(5, 1));
|
||||||
@@ -77,7 +83,7 @@ class EditRegisterValueDialog extends DialogComponentProvider {
|
|||||||
panel.add(new GLabel("End Address:"));
|
panel.add(new GLabel("End Address:"));
|
||||||
panel.add(endAddrField);
|
panel.add(endAddrField);
|
||||||
panel.add(new GLabel("Value:"));
|
panel.add(new GLabel("Value:"));
|
||||||
panel.add(registerValueField);
|
panel.add(registerValueField.getComponent());
|
||||||
panel.getAccessibleContext().setAccessibleName("Edit Register Value");
|
panel.getAccessibleContext().setAccessibleName("Edit Register Value");
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,19 @@ import static ghidra.framework.model.DomainObjectEvent.*;
|
|||||||
import static ghidra.program.util.ProgramEvent.*;
|
import static ghidra.program.util.ProgramEvent.*;
|
||||||
|
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.WindowPosition;
|
import docking.WindowPosition;
|
||||||
import docking.action.*;
|
import docking.action.ToggleDockingAction;
|
||||||
|
import docking.action.builder.ActionBuilder;
|
||||||
|
import docking.action.builder.ToggleActionBuilder;
|
||||||
import generic.theme.GIcon;
|
import generic.theme.GIcon;
|
||||||
|
import ghidra.app.cmd.register.SetRegisterCmd;
|
||||||
import ghidra.app.context.ProgramActionContext;
|
import ghidra.app.context.ProgramActionContext;
|
||||||
|
import ghidra.framework.cmd.Command;
|
||||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||||
import ghidra.framework.model.DomainObjectListener;
|
import ghidra.framework.model.DomainObjectListener;
|
||||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||||
@@ -52,16 +57,13 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
|||||||
private RegisterTree tree;
|
private RegisterTree tree;
|
||||||
private RegisterValuesPanel values;
|
private RegisterValuesPanel values;
|
||||||
|
|
||||||
private DockingAction deleteRegisterValuesAction;
|
private ToggleDockingAction showDefaultValuesAction;
|
||||||
|
|
||||||
private DockingAction selectRegisterValuesAction;
|
|
||||||
private ToggleDockingAction showDefaultRegisterValuesAction;
|
|
||||||
private DomainObjectListener domainObjectListener;
|
private DomainObjectListener domainObjectListener;
|
||||||
private ToggleDockingAction filterRegistersAction;
|
private ToggleDockingAction filterRegistersAction;
|
||||||
private SwingUpdateManager updateMgr;
|
private SwingUpdateManager updateMgr;
|
||||||
|
|
||||||
private ToggleDockingAction followLocationToggleAction;
|
private boolean followLocation = false;
|
||||||
protected boolean followLocation;
|
private Address currentAddress;
|
||||||
|
|
||||||
RegisterManagerProvider(PluginTool tool, String owner) {
|
RegisterManagerProvider(PluginTool tool, String owner) {
|
||||||
super(tool, "Register Manager", owner, ProgramActionContext.class);
|
super(tool, "Register Manager", owner, ProgramActionContext.class);
|
||||||
@@ -86,9 +88,7 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
|||||||
|
|
||||||
tree.addGTreeSelectionListener(e -> showRegister());
|
tree.addGTreeSelectionListener(e -> showRegister());
|
||||||
values.getTable().getSelectionModel().addListSelectionListener(e -> {
|
values.getTable().getSelectionModel().addListSelectionListener(e -> {
|
||||||
JTable table = values.getTable();
|
contextChanged();
|
||||||
deleteRegisterValuesAction.setEnabled(table.getSelectedRowCount() > 0);
|
|
||||||
selectRegisterValuesAction.setEnabled(table.getSelectedRowCount() > 0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tree.setAccessibleNamePrefix("Register Manager");
|
tree.setAccessibleNamePrefix("Register Manager");
|
||||||
@@ -100,80 +100,91 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
|||||||
|
|
||||||
void createActions() {
|
void createActions() {
|
||||||
HelpLocation helpLocation = new HelpLocation("RegisterPlugin", "tool_buttons");
|
HelpLocation helpLocation = new HelpLocation("RegisterPlugin", "tool_buttons");
|
||||||
deleteRegisterValuesAction = new DockingAction("Delete Register Value Ranges", getOwner()) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionContext context) {
|
|
||||||
values.deleteSelectedRanges();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
deleteRegisterValuesAction.setEnabled(false);
|
|
||||||
deleteRegisterValuesAction.setToolBarData(new ToolBarData(DELETE_REGISTER_VALUES_ICON));
|
|
||||||
deleteRegisterValuesAction
|
|
||||||
.setPopupMenuData(new MenuData(new String[] { "Delete Register Value Ranges" }));
|
|
||||||
deleteRegisterValuesAction.setDescription("Delete Register Value Ranges");
|
|
||||||
deleteRegisterValuesAction.setHelpLocation(helpLocation);
|
|
||||||
tool.addLocalAction(this, deleteRegisterValuesAction);
|
|
||||||
|
|
||||||
selectRegisterValuesAction = new DockingAction("Select Register Value Ranges", getOwner()) {
|
new ActionBuilder("Add Register Value Range", getOwner())
|
||||||
@Override
|
.toolBarIcon(Icons.ADD_ICON)
|
||||||
public void actionPerformed(ActionContext context) {
|
.popupMenuPath("Add Value Range")
|
||||||
values.selectedRanges();
|
.popupMenuIcon(Icons.ADD_ICON)
|
||||||
}
|
.description("Add a new register value range")
|
||||||
};
|
.helpLocation(helpLocation)
|
||||||
selectRegisterValuesAction.setEnabled(false);
|
.withContext(RegisterManagerContext.class)
|
||||||
selectRegisterValuesAction.setToolBarData(new ToolBarData(SELECT_REGISTER_VALUES_ICON));
|
.enabledWhen(c -> c.getSelectedRegister() != null)
|
||||||
selectRegisterValuesAction
|
.onAction(c -> addRange(c.getSelectedRegister()))
|
||||||
.setPopupMenuData(new MenuData(new String[] { "Select Register Value Ranges" }));
|
.buildAndInstallLocal(this);
|
||||||
selectRegisterValuesAction.setDescription("Select Register Value Ranges");
|
|
||||||
selectRegisterValuesAction.setHelpLocation(helpLocation);
|
|
||||||
tool.addLocalAction(this, selectRegisterValuesAction);
|
|
||||||
|
|
||||||
showDefaultRegisterValuesAction =
|
new ActionBuilder("Delete Register Value Ranges", getOwner())
|
||||||
new ToggleDockingAction("Show default register values", getOwner()) {
|
.toolBarIcon(DELETE_REGISTER_VALUES_ICON)
|
||||||
@Override
|
.popupMenuPath("Delete Register Value Ranges")
|
||||||
public void actionPerformed(ActionContext context) {
|
.popupMenuIcon(DELETE_REGISTER_VALUES_ICON)
|
||||||
values.setShowDefaultValues(showDefaultRegisterValuesAction.isSelected());
|
.description("Delete selected register value ranges")
|
||||||
}
|
.helpLocation(helpLocation)
|
||||||
};
|
.withContext(RegisterManagerContext.class)
|
||||||
showDefaultRegisterValuesAction.setSelected(false);
|
.enabledWhen(c -> c.hasSelectedRegisterValueRanges())
|
||||||
showDefaultRegisterValuesAction
|
.onAction(c -> values.deleteSelectedRanges())
|
||||||
.setDescription("Toggles showing of default register values");
|
.buildAndInstallLocal(this);
|
||||||
showDefaultRegisterValuesAction
|
|
||||||
.setMenuBarData(new MenuData(new String[] { "Show Default Values" }));
|
|
||||||
showDefaultRegisterValuesAction
|
|
||||||
.setHelpLocation(new HelpLocation("RegisterPlugin", "menu_actions"));
|
|
||||||
tool.addLocalAction(this, showDefaultRegisterValuesAction);
|
|
||||||
|
|
||||||
filterRegistersAction = new ToggleDockingAction("Filter Registers", getOwner()) {
|
new ActionBuilder("Select Register Value Ranges", getOwner())
|
||||||
@Override
|
.toolBarIcon(SELECT_REGISTER_VALUES_ICON)
|
||||||
public void actionPerformed(ActionContext context) {
|
.popupMenuPath("Select Register Value Ranges")
|
||||||
tree.setFiltered(filterRegistersAction.isSelected());
|
.popupMenuIcon(SELECT_REGISTER_VALUES_ICON)
|
||||||
}
|
.helpLocation(helpLocation)
|
||||||
};
|
.description("Create a program selection from the selected rows")
|
||||||
filterRegistersAction.setSelected(false);
|
.withContext(RegisterManagerContext.class)
|
||||||
filterRegistersAction.setDescription(
|
.enabledWhen(c -> c.hasSelectedRegisterValueRanges())
|
||||||
"Toggles filtering out registers that don't have values or default values.");
|
.onAction(c -> values.selectRanges())
|
||||||
filterRegistersAction.setToolBarData(new ToolBarData(FILTER_ICON));
|
.buildAndInstallLocal(this);
|
||||||
filterRegistersAction.setHelpLocation(helpLocation);
|
|
||||||
tool.addLocalAction(this, filterRegistersAction);
|
|
||||||
|
|
||||||
followLocationToggleAction =
|
showDefaultValuesAction =
|
||||||
new ToggleDockingAction("Follow location changes", getOwner()) {
|
new ToggleActionBuilder("Show Default Register Values", getOwner())
|
||||||
@Override
|
.menuPath("Show Default Values")
|
||||||
public void actionPerformed(ActionContext context) {
|
.description("Toggles showing of default register values")
|
||||||
followLocation = followLocationToggleAction.isSelected();
|
.helpLocation(helpLocation)
|
||||||
}
|
.selected(false)
|
||||||
};
|
.onAction(c -> updateShowingDefaultRegisterValues())
|
||||||
followLocationToggleAction.setEnabled(true);
|
.buildAndInstallLocal(this);
|
||||||
followLocationToggleAction.setHelpLocation(helpLocation);
|
|
||||||
followLocationToggleAction.setToolBarData(new ToolBarData(RECV_LOCATION_ICON, "NavAction"));
|
|
||||||
tool.addLocalAction(this, followLocationToggleAction);
|
|
||||||
|
|
||||||
|
filterRegistersAction = new ToggleActionBuilder("Filter Registers", getOwner())
|
||||||
|
.toolBarIcon(FILTER_ICON)
|
||||||
|
.description("Toggles showing only registers with values or default values")
|
||||||
|
.helpLocation(helpLocation)
|
||||||
|
.selected(false)
|
||||||
|
.onAction(c -> tree.setFiltered(filterRegistersAction.isSelected()))
|
||||||
|
.buildAndInstallLocal(this);
|
||||||
|
|
||||||
|
new ToggleActionBuilder("Follow location changes", getOwner())
|
||||||
|
.toolBarIcon(RECV_LOCATION_ICON)
|
||||||
|
.toolBarGroup("NavAction")
|
||||||
|
.description(
|
||||||
|
"If selected, auto select register and value range from listing location")
|
||||||
|
.helpLocation(helpLocation)
|
||||||
|
.selected(false)
|
||||||
|
.onAction(c -> followLocation = !followLocation)
|
||||||
|
.buildAndInstallLocal(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateShowingDefaultRegisterValues() {
|
||||||
|
values.setShowDefaultValues(showDefaultValuesAction.isSelected());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRange(Register register) {
|
||||||
|
EditRegisterValueDialog dialog =
|
||||||
|
new EditRegisterValueDialog(register, currentAddress, currentAddress, null, program);
|
||||||
|
dialog.setTitle("Add Value Range");
|
||||||
|
tool.showDialog(dialog, this);
|
||||||
|
|
||||||
|
if (!dialog.wasCancelled()) {
|
||||||
|
Address start = dialog.getStartAddress();
|
||||||
|
Address end = dialog.getEndAddress();
|
||||||
|
BigInteger value = dialog.getValue();
|
||||||
|
Command<Program> command = new SetRegisterCmd(register, start, end, value);
|
||||||
|
tool.execute(command, program);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showRegister() {
|
private void showRegister() {
|
||||||
Register register = tree.getSelectedRegister();
|
Register register = tree.getSelectedRegister();
|
||||||
values.setRegister(register);
|
values.setRegister(register);
|
||||||
|
contextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -191,7 +202,8 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
|||||||
if (program == null) {
|
if (program == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new ProgramActionContext(this, program);
|
return new RegisterManagerContext(program, tree.getSelectedRegister(),
|
||||||
|
values.hasSelectedRows());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -245,6 +257,7 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setLocation(Register register, Address address) {
|
public void setLocation(Register register, Address address) {
|
||||||
|
currentAddress = address;
|
||||||
if (!followLocation) {
|
if (!followLocation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -254,4 +267,23 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
|||||||
values.setAddress(address);
|
values.setAddress(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class RegisterManagerContext extends ProgramActionContext {
|
||||||
|
private Register register;
|
||||||
|
private boolean hasSelectedRows;
|
||||||
|
|
||||||
|
RegisterManagerContext(Program program, Register register, boolean hasSelectedRows) {
|
||||||
|
super(RegisterManagerProvider.this, program);
|
||||||
|
this.register = register;
|
||||||
|
this.hasSelectedRows = hasSelectedRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSelectedRegisterValueRanges() {
|
||||||
|
return hasSelectedRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Register getSelectedRegister() {
|
||||||
|
return register;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -358,15 +358,13 @@ public class RegisterPlugin extends ProgramPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void locationChanged(ProgramLocation loc) {
|
protected void locationChanged(ProgramLocation loc) {
|
||||||
if (loc instanceof RegisterTransitionFieldLocation) {
|
if (loc instanceof RegisterTransitionFieldLocation regLoc) {
|
||||||
RegisterTransitionFieldLocation regLoc = (RegisterTransitionFieldLocation) loc;
|
|
||||||
Register reg = regLoc.getRegister();
|
Register reg = regLoc.getRegister();
|
||||||
if (reg != null) {
|
if (reg != null) {
|
||||||
registerMgrProvider.setLocation(reg, regLoc.getAddress());
|
registerMgrProvider.setLocation(reg, regLoc.getAddress());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (loc instanceof RegisterFieldLocation) {
|
else if (loc instanceof RegisterFieldLocation regLoc) {
|
||||||
RegisterFieldLocation regLoc = (RegisterFieldLocation) loc;
|
|
||||||
Register reg = regLoc.getRegister();
|
Register reg = regLoc.getRegister();
|
||||||
if (reg != null) {
|
if (reg != null) {
|
||||||
registerMgrProvider.setLocation(reg, regLoc.getAddress());
|
registerMgrProvider.setLocation(reg, regLoc.getAddress());
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ class RegisterValuesPanel extends JPanel {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasSelectedRows() {
|
||||||
|
return table.getSelectedRowCount() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
private void editRow(int row) {
|
private void editRow(int row) {
|
||||||
RegisterValueRange range = model.values.get(row);
|
RegisterValueRange range = model.values.get(row);
|
||||||
Address start = range.getStartAddress();
|
Address start = range.getStartAddress();
|
||||||
@@ -275,7 +279,7 @@ class RegisterValuesPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectedRanges() {
|
void selectRanges() {
|
||||||
int[] rows = table.getSelectedRows();
|
int[] rows = table.getSelectedRows();
|
||||||
AddressSet set = new AddressSet();
|
AddressSet set = new AddressSet();
|
||||||
for (int element : rows) {
|
for (int element : rows) {
|
||||||
|
|||||||
@@ -15,11 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.register;
|
package ghidra.app.plugin.core.register;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.Component;
|
||||||
import java.awt.event.ItemEvent;
|
import java.awt.event.ItemEvent;
|
||||||
import java.awt.event.ItemListener;
|
import java.awt.event.ItemListener;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.ChangeEvent;
|
import javax.swing.event.ChangeEvent;
|
||||||
@@ -28,19 +29,21 @@ import javax.swing.event.ChangeListener;
|
|||||||
import docking.DialogComponentProvider;
|
import docking.DialogComponentProvider;
|
||||||
import docking.widgets.combobox.GComboBox;
|
import docking.widgets.combobox.GComboBox;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
|
import docking.widgets.list.GListCellRenderer;
|
||||||
|
import docking.widgets.textfield.FixedSizeIntegerTextField;
|
||||||
import generic.theme.GThemeDefaults.Ids.Fonts;
|
import generic.theme.GThemeDefaults.Ids.Fonts;
|
||||||
import generic.theme.Gui;
|
import generic.theme.Gui;
|
||||||
import ghidra.app.util.bean.FixedBitSizeValueField;
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
import ghidra.program.model.lang.RegisterValue;
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.layout.VariableHeightPairLayout;
|
||||||
|
|
||||||
public class SetRegisterValueDialog extends DialogComponentProvider {
|
public class SetRegisterValueDialog extends DialogComponentProvider {
|
||||||
private JComboBox<RegisterWrapper> registerComboBox;
|
private JComboBox<RegisterWrapper> registerComboBox;
|
||||||
private FixedBitSizeValueField registerValueField;
|
private FixedSizeIntegerTextField registerValueField;
|
||||||
private JList addressRangeList;
|
private JList<String> addressRangeList;
|
||||||
private BigInteger registerValue;
|
private BigInteger registerValue;
|
||||||
private Register selectedRegister;
|
private Register selectedRegister;
|
||||||
private boolean useValueField;
|
private boolean useValueField;
|
||||||
@@ -58,7 +61,7 @@ public class SetRegisterValueDialog extends DialogComponentProvider {
|
|||||||
addOKButton();
|
addOKButton();
|
||||||
addCancelButton();
|
addCancelButton();
|
||||||
if (useValueField) {
|
if (useValueField) {
|
||||||
setFocusComponent(registerValueField.getTextComponent());
|
setFocusComponent(registerValueField.getComponent());
|
||||||
}
|
}
|
||||||
setSelectedRegister(register);
|
setSelectedRegister(register);
|
||||||
setAddressRanges(addrSet);
|
setAddressRanges(addrSet);
|
||||||
@@ -77,79 +80,72 @@ public class SetRegisterValueDialog extends DialogComponentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JComponent buildWorkPanel(Register[] registers) {
|
private JComponent buildWorkPanel(Register[] registers) {
|
||||||
registerComboBox = new GComboBox<>(wrapRegisters(registers));
|
GLabel registerLabel = new GLabel("Register:");
|
||||||
registerValueField = new FixedBitSizeValueField(32, true, false);
|
GLabel valueLabel = new GLabel("Value:");
|
||||||
|
GLabel addressLabel = new GLabel("Address(es):");
|
||||||
|
registerLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||||
|
valueLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||||
|
addressLabel.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||||
|
addressLabel.setVerticalAlignment(SwingConstants.TOP);
|
||||||
|
|
||||||
|
JPanel panel = new JPanel(new VariableHeightPairLayout(10, 10));
|
||||||
|
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
|
||||||
|
panel.add(registerLabel);
|
||||||
|
panel.add(buildRegisterComboBox(registers));
|
||||||
|
panel.add(valueLabel);
|
||||||
|
panel.add(buildValueField(registers));
|
||||||
|
panel.add(addressLabel);
|
||||||
|
panel.add(buildAddressPanel());
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component buildValueField(Register[] registers) {
|
||||||
|
registerValueField = new FixedSizeIntegerTextField(16, 16);
|
||||||
registerValueField.addChangeListener(new ChangeListener() {
|
registerValueField.addChangeListener(new ChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void stateChanged(ChangeEvent e) {
|
public void stateChanged(ChangeEvent e) {
|
||||||
updateOkEnablement();
|
updateOkEnablement();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return registerValueField.getComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component buildRegisterComboBox(Register[] registers) {
|
||||||
|
registerComboBox = new GComboBox<>(wrapRegisters(registers));
|
||||||
|
registerComboBox.setRenderer(new RegisterComboRenderer());
|
||||||
|
|
||||||
registerComboBox.addItemListener(new ItemListener() {
|
registerComboBox.addItemListener(new ItemListener() {
|
||||||
@Override
|
@Override
|
||||||
public void itemStateChanged(ItemEvent e) {
|
public void itemStateChanged(ItemEvent e) {
|
||||||
registerChanged();
|
registerChanged();
|
||||||
|
updateComboToolTip();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
updateComboToolTip();
|
||||||
|
return registerComboBox;
|
||||||
|
}
|
||||||
|
|
||||||
addressRangeList = new JList();
|
private void updateComboToolTip() {
|
||||||
|
RegisterWrapper item = (RegisterWrapper) registerComboBox.getSelectedItem();
|
||||||
|
String tooltip = item == null ? "" : item.getToolTip();
|
||||||
|
registerComboBox.setToolTipText(tooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component buildAddressPanel() {
|
||||||
|
addressRangeList = new JList<String>();
|
||||||
addressRangeList.setEnabled(false);
|
addressRangeList.setEnabled(false);
|
||||||
Gui.registerFont(addressRangeList, Fonts.MONOSPACED);
|
Gui.registerFont(addressRangeList, Fonts.MONOSPACED);
|
||||||
|
|
||||||
JScrollPane scrollPane = new JScrollPane(addressRangeList);
|
JScrollPane scrollPane = new JScrollPane(addressRangeList);
|
||||||
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||||
Dimension d = scrollPane.getPreferredSize();
|
return scrollPane;
|
||||||
d.height = 120;
|
|
||||||
d.width = 180;
|
|
||||||
scrollPane.setPreferredSize(d);
|
|
||||||
JPanel panel = new JPanel(new GridBagLayout());
|
|
||||||
|
|
||||||
GridBagConstraints gbc = new GridBagConstraints();
|
|
||||||
gbc.anchor = GridBagConstraints.WEST;
|
|
||||||
gbc.insets = new Insets(5, 5, 1, 5);
|
|
||||||
gbc.gridx = 0;
|
|
||||||
gbc.gridy = 0;
|
|
||||||
panel.add(new GLabel("Register:"), gbc);
|
|
||||||
gbc.gridy = 1;
|
|
||||||
if (useValueField) {
|
|
||||||
panel.add(new GLabel("Value:"), gbc);
|
|
||||||
}
|
|
||||||
gbc.gridy = 2;
|
|
||||||
|
|
||||||
gbc.anchor = GridBagConstraints.NORTHWEST;
|
|
||||||
gbc.insets = new Insets(10, 5, 1, 5);
|
|
||||||
GLabel addressLabel = new GLabel("Address(es):");
|
|
||||||
addressLabel.setVerticalAlignment(SwingConstants.TOP);
|
|
||||||
panel.add(addressLabel, gbc);
|
|
||||||
|
|
||||||
gbc.insets = new Insets(5, 5, 1, 5);
|
|
||||||
gbc.weightx = 1.0;
|
|
||||||
gbc.anchor = GridBagConstraints.WEST;
|
|
||||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
|
||||||
gbc.gridx = 1;
|
|
||||||
gbc.gridy = 0;
|
|
||||||
panel.add(registerComboBox, gbc);
|
|
||||||
gbc.gridy = 1;
|
|
||||||
if (useValueField) {
|
|
||||||
panel.add(registerValueField, gbc);
|
|
||||||
}
|
|
||||||
|
|
||||||
gbc.gridy = 2;
|
|
||||||
gbc.weighty = 1.0;
|
|
||||||
gbc.fill = GridBagConstraints.BOTH;
|
|
||||||
panel.add(scrollPane, gbc);
|
|
||||||
|
|
||||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
|
|
||||||
|
|
||||||
return panel;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerChanged() {
|
private void registerChanged() {
|
||||||
RegisterWrapper wrapper = (RegisterWrapper) registerComboBox.getSelectedItem();
|
RegisterWrapper wrapper = (RegisterWrapper) registerComboBox.getSelectedItem();
|
||||||
if (wrapper != null) {
|
if (wrapper != null) {
|
||||||
registerValueField.setBitSize(wrapper.register.getBitLength());
|
int bitLength = wrapper.register.getBitLength();
|
||||||
|
registerValueField.setBitSize(bitLength);
|
||||||
updateOkEnablement();
|
updateOkEnablement();
|
||||||
}
|
}
|
||||||
updateValue();
|
updateValue();
|
||||||
@@ -232,6 +228,21 @@ public class SetRegisterValueDialog extends DialogComponentProvider {
|
|||||||
return selectedRegister;
|
return selectedRegister;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class RegisterComboRenderer extends GListCellRenderer<RegisterWrapper> {
|
||||||
|
@Override
|
||||||
|
public Component getListCellRendererComponent(JList<? extends RegisterWrapper> list,
|
||||||
|
RegisterWrapper value, int index, boolean isSelected, boolean hasFocus) {
|
||||||
|
super.getListCellRendererComponent(list, value, index, isSelected, hasFocus);
|
||||||
|
String toolTip = value.getToolTip();
|
||||||
|
setToolTipText(toolTip);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getItemText(RegisterWrapper value) {
|
||||||
|
return value == null ? "" : value.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RegisterWrapper implements Comparable<RegisterWrapper> {
|
class RegisterWrapper implements Comparable<RegisterWrapper> {
|
||||||
@@ -240,14 +251,24 @@ class RegisterWrapper implements Comparable<RegisterWrapper> {
|
|||||||
|
|
||||||
RegisterWrapper(Register register) {
|
RegisterWrapper(Register register) {
|
||||||
this.register = register;
|
this.register = register;
|
||||||
displayName = register.getName() + " (" + register.getBitLength() + getAliases() + ")";
|
displayName = register.getName() + " (" + register.getBitLength() + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getAliases() {
|
String getToolTip() {
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuffer buf = new StringBuffer();
|
||||||
for (String alias : register.getAliases()) {
|
buf.append(displayName);
|
||||||
buf.append(buf.length() == 0 ? "; " : ", ");
|
buf.append(" Aliases: ");
|
||||||
buf.append(alias);
|
Iterator<String> aliases = register.getAliases().iterator();
|
||||||
|
if (!aliases.hasNext()) {
|
||||||
|
buf.append("none");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buf.append(aliases.next());
|
||||||
|
buf.append(", ");
|
||||||
|
}
|
||||||
|
while (aliases.hasNext()) {
|
||||||
|
buf.append(", ");
|
||||||
|
buf.append(aliases.next());
|
||||||
}
|
}
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class SelectBlockDialog extends ReusableDialogComponentProvider {
|
|||||||
numberInputField = new IntegerTextField(10);
|
numberInputField = new IntegerTextField(10);
|
||||||
numberInputField.getComponent().getAccessibleContext().setAccessibleName("Number Input");
|
numberInputField.getComponent().getAccessibleContext().setAccessibleName("Number Input");
|
||||||
numberInputField.setMaxValue(BigInteger.valueOf(Integer.MAX_VALUE));
|
numberInputField.setMaxValue(BigInteger.valueOf(Integer.MAX_VALUE));
|
||||||
numberInputField.setAllowNegativeValues(false);
|
numberInputField.setMinValue(BigInteger.ZERO);
|
||||||
main.add(numberInputField.getComponent());
|
main.add(numberInputField.getComponent());
|
||||||
main.getAccessibleContext().setAccessibleName("Block");
|
main.getAccessibleContext().setAccessibleName("Block");
|
||||||
return main;
|
return main;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package ghidra.app.plugin.core.string;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -331,7 +332,7 @@ public class StringTableProvider extends ComponentProviderAdapter implements Dom
|
|||||||
|
|
||||||
private Component buildOffsetPanel() {
|
private Component buildOffsetPanel() {
|
||||||
offsetField = new IntegerTextField(4, 0L);
|
offsetField = new IntegerTextField(4, 0L);
|
||||||
offsetField.setAllowNegativeValues(false);
|
offsetField.setMinValue(BigInteger.ZERO);
|
||||||
offsetField.addChangeListener(e -> updatePreview());
|
offsetField.addChangeListener(e -> updatePreview());
|
||||||
|
|
||||||
preview = new JTextField(5);
|
preview = new JTextField(5);
|
||||||
|
|||||||
@@ -15,22 +15,22 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.util;
|
package ghidra.app.util;
|
||||||
|
|
||||||
|
import static docking.widgets.textfield.integer.IntegerFormat.*;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.CardLayout;
|
import java.awt.CardLayout;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.event.DocumentEvent;
|
|
||||||
import javax.swing.event.DocumentListener;
|
|
||||||
|
|
||||||
import docking.widgets.combobox.GComboBox;
|
import docking.widgets.combobox.GComboBox;
|
||||||
import docking.widgets.table.FocusableEditor;
|
import docking.widgets.table.FocusableEditor;
|
||||||
import docking.widgets.textfield.HexDecimalModeTextField;
|
import docking.widgets.textfield.integer.MultiFormatTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
import generic.expressions.ExpressionException;
|
import generic.expressions.ExpressionException;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
@@ -45,7 +45,7 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
|||||||
public final static Predicate<AddressSpace> ALL_MEMORY_SPACES = s -> s.isMemorySpace();
|
public final static Predicate<AddressSpace> ALL_MEMORY_SPACES = s -> s.isMemorySpace();
|
||||||
public final static Predicate<AddressSpace> LOADED_MEMORY_SPACES = s -> s.isLoadedMemorySpace();
|
public final static Predicate<AddressSpace> LOADED_MEMORY_SPACES = s -> s.isLoadedMemorySpace();
|
||||||
|
|
||||||
private HexDecimalModeTextField textField;
|
private MultiFormatTextField textField;
|
||||||
private AddressSpaceField addressSpaceField;
|
private AddressSpaceField addressSpaceField;
|
||||||
AddressEvaluator addressEvaluator;
|
AddressEvaluator addressEvaluator;
|
||||||
private Predicate<AddressSpace> addressSpaceFilter = LOADED_MEMORY_SPACES;
|
private Predicate<AddressSpace> addressSpaceFilter = LOADED_MEMORY_SPACES;
|
||||||
@@ -170,8 +170,9 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
|||||||
* @param hexMode true to assume numbers are hexadecimal.
|
* @param hexMode true to assume numbers are hexadecimal.
|
||||||
*/
|
*/
|
||||||
public void setAssumeHex(boolean hexMode) {
|
public void setAssumeHex(boolean hexMode) {
|
||||||
textField.setHexMode(hexMode);
|
IntegerFormat format = hexMode ? IntegerFormat.HEX : IntegerFormat.DEC;
|
||||||
hexModeChanged(hexMode);
|
textField.setFormat(format);
|
||||||
|
hexModeChanged(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -418,35 +419,17 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
|||||||
|
|
||||||
private void buildComponent() {
|
private void buildComponent() {
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
textField = new HexDecimalModeTextField(10, b -> hexModeChanged(b));
|
textField = new MultiFormatTextField(10, List.of(HEX, DEC), b -> hexModeChanged(b));
|
||||||
textField.setHexMode(true);
|
|
||||||
textField.setName("JTextField");//for JUnits...
|
textField.setName("JTextField");//for JUnits...
|
||||||
addressSpaceField = new AddressSpaceField();
|
addressSpaceField = new AddressSpaceField();
|
||||||
add(textField, BorderLayout.CENTER);
|
add(textField, BorderLayout.CENTER);
|
||||||
comboAdded = false;
|
comboAdded = false;
|
||||||
|
textField.addTextChangedCallback(this::notifyAddressChanged);
|
||||||
textField.getDocument().addDocumentListener(new DocumentListener() {
|
|
||||||
@Override
|
|
||||||
public void insertUpdate(DocumentEvent e) {
|
|
||||||
notifyAddressChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void hexModeChanged(IntegerFormat format) {
|
||||||
public void removeUpdate(DocumentEvent e) {
|
this.assumeHex = format == HEX;
|
||||||
notifyAddressChanged();
|
addressEvaluator.setAssumeHex(assumeHex);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changedUpdate(DocumentEvent e) {
|
|
||||||
notifyAddressChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void hexModeChanged(boolean hexMode) {
|
|
||||||
this.assumeHex = hexMode;
|
|
||||||
addressEvaluator.setAssumeHex(hexMode);
|
|
||||||
notifyAddressChanged();
|
notifyAddressChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package ghidra.app.util.importer.options;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
import ghidra.app.util.*;
|
import ghidra.app.util.*;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.program.model.address.AddressFactory;
|
import ghidra.program.model.address.AddressFactory;
|
||||||
@@ -64,7 +65,7 @@ public class HexLongOption extends AbstractOption<HexLong> {
|
|||||||
setValue(new HexLong(initialState));
|
setValue(new HexLong(initialState));
|
||||||
IntegerTextField field = new IntegerTextField();
|
IntegerTextField field = new IntegerTextField();
|
||||||
field.setValue(initialState);
|
field.setValue(initialState);
|
||||||
field.setHexMode();
|
field.setFormat(IntegerFormat.HEX);
|
||||||
field.getComponent().setToolTipText(getDescription());
|
field.getComponent().setToolTipText(getDescription());
|
||||||
field.addChangeListener(e -> {
|
field.addChangeListener(e -> {
|
||||||
setValue(new HexLong(field.getLongValue()));
|
setValue(new HexLong(field.getLongValue()));
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import docking.widgets.checkbox.GCheckBox;
|
|||||||
import docking.widgets.combobox.GhidraComboBox;
|
import docking.widgets.combobox.GhidraComboBox;
|
||||||
import docking.widgets.label.GDLabel;
|
import docking.widgets.label.GDLabel;
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
import ghidra.framework.options.CustomOptionsEditor;
|
import ghidra.framework.options.CustomOptionsEditor;
|
||||||
import ghidra.util.HTMLUtilities;
|
import ghidra.util.HTMLUtilities;
|
||||||
import ghidra.util.layout.PairLayout;
|
import ghidra.util.layout.PairLayout;
|
||||||
@@ -101,8 +102,8 @@ public class AddressFieldOptionsPropertyEditor extends PropertyEditorSupport
|
|||||||
panel.add(label);
|
panel.add(label);
|
||||||
|
|
||||||
minDigitsField = new IntegerTextField(2);
|
minDigitsField = new IntegerTextField(2);
|
||||||
minDigitsField.setAllowNegativeValues(false);
|
minDigitsField.setMinValue(BigInteger.ZERO);
|
||||||
minDigitsField.setDecimalMode();
|
minDigitsField.setFormat(IntegerFormat.DEC);
|
||||||
minDigitsField.setMaxValue(BigInteger.valueOf(32));
|
minDigitsField.setMaxValue(BigInteger.valueOf(32));
|
||||||
minDigitsField.getComponent().setToolTipText(MIN_HEX_DIGITS_TOOLTIP);
|
minDigitsField.getComponent().setToolTipText(MIN_HEX_DIGITS_TOOLTIP);
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.awt.Container;
|
|||||||
import java.awt.event.ItemEvent;
|
import java.awt.event.ItemEvent;
|
||||||
import java.awt.event.ItemListener;
|
import java.awt.event.ItemListener;
|
||||||
import java.beans.PropertyEditorSupport;
|
import java.beans.PropertyEditorSupport;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.TitledBorder;
|
import javax.swing.border.TitledBorder;
|
||||||
@@ -97,7 +98,7 @@ public class ArrayElementPropertyEditor extends PropertyEditorSupport
|
|||||||
Container parent) {
|
Container parent) {
|
||||||
|
|
||||||
IntegerTextField textField = new IntegerTextField(10);
|
IntegerTextField textField = new IntegerTextField(10);
|
||||||
textField.setAllowNegativeValues(false);
|
textField.setMinValue(BigInteger.ZERO);
|
||||||
textField.setEnabled(true);
|
textField.setEnabled(true);
|
||||||
|
|
||||||
JPanel textFieldPanel = new JPanel();
|
JPanel textFieldPanel = new JPanel();
|
||||||
@@ -132,7 +133,8 @@ public class ArrayElementPropertyEditor extends PropertyEditorSupport
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setLocalValues(ArrayElementWrappedOption namespaceOption) {
|
private void setLocalValues(ArrayElementWrappedOption namespaceOption) {
|
||||||
if (namespaceOption.showMultipleArrayElementPerLine() != groupElementsCheckBox.isSelected()) {
|
if (namespaceOption.showMultipleArrayElementPerLine() != groupElementsCheckBox
|
||||||
|
.isSelected()) {
|
||||||
groupElementsCheckBox.setSelected(namespaceOption.showMultipleArrayElementPerLine());
|
groupElementsCheckBox.setSelected(namespaceOption.showMultipleArrayElementPerLine());
|
||||||
}
|
}
|
||||||
if (namespaceOption.getArrayElementsPerLine() != elementsPerLineField.getIntValue()) {
|
if (namespaceOption.getArrayElementsPerLine() != elementsPerLineField.getIntValue()) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import docking.DialogComponentProvider;
|
|||||||
import docking.DockingWindowManager;
|
import docking.DockingWindowManager;
|
||||||
import docking.widgets.label.GDLabel;
|
import docking.widgets.label.GDLabel;
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
import ghidra.util.Swing;
|
import ghidra.util.Swing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,10 +110,10 @@ public abstract class AbstractNumberInputDialog extends DialogComponentProvider
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (showAsHex) {
|
if (showAsHex) {
|
||||||
numberInputField.setHexMode();
|
numberInputField.setFormat(IntegerFormat.HEX);
|
||||||
}
|
}
|
||||||
if (min.compareTo(BigInteger.valueOf(0)) >= 0) {
|
if (min.compareTo(BigInteger.valueOf(0)) >= 0) {
|
||||||
numberInputField.setAllowNegativeValues(false);
|
numberInputField.setMinValue(BigInteger.ZERO);
|
||||||
}
|
}
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package docking.widgets.table.constrainteditor;
|
package docking.widgets.table.constrainteditor;
|
||||||
|
|
||||||
|
import static docking.widgets.textfield.integer.IntegerFormat.*;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.GridLayout;
|
import java.awt.GridLayout;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
@@ -129,7 +131,8 @@ public class IntegerRangeConstraintEditor<T extends Number>
|
|||||||
BigInteger delta = bigEnd.subtract(bigStart).add(BigInteger.ONE);
|
BigInteger delta = bigEnd.subtract(bigStart).add(BigInteger.ONE);
|
||||||
|
|
||||||
boolean hexMode =
|
boolean hexMode =
|
||||||
lowerSpinner.getTextField().isHexMode() || upperSpinner.getTextField().isHexMode();
|
lowerSpinner.getTextField().getFormat() == HEX ||
|
||||||
|
upperSpinner.getTextField().getFormat() == HEX;
|
||||||
|
|
||||||
String statusMsg = formatStatus(
|
String statusMsg = formatStatus(
|
||||||
String.format("Range Size: " + (hexMode ? "0x%x" : "%,d"), delta), false);
|
String.format("Range Size: " + (hexMode ? "0x%x" : "%,d"), delta), false);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import docking.widgets.label.GDHtmlLabel;
|
|||||||
import docking.widgets.table.constraint.ColumnConstraint;
|
import docking.widgets.table.constraint.ColumnConstraint;
|
||||||
import docking.widgets.table.constraint.SingleValueColumnConstraint;
|
import docking.widgets.table.constraint.SingleValueColumnConstraint;
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,8 +55,8 @@ public class UnsignedLongConstraintEditor extends AbstractColumnConstraintEditor
|
|||||||
getConstraint().getConstraintValue();
|
getConstraint().getConstraintValue();
|
||||||
|
|
||||||
field = new IntegerTextField(16, 0);
|
field = new IntegerTextField(16, 0);
|
||||||
field.setHexMode();
|
field.setFormat(IntegerFormat.HEX);
|
||||||
field.setAllowNegativeValues(false);
|
field.setMinValue(BigInteger.ZERO); // don't allow negative numbers
|
||||||
field.setMaxValue(new BigInteger("FFFFFFFFFFFFFFFF", 16));
|
field.setMaxValue(new BigInteger("FFFFFFFFFFFFFFFF", 16));
|
||||||
field.addChangeListener(e -> valueChanged());
|
field.addChangeListener(e -> valueChanged());
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import docking.widgets.label.GDHtmlLabel;
|
|||||||
import docking.widgets.table.constraint.ColumnConstraint;
|
import docking.widgets.table.constraint.ColumnConstraint;
|
||||||
import docking.widgets.table.constraint.RangeColumnConstraint;
|
import docking.widgets.table.constraint.RangeColumnConstraint;
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||||
import ghidra.util.layout.VerticalLayout;
|
import ghidra.util.layout.VerticalLayout;
|
||||||
|
|
||||||
@@ -80,8 +81,8 @@ public class UnsignedLongRangeConstraintEditor extends AbstractColumnConstraintE
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void configureField(IntegerTextField field) {
|
private void configureField(IntegerTextField field) {
|
||||||
field.setHexMode();
|
field.setFormat(IntegerFormat.HEX);
|
||||||
field.setAllowNegativeValues(false);
|
field.setMinValue(BigInteger.ZERO);
|
||||||
field.setMaxValue(MAX_VALUE);
|
field.setMaxValue(MAX_VALUE);
|
||||||
field.addChangeListener(e -> valueChanged());
|
field.addChangeListener(e -> valueChanged());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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 docking.widgets.textfield;
|
||||||
|
|
||||||
|
import static docking.widgets.textfield.integer.IntegerFormat.*;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import docking.widgets.textfield.integer.AbstractIntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TextField for entering numbers where the values are restricted to those that can be represented
|
||||||
|
* by a specific number of bits. For example, if the bit size is eight, signed values
|
||||||
|
* must be between -128 and 127 and unsigned values must be between 0 and 255. By
|
||||||
|
* default, this class uses all the formats from the {@link IntegerFormat} enum class except for
|
||||||
|
* signed octal and signed binary.
|
||||||
|
*
|
||||||
|
* <P>
|
||||||
|
* This field does continuous checking, so you can't enter a bad value.
|
||||||
|
*<P>
|
||||||
|
* The bitSize can be changed on this field which will cause its min and max values to change to
|
||||||
|
* the appropriate values for that bit size and signedness of the current {@link IntegerFormat}.
|
||||||
|
* Also, if the current value doesn't fit in the new bit size, it will be reset to having no
|
||||||
|
* value (textfield is blank).
|
||||||
|
* <P>
|
||||||
|
* Internally, values are maintained using BigIntegers so this class can accommodate any bit size
|
||||||
|
* desired. There are convenience methods for getting the value as either an int or long. You
|
||||||
|
* should only use these convenience methods if you know the current bit size fits in either a
|
||||||
|
* int or long respectively.
|
||||||
|
*
|
||||||
|
* <P>
|
||||||
|
* There are several configuration options as follows:
|
||||||
|
* <UL>
|
||||||
|
* <LI>Bit Size - This value must be 1 or greater and determines the minimum and maximum allowed
|
||||||
|
* input values when combined with the current format signedness.</LI>
|
||||||
|
* <LI>Use number prefix - If this mode is on, then non-decimal values must be typed with its
|
||||||
|
* prefix(i.e., 0x for hex). When requiring non-decimal prefix, the field is permitted to auto
|
||||||
|
* switch formats based on the prefix (or lack thereof). When the "use prefix" option is off, the
|
||||||
|
* only way to switch formats is to use the built-in ctrl-M action.</LI>
|
||||||
|
* <LI>Show the number mode as hint text - If showing number mode is on, the format short name
|
||||||
|
* is displayed lightly in the bottom right portion of the text field.
|
||||||
|
* See {@link #setShowNumberMode(boolean)}</LI>
|
||||||
|
* </UL>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FixedSizeIntegerTextField extends AbstractIntegerTextField {
|
||||||
|
|
||||||
|
private int bitSize;
|
||||||
|
private BigInteger minSignedValue;
|
||||||
|
private BigInteger minUnsignedValue;
|
||||||
|
private BigInteger maxSignedValue;
|
||||||
|
private BigInteger maxUnsignedValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param columns the number of character positions for the preferred size of the text field
|
||||||
|
* @param bitSize the initial bit size
|
||||||
|
*/
|
||||||
|
public FixedSizeIntegerTextField(int columns, int bitSize) {
|
||||||
|
this(columns, bitSize, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param columns the number of character positions for the preferred size of the text field
|
||||||
|
* @param bitSize the initial bit size
|
||||||
|
* @param initialValue the value to initialize the field to
|
||||||
|
*/
|
||||||
|
public FixedSizeIntegerTextField(int columns, int bitSize, long initialValue) {
|
||||||
|
this(columns, bitSize, BigInteger.valueOf(initialValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param columns the number of character positions for the preferred size of the text field
|
||||||
|
* @param bitSize the initial bit size
|
||||||
|
* @param initialValue the value to initialize the field to
|
||||||
|
*/
|
||||||
|
public FixedSizeIntegerTextField(int columns, int bitSize, BigInteger initialValue) {
|
||||||
|
super(columns, initialValue, DEC, U_DEC, HEX, U_HEX, U_OCT, U_BIN);
|
||||||
|
setBitSize(bitSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bit size for this field, which effectively sets the min and max values for this
|
||||||
|
* field when combined with the signedness of the currently selected {@link IntegerFormat}.
|
||||||
|
* @param bitSize the number of bits that will be used to store this value, effectively
|
||||||
|
* determining its min and max value
|
||||||
|
*/
|
||||||
|
public void setBitSize(int bitSize) {
|
||||||
|
if (bitSize < 1) {
|
||||||
|
throw new IllegalArgumentException("Bit size must be greater than 0");
|
||||||
|
}
|
||||||
|
minUnsignedValue = BigInteger.ZERO;
|
||||||
|
maxUnsignedValue = BigInteger.TWO.pow(bitSize).subtract(BigInteger.ONE);
|
||||||
|
minSignedValue = BigInteger.TWO.pow(bitSize - 1).negate();
|
||||||
|
maxSignedValue = BigInteger.TWO.pow(bitSize - 1).subtract(BigInteger.ONE);
|
||||||
|
|
||||||
|
BigInteger value = getValue();
|
||||||
|
this.bitSize = bitSize;
|
||||||
|
updateMinMax();
|
||||||
|
setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the current bit size for this field}
|
||||||
|
*/
|
||||||
|
public int getBitSize() {
|
||||||
|
return bitSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(BigInteger newValue) {
|
||||||
|
if (newValue != null && !isInBounds(newValue)) {
|
||||||
|
if (currentFormat.isUnsigned()) {
|
||||||
|
newValue = maybeConvertToUnsigned(newValue);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newValue = maybeConvertToSigned(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.setValue(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigInteger maybeConvertToUnsigned(BigInteger value) {
|
||||||
|
// conversion only makes sense if value is a negative number in the signed range
|
||||||
|
if (value.compareTo(minSignedValue) >= 0 && value.compareTo(BigInteger.ZERO) < 0) {
|
||||||
|
return value.add(BigInteger.TWO.pow(bitSize));
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigInteger maybeConvertToSigned(BigInteger value) {
|
||||||
|
// conversion only makes sense if value is a positive number in the unsigned range
|
||||||
|
if (value.compareTo(BigInteger.ZERO) > 0 && value.compareTo(maxUnsignedValue) <= 0) {
|
||||||
|
return value.subtract(BigInteger.TWO.pow(bitSize));
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMinMax() {
|
||||||
|
if (currentFormat.isUnsigned()) {
|
||||||
|
setMinValue(minUnsignedValue);
|
||||||
|
setMaxValue(maxUnsignedValue);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setMinValue(minSignedValue);
|
||||||
|
setMaxValue(maxSignedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFormat(IntegerFormat format) {
|
||||||
|
BigInteger value = getValue();
|
||||||
|
super.setFormat(format);
|
||||||
|
updateMinMax();
|
||||||
|
setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* 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 docking.widgets.textfield;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.ToolTipManager;
|
|
||||||
|
|
||||||
import docking.DockingUtils;
|
|
||||||
import docking.util.GraphicsUtils;
|
|
||||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
|
||||||
import generic.theme.Gui;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides the JTextField mainly to allow hint painting for the current radix mode.
|
|
||||||
*/
|
|
||||||
public class HexDecimalModeTextField extends JTextField {
|
|
||||||
|
|
||||||
private static final String FONT_ID = "font.input.hint";
|
|
||||||
private int hintWidth;
|
|
||||||
private boolean isHexMode;
|
|
||||||
private boolean showNumbericDecoration = true;
|
|
||||||
|
|
||||||
public HexDecimalModeTextField(int columns, Consumer<Boolean> modeConsumer) {
|
|
||||||
super(columns);
|
|
||||||
|
|
||||||
FontMetrics fontMetrics = getFontMetrics(Gui.getFont(FONT_ID));
|
|
||||||
String mode = isHexMode ? "Hex" : "Dec";
|
|
||||||
hintWidth = fontMetrics.stringWidth(mode);
|
|
||||||
|
|
||||||
addKeyListener(new KeyAdapter() {
|
|
||||||
@Override
|
|
||||||
public void keyPressed(KeyEvent e) {
|
|
||||||
if (e.getKeyCode() == KeyEvent.VK_M && DockingUtils.isControlModifier(e)) {
|
|
||||||
isHexMode = !isHexMode;
|
|
||||||
modeConsumer.accept(isHexMode);
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// make sure tooltips will be activated
|
|
||||||
ToolTipManager.sharedInstance().registerComponent(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getToolTipText(MouseEvent event) {
|
|
||||||
|
|
||||||
int hintStart = getBounds().width - hintWidth;
|
|
||||||
if (event.getX() > hintStart) {
|
|
||||||
String key = DockingUtils.CONTROL_KEY_NAME;
|
|
||||||
return "Press '" + key + "-M' to toggle Hex or Decimal Mode";
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.getToolTipText();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHexMode(boolean hexMode) {
|
|
||||||
this.isHexMode = hexMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns on or off the faded text that displays the field's radix mode (hex or decimal).
|
|
||||||
*
|
|
||||||
* @param show true to show the radix mode.
|
|
||||||
*/
|
|
||||||
public void setShowNumberMode(boolean show) {
|
|
||||||
this.showNumbericDecoration = show;
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void paintComponent(Graphics g) {
|
|
||||||
super.paintComponent(g);
|
|
||||||
if (!showNumbericDecoration) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Font savedFont = g.getFont();
|
|
||||||
g.setFont(Gui.getFont(FONT_ID));
|
|
||||||
g.setColor(Messages.HINT);
|
|
||||||
|
|
||||||
Dimension size = getSize();
|
|
||||||
Insets insets = getInsets();
|
|
||||||
int x;
|
|
||||||
if (getHorizontalAlignment() == RIGHT) {
|
|
||||||
x = insets.left;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
x = size.width - insets.right - hintWidth;
|
|
||||||
}
|
|
||||||
int y = size.height - insets.bottom - 1;
|
|
||||||
String mode = isHexMode ? "Hex" : "Dec";
|
|
||||||
GraphicsUtils.drawString(this, g, mode, x, y);
|
|
||||||
g.setFont(savedFont);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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 docking.widgets.textfield.integer;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import docking.widgets.textfield.FixedSizeIntegerTextField;
|
||||||
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Input formats for entering integers into a text field such as the {@link IntegerTextField} or
|
||||||
|
* {@link FixedSizeIntegerTextField}
|
||||||
|
*/
|
||||||
|
public enum IntegerFormat {
|
||||||
|
DEC("Dec", "decimal", "", 10, false),
|
||||||
|
HEX("Hex", "hexadecimal", "0x", 16, false),
|
||||||
|
OCT("Oct", "octal", "0O", 8, false),
|
||||||
|
BIN("Bin", "binary", "0b", 2, false),
|
||||||
|
|
||||||
|
U_DEC("uDec", "unsigned decimal", "", 10, true),
|
||||||
|
U_HEX("uHex", "unsigned hexadecimal", "0x", 16, true),
|
||||||
|
U_OCT("uOct", "unsigned octal", "0O", 8, true),
|
||||||
|
U_BIN("uBin", "unsigned binary", "0b", 2, true);
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private String longName;
|
||||||
|
private String prefix;
|
||||||
|
private int radix;
|
||||||
|
private boolean isUnsigned;
|
||||||
|
|
||||||
|
private IntegerFormat(String name, String description, String prefix, int radix,
|
||||||
|
boolean isUnsigned) {
|
||||||
|
this.name = name;
|
||||||
|
this.longName = description;
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.radix = radix;
|
||||||
|
this.isUnsigned = isUnsigned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the short name of this number format}
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return a descriptive name of this number format}
|
||||||
|
*/
|
||||||
|
public String getDescription() {
|
||||||
|
return longName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given value into a string representation corresponding to this number format.
|
||||||
|
* @param value the value to format into a string
|
||||||
|
* @return A string representation of the given value.
|
||||||
|
*/
|
||||||
|
public String format(BigInteger value) {
|
||||||
|
return value.toString(radix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given string into a BigInteger or null if the string is not properly structured
|
||||||
|
* for this number format.
|
||||||
|
* @param text the text to parse into a BigInteger
|
||||||
|
* @return the BigInteger interpretation of the given string for this number format.
|
||||||
|
*/
|
||||||
|
public BigInteger parse(String text) {
|
||||||
|
try {
|
||||||
|
return new BigInteger(text, radix);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// failed to parse, return null
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the prefix associated with this format}
|
||||||
|
*/
|
||||||
|
public String getPrefix() {
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if this format is intended only for non-negative values. This is more of a hint
|
||||||
|
* to the client text field to determine if "-" characters are allowed to be entered.
|
||||||
|
* @return true if this format is for unsigned numbers
|
||||||
|
*/
|
||||||
|
public boolean isUnsigned() {
|
||||||
|
return isUnsigned;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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 docking.widgets.textfield.integer;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.ToolTipManager;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
import javax.swing.text.Document;
|
||||||
|
|
||||||
|
import docking.DockingUtils;
|
||||||
|
import docking.util.GraphicsUtils;
|
||||||
|
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||||
|
import generic.theme.Gui;
|
||||||
|
import ghidra.util.Swing;
|
||||||
|
import utility.function.Callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the JTextField mainly to allow hint painting for the current input format. It also
|
||||||
|
* handles processing control-M to switch modes.
|
||||||
|
*/
|
||||||
|
public class MultiFormatTextField extends JTextField {
|
||||||
|
|
||||||
|
private static final String FONT_ID = "font.input.hint";
|
||||||
|
private int hintWidth;
|
||||||
|
private boolean showFormatHint = true;
|
||||||
|
private List<IntegerFormat> formats;
|
||||||
|
private int currentFormatIndex;
|
||||||
|
|
||||||
|
public MultiFormatTextField(int columns, List<IntegerFormat> formats,
|
||||||
|
Consumer<IntegerFormat> formatChangeConsumer) {
|
||||||
|
super(columns);
|
||||||
|
|
||||||
|
this.formats = formats;
|
||||||
|
updateFormatNameWidth();
|
||||||
|
|
||||||
|
addKeyListener(new KeyAdapter() {
|
||||||
|
@Override
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
if (e.getKeyCode() == KeyEvent.VK_M && DockingUtils.isControlModifier(e)) {
|
||||||
|
if (++currentFormatIndex >= formats.size()) {
|
||||||
|
currentFormatIndex = 0;
|
||||||
|
}
|
||||||
|
updateFormatNameWidth();
|
||||||
|
formatChangeConsumer.accept(formats.get(currentFormatIndex));
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// make sure tooltips will be activated
|
||||||
|
ToolTipManager.sharedInstance().registerComponent(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the given callback to notify the client when the text has changed in this text field.
|
||||||
|
* @param c the callback to be notified when the text changes in this field
|
||||||
|
*/
|
||||||
|
public void addTextChangedCallback(Callback c) {
|
||||||
|
Document document = getDocument();
|
||||||
|
|
||||||
|
document.addDocumentListener(new DocumentListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
Swing.runLater(() -> c.call());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
Swing.runLater(() -> c.call());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
Swing.runLater(() -> c.call());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFormatNameWidth() {
|
||||||
|
FontMetrics fontMetrics = getFontMetrics(Gui.getFont(FONT_ID));
|
||||||
|
hintWidth = fontMetrics.stringWidth(formats.get(currentFormatIndex).getName());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTipText(MouseEvent event) {
|
||||||
|
|
||||||
|
int hintStart = getBounds().width - hintWidth;
|
||||||
|
|
||||||
|
if (event.getX() > hintStart && formats.size() > 1) {
|
||||||
|
String key = DockingUtils.CONTROL_KEY_NAME;
|
||||||
|
IntegerFormat format = formats.get(currentFormatIndex);
|
||||||
|
return "Enter value in %s format. Press %s-M to cycle input formats."
|
||||||
|
.formatted(format.getDescription(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getToolTipText(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link IntegerFormat} that will be used to format and parse the text in this
|
||||||
|
* field.
|
||||||
|
* @param format the number format that will be used to format and parse the text in this field
|
||||||
|
*/
|
||||||
|
public void setFormat(IntegerFormat format) {
|
||||||
|
int indexOf = formats.indexOf(format);
|
||||||
|
if (indexOf >= 0) {
|
||||||
|
currentFormatIndex = indexOf;
|
||||||
|
}
|
||||||
|
updateFormatNameWidth();
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on or off the faded hint text that displays the field's current format (i.e., hex,
|
||||||
|
* decimal).
|
||||||
|
*
|
||||||
|
* @param show true to show the input format.
|
||||||
|
*/
|
||||||
|
public void setShowInputFormatHint(boolean show) {
|
||||||
|
this.showFormatHint = show;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g);
|
||||||
|
if (!showFormatHint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Font savedFont = g.getFont();
|
||||||
|
g.setFont(Gui.getFont(FONT_ID));
|
||||||
|
g.setColor(Messages.HINT);
|
||||||
|
|
||||||
|
Dimension size = getSize();
|
||||||
|
Insets insets = getInsets();
|
||||||
|
int x;
|
||||||
|
if (getHorizontalAlignment() == RIGHT) {
|
||||||
|
x = insets.left;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
x = size.width - insets.right - hintWidth;
|
||||||
|
}
|
||||||
|
int y = size.height - insets.bottom - 1;
|
||||||
|
IntegerFormat format = formats.get(currentFormatIndex);
|
||||||
|
GraphicsUtils.drawString(this, g, format.getName(), x, y);
|
||||||
|
g.setFont(savedFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ package docking.widgets.values;
|
|||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value class for {@link Integer} Value with an option for display the value as decimal or hex. The
|
* Value class for {@link Integer} Value with an option for display the value as decimal or hex. The
|
||||||
@@ -69,10 +70,10 @@ public class IntValue extends AbstractValue<Integer> {
|
|||||||
public JComponent getComponent() {
|
public JComponent getComponent() {
|
||||||
if (field == null) {
|
if (field == null) {
|
||||||
field = new IntegerTextField(20);
|
field = new IntegerTextField(20);
|
||||||
field.setAllowsHexPrefix(false);
|
field.setUseNumberPrefix(false);
|
||||||
field.setShowNumberMode(false);
|
field.setShowNumberMode(false);
|
||||||
if (displayAsHex) {
|
if (displayAsHex) {
|
||||||
field.setHexMode();
|
field.setFormat(IntegerFormat.HEX);
|
||||||
field.setShowNumberMode(true);
|
field.setShowNumberMode(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package docking.widgets.values;
|
|||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value class for Long Values with an option for display the value as decimal or hex. The
|
* Value class for Long Values with an option for display the value as decimal or hex. The
|
||||||
@@ -57,10 +58,10 @@ public class LongValue extends AbstractValue<Long> {
|
|||||||
public JComponent getComponent() {
|
public JComponent getComponent() {
|
||||||
if (field == null) {
|
if (field == null) {
|
||||||
field = new IntegerTextField(20);
|
field = new IntegerTextField(20);
|
||||||
field.setAllowsHexPrefix(false);
|
field.setUseNumberPrefix(false);
|
||||||
field.setShowNumberMode(false);
|
field.setShowNumberMode(false);
|
||||||
if (displayAsHex) {
|
if (displayAsHex) {
|
||||||
field.setHexMode();
|
field.setFormat(IntegerFormat.HEX);
|
||||||
field.setShowNumberMode(true);
|
field.setShowNumberMode(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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 docking.widgets.textfield;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import docking.test.AbstractDockingTest;
|
||||||
|
import docking.widgets.textfield.integer.AbstractIntegerTextField;
|
||||||
|
import docking.widgets.textfield.integer.IntegerFormat;
|
||||||
|
|
||||||
|
public abstract class AbstractIntegerTextFieldTest<T extends AbstractIntegerTextField>
|
||||||
|
extends AbstractDockingTest {
|
||||||
|
private JFrame frame;
|
||||||
|
protected T field;
|
||||||
|
protected JTextField textField;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
|
field = createField();
|
||||||
|
field.setShowNumberMode(true);
|
||||||
|
textField = (JTextField) field.getComponent();
|
||||||
|
frame = new JFrame("Test");
|
||||||
|
frame.getContentPane().add(field.getComponent());
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected T createField();
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
frame.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setFormat(IntegerFormat format) {
|
||||||
|
runSwing(() -> field.setFormat(format));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IntegerFormat getFormat() {
|
||||||
|
return runSwing(() -> field.getFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setValue(long value) {
|
||||||
|
runSwing(() -> field.setValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getValue() {
|
||||||
|
return runSwing(() -> field.getLongValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getIntValue() {
|
||||||
|
return runSwing(() -> field.getIntValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BigInteger getBigIntegerValue() {
|
||||||
|
return runSwing(() -> field.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getText() {
|
||||||
|
return runSwing(() -> field.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BigInteger getMinValue() {
|
||||||
|
return runSwing(() -> field.getMinValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BigInteger getMaxValue() {
|
||||||
|
return runSwing(() -> field.getMaxValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setText(String text) {
|
||||||
|
runSwing(() -> field.setText(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void typeText(String text) {
|
||||||
|
triggerText(textField, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUsePrefix(boolean b) {
|
||||||
|
runSwing(() -> field.setUseNumberPrefix(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestChangeListener implements ChangeListener {
|
||||||
|
volatile int count;
|
||||||
|
protected AtomicIntegerArray values = new AtomicIntegerArray(10);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
values.set(count++, field.getIntValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,433 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* 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 docking.widgets.textfield;
|
||||||
|
|
||||||
|
import static docking.widgets.textfield.integer.IntegerFormat.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class FixedSizeIntegerTextFieldTest
|
||||||
|
extends AbstractIntegerTextFieldTest<FixedSizeIntegerTextField> {
|
||||||
|
@Override
|
||||||
|
protected FixedSizeIntegerTextField createField() {
|
||||||
|
return new FixedSizeIntegerTextField(10, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedHexSetValue() {
|
||||||
|
setFormat(HEX);
|
||||||
|
setValue(25);
|
||||||
|
assertEquals("0x19", getText());
|
||||||
|
setValue(-1);
|
||||||
|
assertEquals("-0x1", getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedHexSetText() {
|
||||||
|
setFormat(HEX);
|
||||||
|
setText("0x12");
|
||||||
|
assertEquals(18, getValue());
|
||||||
|
setText("");
|
||||||
|
assertEquals(0, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedHexTypeText() {
|
||||||
|
setFormat(HEX);
|
||||||
|
typeText("mnp0x12");
|
||||||
|
assertEquals(18, getValue());
|
||||||
|
setText("");
|
||||||
|
typeText("-0x5");
|
||||||
|
assertEquals(-5, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedHexMin() {
|
||||||
|
setFormat(HEX);
|
||||||
|
setBitSize(8);
|
||||||
|
|
||||||
|
typeText("-0x8");
|
||||||
|
assertEquals("-0x8", getText());
|
||||||
|
typeText("1");
|
||||||
|
assertEquals("-0x8", getText()); // -0x80 (-128) is min allowed for 8 bits
|
||||||
|
typeText("0");
|
||||||
|
assertEquals("-0x80", getText());
|
||||||
|
assertEquals(-128, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedHexMax() {
|
||||||
|
setFormat(HEX);
|
||||||
|
setBitSize(8);
|
||||||
|
|
||||||
|
typeText("0x8");
|
||||||
|
assertEquals("0x8", getText());
|
||||||
|
typeText("0");
|
||||||
|
assertEquals("0x8", getText()); // 7f (127) is the max allowed for 8 bits
|
||||||
|
setText("");
|
||||||
|
typeText("0x7f");
|
||||||
|
assertEquals("0x7f", getText());
|
||||||
|
assertEquals(127, getValue());
|
||||||
|
typeText("1");
|
||||||
|
assertEquals("0x7f", getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedHexConvertsUnsignedValueToNegative() {
|
||||||
|
setFormat(HEX);
|
||||||
|
setBitSize(8);
|
||||||
|
|
||||||
|
setValue(0xff);
|
||||||
|
assertEquals(-1, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedHexSetValue() {
|
||||||
|
setFormat(U_HEX);
|
||||||
|
setValue(25);
|
||||||
|
assertEquals("0x19", getText());
|
||||||
|
setValue(-130);
|
||||||
|
assertEquals("", getText());
|
||||||
|
setValue(0xff);
|
||||||
|
assertEquals("0xff", getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedHexSetText() {
|
||||||
|
setFormat(U_HEX);
|
||||||
|
|
||||||
|
setText("0x12");
|
||||||
|
assertEquals(18, getValue());
|
||||||
|
setText("-0x1");
|
||||||
|
assertEquals(18, getValue()); // didn't change the setText was ignored
|
||||||
|
setText("0xff");
|
||||||
|
assertEquals(255, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedHexTypeText() {
|
||||||
|
setFormat(U_HEX);
|
||||||
|
typeText("mnp0x12");
|
||||||
|
setText("0x13");
|
||||||
|
assertEquals(19, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testunSignedHexMin() {
|
||||||
|
setFormat(U_HEX);
|
||||||
|
setBitSize(8);
|
||||||
|
|
||||||
|
typeText("-0x8");
|
||||||
|
assertEquals("0x8", getText());
|
||||||
|
setText("");
|
||||||
|
typeText("0x0");
|
||||||
|
assertEquals("0x0", getText());
|
||||||
|
assertEquals(0, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testunSignedHexMax() {
|
||||||
|
setFormat(U_HEX);
|
||||||
|
setBitSize(8);
|
||||||
|
|
||||||
|
typeText("0xff");
|
||||||
|
assertEquals("0xff", getText());
|
||||||
|
assertEquals(255, getValue());
|
||||||
|
setText("");
|
||||||
|
typeText("0x10");
|
||||||
|
assertEquals("0x10", getText());
|
||||||
|
setText("0x10");
|
||||||
|
typeText("0");
|
||||||
|
assertEquals("0x10", getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedHexConvertsUnsignedValueToNegative() {
|
||||||
|
setFormat(U_HEX);
|
||||||
|
setBitSize(8);
|
||||||
|
setValue(-1);
|
||||||
|
assertEquals("0xff", getText());
|
||||||
|
assertEquals(255, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedDecimalSetText() {
|
||||||
|
setFormat(DEC);
|
||||||
|
setText("12");
|
||||||
|
assertEquals(12, getValue());
|
||||||
|
setText("-4");
|
||||||
|
assertEquals(-4, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedDecimalTypeText() {
|
||||||
|
setFormat(DEC);
|
||||||
|
typeText("mnp12");
|
||||||
|
setText("12");
|
||||||
|
assertEquals(12, getValue());
|
||||||
|
setText("");
|
||||||
|
typeText("-5");
|
||||||
|
assertEquals(-5, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedDecimalMin() {
|
||||||
|
setFormat(DEC);
|
||||||
|
setBitSize(8);
|
||||||
|
|
||||||
|
typeText("-128");
|
||||||
|
assertEquals("-128", getText());
|
||||||
|
setText("");
|
||||||
|
typeText("-129");
|
||||||
|
assertEquals("-12", getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedDecimalMax() {
|
||||||
|
setFormat(DEC);
|
||||||
|
setBitSize(8);
|
||||||
|
|
||||||
|
typeText("127");
|
||||||
|
assertEquals("127", getText());
|
||||||
|
assertEquals(127, getValue());
|
||||||
|
typeText("3");
|
||||||
|
assertEquals("127", getText()); // 7f (127) is the max allowed for 8 bits
|
||||||
|
setText("");
|
||||||
|
typeText("128");
|
||||||
|
assertEquals("12", getText());
|
||||||
|
assertEquals(12, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedDecimalConvertsUnsignedValueToNegative() {
|
||||||
|
setFormat(DEC);
|
||||||
|
setBitSize(8);
|
||||||
|
|
||||||
|
setValue(255);
|
||||||
|
assertEquals(-1, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedDecimalSetValue() {
|
||||||
|
setFormat(U_DEC);
|
||||||
|
setValue(25);
|
||||||
|
assertEquals("25", getText());
|
||||||
|
setValue(-130);
|
||||||
|
assertEquals("", getText());
|
||||||
|
setValue(255);
|
||||||
|
assertEquals("255", getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedDecimalSetText() {
|
||||||
|
setFormat(U_DEC);
|
||||||
|
|
||||||
|
setText("12");
|
||||||
|
assertEquals(12, getValue());
|
||||||
|
setText("-0x1");
|
||||||
|
assertEquals(12, getValue()); // didn't change the setText was ignored
|
||||||
|
setText("255");
|
||||||
|
assertEquals(255, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedDecimalTypeText() {
|
||||||
|
setFormat(U_DEC);
|
||||||
|
typeText("mnp12");
|
||||||
|
setText("12");
|
||||||
|
assertEquals(12, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnSignedDecimalMin() {
|
||||||
|
setFormat(U_DEC);
|
||||||
|
setBitSize(8);
|
||||||
|
|
||||||
|
typeText("-8");
|
||||||
|
assertEquals("8", getText());
|
||||||
|
setText("");
|
||||||
|
typeText("0");
|
||||||
|
assertEquals("0", getText());
|
||||||
|
assertEquals(0, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedDecimalMax() {
|
||||||
|
setFormat(U_DEC);
|
||||||
|
setBitSize(8);
|
||||||
|
|
||||||
|
typeText("255");
|
||||||
|
assertEquals("255", getText());
|
||||||
|
assertEquals(255, getValue());
|
||||||
|
setText("");
|
||||||
|
typeText("256");
|
||||||
|
assertEquals("25", getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedDecimalConvertsUnsignedValueToNegative() {
|
||||||
|
setFormat(U_DEC);
|
||||||
|
setBitSize(8);
|
||||||
|
setValue(-1);
|
||||||
|
assertEquals("255", getText());
|
||||||
|
assertEquals(255, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedOctSetValue() {
|
||||||
|
setFormat(U_OCT);
|
||||||
|
setValue(25);
|
||||||
|
assertEquals("0O31", getText());
|
||||||
|
setValue(-1);
|
||||||
|
assertEquals("0O377", getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedOctSetText() {
|
||||||
|
setFormat(U_OCT);
|
||||||
|
setText("0O12");
|
||||||
|
assertEquals(10, getValue());
|
||||||
|
setText("");
|
||||||
|
assertEquals(0, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedOctTypeText() {
|
||||||
|
setFormat(U_OCT);
|
||||||
|
typeText("mnp0O12");
|
||||||
|
assertEquals(10, getValue());
|
||||||
|
setText("");
|
||||||
|
typeText("-0O5"); // "-" will be ignored
|
||||||
|
assertEquals(5, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedBinarySetValue() {
|
||||||
|
setFormat(U_BIN);
|
||||||
|
setValue(0x25);
|
||||||
|
assertEquals("0b100101", getText());
|
||||||
|
setValue(-1);
|
||||||
|
assertEquals("0b11111111", getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsignedBinarySetText() {
|
||||||
|
setFormat(U_BIN);
|
||||||
|
setText("0b101");
|
||||||
|
assertEquals(5, getValue());
|
||||||
|
setText("");
|
||||||
|
assertEquals(0, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignedBinaryTypeText() {
|
||||||
|
setFormat(U_BIN);
|
||||||
|
typeText("mnp0b1210"); // the mnp and 2 are ignored
|
||||||
|
assertEquals("0b110", getText());
|
||||||
|
assertEquals(6, getValue());
|
||||||
|
setText("");
|
||||||
|
typeText("-0b101"); // "-" will be ignored
|
||||||
|
assertEquals(5, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangingBitSizeSigned() {
|
||||||
|
setFormat(DEC);
|
||||||
|
setBitSize(8);
|
||||||
|
setValue(25);
|
||||||
|
assertEquals("25", getText());
|
||||||
|
assertEquals(BigInteger.valueOf(-128), getMinValue());
|
||||||
|
assertEquals(BigInteger.valueOf(127), getMaxValue());
|
||||||
|
|
||||||
|
setBitSize(16);
|
||||||
|
assertEquals("25", getText());
|
||||||
|
assertEquals(BigInteger.valueOf(-32768), getMinValue());
|
||||||
|
assertEquals(BigInteger.valueOf(32767), getMaxValue());
|
||||||
|
|
||||||
|
setValue(32000);
|
||||||
|
assertEquals("32000", getText());
|
||||||
|
setBitSize(8);
|
||||||
|
assertEquals(BigInteger.valueOf(-128), getMinValue());
|
||||||
|
assertEquals(BigInteger.valueOf(127), getMaxValue());
|
||||||
|
assertEquals("", getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangingBitSizeUnsigned() {
|
||||||
|
setFormat(U_DEC);
|
||||||
|
setBitSize(8);
|
||||||
|
setValue(25);
|
||||||
|
assertEquals("25", getText());
|
||||||
|
assertEquals(BigInteger.valueOf(0), getMinValue());
|
||||||
|
assertEquals(BigInteger.valueOf(255), getMaxValue());
|
||||||
|
|
||||||
|
setBitSize(16);
|
||||||
|
assertEquals("25", getText());
|
||||||
|
assertEquals(BigInteger.valueOf(0), getMinValue());
|
||||||
|
assertEquals(BigInteger.valueOf(65535), getMaxValue());
|
||||||
|
|
||||||
|
setValue(65000);
|
||||||
|
assertEquals("65000", getText());
|
||||||
|
setBitSize(8);
|
||||||
|
assertEquals(BigInteger.valueOf(0), getMinValue());
|
||||||
|
assertEquals(BigInteger.valueOf(255), getMaxValue());
|
||||||
|
assertEquals("", getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangingFromSignedToUnsignedUpperBitNotSet() {
|
||||||
|
setFormat(DEC);
|
||||||
|
setBitSize(8);
|
||||||
|
setValue(8);
|
||||||
|
setFormat(U_DEC);
|
||||||
|
assertEquals(8, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangingFromSignedToUnsingedUpperBitSet() {
|
||||||
|
setFormat(DEC);
|
||||||
|
setValue(-1);
|
||||||
|
assertEquals(-1, getValue());
|
||||||
|
setFormat(U_DEC);
|
||||||
|
assertEquals(255, getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangingFromUnsignedToSignedUpperBitNotSet() {
|
||||||
|
setFormat(U_DEC);
|
||||||
|
setBitSize(8);
|
||||||
|
setValue(8);
|
||||||
|
setFormat(DEC);
|
||||||
|
assertEquals(8, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangingFromUnsignedToSingedUpperBitSet() {
|
||||||
|
setFormat(U_DEC);
|
||||||
|
setValue(255);
|
||||||
|
assertEquals(255, getValue());
|
||||||
|
setFormat(DEC);
|
||||||
|
assertEquals(-1, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setBitSize(int value) {
|
||||||
|
runSwing(() -> field.setBitSize(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,177 +15,156 @@
|
|||||||
*/
|
*/
|
||||||
package docking.widgets.textfield;
|
package docking.widgets.textfield;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static docking.widgets.textfield.integer.IntegerFormat.*;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
|
||||||
|
|
||||||
import javax.swing.JFrame;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.UIManager;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import docking.test.AbstractDockingTest;
|
public class IntegerTextFieldTest extends AbstractIntegerTextFieldTest<IntegerTextField> {
|
||||||
|
|
||||||
public class IntegerTextFieldTest extends AbstractDockingTest {
|
@Override
|
||||||
|
protected IntegerTextField createField() {
|
||||||
private JFrame frame;
|
return new IntegerTextField(10);
|
||||||
private IntegerTextField field;
|
|
||||||
private JTextField textField;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
|
||||||
field = new IntegerTextField(10);
|
|
||||||
field.setShowNumberMode(true);
|
|
||||||
textField = (JTextField) field.getComponent();
|
|
||||||
frame = new JFrame("Test");
|
|
||||||
frame.getContentPane().add(field.getComponent());
|
|
||||||
frame.pack();
|
|
||||||
frame.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() throws Exception {
|
|
||||||
frame.setVisible(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultState() {
|
public void testDefaultState() {
|
||||||
assertNull(field.getValue());// no value
|
assertNull(getBigIntegerValue());// no value
|
||||||
assertEquals(0, field.getIntValue());// the "int value" return for null is 0
|
assertEquals(0, getIntValue()); // the "int value" return for null is 0
|
||||||
assertEquals(0, field.getLongValue());
|
assertEquals(0, getValue());
|
||||||
assertTrue(!field.isHexMode());
|
assertEquals(DEC, getFormat());
|
||||||
assertNull(field.getMaxValue());
|
assertNull(getMaxValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTypeValidDecimalNumber() {
|
public void testTypeValidDecimalNumber() {
|
||||||
triggerText(textField, "123");
|
typeText("123");
|
||||||
assertEquals(123, field.getIntValue());
|
assertEquals(123, getIntValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTypeValidHexNumber() {
|
public void testTypeValidHexNumber() {
|
||||||
triggerText(textField, "0x2abcdef");
|
typeText("0x2abcdef");
|
||||||
assertEquals(0x2abcdef, field.getIntValue());
|
assertEquals(0x2abcdef, getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInvalidCharsIgnored() {
|
public void testInvalidCharsIgnored() {
|
||||||
triggerText(textField, "123ghijklmnopqrstuvwxyz4");
|
typeText("123ghijklmnopqrstuvwxyz4");
|
||||||
assertEquals(1234, field.getIntValue());
|
assertEquals(1234, getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHexCharsIgnoredInDecimalMode() {
|
public void testHexCharsIgnoredInDecimalMode() {
|
||||||
assertTrue(!field.isHexMode());
|
setFormat(HEX);
|
||||||
triggerText(textField, "123ghijklmnopqrstuvwxyz4");
|
typeText("123ghijklmnopqrstuvwxyz4");
|
||||||
assertEquals(1234, field.getIntValue());
|
assertEquals(1234, getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testXchangesHexMode() {
|
public void testXchangesHexMode() {
|
||||||
assertTrue(!field.isHexMode());
|
assertEquals(DEC, getFormat());
|
||||||
triggerText(textField, "0");
|
typeText("0");
|
||||||
assertTrue(!field.isHexMode());
|
assertEquals(DEC, getFormat());
|
||||||
triggerText(textField, "x");
|
typeText("x");
|
||||||
assertTrue(field.isHexMode());
|
assertEquals(HEX, getFormat());
|
||||||
triggerBackspace(textField);
|
triggerBackspace(textField);
|
||||||
assertTrue(!field.isHexMode());
|
assertEquals(HEX, getFormat());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHexModeWithoutPrefix() {
|
public void testHexModeWithoutPrefix() {
|
||||||
triggerText(textField, "abc");// not allowed when using hex prefix, so expect empty
|
setFormat(HEX);
|
||||||
assertEquals(null, field.getValue());
|
typeText("a");
|
||||||
|
assertEquals(null, getBigIntegerValue());
|
||||||
|
|
||||||
field.setAllowsHexPrefix(false);
|
setUsePrefix(false);
|
||||||
field.setHexMode();
|
typeText("abc");
|
||||||
triggerText(textField, "abc");
|
assertEquals(0xabc, getValue());
|
||||||
assertEquals(0xabc, field.getIntValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNegative() {
|
public void testNegative() {
|
||||||
triggerText(textField, "-123");
|
typeText("-123");
|
||||||
assertEquals(-123, field.getIntValue());
|
assertEquals(-123, getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNegativeHex() {
|
public void testNegativeHex() {
|
||||||
triggerText(textField, "-0xa");
|
typeText("-0xa");
|
||||||
assertEquals(-10, field.getIntValue());
|
assertEquals(-10, getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNegativeNotAllowed() {
|
public void testNegativeNotAllowed() {
|
||||||
field.setAllowNegativeValues(false);
|
setMinValue(BigInteger.ZERO);
|
||||||
triggerText(textField, "-123");
|
typeText("-123");
|
||||||
assertEquals(123, field.getIntValue());
|
assertEquals(123, getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetNegativeWithCurrentNegativeValue() {
|
public void testSetNegativeWithCurrentNegativeValue() {
|
||||||
field.setValue(-123);
|
setValue(-123);
|
||||||
field.setAllowNegativeValues(false);
|
setMinValue(BigInteger.ZERO);
|
||||||
assertEquals(null, field.getValue());
|
assertEquals(null, field.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMax() {
|
public void testMax() {
|
||||||
field.setMaxValue(BigInteger.valueOf(13l));
|
field.setMaxValue(BigInteger.valueOf(13l));
|
||||||
triggerText(textField, "12");
|
typeText("12");
|
||||||
assertEquals(12, field.getIntValue());
|
assertEquals(12, getValue());
|
||||||
|
|
||||||
field.setValue(null);
|
setText("");
|
||||||
triggerText(textField, "13");
|
typeText("13");
|
||||||
assertEquals(13, field.getIntValue());
|
assertEquals(13, getValue());
|
||||||
|
|
||||||
field.setValue(null);
|
setText("");
|
||||||
triggerText(textField, "14");// four should be ignored
|
typeText("14");// four should be ignored
|
||||||
assertEquals(1, field.getIntValue());
|
assertEquals(1, getValue());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetMaxToValueSmallerThanCurrent() {
|
public void testSetMaxToValueSmallerThanCurrent() {
|
||||||
field.setValue(500);
|
setValue(500);
|
||||||
field.setMaxValue(BigInteger.valueOf(400));
|
field.setMaxValue(BigInteger.valueOf(400));
|
||||||
assertEquals(400, field.getIntValue());
|
assertNull(field.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMinSetTo1() {
|
||||||
|
field.setMinValue(BigInteger.ONE);
|
||||||
|
setValue(0);
|
||||||
|
assertEquals(1, getValue());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMaxInHex() {
|
public void testMaxInHex() {
|
||||||
field.setMaxValue(BigInteger.valueOf(0xd));
|
field.setMaxValue(BigInteger.valueOf(0xd));
|
||||||
triggerText(textField, "0xc");
|
typeText("0xc");
|
||||||
assertEquals(12, field.getIntValue());
|
assertEquals(12, getValue());
|
||||||
|
|
||||||
field.setValue(null);
|
setText("");
|
||||||
triggerText(textField, "0xd");
|
typeText("0xd");
|
||||||
assertEquals(13, field.getIntValue());
|
assertEquals(13, getValue());
|
||||||
|
|
||||||
field.setValue(null);
|
setText("");
|
||||||
triggerText(textField, "0xe");// e should be ignored
|
typeText("0xe");// e should be ignored
|
||||||
assertEquals(0, field.getIntValue());
|
assertEquals(0, getValue());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSwitchingHexMode() {
|
public void testSwitchingHexMode() {
|
||||||
field.setValue(255);
|
setValue(255);
|
||||||
assertEquals("255", field.getText());
|
assertEquals("255", field.getText());
|
||||||
field.setHexMode();
|
setFormat(HEX);
|
||||||
assertEquals("0xff", field.getText());
|
assertEquals("0xff", field.getText());
|
||||||
field.setDecimalMode();
|
setFormat(DEC);
|
||||||
assertEquals("255", field.getText());
|
assertEquals("255", field.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +173,7 @@ public class IntegerTextFieldTest extends AbstractDockingTest {
|
|||||||
TestChangeListener listener = new TestChangeListener();
|
TestChangeListener listener = new TestChangeListener();
|
||||||
field.addChangeListener(listener);
|
field.addChangeListener(listener);
|
||||||
|
|
||||||
triggerText(textField, "123");
|
typeText("123");
|
||||||
assertEquals(3, listener.count);
|
assertEquals(3, listener.count);
|
||||||
assertEquals(1, listener.values.get(0));
|
assertEquals(1, listener.values.get(0));
|
||||||
assertEquals(12, listener.values.get(1));
|
assertEquals(12, listener.values.get(1));
|
||||||
@@ -207,129 +186,149 @@ public class IntegerTextFieldTest extends AbstractDockingTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testChangeListenerAfterSwitchingModes() {
|
public void testChangeListenerAfterSwitchingModes() {
|
||||||
triggerText(textField, "123");
|
setFormat(DEC);
|
||||||
|
typeText("12");
|
||||||
|
|
||||||
TestChangeListener listener = new TestChangeListener();
|
TestChangeListener listener = new TestChangeListener();
|
||||||
field.addChangeListener(listener);
|
field.addChangeListener(listener);
|
||||||
|
|
||||||
setHexMode();
|
setFormat(HEX);
|
||||||
|
assertEquals("0xc", getText());
|
||||||
|
|
||||||
assertEquals(2, listener.count);
|
assertEquals(2, listener.count);
|
||||||
assertEquals(123, listener.values.get(1));
|
assertEquals(12, listener.values.get(1));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNegativeHexFromValue() {
|
public void testNegativeHexFromValue() {
|
||||||
field.setValue(-255);
|
setValue(-255);
|
||||||
setHexMode();
|
setFormat(HEX);
|
||||||
assertEquals("-0xff", field.getText());
|
assertEquals("-0xff", field.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNullValue() {
|
public void testNullValue() {
|
||||||
field.setValue(12);
|
setValue(12);
|
||||||
assertEquals("12", field.getText());
|
assertEquals("12", field.getText());
|
||||||
field.setValue(null);
|
setText("");
|
||||||
assertEquals("", field.getText());
|
assertEquals("", field.getText());
|
||||||
assertEquals(0, field.getIntValue());
|
assertEquals(0, getValue());
|
||||||
assertEquals(0l, field.getLongValue());
|
assertEquals(null, getBigIntegerValue());
|
||||||
assertEquals(null, field.getValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHexValueInDontRequireHexPrefixMode() {
|
public void testHexValueInDontRequireHexPrefixMode() {
|
||||||
field.setAllowsHexPrefix(false);
|
field.setUseNumberPrefix(false);
|
||||||
field.setHexMode();
|
field.setFormat(HEX);
|
||||||
field.setValue(255);
|
setValue(255);
|
||||||
assertEquals("ff", field.getText());
|
assertEquals("ff", field.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAutoModeSwitchingIsOffWhenPrefixNotUsed() {
|
||||||
|
field.setUseNumberPrefix(false);
|
||||||
|
field.setFormat(HEX);
|
||||||
|
typeText("15");
|
||||||
|
assertEquals(HEX, getFormat());
|
||||||
|
assertEquals(21, getValue());
|
||||||
|
field.setFormat(DEC);
|
||||||
|
field.setText("");
|
||||||
|
typeText("0x15");
|
||||||
|
assertEquals(DEC, getFormat());
|
||||||
|
assertEquals("015", getText());
|
||||||
|
assertEquals(15, getValue()); // the 0x should have been ignored
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetNotAllowNegativeModeWhileCurrentValueIsNegative() {
|
public void testSetNotAllowNegativeModeWhileCurrentValueIsNegative() {
|
||||||
field.setValue(-10);
|
setValue(-10);
|
||||||
field.setAllowNegativeValues(false);
|
setMinValue(BigInteger.ZERO);
|
||||||
assertEquals("", field.getText());
|
assertEquals("", field.getText());
|
||||||
assertEquals(0, field.getIntValue());
|
assertEquals(0, getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetLongValue() {
|
public void testSetLongValue() {
|
||||||
field.setValue(100L);
|
setValue(100L);
|
||||||
assertEquals(100L, field.getLongValue());
|
assertEquals(100L, field.getLongValue());
|
||||||
assertEquals(100, field.getIntValue());
|
assertEquals(100, getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSettingNegativeNumberWhenNegativesArentAllowed() {
|
public void testSettingNegativeNumberWhenNegativesArentAllowed() {
|
||||||
field.setValue(10);
|
setValue(10);
|
||||||
field.setAllowNegativeValues(false);
|
setMinValue(BigInteger.ZERO);
|
||||||
field.setValue(-10);
|
setValue(-10);
|
||||||
assertEquals("", field.getText());
|
assertEquals("", field.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUseHexPrefixUpdatesTextField() {
|
public void testUseHexPrefixUpdatesTextField() {
|
||||||
field.setAllowsHexPrefix(false);
|
field.setUseNumberPrefix(false);
|
||||||
field.setHexMode();
|
setFormat(HEX);
|
||||||
field.setValue(255);
|
setValue(255);
|
||||||
assertEquals("ff", field.getText());
|
assertEquals("ff", field.getText());
|
||||||
field.setAllowsHexPrefix(true);
|
field.setUseNumberPrefix(true);
|
||||||
assertEquals("0xff", field.getText());
|
assertEquals("0xff", field.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPastingBadText() {
|
public void testPastingBadText() {
|
||||||
field.setHexMode();
|
setFormat(HEX);
|
||||||
field.setValue(0);
|
setValue(0);
|
||||||
assertFalse(field.setText("asdf 0x azzz"));
|
assertFalse(field.setText("asdf 0x azzz"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetText() {
|
public void testSetText() {
|
||||||
field.setHexMode();
|
setFormat(HEX);
|
||||||
field.setValue(0);
|
setValue(0);
|
||||||
assertTrue(field.setText("0x15"));
|
assertTrue(field.setText("0x15"));
|
||||||
assertEquals(0x15, field.getIntValue());
|
assertEquals(0x15, getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetTextWithInvalidValue() {
|
public void testSetTextWithInvalidValue() {
|
||||||
field.setHexMode();
|
setFormat(HEX);
|
||||||
field.setValue(0);
|
setValue(0);
|
||||||
assertFalse(field.setText("bad value"));
|
assertFalse(field.setText("bad value"));
|
||||||
assertEquals(0, field.getIntValue());
|
assertEquals(0, getValue());
|
||||||
assertTrue(field.isHexMode());
|
assertEquals(HEX, getFormat());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSucessfulSetTextChangesHexMode() {
|
public void testSucessfulSetTextChangesHexMode() {
|
||||||
field.setHexMode();
|
setFormat(HEX);
|
||||||
field.setValue(0);
|
setValue(0);
|
||||||
assertTrue(field.setText("33"));
|
assertTrue(field.setText("33"));
|
||||||
assertEquals(33, field.getIntValue());
|
assertEquals(33, getValue());
|
||||||
assertFalse(field.isHexMode());
|
assertEquals(DEC, getFormat());
|
||||||
|
|
||||||
assertTrue(field.setText("0x33"));
|
assertTrue(field.setText("0x33"));
|
||||||
assertEquals(0x33, field.getIntValue());
|
assertEquals(0x33, getValue());
|
||||||
assertTrue(field.isHexMode());
|
assertEquals(HEX, getFormat());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setHexMode() {
|
@Test
|
||||||
runSwing(() -> field.setHexMode());
|
public void testMinValueOfOneDecimalFormat() {
|
||||||
waitForSwing();
|
setFormat(DEC);
|
||||||
|
field.setMinValue(BigInteger.ONE);
|
||||||
|
typeText("0");
|
||||||
|
assertEquals("", field.getText());
|
||||||
|
typeText("1");
|
||||||
|
assertEquals("1", field.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestChangeListener implements ChangeListener {
|
@Test
|
||||||
volatile int count;
|
public void testMinValueOfOneHexFormat() {
|
||||||
private AtomicIntegerArray values = new AtomicIntegerArray(10);
|
setFormat(HEX);
|
||||||
|
field.setMinValue(BigInteger.ONE);
|
||||||
@Override
|
typeText("0x1");
|
||||||
public void stateChanged(ChangeEvent e) {
|
assertEquals("0x1", field.getText());
|
||||||
values.set(count++, field.getIntValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setMinValue(BigInteger minValue) {
|
||||||
|
runSwing(() -> field.setMinValue(minValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||