JTableHeader + TableRowSorter Customization

Okay guys, need some help here.

I am trying to customize first of all a TableRowSorter to display sectional header information like in Windows Media Player or Explorer when you group data.

Here is a screen shot: [url]http://www.geocites.com/icewalker2g/images/aaesshot1.jpg[/url]

So far so good with rendering but the table sorting is not working as expected.

Here is screen shot 2: [url]http://www.geocites.com/icewalker2g/images/aaesshot2.jpg[/url]

These are the required sources for the rendering and sorting of the JTable.

1. TitledSeparator:

2. LineSeparatorRowSorter:

3. LibTableCellRenderer:

I put everything together as a neat little file which you can download here: http://www.geocities.com/icewalker2g/downloads/rowsorter.zip

The problem is a bit hard to explain cause I dont know where to look as at now. If anyone can help, I'll be glad. Here is a small sample application to test the code:

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.event.*;

import javax.swing.filechooser.*;

import javax.swing.border.*;

import javax.swing.table.*;

import java.util.*;

publicclass RowSorterTestextends JFrame{

LineSeparatorRowSorter rowSorter;

LibTableCellRenderer renderer =new LibTableCellRenderer();

public RowSorterTest(){

super("Row Sort Test");

createUI();

}

publicvoid createUI(){

Vector<Object> rows =new Vector<Object>();

for(int i = 0; i < 10; i++){

Vector<String> row =new Vector<String>();

for(int j = 0; j < 4; j++){

row.addElement("Item " + i +" " + j +".htm");

}

rows.addElement( row );

}

Vector<String> cols =new Vector<String>();

cols.addElement("File Name");

cols.addElement("File Description");

cols.addElement("File Location");

cols.addElement("File Size");

DefaultTableModel model =new DefaultTableModel(rows, cols);

JTable table =new JTable( model ){

public TableCellRenderer getCellRenderer(int row,int col){

return renderer;

}

};

table.setRowSorter( rowSorter =new LineSeparatorRowSorter( table ) );

table.setShowGrid(false);

rowSorter.sort();

JScrollPane scroll =new JScrollPane( table );

getContentPane().add( scroll );

pack();

}

publicstaticvoid main(String[] args){

new RowSorterTest().setVisible(true);

}

}

I hope someone can take a wack at this problem cause its been driving me nuts for like 48 hours now.

Thanks in advance for any help given.

[4577 byte] By [icewalker2ga] at [2007-11-27 8:18:47]
# 1
the links don't work for me (perhaps a cookie thing) - just takes me to www.geocities.comDoubt I could help (except for the bump), never used TableSorter, but what's itdoing that's wrong Vs what it's supposed to do?
Michael_Dunna at 2007-7-12 20:07:05 > top of Java-index,Desktop,Core GUI APIs...
# 2

My Bad with the links. I believe was in a haste when I typed them.

Screenshot 1: http://www.geocities.com/icewalker2g/images/aaesshot1.jpg

Screenshot 2: http://www.geocities.com/icewalker2g/images/aaesshot2.jpg

However, I was going about this the wrong way. I did not need to mess around with the RowSorter in anyway, I only needed to deal with the renderer.

I rewrote the code, and focussing on the renderer only and voila. Instant success. Now I can sort and filter with the necessary row separator. It still has relevant bugs in it but it is better than a no show.

I'll make sure to post it on my site when it is complete.

Here is the somewhat buggy code. The WindowFileSystem class and all other relevant classes can found in this zip file: http://www.geocities.com/icewalker2g/downloads/rowsorter.zip

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.event.*;

import javax.swing.filechooser.*;

import javax.swing.border.*;

import javax.swing.table.*;

import javax.swing.tree.*;

import java.io.*;

import java.sql.*;

import java.util.*;

public class LibTableCellRenderer extends DefaultTableCellRenderer {

private FileSystemView sysView = FileSystemView.getFileSystemView();

private WindowsFileSystem fileSystem = new WindowsFileSystem();

JTable table = null;

boolean isOnRow = false;

int currentHighlightRow = -1, sortColumn = 0, titleRow = -1;

Object rowValue = "";

Vector<Integer> sepRows = new Vector<Integer>();

Vector<String> sepTitles = new Vector<String>();

TitledSeparator tsep = null;

public LibTableCellRenderer() {

super();

}

public void attachListener() {

/*table.addMouseListener(new MouseAdapter() {

public void mouseEntered(MouseEvent e) {

isOnRow = true;

}

public void mouseExited(MouseEvent e) {

isOnRow = false;

currentHighlightRow = -1;

table.repaint();

}

});

table.addMouseMotionListener( new MouseMotionAdapter() {

public void mouseMoved(MouseEvent e) {

isOnRow = true;

currentHighlightRow = table.rowAtPoint( e.getPoint() );

table.repaint();

}

});*/

table.getTableHeader().addMouseListener( new MouseAdapter() {

public void mouseReleased(MouseEvent e) {

sortColumn = table.columnAtPoint( e.getPoint() );

System.out.println("Sort Column: " + sortColumn);

//setSortColumn(sortColumn);

}

});

table.getRowSorter().addRowSorterListener( new RowSorterListener() {

public void sorterChanged(RowSorterEvent e) {

if(e.getType() == RowSorterEvent.Type.SORTED ) {

createRowSeparators();

System.out.println("Seperator Rows Recreated");

}

}

});

}

public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,

boolean hasFocus, int row, int col) {

if(this.table == null) {

this.table = table;

attachListener();

createRowSeparators();

}

String repValue = "";

if(value == null) {

repValue = " ";

} else {

repValue = value.toString();

}

super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);

setText( repValue );

setIcon( new EmptyIcon(1,16) );

setHorizontalAlignment(JLabel.LEFT);

setForeground( table.getForeground() );

if(isOnRow && currentHighlightRow == row) {

setBackground( new Color(220, 240, 250) );

} else {

if(row % 2 == 0) {

setBackground( new Color(232,242,252) );

} else {

setBackground(Color.white);

}

}

if( value != null && table.getColumnName(col).equalsIgnoreCase("File Size") ) {

try {

int filePathCol = TableUtilities.getColumnIndex( table.getModel(), "File Path" );

value = FileUtilities.getFileSizeAsString( new File( table.getModel().getValueAt( table.convertRowIndexToModel(row), filePathCol).toString() ), "KB");

} catch(Exception e) {}

setText(value.toString());

setHorizontalAlignment(JLabel.RIGHT);

} else if( value != null && table.getColumnName(col).equalsIgnoreCase("File Name") ){

String text = value.toString();

String ext = text.substring( text.lastIndexOf(".") + 1, text.length() );

setIconTextGap(7);

setIcon( getFileIcon(ext) );

setText( text.substring(0, text.lastIndexOf(".") ) );

setBorder( new EmptyBorder(1,5,1,1) );

}

if(isSelected) {

setBackground( new Color(51, 153, 255) );

setForeground( Color.white );

}

//if(col == sortColumn && !repValue.equalsIgnoreCase(rowValue.toString())) {

// rowValue = repValue;

// titleRow = row;

//}

if( sepRows.contains(row) ) {

if(col == 0) {

tsep = new TitledSeparator( sepTitles.elementAt( sepRows.indexOf(row) ) );

} else {

tsep = new TitledSeparator("") {

protected void paintComponent(Graphics g) {

super.paintComponent(g);

if(comp != null) {

int width = getWidth();

int height = getHeight();

FontMetrics fm = g.getFontMetrics(getFont());

//int strWidth = (int)fm.getStringBounds(getText(), g).getWidth();

int strWidth = comp.getWidth();

int rectWidth = getWidth(), rectHeight = 2;

g.setColor( defaults.getColor("Separator.highlight") );

g.fillRect(width - rectWidth, height/2, rectWidth, 2);

g.setColor( defaults.getColor("Separator.foreground") );

g.fillRect(width - rectWidth, height/2, rectWidth, 1);

}

}

};

tsep.setBorder( null );

((JComponent)tsep.getComponent()).setBorder(null);

}

JPanel panel = new JPanel( new BorderLayout() );

panel.setBorder(null);

panel.setBackground(Color.white);

panel.add( tsep );

panel.add( this, BorderLayout.SOUTH );

table.setRowHeight( row, 45 );

return panel;

} else {

if(table.getRowHeight(row) != 20)

table.setRowHeight(row, 20);

}

return this;

}

public void setSortColumn(int column) {

this.sortColumn = column;

createRowSeparators();

}

public void createRowSeparators() {

if(table == null) {

return;

}

sepRows.removeAllElements();

sepTitles.removeAllElements();

Object rv = "";

for(int i = 0; i < table.getRowCount(); i++) {

Object value = table.getValueAt( i, sortColumn);

if(!value.equals(rv)) {

sepRows.addElement(i);

sepTitles.addElement(value.toString());

rv = value;

}

}

rv = null;

}

public Icon getFileIcon(String ext) {

File f = new File(fileSystem.getTempFolder(), "f." + ext);

if(ext.equalsIgnoreCase("folder")) {

f = new File(fileSystem.getTempFolder(), "mmptmp");

f.mkdir();

} else {

try {

f.createNewFile();

} catch(IOException ioe) {}

}

return sysView.getSystemIcon(f);

}

}

ICE

icewalker2ga at 2007-7-12 20:07:05 > top of Java-index,Desktop,Core GUI APIs...
# 3

Okay so I'm still stuck with this table thingy. At least, for everything works in terms of row sorting and separation.

What I need help with now, is trying to render text across columns. This may sound absurd but it is what is required. For now, the Separator text is rendered in the first column and for text which is longer than the column width, the trailing dots (ie . . . ) are shown indicating there is more text to be displayed.

I want to know if it is remotely possible to achieve this, i.e. draw text across multiple columns in a JTable?

ICE

icewalker2ga at 2007-7-12 20:07:05 > top of Java-index,Desktop,Core GUI APIs...
# 4
> draw text across multiple columns in a JTable?are you talking about merging the cells?
Michael_Dunna at 2007-7-12 20:07:05 > top of Java-index,Desktop,Core GUI APIs...
# 5

> are you talking about merging the cells?

Yes and No.

If you look at the implementation above, what I'm actually doing is placing the LineSeparator and the CellRenderer in a single JPanel and returning that for rendering when a row separation needs to be done. So I know, with that I cant draw across cells easily.

If I find a way to merge cells (I already gotten code for that) that would break this implementation, since both the cell content and the line separator will be running across multiple columns.

I also cant insert separate rows for the separator into the TableModel cause that would destroy the sort. (Refer to TableRowSorter/DefaultRowSorter documentation)

So Thats why I need some way to draw across multiple cells.

icewalker2ga at 2007-7-12 20:07:05 > top of Java-index,Desktop,Core GUI APIs...