Revision 2937

View differences:

org.gvsig.tools/library/trunk/org.gvsig.tools/org.gvsig.tools.swing/org.gvsig.tools.swing.impl/src/main/java/org/gvsig/tools/swing/impl/hexeditor/ByteBuffer.java
1
/*
2
 * Copyright (c) 2008 Robert Futrell
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *     * Redistributions of source code must retain the above copyright
8
 *       notice, this list of conditions and the following disclaimer.
9
 *     * Redistributions in binary form must reproduce the above copyright
10
 *       notice, this list of conditions and the following disclaimer in the
11
 *       documentation and/or other materials provided with the distribution.
12
 *     * Neither the name "HexEditor" nor the names of its contributors may
13
 *       be used to endorse or promote products derived from this software
14
 *       without specific prior written permission.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR IMPLIED
17
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
 * EVENT SHALL THE CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT,
20
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 */
27
package org.gvsig.tools.swing.impl.hexeditor;
28

  
29
import java.io.BufferedInputStream;
30
import java.io.ByteArrayOutputStream;
31
import java.io.File;
32
import java.io.FileInputStream;
33
import java.io.IOException;
34
import java.io.InputStream;
35

  
36
public class ByteBuffer {
37

  
38
	/**
39
	 * The byte buffer that contains the document content.
40
	 */
41
	private byte[] buffer;
42

  
43

  
44
	public ByteBuffer(int size) {
45
		buffer = new byte[size];
46
	}
47

  
48

  
49
	public ByteBuffer(String file) throws IOException {
50
		this(new File(file));
51
	}
52

  
53

  
54
	public ByteBuffer(File file) throws IOException {
55

  
56
		int size = (int)file.length();
57
		if (size<0) { // Probably never happens.
58
			throw new IOException("Negative file length: " + size);
59
		}
60
		buffer = new byte[size];
61

  
62
		if (size>0) {
63
			BufferedInputStream in = new BufferedInputStream(
64
										new FileInputStream(file));
65
			int pos = 0;
66
			int count = 0;
67
			try {
68
				while (pos<buffer.length &&
69
						(count=in.read(buffer, pos, buffer.length-pos))>-1) {
70
					pos += count;
71
				}
72
			} finally {
73
				in.close();
74
			}
75
		}
76

  
77
	}
78

  
79

  
80
	/**
81
	 * Creates a buffer representing the contents read from an input stream.
82
	 *
83
	 * @param in The input stream to read from.
84
	 * @throws IOException If an IO error occurs.
85
	 */
86
	public ByteBuffer(InputStream in) throws IOException {
87
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
88
		buffer = new byte[4096]; // Use as a temporary buffer.
89
		int count = 0;
90
		while ((count=in.read(buffer, 0,buffer.length))>-1) {
91
			baos.write(buffer, 0,count);
92
		}
93
		buffer = baos.toByteArray();
94
	}
95

  
96

  
97
	public byte getByte(int offset) {
98
		return buffer[offset];
99
	}
100

  
101
        public byte[] getBytes() {
102
            return this.buffer;
103
        }
104

  
105
	public int getSize() {
106
		return buffer.length;
107
	}
108

  
109

  
110
	public void insertByte(int offset, byte b) {
111
		byte[] buf2 = new byte[buffer.length+1];
112
		System.arraycopy(buffer,0, buf2,0, offset);
113
		buf2[offset] = b;
114
		System.arraycopy(buffer,offset, buf2,offset+1, buffer.length-offset);
115
		buffer = buf2;
116
	}
117

  
118

  
119
	public void insertBytes(int offs, byte[] b) {
120

  
121
		if (b==null || b.length==0) {
122
			return;
123
		}
124

  
125
		byte[] buf2 = new byte[buffer.length+b.length];
126
		System.arraycopy(buffer,0,    buf2,0,             offs);
127
		System.arraycopy(b,0,         buf2,offs,          b.length);
128
		System.arraycopy(buffer,offs, buf2,offs+b.length, buffer.length-offs);
129
		buffer = buf2;
130

  
131
	}
132

  
133

  
134
	public int read(int offset, byte[] buf) {
135
		if (buf==null) {
136
			return -1;
137
		}
138
		int count = Math.min(buf.length, getSize()-offset);
139
		System.arraycopy(buffer,offset, buf,0, count);
140
		return count;
141
	}
142

  
143

  
144
	public void remove(int offset, int len) {
145
		remove(offset, len, null);
146
	}
147

  
148

  
149
	public void remove(int offset, int len, byte[] removed) {
150
		if (removed!=null) {
151
			System.arraycopy(buffer,offset, removed,0, len);
152
		}
153
		byte[] buf = new byte[buffer.length-len];
154
		System.arraycopy(buffer,0, buf,0, offset);
155
		System.arraycopy(buffer,offset+len, buf,offset, buf.length-offset);
156
		buffer = buf;
157
	}
158

  
159

  
160
	public void setByte(int offset, byte b) {
161
		buffer[offset] = b;
162
	}
163

  
164

  
165
}
org.gvsig.tools/library/trunk/org.gvsig.tools/org.gvsig.tools.swing/org.gvsig.tools.swing.impl/src/main/java/org/gvsig/tools/swing/impl/hexeditor/event/HexEditorListener.java
1
package org.gvsig.tools.swing.impl.hexeditor.event;
2

  
3
import java.util.EventListener;
4
import org.gvsig.tools.swing.impl.hexeditor.swing.HexEditor;
5

  
6

  
7

  
8
/**
9
 * An object listening for events from a {@link HexEditor}.
10
 *
11
 * @author Robert Futrell
12
 * @version 1.0
13
 */
14
public interface HexEditorListener extends EventListener {
15

  
16

  
17
	/**
18
	 * Called when bytes in a hex editor are added, removed, or modified.
19
	 *
20
	 * @param e The event object.
21
	 */
22
	public void hexBytesChanged(HexEditorEvent e);
23

  
24

  
25
}
org.gvsig.tools/library/trunk/org.gvsig.tools/org.gvsig.tools.swing/org.gvsig.tools.swing.impl/src/main/java/org/gvsig/tools/swing/impl/hexeditor/event/SelectionChangedEvent.java
1
/*
2
 * Copyright (c) 2011 Robert Futrell
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *     * Redistributions of source code must retain the above copyright
8
 *       notice, this list of conditions and the following disclaimer.
9
 *     * Redistributions in binary form must reproduce the above copyright
10
 *       notice, this list of conditions and the following disclaimer in the
11
 *       documentation and/or other materials provided with the distribution.
12
 *     * Neither the name "HexEditor" nor the names of its contributors may
13
 *       be used to endorse or promote products derived from this software
14
 *       without specific prior written permission.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR IMPLIED
17
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
 * EVENT SHALL THE CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT,
20
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 */
27
package org.gvsig.tools.swing.impl.hexeditor.event;
28

  
29
import java.util.EventObject;
30

  
31

  
32
/**
33
 * Occurs when the cell selection within the hex editor becomes changed.
34
 * 
35
 * @author PAX
36
 * @version 1.0
37
 */
38
public class SelectionChangedEvent extends EventObject {
39

  
40
	private static final long serialVersionUID = 1L;
41

  
42
	/**
43
	 * The previous selection start index.
44
	 */
45
	private int previousSelecStart;
46

  
47
	/**
48
	 * The new selection start index.
49
	 */
50
	private int newSelecStart;
51

  
52
	/**
53
	 * The previous selection end index.
54
	 */
55
	private int previousSelecEnd;
56

  
57
	/**
58
	 * The new selection end index.
59
	 */
60
	private int newSelecEnd;
61

  
62

  
63
	/**
64
	 * Constructor.
65
	 * 
66
	 * @param source The instance which creates this event.
67
	 * @param previousSelecStart The previous selection start index.
68
	 * @param previousSelecEnd The previous selection end index.
69
	 * @param newSelecStart The new selection start index.
70
	 * @param newSelecEnd The new selection end index.
71
	 */
72
	public SelectionChangedEvent(Object source, int previousSelecStart,
73
			int previousSelecEnd, int newSelecStart, int newSelecEnd) {
74
		super(source);
75
		this.previousSelecStart = previousSelecStart;
76
		this.previousSelecEnd = previousSelecEnd;
77
		this.newSelecStart = newSelecStart;
78
		this.newSelecEnd = newSelecEnd;
79
	}
80

  
81

  
82
	/**
83
	 * @return The new selection end index.
84
	 */
85
	public int getNewSelecEnd() {
86
		return this.newSelecEnd;
87
	}
88

  
89

  
90
	/**
91
	 * @return The new selection start index.
92
	 */
93
	public int getNewSelecStart() {
94
		return this.newSelecStart;
95
	}
96

  
97

  
98
	/**
99
	 * @return The previous selection end index.
100
	 */
101
	public int getPreviousSelecEnd() {
102
		return this.previousSelecEnd;
103
	}
104

  
105

  
106
	/**
107
	 * @return The previous selection start index.
108
	 */
109
	public int getPreviousSelecStart() {
110
		return this.previousSelecStart;
111
	}
112

  
113

  
114
	public String toString() {
115
		StringBuffer result = new StringBuffer("Old selection: [");
116
		result.append(getPreviousSelecStart()).append(", ");
117
		result.append(getPreviousSelecEnd()).append("]; New selection: [");
118
		result.append(getNewSelecStart()).append(", ");
119
		result.append(getNewSelecEnd()).append(']');
120
		return result.toString();
121
	}
122

  
123

  
124
}
org.gvsig.tools/library/trunk/org.gvsig.tools/org.gvsig.tools.swing/org.gvsig.tools.swing.impl/src/main/java/org/gvsig/tools/swing/impl/hexeditor/event/SelectionChangedListener.java
1
/*
2
 * Copyright (c) 2011 Robert Futrell
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *     * Redistributions of source code must retain the above copyright
8
 *       notice, this list of conditions and the following disclaimer.
9
 *     * Redistributions in binary form must reproduce the above copyright
10
 *       notice, this list of conditions and the following disclaimer in the
11
 *       documentation and/or other materials provided with the distribution.
12
 *     * Neither the name "HexEditor" nor the names of its contributors may
13
 *       be used to endorse or promote products derived from this software
14
 *       without specific prior written permission.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR IMPLIED
17
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
 * EVENT SHALL THE CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT,
20
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 */
27
package org.gvsig.tools.swing.impl.hexeditor.event;
28

  
29
import java.util.EventListener;
30

  
31

  
32
/**
33
 * Somebody who is interested whether the cell selection within the hex editor
34
 * becomes changed.
35
 * 
36
 * @author PAX
37
 * @version 1.0
38
 */
39
public interface SelectionChangedListener extends EventListener {
40

  
41

  
42
	/**
43
	 * Becomes invoked as soon as the selection of the hex editor's content has
44
	 * changed.
45
	 * 
46
	 * @param event Contains specific information about the previous selection
47
	 *        state and the new one.
48
	 */
49
	public void selectionChanged(SelectionChangedEvent event);
50

  
51

  
52
}
org.gvsig.tools/library/trunk/org.gvsig.tools/org.gvsig.tools.swing/org.gvsig.tools.swing.impl/src/main/java/org/gvsig/tools/swing/impl/hexeditor/event/HexEditorEvent.java
1
package org.gvsig.tools.swing.impl.hexeditor.event;
2

  
3
import java.util.EventObject;
4
import org.gvsig.tools.swing.impl.hexeditor.swing.HexEditor;
5

  
6

  
7

  
8
/**
9
 * An event that is fired when certain events occur in a hex editor.
10
 *
11
 * @author Robert Futrell
12
 * @version 1.0
13
 */
14
public class HexEditorEvent extends EventObject {
15

  
16
	private static final long serialVersionUID = 1L;
17

  
18
	/**
19
	 * The offset of the change.
20
	 */
21
	private int offset;
22

  
23
	/**
24
	 * The number of bytes added.
25
	 */
26
	private int added;
27

  
28
	/**
29
	 * The number of bytes removed.
30
	 */
31
	private int removed;
32

  
33

  
34
	/**
35
	 * Creates a new event object.
36
	 *
37
	 * @param editor The source of this event.
38
	 * @param offs The offset at which bytes were added or removed.
39
	 * @param added The number of bytes added, or <code>0</code> for none.
40
	 * @param removed The number of bytes removed, or <code>0</code> for none.
41
	 */
42
	public HexEditorEvent(HexEditor editor, int offs, int added, int removed) {
43
		super(editor);
44
		this.offset = offs;
45
		this.added = added;
46
		this.removed = removed;
47
	}
48

  
49

  
50
	/**
51
	 * Returns the number of bytes added.  If this value equals the number
52
	 * of bytes removed, the bytes were actually modified.
53
	 *
54
	 * @return The number of bytes added.
55
	 * @see #getRemovedCount()
56
	 */
57
	public int getAddedCount() {
58
		return added;
59
	}
60

  
61

  
62
	/**
63
	 * Returns the hex editor that fired this event.
64
	 *
65
	 * @return The hex editor.
66
	 */
67
	public HexEditor getHexEditor() {
68
		return (HexEditor)getSource();
69
	}
70

  
71

  
72
	/**
73
	 * Returns the offset of the change.
74
	 *
75
	 * @return The offset of the change.
76
	 */
77
	public int getOffset() {
78
		return offset;
79
	}
80

  
81

  
82
	/**
83
	 * Returns the number of bytes removed.  If this value equals the number
84
	 * of bytes added, then the bytes were actually modified.
85
	 *
86
	 * @return The number of bytes removed.
87
	 * @see #getAddedCount()
88
	 */
89
	public int getRemovedCount() {
90
		return removed;
91
	}
92

  
93

  
94
	/**
95
	 * Returns whether this was a "modification" of bytes; that is, no bytes
96
	 * were added or removed, bytes were only modified.  This is equivalent
97
	 * to <code>getAddedCount() == getRemovedCount()</code>.
98
	 *
99
	 * @return Whether this is just a "modification" of bytes.
100
	 */
101
	public boolean isModification() {
102
		return getAddedCount()==getRemovedCount();
103
	}
104

  
105

  
106
}
org.gvsig.tools/library/trunk/org.gvsig.tools/org.gvsig.tools.swing/org.gvsig.tools.swing.impl/src/main/java/org/gvsig/tools/swing/impl/hexeditor/swing/ByteArrayTransferable.java
1
package org.gvsig.tools.swing.impl.hexeditor.swing;
2

  
3
import java.awt.datatransfer.DataFlavor;
4
import java.awt.datatransfer.Transferable;
5
import java.awt.datatransfer.UnsupportedFlavorException;
6
import java.io.IOException;
7
import java.io.StringReader;
8

  
9

  
10
/**
11
 * A <code>Transferable</code> that transfers an array of bytes.
12
 *
13
 * @author Robert Futrell
14
 * @version 1.0
15
 */
16
class ByteArrayTransferable implements Transferable {
17

  
18
	private int offset;
19
	private byte[] bytes;
20

  
21
	private static final DataFlavor[] FLAVORS = {
22
		DataFlavor.stringFlavor,
23
		DataFlavor.plainTextFlavor,
24
	};
25

  
26

  
27
	/**
28
	 * Creates a transferable object.
29
	 *
30
	 * @param bytes The bytes to transfer.
31
	 */
32
	public ByteArrayTransferable(int offset, byte[] bytes) {
33
		this.offset = offset;
34
		if (bytes!=null) {
35
			this.bytes = (byte[])bytes.clone();
36
		}
37
		else {
38
			this.bytes = new byte[0];
39
		}
40
	}
41

  
42

  
43
	/**
44
	 * Returns the number of bytes being transferred.
45
	 *
46
	 * @return The number of bytes being transferred.
47
	 * @see #getOffset()
48
	 */
49
	public int getLength() {
50
		return bytes.length;
51
	}
52

  
53

  
54
	/**
55
	 * Returns the offset of the first byte being transferred.
56
	 *
57
	 * @return The offset of the first byte.
58
	 * @see #getLength()
59
	 */
60
	public int getOffset() {
61
		return offset;
62
	}
63

  
64

  
65
	/**
66
	 * Returns the data being transferred in a format specified by the
67
	 * <code>DataFlavor</code>.
68
	 *
69
	 * @param flavor Dictates in what format the data should be returned.
70
	 * @throws UnsupportedFlavorException If the specified flavor is not
71
	 *         supported.
72
	 * @throws IOException If an IO error occurs.
73
	 * @see DataFlavor#getRepresentationClass()
74
	 */
75
	public Object getTransferData(DataFlavor flavor)
76
			throws UnsupportedFlavorException, IOException {
77
		if (flavor.equals(FLAVORS[0])) {
78
			return new String(bytes); // Use platform default charset.
79
		}
80
		else if (flavor.equals(FLAVORS[1])) {
81
			return new StringReader(new String(bytes));
82
		}
83
	    throw new UnsupportedFlavorException(flavor);
84
	}
85

  
86

  
87
	/**
88
	 * Returns an array of DataFlavor objects indicating the flavors the data 
89
	 * can be provided in.  The array is ordered according to preference for 
90
	 * providing the data (from most richly descriptive to least descriptive).
91
	 *
92
	 * @return An array of data flavors in which this data can be transferred.
93
	 */
94
	public DataFlavor[] getTransferDataFlavors() {
95
		return (DataFlavor[])FLAVORS.clone();
96
	}
97

  
98

  
99
	/**
100
	 * Returns whether a data flavor is supported.
101
	 *
102
	 * @param flavor The flavor to check.
103
	 * @return Whether the specified flavor is supported.
104
	 */
105
	public boolean isDataFlavorSupported(DataFlavor flavor) {
106
		for (int i=0; i<FLAVORS.length; i++) {
107
			if (flavor.equals(FLAVORS[i])) {
108
				return true;
109
			}
110
		}
111
		return false;
112
	}
113

  
114

  
115
}
org.gvsig.tools/library/trunk/org.gvsig.tools/org.gvsig.tools.swing/org.gvsig.tools.swing.impl/src/main/java/org/gvsig/tools/swing/impl/hexeditor/swing/HexEditor.java
1
/*
2
 * Copyright (c) 2008 Robert Futrell
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *     * Redistributions of source code must retain the above copyright
8
 *       notice, this list of conditions and the following disclaimer.
9
 *     * Redistributions in binary form must reproduce the above copyright
10
 *       notice, this list of conditions and the following disclaimer in the
11
 *       documentation and/or other materials provided with the distribution.
12
 *     * Neither the name "HexEditor" nor the names of its contributors may
13
 *       be used to endorse or promote products derived from this software
14
 *       without specific prior written permission.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR IMPLIED
17
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
 * EVENT SHALL THE CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT,
20
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 */
27
package org.gvsig.tools.swing.impl.hexeditor.swing;
28

  
29
import java.awt.Color;
30
import java.awt.EventQueue;
31
import java.awt.Point;
32
import java.awt.event.ActionEvent;
33
import java.io.IOException;
34
import java.io.InputStream;
35
import javax.swing.Action;
36
import javax.swing.JScrollPane;
37
import javax.swing.TransferHandler;
38
import javax.swing.UIManager;
39

  
40
import org.gvsig.tools.swing.impl.hexeditor.event.HexEditorEvent;
41
import org.gvsig.tools.swing.impl.hexeditor.event.HexEditorListener;
42
import org.gvsig.tools.swing.impl.hexeditor.event.SelectionChangedListener;
43

  
44

  
45

  
46
/**
47
 * A Swing hex editor component.<p>
48
 *
49
 * The hex editor's functionality includes:
50
 * <ul>
51
 *    <li>Cut, copy, paste, delete of 1 or more bytes
52
 *    <li>Undo/redo
53
 *    <li>Selecting a contiguous block of bytes
54
 * </ul>
55
 *
56
 * @author Robert Futrell
57
 * @version 1.0
58
 */
59
public class HexEditor extends JScrollPane {
60

  
61
	private static final long serialVersionUID = 1L;
62

  
63
	/**
64
	 * Property fired when the alternating of column background colors is
65
	 * toggled.
66
	 */
67
	public static final String PROPERTY_ALTERNATE_COLUMN_BG = "alternateColBG";
68

  
69
	/**
70
	 * Property fired when the alternating of row background colors is
71
	 * toggled.
72
	 */
73
	public static final String PROPERTY_ALTERNATE_ROW_BG = "alternateRowBG";
74

  
75
	/**
76
	 * Property fired when the highlight color of the ascii dump column
77
	 * is changed.
78
	 */
79
	public static final String PROPERTY_ASCII_DUMP_HIGHLIGHT_COLOR =
80
												"asciiDumpHighlightColor";
81

  
82
	/**
83
	 * Property fired when the visibility of the highlight in the "ascii
84
	 * dump" column is toggled.
85
	 */
86
	public static final String PROPERTY_HIGHLIGHT_ASCII_DUMP =
87
												"highlightAsciiDump";
88

  
89
	/**
90
	 * Property fired when the padding of low bytes (<code>0 - 15</code>)
91
	 * is toggled.
92
	 */
93
	public static final String PROPERTY_PAD_LOW_BYTES = "padLowBytes";
94

  
95
	/**
96
	 * Property fired when the visibility of the grid is toggled.
97
	 */
98
	public static final String PROPERTY_SHOW_GRID = "showGrid";
99

  
100
	private HexTable table;
101
	private boolean alternateRowBG;
102
	private boolean alternateColumnBG;
103
	private boolean highlightSelectionInAsciiDump;
104
	private Color highlightSelectionInAsciiDumpColor;
105
	private boolean padLowBytes;
106

  
107
	private static final TransferHandler DEFAULT_TRANSFER_HANDLER =
108
							new HexEditorTransferHandler();
109

  
110
	static final int DUMP_COLUMN_WIDTH		= 140;
111
//	private static final String MSG = "org.fife.ui.hex.HexEditor";
112

  
113

  
114
	/**
115
	 * Creates a new <code>HexEditor</code> component.
116
	 */
117
	public HexEditor() {
118

  
119
//		ResourceBundle msg = ResourceBundle.getBundle(MSG);
120

  
121
		HexTableModel model = new HexTableModel(this); //, msg);
122
		table = new HexTable(this, model);
123
		setViewportView(table);
124
		setShowRowHeader(true);
125

  
126
		setAlternateRowBG(false);
127
		setAlternateColumnBG(false);
128
		setHighlightSelectionInAsciiDump(true);
129
		setHighlightSelectionInAsciiDumpColor(new Color(255,255,192));
130
		setPadLowBytes(true);
131
		setCellEditable(true);
132

  
133
		setTransferHandler(DEFAULT_TRANSFER_HANDLER);
134

  
135
	}
136

  
137

  
138
	/**
139
	 * Adds a hex editor listener to this editor.
140
	 *
141
	 * @param l The listener to add.
142
	 * @see #removeHexEditorListener(HexEditorListener)
143
	 */
144
	public void addHexEditorListener(HexEditorListener l) {
145
		listenerList.add(HexEditorListener.class, l);
146
	}
147

  
148

  
149
	/**
150
	 * Registers a prospect who is interested when the text selection from the
151
	 * hex editor becomes changed.
152
	 * 
153
	 * @param l The concerning listener.
154
	 * @see #removeSelectionChangedListener(SelectionChangedListener)
155
	 */
156
	public void addSelectionChangedListener(SelectionChangedListener l) {
157
		table.addSelectionChangedListener(l);
158
	}
159

  
160

  
161
	/**
162
	 * Returns the offset into the bytes being edited represented at the
163
	 * specified cell in the table, if any.
164
	 *
165
	 * @param row The row in the table.
166
	 * @param col The column in the table.
167
	 * @return The offset into the byte array, or <code>-1</code> if the
168
	 *         cell does not represent part of the byte array (such as the
169
	 *         tailing "ascii dump" column's cells).
170
	 * @see #offsetToCell(int)
171
	 */
172
	public int cellToOffset(int row, int col) {
173
		return table.cellToOffset(row, col);
174
	}
175

  
176

  
177
	/**
178
	 * Copies the currently selected bytes to the clipboard.
179
	 *
180
	 * @see #cut()
181
	 * @see #paste()
182
	 * @see #delete()
183
	 */
184
	public void copy() {
185
		invokeAction(TransferHandler.getCopyAction());
186
	}
187

  
188

  
189
	/**
190
	 * Removes the currently selected bytes and moves them to the clipboard.
191
	 *
192
	 * @see #copy()
193
	 * @see #paste()
194
	 * @see #delete()
195
	 */
196
	public void cut() {
197
		invokeAction(TransferHandler.getCutAction());
198
	}
199

  
200

  
201
	/**
202
	 * Removes the currently selected bytes.
203
	 *
204
	 * @see #cut()
205
	 * @see #copy()
206
	 * @see #paste()
207
	 */
208
	public void delete() {
209

  
210
		// Sanity check (should never happen)
211
		if (table.leadSelectionIndex==-1 || table.anchorSelectionIndex==-1) {
212
			UIManager.getLookAndFeel().provideErrorFeedback(table);
213
			return;
214
		}
215

  
216
		int start = table.getSmallestSelectionIndex();
217
		int end = table.getLargestSelectionIndex();
218
		int len = end-start+1;
219
		removeBytes(start, len);
220

  
221
	}
222

  
223

  
224
	/**
225
	 * Notifies all interested listeners of an event in this hex editor.
226
	 *
227
	 * @param offset The offset of the change.
228
	 * @param added The number of bytes added.
229
	 * @param removed The number of bytes removed.
230
	 */
231
	protected void fireHexEditorEvent(int offset, int added, int removed) {
232
		HexEditorEvent e = null;
233
		// Guaranteed to return a non-null array
234
		Object[] listeners = listenerList.getListenerList();
235
		// Process the listeners last to first, notifying
236
		// those that are interested in this event
237
		for (int i=listeners.length-2; i>=0; i-=2) {
238
			if (listeners[i]==HexEditorListener.class) {
239
				// Lazily create the event in case there are no listeners.
240
				if (e==null) {
241
					e = new HexEditorEvent(this, offset, added, removed);
242
				}
243
				((HexEditorListener)listeners[i+1]).hexBytesChanged(e);
244
			}
245
		}
246
	}
247

  
248

  
249
	/**
250
	 * Returns whether the color of columns in the hex editor should alternate.
251
	 *
252
	 * @return Whether the column color alternates.
253
	 * @see #setAlternateColumnBG(boolean)
254
	 * @see #getAlternateRowBG()
255
	 */
256
	public boolean getAlternateColumnBG() {
257
		return alternateColumnBG;
258
	}
259

  
260

  
261
	/**
262
	 * Returns whether the color of rows in the hex editor should alternate.
263
	 *
264
	 * @return Whether the row color alternates.
265
	 * @see #setAlternateRowBG(boolean)
266
	 * @see #getAlternateColumnBG()
267
	 */
268
	public boolean getAlternateRowBG() {
269
		return alternateRowBG;
270
	}
271

  
272

  
273
	/**
274
	 * Returns the byte at the specified offset.
275
	 *
276
	 * @param offset The offset.
277
	 * @return The byte.
278
	 */
279
	public byte getByte(int offset) {
280
		return table.getByte(offset);
281
	}
282

  
283

  
284
	/**
285
	 * Returns the number of bytes being edited.
286
	 *
287
	 * @return The number of bytes.
288
	 */
289
	public int getByteCount() {
290
		return table.getByteCount();
291
	}
292

  
293
        public byte[] getBytes() {
294
            return this.table.getBytes();
295
        }
296

  
297
	/**
298
	 * Returns whether the selected bytes should also appear selected in
299
	 * the "ascii dump" column.
300
	 *
301
	 * @return Whether the selected bytes should also be selected in the
302
	 *         "ascii dump" column.
303
	 * @see #setHighlightSelectionInAsciiDump(boolean)
304
	 */
305
	public boolean getHighlightSelectionInAsciiDump() {
306
		return highlightSelectionInAsciiDump;
307
	}
308

  
309

  
310
	/**
311
	 * Returns the color used to highlight the selected bytes in the
312
	 * "ascii dump" column.
313
	 *
314
	 * @return The color used.
315
	 * @see #setHighlightSelectionInAsciiDumpColor(Color)
316
	 * @see #getHighlightSelectionInAsciiDump()
317
	 */
318
	public Color getHighlightSelectionInAsciiDumpColor() {
319
		return highlightSelectionInAsciiDumpColor;
320
	}
321

  
322

  
323
	/**
324
	 * Returns the largest selection index.
325
	 *
326
	 * @return The largest selection index.
327
	 * @see #getSmallestSelectionIndex()
328
	 */
329
	public int getLargestSelectionIndex() {
330
		return table.getLargestSelectionIndex();
331
	}
332

  
333

  
334
	/**
335
	 * Returns whether bytes that can be displayed as a single char
336
	 * (i.e. <code>0 - 15</code>) are prepended with a "<code>0</code>"
337
	 * char to make them two characters wide in the display.
338
	 *
339
	 * @return Whether bytes with one-character hex representations
340
	 *         will be prepended with a "<code>0</code>" in the display.
341
	 * @see #setPadLowBytes(boolean)
342
	 */
343
	public boolean getPadLowBytes() {
344
		return padLowBytes;
345
	}
346

  
347

  
348
	/**
349
	 * Returns the smallest selection index.
350
	 *
351
	 * @return The smallest selection index.
352
	 * @see #getLargestSelectionIndex()
353
	 */
354
	public int getSmallestSelectionIndex() {
355
		return table.getSmallestSelectionIndex();
356
	}
357

  
358

  
359
	/**
360
	 * Returns the table actually containing the hex data.
361
	 *
362
	 * @return The table.
363
	 */
364
	HexTable getTable() {
365
		return table;
366
	}
367

  
368

  
369
	private void invokeAction(Action a) {
370
		a.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
371
								(String)a.getValue(Action.NAME),
372
								EventQueue.getMostRecentEventTime(),
373
								0));
374
	}
375

  
376

  
377
	/**
378
	 * Returns the cell representing the specified offset into the hex
379
	 * document.
380
	 *
381
	 * @param offset The offset into the document.
382
	 * @return The cell, in the form <code>(row, col)</code>.  If the
383
	 *         specified offset is invalid, <code>(-1, -1)</code> is returned.
384
	 * @see #cellToOffset(int, int)
385
	 */
386
	public Point offsetToCell(int offset) {
387
		return table.offsetToCell(offset);
388
	}
389

  
390

  
391
	/**
392
	 * Sets the contents in the hex editor to the contents of the specified
393
	 * file.
394
	 *
395
	 * @param fileName The name of the file to open.
396
	 * @throws IOException If an IO error occurs.
397
	 */
398
	public void open(String fileName) throws IOException {
399
		table.open(fileName);
400
	}
401

  
402

  
403
	/**
404
	 * Sets the contents in the hex editor to the contents of the specified
405
	 * input stream.
406
	 *
407
	 * @param in An input stream.
408
	 * @throws IOException If an IO error occurs.
409
	 */
410
	public void open(InputStream in) throws IOException {
411
		table.open(in);
412
	}
413

  
414

  
415
	/**
416
	 * "Pastes" the bytes in the clipboard into the current selection in
417
	 * the hex editor.
418
	 *
419
	 * @see #copy()
420
	 * @see #cut()
421
	 * @see #delete()
422
	 */
423
	public void paste() {
424
		invokeAction(TransferHandler.getPasteAction());
425
	}
426

  
427

  
428
	/**
429
	 * Tries to redo the last action undone.
430
	 *
431
	 * @return Whether there is another action to redo after this one.
432
	 * @see #undo()
433
	 */
434
	public boolean redo() {
435
		return table.redo();
436
	}
437

  
438

  
439
	/**
440
	 * Removes a range of bytes.
441
	 *
442
	 * @param offs The offset of the range of bytes to remove.
443
	 * @param len The number of bytes to remove.
444
	 * @see #replaceBytes(int, int, byte[])
445
	 */
446
	public void removeBytes(int offs, int len) {
447
		table.removeBytes(offs, len);
448
		table.changeSelectionByOffset(offs, false);
449
	}
450

  
451

  
452
	/**
453
	 * Removes the specified hex editor listener from this editor.
454
	 *
455
	 * @param l The listener to remove.
456
	 * @see #addHexEditorListener(HexEditorListener)
457
	 */
458
	public void removeHexEditorListener(HexEditorListener l) {
459
		listenerList.remove(HexEditorListener.class, l);
460
	}
461

  
462

  
463
	/**
464
	 * Removes a listener who isn't any longer interested whether the text
465
	 * selection from the hex editor becomes changed.
466
	 * 
467
	 * @param l The concerning previous prospect.
468
	 * @see #addSelectionChangedListener(SelectionChangedListener)
469
	 */
470
	public void removeSelectionChangedListener(SelectionChangedListener l) {
471
		table.removeSelectionChangedListener(l);
472
	}
473

  
474

  
475
	/**
476
	 * Replaces a range of bytes.
477
	 *
478
	 * @param offset The offset of the range of bytes to replace.
479
	 * @param len The number of bytes to replace.
480
	 * @param bytes The bytes to replace the range with.
481
	 * @see #removeBytes(int, int)
482
	 * @see #replaceSelection(byte[])
483
	 */
484
	public void replaceBytes(int offset, int len, byte[] bytes) {
485
		if (len==1) { // Just insert if 1 bytes is selected.
486
			len = 0;
487
		}
488
		table.replaceBytes(offset, len, bytes);
489
		table.changeSelectionByOffset(table.anchorSelectionIndex, false);
490
		int count = bytes==null ? 0 : bytes.length;
491
		table.setSelectionByOffsets(offset, offset+count-1);
492
	}
493

  
494

  
495
	/**
496
	 * Replaces the currently selected bytes (if greater or equal to 1) with the specified
497
	 * new bytes.
498
	 *
499
	 * @param bytes The new bytes.  If this is <code>null</code> or an empty
500
	 *        array, calling this method simply removes the currently
501
	 *        selected bytes.
502
	 * @see #replaceBytes(int, int, byte[])
503
	 */
504
	public void replaceSelection(byte[] bytes) {
505
		int offset = table.getSmallestSelectionIndex();
506
		int len = table.getLargestSelectionIndex()-offset+1;
507
		replaceBytes(offset, len, bytes);
508
	}
509

  
510

  
511
	/**
512
	 * Sets whether the column color should alternate in the hex editor.
513
	 * This method fires a property change event of type
514
	 * {@link #PROPERTY_ALTERNATE_COLUMN_BG}.
515
	 *
516
	 * @param alternate Whether the column color should alternate.
517
	 * @see #getAlternateColumnBG()
518
	 * @see #setAlternateRowBG(boolean)
519
	 */
520
	public void setAlternateColumnBG(boolean alternate) {
521
		if (alternate!=alternateColumnBG) {
522
			this.alternateColumnBG = alternate;
523
			table.repaint();
524
			firePropertyChange(PROPERTY_ALTERNATE_COLUMN_BG,
525
									!alternate, alternate);
526
		}
527
	}
528

  
529

  
530
	/**
531
	 * Sets whether the row color should alternate in the hex editor.  This
532
	 * method fires a property change event of type
533
	 * {@link #PROPERTY_ALTERNATE_ROW_BG}.
534
	 *
535
	 * @param alternate Whether the row color should alternate.
536
	 * @see #getAlternateRowBG()
537
	 * @see #setAlternateColumnBG(boolean)
538
	 */
539
	public void setAlternateRowBG(boolean alternate) {
540
		if (alternate!=alternateRowBG) {
541
			this.alternateRowBG = alternate;
542
			table.repaint();
543
			firePropertyChange(PROPERTY_ALTERNATE_ROW_BG,!alternate, alternate);
544
		}
545
	}
546

  
547

  
548
	/**
549
	 * Toggles whether the cells in the hex editor are editable by clicking
550
	 * in them.
551
	 *
552
	 * @param cellEditable Whether individual editor cells should be editable.
553
	 */
554
	public void setCellEditable(boolean cellEditable) {
555
		table.setCellEditable(cellEditable);
556
	}
557

  
558

  
559
	/**
560
	 * Sets whether the selected bytes should also appear selected in the
561
	 * "ascii dump" column.  This method fires a property change event of
562
	 * type {@link #PROPERTY_HIGHLIGHT_ASCII_DUMP}.
563
	 *
564
	 * @param highlight Whether the selected bytes should also appear
565
	 *        selected in the "ascii dump" column.
566
	 * @see #getHighlightSelectionInAsciiDump()
567
	 * @see #setHighlightSelectionInAsciiDumpColor(Color)
568
	 */
569
	public void setHighlightSelectionInAsciiDump(boolean highlight) {
570
		if (highlight!=highlightSelectionInAsciiDump) {
571
			highlightSelectionInAsciiDump = highlight;
572
			table.repaint();
573
			firePropertyChange(PROPERTY_HIGHLIGHT_ASCII_DUMP,
574
								!highlight, highlight);
575
		}
576
	}
577

  
578

  
579
	/**
580
	 * Sets what color should be used for the "ascii dump" selection.
581
	 * This method fires a property change event of type
582
	 * {@link #PROPERTY_ASCII_DUMP_HIGHLIGHT_COLOR}.
583
	 * 
584
	 * @param c The color to use.
585
	 * @see #getHighlightSelectionInAsciiDumpColor()
586
	 * @see #setHighlightSelectionInAsciiDump(boolean)
587
	 */
588
	public void setHighlightSelectionInAsciiDumpColor(Color c) {
589
		if (c!=null && !c.equals(highlightSelectionInAsciiDumpColor)) {
590
			Color old = highlightSelectionInAsciiDumpColor;
591
			highlightSelectionInAsciiDumpColor = c;
592
			table.repaint();
593
			firePropertyChange(PROPERTY_ASCII_DUMP_HIGHLIGHT_COLOR, old, c);
594
		}
595
	}
596

  
597

  
598
	/**
599
	 * Sets whether bytes that can be displayed as a single char
600
	 * (i.e. <code>0 - 15</code>) are prepended with a "<code>0</code>"
601
	 * char to make them two characters wide in the display.  This method
602
	 * fires a property change event of type {@link #PROPERTY_PAD_LOW_BYTES}.
603
	 *
604
	 * @param pad Whether to pad such bytes in the display.
605
	 * @see #getPadLowBytes()
606
	 */
607
	public void setPadLowBytes(boolean pad) {
608
		if (padLowBytes!=pad) {
609
			padLowBytes = pad;
610
			table.repaint();
611
			firePropertyChange(PROPERTY_PAD_LOW_BYTES, !pad, pad);
612
		}
613
	}
614

  
615

  
616
	/**
617
	 * Sets the range of bytes to select in the hex editor.
618
	 *
619
	 * @param startOffs The first byte to select.
620
	 * @param endOffs The last byte to select.
621
	 */
622
	public void setSelectedRange(int startOffs, int endOffs) {
623
		table.setSelectionByOffsets(startOffs, endOffs);
624
	}
625

  
626

  
627
	/**
628
	 * Toggles whether table's column header is visible.
629
	 *
630
	 * @param show Whether to show the table column header.
631
	 * @see #setShowRowHeader(boolean)
632
	 */
633
	public void setShowColumnHeader(boolean show) {
634
		// Since table header is added to a parent JScrollPane as a
635
		// column header, we have to remove it from the scrollpane to
636
		// make it not visible.
637
		setColumnHeaderView(show ? table.getTableHeader() : null);
638
	}
639

  
640

  
641
	/**
642
	 * Toggles whether the table's grid lines are visible.  This method
643
	 * fires a property change event of type {@link #PROPERTY_SHOW_GRID}.
644
	 *
645
	 * @param show Whether grid lines are visible.
646
	 */
647
	public void setShowGrid(boolean show) {
648
		 // There is no "getShowGrid()" method.
649
		if (show!=table.getShowHorizontalLines()) {
650
			table.setShowGrid(show);
651
			firePropertyChange(PROPERTY_SHOW_GRID, !show, show);
652
		}
653
	}
654

  
655

  
656
	/**
657
	 * Toggles whether table's row header is visible.
658
	 *
659
	 * @param show Whether to show the table row header.
660
	 * @see #setShowColumnHeader(boolean)
661
	 */
662
	public void setShowRowHeader(boolean show) {
663
		setRowHeaderView(show ? new HexEditorRowHeader(table) : null);
664
	}
665

  
666

  
667
	/**
668
	 * Tries to undo the last action.
669
	 *
670
	 * @return Whether there is another action to undo after this one.
671
	 * @see #redo()
672
	 */
673
	public boolean undo() {
674
		return table.undo();
675
	}
676

  
677
}
org.gvsig.tools/library/trunk/org.gvsig.tools/org.gvsig.tools.swing/org.gvsig.tools.swing.impl/src/main/java/org/gvsig/tools/swing/impl/hexeditor/swing/HexEditorRowHeader.java
1
/*
2
 * Copyright (c) 2008 Robert Futrell
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *     * Redistributions of source code must retain the above copyright
8
 *       notice, this list of conditions and the following disclaimer.
9
 *     * Redistributions in binary form must reproduce the above copyright
10
 *       notice, this list of conditions and the following disclaimer in the
11
 *       documentation and/or other materials provided with the distribution.
12
 *     * Neither the name "HexEditor" nor the names of its contributors may
13
 *       be used to endorse or promote products derived from this software
14
 *       without specific prior written permission.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR IMPLIED
17
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
 * EVENT SHALL THE CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT,
20
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 */
27
package org.gvsig.tools.swing.impl.hexeditor.swing;
28

  
29
import java.awt.Component;
30
import java.awt.Graphics;
31
import javax.swing.AbstractListModel;
32
import javax.swing.BorderFactory;
33
import javax.swing.DefaultListCellRenderer;
34
import javax.swing.JLabel;
35
import javax.swing.JList;
36
import javax.swing.ListSelectionModel;
37

  
38

  
39
import javax.swing.border.EmptyBorder;
40
import javax.swing.border.Border;
41
import javax.swing.event.TableModelEvent;
42
import javax.swing.event.TableModelListener;
43

  
44

  
45
/**
46
 * Header of the hex table; displays address of the first byte on the
47
 * row.
48
 *
49
 * @author Robert Futrell
50
 * @version 1.0
51
 */
52
class HexEditorRowHeader extends JList implements TableModelListener {
53

  
54
	private static final long serialVersionUID = 1L;
55

  
56
	private HexTable table;
57
	private RowHeaderListModel model;
58

  
59
	private static final Border CELL_BORDER =
60
							BorderFactory.createEmptyBorder(0,5,0,5);
61

  
62

  
63
	/**
64
	 * Constructor.
65
	 *
66
	 * @param table The table displaying the hex content.
67
	 */
68
	public HexEditorRowHeader(HexTable table) {
69
		this.table = table;
70
		model = new RowHeaderListModel();
71
		setModel(model);
72
		setFocusable(false);
73
		setFont(table.getFont());
74
		setFixedCellHeight(table.getRowHeight());
75
		setCellRenderer(new CellRenderer());
76
		setBorder(new RowHeaderBorder());
77
		setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
78
		syncRowCount(); // Initialize to initial size of table.
79
		table.getModel().addTableModelListener(this);
80
	}
81

  
82

  
83
	public void addSelectionInterval(int anchor, int lead) {
84
		super.addSelectionInterval(anchor, lead);
85
		int min = Math.min(anchor, lead);
86
		int max = Math.max(anchor, lead);
87
		table.setSelectedRows(min, max);
88
	}
89

  
90

  
91
	public void removeSelectionInterval(int index0, int index1) {
92
		super.removeSelectionInterval(index0, index1);
93
		int anchor = getAnchorSelectionIndex();
94
		int lead = getLeadSelectionIndex();
95
		table.setSelectedRows(Math.min(anchor, lead), Math.max(anchor, lead));
96
	}
97

  
98

  
99
	public void setSelectionInterval(int anchor, int lead) {
100
		super.setSelectionInterval(anchor, lead);
101
		int min = Math.min(anchor, lead);
102
		int max = Math.max(anchor, lead);
103
		// Table may be showing 0 bytes, but we're showing 1 row header
104
		if (max<table.getRowCount()) {
105
			table.setSelectedRows(min, max);
106
		}
107
	}
108

  
109

  
110
	private void syncRowCount() {
111
		if (table.getRowCount()!=model.getSize()) {
112
			// Always keep 1 row, even if showing 0 bytes in editor
113
			model.setSize(Math.max(1, table.getRowCount()));
114
		}
115
	}
116

  
117

  
118
	public void tableChanged(TableModelEvent e) {
119
		syncRowCount();
120
	}
121

  
122

  
123
	/**
124
	 * Renders the cells of the row header.
125
	 *
126
	 * @author Robert Futrell
127
	 * @version 1.0
128
	 */
129
	private class CellRenderer extends DefaultListCellRenderer {
130

  
131
		private static final long serialVersionUID = 1L;
132

  
133
		public CellRenderer() {
134
			setHorizontalAlignment(JLabel.RIGHT);
135
		}
136

  
137
		public Component getListCellRendererComponent(JList list, Object value,
138
							int index, boolean selected, boolean hasFocus) {
139
			// Never paint cells as "selected."
140
			super.getListCellRendererComponent(list, value, index,
141
												false, hasFocus);
142
			setBorder(CELL_BORDER);
143
//			setBackground(table.getBackground());
144
			return this;
145
		}
146

  
147
	}
148

  
149

  
150
	/**
151
	 * List model used by the header for the hex table.
152
	 *
153
	 * @author Robert Futrell
154
	 * @version 1.0
155
	 */
156
	private static class RowHeaderListModel extends AbstractListModel {
157

  
158
		private static final long serialVersionUID = 1L;
159

  
160
		private int size;
161

  
162
		public Object getElementAt(int index) {
163
			return "0x" + Integer.toHexString(index*16);
164
		}
165

  
166
		public int getSize() {
167
			return size;
168
		}
169

  
170
		public void setSize(int size) {
171
			int old = this.size;
172
			this.size = size;
173
			int diff = size - old;
174
			if (diff>0) {
175
				fireIntervalAdded(this, old, size-1);
176
			}
177
			else if (diff<0) {
178
				fireIntervalRemoved(this, size+1, old-1);
179
			}
180
		}
181

  
182
	}
183

  
184

  
185
	/**
186
	 * Border for the entire row header.  This draws a line to separate the
187
	 * header from the table contents, and gives a small amount of whitespace
188
	 * to separate the two.
189
	 *
190
	 * @author Robert Futrell
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff