/**
* MultiLineTable.java
*
* This class provides a JTable that supports multiline cells.
* (c) 2006 EduMIPS64 project - Rizzo Vanni G.
*
* Special Thanks to Thomas Wernitz (thomas_wernitz@clear.net.nz)
* for his source code.
*
* This file is part of the EduMIPS64 project, and is released under the GNU
* General Public License.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.edumips64.ui.swing;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.*;
import java.util.*;
class MultiLineTable extends JTable {
MultiLineTable(TableModel dm) {
this(dm, null, null);
}
private MultiLineTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) {
super(dm, cm, sm);
setUI(new MultiLineBasicTableUI());
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
revalidate();
}
});
}
@Override
public int rowAtPoint(Point point) {
int y = point.y;
int rowSpacing = getIntercellSpacing().height;
int rowCount = getRowCount();
int rowHeight = 0;
for (int row = 0; row < rowCount; row++) {
rowHeight += getRowHeight(row) + rowSpacing;
if (y < rowHeight) {
return row;
}
}
return -1;
}
int getHeight(String text, int width) {
if (text == null) {
text = "";
}
FontMetrics fm = getFontMetrics(getFont());
int numLines = 1;
Segment s = new Segment(text.toCharArray(), 0, 0);
s.count = s.array.length;
TabExpander te = new MyTabExpander(fm);
int breaks = getBreakLocation(s, fm, 0, width, te, 0);
while ((breaks + s.offset) < s.array.length) {
s.offset += breaks;
s.count = s.array.length - s.offset;
numLines++;
breaks = getBreakLocation(s, fm, 0, width, te, 0);
}
return numLines * fm.getHeight();
}
public boolean isCellEditable(int r, int c) {
return false;
}
private int getTabbedTextOffset(Segment s,
FontMetrics metrics,
int x0, int x, TabExpander e,
int startOffset,
boolean round) {
int currX = x0;
int nextX = currX;
char[] txt = s.array;
int n = s.offset + s.count;
for (int i = s.offset; i < n; i++) {
if (txt[i] == '\t') {
if (e != null) {
nextX = (int) e.nextTabStop((float) nextX,
startOffset + i - s.offset);
} else {
nextX += metrics.charWidth(' ');
}
} else if (txt[i] == '\n') {
return i - s.offset;
} else if (txt[i] == '\r') {
return i + 1 - s.offset; // kill the newline as well
} else {
nextX += metrics.charWidth(txt[i]);
}
if ((x >= currX) && (x < nextX)) {
// found the hit position... return the appropriate side
if ((!round) || ((x - currX) < (nextX - x))) {
return i - s.offset;
} else {
return i + 1 - s.offset;
}
}
currX = nextX;
}
return s.count;
}
private int getBreakLocation(Segment s, FontMetrics metrics,
int x0, int x, TabExpander e,
int startOffset) {
int index = getTabbedTextOffset(s, metrics, x0, x,
e, startOffset, false);
if ((s.offset + index) < s.array.length) {
for (int i = s.offset + Math.min(index, s.count - 1);
i >= s.offset; i--) {
char ch = s.array[i];
if (Character.isWhitespace(ch)) {
// found whitespace, break here
index = i - s.offset + 1;
break;
}
}
}
return index;
}
private class MyTabExpander implements TabExpander {
int tabSize;
MyTabExpander(FontMetrics metrics) {
tabSize = 5 * metrics.charWidth('m');
}
public float nextTabStop(float x, int offset) {
int ntabs = (int) x / tabSize;
return (ntabs + 1) * tabSize;
}
}
public int getRowHeight() {
//getRowHeight() not valid in MultiLineTable
//Thread.dumpStack();
return -1;
}
public int getRowHeight(int row) {
TableModel tm = getModel();
int height = getFontMetrics(getFont()).getHeight();
Enumeration<TableColumn> cols = getColumnModel().getColumns();
int i = 0;
while (cols.hasMoreElements()) {
TableColumn col = cols.nextElement();
TableCellRenderer tcr = col.getCellRenderer();
// without the revalidate hack above, the call th getWidth does not give the
// right value at the right time. Take out the revalidate and uncomment the
// next line to see for your self. If you find a way to do it right, drop me
// a mail please! :)
// System.out.println(col.getWidth());
int colWidth = col.getWidth();
if (tcr instanceof MultiLineCellRenderer) {
height = Math.max(height, getHeight((String) tm.getValueAt(row, i), colWidth));
}
i++;
}
return height;
}
public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
Rectangle cellFrame;
TableColumn aColumn;
cellFrame = new Rectangle();
// cellFrame.height = getRowHeight() + rowMargin;
// cellFrame.y = row * cellFrame.height;
cellFrame.height = getRowHeight(row) + rowMargin;
cellFrame.y = 0;
for (int i = 0; i < row; i++) {
cellFrame.y += getRowHeight(i) + rowMargin;
}
int index = 0;
int columnMargin = getColumnModel().getColumnMargin();
Enumeration<TableColumn> enumeration = getColumnModel().getColumns();
while (enumeration.hasMoreElements()) {
aColumn = enumeration.nextElement();
cellFrame.width = aColumn.getWidth() + columnMargin;
if (index == column) {
break;
}
cellFrame.x += cellFrame.width;
index++;
}
if (!includeSpacing) {
Dimension spacing = getIntercellSpacing();
// This is not the same as grow(), it rounds differently.
cellFrame.setBounds(cellFrame.x + spacing.width / 2,
cellFrame.y + spacing.height / 2,
cellFrame.width - spacing.width,
cellFrame.height - spacing.height);
}
return cellFrame;
}
} // MultiLineTable