マイペースなプログラミング日記

DTMやプログラミングにお熱なd-kamiがマイペースに書くブログ

バイナリエディタを目指して その4

今回は前回のやつをちょっと整理する。キャレットのところを除けばほぼパッケージをわけたぐらい。それで、キャレットに関する変更を説明する。まず、描画専用のHighlighterというクラスを作った。実際、これはinterfaceで、これを実装したクラスが描画を行う。決まりきった処理を行うメソッドはAbstractHighlighterで実装して、デフォルトのHighlighterであるDefaultHighlighterを作り、特に設定がなければこのクラスで描画を行う。次に変更したのは色の指定の仕方。プロパティファイルを用意して、そこから読み込まれるように変更。とはいっても、これを行ってるクラスはテスト用クラスなので、内容は適当だ。それらの変更を加えたクラスのソースを載せておく。

まずはCaretクラス。キャレットの位置とHighlighterを持つクラス。drwはHighlighterのdrawをそのまま呼んでるだけ

package hexedit.caret;

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Color;

public class Caret{
    private int row;
    private int column;
    private int width;
    private boolean isHigh;
    private Highlighter highlighter;

    public Caret(int width){
        row = 0;
        column = 0;
        this.width = width;
        isHigh = true;

        highlighter = new DefaultHighlighter();
        highlighter.setBackgroundColor(new Color(0x60ffCC80, true));
        highlighter.setRoundColor(new Color(0x60ffCC80, true));
    }

    public void nextRow(){
        row++;
    }

    public void beforeRow(){
        if(row > 0)
            row--;
    }

    public void nextColumn(){
        column++;
        if(column >= width){
            column = 0;
            nextRow();
        }
    }

    public void beforeColumn(){
        column--;
        if(column < 0){
            if(row == 0)
                column = 0;
            else
                column = width - 1;

            beforeRow();
        }
    }

    public int getRow(){
        return row;
    }

    public void setRow(int row){
        this.row = row;
    }

    public int getColumn(){
        return column;
    }

    public void setColumn(int column){
        this.column = column;
        isHigh = true;
    }

    public boolean isHigh(){
        return isHigh;
    }

    public void setHigh(boolean isHigh){
        isHigh = isHigh;
    }

    public void setBackgroundColor(Color color){
        highlighter.setBackgroundColor(color);
    }

    public void setRoundColor(Color color){
        highlighter.setRoundColor(color);
    }

    public void draw(Graphics g, Rectangle rect){
        highlighter.draw(g, rect);
    }
}

次にHighlighter。これは、描画メソッドと色指定メソッドを定義している。

package hexedit.caret;

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Color;

public interface Highlighter{
    void draw(Graphics g, Rectangle rect);
    void setBackgroundColor(Color color);
    void setRoundColor(Color color);
}

次にAbstractHighlighter。色を表す変数を持たせ、色指定メソッドだけを実装している。

package hexedit.caret;

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Color;

public abstract class AbstractHighlighter implements Highlighter{
    protected Color background;
    protected Color roundColor;

    public void setBackgroundColor(Color background){
        this.background = background;
    }

    public void setRoundColor(Color roundColor){
        this.roundColor = roundColor;
    }
}

次にDefaultHighlighter。Graphics#setColorして、Graphics#fillRectしてるだけ。描画後に元の色に戻しておいたほうがいいのかもしれない

package hexedit.caret;

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Color;

public class DefaultHighlighter extends AbstractHighlighter{
    public void draw(Graphics g, Rectangle rect){
        g.setColor(background);
        g.fillRect(rect.x, rect.y, rect.width, rect.height);
    }
}

次にHexEditor。paintComponentの内容が変わり、キャレットの描画はCaretを通してHighlighterに任せている。またキャレットの色を変更するメソッドを持たせた。

package hexedit.editor;

import javax.swing.JComponent;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

import java.awt.Point;
import java.awt.Dimension;
import java.awt.Rectangle;

import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;

import hexedit.document.BinaryDocument;
import hexedit.caret.Caret;

public class HexEditor extends JComponent implements KeyListener{
    private BinaryDocument document;
    private Caret caret;

    private static final String[] DATA_TABLE = new String[]{
        "0", "1", "2", "3", "4", "5", "6", "7",
        "8", "9", "A", "B", "C", "D", "E", "F"
    };

    public HexEditor(){
        this(new BinaryDocument());
    }

    public HexEditor(BinaryDocument document){
        this.document = document;
        caret = new Caret(16);

        int requireColumn = (document.getSize() / 16) + 10;
        setPreferredSize(new Dimension(16 * 26 + 8, requireColumn * 20));

        setFocusable(true);
        addKeyListener(this);
    }

    public void paintComponent(Graphics g){
        g.setFont(new Font("Monospaced", Font.PLAIN, 16));
        g.setColor(Color.BLACK);

        int row = 128;
        int column = 16;

        for(int y = 0; y < row; y++){
            for(int x = 0; x < column; x++){
                int index = (y * column) + x;
                byte data = document.get(index);

                byte high = (byte)((data >> 4) & 0x0F);
                byte low = (byte)(data & 0x0F);

                g.drawString(DATA_TABLE[high], x * 26 + 8, (y + 1) * 20);
                g.drawString(DATA_TABLE[low], (x * 26) + 8 + 8, (y + 1) * 20);
            }
        }

        Rectangle caretRectangle = new Rectangle(caret.getColumn() * 26 + 6, caret.getRow() * 20 + 4, 22, 20);
        caret.draw(g, caretRectangle);
    }

    public void setCaretBackground(Color color){
        caret.setBackgroundColor(color);
    }

    public void setCaretRoundColor(Color color){
        caret.setRoundColor(color);
    }

    public void keyPressed(KeyEvent e){
        switch(e.getKeyCode()){
        case KeyEvent.VK_UP:
            caret.beforeRow();
            break;

        case KeyEvent.VK_DOWN:
            caret.nextRow();
            break;

        case KeyEvent.VK_LEFT:
            caret.beforeColumn();
            break;

        case KeyEvent.VK_RIGHT:
            caret.nextColumn();
            break;

        default:
            break;
        }

        repaint();
    }

    public void keyTyped(KeyEvent e){}
    public void keyReleased(KeyEvent e){}
}

次にBinaryDocument。ただ、パッケージ名が変わっただけ

package hexedit.document;

public class BinaryDocument{
    private int size;
    private int capacity;
    private byte[] contents;

    public BinaryDocument(){
        this(0);
    }

    public BinaryDocument(int size){
        this.size = size;
        this.capacity = (size * 13 / 10) + 10;
        this.contents = new byte[capacity];
    }

    public void set(byte data, int index){
        if(index >= capacity)
            reallocate(index);

        if(index > size)
            size = index;

        contents[index] = data;
    }

    public byte get(int index){
        if(index >= size)
            return (byte)0;

        return contents[index];
    }

    public byte[] gets(int index, int length){
         byte[] data = new byte[length];

         for(int i = 0; i < length; i++){
              data[i] = get(index + i);
         }

         return data;
    }

    private void reallocate(int newSize){
         capacity = (newSize * 13 / 10) + 10;
         byte[] newContents = new byte[capacity];

         for(int i = 0; i < size; i++){
             newContents[i] = contents[i];
         }

         contents = newContents;
         size = newSize;
    }

    public int getSize(){
        return size;
    }
}

次にMain。XML形式のプロパティファイルを読み込んで、Caretの色を指定するようにした。

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import java.awt.Color;

import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;

import java.util.Properties;

import hexedit.editor.HexEditor;
import hexedit.document.BinaryDocument;
import hexedit.caret.Caret;

public class Main{
    public static void main(String[] args) throws IOException{
        BinaryDocument document = new BinaryDocument(10);

        int index = 0;
        int n;
        byte[] data = new byte[1024];
        BufferedInputStream in = new BufferedInputStream(new FileInputStream("./Main.java"));

        while((n = in.read(data, 0, 1024)) > 0){
            for(int i = 0; i < n; i++){
                document.set(data[i], index++);
            }
        }

        Properties properties = new Properties();
        properties.loadFromXML(new FileInputStream("config.properties"));

        HexEditor editor = new HexEditor(document);

        Color color = new Color(Integer.parseInt(properties.getProperty("HexEdit.Caret.BackgroundColor"), 16), true);
        editor.setCaretBackground(color);

        JFrame frame = new JFrame();
        frame.add(new JScrollPane(editor));
        frame.setSize(600, 500);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

最後にconfig.properties。XML形式のプロパティファイル。キャレットの色を16進数のARGB形式で指定する

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <comment>HexEdit Properties</comment>
    <entry key="HexEdit.Caret.BackgroundColor">6066CCff</entry>
    <entry key="HexEdit.Caret.RoundColor">6066CCff</entry>
</properties>