Added Go To action for hex viewer
This commit is contained in:
parent
63a683c9e9
commit
af78e3cde1
5 changed files with 898 additions and 2 deletions
|
@ -0,0 +1,425 @@
|
|||
package the.bytecode.club.bytecodeviewer.gui.hexviewer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Insets;
|
||||
import java.awt.LayoutManager;
|
||||
import java.awt.event.FocusAdapter;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import org.exbin.bined.CodeAreaUtils;
|
||||
import org.exbin.bined.CodeCharactersCase;
|
||||
import org.exbin.bined.PositionCodeType;
|
||||
|
||||
/**
|
||||
* Spinner supporting multiple bases.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class BaseSwitchableSpinnerPanel extends javax.swing.JPanel {
|
||||
|
||||
private boolean adjusting;
|
||||
private final PositionSpinnerEditor spinnerEditor;
|
||||
private static final String SPINNER_PROPERTY = "value";
|
||||
|
||||
public BaseSwitchableSpinnerPanel() {
|
||||
initComponents();
|
||||
spinnerEditor = new PositionSpinnerEditor(spinner);
|
||||
spinner.setEditor(spinnerEditor);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// Spinner selection workaround from http://forums.sun.com/thread.jspa?threadID=409748&forumID=57
|
||||
spinnerEditor.getTextField().addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
if (e.getSource() instanceof JTextComponent) {
|
||||
final JTextComponent textComponent = ((JTextComponent) e.getSource());
|
||||
SwingUtilities.invokeLater(textComponent::selectAll);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Dimension preferredSize = baseSwitchButton.getPreferredSize();
|
||||
setPreferredSize(new Dimension(preferredSize.width * 4, preferredSize.height));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
baseSwitchPopupMenu = new javax.swing.JPopupMenu();
|
||||
octalMenuItem = new javax.swing.JMenuItem();
|
||||
decimalMenuItem = new javax.swing.JMenuItem();
|
||||
hexadecimalMenuItem = new javax.swing.JMenuItem();
|
||||
baseSwitchButton = new javax.swing.JButton();
|
||||
spinner = new javax.swing.JSpinner();
|
||||
|
||||
octalMenuItem.setText("OCT");
|
||||
octalMenuItem.setToolTipText("Octal");
|
||||
octalMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
octalMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
baseSwitchPopupMenu.add(octalMenuItem);
|
||||
|
||||
decimalMenuItem.setText("DEC");
|
||||
decimalMenuItem.setToolTipText("Decimal");
|
||||
decimalMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
decimalMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
baseSwitchPopupMenu.add(decimalMenuItem);
|
||||
|
||||
hexadecimalMenuItem.setText("HEX");
|
||||
hexadecimalMenuItem.setToolTipText("Hexadecimal");
|
||||
hexadecimalMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
hexadecimalMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
baseSwitchPopupMenu.add(hexadecimalMenuItem);
|
||||
|
||||
setPreferredSize(new java.awt.Dimension(400, 300));
|
||||
|
||||
baseSwitchButton.setText("DEC");
|
||||
baseSwitchButton.setToolTipText("Decimal");
|
||||
baseSwitchButton.setComponentPopupMenu(baseSwitchPopupMenu);
|
||||
baseSwitchButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
baseSwitchButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
spinner.setModel(new javax.swing.SpinnerNumberModel(0L, null, null, 1L));
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(baseSwitchButton, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(spinner, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(spinner)
|
||||
.addComponent(baseSwitchButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void baseSwitchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_baseSwitchButtonActionPerformed
|
||||
PositionCodeType positionCodeType = spinnerEditor.getPositionCodeType();
|
||||
switch (positionCodeType) {
|
||||
case OCTAL: {
|
||||
switchNumBase(PositionCodeType.DECIMAL);
|
||||
break;
|
||||
}
|
||||
case DECIMAL: {
|
||||
switchNumBase(PositionCodeType.HEXADECIMAL);
|
||||
break;
|
||||
}
|
||||
case HEXADECIMAL: {
|
||||
switchNumBase(PositionCodeType.OCTAL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw CodeAreaUtils.getInvalidTypeException(positionCodeType);
|
||||
}
|
||||
}//GEN-LAST:event_baseSwitchButtonActionPerformed
|
||||
|
||||
private void octalMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_octalMenuItemActionPerformed
|
||||
switchNumBase(PositionCodeType.OCTAL);
|
||||
}//GEN-LAST:event_octalMenuItemActionPerformed
|
||||
|
||||
private void decimalMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_decimalMenuItemActionPerformed
|
||||
switchNumBase(PositionCodeType.DECIMAL);
|
||||
}//GEN-LAST:event_decimalMenuItemActionPerformed
|
||||
|
||||
private void hexadecimalMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hexadecimalMenuItemActionPerformed
|
||||
switchNumBase(PositionCodeType.HEXADECIMAL);
|
||||
}//GEN-LAST:event_hexadecimalMenuItemActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton baseSwitchButton;
|
||||
private javax.swing.JPopupMenu baseSwitchPopupMenu;
|
||||
private javax.swing.JMenuItem decimalMenuItem;
|
||||
private javax.swing.JMenuItem hexadecimalMenuItem;
|
||||
private javax.swing.JMenuItem octalMenuItem;
|
||||
private javax.swing.JSpinner spinner;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
private void switchNumBase(PositionCodeType codeType) {
|
||||
adjusting = true;
|
||||
long value = getValue();
|
||||
int position = codeType.ordinal();
|
||||
baseSwitchButton.setText(codeType.name().substring(0, 3));
|
||||
baseSwitchButton.setToolTipText(((JMenuItem) baseSwitchPopupMenu.getComponent(position)).getToolTipText());
|
||||
spinnerEditor.setPositionCodeType(codeType);
|
||||
setValue(value);
|
||||
adjusting = false;
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return (Long) spinner.getValue();
|
||||
}
|
||||
|
||||
public void setValue(long value) {
|
||||
spinnerEditor.setPositionValue(value);
|
||||
}
|
||||
|
||||
public void acceptInput() {
|
||||
try {
|
||||
spinner.commitEdit();
|
||||
} catch (ParseException ex) {
|
||||
// Ignore parse exception
|
||||
}
|
||||
}
|
||||
|
||||
public void initFocus() {
|
||||
/* ((JSpinner.DefaultEditor) positionSpinner.getEditor()) */
|
||||
spinnerEditor.getTextField().requestFocusInWindow();
|
||||
}
|
||||
|
||||
public void setMinimum(long minimum) {
|
||||
((SpinnerNumberModel) spinner.getModel()).setMinimum(minimum);
|
||||
}
|
||||
|
||||
public void setMaximum(long maximum) {
|
||||
((SpinnerNumberModel) spinner.getModel()).setMaximum(maximum);
|
||||
}
|
||||
|
||||
public void revalidateSpinner() {
|
||||
spinner.revalidate();
|
||||
}
|
||||
|
||||
public void addChangeListener(ChangeListener changeListener) {
|
||||
spinner.addChangeListener(changeListener);
|
||||
}
|
||||
|
||||
public void removeChangeListener(ChangeListener changeListener) {
|
||||
spinner.removeChangeListener(changeListener);
|
||||
}
|
||||
|
||||
@ParametersAreNonnullByDefault
|
||||
private class PositionSpinnerEditor extends JPanel implements ChangeListener, PropertyChangeListener, LayoutManager {
|
||||
|
||||
private static final int LENGTH_LIMIT = 21;
|
||||
|
||||
private PositionCodeType positionCodeType = PositionCodeType.DECIMAL;
|
||||
|
||||
private final char[] cache = new char[LENGTH_LIMIT];
|
||||
|
||||
private final JTextField textField;
|
||||
private final JSpinner spinner;
|
||||
|
||||
public PositionSpinnerEditor(JSpinner spinner) {
|
||||
this.spinner = spinner;
|
||||
textField = new JTextField();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
textField.setName("Spinner.textField");
|
||||
textField.setText(getPositionAsString((Long) spinner.getValue()));
|
||||
textField.addPropertyChangeListener(this);
|
||||
textField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
private final PropertyChangeEvent changeEvent = new PropertyChangeEvent(textField, SPINNER_PROPERTY, null, null);
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
public void notifyChanged() {
|
||||
propertyChange(changeEvent);
|
||||
}
|
||||
});
|
||||
textField.setEditable(true);
|
||||
textField.setInheritsPopupMenu(true);
|
||||
|
||||
String toolTipText = spinner.getToolTipText();
|
||||
if (toolTipText != null) {
|
||||
textField.setToolTipText(toolTipText);
|
||||
}
|
||||
|
||||
add(textField);
|
||||
|
||||
setLayout(this);
|
||||
spinner.addChangeListener(this);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private JTextField getTextField() {
|
||||
return textField;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private JSpinner getSpinner() {
|
||||
return spinner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
if (adjusting) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSpinner sourceSpinner = (JSpinner) (e.getSource());
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
textField.setText(getPositionAsString((Long) sourceSpinner.getValue()));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent e) {
|
||||
if (adjusting) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSpinner sourceSpinner = getSpinner();
|
||||
|
||||
Object source = e.getSource();
|
||||
String name = e.getPropertyName();
|
||||
if ((source instanceof JTextField) && SPINNER_PROPERTY.equals(name)) {
|
||||
Long lastValue = (Long) sourceSpinner.getValue();
|
||||
|
||||
// Try to set the new value
|
||||
try {
|
||||
sourceSpinner.setValue(valueOfPosition(getTextField().getText()));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// SpinnerModel didn't like new value, reset
|
||||
try {
|
||||
sourceSpinner.setValue(lastValue);
|
||||
} catch (IllegalArgumentException iae2) {
|
||||
// Still bogus, nothing else we can do, the
|
||||
// SpinnerModel and JFormattedTextField are now out
|
||||
// of sync.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPositionValue(long positionValue) {
|
||||
textField.setText(getPositionAsString(positionValue));
|
||||
spinner.setValue(positionValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLayoutComponent(String name, Component comp) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLayoutComponent(Component comp) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the parents insets.
|
||||
*/
|
||||
@Nonnull
|
||||
private Dimension insetSize(Container parent) {
|
||||
Insets insets = parent.getInsets();
|
||||
int width = insets.left + insets.right;
|
||||
int height = insets.top + insets.bottom;
|
||||
return new Dimension(width, height);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Dimension preferredLayoutSize(Container parent) {
|
||||
Dimension preferredSize = insetSize(parent);
|
||||
if (parent.getComponentCount() > 0) {
|
||||
Dimension childSize = getComponent(0).getPreferredSize();
|
||||
preferredSize.width += childSize.width;
|
||||
preferredSize.height += childSize.height;
|
||||
}
|
||||
return preferredSize;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Dimension minimumLayoutSize(Container parent) {
|
||||
Dimension minimumSize = insetSize(parent);
|
||||
if (parent.getComponentCount() > 0) {
|
||||
Dimension childSize = getComponent(0).getMinimumSize();
|
||||
minimumSize.width += childSize.width;
|
||||
minimumSize.height += childSize.height;
|
||||
}
|
||||
return minimumSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void layoutContainer(Container parent) {
|
||||
if (parent.getComponentCount() > 0) {
|
||||
Insets insets = parent.getInsets();
|
||||
int width = parent.getWidth() - (insets.left + insets.right);
|
||||
int height = parent.getHeight() - (insets.top + insets.bottom);
|
||||
getComponent(0).setBounds(insets.left, insets.top, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public PositionCodeType getPositionCodeType() {
|
||||
return positionCodeType;
|
||||
}
|
||||
|
||||
public void setPositionCodeType(PositionCodeType positionCodeType) {
|
||||
this.positionCodeType = positionCodeType;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String getPositionAsString(long position) {
|
||||
if (position < 0) {
|
||||
return "-" + getNonNegativePostionAsString(-position);
|
||||
}
|
||||
return getNonNegativePostionAsString(position);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String getNonNegativePostionAsString(long position) {
|
||||
Arrays.fill(cache, ' ');
|
||||
CodeAreaUtils.longToBaseCode(cache, 0, position, positionCodeType.getBase(), LENGTH_LIMIT, false, CodeCharactersCase.LOWER);
|
||||
return new String(cache).trim();
|
||||
}
|
||||
|
||||
private long valueOfPosition(String position) {
|
||||
return Long.parseLong(position, positionCodeType.getBase());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
package the.bytecode.club.bytecodeviewer.gui.hexviewer;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import org.exbin.bined.CodeAreaUtils;
|
||||
|
||||
/**
|
||||
* Go-to position panel for binary editor.
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class GoToBinaryPanel extends javax.swing.JPanel {
|
||||
|
||||
private long cursorPosition;
|
||||
private long maxPosition;
|
||||
private GoToBinaryPositionMode goToMode = GoToBinaryPositionMode.FROM_START;
|
||||
|
||||
public GoToBinaryPanel() {
|
||||
initComponents();
|
||||
|
||||
baseSwitchableSpinnerPanel.setMinimum(0l);
|
||||
baseSwitchableSpinnerPanel.addChangeListener((javax.swing.event.ChangeEvent evt) -> {
|
||||
updateTargetPosition();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
positionTypeButtonGroup = new javax.swing.ButtonGroup();
|
||||
currentPositionLabel = new javax.swing.JLabel();
|
||||
currentPositionTextField = new javax.swing.JTextField();
|
||||
goToPanel = new javax.swing.JPanel();
|
||||
fromStartRadioButton = new javax.swing.JRadioButton();
|
||||
fromEndRadioButton = new javax.swing.JRadioButton();
|
||||
fromCursorRadioButton = new javax.swing.JRadioButton();
|
||||
positionLabel = new javax.swing.JLabel();
|
||||
baseSwitchableSpinnerPanel = new BaseSwitchableSpinnerPanel();
|
||||
targetPositionLabel = new javax.swing.JLabel();
|
||||
targetPositionTextField = new javax.swing.JTextField();
|
||||
|
||||
currentPositionLabel.setText("Current Position");
|
||||
|
||||
currentPositionTextField.setEditable(false);
|
||||
currentPositionTextField.setText("0");
|
||||
|
||||
goToPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Go To Position"));
|
||||
|
||||
positionTypeButtonGroup.add(fromStartRadioButton);
|
||||
fromStartRadioButton.setSelected(true);
|
||||
fromStartRadioButton.setText("Position from start");
|
||||
fromStartRadioButton.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
fromStartRadioButtonItemStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
positionTypeButtonGroup.add(fromEndRadioButton);
|
||||
fromEndRadioButton.setText("Position from end");
|
||||
fromEndRadioButton.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
fromEndRadioButtonItemStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
positionTypeButtonGroup.add(fromCursorRadioButton);
|
||||
fromCursorRadioButton.setText("Position relative to cursor");
|
||||
fromCursorRadioButton.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
fromCursorRadioButtonItemStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
positionLabel.setText("Position");
|
||||
|
||||
javax.swing.GroupLayout goToPanelLayout = new javax.swing.GroupLayout(goToPanel);
|
||||
goToPanel.setLayout(goToPanelLayout);
|
||||
goToPanelLayout.setHorizontalGroup(
|
||||
goToPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(fromStartRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(fromCursorRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, 412, Short.MAX_VALUE)
|
||||
.addComponent(fromEndRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(goToPanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(goToPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(baseSwitchableSpinnerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(goToPanelLayout.createSequentialGroup()
|
||||
.addComponent(positionLabel)
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
goToPanelLayout.setVerticalGroup(
|
||||
goToPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(goToPanelLayout.createSequentialGroup()
|
||||
.addComponent(fromStartRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(fromEndRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(fromCursorRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(positionLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(baseSwitchableSpinnerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
targetPositionLabel.setText("Target Position");
|
||||
|
||||
targetPositionTextField.setEditable(false);
|
||||
targetPositionTextField.setText("0");
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(currentPositionTextField)
|
||||
.addComponent(goToPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(targetPositionTextField)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(currentPositionLabel)
|
||||
.addComponent(targetPositionLabel))
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(currentPositionLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(currentPositionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(goToPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(targetPositionLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(targetPositionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void fromStartRadioButtonItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_fromStartRadioButtonItemStateChanged
|
||||
if (fromStartRadioButton.isSelected()) {
|
||||
switchGoToMode(GoToBinaryPositionMode.FROM_START);
|
||||
}
|
||||
}//GEN-LAST:event_fromStartRadioButtonItemStateChanged
|
||||
|
||||
private void fromEndRadioButtonItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_fromEndRadioButtonItemStateChanged
|
||||
if (fromEndRadioButton.isSelected()) {
|
||||
switchGoToMode(GoToBinaryPositionMode.FROM_END);
|
||||
}
|
||||
}//GEN-LAST:event_fromEndRadioButtonItemStateChanged
|
||||
|
||||
private void fromCursorRadioButtonItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_fromCursorRadioButtonItemStateChanged
|
||||
if (fromCursorRadioButton.isSelected()) {
|
||||
switchGoToMode(GoToBinaryPositionMode.FROM_CURSOR);
|
||||
}
|
||||
}//GEN-LAST:event_fromCursorRadioButtonItemStateChanged
|
||||
|
||||
private void updateTargetPosition() {
|
||||
targetPositionTextField.setText(String.valueOf(getTargetPosition()));
|
||||
}
|
||||
|
||||
public void initFocus() {
|
||||
baseSwitchableSpinnerPanel.initFocus();
|
||||
}
|
||||
|
||||
public long getTargetPosition() {
|
||||
long absolutePosition;
|
||||
long position = getPositionValue();
|
||||
switch (goToMode) {
|
||||
case FROM_START:
|
||||
absolutePosition = position;
|
||||
break;
|
||||
case FROM_END:
|
||||
absolutePosition = maxPosition - position;
|
||||
break;
|
||||
case FROM_CURSOR:
|
||||
absolutePosition = cursorPosition + position;
|
||||
break;
|
||||
default:
|
||||
throw CodeAreaUtils.getInvalidTypeException(goToMode);
|
||||
}
|
||||
|
||||
if (absolutePosition < 0) {
|
||||
absolutePosition = 0;
|
||||
} else if (absolutePosition > maxPosition) {
|
||||
absolutePosition = maxPosition;
|
||||
}
|
||||
return absolutePosition;
|
||||
}
|
||||
|
||||
public void setTargetPosition(long absolutePosition) {
|
||||
if (absolutePosition < 0) {
|
||||
absolutePosition = 0;
|
||||
} else if (absolutePosition > maxPosition) {
|
||||
absolutePosition = maxPosition;
|
||||
}
|
||||
switch (goToMode) {
|
||||
case FROM_START:
|
||||
setPositionValue(absolutePosition);
|
||||
break;
|
||||
case FROM_END:
|
||||
setPositionValue(maxPosition - absolutePosition);
|
||||
break;
|
||||
case FROM_CURSOR:
|
||||
setPositionValue(absolutePosition - cursorPosition);
|
||||
break;
|
||||
default:
|
||||
throw CodeAreaUtils.getInvalidTypeException(goToMode);
|
||||
}
|
||||
updateTargetPosition();
|
||||
}
|
||||
|
||||
public long getCursorPosition() {
|
||||
return cursorPosition;
|
||||
}
|
||||
|
||||
public void setCursorPosition(long cursorPosition) {
|
||||
this.cursorPosition = cursorPosition;
|
||||
setPositionValue(cursorPosition);
|
||||
currentPositionTextField.setText(String.valueOf(cursorPosition));
|
||||
}
|
||||
|
||||
public void setMaxPosition(long maxPosition) {
|
||||
this.maxPosition = maxPosition;
|
||||
baseSwitchableSpinnerPanel.setMaximum(maxPosition);
|
||||
updateTargetPosition();
|
||||
}
|
||||
|
||||
public void setSelected() {
|
||||
baseSwitchableSpinnerPanel.requestFocusInWindow();
|
||||
}
|
||||
|
||||
private void switchGoToMode(GoToBinaryPositionMode goToMode) {
|
||||
if (this.goToMode == goToMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
long absolutePosition = getTargetPosition();
|
||||
this.goToMode = goToMode;
|
||||
switch (goToMode) {
|
||||
case FROM_START: {
|
||||
setPositionValue(0l);
|
||||
baseSwitchableSpinnerPanel.setMinimum(0l);
|
||||
baseSwitchableSpinnerPanel.setMaximum(maxPosition);
|
||||
baseSwitchableSpinnerPanel.revalidateSpinner();
|
||||
break;
|
||||
}
|
||||
case FROM_END: {
|
||||
setPositionValue(0l);
|
||||
baseSwitchableSpinnerPanel.setMinimum(0l);
|
||||
baseSwitchableSpinnerPanel.setMaximum(maxPosition);
|
||||
baseSwitchableSpinnerPanel.revalidateSpinner();
|
||||
break;
|
||||
}
|
||||
case FROM_CURSOR: {
|
||||
setPositionValue(0l);
|
||||
baseSwitchableSpinnerPanel.setMinimum(-cursorPosition);
|
||||
baseSwitchableSpinnerPanel.setMaximum(maxPosition - cursorPosition);
|
||||
baseSwitchableSpinnerPanel.revalidateSpinner();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw CodeAreaUtils.getInvalidTypeException(goToMode);
|
||||
}
|
||||
setTargetPosition(absolutePosition);
|
||||
}
|
||||
|
||||
private long getPositionValue() {
|
||||
return (Long) baseSwitchableSpinnerPanel.getValue();
|
||||
}
|
||||
|
||||
private void setPositionValue(long value) {
|
||||
baseSwitchableSpinnerPanel.setValue(value);
|
||||
updateTargetPosition();
|
||||
// positionSpinner.setValue(value);
|
||||
// positionSpinner.firePropertyChange(SPINNER_PROPERTY, value, value);
|
||||
}
|
||||
|
||||
public void acceptInput() {
|
||||
baseSwitchableSpinnerPanel.acceptInput();
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private BaseSwitchableSpinnerPanel baseSwitchableSpinnerPanel;
|
||||
private javax.swing.JLabel currentPositionLabel;
|
||||
private javax.swing.JTextField currentPositionTextField;
|
||||
private javax.swing.JRadioButton fromCursorRadioButton;
|
||||
private javax.swing.JRadioButton fromEndRadioButton;
|
||||
private javax.swing.JRadioButton fromStartRadioButton;
|
||||
private javax.swing.JPanel goToPanel;
|
||||
private javax.swing.JLabel positionLabel;
|
||||
private javax.swing.ButtonGroup positionTypeButtonGroup;
|
||||
private javax.swing.JLabel targetPositionLabel;
|
||||
private javax.swing.JTextField targetPositionTextField;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package the.bytecode.club.bytecodeviewer.gui.hexviewer;
|
||||
|
||||
/**
|
||||
* Mode for calculation of the go-to position in binary document.
|
||||
*/
|
||||
public enum GoToBinaryPositionMode {
|
||||
/**
|
||||
* Count from start of the document.
|
||||
*/
|
||||
FROM_START,
|
||||
/**
|
||||
* Count from end of the document.
|
||||
*/
|
||||
FROM_END,
|
||||
/**
|
||||
* Count from current position of the cursor in the document.
|
||||
*/
|
||||
FROM_CURSOR
|
||||
}
|
|
@ -16,7 +16,7 @@ import java.awt.event.KeyEvent;
|
|||
|
||||
/**
|
||||
* Binary/hexadecimal viewer based on BinEd library.
|
||||
*
|
||||
*
|
||||
* @author hajdam
|
||||
*/
|
||||
public class HexViewer extends JPanel {
|
||||
|
@ -33,13 +33,19 @@ public class HexViewer extends JPanel {
|
|||
private javax.swing.JToggleButton lineWrappingToggleButton;
|
||||
private JButton cycleCodeTypeButton;
|
||||
private BinaryStatusApi binaryStatus;
|
||||
private final AbstractAction goToAction;
|
||||
|
||||
public HexViewer(byte[] contentData) {
|
||||
super(new BorderLayout());
|
||||
codeArea = new CodeArea();
|
||||
codeArea.setPainter(new HighlightNonAsciiCodeAreaPainter(codeArea));
|
||||
toolBar = new JToolBar();
|
||||
statusPanel = new BinaryStatusPanel();
|
||||
statusPanel = new BinaryStatusPanel() {
|
||||
@Override
|
||||
public Dimension getMinimumSize() {
|
||||
return new Dimension(0, super.getMinimumSize().height);
|
||||
}
|
||||
};
|
||||
valuesPanel = new ValuesPanel();
|
||||
codeArea.setContentData(new ByteArrayData(contentData));
|
||||
codeArea.setEditMode(EditMode.READ_ONLY);
|
||||
|
@ -55,6 +61,49 @@ public class HexViewer extends JPanel {
|
|||
}
|
||||
};
|
||||
|
||||
goToAction = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final GoToBinaryPanel goToPanel = new GoToBinaryPanel();
|
||||
goToPanel.setCursorPosition(codeArea.getCaret().getCaretPosition().getDataPosition());
|
||||
goToPanel.setMaxPosition(codeArea.getDataSize());
|
||||
final JDialog dialog = new JDialog((JFrame) SwingUtilities.getRoot(HexViewer.this), Dialog.ModalityType.APPLICATION_MODAL);
|
||||
OkCancelPanel okCancelPanel = new OkCancelPanel() {
|
||||
@Override
|
||||
protected void okAction() {
|
||||
goToPanel.acceptInput();
|
||||
codeArea.setCaretPosition(goToPanel.getTargetPosition());
|
||||
codeArea.revealCursor();
|
||||
dialog.setVisible(false);
|
||||
dialog.dispose();
|
||||
codeArea.requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelAction() {
|
||||
dialog.setVisible(false);
|
||||
dialog.dispose();
|
||||
}
|
||||
};
|
||||
|
||||
final String ESC_CANCEL = "esc-cancel";
|
||||
dialog.getRootPane().getActionMap().put(ESC_CANCEL, new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
okCancelPanel.cancelAction();
|
||||
}
|
||||
});
|
||||
dialog.getRootPane().getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), ESC_CANCEL);
|
||||
okCancelPanel.setOkButtonText("Go To");
|
||||
dialog.setTitle("Go To Position");
|
||||
dialog.add(goToPanel, BorderLayout.CENTER);
|
||||
dialog.add(okCancelPanel, BorderLayout.SOUTH);
|
||||
dialog.pack();
|
||||
dialog.setLocationByPlatform(true);
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
@ -111,6 +160,10 @@ public class HexViewer extends JPanel {
|
|||
|
||||
registerBinaryStatus(statusPanel);
|
||||
add(statusPanel, BorderLayout.SOUTH);
|
||||
|
||||
final String GO_TO_ACTION = "goToAction";
|
||||
codeArea.getActionMap().put(GO_TO_ACTION, goToAction);
|
||||
codeArea.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_G, HexViewer.getMetaMask()), GO_TO_ACTION);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
@ -269,6 +322,13 @@ public class HexViewer extends JPanel {
|
|||
codeArea.selectAll();
|
||||
});
|
||||
menu.add(selectAllMenuItem);
|
||||
menu.addSeparator();
|
||||
|
||||
final JMenuItem goToMenuItem = new JMenuItem("Go To...");
|
||||
goToMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G, HexViewer.getMetaMask()));
|
||||
goToMenuItem.addActionListener(goToAction::actionPerformed);
|
||||
menu.add(goToMenuItem);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package the.bytecode.club.bytecodeviewer.gui.hexviewer;
|
||||
|
||||
/**
|
||||
* OK/Cancel Panel.
|
||||
*/
|
||||
public class OkCancelPanel extends javax.swing.JPanel {
|
||||
|
||||
public OkCancelPanel() {
|
||||
initComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
cancelButton = new javax.swing.JButton();
|
||||
okButton = new javax.swing.JButton();
|
||||
|
||||
cancelButton.setText("Cancel");
|
||||
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
cancelButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
okButton.setText("Ok");
|
||||
okButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
okButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(okButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(cancelButton)
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(cancelButton)
|
||||
.addComponent(okButton))
|
||||
.addContainerGap())
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
|
||||
cancelAction();
|
||||
}//GEN-LAST:event_cancelButtonActionPerformed
|
||||
|
||||
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
|
||||
okAction();
|
||||
}//GEN-LAST:event_okButtonActionPerformed
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton cancelButton;
|
||||
private javax.swing.JButton okButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
protected void okAction() {
|
||||
}
|
||||
|
||||
protected void cancelAction() {
|
||||
}
|
||||
|
||||
public void setOkButtonText(String text) {
|
||||
okButton.setText(text);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue