//============================================================================== // HexDumpGui.java //============================================================================== package tribble.gui; // System imports import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.Class; import java.lang.Integer; import java.lang.Runnable; import java.lang.String; import java.lang.System; import java.lang.Thread; import java.lang.Throwable; import java.util.Date; import java.util.TimeZone; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.event.TableModelEvent; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; // Local imports // (None) /******************************************************************************* * Hexadecimal file displayer. * Displays the contents of a file in hexadecimal form in a GUI window. * * *

* Bugs * *

*

* * * @version $Revision: 1.11 $ $Date: 2005/06/27 03:42:14 $ * @since 1.1, 2005-04-15 * @author David R. Tribble * (david@tribble.com). *
* * Copyright ©2005 by David R. Tribble, all rights reserved. *
* Permission is granted to freely use and distribute this source * code provided that the original copyright and authorship notices * remain intact. * * @see tribble.util.HexDump */ public class HexDumpGui extends javax.swing.JFrame implements java.awt.event.ActionListener, java.awt.event.AdjustmentListener { // Identification /** Revision information. */ static final String REV = "@(#)tribble/gui/HexDumpGui.java $Revision: 1.11 $ $Date: 2005/06/27 03:42:14 $\n"; static final String PROG = "HexDumpGui"; static final String AUTHOR = "David R. Tribble"; // 'Help>About' display text static final String[] ABOUT = new String[5]; { String s; // Build the 'Help>About' display text ABOUT[0] = "Displays the contents of a file " + "in hexadecimal and character form."; ABOUT[1] = "\n"; ABOUT[2] = "Copyright \u00A9" + "2005 by " + AUTHOR; ABOUT[3] = "Author: " + AUTHOR; s = "$Revision: 1.11 $"; ABOUT[4] = s.substring(1, s.length()-2); s = "$Date: 2005/06/27 03:42:14 $"; ABOUT[4] += ", " + s.substring(1, 17); ///+TEMPORARY ABOUT[4] += "\n[BETA VERSION, INCOMPLETE]"; }; // 'Help>Usage' display text static final String[] HELP = { // The 'Help>Usage' display text "This utility program displays a data file in its raw form, i.e., it" + " displays the contents of the file as a sequence of bytes. " + " The contents are displayed in both hexadecimal and character" + " form.\n", "\n", "The \"File Information\" portion of the window shows information about" + " the displayed file.\n", "\n", "The contents of the data file is displayed as a table of three" + " columns. " + " The first column is the address, or offset, of the line of" + " data, which is the number of bytes from the beginning of the" + " file. " + " The second column displays 16 bytes of data from the file in" + " hexadecimal form. " + " The third column displays the data bytes in character form.\n", "\n", "Hexadecimal newline character codes (linefeed 0A, vertical tab 0B," + " formfeed 0C, and carriage return 0D) are followed by a bullet" + " symbol, making it easier to see line breaks within text data" + " files. " + " Similarly, newline characters are displayed as bullet symbols. " + " Other unprintable (control) characters are displayed as box" + " symbols.\n", "\n", "The table may be repositioned by moving the scrollbar or by using the" + " cursor movement keys (Up, Down, PgUp, PgDn, Ctrl-Home," + " Ctrl-End, etc.). " + " Rows within the table can be selected and copied using the" + " mouse and the standard copy (Ctrl-C, Ctrl-Ins) buttons.\n", "\n", "Clicking on the Address, Data, and Character column headings causes" + " the column to change format, i.e., from hexadecimal to decimal" + " to octal.\n", }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Package private constants // Manifest constants static final int BUF_SIZE = 32*1024; static final int TABLE_ROWS = 25; // File information labels static final String LABEL_FILE = "File: "; static final String LABEL_DIR = "Directory: "; static final String LABEL_MOD = "Modified: "; static final String LABEL_ADDR = "Address: "; static final String LABEL_SIZE = "Size: "; // Menu titles static final String MENU_FILE = "File"; static final String MENU_FILE_OPEN = "Open"; static final String MENU_FILE_EXIT = "Exit"; static final String MENU_VIEW = "View"; static final String MENU_VIEW_ADDR = "Address"; static final String MENU_VIEW_DATA = "Data"; static final String MENU_VIEW_CHAR = "Characters"; static final String MENU_VIEW_AS_HEX = "Hex"; static final String MENU_VIEW_AS_DEC = "Decimal"; static final String MENU_VIEW_AS_OCT = "Octal"; static final String MENU_VIEW_AS_ASCII = "ASCII"; static final String MENU_VIEW_AS_EBCDIC = "EBCDIC"; static final String MENU_HELP = "Help"; static final String MENU_HELP_USAGE = "Usage"; static final String MENU_HELP_ABOUT = "About"; // Menu accelerator characters static final char ACC_FILE = 'F'; static final char ACC_FILE_OPEN = 'O'; static final char ACC_FILE_EXIT = 'x'; static final char ACC_VIEW = 'V'; static final char ACC_VIEW_ADDR = 'A'; static final char ACC_VIEW_DATA = 'D'; static final char ACC_VIEW_CHAR = 'C'; static final char ACC_VIEW_AS_HEX = 'H'; static final char ACC_VIEW_AS_DEC = 'D'; static final char ACC_VIEW_AS_OCT = 'O'; static final char ACC_VIEW_AS_ASCII = 'A'; static final char ACC_VIEW_AS_EBCDIC = 'E'; static final char ACC_HELP = 'H'; static final char ACC_HELP_USAGE = 'U'; static final char ACC_HELP_ABOUT = 'A'; // Menu tip help texts static final String TIP_FILE = "File menu"; static final String TIP_FILE_OPEN = "Open a file"; static final String TIP_FILE_EXIT = "Quit this program"; static final String TIP_VIEW = "View menu"; static final String TIP_VIEW_ADDR = "View address"; static final String TIP_VIEW_DATA = "View data"; static final String TIP_VIEW_CHAR = "View characters"; static final String TIP_VIEW_AS_HEX = "View in hexadecimal form"; static final String TIP_VIEW_AS_DEC = "View in decimal form"; static final String TIP_VIEW_AS_OCT = "View in octal form"; static final String TIP_VIEW_AS_ASCII = "View as ASCII"; static final String TIP_VIEW_AS_EBCDIC = "View as EBCDIC"; static final String TIP_HELP = "Help menu"; static final String TIP_HELP_USAGE = "Help about using this program"; static final String TIP_HELP_ABOUT = "Information about this program"; // File Information panel tip help texts static final String TIP_INFO_FILE = "File name"; static final String TIP_INFO_DIR = "Directory (folder) containing file"; static final String TIP_INFO_MOD = "File modification date"; static final String TIP_INFO_SIZE = "Length of file (bytes)"; static final String TIP_INFO_ADDR = "Current location within file"; // Menu action command names static final String CMD_FILE = "file"; static final String CMD_FILE_OPEN = "file>open"; static final String CMD_FILE_EXIT = "file>exit"; static final String CMD_VIEW = "view"; static final String CMD_VIEW_ADDR = "view>addr"; static final String CMD_VIEW_DATA = "view>data"; static final String CMD_VIEW_CHAR = "view>char"; static final String CMD_VIEW_ADDR_HEX = "view>addr>hex"; static final String CMD_VIEW_ADDR_DEC = "view>addr>dec"; static final String CMD_VIEW_ADDR_OCT = "view>addr>oct"; static final String CMD_VIEW_DATA_HEX = "view>data>hex"; static final String CMD_VIEW_DATA_DEC = "view>data>dec"; static final String CMD_VIEW_DATA_OCT = "view>data>oct"; static final String CMD_VIEW_CHAR_ASC = "view>char>asc"; static final String CMD_VIEW_CHAR_EBC = "view>char>ebc"; static final String CMD_HELP = "help"; static final String CMD_HELP_USAGE = "help>usage"; static final String CMD_HELP_ABOUT = "help>about"; static final String CMD_BUT_ADDR = ":addr"; static final String CMD_BUT_DATA = ":data"; static final String CMD_BUT_CHAR = ":char"; static final String CMD_SET_ADDR = "set:addr"; // Hex dump table column numbers static final int COL_ADDRESS = 0; static final int COL_DATA = 1; static final int COL_CHARACTERS = 2; static final int COL__MAX = 3; // Address radix types static final short ADDR_RADIX_HEX = 0; static final short ADDR_RADIX_DEC = 1; static final short ADDR_RADIX_OCT = 2; // Various color settings static final Color COLOR_SELECT = new Color(0xFF, 0xFF, 0xC0); static final Color COLOR_BACKGROUND = Color.WHITE; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public static methods /*************************************************************************** * Execute this class. * * @param args * Command line arguments. * * @since 1.1, 2005-04-15 */ public static void main(final String[] args) throws Exception { Runnable thr; // Create and show this application's GUI createAndShowGUI(args); System.out.println("% main: return"); System.out.println("%----------------------------------------"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private static methods /*************************************************************************** * Create the GUI and show it. * For thread safety, this method should be invoked from the * event-dispatching thread. * * @param args * Command line arguments. * * @since 1.1, 2005-04-15 */ private static void createAndShowGUI(String[] args) { HexDumpGui frame; System.out.println("% createAndShowGUI()"); // Create and set up the hex dump window/frame ///JFrame.setDefaultLookAndFeelDecorated(true); frame = new HexDumpGui(); try { ///+Reorder these, to correct the chooseFile()/exec() ordering problem frame.exec(args); // Display the window/frame frame.setUpGui(); frame.setFileInfo(); frame.pack(); frame.m_table.requestFocus(); frame.setVisible(true); } catch (IOException ex) { // Error encountered ex.printStackTrace(System.out); ///+INCOMPLETE //...punt? } System.out.println("% createAndShowGUI: return"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Protected variables /** Main content pane for this frame. */ protected JPanel m_cont; /** Frame menu bar. */ protected JMenuBar m_menu; /** Displayed file info: File name. */ protected JTextField m_fileNameText; /** Displayed file info: File directory. */ protected JTextField m_fileDirText; /** Displayed file info: Modification date, local timezone. */ protected JTextField m_fileDateText1; /** Displayed file info: Modification date, UTC (Z) timezone. */ protected JTextField m_fileDateText2; /** Displayed file info: Length. */ protected JTextField m_fileLenText; /** Displayed file info: Current location/offset. */ protected JTextField m_fileAddress; /** Current address display radix setting. */ protected short m_addrRadix = ADDR_RADIX_HEX; /** Hexadecimal display table. */ protected JTable m_table; /** Hexadecimal table display model. */ protected MyTableModel m_tableModel; /** Table display header. */ protected JTableHeader m_tableHdr; /** Hexadecimal table display scrollbar. */ protected JScrollBar m_scrollBar; /** Table display row height (pixels). */ protected int m_rowHeight; /** Current date/time. */ protected Date m_now = new Date(); /** Current directory. */ protected File m_curDir = new File(System.getProperty("user.dir")); /** Date/time formatter, local timezone. */ protected SimpleDateFormat m_dateFmtLoc = new SimpleDateFormat("EEE yyyy-MM-dd HH:mm:ss.SSS zzz"); /** Date/time formatter, UTC (Z) timezone. */ protected SimpleDateFormat m_dateFmtZ = new SimpleDateFormat("EEE yyyy-MM-dd HH:mm:ss.SSS zzz"); { m_dateFmtZ.setTimeZone(TimeZone.getTimeZone("UTC")); } /** Number formatter. */ protected DecimalFormat m_numFmt = new DecimalFormat("#,##0"); /** Location/offset of the first address of the dumped data file. */ protected long m_startAddr = 0x0000000000000000L; /** Location/offset of the last address of the dumped data file. */ protected long m_endAddr = 0x7FFFFFFFFFFFFFFFL; /** File data stream. */ protected RandomAccessFile m_in; /** File name to dump. */ protected File m_fname; /** Length of the data file. */ protected long m_fileSize; /** Currently displayed file data block. */ protected byte[][] m_data = { new byte[BUF_SIZE], new byte[BUF_SIZE], }; /** Lowest location/offset of the file data buffers. */ protected long m_lowAddr; /** Character buffer. */ protected char[] m_charBuf = new char[80]; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public constructors /*************************************************************************** * Constructor. * * @since 1.1, 2005-04-15 */ public HexDumpGui() { // Initialize super(PROG); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public methods /************************************************************************** * Handles menubar actions for the top frame. * * @param ev * The menubar event, which will contain a command string equal to one of the * CMD_XXX_YYY constants. * * @since 1.1, 2005-04-30 */ public void actionPerformed(ActionEvent ev) //implements java.awt.event.ActionListener { String cmd; String val; // Retrieve the action's command cmd = ev.getActionCommand(); System.out.println("% actionPerformed: '" + cmd + "'"); if (cmd == CMD_FILE_EXIT) { // Terminate this program shutDown(); } else if (cmd == CMD_FILE_OPEN) { // Open a new data file closeFile(); if (chooseFile()) { ///+INCOMPLETE ///...readFile(); setFileInfo(); ///...set a flag to reread and redisplay the new file? //readFile(); //reDisplay(); } } else if (cmd == CMD_VIEW_ADDR_HEX) { // Set the address display radix ///+INCOMPLETE } else if (cmd == CMD_VIEW_ADDR_DEC) { // Set the address display radix ///+INCOMPLETE } else if (cmd == CMD_VIEW_ADDR_OCT) { // Set the address display radix ///+INCOMPLETE } else if (cmd == CMD_VIEW_DATA_HEX) { // Set the data display radix ///+INCOMPLETE } else if (cmd == CMD_VIEW_DATA_DEC) { // Set the data display radix ///+INCOMPLETE } else if (cmd == CMD_VIEW_DATA_OCT) { // Set the data display radix ///+INCOMPLETE } else if (cmd == CMD_VIEW_CHAR_ASC) { // Set the character display type ///+INCOMPLETE } else if (cmd == CMD_VIEW_CHAR_EBC) { // Set the character display type ///+INCOMPLETE } else if (cmd == CMD_HELP_USAGE) { // Show program help info showHelp(); } else if (cmd == CMD_HELP_ABOUT) { // Show program help info showAbout(); } else if (cmd == CMD_SET_ADDR) { ///+INCOMPLETE // Set a new file address/offset val = m_fileAddress.getText(); System.out.println("% address: '" + val + "'"); ///... } } /************************************************************************** * Handles scrollbar adjustment events for the top frame. * * @param ev * The scrollbar event. * * @since 1.9, 2005-06-02 */ public void adjustmentValueChanged(AdjustmentEvent ev) //implements java.awt.event.AdjustmentListener { int val; ///+INCOMPLETE val = ev.getValue(); // Pixel count ///System.out.println("% scroll: " + val + "px"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Protected methods /*************************************************************************** * Finalization. * * @since 1.8, 2005-05-31 */ protected synchronized void finalize() throws Throwable { System.out.println("% finalize"); // Shut down this GUI frame shutDown(); // Cascade super.finalize(); } /*************************************************************************** * Display a 'Help>Usage' window for this program. * * @since 1.10, 2005-06-05 */ protected void showHelp() { JTextArea text; JScrollPane pane; // Build a usage message text = new JTextArea(30, 40); text.setEditable(false); text.setLineWrap(true); text.setWrapStyleWord(true); text.setSize(400, 0); for (int i = 0; i < HELP.length; i++) text.append(HELP[i]); pane = new JScrollPane(text); text.requestFocus(); //? // Display the message JOptionPane.showMessageDialog(null, pane, PROG + " - Usage", JOptionPane.PLAIN_MESSAGE); } /*************************************************************************** * Display a 'Help>About' window for this program. * * @since 1.7, 2005-05-29 */ protected void showAbout() { // Display an info message JOptionPane.showMessageDialog(this, ABOUT, PROG + " - About", JOptionPane.PLAIN_MESSAGE); } /*************************************************************************** * Execute this class. * * @param args * Command line arguments. * * @throws IOException * Thrown if an I/O (read) error occurs while reading the contents of a file * to be dumped. * * @since 1.1, 2005-04-15 */ protected void exec(String[] args) throws IOException { int argc; // Parse the command line options argc = 0; ///... if (argc >= args.length) { if (!chooseFile()) { ///+INCOMPLETE // Terminate this program return; } } else m_fname = new File(args[0]); if (m_fname != null) System.out.println("% fname='" + m_fname.getName() + "'"); ///+Handle IOException // Read the selected data file readFile(); ///+INCOMPLETE //... } /*************************************************************************** * Read the initial contents of the data file to dump. * * @throws IOException * Thrown if an I/O (read) error occurs while reading the contents of a file * to be dumped. * * @since 1.1, 2005-05-01 */ protected void readFile() throws IOException { // Close the previous file, if any if (m_in != null) m_in.close(); m_in = null; m_fileSize = 0; // Check the data file if (!m_fname.exists()) throw new IOException("Missing file: '" + m_fname.getPath() + "'"); // Read data from the selected file m_fileSize = m_fname.length(); this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); m_in = new RandomAccessFile(m_fname, "r"); if (m_in.read(m_data[0]) >= m_data.length) m_in.read(m_data[1]); this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); ///+Handle IOException ///+INCOMPLETE //... } /*************************************************************************** * Shut down this GUI frame. * * @since 1.1, 2005-05-01 */ protected void shutDown() { System.out.println("% shutDown"); // Clean up closeFile(); // Close this GUI window/frame System.out.println("% dispose"); this.dispose(); System.out.println("% system.exit"); System.exit(0); } /*************************************************************************** * Close the data file. * * @since 1.1, 2005-05-01 */ protected void closeFile() { System.out.println("% closeFile"); // Clean up if (m_in != null) { try { m_in.close(); } catch (IOException ex) { // Ignore } m_in = null; } } /*************************************************************************** * Set up this GUI frame. * * @since 1.1, 2005-04-15 */ protected void setUpGui() { int w; //-------------------------------------- // Initialize this window/frame this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); w = (int) m_now.getTime(); this.setLocation(w%200 + 50, (w >> 3)%150 + 30); // Clean up previous display, if any if (m_cont != null) { System.out.println("% dispose prev frame"); m_cont = null; this.dispose(); } //-------------------------------------- // Create a menubar { JMenu menu; JMenu menu2; JMenuItem mi; m_menu = new JMenuBar(); this.setJMenuBar(m_menu); ///+Replace redundant code with static helper method(s) // Build the "File" menu bar menu = new JMenu(MENU_FILE); menu.setMnemonic(ACC_FILE); menu.setToolTipText(TIP_FILE); menu.setActionCommand(CMD_FILE); m_menu.add(menu); // Build the "File>Open" menu item mi = new JMenuItem(MENU_FILE_OPEN); mi.setMnemonic(ACC_FILE_OPEN); mi.setToolTipText(TIP_FILE_OPEN); mi.setActionCommand(CMD_FILE_OPEN); mi.addActionListener(this); mi.setEnabled(false); //+TEMPORARY menu.add(mi); // Build the "File>Exit" menu item mi = new JMenuItem(MENU_FILE_EXIT); mi.setMnemonic(ACC_FILE_EXIT); mi.setToolTipText(TIP_FILE_EXIT); mi.setActionCommand(CMD_FILE_EXIT); mi.addActionListener(this); menu.add(mi); //-------------------------------------- // Build the "View" menu bar menu = new JMenu(MENU_VIEW); menu.setMnemonic(ACC_VIEW); menu.setToolTipText(TIP_VIEW); menu.setActionCommand(CMD_VIEW); m_menu.add(menu); // Build the "View>Addr" menu item menu2 = new JMenu(MENU_VIEW_ADDR); menu2.setMnemonic(ACC_VIEW_ADDR); menu2.setToolTipText(TIP_VIEW_ADDR); menu2.setActionCommand(CMD_VIEW_ADDR); menu.add(menu2); // Build the "View>Addr>Hex" menu item mi = new JMenuItem(MENU_VIEW_AS_HEX); mi.setMnemonic(ACC_VIEW_AS_HEX); mi.setToolTipText(TIP_VIEW_AS_HEX); mi.setActionCommand(CMD_VIEW_ADDR_HEX); mi.addActionListener(this); mi.setEnabled(false); //+TEMPORARY menu2.add(mi); // Build the "View>Addr>Decimal" menu item mi = new JMenuItem(MENU_VIEW_AS_DEC); mi.setMnemonic(ACC_VIEW_AS_DEC); mi.setToolTipText(TIP_VIEW_AS_DEC); mi.setActionCommand(CMD_VIEW_ADDR_DEC); mi.addActionListener(this); mi.setEnabled(false); //+TEMPORARY menu2.add(mi); // Build the "View>Addr>Octal" menu item mi = new JMenuItem(MENU_VIEW_AS_OCT); mi.setMnemonic(ACC_VIEW_AS_OCT); mi.setToolTipText(TIP_VIEW_AS_OCT); mi.setActionCommand(CMD_VIEW_ADDR_OCT); mi.addActionListener(this); mi.setEnabled(false); //+TEMPORARY menu2.add(mi); // Build the "View>Data" menu item menu2 = new JMenu(MENU_VIEW_DATA); menu2.setMnemonic(ACC_VIEW_DATA); menu2.setToolTipText(TIP_VIEW_DATA); menu2.setActionCommand(CMD_VIEW_DATA); menu.add(menu2); // Build the "View>Data>Hex" menu item mi = new JMenuItem(MENU_VIEW_AS_HEX); mi.setMnemonic(ACC_VIEW_AS_HEX); mi.setToolTipText(TIP_VIEW_AS_HEX); mi.setActionCommand(CMD_VIEW_DATA_HEX); mi.addActionListener(this); mi.setEnabled(false); //+TEMPORARY menu2.add(mi); // Build the "View>Data>Decimal" menu item mi = new JMenuItem(MENU_VIEW_AS_DEC); mi.setMnemonic(ACC_VIEW_AS_DEC); mi.setToolTipText(TIP_VIEW_AS_DEC); mi.setActionCommand(CMD_VIEW_DATA_DEC); mi.addActionListener(this); mi.setEnabled(false); //+TEMPORARY menu2.add(mi); // Build the "View>Data>Octal" menu item mi = new JMenuItem(MENU_VIEW_AS_OCT); mi.setMnemonic(ACC_VIEW_AS_OCT); mi.setToolTipText(TIP_VIEW_AS_OCT); mi.setActionCommand(CMD_VIEW_DATA_OCT); mi.addActionListener(this); mi.setEnabled(false); //+TEMPORARY menu2.add(mi); // Build the "View>Char" menu item menu2 = new JMenu(MENU_VIEW_CHAR); menu2.setMnemonic(ACC_VIEW_CHAR); menu2.setToolTipText(TIP_VIEW_CHAR); menu2.setActionCommand(CMD_VIEW_CHAR); menu.add(menu2); // Build the "View>Char>Normal" menu item mi = new JMenuItem(MENU_VIEW_AS_ASCII); mi.setMnemonic(ACC_VIEW_AS_ASCII); mi.setToolTipText(TIP_VIEW_AS_ASCII); mi.setActionCommand(CMD_VIEW_CHAR_ASC); mi.addActionListener(this); mi.setEnabled(false); //+TEMPORARY menu2.add(mi); // Build the "View>Char>EBCDIC" menu item mi = new JMenuItem(MENU_VIEW_AS_EBCDIC); mi.setMnemonic(ACC_VIEW_AS_EBCDIC); mi.setToolTipText(TIP_VIEW_AS_EBCDIC); mi.setActionCommand(CMD_VIEW_CHAR_EBC); mi.addActionListener(this); mi.setEnabled(false); //+TEMPORARY menu2.add(mi); //-------------------------------------- // Build the "Help" menu bar menu = new JMenu(MENU_HELP); menu.setMnemonic(ACC_HELP); menu.setToolTipText(TIP_HELP); menu.setActionCommand(CMD_HELP); ///m_menu.setHelpMenu(menu); m_menu.add(menu); // Build the "Help>Usage" menu item mi = new JMenuItem(MENU_HELP_USAGE); mi.setMnemonic(ACC_HELP_USAGE); mi.setToolTipText(TIP_HELP_USAGE); mi.setActionCommand(CMD_HELP_USAGE); mi.addActionListener(this); menu.add(mi); // Build the "Help>About" menu item mi = new JMenuItem(MENU_HELP_ABOUT); mi.setMnemonic(ACC_HELP_ABOUT); mi.setToolTipText(TIP_HELP_ABOUT); mi.setActionCommand(CMD_HELP_ABOUT); mi.addActionListener(this); menu.add(mi); } //-------------------------------------- // Create and set up the main content pane within the window/frame m_cont = new JPanel(new BorderLayout()); m_cont.setOpaque(true); this.setContentPane(m_cont); //-------------------------------------- // Create the file info panel { JPanel pane; JPanel rowPane; Font font; JTextField text; JLabel lbl; GridBagConstraints c0; GridBagConstraints c1; GridBagConstraints c2; // Set up the "File Information" panel pane = new JPanel(new GridBagLayout()); font = pane.getFont(); font = new Font(font.getName(), Font.BOLD, font.getSize()); c0 = new GridBagConstraints(); c0.gridheight = 1; c0.gridwidth = 1; c0.gridy = 0; c0.gridx = 0; c0.fill = GridBagConstraints.HORIZONTAL; c0.weightx = 0.0; c1 = new GridBagConstraints(); c1.gridheight = 1; c1.gridwidth = 5; c1.gridy = 0; c1.gridx = 1; c1.fill = GridBagConstraints.HORIZONTAL; c1.weightx = 1.0; c2 = new GridBagConstraints(); c2.gridheight = 1; c2.gridwidth = 2; c2.gridy = 0; c2.gridx = 1; c2.fill = GridBagConstraints.HORIZONTAL; c2.weightx = 0.0; // Set up the "File:" row text = new JTextField(); text.setBackground(COLOR_BACKGROUND); text.setFont(font); text.setEditable(false); text.setToolTipText(TIP_INFO_FILE); m_fileNameText = text; lbl = new JLabel(LABEL_FILE, SwingConstants.RIGHT); lbl.setLabelFor(text); pane.add(lbl, c0); pane.add(text, c1); // Set up the "Directory:" row text = new JTextField(); text.setBackground(COLOR_BACKGROUND); text.setFont(font); text.setEditable(false); text.setToolTipText(TIP_INFO_DIR); m_fileDirText = text; lbl = new JLabel(LABEL_DIR, SwingConstants.RIGHT); lbl.setLabelFor(text); c0.gridy++; pane.add(lbl, c0); c1.gridy = c0.gridy; pane.add(text, c1); // Set up the "Modified:" row text = new JTextField(); text.setBackground(COLOR_BACKGROUND); text.setFont(font); text.setEditable(false); text.setToolTipText(TIP_INFO_MOD); m_fileDateText1 = text; lbl = new JLabel(LABEL_MOD, SwingConstants.RIGHT); lbl.setLabelFor(text); c0.gridy++; pane.add(lbl, c0); c2.gridy = c0.gridy; pane.add(text, c2); text = new JTextField(); text.setBackground(COLOR_BACKGROUND); text.setFont(font); text.setEditable(false); text.setToolTipText(TIP_INFO_MOD); m_fileDateText2 = text; c2.gridx += 2; pane.add(text, c2); c2.gridx -= 2; // Set up the "Size:" row text = new JTextField(); text.setBackground(COLOR_BACKGROUND); text.setFont(font); text.setEditable(false); text.setToolTipText(TIP_INFO_SIZE); m_fileLenText = text; lbl = new JLabel(LABEL_SIZE, SwingConstants.RIGHT); lbl.setLabelFor(text); c0.gridy++; pane.add(lbl, c0); c2.gridy = c0.gridy; pane.add(text, c2); // Set up the "Address:" row text = new JTextField(); text.setBackground(COLOR_BACKGROUND); text.setFont(font); text.setEditable(false); ///+TEMPORARY text.setToolTipText(TIP_INFO_ADDR); text.setActionCommand(CMD_SET_ADDR); text.addActionListener(this); m_fileAddress = text; lbl = new JLabel(LABEL_ADDR, SwingConstants.RIGHT); lbl.setLabelFor(text); c0.gridx += 3; c0.insets = new Insets(0, 12, 0, 0); pane.add(lbl, c0); c2.gridy = c0.gridy; c2.gridx = c0.gridx + 1; c2.gridwidth = 1; pane.add(text, c2); // Set up the border for the "File Information" panel pane.setBorder( BorderFactory.createCompoundBorder( BorderFactory.createEtchedBorder(), BorderFactory.createEmptyBorder(3, 5, 3, 3))); // Add the "File Information" panel to this frame m_cont.add(pane, BorderLayout.NORTH); } //+THIS needs to be redrawn after each chooseFile() call //-------------------------------------- // Create the scrollable hex dump table { JScrollPane pane; Font font; FontMetrics met; MyHeaderRenderer[] hRend; MyCellRenderer cRend; TableColumnModel mdl; TableColumn col; Dimension dim; int tw; int th; // Create and initialize the hex display table m_tableModel = new MyTableModel(this); m_table = new JTable(m_tableModel); m_table.setShowHorizontalLines(false); m_table.setSelectionBackground(COLOR_SELECT); m_table.setCellSelectionEnabled(true); m_table.setRowSelectionAllowed(true); m_table.setColumnSelectionAllowed(false); //m_table.setDoubleBuffered(true); font = m_table.getFont(); font = new Font("monospaced", font.getStyle(), font.getSize()); m_table.setFont(font); met = m_cont.getFontMetrics(font); mdl = m_table.getColumnModel(); tw = 35; // Establish appropriate column widths hRend = new MyHeaderRenderer[3]; cRend = new MyCellRenderer(this); col = mdl.getColumn(COL_ADDRESS); hRend[COL_ADDRESS] = new MyHeaderRenderer(this, COL_ADDRESS); col.setHeaderRenderer(hRend[0]); col.setCellRenderer(cRend); w = met.stringWidth("9,999,999,999"); System.out.println("% text.Addr.width: " + w); col.setMaxWidth(w + 16); col.setMinWidth(w + 4); col.setPreferredWidth(w + 4); tw += w; col = mdl.getColumn(COL_DATA); hRend[COL_DATA] = new MyHeaderRenderer(this, COL_DATA); col.setHeaderRenderer(hRend[1]); col.setCellRenderer(cRend); w = met.stringWidth("FF FF FF FF/ FF FF FF FF/ " + "FF FF FF FF/ FF FF FF FF/"); System.out.println("% text.Data.width: " + w); col.setMaxWidth(w + 16); col.setMinWidth(w + 4); col.setPreferredWidth(w + 4); tw += w; col = mdl.getColumn(COL_CHARACTERS); hRend[COL_CHARACTERS] = new MyHeaderRenderer(this, COL_CHARACTERS); col.setHeaderRenderer(hRend[2]); col.setCellRenderer(cRend); w = met.stringWidth("WWWWWWWWWWWWWWWW"); System.out.println("% text.Char.width: " + w); col.setMaxWidth(w + 16); col.setMinWidth(w + 4); col.setPreferredWidth(w + 4); tw += w; System.out.println("% getTotalColumnWidth: " + mdl.getTotalColumnWidth()); // Establish an appropriate size for the scroll pane th = m_table.getRowHeight(); m_rowHeight = th; System.out.println("% row height: " + th); th = TABLE_ROWS*th - 1; dim = m_table.getPreferredScrollableViewportSize(); System.out.println("% preferredScrollableViewportSize(" + dim.width + "," + dim.height + ")->(" + tw + "," + th + ")"); dim.width = tw; dim.height = th; m_table.setPreferredScrollableViewportSize(dim); // Create and initialize a mouse handler for the table header m_tableHdr = m_table.getTableHeader(); m_tableHdr.addMouseListener( new MyMouseListener(this, hRend, m_tableModel)); // Create and initialize the scroll pane for the table pane = new JScrollPane(m_table, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); m_cont.add(pane, BorderLayout.CENTER); m_scrollBar = pane.getVerticalScrollBar(); m_scrollBar.addAdjustmentListener(this); } } /*************************************************************************** * Set the displayed file information for the current data file. * * @since 1.7, 2005-05-09 */ protected void setFileInfo() { String s; String s2; System.out.println("% setFileInfo: " + (m_fname != null ? m_fname.getPath() : "null")); // Fill the "File:" display field s = ""; if (m_fname != null) s = ' ' + m_fname.getName(); m_fileNameText.setText(s); // Set the frame title this.setTitle(PROG + " - " + s); // Fill the "Directory:" display field s = ""; if (m_fname != null) { s = m_fname.getAbsoluteFile().getParent(); System.out.println("% setFileInfo: abspath: " + s); if (s == null) s = File.pathSeparator; if (s.length() > 1 && s.charAt(1) == ':') // Win32 s = s.substring(0, 1).toUpperCase() + s.substring(1); s = ' ' + s; } m_fileDirText.setText(s); // Fill the "Modified:" display field if (m_fname != null && m_fname.exists()) { long mtime; // Fill the "Modified:" display field s = "Unknown"; s2 = "Unknown"; mtime = m_fname.lastModified(); if (mtime > 0) { Date t; t = new Date(mtime); s = ' ' + m_dateFmtLoc.format(t); s2 = ' ' + m_dateFmtZ.format(t); } m_fileDateText1.setText(s); m_fileDateText2.setText(s2); // Fill the "Modified:" display field s = "Unknown"; m_fileSize = m_fname.length(); if (m_fileSize >= 0) { String d; String h; d = m_numFmt.format(m_fileSize); h = Long.toHexString(m_fileSize).toUpperCase(); while (h.length() % 4 > 0) h = '0' + h; s = ' ' + d + " (" + h + "H)"; // + " Bytes" System.out.println("% fileSize: " + s); } m_fileLenText.setText(s); // Fill the "Address:" display field setFileAddress(m_lowAddr); ///+REDO } else { // Fill the display fields with blanks m_fileDateText1.setText(""); m_fileDateText2.setText(""); m_fileLenText.setText(""); setFileAddress(0x00000000); } } /*************************************************************************** * Set the displayed file address/offset information for the current data * file. * * @param addr * The updated current address/offset of the displayed data file. * * @since 1.8, 2005-05-31 */ protected void setFileAddress(long addr) { String s; // Fill the "Address:" display field switch (m_addrRadix) { case ADDR_RADIX_HEX: default: s = addressAsHex(addr); break; case ADDR_RADIX_DEC: s = addressAsDec(addr, false); break; case ADDR_RADIX_OCT: s = addressAsOct(addr); break; } //System.out.println("% location: " + s); m_fileAddress.setText(' ' + s); } /*************************************************************************** * Open a file chooser dialog and allow the user to select a file to dump. * * @return * True if a file was chosen, otherwise false. * * @since 1.1, 2005-04-30 */ protected boolean chooseFile() { JFileChooser fcDiag; int rc; System.out.println("% chooseFile: curDir='" + m_curDir.getPath() + "'"); // Open a file chooser dialog fcDiag = new JFileChooser(m_curDir); fcDiag.setFileSelectionMode(JFileChooser.FILES_ONLY); rc = fcDiag.showOpenDialog(this); switch (rc) { case JFileChooser.APPROVE_OPTION: // User chose a file m_fname = fcDiag.getSelectedFile(); m_curDir = m_fname.getParentFile(); System.out.println("> fcDiag: '" + m_fname.getName() + "'"); System.out.println("> curDir: '" + m_curDir.getPath() + "'"); return (true); case JFileChooser.CANCEL_OPTION: case JFileChooser.ERROR_OPTION: default: // Dialog was cancelled, no file was selected System.out.println("> fcDiag: CANCEL"); return (false); } } /*************************************************************************** * Insure that the data contents of the displayed file is read into the * internal display buffer. * *

* This is the crux of the entire GUI application. It operates by * maintaining a 64 KB buffer of the contents of the data file being * displayed by the GUI. The 64 KB buffer is composed of two 32 KB * ({@link #BUF_SIZE}) buffers, which together comprise a contiguous window * into the data file at any given moment. * *

* The display window (the table pane) represents the contents of a portion * of the 64 KB file buffer. As the table pane is scrolled up or down, the * file buffer is adjusted accordingly. Smooth scrolling (one line at a * time) causes the two 32 KB halves of the buffer to be shifted and one of * them to be re-filled with data read from the data file. More coarse * scrolling causes both buffer halves to be re-filled appropriately. * * * @param addr * Starting address (offset) of the date within the file. * Sixteen (16, 0x0010) bytes or less will be read into the buffer. * * @param offsets * A two-element array whose elements are modified: the first element is set * to the offset within the returned array of the first data byte for address * addr, and the second element is set to the last (maximum) offset * contained within the returned array. * * @return * A byte array (buffer) containing the requested data contents. * The contents of offsets are also modified. * * @throws IOException * Throw if an I/O (read) error occurs while reading the contents of the data * file. * * @since 1.1, 2005-04-30 */ protected byte[] locateData(long addr, int[] offsets) throws IOException { // Check if a buffer refresh (read) is necessary if (addr < m_lowAddr) { this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); if (addr < m_lowAddr - BUF_SIZE) { // Refill both lower and upper buffers m_lowAddr = (addr/BUF_SIZE)*BUF_SIZE; // Fill the lower and (possibly) the upper buffer m_in.seek(m_lowAddr); if (m_in.read(m_data[0]) >= m_data.length) m_in.read(m_data[1]); } else { byte[] tmp; // Shift the buffers down and fill the lower buffer tmp = m_data[1]; m_data[1] = m_data[0]; m_data[0] = tmp; // Fill the lower buffer m_lowAddr -= BUF_SIZE; m_in.seek(m_lowAddr); m_in.read(m_data[0]); } this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } else if (addr >= m_lowAddr + 2*BUF_SIZE) { this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); if (addr >= m_lowAddr + 3*BUF_SIZE) { ///+Also recheck the file size if the very last row is selected // Refill both lower and upper buffers m_lowAddr = (addr/BUF_SIZE - 1)*BUF_SIZE; // Fill the lower and (possibly) the upper buffer m_in.seek(m_lowAddr); if (m_in.read(m_data[0]) >= m_data.length) m_in.read(m_data[1]); } else { byte[] tmp; long fsize; ///+Also recheck the file size if the very last row is selected // Shift the buffers up and fill the upper buffer tmp = m_data[0]; m_data[0] = m_data[1]; m_data[1] = tmp; // Check the data file length again, in case it has changed fsize = m_fname.length(); if (fsize != m_fileSize) { System.out.println("$ locateData: file size changed to: " + fsize); ///+++INCOMPLETE // The data file size has changed, // so the display must be redrawn ///... m_fileSize = fsize; } // Fill the upper buffer m_lowAddr += BUF_SIZE; m_in.seek(m_lowAddr + BUF_SIZE); m_in.read(m_data[1]); } this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } // Locate the address within the current buffer addr -= m_lowAddr; offsets[0] = (int) addr; offsets[1] = BUF_SIZE; ///+TEMPORARY, FOR TESTING ONLY ///if (addr == 0x00002000) ///throw new IOException("Invalid TEST offset: " + Long.toHexString(addr)); if (addr < BUF_SIZE) { // Use the lower buffer if (m_fileSize < m_lowAddr + BUF_SIZE) offsets[1] = (int) (m_fileSize - m_lowAddr); return (m_data[0]); } else { // Use the upper buffer offsets[0] -= BUF_SIZE; if (m_fileSize < m_lowAddr + 2*BUF_SIZE) offsets[1] = (int) (m_fileSize - (m_lowAddr+BUF_SIZE)); return (m_data[1]); } } /*************************************************************************** * Display a warning dialog for an I/O error that has occurred. * * @param ex * The I/O exception that occurred. * * @param addr * The file address/offset where the exception occurred, in the form * "XX XXXX XXXX". * * @since 1.7, 2005-04-30 */ protected void showIOException(IOException ex, long addr) { String[] msg; System.out.println("% showIOException: " + ex.getMessage()); // Create a warning dialog panel msg = new String[5]; msg[0] = "I/O Error"; msg[1] = "\n"; msg[2] = "File: " + m_fname.getName(); msg[3] = "Address: " + addressAsHex(addr) + 'H'; msg[4] = ex.getMessage(); JOptionPane.showMessageDialog(this, msg, PROG, JOptionPane.ERROR_MESSAGE); ///+INCOMPLETE ///...reset display window offsets, etc., ///...to prevent an infinite Warning loop } /*************************************************************************** * Convert a file address/offset into its hexadecimal form. * * @param addr * A file address/offset. * * @return * A string of the form "HHHH HHHH". * * @since 1.8, 2005-06-01 */ protected String addressAsHex(long addr) { int i, j; // Convert an address/offset into its hex form "HH HHHH HHHH" for (i = 8, j = 8; i > 0; i--) { int d; if (i == 4) m_charBuf[j--] = ' '; d = (int) (addr & 0x0F); addr >>>= 4; d = (d < 0xA ? d+'0' : d-0xA+'A'); m_charBuf[j--] = (char) d; } return (new String(m_charBuf, 0, 8+1)); } /*************************************************************************** * Convert a file address/offset into its decimal form. * * @param addr * A file address/offset. * * @param fill * If true, right-justify the returned string by padding it with leading * spaces, otherwise return the string unpadded. * * @return * A string of the form "D,DDD,DDD,DDD" (leading zeros are * replaced with spaces). * * @since 1.10, 2005-06-07 */ protected String addressAsDec(long addr, boolean fill) { int i, j; // Convert an address/offset into its decimal form "DDD,DDD,DDD,DDD" for (i = 10, j = 10+2; i > 0; i--) { int d; if (i == 1 || i == 4 || i == 7) m_charBuf[j--] = ','; d = (int) (addr % 10); addr /= 10; m_charBuf[j--] = (char) (d + '0'); if (addr == 0) break; } if (fill) { while (j >= 0) m_charBuf[j--] = ' '; } return (new String(m_charBuf, j+1, 10+2-j)); } /*************************************************************************** * Convert a file address/offset into its octal form. * * @param addr * A file address/offset. * * @return * A string of the form "QQQ QQQQ QQQQ". * * @since 1.10, 2005-06-07 */ protected String addressAsOct(long addr) { int i, j; // Convert an address/offset into its octal form "QQQ QQQQ QQQQ" for (i = 11, j = 11+1; i > 0; i--) { int d; if (i == 3 || i == 7) m_charBuf[j--] = ' '; d = (int) (addr & 0x07); addr >>>= 3; m_charBuf[j--] = (char) (d + '0'); } return (new String(m_charBuf, 0, 11+2)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Inner classes /*************************************************************************** * Hexadecimal file displayer helper class. * Displays the contents of a file in hexadecimal form in a GUI window. * * * @version $Revision: 1.11 $ $Date: 2005/06/27 03:42:14 $ * @since {@link HexDumpGui} 1.1, 2005-04-15 * @author David R. Tribble * (david@tribble.com). *
* * Copyright ©2005 by David R. Tribble, all rights reserved. *
* Permission is granted to freely use and distribute this source * code provided that the original copyright and authorship notices * remain intact. */ static class MyTableModel extends javax.swing.table.AbstractTableModel { // Identification /** Revision information. */ static final String REV = "@(#)tribble/gui/HexDumpGui.java $Revision: 1.11 $ $Date: 2005/06/27 03:42:14 $\n"; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private constants // Special display characters private static final char CHAR_BUL = (char) 0x2022; private static final char CHAR_DOT = (char) 0x2022; private static final char CHAR_BOX = (char) 0x2423; // or 2022,20DE,2610,2423,25AF // Address radix types private static final short ADDR_RADIX_HEX = HexDumpGui.ADDR_RADIX_HEX; private static final short ADDR_RADIX_DEC = HexDumpGui.ADDR_RADIX_DEC; private static final short ADDR_RADIX_OCT = HexDumpGui.ADDR_RADIX_OCT; // Data radix types private static final short DATA_RADIX_HEX = 0; private static final short DATA_RADIX_DEC = 1; private static final short DATA_RADIX_OCT = 2; // ASCII/EBCDIC character conversion table // Based on ISO 8859-1 (Latin-1, ASCII) and IBM CP 1047 private static final String EBCDIC_CP_01047s = /* 0x00 */ "\u0100\u0101\u0102\u0103\u0080\u0109\u0081\u007F" /* 0x08 */ + "\u0082\u0083\u0084\u010B\u010C\u010D\u010E\u010F" /* 0x10 */ + "\u0110\u0111\u0112\u0113\u0085\u010A\u0108\u0086" /* 0x18 */ + "\u0118\u0119\u0087\u0088\u011C\u011D\u011E\u011F" /* 0x20 */ + "\u0089\u008A\u008B\u008C\u008D\u008E\u0117\u011B" /* 0x28 */ + "\u008F\u0090\u0091\u0092\u0093\u0105\u0106\u0107" /* 0x30 */ + "\u0094\u0095\u0116\u0096\u0097\u0098\u0099\u0104" /* 0x38 */ + "\u009A\u009B\u009C\u009D\u0114\u0115\u009E\u011A" /* 0x40 */ + "\u0020\u00A0\u00E2\u00E4\u00E0\u00E1\u00E3\u00E5" /* 0x48 */ + "\u00E7\u00F1\u00A2\u002E\u003C\u0028\u002B\u007C" /* 0x50 */ + "\u0026\u00E9\u00EA\u00EB\u00E8\u00ED\u00EE\u00EF" /* 0x58 */ + "\u00EC\u00DF\u0021\u0024\u002A\u0029\u003B\u005E" /* 0x60 */ + "\u002D\u002F\u00C2\u00C4\u00C0\u00C1\u00C3\u00C5" /* 0x68 */ + "\u00C7\u00D1\u00A6\u002C\u0025\u005F\u003E\u003F" /* 0x70 */ + "\u00F8\u00C9\u00CA\u00CB\u00C8\u00CD\u00CE\u00CF" /* 0x78 */ + "\u00CC\u0060\u003A\u0023\u0040\u0027\u003D\u0122" /* 0x80 */ + "\u00D8\u0061\u0062\u0063\u0064\u0065\u0066\u0067" /* 0x88 */ + "\u0068\u0069\u00AB\u00BB\u00F0\u00FD\u00FE\u00B1" /* 0x90 */ + "\u00B0\u006A\u006B\u006C\u006D\u006E\u006F\u0070" /* 0x98 */ + "\u0071\u0072\u00AA\u00BA\u00E6\u00B8\u00C6\u00A4" /* 0xA0 */ + "\u00B5\u007E\u0073\u0074\u0075\u0076\u0077\u0078" /* 0xA8 */ + "\u0079\u007A\u00A1\u00BF\u00D0\u005B\u00DE\u00AE" /* 0xB0 */ + "\u00AC\u00A3\u00A5\u00B7\u00A9\u00A7\u00B6\u00BC" /* 0xB8 */ + "\u00BD\u00BE\u00DD\u00A8\u00AF\u005D\u00B4\u00D7" /* 0xC0 */ + "\u007B\u0041\u0042\u0043\u0044\u0045\u0046\u0047" /* 0xC8 */ + "\u0048\u0049\u00AD\u00F4\u00F6\u00F2\u00F3\u00F5" /* 0xD0 */ + "\u007D\u004A\u004B\u004C\u004D\u004E\u004F\u0050" /* 0xD8 */ + "\u0051\u0052\u00B9\u00FB\u00FC\u00F9\u00FA\u00FF" /* 0xE0 */ + "\u015C\u00F7\u0053\u0054\u0055\u0056\u0057\u0058" /* 0xE8 */ + "\u0059\u005A\u00B2\u00D4\u00D6\u00D2\u00D3\u00D5" /* 0xF0 */ + "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037" /* 0xF8 */ + "\u0038\u0039\u00B3\u00DB\u00DC\u00D9\u00DA\u009F"; private static final byte[] EBCDIC_CP = new byte[0x100]; { for (int i = 0x00; i < 0x100; i++) EBCDIC_CP[i] = (byte) EBCDIC_CP_01047s.charAt(i); } private static final byte[] ASCII_CP = new byte[0x100]; { for (int i = 0x00; i < 0x100; i++) ASCII_CP[i] = (byte) i; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private variables /** Parent hexadecimal file displayer object. */ private HexDumpGui m_mom; /** Hex character buffer. */ private char[] m_charBuf = new char[80]; /** Data buffer offsets. */ private int[] m_offs = new int[2]; /** Current address display radix setting. */ private short m_addrRadix = ADDR_RADIX_HEX; /** Current data display radix setting. */ private short m_dataRadix = DATA_RADIX_HEX; /** Current character display type setting. */ private byte[] m_charTable = ASCII_CP; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors /*********************************************************************** * Constructor. * * @param parent * Parent hexadecimal file displayer object for this table model. * * @since 1.1, 2005-04-15 */ MyTableModel(HexDumpGui parent) { // Initialize m_mom = parent; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public methods /**********************************************************************/ public int getColumnCount() //overrides javax.swing.table.AbstractTableModel { System.out.println("> getColumnCount: " + m_mom.COL__MAX); return (m_mom.COL__MAX); } /**********************************************************************/ int rowCount_; ///+TEMPORARY public int getRowCount() //overrides javax.swing.table.AbstractTableModel { long m; int n; ///+FIX THIS to limit the return value to Integer.MAX // Determine the total number of rows in the hex display table m = (m_mom.m_fileSize+0x10-1) / 0x10; if (m > 0x7FFFFFFFL) m = 0x7FFFFFFFL; n = (int) m; if (n != rowCount_) System.out.println("> getRowCount: " + n); rowCount_ = n; return (n); } /**********************************************************************/ public String getColumnName(int col) //overrides javax.swing.table.AbstractTableModel { System.out.println("> getColumnName(" + col + ")"); return (""); } /**********************************************************************/ public Object getValueAt(int row, int col) //overrides javax.swing.table.AbstractTableModel { long addr; // Determine the file offset for the displayed data block addr = row*0x10L + m_mom.m_startAddr; if (addr >= m_mom.m_fileSize) return ("?"); // Build the displayable text for the column try { int off; int max; byte[] data; String s; int pb; int i, j; // Build the displayable text for the column switch (col) { case COL_ADDRESS: ///+Optimize by prebuilding a string "0000.0000|0000.0010|..." for the current ///+data buffer, then just doling out substrings of it // Location/address column switch (m_addrRadix) { case ADDR_RADIX_HEX: default: return (m_mom.addressAsHex(addr)); case ADDR_RADIX_DEC: return (m_mom.addressAsDec(addr, true)); case ADDR_RADIX_OCT: return (m_mom.addressAsOct(addr)); } //-------------------------------- case COL_DATA: // Hexadecimal byte data column data = m_mom.locateData(addr, m_offs); off = m_offs[0]; max = m_offs[1]; pb = -1; // hex "HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH" // word "HHHH HHHH HHHH HHHH HHHH HHHH HHHH HHHH" // dec "DDD DDD DDD DDD DDD DDD DDD DDD DDD DDD DDD DDD DDD DDD DDD DDD" // oct "QQQ QQQ QQQ QQQ QQQ QQQ QQQ QQQ QQQ QQQ QQQ QQQ QQQ QQQ QQQ QQQ" for (i = 0, j = 0; i < 0x10 && off+i < max; i++) { int b; int b2; int d; // Format the next data byte b = data[off+i] & 0xFF; b2 = m_charTable[data[off+i] & 0xFF]; //if (i > 0) // m_charBuf[j++] = ' '; if (i > 0) { if ((b2 < 0x0A || b2 > 0x0D) && (pb >= 0x0A && pb <= 0x0D)) m_charBuf[j++] = CHAR_BUL; else m_charBuf[j++] = ' '; } pb = b2; if (i%4 == 0) m_charBuf[j++] = ' '; d = b >>> 4; d = (d < 0xA ? d+'0' : d-0xA+'A'); m_charBuf[j++] = (char) d; d = b & 0x0F; d = (d < 0xA ? d+'0' : d-0xA+'A'); m_charBuf[j++] = (char) d; if (i == 0x10-1 || off+i == max-1) { if (b2 >= 0x0A && b2 <= 0x0D) m_charBuf[j++] = CHAR_BUL; else m_charBuf[j++] = ' '; } } while (j < 0x10*3 + 4) m_charBuf[j++] = ' '; return (new String(m_charBuf, 0, j)); //-------------------------------- case COL_CHARACTERS: // Character byte data column data = m_mom.locateData(addr, m_offs); off = m_offs[0]; max = m_offs[1]; for (i = 0; i < 0x10 && off+i < max; i++) { int b; b = m_charTable[data[off+i] & 0xFF]; if (b >= 0x0A && b <= 0x0D) b = CHAR_DOT; else if (b < 0x20) b = CHAR_BOX; else if (b >= 0x7F && b < 0xA0) b = CHAR_BOX; m_charBuf[i] = (char) b; } return (new String(m_charBuf, 0, i)); //-------------------------------- default: throw new RuntimeException(); } } catch (IOException ex) { TableModelEvent evt; System.out.println("$ IOException (" + Long.toHexString(addr) + "): " + ex.getMessage()); // Display a pop-up warning m_mom.showIOException(ex, addr); ///+++INCOMPLETE // I/O error occurred, // remove the offending row from the displayed table row = (row > 0 ? row-1 : 0); m_mom.m_fileSize = (row*0x10L + m_mom.m_startAddr); // Trigger a table change event hasChanged(row); return ("????????"); } } /*********************************************************************** * Determines the default renderer/editor for each table column. * * @since 1.1, 2005-04-15 */ public Class getColumnClass(int col) //overrides javax.swing.table.AbstractTableModel { return (String.class); } /*+++NOT NEEDED /*********************************************************************** * Don't need to implement this method unless the table is editable. * * @since 1.1, 2005-04-15 *\/ public boolean isCellEditable(int row, int col) //overrides javax.swing.table.AbstractTableModel { // Note that the data/cell address is constant, // no matter where the cell appears onscreen return (false); } +++*/ /*+++NOT NEEDED /*********************************************************************** * Don't need to implement this method unless the table's data can * change. * * @since 1.1, 2005-04-15 *\/ public void setValueAt(Object value, int row, int col) //overrides javax.swing.table.AbstractTableModel { // Do nothing ///data[row][col] = value; ///fireTableCellUpdated(row, col); } +++*/ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Package private methods /*********************************************************************** * Rotate the setting type of a table column. * * @since 1.10, 2005-06-07 */ void bump(int col) { ///System.out.println("% model.bump(" + col + ")"); // Rotate the current setting for a column switch (col) { case COL_ADDRESS: m_addrRadix++; if (m_addrRadix > ADDR_RADIX_OCT) m_addrRadix = ADDR_RADIX_HEX; m_mom.m_addrRadix = m_addrRadix; break; case COL_DATA: m_dataRadix++; if (m_dataRadix > DATA_RADIX_OCT) m_dataRadix = DATA_RADIX_HEX; break; case COL_CHARACTERS: m_charTable = (m_charTable == ASCII_CP ? EBCDIC_CP : ASCII_CP); break; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private methods /*********************************************************************** * Fire a table change event. * * @param row * Last table row index known to be valid (displayable). * * @since 1.7, 2005-05-10 */ private void hasChanged(int row) { TableModelEvent evt; System.out.println("$ hasChanged"); // Trigger a table change event evt = new TableModelEvent(this, row, row, TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE); fireTableChanged(evt); } } /*************************************************************************** * Hexadecimal file displayer helper class. * Renders the column headings of the displayable table window of the GUI. * * * @version $Revision: 1.11 $ $Date: 2005/06/27 03:42:14 $ * @since {@link HexDumpGui} 1.10, 2005-06-07 * @author David R. Tribble * (david@tribble.com). *
* * Copyright ©2005 by David R. Tribble, all rights reserved. *
* Permission is granted to freely use and distribute this source * code provided that the original copyright and authorship notices * remain intact. */ static class MyHeaderRenderer extends javax.swing.table.DefaultTableCellRenderer implements javax.swing.table.TableCellRenderer, ///+++NEW, NECESSARY? java.awt.event.ActionListener { // Identification /** Revision information. */ static final String REV = "@(#)tribble/gui/HexDumpGui.java $Revision: 1.11 $ $Date: 2005/06/27 03:42:14 $\n"; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private constants // "Address" column header labels private static final String COL_ADDR_HEX = "Address"; private static final String COL_ADDR_DEC = "Address"; private static final String COL_ADDR_OCT = "Address"; // "Data" column header labels private static final String COL_DATA_HEX = "Data - Hexadecimal"; private static final String COL_DATA_DEC = "Data - Decimal"; private static final String COL_DATA_OCT = "Data - Octal"; // "Character" column header labels private static final String COL_CHAR_ASC = "Character"; private static final String COL_CHAR_EBC = "EBCDIC"; // Table column header button accelerator mnemonics private static final char COL_ACC_ADDR = 'A'; private static final char COL_ACC_DATA = 'D'; private static final char COL_ACC_CHAR = 'C'; // Table column accelerator mnemonics private static final char[] MNEMONICS = { COL_ACC_ADDR, COL_ACC_DATA, COL_ACC_CHAR, }; // Table column header labels private static final String[][] HEADINGS = { ///+TEMPORARY, REMOVE WHEN IMPLEMENTED { COL_ADDR_HEX, COL_ADDR_DEC, COL_ADDR_OCT }, { COL_DATA_HEX }, { COL_CHAR_ASC, COL_CHAR_EBC }, ///+RESTORE WHEN IMPLEMENTED { COL_ADDR_HEX, COL_ADDR_DEC, COL_ADDR_OCT }, { COL_DATA_HEX, COL_DATA_DEC, COL_DATA_OCT }, { COL_CHAR_ASC, COL_CHAR_EBC }, }; // Table column action commands private static final String[] COMMANDS = { HexDumpGui.CMD_BUT_ADDR, HexDumpGui.CMD_BUT_DATA, HexDumpGui.CMD_BUT_CHAR, }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private variables /** Parent hexadecimal file displayer object. */ private HexDumpGui m_mom; /** Column header label button. */ private JButton m_button = new JButton(); /** Column header index. */ private short m_col; /** Current column header setting. */ private short m_setting; /** Maximum column header setting. */ private short m_max; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors /*********************************************************************** * Constructor. * * @param parent * Parent hexadecimal file displayer object for this table model. * * @param col * Header column index. * * @since 1.10, 2005-06-07 */ MyHeaderRenderer(HexDumpGui parent, int col) { // Initialize m_mom = parent; m_col = (short) col; m_max = (short) HEADINGS[col].length; /*+++INCOMPLETE,EXPERIMENTAL m_button.setMnemonic(MNEMONICS[col]); m_button.setActionCommand(COMMANDS[col]); m_button.addActionListener(this); +++*/ System.out.println("% MyHeaderRenderer.ctor: col=" + m_col + " max=" + m_max); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public methods /********************************************************************** * Handles table column header button events for the GUI. * * @param ev * The button event, which will contain a command string equal to one of * the CMD_XXX_YYY constants. * * @since 1.11, 2005-06-12 */ ///+++NEW, NECESSARY? public void actionPerformed(ActionEvent ev) //implements java.awt.event.ActionListener { String cmd; String val; // Retrieve the action's command cmd = ev.getActionCommand(); System.out.println("% actionPerformed: '" + cmd + "'"); /*+++INCOMPLETE if (cmd == CMD_FILE_EXIT) { ...; } ...; ///System.out.println("&& mouse.click (" + loc.x + "," + loc.y ///+ ") col:" + col); // Rotate the column setting m_rends[col].bump(); m_model.bump(col); +++*/ } /*********************************************************************** * Renders a column heading of the hexadecimal file dump table. * * @param tbl * The table containing the cell to be rendered. * * @param val * The cell value to render. * * @param isSelected * True if the cell (row) is currently selected. * * @param hasFocus * True if the cell (row) has focus, i.e., was the last one selected. * * @param row * Cell's row index (Y coordinate). * * @param col * Cell's column index (X coordinate). * Note: this is the actual index for the column as it is * currently displayed, which might not be the same as the logical * column index (i.e., one of the * {@link #HexDumpGui.COL_ADDRESS HexDumpGui.COL_XXX}) constants. * * @since 1.10, 2005-06-07 */ public Component getTableCellRendererComponent(JTable tbl, Object val, boolean isSelected, boolean hasFocus, int row, int col) //implements javax.swing.table.TableCellRenderer { ///System.out.println("&& hdr.render(" + row + "," + col + ":" + m_col ///+ (col == m_col ? "" : "!") + "): " + m_setting + " " ///+ (isSelected ? "S" : " ") + (hasFocus ? "F" : "")); // Set the column header text // Note: Ignore 'col' and use 'm_col' instead m_button.setText(HEADINGS[m_col][m_setting]); return (m_button); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Package private methods /*********************************************************************** * Retrieve the button for this table column header. * * @since 1.11, 2005-06-11 */ ///+++NEW, NECESSARY? JButton getButton() { // Retrieve the button for the column header return (m_button); } /*********************************************************************** * Rotate the setting of the table column header. * * @since 1.10, 2005-06-07 */ void bump() { // Rotate the current setting for the column header m_setting++; if (m_setting >= m_max) m_setting = 0; ///System.out.println("% rend.bump(" + m_col + "): " + m_setting); } } /*************************************************************************** * Hexadecimal file displayer helper class. * Renders the displayable contents of a file in hexadecimal form in a GUI * table window. * * * @version $Revision: 1.11 $ $Date: 2005/06/27 03:42:14 $ * @since {@link HexDumpGui} 1.9, 2005-06-01 * @author David R. Tribble * (david@tribble.com). *
* * Copyright ©2005 by David R. Tribble, all rights reserved. *
* Permission is granted to freely use and distribute this source * code provided that the original copyright and authorship notices * remain intact. */ static class MyCellRenderer extends javax.swing.table.DefaultTableCellRenderer implements javax.swing.table.TableCellRenderer { // Identification /** Revision information. */ static final String REV = "@(#)tribble/gui/HexDumpGui.java $Revision: 1.11 $ $Date: 2005/06/27 03:42:14 $\n"; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private variables /** Parent hexadecimal file displayer object. */ private HexDumpGui m_mom; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors /*********************************************************************** * Constructor. * * @param parent * Parent hexadecimal file displayer object for this table model. * * @since 1.9, 2005-06-01 */ MyCellRenderer(HexDumpGui parent) { // Initialize m_mom = parent; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public methods /*********************************************************************** * Renders a cell within the hexadecimal file dump table. * * @param tbl * The table containing the cell to be rendered. * * @param val * The cell value to render. * * @param isSelected * True if the cell (row) is currently selected. * * @param hasFocus * True if the cell (row) has focus, i.e., was the last one selected. * * @param row * Cell's row number (Y coordinate). * * @param * Cell's column number (X coordinate). * * @since 1.9, 2005-06-01 */ public Component getTableCellRendererComponent(JTable tbl, Object val, boolean isSelected, boolean hasFocus, int row, int col) //implements javax.swing.table.TableCellRenderer { JLabel comp; long addr; ///System.out.println("&& render[" + col + "," + row + "] " ///+ (isSelected ? "S" : " ") + (hasFocus ? "F" : "")); // Format and retrieve the component text if (hasFocus) isSelected = true; comp = (JLabel) super.getTableCellRendererComponent(tbl, val, isSelected, false, row, col); comp.setHorizontalAlignment((col == COL_ADDRESS) ? SwingConstants.CENTER : SwingConstants.LEFT); // Reset the current address/offset display field if (isSelected && col == COL_ADDRESS) { // Determine the file offset for the displayed data block addr = row*0x10L + m_mom.m_startAddr; m_mom.setFileAddress(addr); } return (comp); } } /*************************************************************************** * Hexadecimal file displayer helper class. * Handles mouse events for the column headings of the displayable table * window of the GUI. * * * @version $Revision: 1.11 $ $Date: 2005/06/27 03:42:14 $ * @since {@link HexDumpGui} 1.10, 2005-06-07 * @author David R. Tribble * (david@tribble.com). *
* * Copyright ©2005 by David R. Tribble, all rights reserved. *
* Permission is granted to freely use and distribute this source * code provided that the original copyright and authorship notices * remain intact. */ static class MyMouseListener extends java.awt.event.MouseAdapter implements java.awt.event.MouseListener { // Identification /** Revision information. */ static final String REV = "@(#)tribble/gui/HexDumpGui.java $Revision: 1.11 $ $Date: 2005/06/27 03:42:14 $\n"; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private variables /** Parent hexadecimal file displayer object. */ private HexDumpGui m_mom; /** Table column header renderers. */ private MyHeaderRenderer[] m_rends; /** Table display model. */ private MyTableModel m_model; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors /*********************************************************************** * Constructor. * * @param parent * Parent hexadecimal file displayer object for this table model. * * @param rends * Renderers for the column headings. * * @param model * Table display model. * * @since 1.10, 2005-06-07 */ MyMouseListener(HexDumpGui parent, MyHeaderRenderer[] rends, MyTableModel model) { // Initialize m_mom = parent; m_rends = rends; m_model = model; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public methods /*********************************************************************** * Handles a mouse click event for a column heading of the hexadecimal * file dump table. * * @param ev * A mouse click event. * * @since 1.10, 2005-06-07 */ public void mouseClicked(MouseEvent ev) //implements java.awt.MouseListener { Point loc; int col; // Determine the table column heading that was clicked loc = ev.getPoint(); col = m_mom.m_table.columnAtPoint(loc); col = m_mom.m_table.convertColumnIndexToModel(col); ///System.out.println("&& mouse.click (" + loc.x + "," + loc.y ///+ ") col:" + col); // Rotate the column setting m_rends[col].bump(); m_model.bump(col); } } } // End HexDumpGui.java