All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.ui.hex.swing.HexTableModel Maven / Gradle / Ivy

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 org.fife.ui.hex.ByteBuffer;

import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.ResourceBundle;


/**
 * The table model used by the JTable in the hex editor.
 *
 * @author Robert Futrell
 * @version 1.0
 */
public class HexTableModel extends AbstractTableModel {

	private static final long serialVersionUID = 1L;

	private HexEditor editor;
	private ByteBuffer doc;
	private int bytesPerRow;
	private UndoManager undoManager;
	private String[] columnNames;
	private byte[] bitBuf = new byte[16];
	private char[] dumpColBuf;
	private String[] byteStrVals;


	/**
	 * Creates the model.
	 *
	 * @param editor The parent hex editor.
	 * @param msg The resource bundle for localizations.
	 */
	public HexTableModel(HexEditor editor, ResourceBundle msg) {

		this.editor = editor;
		doc = new ByteBuffer(16);
		bytesPerRow = 16;

		undoManager = new UndoManager();

		columnNames = new String[17];
		for (int i=0; i<16; i++) {
			columnNames[i] = "+" + Integer.toHexString(i).toUpperCase();
		}
		columnNames[16] = msg.getString("AsciiDump");

		dumpColBuf = new char[16];
		Arrays.fill(dumpColBuf, ' ');

		byteStrVals = new String[256];
		for (int i=0; i0 ? 1 : 0);
	}


	/**
	 * Returns the value to display at the specified cell in the table, as
	 * a String.
	 *
	 * @param row The row of the cell.
	 * @param col The column of the cell.
	 * @return The value to display.
	 */
	public Object getValueAt(int row, int col) {

		if (col==bytesPerRow) {
			// Get ascii dump of entire row
			int pos = editor.cellToOffset(row, 0);
			if (pos==-1) { // A cleared row (from deletions)
				return "";
			}
			int count = doc.read(pos, bitBuf);
			for (int i=0; i0x7e) {
					ch = '.';
				}
				dumpColBuf[i] = ch;
			}
			return new String(dumpColBuf, 0,count);
		}

		int pos = editor.cellToOffset(row, col);
		// & with 0xff to convert to unsigned
		return pos==-1 ? "" : byteStrVals[doc.getByte(pos)&0xff];

	}


	/**
	 * Redoes the last undoable edit undone.
	 *
	 * @return Whether there is another operation that can be redone
	 *         after this one.
	 * @see #undo()
	 */
	public boolean redo() {
		boolean canRedo = undoManager.canRedo();
		if (canRedo) {
			undoManager.redo();
			canRedo = undoManager.canRedo();
		}
		else {
			UIManager.getLookAndFeel().provideErrorFeedback(editor);
		}
		return canRedo;
	}


	/**
	 * Removes bytes from the document.
	 *
	 * @param offset The offset at which to remove.
	 * @param len The number of bytes to remove.  If this value is
	 *        <= 0, nothing happens.
	 * @see #replaceBytes(int, int, byte[])
	 */
	public void removeBytes(int offset, int len) {
		replaceBytes(offset, len, null);
	}


	/**
	 * Replaces bytes in the document.
	 *
	 * @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 with.  If this is null
	 *        or an empty array, then this method call will be equivalent to
	 *        removeBytes(offset, len).
	 * @see #removeBytes(int, int)
	 */
	public void replaceBytes(int offset, int len, byte[] bytes) {

		byte[] removed = null;
		if (len>0) {
			removed = new byte[len];
			doc.remove(offset, len, removed);
		}

		byte[] added = null;
		if (bytes!=null && bytes.length>0) {
			doc.insertBytes(offset, bytes);
			added = (byte[])bytes.clone();
		}

		if (removed!=null || added!=null) {
			undoManager.addEdit(
					new BytesReplacedUndoableEdit(offset, removed, added));
			fireTableDataChanged();
			int addCount = added==null ? 0 : added.length;
			int remCount = removed==null ? 0 : removed.length;
			editor.fireHexEditorEvent(offset, addCount, remCount);
		}

	}


	/**
	 * Sets the bytes displayed to those in the given file.
	 *
	 * @param fileName The name of a file.
	 * @throws IOException If an IO error occurs reading the file.
	 * @see #setBytes(InputStream)
	 */
	public void setBytes(String fileName) throws IOException {
		doc = new ByteBuffer(fileName);
		undoManager.discardAllEdits();
		fireTableDataChanged();
		editor.fireHexEditorEvent(0, doc.getSize(), 0);
	}


	/**
	 * Sets the bytes displayed to those from an input stream.
	 *
	 * @param in The input stream to read from.
	 * @throws IOException If an IO error occurs.
	 * @see #setBytes(String)
	 */
	public void setBytes(InputStream in) throws IOException {
		doc = new ByteBuffer(in);
		undoManager.discardAllEdits();
		fireTableDataChanged();
		editor.fireHexEditorEvent(0, doc.getSize(), 0);
	}


	/**
	 * Sets the value of a cell in the table.
	 *
	 * @param value The new value for the cell.  This should be a String
	 *        representing an unsigned byte in hexadecimal, i.e.
	 *        "00" to "ff".
	 * @param row The row of the cell to change.
	 * @param col The column of the cell to change.
	 */
	public void setValueAt(Object value, int row, int col) {
		byte b = (byte)Integer.parseInt((String)value, 16);
		int offset = editor.cellToOffset(row, col);
		if (offset>-1) { // i.e., not col 17...
			byte old = doc.getByte(offset);
			if (old==b) {
				return;
			}
			doc.setByte(offset, b);
			undoManager.addEdit(new ByteChangedUndoableEdit(offset, old, b));
			fireTableCellUpdated(row, col);
			fireTableCellUpdated(row, bytesPerRow); // "Ascii dump" column
			editor.fireHexEditorEvent(offset, 1, 1);
		}
	}


	/**
	 * Undoes the previous undoable edit.
	 *
	 * @return Whether there is another operation that can be undone
	 *         after this one.
	 * @see #redo()
	 */
	public boolean undo() {
		boolean canUndo = undoManager.canUndo();
		if (canUndo) {
			undoManager.undo();
			canUndo = undoManager.canUndo();
		}
		else {
			UIManager.getLookAndFeel().provideErrorFeedback(editor);
		}
		return canUndo;
	}


	/**
	 * An "undoable event" representing a single byte changing value.
	 *
	 * @author Robert Futrell
	 * @version 1.0
	 */
	private class ByteChangedUndoableEdit extends AbstractUndoableEdit {

		private static final long serialVersionUID = 1L;

		private int offs;
		private byte oldVal;
		private byte newVal;

		public ByteChangedUndoableEdit(int offs, byte oldVal, byte newVal) {
			this.offs = offs;
			this.oldVal = oldVal;
			this.newVal = newVal;
		}

		public void undo() {
			super.undo();
			if (getByteCount()0) {
				endOffs += toAdd.length - 1;
			}
			editor.setSelectedRange(offs, endOffs);
			editor.fireHexEditorEvent(offs, addCount, remCount);
		}

	}


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy