org.fife.ui.hex.swing.HexEditor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hexeditor Show documentation
Show all versions of hexeditor Show documentation
HexEditor from fifesoft.com.
The newest version!
/*
* Copyright (c) 2008 Robert Futrell
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "HexEditor" nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.fife.ui.hex.swing;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.*;
import java.util.ResourceBundle;
import javax.swing.*;
import org.fife.ui.hex.event.HexEditorEvent;
import org.fife.ui.hex.event.HexEditorListener;
/**
* A Swing hex editor component.
*
* The hex editor's functionality includes:
*
* - Cut, copy, paste, delete of 1 or more bytes
*
- Undo/redo
*
- Selecting a contiguous block of bytes
*
*
* @author Robert Futrell
* @version 1.0
*/
public class HexEditor extends JScrollPane {
private static final long serialVersionUID = 1L;
/**
* Property fired when the alternating of column background colors is
* toggled.
*/
public static final String PROPERTY_ALTERNATE_COLUMN_BG = "alternateColBG";
/**
* Property fired when the alternating of row background colors is
* toggled.
*/
public static final String PROPERTY_ALTERNATE_ROW_BG = "alternateRowBG";
/**
* Property fired when the highlight color of the ascii dump column
* is changed.
*/
public static final String PROPERTY_ASCII_DUMP_HIGHLIGHT_COLOR =
"asciiDumpHighlightColor";
/**
* Property fired when the visibility of the highlight in the "ascii
* dump" column is toggled.
*/
public static final String PROPERTY_HIGHLIGHT_ASCII_DUMP =
"highlightAsciiDump";
/**
* Property fired when the visibility of the grid is toggled.
*/
public static final String PROPERTY_SHOW_GRID = "showGrid";
private HexTable table;
private boolean alternateRowBG;
private boolean alternateColumnBG;
private boolean highlightSelectionInAsciiDump;
private Color highlightSelectionInAsciiDumpColor;
private static final TransferHandler DEFAULT_TRANSFER_HANDLER =
new HexEditorTransferHandler();
static final int DUMP_COLUMN_WIDTH = 200;
private static final String MSG = "org.fife.ui.hex.HexEditor";
/**
* Creates a new HexEditor component.
*/
public HexEditor() {
ResourceBundle msg = ResourceBundle.getBundle(MSG);
HexTableModel model = new HexTableModel(this, msg);
table = new HexTable(this, model);
setViewportView(table);
setShowRowHeader(true);
setAlternateRowBG(false);
setAlternateColumnBG(false);
setHighlightSelectionInAsciiDump(true);
setHighlightSelectionInAsciiDumpColor(new Color(255,255,192));
setTransferHandler(DEFAULT_TRANSFER_HANDLER);
}
public void setEditable(final boolean editable)
{
if (this.table != null)
this.table.setEditable(editable);
}
/**
* Adds a hex editor listener to this editor.
*
* @param l The listener to add.
* @see #removeHexEditorListener(HexEditorListener)
*/
public void addHexEditorListener(HexEditorListener l) {
listenerList.add(HexEditorListener.class, l);
}
/**
* Returns the offset into the bytes being edited represented at the
* specified cell in the table, if any.
*
* @param row The row in the table.
* @param col The column in the table.
* @return The offset into the byte array, or -1 if the
* cell does not represent part of the byte array (such as the
* tailing "ascii dump" column's cells).
* @see #offsetToCell(int)
*/
public int cellToOffset(int row, int col) {
return table.cellToOffset(row, col);
}
/**
* Copies the currently selected bytes to the clipboard.
*
* @see #cut()
* @see #paste()
* @see #delete()
*/
public void copy() {
invokeAction(TransferHandler.getCopyAction());
}
/**
* Removes the currently selected bytes and moves them to the clipboard.
*
* @see #copy()
* @see #paste()
* @see #delete()
*/
public void cut() {
invokeAction(TransferHandler.getCutAction());
}
/**
* Removes the currently selected bytes.
*
* @see #cut()
* @see #copy()
* @see #paste()
*/
public void delete() {
// Sanity check (should never happen)
if (table.leadSelectionIndex==-1 || table.anchorSelectionIndex==-1) {
UIManager.getLookAndFeel().provideErrorFeedback(table);
return;
}
int start = table.getSmallestSelectionIndex();
int end = table.getLargestSelectionIndex();
int len = end-start+1;
removeBytes(start, len);
}
/**
* Notifies all interested listeners of an event in this hex editor.
*
* @param offset The offset of the change.
* @param added The number of bytes added.
* @param removed The number of bytes removed.
*/
protected void fireHexEditorEvent(int offset, int added, int removed) {
HexEditorEvent e = null;
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i=listeners.length-2; i>=0; i-=2) {
if (listeners[i]==HexEditorListener.class) {
// Lazily create the event in case there are no listeners.
if (e==null) {
e = new HexEditorEvent(this, offset, added, removed);
}
((HexEditorListener)listeners[i+1]).hexBytesChanged(e);
}
}
}
/**
* Returns whether the color of columns in the hex editor should alternate.
*
* @return Whether the column color alternates.
* @see #setAlternateColumnBG(boolean)
* @see #getAlternateRowBG()
*/
public boolean getAlternateColumnBG() {
return alternateColumnBG;
}
/**
* Returns whether the color of rows in the hex editor should alternate.
*
* @return Whether the row color alternates.
* @see #setAlternateRowBG(boolean)
* @see #getAlternateColumnBG()
*/
public boolean getAlternateRowBG() {
return alternateRowBG;
}
/**
* Returns the byte at the specified offset.
*
* @param offset The offset.
* @return The byte.
*/
public byte getByte(int offset) {
return table.getByte(offset);
}
/**
* Returns the number of bytes being edited.
*
* @return The number of bytes.
*/
public int getByteCount() {
return table.getByteCount();
}
/**
* Returns whether the selected bytes should also appear selected in
* the "ascii dump" column.
*
* @return Whether the selected bytes should also be selected in the
* "ascii dump" column.
* @see #setHighlightSelectionInAsciiDump(boolean)
*/
public boolean getHighlightSelectionInAsciiDump() {
return highlightSelectionInAsciiDump;
}
/**
* Returns the color used to highlight the selected bytes in the
* "ascii dump" column.
*
* @return The color used.
* @see #setHighlightSelectionInAsciiDumpColor(Color)
* @see #getHighlightSelectionInAsciiDump()
*/
public Color getHighlightSelectionInAsciiDumpColor() {
return highlightSelectionInAsciiDumpColor;
}
/**
* Returns the largest selection index.
*
* @return The largest selection index.
* @see #getSmallestSelectionIndex()
*/
public int getLargestSelectionIndex() {
return table.getLargestSelectionIndex();
}
/**
* Returns the smallest selection index.
*
* @return The smallest selection index.
* @see #getLargestSelectionIndex()
*/
public int getSmallestSelectionIndex() {
return table.getSmallestSelectionIndex();
}
/**
* Returns the table actually containing the hex data.
*
* @return The table.
*/
HexTable getTable() {
return table;
}
private void invokeAction(Action a) {
a.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
(String)a.getValue(Action.NAME),
EventQueue.getMostRecentEventTime(),
0));
}
/**
* Returns the cell representing the specified offset into the hex
* document.
*
* @param offset The offset into the document.
* @return The cell, in the form (row, col). If the
* specified offset is invalid, (-1, -1) is returned.
* @see #cellToOffset(int, int)
*/
public Point offsetToCell(int offset) {
return table.offsetToCell(offset);
}
/**
* Sets the contents in the hex editor to the contents of the specified
* file.
*
* @param fileName The name of the file to open.
* @throws IOException If an IO error occurs.
*/
public void open(String fileName) throws IOException {
table.open(fileName);
}
/**
* Sets the contents in the hex editor to the contents of the specified
* input stream.
*
* @param in An input stream.
* @throws IOException If an IO error occurs.
*/
public void open(InputStream in) throws IOException {
table.open(in);
}
/**
* "Pastes" the bytes in the clipboard into the current selection in
* the hex editor.
*
* @see #copy()
* @see #cut()
* @see #delete()
*/
public void paste() {
invokeAction(TransferHandler.getPasteAction());
}
/**
* Tries to redo the last action undone.
*
* @return Whether there is another action to redo after this one.
* @see #undo()
*/
public boolean redo() {
return table.redo();
}
/**
* Removes a range of bytes.
*
* @param offs The offset of the range of bytes to remove.
* @param len The number of bytes to remove.
* @see #replaceBytes(int, int, byte[])
*/
public void removeBytes(int offs, int len) {
table.removeBytes(offs, len);
table.changeSelectionByOffset(offs, false);
}
/**
* Removes the specified hex editor listener from this editor.
*
* @param l The listener to remove.
* @see #addHexEditorListener(HexEditorListener)
*/
public void removeHexEditorListener(HexEditorListener l) {
listenerList.remove(HexEditorListener.class, l);
}
/**
* Replaces a range of bytes.
*
* @param offset The offset of the range of bytes to replace.
* @param len The number of bytes to replace.
* @param bytes The bytes to replace the range with.
* @see #removeBytes(int, int)
* @see #replaceSelection(byte[])
*/
public void replaceBytes(int offset, int len, byte[] bytes) {
if (len==1) { // Just insert if 1 bytes is selected.
len = 0;
}
table.replaceBytes(offset, len, bytes);
table.changeSelectionByOffset(table.anchorSelectionIndex, false);
int count = bytes==null ? 0 : bytes.length;
table.setSelectionByOffsets(offset, offset+count-1);
}
/**
* Replaces the currently selected bytes (if >=1) with the specified
* new bytes.
*
* @param bytes The new bytes. If this is null or an empty
* array, calling this method simply removes the currently
* selected bytes.
* @see #replaceBytes(int, int, byte[])
*/
public void replaceSelection(byte[] bytes) {
int offset = table.getSmallestSelectionIndex();
int len = table.getLargestSelectionIndex()-offset+1;
replaceBytes(offset, len, bytes);
}
/**
* Sets whether the column color should alternate in the hex editor.
* This method fires a property change event of type
* {@link #PROPERTY_ALTERNATE_COLUMN_BG}.
*
* @param alternate Whether the column color should alternate.
* @see #getAlternateColumnBG()
* @see #setAlternateRowBG(boolean)
*/
public void setAlternateColumnBG(boolean alternate) {
if (alternate!=alternateColumnBG) {
this.alternateColumnBG = alternate;
table.repaint();
firePropertyChange(PROPERTY_ALTERNATE_COLUMN_BG,
!alternate, alternate);
}
}
/**
* Sets whether the row color should alternate in the hex editor. This
* method fires a property change event of type
* {@link #PROPERTY_ALTERNATE_ROW_BG}.
*
* @param alternate Whether the row color should alternate.
* @see #getAlternateRowBG()
* @see #setAlternateColumnBG(boolean)
*/
public void setAlternateRowBG(boolean alternate) {
if (alternate!=alternateRowBG) {
this.alternateRowBG = alternate;
table.repaint();
firePropertyChange(PROPERTY_ALTERNATE_ROW_BG,!alternate, alternate);
}
}
/**
* Sets whether the selected bytes should also appear selected in the
* "ascii dump" column. This method fires a property change event of
* type {@link #PROPERTY_HIGHLIGHT_ASCII_DUMP}.
*
* @param highlight Whether the selected bytes should also appear
* selected in the "ascii dump" column.
* @see #getHighlightSelectionInAsciiDump()
* @see #setHighlightSelectionInAsciiDumpColor(Color)
*/
public void setHighlightSelectionInAsciiDump(boolean highlight) {
if (highlight!=highlightSelectionInAsciiDump) {
highlightSelectionInAsciiDump = highlight;
table.repaint();
firePropertyChange(PROPERTY_HIGHLIGHT_ASCII_DUMP,
!highlight, highlight);
}
}
/**
* Sets what color should be used for the "ascii dump" selection.
* This method fires a property change event of type
* {@link #PROPERTY_ASCII_DUMP_HIGHLIGHT_COLOR}.
*
* @param c The color to use.
* @see #getHighlightSelectionInAsciiDumpColor()
* @see #setHighlightSelectionInAsciiDump(boolean)
*/
public void setHighlightSelectionInAsciiDumpColor(Color c) {
if (c!=null && !c.equals(highlightSelectionInAsciiDumpColor)) {
Color old = highlightSelectionInAsciiDumpColor;
highlightSelectionInAsciiDumpColor = c;
table.repaint();
firePropertyChange(PROPERTY_ASCII_DUMP_HIGHLIGHT_COLOR, old, c);
}
}
/**
* Sets the range of bytes to select in the hex editor.
*
* @param startOffs The first byte to select.
* @param endOffs The last byte to select.
*/
public void setSelectedRange(int startOffs, int endOffs) {
table.setSelectionByOffsets(startOffs, endOffs);
}
/**
* Toggles whether table's column header is visible.
*
* @param show Whether to show the table column header.
* @see #setShowRowHeader(boolean)
*/
public void setShowColumnHeader(boolean show) {
// Since table header is added to a parent JScrollPane as a
// column header, we have to remove it from the scrollpane to
// make it not visible.
setColumnHeaderView(show ? table.getTableHeader() : null);
}
/**
* Toggles whether the table's grid lines are visible. This method
* fires a property change event of type {@link #PROPERTY_SHOW_GRID}.
*
* @param show Whether grid lines are visible.
*/
public void setShowGrid(boolean show) {
// There is no "getShowGrid()" method.
if (show!=table.getShowHorizontalLines()) {
table.setShowGrid(show);
firePropertyChange(PROPERTY_SHOW_GRID, !show, show);
}
}
/**
* Toggles whether table's row header is visible.
*
* @param show Whether to show the table row header.
* @see #setShowColumnHeader(boolean)
*/
public void setShowRowHeader(boolean show) {
setRowHeaderView(show ? new HexEditorRowHeader(table) : null);
}
/**
* Tries to undo the last action.
*
* @return Whether there is another action to undo after this one.
* @see #redo()
*/
public boolean undo() {
return table.undo();
}
} © 2015 - 2025 Weber Informatics LLC | Privacy Policy