Ich dachte eigentlich schon, ich bekomme das ganze überhaupt nicht mehr hin. Das Hauptproblem besteht in der Navigation die natürlich in Actions gemacht wird die vom TableUI installiert werden. Alle Navigations-Aktionen (und das sind etliche) gehen davon aus, dass jede Zelle selektiert werden kann. Man müsste alle ersetzen um eine Einhundertprozentlösung zu erreichen...
Außerdem habe ich den Ansatz getestet, einfach zwei Tabellen nebeneinander auf ein JPanel-Derivat zu legen, dieses auf dem Viewport eines JScrollPane zu installieren und alle Gemeinsamkeiten gleich zu setzen; also gleiche TableModel-Instanz, gleiche RowSorter-Instanz, etc. Dann könnte man der linken Tabelle ein ColumnModel geben das nur die nicht auswählbaren Spalten kennt und der rechten eines das die auswählbaren Spalten kennt. Funktioniert auch problemlos. Allerdings benötigt man dann ein drittes Column Model, das beide Column Models hintereinander hängt, bei moveColumn aufpasst, dass es die Columns aus dem ersten nicht ins zweite verschieben kann, usw. Dann muss man noch circa 250 Zeilen Quelltext aus der JTable-Klasse kopieren (configureEnclosingScrollPane() und unconfiger...) damit die JScrollPane auch so aussieht wie die
einer Tabelle und von dem Panel
einen JTableHeader bekommt. Und dann muss dieses JPanel-Derivat auch noch Scrollable implementieren. Das wär's dann auch schon.
Nach einigen Tests und Gedankenspielen ist es dann wieder der Ansatz geworden den ich zuerst gehabt und fast verworfen hatte. Eine ColumnSelectionModel-Implementation die den LeadSelectionIndex prüft und verändert und eine ColumnModel-Implementation die das ColumnSelectionModel bzgl. der gesperrten Spalten aktualisieren kann. Ohne die Navigations-Aktionen anzufassen ist diese Lösung ebenfalls nicht einhundertprozentig. Wenn man bspw. die letzte Spalte sperrt, und mit TAB navigiert, dann gelangt man nicht in die nächste Zeile; das kann das
ColumnSelectionModel ja auch nicht. Das SelectionModel rät eben nur, wohin es nun den LeadSelectionIndex verschieben soll, wenn der eigentlich gewünschte LeadIndex eine gesperrte Spalte trifft. Aber das Verhalten ist mindestens annehmbar.
Die Klassennamen gefallen mir nicht sonderlich, die Nested Classes und Interfaces sollten aus der Testklasse raus, die Methodennamen sind auch nicht ganz in Ordnung und Javadoc ist auch zu spärlich gesäht... Aber für den Anfang ist's okay. Teste mal, ob die Lösung zu Deinem Problem passt und ob Du sie so oder ähnlich übernehmen willst.
[HIGHLIGHT="Java"]import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.util.*;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableColumn;
public class TableCellFocusTest {
/**
* Extension of the {@link DefaultTableColumnModel} to support denying focus
* on specified columns. Note that the extension does not work if the
* selection model is re-assigned an instance not implementing the
* {@link ControlledLeadSelectionModel} interface.
*
* @version $Revision$ as of $Date$
* @author Sebastian Haufe
*/
static class FocusSelectableColumnModel extends DefaultTableColumnModel {
// -----------------------------------------------------------------------
// Instance fields
// -----------------------------------------------------------------------
private final Set<Object> notFocusable =
Collections.newSetFromMap(new WeakHashMap<Object, Boolean>());
// -----------------------------------------------------------------------
// Construction
// -----------------------------------------------------------------------
@Override
protected ListSelectionModel createSelectionModel() {
return new DefaultControlledLeadSelectionModel();
}
// -----------------------------------------------------------------------
// Overridden Methods
// -----------------------------------------------------------------------
@Override
public void moveColumn(int columnIndex, int newIndex) {
super.moveColumn(columnIndex, newIndex);
updateFocusableColumn(newIndex);
}
@Override
public void addColumn(TableColumn column) {
super.addColumn(column);
updateFocusableColumn(getColumnIndex(column.getIdentifier()));
}
@Override
public void removeColumn(TableColumn column) {
super.removeColumn(column);
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
super.propertyChange(evt);
if ("identifier".equals(evt.getPropertyName())
&& notFocusable.remove(evt.getOldValue())) {
notFocusable.add(evt.getNewValue());
updateFocusableColumns();
}
}
private void updateFocusableColumns() {
final ListSelectionModel lsm = getSelectionModel();
if (lsm instanceof ControlledLeadSelectionModel) {
final ControlledLeadSelectionModel selModel =
(ControlledLeadSelectionModel) lsm;
final int columnCount = getColumnCount();
for (int i = 0; i < columnCount; i++) {
final TableColumn column = getColumn(i);
final Object identifier = column.getIdentifier();
selModel
.setAcceptedLeadIndex(i, !notFocusable.contains(identifier));
selModel.setSize(getColumnCount());
}
}
}
private void updateFocusableColumn(int index) {
final ListSelectionModel lsm = getSelectionModel();
if (lsm instanceof ControlledLeadSelectionModel) {
final ControlledLeadSelectionModel selModel =
(ControlledLeadSelectionModel) lsm;
final TableColumn column = getColumn(index);
final Object identifier = column.getIdentifier();
selModel.setAcceptedLeadIndex(index, !notFocusable
.contains(identifier));
selModel.setSize(getColumnCount());
}
}
// -----------------------------------------------------------------------
// API extension
// -----------------------------------------------------------------------
/**
* Set the given column focusable / unfocusable. If the column currently
* has focus, removes the focus.
*
* @param column the column
* @param focusable {@code true} to allow focus, {@code false} to deny
*/
public void setColumnFocusable(TableColumn column, boolean focusable) {
final Object identifier = column.getIdentifier();
final boolean changed;
if (focusable) {
changed = notFocusable.remove(identifier);
} else {
changed = notFocusable.add(identifier);
}
if (changed) {
updateFocusableColumn(getColumnIndex(identifier));
}
}
/**
* Determines for the given column whether it is focusable.
*
* @param column the column
* @return {@code true} if allowed to be focused, {@code false} if denied
*/
public boolean isColumnFocusable(TableColumn column) {
return !notFocusable.contains(column.getIdentifier());
}
}
/**
* Selection Model extension to support blocking certain indices for lead
* selection.
*
* @version $Revision$ as of $Date$
* @author Sebastian Haufe
*/
interface ControlledLeadSelectionModel extends ListSelectionModel {
/**
* Returns whether the given index is accepted as lead selection index
* within {@code this} selection model.
*
* @param index the index to check
* @return {@code true} if accepted
*/
boolean isAcceptedLeadIndex(int index);
/**
* Sets whether the given index is accepted as lead selection index within
* {@code this} selection model.
*
* @param index the index
* @param accepted
*/
void setAcceptedLeadIndex(int index, boolean accepted);
/**
* Sets the size of the selection model. The size is necessary to
* automatically determine an alternative lead index, if the original lead
* index is not accepted by {@link #isAcceptedLeadIndex(int)}.
*
* @param size the size
*/
void setSize(int size);
/**
* Gets the size of the selection model.
*
* @return the size
* @see #setSize(int)
*/
int getSize();
}
/**
* Default implementation of the {@link ControlledLeadSelectionModel}
* interface; automatically changing the lead index to an alternative index,
* if the desired lead index is not accepted.
* <p>
* Note that the algorithm to determine the alternative lead index is a
* trying to figure out the desired behavior from the, seen from the point
* of view of a user navigating with the cursor keys.
*
* @version $Revision$ as of $Date$
* @author Sebastian Haufe
*/
static class DefaultControlledLeadSelectionModel
extends DefaultListSelectionModel implements ControlledLeadSelectionModel {
private final BitSet leadBlocked = new BitSet();
private int size = 0;
// -----------------------------------------------------------------------
// Implementing LeadSelectableSelectionModel
// -----------------------------------------------------------------------
public boolean isAcceptedLeadIndex(int index) {
return !leadBlocked.get(index);
}
public void setAcceptedLeadIndex(int index, boolean accepted) {
leadBlocked.set(index, !accepted);
if (getLeadSelectionIndex() == index && !accepted) {
setLeadSelectionIndex(getLeadSelectionIndex());
}
}
public void setSize(int size) {
this.size = size;
}
public int getSize() {
return size;
}
// -----------------------------------------------------------------------
// Fix Index according to lead selectable rules
// -----------------------------------------------------------------------
private int findAcceptableLeadIndex(int index) {
if (index == -1) {
return -1;
}
final int oldLead = getLeadSelectionIndex();
final int dLead = index - oldLead;
final int maxIndex = getSize() - 1;
int result;
switch (dLead) {
// trivial, occurs on usual cursor navigation
case 1:
result = findForward(index, maxIndex);
if (result == -1) {
result = findForward(0, index);
}
break;
case -1:
result = findBackward(index, 0);
if (result == -1) {
result = findBackward(maxIndex, index);
}
break;
default:
if (oldLead == -1 || dLead >= 0) {
result = findForward(index, maxIndex);
if (result == -1) {
result = findBackward(index, 0);
}
} else {
result = findBackward(index, 0);
if (result == -1) {
result = findForward(index, maxIndex);
}
}
break;
}
return result != -1 || oldLead == -1
? result
: isAcceptedLeadIndex(oldLead) ? oldLead : -1;
}
private int findForward(int index, final int maxIndex) {
for (int i = index; i <= maxIndex; i++) {
if (isAcceptedLeadIndex(i)) {
return i;
}
}
return -1;
}
private int findBackward(int index, int minIndex) {
for (int i = index; i >= minIndex; i--) {
if (isAcceptedLeadIndex(i)) {
return i;
}
}
return -1;
}
// -----------------------------------------------------------------------
// Overridden Methods
// -----------------------------------------------------------------------
@Override
public void setLeadSelectionIndex(int index) {
super.setLeadSelectionIndex(findAcceptableLeadIndex(index));
}
@Override
public void moveLeadSelectionIndex(int leadIndex) {
super.moveLeadSelectionIndex(findAcceptableLeadIndex(leadIndex));
}
@Override
public void removeSelectionInterval(int index0, int index1) {
super.removeSelectionInterval(index0, findAcceptableLeadIndex(index1));
}
@Override
public void addSelectionInterval(int index0, int index1) {
super.addSelectionInterval(index0, findAcceptableLeadIndex(index1));
}
@Override
public void setSelectionInterval(int index0, int index1) {
super.setSelectionInterval(index0, findAcceptableLeadIndex(index1));
}
@Override
public void removeIndexInterval(int index0, int index1) {
final int gap = index1 - index0 + 1;
final int last = getSize() - gap;
for (int i = index1; i <= last; i++) {
leadBlocked.set(i, leadBlocked.get(i + gap));
}
size -= gap;
super.removeIndexInterval(index0, index1);
}
@Override
public void insertIndexInterval(int index, int length, boolean before) {
final int insMinIndex = (before) ? index : index + 1;
final int maxIndex = getSize() - 1;
for (int i = maxIndex; i >= insMinIndex; i--) {
leadBlocked.set(i + length, leadBlocked.get(i));
}
size += length;
super.insertIndexInterval(index, length, before);
}
}
// -------------------------------------------------------------------------
// Program Entry Point
// -------------------------------------------------------------------------
/**
* Test main method.
*
* @param args ignored
*/
public static void main(String[] args) {
final JTable table1 = new JTable(20, 10);
table1.setAutoCreateRowSorter(true);
final FocusSelectableColumnModel columnModel =
new FocusSelectableColumnModel();
table1.setColumnModel(columnModel);
table1.createDefaultColumnsFromModel();
final DefaultTableCellRenderer renderer = new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
setBackground(Color.LIGHT_GRAY);
return this;
}
};
int[] blocked = { 0, 2, 8, 9 };
for (int i : blocked) {
final TableColumn col = columnModel.getColumn(i);
columnModel.setColumnFocusable(col, false);
col.setCellRenderer(renderer);
}
final JPanel mainPanel = new JPanel(new BorderLayout(6, 6));
mainPanel.add(new JScrollPane(table1), BorderLayout.CENTER);
final JFrame f = new JFrame("Table Cell Focus Fun");
f.setContentPane(mainPanel);
f.pack();
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setVisible(true);
}
}[/HIGHLIGHT]
Gute Nacht.
Ebenius