/* * Hex.java * * Created on May 30, 2008, 4:45 PM * * @author pquiring * */ import javaforce.*; import java.awt.*; import java.awt.event.*; import java.awt.datatransfer.*; import javax.swing.*; import java.util.*; public class Hex extends JComponent implements KeyListener { public Hex(jfhex jh) { this.jh = jh; setFocusable(true); txt = new StringBuffer(); addKeyListener(this); changeFont(Settings.fnt); setText(""); } //public data public StringBuffer txt; //private data private jfhex jh; private static int fx, fy; //font size x/y private static int baseline; private static Color foreColor = Color.BLACK, backColor = Color.WHITE; private boolean cursorShown = false; private int selectStart = -1, selectEnd = -1; private static Color cursorColor = new Color(0x7777ff); private static Color selectColor = new Color(0x77ff77); private int cx, cy; //cursor pos private boolean leftSide; //on left side (TAB switches) private boolean leftNibble; //on left Nibble (if on left side) (a nibble is 4 bits 0xa thru 0xf) private boolean insertMode = true; private final int INF = 0x7fff; public static void changeFont(Font newFont) { int metrics[] = JF.getFontMetrics(Settings.fnt); fx = metrics[0]; fy = metrics[1] + metrics[2]; baseline = metrics[2]; } public void setText(String text) { txt.setLength(0); txt.append(text); cx = cy = 0; leftSide = true; leftNibble = true; selectStart = selectEnd = -1; setSize(getPreferredSize()); invalidate(); repaint(); } public String getText() { return txt.toString(); } public void setForeColor(int newClr) { foreColor = new Color(newClr); } public void setBackColor(int newClr) { backColor = new Color(newClr); } public void setForeColor(Color newClr) { foreColor = newClr; } public void setBackColor(Color newClr) { backColor = newClr; } public Color getForeColor() { return foreColor; } public Color getBackColor() { return backColor; } public void update(boolean showCursor) { if (showCursor) scrollRectToVisible(new Rectangle(0,cy * fy,fx,fy)); repaint(); } //0 9 57 //01234567 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx ################ public void paintComponent(Graphics g) { Rectangle r = g.getClipBounds(); int starty; int endy; starty = r.y / fy; endy = (r.y + r.height) / fy + 1; g.setFont(Settings.fnt); char ch; //clear g.setColor(backColor); g.fillRect(r.x, r.y, r.width, r.height); //draw cursor g.setColor(cursorColor); if (leftSide) { if (insertMode) g.fillRect((cx * 3 + 9 + (leftNibble ? 0 : 1)) * fx,cy * fy,fx,fy); else g.drawRect((cx * 3 + 9 + (leftNibble ? 0 : 1)) * fx,cy * fy,fx,fy); } else { if (insertMode) g.fillRect((cx + 57) * fx,cy * fy,fx,fy); else g.drawRect((cx + 57) * fx,cy * fy,fx,fy); } g.setColor(foreColor); int cp = cy * 16 + cx; for(int y = starty;y < endy;y++) { g.drawString(String.format("%08x",y * 16), 0, (y+1) * fy - 1 - baseline); for(int x = 0;x < 16;x++) { int p = y * 16 + x; if (p >= txt.length()) return; if (((p >= selectStart) && (p <= selectEnd)) || ((p >= selectEnd) && (p <= selectStart) && (selectEnd >= 0))) { g.setColor(selectColor); g.fillRect((x * 3 + 9) * fx,y * fy,fx*2,fy); g.fillRect((x + 57) * fx,y * fy,fx,fy); if (p == cp) { //draw cursor (over selection) g.setColor(cursorColor); if (leftSide) { if (insertMode) g.fillRect((cx * 3 + 9 + (leftNibble ? 0 : 1)) * fx,cy * fy,fx,fy); else g.drawRect((cx * 3 + 9 + (leftNibble ? 0 : 1)) * fx,cy * fy,fx,fy); } else { if (insertMode) g.fillRect((cx + 57) * fx,cy * fy,fx,fy); else g.drawRect((cx + 57) * fx,cy * fy,fx,fy); } } g.setColor(foreColor); } ch = txt.charAt(p); g.drawString(String.format("%x", (int)((ch&0xf0) >> 4)), (x * 3 + 9) * fx + fx/4,(y+1) * fy - 1 - baseline); g.drawString(String.format("%x", (int)(ch&0x0f)), (x * 3 + 10) * fx + fx/4,(y+1) * fy - 1 - baseline); if (ch == 0) continue; if (ch > 127) ch = ASCII8.convert(ch); g.drawString("" + ch, (x + 57) * fx,(y+1) * fy - 1 - baseline); } } } public void copy() { int tmp; if ((selectStart == -1) || (selectEnd == -1)) return; //nothing to copy try { if (selectStart > selectEnd) { tmp = selectStart; selectStart = selectEnd; selectEnd = tmp; } String str = txt.substring(selectStart, selectEnd+1); StringSelection ss = new StringSelection(str); Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); if (cb == null) return; cb.setContents(ss, ss); } catch (Exception e) {} } public void paste() { try { Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); String str = (String)cb.getContents(null).getTransferData(DataFlavor.stringFlavor); if (str == null) return; boolean orgleftSide = leftSide; leftSide = false; insert(str.toCharArray()); leftSide = orgleftSide; } catch (Exception e) {} jh.changed(); repaint(); } public void delete() { if (selectStart == -1) return; copy(); txt.delete(selectStart, selectEnd+1); setCaretPosition(selectStart); selectStart = selectEnd = -1; jh.changed(); repaint(); } public void cut() { if (selectStart == -1) return; int tmp; if (selectStart > selectEnd) { tmp = selectStart; selectStart = selectEnd; selectEnd = tmp; } txt.delete(selectStart, selectEnd+1); setCaretPosition(selectStart); selectStart = selectEnd = -1; jh.changed(); repaint(); } public void paste(char str[]) { try { boolean orgleftSide = leftSide; leftSide = false; insert(str); leftSide = orgleftSide; } catch (Exception e) {} jh.changed(); repaint(); } public boolean eof() { return (cy * 16 + cx == txt.length()); } public void insert(char ch) { if (ch == 9) return; if (leftSide) { //can only type 0-9,a-f ch = Character.toLowerCase(ch); int value = 0; if ((ch >= '0') && (ch <= '9')) { value = ch - '0'; } else if ((ch >= 'a') && (ch <= 'f')) { value = ch - ('a' - 10); } else return; if (leftNibble) { ch = (char)(value << 4); txt.insert(cy * 16 + cx, ch); } else { if (!eof()) value += txt.charAt(cy * 16 + cx) & 0xf0; ch = (char)value; if (!eof()) txt.setCharAt(cy * 16 + cx, ch); else txt.insert(cy * 16 + cx, ch); } } else { txt.insert(cy * 16 + cx, ch); } move(1, 0, false); jh.changed(); } public void insert(char chs[]) { for(int a=0;a<chs.length;a++) insert(chs[a]); } public void overwrite(char ch) { if (ch == 9) return; if (cy * 16 + cx >= txt.length()) txt.append((char)0); if (leftSide) { //can only type 0-9,a-f ch = Character.toLowerCase(ch); int value = 0; if ((ch >= '0') && (ch <= '9')) { value = ch - '0'; } else if ((ch >= 'a') && (ch <= 'f')) { value = ch - ('a' - 10); } else return; if (leftNibble) { value <<= 4; if (!eof()) value += txt.charAt(cy * 16 + cx) & 0x0f; ch = (char)(value); if (!eof()) txt.setCharAt(cy * 16 + cx, ch); else txt.insert(cy * 16 + cx, ch); } else { value += txt.charAt(cy * 16 + cx) & 0xf0; ch = (char)value; if (!eof()) txt.setCharAt(cy * 16 + cx, ch); else txt.insert(cy * 16 + cx, ch); } } else { txt.setCharAt(cy * 16 + cx, ch); } move(1, 0, false); jh.changed(); } public void overwrite(char chs[]) { for(int a=0;a<chs.length;a++) overwrite(chs[a]); } public void switchSide() { if (leftSide) leftSide = false; else leftSide = true; leftNibble = true; update(true); } public void switchInsertMode() { if (insertMode) insertMode = false; else insertMode = true; update(true); } public Dimension getPreferredSize() { int x = (57 + 16) * fx; int y = ((txt.length() / 16) + 1) * fy; return new Dimension(x,y); } public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } //Note : abs(x) can not be > 1 public void move(int x, int y, boolean selecting) { if (selecting) { if (selectStart == -1) selectStart = cy * 16 + cx; } else { selectStart = selectEnd = -1; } if (x != 0) { if (leftSide) { if (leftNibble) { leftNibble = false; if (x == 1) x = 0; } else { leftNibble = true; if (x == -1) x = 0; } } cx += x; if (cx == -1) {cx = 15; cy--;} if (cx == 16) {cx = 0; cy++;} } cy += y; if ((cy < 0) || (y == -INF)) {cx = 0; cy = 0; leftNibble = true;} int len = txt.length(); if ((cy * 16 + cx > len) || (y == INF)) { cy = len / 16; cx = len - (cy * 16); } if (selecting) selectEnd = cy * 16 + cx; update(true); } public void setCaretPosition(int pos) { if ((pos < 0) || (pos > txt.length())) { int len = txt.length(); cy = len / 16; cx = len - (cy * 16); } else { cy = pos / 16; cx = pos - (cy * 16); } update(true); } public int getCaretPosition() { return cy * 16 + cx; } public void select(int start, int end) { selectStart = start; selectEnd = end; setCaretPosition(start); } public boolean isSelect() { return selectStart != -1; } public long getOffset() { return cy * 16 + cx; } //interface KeyListener public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); int keyMods = e.getModifiers(); if (keyMods == KeyEvent.CTRL_MASK) { switch (keyCode) { case KeyEvent.VK_A: selectStart = 0; selectEnd = txt.length(); break; case KeyEvent.VK_C: //no break case KeyEvent.VK_INSERT: copy(); break; case KeyEvent.VK_V: paste(); break; case KeyEvent.VK_HOME: move(0, -INF, false); e.consume(); break; case KeyEvent.VK_X: delete(); break; case KeyEvent.VK_END: move(0, INF, false); e.consume(); break; } } if (keyMods == (KeyEvent.SHIFT_MASK | KeyEvent.CTRL_MASK)) { switch (keyCode) { case KeyEvent.VK_HOME: move(0, -INF, true); e.consume(); break; case KeyEvent.VK_END: move(0, INF, true); e.consume(); break; } } if (keyMods == KeyEvent.SHIFT_MASK) { switch (keyCode) { case KeyEvent.VK_UP: move(0, -1, true); e.consume(); break; case KeyEvent.VK_DOWN: move(0, 1, true); e.consume(); break; case KeyEvent.VK_LEFT: move(-1, 0, true); e.consume(); break; case KeyEvent.VK_RIGHT: move(1, 0, true); e.consume(); break; case KeyEvent.VK_PAGE_UP: move(0, -16, true); e.consume(); break; case KeyEvent.VK_PAGE_DOWN: move(0, 16, true); e.consume(); break; case KeyEvent.VK_INSERT: paste(); break; case KeyEvent.VK_DELETE: delete(); break; } } if (keyMods == 0) { switch (keyCode) { case KeyEvent.VK_UP: move(0, -1, false); e.consume(); break; case KeyEvent.VK_DOWN: move(0, 1, false); e.consume(); break; case KeyEvent.VK_LEFT: move(-1, 0, false); e.consume(); break; case KeyEvent.VK_RIGHT: move(1, 0, false); e.consume(); break; case KeyEvent.VK_PAGE_UP: move(0, -16, false); e.consume(); break; case KeyEvent.VK_PAGE_DOWN: move(0, 16, false); e.consume(); break; case KeyEvent.VK_TAB: switchSide(); e.consume(); break; case KeyEvent.VK_INSERT: switchInsertMode(); break; case KeyEvent.VK_DELETE: { if (selectStart != -1) { cut(); } else { if ((txt.length() > 1) && (!eof())) { txt.delete(getCaretPosition(), getCaretPosition()+1); jh.changed(); repaint(); } } } } } } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { if (e.getModifiers() == KeyEvent.CTRL_MASK) return; if (e.getModifiers() == KeyEvent.ALT_MASK) return; if (e.getModifiers() == (KeyEvent.CTRL_MASK | KeyEvent.ALT_MASK)) return; char key = e.getKeyChar(); switch (key) { case 8: //backspace case 0x7f: //delete return; } if (selectStart != -1) delete(); if (insertMode) insert(key); else overwrite(key); } //override isManagingFocus - this allows TAB to be processed (only when there is one tab) //WARNING : this member is deprecated - need to find a better way public boolean isManagingFocus() {return true;} }