001: package org.gui4j.core.swing;
002:
003: /**
004: * MultiLineTable.java
005: *
006: * Created: Tue May 18 13:15:59 1999
007: *
008: * @author Thomas Wernitz, Da Vinci Communications Ltd <thomas_wernitz@clear.net.nz>
009: *
010: * credit to Zafir Anjum for JTableEx and thanks to SUN for their source code ;)
011: */
012:
013: import java.awt.Dimension;
014: import java.awt.FontMetrics;
015: import java.awt.Point;
016: import java.awt.Rectangle;
017: import java.awt.event.ComponentAdapter;
018: import java.awt.event.ComponentEvent;
019: import java.util.Enumeration;
020: import java.util.Vector;
021:
022: import javax.swing.JTable;
023: import javax.swing.ListSelectionModel;
024: import javax.swing.event.ListSelectionEvent;
025: import javax.swing.table.DefaultTableModel;
026: import javax.swing.table.TableCellRenderer;
027: import javax.swing.table.TableColumn;
028: import javax.swing.table.TableColumnModel;
029: import javax.swing.table.TableModel;
030: import javax.swing.text.Segment;
031: import javax.swing.text.TabExpander;
032:
033: public class MultiLineTable extends JTable {
034:
035: public MultiLineTable() {
036: this (null, null, null);
037: }
038:
039: public MultiLineTable(TableModel dm) {
040: this (dm, null, null);
041: }
042:
043: public MultiLineTable(TableModel dm, TableColumnModel cm) {
044: this (dm, cm, null);
045: }
046:
047: public MultiLineTable(TableModel dm, TableColumnModel cm,
048: ListSelectionModel sm) {
049: super (dm, cm, sm);
050: setUI(new MultiLineBasicTableUI());
051: // I know this sucks tremendously, but I was too lazy to find a proper solution. :(
052: // The problem is, that without this hack, a resize that changes the number of lines in
053: // the TextArea does not result in the proper resizing of the table, because the
054: // new width of the column is not available through getWidth until resize is complete.
055: // Does this make sense? 8-/ Have a look into getRowHeight(int)!
056: addComponentListener(new ComponentAdapter() {
057: public void componentResized(ComponentEvent e) {
058: revalidate();
059: }
060: });
061: }
062:
063: public MultiLineTable(int numRows, int numColumns) {
064: this (new DefaultTableModel(numRows, numColumns));
065: }
066:
067: public MultiLineTable(final Vector rowData, final Vector columnNames) {
068: super (rowData, columnNames);
069: setUI(new MultiLineBasicTableUI());
070: addComponentListener(new ComponentAdapter() {
071: public void componentResized(ComponentEvent e) {
072: revalidate();
073: }
074: });
075: }
076:
077: public MultiLineTable(final Object[][] rowData,
078: final Object[] columnNames) {
079: super (rowData, columnNames);
080: setUI(new MultiLineBasicTableUI());
081: addComponentListener(new ComponentAdapter() {
082: public void componentResized(ComponentEvent e) {
083: revalidate();
084: }
085: });
086: }
087:
088: public int rowAtPoint(Point point) {
089: int y = point.y;
090: int rowSpacing = getIntercellSpacing().height;
091: int rowCount = getRowCount();
092: int lRowHeight = 0;
093: for (int row = 0; row < rowCount; row++) {
094: lRowHeight += getRowHeight(row) + rowSpacing;
095: if (y < lRowHeight) {
096: return row;
097: }
098: }
099: return -1;
100: }
101:
102: public int getHeight(String text, int width) {
103: FontMetrics fm = getFontMetrics(getFont());
104: int numLines = 1;
105: Segment s = new Segment(text.toCharArray(), 0, 0);
106: s.count = s.array.length;
107: TabExpander te = new MyTabExpander(fm);
108: int breaks = getBreakLocation(s, fm, 0, width, te, 0);
109: while ((breaks + s.offset) < s.array.length) {
110: s.offset += breaks;
111: s.count = s.array.length - s.offset;
112: numLines++;
113: breaks = getBreakLocation(s, fm, 0, width, te, 0);
114: }
115: return numLines * fm.getHeight();
116: }
117:
118: public int getTabbedTextOffset(Segment s, FontMetrics metrics,
119: int x0, int x, TabExpander e, int startOffset, boolean round) {
120: int currX = x0;
121: int nextX = currX;
122: char[] txt = s.array;
123: int n = s.offset + s.count;
124: for (int i = s.offset; i < n; i++) {
125: if (txt[i] == '\t') {
126: if (e != null) {
127: nextX = (int) e.nextTabStop(nextX, startOffset + i
128: - s.offset);
129: } else {
130: nextX += metrics.charWidth(' ');
131: }
132: } else if (txt[i] == '\n') {
133: return i - s.offset;
134: } else if (txt[i] == '\r') {
135: return i + 1 - s.offset; // kill the newline as well
136: } else {
137: nextX += metrics.charWidth(txt[i]);
138: }
139: if ((x >= currX) && (x < nextX)) {
140: // found the hit position... return the appropriate side
141: if ((round == false) || ((x - currX) < (nextX - x))) {
142: return i - s.offset;
143: } else {
144: return i + 1 - s.offset;
145: }
146: }
147: currX = nextX;
148: }
149:
150: return s.count;
151: }
152:
153: public int getBreakLocation(Segment s, FontMetrics metrics, int x0,
154: int x, TabExpander e, int startOffset) {
155:
156: int index = getTabbedTextOffset(s, metrics, x0, x, e,
157: startOffset, false);
158:
159: if ((s.offset + index) < s.array.length) {
160: for (int i = s.offset + Math.min(index, s.count - 1); i >= s.offset; i--) {
161:
162: char ch = s.array[i];
163: if (Character.isWhitespace(ch)) {
164: // found whitespace, break here
165: index = i - s.offset + 1;
166: break;
167: }
168: }
169: }
170: return index;
171: }
172:
173: class MyTabExpander implements TabExpander {
174: int tabSize;
175:
176: public MyTabExpander(FontMetrics metrics) {
177: tabSize = 5 * metrics.charWidth('m');
178: }
179:
180: public float nextTabStop(float x, int offset) {
181: int ntabs = (int) x / tabSize;
182: return (ntabs + 1) * tabSize;
183: }
184: }
185:
186: public int getRowHeight() {
187: System.err
188: .println("getRowHeight() not valid in MultiLineTable");
189: Thread.dumpStack();
190: return -1;
191: }
192:
193: public int getRowHeight(int row) {
194: TableModel tm = getModel();
195: int fontHeight = getFontMetrics(getFont()).getHeight();
196: int height = fontHeight;
197: Enumeration cols = getColumnModel().getColumns();
198: int i = 0;
199: while (cols.hasMoreElements()) {
200: TableColumn col = (TableColumn) cols.nextElement();
201: TableCellRenderer tcr = col.getCellRenderer();
202: // without the revalidate hack above, the call th getWidth does not give the
203: // right value at the right time. Take out the revalidate and uncomment the
204: // next line to see for your self. If you find a way to do it right, drop me
205: // a mail please! :)
206: // System.out.println(col.getWidth());
207: int colWidth = col.getWidth();
208: if (tcr instanceof MultiLineCellRenderer) {
209: height = Math.max(height, getHeight((String) tm
210: .getValueAt(row, i), colWidth));
211: }
212: i++;
213: }
214: return height;
215: }
216:
217: public Rectangle getCellRect(int row, int column,
218: boolean includeSpacing) {
219: Rectangle cellFrame;
220: TableColumn aColumn;
221:
222: cellFrame = new Rectangle();
223: // cellFrame.height = getRowHeight() + rowMargin;
224: // cellFrame.y = row * cellFrame.height;
225: cellFrame.height = getRowHeight(row) + rowMargin;
226: cellFrame.y = 0;
227: for (int i = 0; i < row; i++) {
228: cellFrame.y += getRowHeight(i) + rowMargin;
229: }
230:
231: int index = 0;
232: int columnMargin = getColumnModel().getColumnMargin();
233: Enumeration enumeration = getColumnModel().getColumns();
234: while (enumeration.hasMoreElements()) {
235: aColumn = (TableColumn) enumeration.nextElement();
236: cellFrame.width = aColumn.getWidth() + columnMargin;
237:
238: if (index == column)
239: break;
240:
241: cellFrame.x += cellFrame.width;
242: index++;
243: }
244:
245: if (!includeSpacing) {
246: Dimension spacing = getIntercellSpacing();
247: // This is not the same as grow(), it rounds differently.
248: cellFrame.setBounds(cellFrame.x + spacing.width / 2,
249: cellFrame.y + spacing.height / 2, cellFrame.width
250: - spacing.width, cellFrame.height
251: - spacing.height);
252: }
253: return cellFrame;
254: }
255:
256: public void columnSelectionChanged(ListSelectionEvent e) {
257: repaint();
258: }
259:
260: public void valueChanged(ListSelectionEvent e) {
261: int firstIndex = e.getFirstIndex();
262: int lastIndex = e.getLastIndex();
263: if (firstIndex == -1 && lastIndex == -1) { // Selection cleared.
264: repaint();
265: }
266: Rectangle dirtyRegion = getCellRect(firstIndex, 0, false);
267: int numColumns = getColumnCount();
268: int index = firstIndex;
269: for (int i = 0; i < numColumns; i++) {
270: dirtyRegion.add(getCellRect(index, i, false));
271: }
272: index = lastIndex;
273: for (int i = 0; i < numColumns; i++) {
274: dirtyRegion.add(getCellRect(index, i, false));
275: }
276: repaint(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width,
277: dirtyRegion.height);
278: }
279:
280: } // MultiLineTable
|