Skip to content

Instantly share code, notes, and snippets.

@exhuma
Created May 17, 2011 14:10
Show Gist options
  • Save exhuma/976531 to your computer and use it in GitHub Desktop.
Save exhuma/976531 to your computer and use it in GitHub Desktop.
Avoid focus on non-editable cells in a JTable
package lu.statec.gui.blaise;
import java.awt.event.KeyEvent;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.table.TableModel;
import org.jdesktop.swingx.JXTable;
/**
* This table overrides the selection behavior. If the selection happens to land
* on a non-editable cell, the selection is forwarded to the next editable cell.
*
* Additionally, the "Enter"/"Return" key is rebound, so it selects the next
* cell instead of next row.
*
* NOTE:
* It extends JXTable from swingx. This is for convenience only. If you don't
* have or want to use swingx, you can simply replace it with a simple JTable
* in the class definition.
*
* Original idea by Joe Knapka can be found at:
* http://www.javakb.com/Uwe/Forum.aspx/java-gui/3796/Cell-focus-in-JTable-avoid-focus-on-non-editable-cells
*
* @author Michel Albert
*/
public class QuickEditTable extends JXTable {
public static enum DIR {
Up, Down, Left, Right
}
/**
* The logger
*/
private static final Logger LOG = Logger.getLogger(QuickEditTable.class.getCanonicalName());
/**
* Standard constructor
*/
public QuickEditTable() {
init();
}
/**
* Default constructor, initialising the table with a TableModel
*
* @param tableModel
*/
public QuickEditTable(TableModel tableModel) {
super(tableModel);
init();
}
/**
* Initialisations common to all constructors.
*/
private void init() {
// When pressing enter, move to the next cell instead of the next row.
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "selectNextColumnCell");
}
@Override
public void changeSelection(int row, int col, boolean toggle, boolean expand) {
// This method is called when the user tries to move to a different cell.
// If the cell they're trying to move to is not editable, we look for
// the next cell in the proper direction that is editable.
// initialise the new (future) selection to the indices as specified in
// the method call.
// if everything is "normal" we can just use these values. All special
// cases will overwrite the value contained herein. Eventually, these
// variables are returned.
int targetViewRow = row;
int targetViewCol = col;
int targetModelRow = convertRowIndexToModel(targetViewRow);
int targetModelCol = convertColumnIndexToModel(targetViewCol);
if (LOG.isLoggable(Level.FINE)){
LOG.fine(String.format("Attempting to change the table selection "
+ "to row %d, col %d, using toggle=%b and expand=%b",
targetViewRow, targetViewCol, toggle, expand));
LOG.fine(String.format(" --> this maps to model row %d and col %d",
targetModelRow, targetModelCol));
}
if (!getModel().isCellEditable(targetModelRow, targetModelCol)) {
if (LOG.isLoggable(Level.FINE)){
LOG.fine(String.format("Cell %d,%d (%d,%d) is not editable!",
targetViewRow, targetViewCol, targetModelRow, targetModelCol));
}
// Find the row and column we're coming from.
int oldViewRow = getEditingRow();
int oldViewCol = getEditingColumn();
if (oldViewRow == -1) {
oldViewRow = getSelectedRow();
}
if (oldViewCol == -1) {
oldViewCol = getSelectedColumn();
}
if (LOG.isLoggable(Level.FINE)) {
LOG.fine(String.format("We came from cell %d,%d",
oldViewRow, oldViewCol));
}
DIR direction;
if (oldViewCol == targetViewCol && oldViewRow < targetViewRow) {
direction = DIR.Down;
} else if (oldViewCol == targetViewCol && oldViewRow >= targetViewRow) {
direction = DIR.Up;
} else if (oldViewCol == targetViewCol && oldViewRow == targetViewRow) {
direction = DIR.Left;
} else {
// defaulting to right
direction = DIR.Right;
}
LOG.fine(String.format("Moving %s", direction));
// determine next cell position
while (!getModel().isCellEditable(targetModelRow, targetModelCol)) {
LOG.fine(String.format("Model-Cell %d,%d is still not editable",
targetModelRow, targetModelCol));
switch (direction) {
case Up:
targetViewRow -= 1;
if (targetViewRow < 0) {
targetViewRow = getRowCount() - 1;
}
break;
case Down:
targetViewRow += 1;
if (targetViewRow > getRowCount() - 1) {
targetViewRow = 0;
}
break;
case Left:
targetViewCol -= 1;
if (targetViewCol < 0) {
targetViewCol = getRowCount() - 1;
targetViewRow -= 1;
if (targetViewRow < 0) {
targetViewRow = getRowCount() - 1;
}
}
break;
case Right:
targetViewCol += 1;
if (targetViewCol > getColumnCount() - 1) {
targetViewCol = 0;
targetViewRow += 1;
if (targetViewRow > getRowCount() - 1) {
targetViewRow = 0;
}
}
break;
}
targetModelRow = convertRowIndexToModel(targetViewRow);
targetModelCol = convertColumnIndexToModel(targetViewCol);
}
LOG.fine(String.format("Trying to move selection to %d,%d instead!",
targetViewRow, targetViewCol));
}
super.changeSelection(targetViewRow, targetViewCol, toggle, expand);
}
}
@exhuma
Copy link
Author

exhuma commented Jun 6, 2011

Rewrote the selection logic slightly. My previous modification had a flawed logic concerning the model <-> view conversion of column indices. This implementation seems to work correctly. At the same time, I inlined the two separate methods into one block.

@picen
Copy link

picen commented Nov 11, 2017

Hello, it's a great pleasure to say hello, I do not speak or write in English and I apologize if it does not read well since I use Google translation, I'm a Java beginner and I do not know how to start this class to use a JFrame, I have a project at the university and they ask me to do what they implemented in this class. I do not know if you could help me use your class from a jframe that has a table to evaluate the cells when they have the focus, thanks in advance.
I use the Netbeans IDE and the table is created graphically, not by code, my question is how do I apply its class to the table that I create graphically. So I have tried but it does not work for me:

Object [] col = {"Sku", "Descripcon", "Measure", "Quantity", "Price", "SubTotal"};

// this table is the one I have created from the graphic environment and here I assign it to its class but it does not work
table = new CustomSelectTable (model);

I do not know how it would be the correct way to implement your class to the JTable that you create.

@picen
Copy link

picen commented Nov 12, 2017

And modelÑ
DefaultTableModel model = new DefaultTableModel(null,col);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment