GP-3049 Improved Register Manager provider
@@ -24,6 +24,7 @@ import javax.swing.*;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
|
||||
public class HexBigIntegerTableCellEditor extends AbstractCellEditor implements TableCellEditor {
|
||||
protected IntegerTextField input;
|
||||
@@ -48,11 +49,11 @@ public class HexBigIntegerTableCellEditor extends AbstractCellEditor implements
|
||||
int row, int column) {
|
||||
input = new IntegerTextField();
|
||||
input.getComponent().setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
|
||||
input.setAllowNegativeValues(true);
|
||||
input.setHexMode();
|
||||
input.setAllowsHexPrefix(false);
|
||||
input.setMinValue(null); // allow negative numbers
|
||||
input.setFormat(IntegerFormat.HEX);
|
||||
input.setUseNumberPrefix(false);
|
||||
input.setShowNumberMode(true);
|
||||
input.setHorizontalAlignment(JTextField.RIGHT);
|
||||
input.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
|
||||
if (value != null) {
|
||||
input.setValue((BigInteger) value);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package ghidra.machinelearning.functionfinding;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.LongStream;
|
||||
@@ -438,7 +439,7 @@ public class FunctionStartRFParamsDialog extends ReusableDialogComponentProvider
|
||||
minUndefRangeLabel.setToolTipText(MIN_UNDEFINED_RANGE_SIZE_TIP);
|
||||
funcDataPanel.add(minUndefRangeLabel);
|
||||
minUndefRangeField = new IntegerTextField();
|
||||
minUndefRangeField.setAllowNegativeValues(false);
|
||||
minUndefRangeField.setMinValue(BigInteger.ZERO);
|
||||
minUndefRangeField.setValue(plugin.getMinUndefinedRangeSize());
|
||||
funcDataPanel.add(minUndefRangeField.getComponent());
|
||||
|
||||
|
||||
@@ -127,8 +127,7 @@ public class BSimSearchDialog extends AbstractBSimSearchDialog {
|
||||
maxResultsField = new IntegerTextField(10);
|
||||
maxResultsField.setValue(100);
|
||||
maxResultsField.setMinValue(BigInteger.ONE);
|
||||
maxResultsField.setAllowNegativeValues(false);
|
||||
maxResultsField.setAllowsHexPrefix(false);
|
||||
maxResultsField.setUseNumberPrefix(false);
|
||||
maxResultsField.setShowNumberMode(false);
|
||||
|
||||
JComponent maxResultsComponent = maxResultsField.getComponent();
|
||||
|
||||
@@ -71,24 +71,27 @@
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<H3><A name="tool_buttons"></A>Tool Buttons</H3>
|
||||
</BLOCKQUOTE>
|
||||
<H3><A name="tool_buttons"></A>Toolbar Buttons</H3>
|
||||
|
||||
<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
|
||||
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
|
||||
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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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 |
@@ -31,6 +31,7 @@ import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.dialogs.StringChoices;
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import ghidra.docking.settings.*;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.util.BigEndianDataConverter;
|
||||
@@ -734,7 +735,8 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
|
||||
}
|
||||
|
||||
private void updateHexMode() {
|
||||
intHexModeMap.put(rowobject.definition.getName(), intTextField.isHexMode());
|
||||
intHexModeMap.put(rowobject.definition.getName(),
|
||||
intTextField.getFormat() == IntegerFormat.HEX);
|
||||
}
|
||||
|
||||
private Number getNumber() {
|
||||
@@ -805,14 +807,14 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
|
||||
mode = NUMBER;
|
||||
NumberSettingsDefinition def = (NumberSettingsDefinition) rowobject.definition;
|
||||
if (def.isHexModePreferred() || isHexModeEnabled(def)) {
|
||||
intTextField.setHexMode();
|
||||
intTextField.setFormat(IntegerFormat.HEX);
|
||||
}
|
||||
else {
|
||||
intTextField.setDecimalMode();
|
||||
intTextField.setFormat(IntegerFormat.DEC);
|
||||
}
|
||||
|
||||
intTextField.setMaxValue(def.getMaxValue());
|
||||
intTextField.setAllowNegativeValues(def.allowNegativeValue());
|
||||
intTextField.setMinValue(def.allowNegativeValue() ? null : BigInteger.ZERO);
|
||||
|
||||
if (value == null) {
|
||||
intTextField.setValue(null);
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import docking.widgets.DropDownSelectionTextField;
|
||||
import docking.widgets.table.FocusableEditor;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.program.model.address.Address;
|
||||
@@ -163,7 +164,7 @@ class VarnodeLocationCellEditor extends AbstractCellEditor
|
||||
|
||||
private Component createStackOffsetEditor(VarnodeInfo varnode) {
|
||||
offsetInput = new IntegerTextField();
|
||||
offsetInput.setHexMode();
|
||||
offsetInput.setFormat(IntegerFormat.HEX);
|
||||
Address address = varnode.getAddress();
|
||||
if (address != null) {
|
||||
offsetInput.setValue(address.getOffset());
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -24,6 +24,7 @@ import javax.swing.*;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
|
||||
class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEditor {
|
||||
@@ -56,8 +57,8 @@ class VarnodeSizeCellEditor extends AbstractCellEditor implements TableCellEdito
|
||||
int row, int column) {
|
||||
|
||||
input = new IntegerTextField();
|
||||
input.setAllowNegativeValues(false);
|
||||
input.setDecimalMode();
|
||||
input.setMinValue(BigInteger.ZERO);
|
||||
input.setFormat(IntegerFormat.DEC);
|
||||
Integer size = (Integer) value;
|
||||
if (size != null) {
|
||||
input.setValue(size.longValue());
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package ghidra.app.plugin.core.memory;
|
||||
|
||||
import java.awt.*;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -28,6 +29,7 @@ import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import ghidra.app.plugin.core.memory.AddBlockModel.InitializedType;
|
||||
import ghidra.app.plugin.core.misc.RegisterField;
|
||||
import ghidra.app.util.AddressInput;
|
||||
@@ -474,16 +476,16 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
||||
JPanel schemePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
|
||||
schemeDestByteCountField = new IntegerTextField(4, 1);
|
||||
schemeDestByteCountField.setAllowNegativeValues(false);
|
||||
schemeDestByteCountField.setAllowsHexPrefix(false);
|
||||
schemeDestByteCountField.setDecimalMode();
|
||||
schemeDestByteCountField.setMinValue(BigInteger.ZERO);
|
||||
schemeDestByteCountField.setUseNumberPrefix(false);
|
||||
schemeDestByteCountField.setFormat(IntegerFormat.DEC);
|
||||
schemeDestByteCountField.addChangeListener(ev -> schemeDestByteCountChanged());
|
||||
schemeDestByteCountField.setAccessibleName("Mapping Ratio: Destination Size");
|
||||
|
||||
schemeSrcByteCountField = new IntegerTextField(4, 1);
|
||||
schemeSrcByteCountField.setAllowNegativeValues(false);
|
||||
schemeSrcByteCountField.setAllowsHexPrefix(false);
|
||||
schemeSrcByteCountField.setDecimalMode();
|
||||
schemeSrcByteCountField.setMinValue(BigInteger.ZERO);
|
||||
schemeSrcByteCountField.setUseNumberPrefix(false);
|
||||
schemeSrcByteCountField.setFormat(IntegerFormat.DEC);
|
||||
schemeSrcByteCountField.addChangeListener(ev -> schemeSrcByteCountChanged());
|
||||
schemeSrcByteCountField.setAccessibleName("Mapping Ratio: Source Size");
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ import javax.swing.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.textfield.FixedSizeIntegerTextField;
|
||||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.app.util.bean.FixedBitSizeValueField;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Register;
|
||||
@@ -36,7 +36,7 @@ class EditRegisterValueDialog extends DialogComponentProvider {
|
||||
|
||||
private AddressInput startAddrField;
|
||||
private AddressInput endAddrField;
|
||||
private FixedBitSizeValueField registerValueField;
|
||||
private FixedSizeIntegerTextField registerValueField;
|
||||
private boolean wasCancelled = true;
|
||||
|
||||
EditRegisterValueDialog(Register register, Address start, Address end, BigInteger value,
|
||||
@@ -61,10 +61,16 @@ class EditRegisterValueDialog extends DialogComponentProvider {
|
||||
startAddrField = new AddressInput(program, addressChangeListener);
|
||||
endAddrField = new AddressInput(program, addressChangeListener);
|
||||
|
||||
registerValueField = new FixedBitSizeValueField(register.getBitLength(), true, false);
|
||||
registerValueField.getAccessibleContext().setAccessibleName("Register Value");
|
||||
startAddrField.setAddress(start);
|
||||
endAddrField.setAddress(end);
|
||||
registerValueField = new FixedSizeIntegerTextField(16, register.getBitLength());
|
||||
registerValueField.getComponent()
|
||||
.getAccessibleContext()
|
||||
.setAccessibleName("Register Value");
|
||||
if (start != null) {
|
||||
startAddrField.setAddress(start);
|
||||
}
|
||||
if (end != null) {
|
||||
endAddrField.setAddress(end);
|
||||
}
|
||||
registerValueField.setValue(value);
|
||||
|
||||
JPanel panel = new JPanel(new PairLayout(5, 1));
|
||||
@@ -77,7 +83,7 @@ class EditRegisterValueDialog extends DialogComponentProvider {
|
||||
panel.add(new GLabel("End Address:"));
|
||||
panel.add(endAddrField);
|
||||
panel.add(new GLabel("Value:"));
|
||||
panel.add(registerValueField);
|
||||
panel.add(registerValueField.getComponent());
|
||||
panel.getAccessibleContext().setAccessibleName("Edit Register Value");
|
||||
return panel;
|
||||
}
|
||||
|
||||
@@ -19,14 +19,19 @@ import static ghidra.framework.model.DomainObjectEvent.*;
|
||||
import static ghidra.program.util.ProgramEvent.*;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
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 ghidra.app.cmd.register.SetRegisterCmd;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||
import ghidra.framework.model.DomainObjectListener;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
@@ -52,16 +57,13 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
private RegisterTree tree;
|
||||
private RegisterValuesPanel values;
|
||||
|
||||
private DockingAction deleteRegisterValuesAction;
|
||||
|
||||
private DockingAction selectRegisterValuesAction;
|
||||
private ToggleDockingAction showDefaultRegisterValuesAction;
|
||||
private ToggleDockingAction showDefaultValuesAction;
|
||||
private DomainObjectListener domainObjectListener;
|
||||
private ToggleDockingAction filterRegistersAction;
|
||||
private SwingUpdateManager updateMgr;
|
||||
|
||||
private ToggleDockingAction followLocationToggleAction;
|
||||
protected boolean followLocation;
|
||||
private boolean followLocation = false;
|
||||
private Address currentAddress;
|
||||
|
||||
RegisterManagerProvider(PluginTool tool, String owner) {
|
||||
super(tool, "Register Manager", owner, ProgramActionContext.class);
|
||||
@@ -86,9 +88,7 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
|
||||
tree.addGTreeSelectionListener(e -> showRegister());
|
||||
values.getTable().getSelectionModel().addListSelectionListener(e -> {
|
||||
JTable table = values.getTable();
|
||||
deleteRegisterValuesAction.setEnabled(table.getSelectedRowCount() > 0);
|
||||
selectRegisterValuesAction.setEnabled(table.getSelectedRowCount() > 0);
|
||||
contextChanged();
|
||||
});
|
||||
|
||||
tree.setAccessibleNamePrefix("Register Manager");
|
||||
@@ -100,80 +100,91 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
|
||||
void createActions() {
|
||||
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()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
values.selectedRanges();
|
||||
}
|
||||
};
|
||||
selectRegisterValuesAction.setEnabled(false);
|
||||
selectRegisterValuesAction.setToolBarData(new ToolBarData(SELECT_REGISTER_VALUES_ICON));
|
||||
selectRegisterValuesAction
|
||||
.setPopupMenuData(new MenuData(new String[] { "Select Register Value Ranges" }));
|
||||
selectRegisterValuesAction.setDescription("Select Register Value Ranges");
|
||||
selectRegisterValuesAction.setHelpLocation(helpLocation);
|
||||
tool.addLocalAction(this, selectRegisterValuesAction);
|
||||
new ActionBuilder("Add Register Value Range", getOwner())
|
||||
.toolBarIcon(Icons.ADD_ICON)
|
||||
.popupMenuPath("Add Value Range")
|
||||
.popupMenuIcon(Icons.ADD_ICON)
|
||||
.description("Add a new register value range")
|
||||
.helpLocation(helpLocation)
|
||||
.withContext(RegisterManagerContext.class)
|
||||
.enabledWhen(c -> c.getSelectedRegister() != null)
|
||||
.onAction(c -> addRange(c.getSelectedRegister()))
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
showDefaultRegisterValuesAction =
|
||||
new ToggleDockingAction("Show default register values", getOwner()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
values.setShowDefaultValues(showDefaultRegisterValuesAction.isSelected());
|
||||
}
|
||||
};
|
||||
showDefaultRegisterValuesAction.setSelected(false);
|
||||
showDefaultRegisterValuesAction
|
||||
.setDescription("Toggles showing of default register values");
|
||||
showDefaultRegisterValuesAction
|
||||
.setMenuBarData(new MenuData(new String[] { "Show Default Values" }));
|
||||
showDefaultRegisterValuesAction
|
||||
.setHelpLocation(new HelpLocation("RegisterPlugin", "menu_actions"));
|
||||
tool.addLocalAction(this, showDefaultRegisterValuesAction);
|
||||
new ActionBuilder("Delete Register Value Ranges", getOwner())
|
||||
.toolBarIcon(DELETE_REGISTER_VALUES_ICON)
|
||||
.popupMenuPath("Delete Register Value Ranges")
|
||||
.popupMenuIcon(DELETE_REGISTER_VALUES_ICON)
|
||||
.description("Delete selected register value ranges")
|
||||
.helpLocation(helpLocation)
|
||||
.withContext(RegisterManagerContext.class)
|
||||
.enabledWhen(c -> c.hasSelectedRegisterValueRanges())
|
||||
.onAction(c -> values.deleteSelectedRanges())
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
filterRegistersAction = new ToggleDockingAction("Filter Registers", getOwner()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
tree.setFiltered(filterRegistersAction.isSelected());
|
||||
}
|
||||
};
|
||||
filterRegistersAction.setSelected(false);
|
||||
filterRegistersAction.setDescription(
|
||||
"Toggles filtering out registers that don't have values or default values.");
|
||||
filterRegistersAction.setToolBarData(new ToolBarData(FILTER_ICON));
|
||||
filterRegistersAction.setHelpLocation(helpLocation);
|
||||
tool.addLocalAction(this, filterRegistersAction);
|
||||
new ActionBuilder("Select Register Value Ranges", getOwner())
|
||||
.toolBarIcon(SELECT_REGISTER_VALUES_ICON)
|
||||
.popupMenuPath("Select Register Value Ranges")
|
||||
.popupMenuIcon(SELECT_REGISTER_VALUES_ICON)
|
||||
.helpLocation(helpLocation)
|
||||
.description("Create a program selection from the selected rows")
|
||||
.withContext(RegisterManagerContext.class)
|
||||
.enabledWhen(c -> c.hasSelectedRegisterValueRanges())
|
||||
.onAction(c -> values.selectRanges())
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
followLocationToggleAction =
|
||||
new ToggleDockingAction("Follow location changes", getOwner()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
followLocation = followLocationToggleAction.isSelected();
|
||||
}
|
||||
};
|
||||
followLocationToggleAction.setEnabled(true);
|
||||
followLocationToggleAction.setHelpLocation(helpLocation);
|
||||
followLocationToggleAction.setToolBarData(new ToolBarData(RECV_LOCATION_ICON, "NavAction"));
|
||||
tool.addLocalAction(this, followLocationToggleAction);
|
||||
showDefaultValuesAction =
|
||||
new ToggleActionBuilder("Show Default Register Values", getOwner())
|
||||
.menuPath("Show Default Values")
|
||||
.description("Toggles showing of default register values")
|
||||
.helpLocation(helpLocation)
|
||||
.selected(false)
|
||||
.onAction(c -> updateShowingDefaultRegisterValues())
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
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() {
|
||||
Register register = tree.getSelectedRegister();
|
||||
values.setRegister(register);
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -191,7 +202,8 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
return new ProgramActionContext(this, program);
|
||||
return new RegisterManagerContext(program, tree.getSelectedRegister(),
|
||||
values.hasSelectedRows());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -245,6 +257,7 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
public void setLocation(Register register, Address address) {
|
||||
currentAddress = address;
|
||||
if (!followLocation) {
|
||||
return;
|
||||
}
|
||||
@@ -254,4 +267,23 @@ public class RegisterManagerProvider extends ComponentProviderAdapter {
|
||||
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
|
||||
protected void locationChanged(ProgramLocation loc) {
|
||||
if (loc instanceof RegisterTransitionFieldLocation) {
|
||||
RegisterTransitionFieldLocation regLoc = (RegisterTransitionFieldLocation) loc;
|
||||
if (loc instanceof RegisterTransitionFieldLocation regLoc) {
|
||||
Register reg = regLoc.getRegister();
|
||||
if (reg != null) {
|
||||
registerMgrProvider.setLocation(reg, regLoc.getAddress());
|
||||
}
|
||||
}
|
||||
else if (loc instanceof RegisterFieldLocation) {
|
||||
RegisterFieldLocation regLoc = (RegisterFieldLocation) loc;
|
||||
else if (loc instanceof RegisterFieldLocation regLoc) {
|
||||
Register reg = regLoc.getRegister();
|
||||
if (reg != null) {
|
||||
registerMgrProvider.setLocation(reg, regLoc.getAddress());
|
||||
|
||||
@@ -83,6 +83,10 @@ class RegisterValuesPanel extends JPanel {
|
||||
|
||||
}
|
||||
|
||||
boolean hasSelectedRows() {
|
||||
return table.getSelectedRowCount() > 0;
|
||||
}
|
||||
|
||||
private void editRow(int row) {
|
||||
RegisterValueRange range = model.values.get(row);
|
||||
Address start = range.getStartAddress();
|
||||
@@ -275,7 +279,7 @@ class RegisterValuesPanel extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
void selectedRanges() {
|
||||
void selectRanges() {
|
||||
int[] rows = table.getSelectedRows();
|
||||
AddressSet set = new AddressSet();
|
||||
for (int element : rows) {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.register;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
@@ -28,19 +29,21 @@ import javax.swing.event.ChangeListener;
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.list.GListCellRenderer;
|
||||
import docking.widgets.textfield.FixedSizeIntegerTextField;
|
||||
import generic.theme.GThemeDefaults.Ids.Fonts;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.app.util.bean.FixedBitSizeValueField;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.layout.VariableHeightPairLayout;
|
||||
|
||||
public class SetRegisterValueDialog extends DialogComponentProvider {
|
||||
private JComboBox<RegisterWrapper> registerComboBox;
|
||||
private FixedBitSizeValueField registerValueField;
|
||||
private JList addressRangeList;
|
||||
private FixedSizeIntegerTextField registerValueField;
|
||||
private JList<String> addressRangeList;
|
||||
private BigInteger registerValue;
|
||||
private Register selectedRegister;
|
||||
private boolean useValueField;
|
||||
@@ -58,7 +61,7 @@ public class SetRegisterValueDialog extends DialogComponentProvider {
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
if (useValueField) {
|
||||
setFocusComponent(registerValueField.getTextComponent());
|
||||
setFocusComponent(registerValueField.getComponent());
|
||||
}
|
||||
setSelectedRegister(register);
|
||||
setAddressRanges(addrSet);
|
||||
@@ -77,79 +80,72 @@ public class SetRegisterValueDialog extends DialogComponentProvider {
|
||||
}
|
||||
|
||||
private JComponent buildWorkPanel(Register[] registers) {
|
||||
registerComboBox = new GComboBox<>(wrapRegisters(registers));
|
||||
registerValueField = new FixedBitSizeValueField(32, true, false);
|
||||
GLabel registerLabel = new GLabel("Register:");
|
||||
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() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
updateOkEnablement();
|
||||
}
|
||||
});
|
||||
return registerValueField.getComponent();
|
||||
}
|
||||
|
||||
private Component buildRegisterComboBox(Register[] registers) {
|
||||
registerComboBox = new GComboBox<>(wrapRegisters(registers));
|
||||
registerComboBox.setRenderer(new RegisterComboRenderer());
|
||||
|
||||
registerComboBox.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
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);
|
||||
Gui.registerFont(addressRangeList, Fonts.MONOSPACED);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(addressRangeList);
|
||||
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
Dimension d = scrollPane.getPreferredSize();
|
||||
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;
|
||||
|
||||
return scrollPane;
|
||||
}
|
||||
|
||||
private void registerChanged() {
|
||||
RegisterWrapper wrapper = (RegisterWrapper) registerComboBox.getSelectedItem();
|
||||
if (wrapper != null) {
|
||||
registerValueField.setBitSize(wrapper.register.getBitLength());
|
||||
int bitLength = wrapper.register.getBitLength();
|
||||
registerValueField.setBitSize(bitLength);
|
||||
updateOkEnablement();
|
||||
}
|
||||
updateValue();
|
||||
@@ -232,6 +228,21 @@ public class SetRegisterValueDialog extends DialogComponentProvider {
|
||||
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> {
|
||||
@@ -240,14 +251,24 @@ class RegisterWrapper implements Comparable<RegisterWrapper> {
|
||||
|
||||
RegisterWrapper(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();
|
||||
for (String alias : register.getAliases()) {
|
||||
buf.append(buf.length() == 0 ? "; " : ", ");
|
||||
buf.append(alias);
|
||||
buf.append(displayName);
|
||||
buf.append(" Aliases: ");
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -97,7 +97,7 @@ class SelectBlockDialog extends ReusableDialogComponentProvider {
|
||||
numberInputField = new IntegerTextField(10);
|
||||
numberInputField.getComponent().getAccessibleContext().setAccessibleName("Number Input");
|
||||
numberInputField.setMaxValue(BigInteger.valueOf(Integer.MAX_VALUE));
|
||||
numberInputField.setAllowNegativeValues(false);
|
||||
numberInputField.setMinValue(BigInteger.ZERO);
|
||||
main.add(numberInputField.getComponent());
|
||||
main.getAccessibleContext().setAccessibleName("Block");
|
||||
return main;
|
||||
|
||||
@@ -17,6 +17,7 @@ package ghidra.app.plugin.core.string;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -331,7 +332,7 @@ public class StringTableProvider extends ComponentProviderAdapter implements Dom
|
||||
|
||||
private Component buildOffsetPanel() {
|
||||
offsetField = new IntegerTextField(4, 0L);
|
||||
offsetField.setAllowNegativeValues(false);
|
||||
offsetField.setMinValue(BigInteger.ZERO);
|
||||
offsetField.addChangeListener(e -> updatePreview());
|
||||
|
||||
preview = new JTextField(5);
|
||||
|
||||
@@ -15,22 +15,22 @@
|
||||
*/
|
||||
package ghidra.app.util;
|
||||
|
||||
import static docking.widgets.textfield.integer.IntegerFormat.*;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.CardLayout;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
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 ghidra.program.model.address.*;
|
||||
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> LOADED_MEMORY_SPACES = s -> s.isLoadedMemorySpace();
|
||||
|
||||
private HexDecimalModeTextField textField;
|
||||
private MultiFormatTextField textField;
|
||||
private AddressSpaceField addressSpaceField;
|
||||
AddressEvaluator addressEvaluator;
|
||||
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.
|
||||
*/
|
||||
public void setAssumeHex(boolean hexMode) {
|
||||
textField.setHexMode(hexMode);
|
||||
hexModeChanged(hexMode);
|
||||
IntegerFormat format = hexMode ? IntegerFormat.HEX : IntegerFormat.DEC;
|
||||
textField.setFormat(format);
|
||||
hexModeChanged(format);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -418,35 +419,17 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
||||
|
||||
private void buildComponent() {
|
||||
setLayout(new BorderLayout());
|
||||
textField = new HexDecimalModeTextField(10, b -> hexModeChanged(b));
|
||||
textField.setHexMode(true);
|
||||
textField = new MultiFormatTextField(10, List.of(HEX, DEC), b -> hexModeChanged(b));
|
||||
textField.setName("JTextField");//for JUnits...
|
||||
addressSpaceField = new AddressSpaceField();
|
||||
add(textField, BorderLayout.CENTER);
|
||||
comboAdded = false;
|
||||
|
||||
textField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
notifyAddressChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
notifyAddressChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
notifyAddressChanged();
|
||||
}
|
||||
});
|
||||
|
||||
textField.addTextChangedCallback(this::notifyAddressChanged);
|
||||
}
|
||||
|
||||
private void hexModeChanged(boolean hexMode) {
|
||||
this.assumeHex = hexMode;
|
||||
addressEvaluator.setAssumeHex(hexMode);
|
||||
private void hexModeChanged(IntegerFormat format) {
|
||||
this.assumeHex = format == HEX;
|
||||
addressEvaluator.setAssumeHex(assumeHex);
|
||||
notifyAddressChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package ghidra.app.util.importer.options;
|
||||
import java.awt.Component;
|
||||
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.app.util.opinion.Loader;
|
||||
import ghidra.framework.options.SaveState;
|
||||
@@ -66,7 +67,7 @@ public class HexLongOption extends AbstractOption<HexLong> {
|
||||
setValue(new HexLong(initialState));
|
||||
IntegerTextField field = new IntegerTextField();
|
||||
field.setValue(initialState);
|
||||
field.setHexMode();
|
||||
field.setFormat(IntegerFormat.HEX);
|
||||
field.getComponent().setToolTipText(getDescription());
|
||||
field.addChangeListener(e -> {
|
||||
setValue(new HexLong(field.getLongValue()));
|
||||
|
||||
@@ -26,6 +26,7 @@ import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import ghidra.framework.options.CustomOptionsEditor;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
@@ -101,8 +102,8 @@ public class AddressFieldOptionsPropertyEditor extends PropertyEditorSupport
|
||||
panel.add(label);
|
||||
|
||||
minDigitsField = new IntegerTextField(2);
|
||||
minDigitsField.setAllowNegativeValues(false);
|
||||
minDigitsField.setDecimalMode();
|
||||
minDigitsField.setMinValue(BigInteger.ZERO);
|
||||
minDigitsField.setFormat(IntegerFormat.DEC);
|
||||
minDigitsField.setMaxValue(BigInteger.valueOf(32));
|
||||
minDigitsField.getComponent().setToolTipText(MIN_HEX_DIGITS_TOOLTIP);
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -20,6 +20,7 @@ import java.awt.Container;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.TitledBorder;
|
||||
@@ -97,7 +98,7 @@ public class ArrayElementPropertyEditor extends PropertyEditorSupport
|
||||
Container parent) {
|
||||
|
||||
IntegerTextField textField = new IntegerTextField(10);
|
||||
textField.setAllowNegativeValues(false);
|
||||
textField.setMinValue(BigInteger.ZERO);
|
||||
textField.setEnabled(true);
|
||||
|
||||
JPanel textFieldPanel = new JPanel();
|
||||
@@ -132,7 +133,8 @@ public class ArrayElementPropertyEditor extends PropertyEditorSupport
|
||||
}
|
||||
|
||||
private void setLocalValues(ArrayElementWrappedOption namespaceOption) {
|
||||
if (namespaceOption.showMultipleArrayElementPerLine() != groupElementsCheckBox.isSelected()) {
|
||||
if (namespaceOption.showMultipleArrayElementPerLine() != groupElementsCheckBox
|
||||
.isSelected()) {
|
||||
groupElementsCheckBox.setSelected(namespaceOption.showMultipleArrayElementPerLine());
|
||||
}
|
||||
if (namespaceOption.getArrayElementsPerLine() != elementsPerLineField.getIntValue()) {
|
||||
|
||||
@@ -64,7 +64,7 @@ public class ProgramByteSource implements AddressableByteSource {
|
||||
long offset = location.getByteAddress().subtract(location.getProgram().getImageBase());
|
||||
return getProgram().getImageBase().add(offset);
|
||||
}
|
||||
|
||||
|
||||
public Program getProgram() {
|
||||
return memory.getProgram();
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -24,6 +24,7 @@ import docking.DialogComponentProvider;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
/**
|
||||
@@ -109,10 +110,10 @@ public abstract class AbstractNumberInputDialog extends DialogComponentProvider
|
||||
});
|
||||
|
||||
if (showAsHex) {
|
||||
numberInputField.setHexMode();
|
||||
numberInputField.setFormat(IntegerFormat.HEX);
|
||||
}
|
||||
if (min.compareTo(BigInteger.valueOf(0)) >= 0) {
|
||||
numberInputField.setAllowNegativeValues(false);
|
||||
numberInputField.setMinValue(BigInteger.ZERO);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package docking.widgets.table.constrainteditor;
|
||||
|
||||
import static docking.widgets.textfield.integer.IntegerFormat.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.GridLayout;
|
||||
import java.math.BigInteger;
|
||||
@@ -129,7 +131,8 @@ public class IntegerRangeConstraintEditor<T extends Number>
|
||||
BigInteger delta = bigEnd.subtract(bigStart).add(BigInteger.ONE);
|
||||
|
||||
boolean hexMode =
|
||||
lowerSpinner.getTextField().isHexMode() || upperSpinner.getTextField().isHexMode();
|
||||
lowerSpinner.getTextField().getFormat() == HEX ||
|
||||
upperSpinner.getTextField().getFormat() == HEX;
|
||||
|
||||
String statusMsg = formatStatus(
|
||||
String.format("Range Size: " + (hexMode ? "0x%x" : "%,d"), delta), false);
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -25,6 +25,7 @@ import docking.widgets.label.GDHtmlLabel;
|
||||
import docking.widgets.table.constraint.ColumnConstraint;
|
||||
import docking.widgets.table.constraint.SingleValueColumnConstraint;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
|
||||
/**
|
||||
@@ -54,8 +55,8 @@ public class UnsignedLongConstraintEditor extends AbstractColumnConstraintEditor
|
||||
getConstraint().getConstraintValue();
|
||||
|
||||
field = new IntegerTextField(16, 0);
|
||||
field.setHexMode();
|
||||
field.setAllowNegativeValues(false);
|
||||
field.setFormat(IntegerFormat.HEX);
|
||||
field.setMinValue(BigInteger.ZERO); // don't allow negative numbers
|
||||
field.setMaxValue(new BigInteger("FFFFFFFFFFFFFFFF", 16));
|
||||
field.addChangeListener(e -> valueChanged());
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -25,6 +25,7 @@ import docking.widgets.label.GDHtmlLabel;
|
||||
import docking.widgets.table.constraint.ColumnConstraint;
|
||||
import docking.widgets.table.constraint.RangeColumnConstraint;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import docking.widgets.textfield.integer.IntegerFormat;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import ghidra.util.layout.VerticalLayout;
|
||||
|
||||
@@ -80,8 +81,8 @@ public class UnsignedLongRangeConstraintEditor extends AbstractColumnConstraintE
|
||||
}
|
||||
|
||||
private void configureField(IntegerTextField field) {
|
||||
field.setHexMode();
|
||||
field.setAllowNegativeValues(false);
|
||||
field.setFormat(IntegerFormat.HEX);
|
||||
field.setMinValue(BigInteger.ZERO);
|
||||
field.setMaxValue(MAX_VALUE);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -18,6 +18,7 @@ package docking.widgets.values;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
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
|
||||
@@ -69,10 +70,10 @@ public class IntValue extends AbstractValue<Integer> {
|
||||
public JComponent getComponent() {
|
||||
if (field == null) {
|
||||
field = new IntegerTextField(20);
|
||||
field.setAllowsHexPrefix(false);
|
||||
field.setUseNumberPrefix(false);
|
||||
field.setShowNumberMode(false);
|
||||
if (displayAsHex) {
|
||||
field.setHexMode();
|
||||
field.setFormat(IntegerFormat.HEX);
|
||||
field.setShowNumberMode(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -18,6 +18,7 @@ package docking.widgets.values;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
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
|
||||
@@ -57,10 +58,10 @@ public class LongValue extends AbstractValue<Long> {
|
||||
public JComponent getComponent() {
|
||||
if (field == null) {
|
||||
field = new IntegerTextField(20);
|
||||
field.setAllowsHexPrefix(false);
|
||||
field.setUseNumberPrefix(false);
|
||||
field.setShowNumberMode(false);
|
||||
if (displayAsHex) {
|
||||
field.setHexMode();
|
||||
field.setFormat(IntegerFormat.HEX);
|
||||
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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static docking.widgets.textfield.integer.IntegerFormat.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
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 docking.test.AbstractDockingTest;
|
||||
public class IntegerTextFieldTest extends AbstractIntegerTextFieldTest<IntegerTextField> {
|
||||
|
||||
public class IntegerTextFieldTest extends AbstractDockingTest {
|
||||
|
||||
private JFrame frame;
|
||||
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);
|
||||
@Override
|
||||
protected IntegerTextField createField() {
|
||||
return new IntegerTextField(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultState() {
|
||||
assertNull(field.getValue());// no value
|
||||
assertEquals(0, field.getIntValue());// the "int value" return for null is 0
|
||||
assertEquals(0, field.getLongValue());
|
||||
assertTrue(!field.isHexMode());
|
||||
assertNull(field.getMaxValue());
|
||||
assertNull(getBigIntegerValue());// no value
|
||||
assertEquals(0, getIntValue()); // the "int value" return for null is 0
|
||||
assertEquals(0, getValue());
|
||||
assertEquals(DEC, getFormat());
|
||||
assertNull(getMaxValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeValidDecimalNumber() {
|
||||
triggerText(textField, "123");
|
||||
assertEquals(123, field.getIntValue());
|
||||
typeText("123");
|
||||
assertEquals(123, getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeValidHexNumber() {
|
||||
triggerText(textField, "0x2abcdef");
|
||||
assertEquals(0x2abcdef, field.getIntValue());
|
||||
typeText("0x2abcdef");
|
||||
assertEquals(0x2abcdef, getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidCharsIgnored() {
|
||||
triggerText(textField, "123ghijklmnopqrstuvwxyz4");
|
||||
assertEquals(1234, field.getIntValue());
|
||||
typeText("123ghijklmnopqrstuvwxyz4");
|
||||
assertEquals(1234, getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexCharsIgnoredInDecimalMode() {
|
||||
assertTrue(!field.isHexMode());
|
||||
triggerText(textField, "123ghijklmnopqrstuvwxyz4");
|
||||
assertEquals(1234, field.getIntValue());
|
||||
setFormat(HEX);
|
||||
typeText("123ghijklmnopqrstuvwxyz4");
|
||||
assertEquals(1234, getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXchangesHexMode() {
|
||||
assertTrue(!field.isHexMode());
|
||||
triggerText(textField, "0");
|
||||
assertTrue(!field.isHexMode());
|
||||
triggerText(textField, "x");
|
||||
assertTrue(field.isHexMode());
|
||||
assertEquals(DEC, getFormat());
|
||||
typeText("0");
|
||||
assertEquals(DEC, getFormat());
|
||||
typeText("x");
|
||||
assertEquals(HEX, getFormat());
|
||||
triggerBackspace(textField);
|
||||
assertTrue(!field.isHexMode());
|
||||
assertEquals(HEX, getFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexModeWithoutPrefix() {
|
||||
triggerText(textField, "abc");// not allowed when using hex prefix, so expect empty
|
||||
assertEquals(null, field.getValue());
|
||||
setFormat(HEX);
|
||||
typeText("a");
|
||||
assertEquals(null, getBigIntegerValue());
|
||||
|
||||
field.setAllowsHexPrefix(false);
|
||||
field.setHexMode();
|
||||
triggerText(textField, "abc");
|
||||
assertEquals(0xabc, field.getIntValue());
|
||||
setUsePrefix(false);
|
||||
typeText("abc");
|
||||
assertEquals(0xabc, getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegative() {
|
||||
triggerText(textField, "-123");
|
||||
assertEquals(-123, field.getIntValue());
|
||||
typeText("-123");
|
||||
assertEquals(-123, getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeHex() {
|
||||
triggerText(textField, "-0xa");
|
||||
assertEquals(-10, field.getIntValue());
|
||||
typeText("-0xa");
|
||||
assertEquals(-10, getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeNotAllowed() {
|
||||
field.setAllowNegativeValues(false);
|
||||
triggerText(textField, "-123");
|
||||
assertEquals(123, field.getIntValue());
|
||||
setMinValue(BigInteger.ZERO);
|
||||
typeText("-123");
|
||||
assertEquals(123, getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetNegativeWithCurrentNegativeValue() {
|
||||
field.setValue(-123);
|
||||
field.setAllowNegativeValues(false);
|
||||
setValue(-123);
|
||||
setMinValue(BigInteger.ZERO);
|
||||
assertEquals(null, field.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMax() {
|
||||
field.setMaxValue(BigInteger.valueOf(13l));
|
||||
triggerText(textField, "12");
|
||||
assertEquals(12, field.getIntValue());
|
||||
typeText("12");
|
||||
assertEquals(12, getValue());
|
||||
|
||||
field.setValue(null);
|
||||
triggerText(textField, "13");
|
||||
assertEquals(13, field.getIntValue());
|
||||
setText("");
|
||||
typeText("13");
|
||||
assertEquals(13, getValue());
|
||||
|
||||
field.setValue(null);
|
||||
triggerText(textField, "14");// four should be ignored
|
||||
assertEquals(1, field.getIntValue());
|
||||
setText("");
|
||||
typeText("14");// four should be ignored
|
||||
assertEquals(1, getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetMaxToValueSmallerThanCurrent() {
|
||||
field.setValue(500);
|
||||
setValue(500);
|
||||
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
|
||||
public void testMaxInHex() {
|
||||
field.setMaxValue(BigInteger.valueOf(0xd));
|
||||
triggerText(textField, "0xc");
|
||||
assertEquals(12, field.getIntValue());
|
||||
typeText("0xc");
|
||||
assertEquals(12, getValue());
|
||||
|
||||
field.setValue(null);
|
||||
triggerText(textField, "0xd");
|
||||
assertEquals(13, field.getIntValue());
|
||||
setText("");
|
||||
typeText("0xd");
|
||||
assertEquals(13, getValue());
|
||||
|
||||
field.setValue(null);
|
||||
triggerText(textField, "0xe");// e should be ignored
|
||||
assertEquals(0, field.getIntValue());
|
||||
setText("");
|
||||
typeText("0xe");// e should be ignored
|
||||
assertEquals(0, getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSwitchingHexMode() {
|
||||
field.setValue(255);
|
||||
setValue(255);
|
||||
assertEquals("255", field.getText());
|
||||
field.setHexMode();
|
||||
setFormat(HEX);
|
||||
assertEquals("0xff", field.getText());
|
||||
field.setDecimalMode();
|
||||
setFormat(DEC);
|
||||
assertEquals("255", field.getText());
|
||||
}
|
||||
|
||||
@@ -194,7 +173,7 @@ public class IntegerTextFieldTest extends AbstractDockingTest {
|
||||
TestChangeListener listener = new TestChangeListener();
|
||||
field.addChangeListener(listener);
|
||||
|
||||
triggerText(textField, "123");
|
||||
typeText("123");
|
||||
assertEquals(3, listener.count);
|
||||
assertEquals(1, listener.values.get(0));
|
||||
assertEquals(12, listener.values.get(1));
|
||||
@@ -207,129 +186,149 @@ public class IntegerTextFieldTest extends AbstractDockingTest {
|
||||
|
||||
@Test
|
||||
public void testChangeListenerAfterSwitchingModes() {
|
||||
triggerText(textField, "123");
|
||||
setFormat(DEC);
|
||||
typeText("12");
|
||||
|
||||
TestChangeListener listener = new TestChangeListener();
|
||||
field.addChangeListener(listener);
|
||||
|
||||
setHexMode();
|
||||
setFormat(HEX);
|
||||
assertEquals("0xc", getText());
|
||||
|
||||
assertEquals(2, listener.count);
|
||||
assertEquals(123, listener.values.get(1));
|
||||
assertEquals(12, listener.values.get(1));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeHexFromValue() {
|
||||
field.setValue(-255);
|
||||
setHexMode();
|
||||
setValue(-255);
|
||||
setFormat(HEX);
|
||||
assertEquals("-0xff", field.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullValue() {
|
||||
field.setValue(12);
|
||||
setValue(12);
|
||||
assertEquals("12", field.getText());
|
||||
field.setValue(null);
|
||||
setText("");
|
||||
assertEquals("", field.getText());
|
||||
assertEquals(0, field.getIntValue());
|
||||
assertEquals(0l, field.getLongValue());
|
||||
assertEquals(null, field.getValue());
|
||||
assertEquals(0, getValue());
|
||||
assertEquals(null, getBigIntegerValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexValueInDontRequireHexPrefixMode() {
|
||||
field.setAllowsHexPrefix(false);
|
||||
field.setHexMode();
|
||||
field.setValue(255);
|
||||
field.setUseNumberPrefix(false);
|
||||
field.setFormat(HEX);
|
||||
setValue(255);
|
||||
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
|
||||
public void testSetNotAllowNegativeModeWhileCurrentValueIsNegative() {
|
||||
field.setValue(-10);
|
||||
field.setAllowNegativeValues(false);
|
||||
setValue(-10);
|
||||
setMinValue(BigInteger.ZERO);
|
||||
assertEquals("", field.getText());
|
||||
assertEquals(0, field.getIntValue());
|
||||
assertEquals(0, getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetLongValue() {
|
||||
field.setValue(100L);
|
||||
setValue(100L);
|
||||
assertEquals(100L, field.getLongValue());
|
||||
assertEquals(100, field.getIntValue());
|
||||
assertEquals(100, getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSettingNegativeNumberWhenNegativesArentAllowed() {
|
||||
field.setValue(10);
|
||||
field.setAllowNegativeValues(false);
|
||||
field.setValue(-10);
|
||||
setValue(10);
|
||||
setMinValue(BigInteger.ZERO);
|
||||
setValue(-10);
|
||||
assertEquals("", field.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUseHexPrefixUpdatesTextField() {
|
||||
field.setAllowsHexPrefix(false);
|
||||
field.setHexMode();
|
||||
field.setValue(255);
|
||||
field.setUseNumberPrefix(false);
|
||||
setFormat(HEX);
|
||||
setValue(255);
|
||||
assertEquals("ff", field.getText());
|
||||
field.setAllowsHexPrefix(true);
|
||||
field.setUseNumberPrefix(true);
|
||||
assertEquals("0xff", field.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPastingBadText() {
|
||||
field.setHexMode();
|
||||
field.setValue(0);
|
||||
setFormat(HEX);
|
||||
setValue(0);
|
||||
assertFalse(field.setText("asdf 0x azzz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetText() {
|
||||
field.setHexMode();
|
||||
field.setValue(0);
|
||||
setFormat(HEX);
|
||||
setValue(0);
|
||||
assertTrue(field.setText("0x15"));
|
||||
assertEquals(0x15, field.getIntValue());
|
||||
assertEquals(0x15, getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetTextWithInvalidValue() {
|
||||
field.setHexMode();
|
||||
field.setValue(0);
|
||||
setFormat(HEX);
|
||||
setValue(0);
|
||||
assertFalse(field.setText("bad value"));
|
||||
assertEquals(0, field.getIntValue());
|
||||
assertTrue(field.isHexMode());
|
||||
assertEquals(0, getValue());
|
||||
assertEquals(HEX, getFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSucessfulSetTextChangesHexMode() {
|
||||
field.setHexMode();
|
||||
field.setValue(0);
|
||||
setFormat(HEX);
|
||||
setValue(0);
|
||||
assertTrue(field.setText("33"));
|
||||
assertEquals(33, field.getIntValue());
|
||||
assertFalse(field.isHexMode());
|
||||
assertEquals(33, getValue());
|
||||
assertEquals(DEC, getFormat());
|
||||
|
||||
assertTrue(field.setText("0x33"));
|
||||
assertEquals(0x33, field.getIntValue());
|
||||
assertTrue(field.isHexMode());
|
||||
|
||||
assertEquals(0x33, getValue());
|
||||
assertEquals(HEX, getFormat());
|
||||
}
|
||||
|
||||
private void setHexMode() {
|
||||
runSwing(() -> field.setHexMode());
|
||||
waitForSwing();
|
||||
@Test
|
||||
public void testMinValueOfOneDecimalFormat() {
|
||||
setFormat(DEC);
|
||||
field.setMinValue(BigInteger.ONE);
|
||||
typeText("0");
|
||||
assertEquals("", field.getText());
|
||||
typeText("1");
|
||||
assertEquals("1", field.getText());
|
||||
}
|
||||
|
||||
class TestChangeListener implements ChangeListener {
|
||||
volatile int count;
|
||||
private AtomicIntegerArray values = new AtomicIntegerArray(10);
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
values.set(count++, field.getIntValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinValueOfOneHexFormat() {
|
||||
setFormat(HEX);
|
||||
field.setMinValue(BigInteger.ONE);
|
||||
typeText("0x1");
|
||||
assertEquals("0x1", field.getText());
|
||||
}
|
||||
|
||||
protected void setMinValue(BigInteger minValue) {
|
||||
runSwing(() -> field.setMinValue(minValue));
|
||||
}
|
||||
}
|
||||
|
||||