tags:

views:

960

answers:

5

I have a JTable and I need to be able to reorder the columns. However I want the first column to not be able to be re-ordered. I used the following to enable reordering:

table.getTableHeader().setReorderingAllowed(true);

The columns can now be reordered including the first column which I don't want. Is there any way to lock the first column?

I have seen some solutions that use two tables with the first column being in a separate table, but maybe there's a better/simpler way.

+2  A: 

I think that you need to override the columnMoved() method in TableColumnModelListener. the TableColumnModelEvent class has a getFromIndex() method that you should be able to look at to determine if it's your fixed column, and then you should be able to cancel the event.

Hope that helps. A

phatmanace
This looks to be the best way to go about it.
jjnguy
I've been toying with that, my question is (since I'm a Java neophyte) how do I cancel the event?
Bearddo
you choose whether to pass the method call to the superclass. if you don't, it won't be applied to the table.
phatmanace
A: 

First you need to define a better and simpler way. What don't you like about the 2 table approach?

You can't use a TableColumnModelListener, because the event is fired "after" the column has already been moved.

The code for dragging the column is found in the BasicTableHeaderUI. So you could try overriding the code there, but then you would need to do it for all LAFs.

The above code invokes JTableHeader.getReorderingAllowed() on a mousePressed event to determine if column reordering is allowed. I guess you could override that method in the JTableHeader and perhaps use the MouseInfo class to get the current mouse location to determine if it was over the first column and then return false. But then now you would also need to create a custom JTable that uses the custom table header.

Of course with either of the above suggestions you might be able to prevent the first column from being moved. But don't forget you also need to prevent the 2nd column from being inserted before the first column. I don't believe there is a short simple answer to the question.

Fixed Column Table is my version of how this would be imlemented with two tables. Is it better? I don't know, but it is simple since its only a single line of code to use it.

camickr
The "2tables approach" he probably doesn't like because that makes a break from the datamodel, I'm guessing. It would force to finally have data separated in two models, it's sure simple on the JTable, on the graphical side, but it's more annoying on the model side.
Gnoupi
It does not force you to use two models. Only the OP knows what his requirements are. We can't read his mind.
camickr
You don't need two models, you can easily do the two table approach from the same TableModel. This is the simplest approach, both from a conceptual standpoint and from a maintenance standpoint.
hasalottajava
A: 

I had the same issue, and I was searching about it. So far I found two ways of doing that.

  • The "if I was rewriting it myself" method : Modifying the base classes from Java.

TableColumn would need a new property, like the "resizingAllowed", it would need the "reorderingAllowed". From this, the modifications take place in BasicTableHeaderUI :

There is already :

private static boolean canResize(TableColumn column,
                                 JTableHeader header) {
    return (column != null) && header.getResizingAllowed()
                            && column.getResizable();
}

It would need too :

private static boolean canMove(TableColumn column,
                               JTableHeader header) {
    return (column != null) && header.getReorderingAllowed()
                                && column.getReorderable();
}

(Note that if you don't want the first column only to not move, you can do without changing the TableColumns :

private static boolean canMove(TableColumn column,
                                 JTableHeader header) {
    return (column != null) && header.getReorderingAllowed()
                            && header.getColumnModel().getColumnIndex(column.getIdentifier()) != 0;
}

)

After, two places to modify in the MouseInputListener :

  • in the mousePressed, calling the canMove() instead of the header.getReorderingAllowed(). This ensures that a column which shouldn't be moved, won't be.
  • But this is not enough, we need to prevent the immobile columns from being moved during dragging another one. You need to change the mouseDragged, too, when it is getting the "newColumnIndex" :

    if (0 < newColumnIndex && newColumnIndex < cm.getColumnCount())

You need to add the condition if this new index can be moved, for example using the "canMove()" method. This way, when you will drag a column to this immobile one, you will still drag it, but it won't swap them.

Note that this method would require you to explicitly set the UI for the JTableHeader used for your JTable, which is not really ideal. But this is the most adapted though, as it deals with the problem on the place it is supposed to.


  • The "Let's try to block the normal behavior with what we actually have" method : Not modifying the UI, this method focus on the JTableHeader to block the commands made by the UI.

First, to block dragging the first column, we need a subclass from JTableHeader, with this overridden method :

@Override
public void setDraggedColumn(TableColumn pAColumn)
{
    int lIndex  = -1;
    if (pAColumn != null)
        lIndex = getColumnModel().getColumnIndex(pAColumn.getIdentifier());
    if (lIndex != 0)
        super.setDraggedColumn(pAColumn);
}

This will prevent a user from dragging the first column. But like described earlier, this is only one part of the problem, we need to prevent another dragged column from swapping with this first one.

So far, I don't have a correct method for this. I tried by subclassing the TableColumnModel, and overriding the moveColumn() method :

@Override
public void moveColumn(int pColumnIndex, int pNewIndex)
{
    //Move only if the first column is not concerned
    if (pColumnIndex =! 0 && pNewIndex != 0)
        super.moveColumn(pColumnIndex, pNewIndex);
}

But this won't work, as the UI will update anyway the mouse position in the mouseDragged method, you will have a jump from your dragged column to another place.

So I'm still searching, and wonder if someone has propositions concerning this part.

Gnoupi
A: 

i have used the "The "Let's try to block the normal behavior with what we actually have" method" approach. Gnoupi said that he did not solve the second part of the problem. Here is the solution for just Windows XP L&F: 1) copy XPStyle class to yourself. 2) extend WindowsTableHeaderUI. Take a look at the source code http://docs.google.com/Doc?docid=0AUKZi9wFphLXZGRtd21yN2pfMWc0bmhkcjN0&amp;hl=en. 3) use it: getTableHeader().setUI(new TreeTableWindowsTableHeaderUI()); Thanks to Gnoupi for the efforts.

StanislavKo
+1  A: 

This is the solution that I used to prevent the 1st column from being re-ordered

private int columnValue = -1; 
private int columnNewValue = -1; 


tblResults.getColumnModel().addColumnModelListener(new TableColumnModelListener() 
{ 
    public void columnAdded(TableColumnModelEvent e) {} 

    public void columnMarginChanged(ChangeEvent e) {} 

    public void columnMoved(TableColumnModelEvent e) 
    { 
        if (columnValue == -1) 
            columnValue = e.getFromIndex(); 

        columnNewValue = e.getToIndex(); 
    } 

    public void columnRemoved(TableColumnModelEvent e) {} 

    public void columnSelectionChanged(ListSelectionEvent e) {} 
}); 

tblResults.getTableHeader().addMouseListener(new MouseAdapter() 
{ 
    @Override 
    public void mouseReleased(MouseEvent e) 
    { 
        if (columnValue != -1 && (columnValue == 0 || columnNewValue == 0)) 
        tblResults.moveColumn(columnNewValue, columnValue); 

        columnValue = -1; 
        columnNewValue = -1; 
    } 
}); 

Cheers,

denrosssalenga