I have a JTable where columns 3 and 4 use a JComboBox as the cell editor. Column 3 specifies a type, and column 4 specifies a subtype. The list of subtypes available in column 4 is dependent on what value the user has selected for the type. In addition, the list of types and subtypes are editable by the user (details are not important here, just that both the list of types and subtypes can change at runtime).
Because the content of the subtype combobox is dynamic, my intent is to detect when the user clicks into a subtype cell, and set the list model (set the list of subtype items available) at that moment based on the contents of the type within that row.
However, I'm having difficulty detecting a mouse click in the needed cells. I've posted my code and output below.
The problem is that the mouse listener doesn't detect a click within the combo box. Presumably, that event is fired by the combo box, not the table. I can use ListSelectionListener in combination with the TableColumnModelListener, but I have to imagine there is a better way. I tried to use events related to the combo box, but I can't get information on which combo box was selected (so I don't know where in the table I am). I tried to get the combo box's container and locate the click that way, but the getParent() method of the combo box returns null.
EDIT: From within the ComboBox ActionListener, I also tried using the JTable's getSelectedRow() and getSelectedColumn() methods, but they return the values of the old cell, not the new cell.
Any suggestions on how to do this a bit more concisely is appreciated.
Here are the relevant code snippets (the event handlers don't do much yet, just trying to get information):
// Setup table and table model
tableTypeDataModel = new TypeAssignmentTableModel();
JTable typeTable = new JTable(tableTypeDataModel);
// Add a mouse listener to the table.
typeTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e)
{
Point pnt = e.getPoint();
int row = typeTable.rowAtPoint(pnt);
int col = typeTable.columnAtPoint(pnt);
System.out.println(Integer.toString(row) ", " Integer.toString(col));
}
});
typeTable.getSelectionModel().addListSelectionListener(new myListSelectionListener());
typeTable.setFillsViewportHeight(true);
JScrollPane scpnTypeTable = new JScrollPane(typeTable);
// Set columns and model listener
TableColumnModel cm = typeTable.getColumnModel();
cm.addColumnModelListener(new myColumnListSelectionListener());
// Setup type column
JComboBox<String> cmbTableTypes = new JComboBox<>(arrOfTypes);
cmbTableTypes.addActionListener(new myCBOActionListener());
cm.getColumn(TYPECOL_TYPE).setCellEditor(new DefaultCellEditor(cmbTableTypes));
// This just adds left padding to the cell
IndentedTableCellRenderer tableTypeRenderer = new IndentedTableCellRenderer(5, 0);
cm.getColumn(TYPECOL_TYPE).setCellRenderer(tableTypeRenderer);
// Setup subtype column
tableSubtypeCboModel = new DefaultComboBoxModel<>();
JComboBox<String> cmbTableSubtypes = new JComboBox<>(tableSubtypeCboModel);
cm.getColumn(TYPECOL_SUBTYPE).setCellEditor(new DefaultCellEditor(cmbTableSubtypes));
IndentedTableCellRenderer tableSubtypeRenderer = new IndentedTableCellRenderer(5, 0);
cm.getColumn(TYPECOL_SUBTYPE).setCellRenderer(tableSubtypeRenderer);
And...
class myListSelectionListener implements ListSelectionListener {
@Override
public void valueChanged(ListSelectionEvent e) {
System.out.println("Table List selection listener = " e.getSource().toString());
}
}
class myColumnListSelectionListener implements TableColumnModelListener {
// other methods empty and not shown...
@Override
public void columnSelectionChanged(ListSelectionEvent e) {
// TODO Auto-generated method stub
System.out.println("Table column model listener Source = "
e.getSource().toString());
}
}
class myCBOActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
JComboBox item = (JComboBox) e.getSource();
System.out.println("CBO Action Listener = " item.getClass().toString());
String msg = item.getParent() == null ? "null" : item.getParent().toString();
System.out.println("CBO Action Listener item parent is " msg);
}
}
Here is the console output after clicking the following cells:
Row 1, Col 0 (just a regular cell)
Row 2, Col 1 (just a regular cell)
Row 3, Col 2 (just a regular cell)
Row 4, Col 3 (this is the Type column with a combo box editor)
Console Output:
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ~{0}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ~{1}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ={1}
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ={0}
1, 0
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ~{1}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ~{2}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ={2}
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ={1}
2, 1
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ~{2}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ~{3}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ={3}
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ={2}
3, 2
CBO Action Listener = class javax.swing.JComboBox
CBO Action Listener item parent is null
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ~{3}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ~{4}
Table List selection listener = javax.swing.DefaultListSelectionModel 1902920301 ={4}
Table column model listener Source = javax.swing.DefaultListSelectionModel 724380095 ={3}
Thanks.
CodePudding user response:
You could manage your subtype JCombobox
as a global and then add an ActionListener
that updates the subtype values when the subtype JCombobox
component is "selected" in the UI.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import javax.swing.*;
public class whatev{
JComboBox types = new JComboBox();
JComboBox subtypes = new JComboBox();
Map<String,int[]> m = new HashMap<String,int[]>(); //example source mapping types to sub-types
int[] t1 = {1,3};
int[] t2 = {4, 5};
public static void main(String[] args){ new whatev(); }
whatev(){
//Add example data to types
types.addItem("Type 1");
types.addItem("Type 2");
types.setSelectedIndex(0);
//init map data for example
m.put("Type 1", t1);
m.put("Type 2", t2);
//initialize subtype JCombobox
updateSubtypes();
//add appropriate listener to subtype box
subtypes.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateSubtypes();
}
});
}
private void updateSubtypes() {
int[] x = m.get(types.getSelectedItem()); //pull appropriate subtypes from type/subtype mapping source
subtypes.removeAllItems(); //purge old objects in subtype
for(int v : x) { subtypes.addItem(v); } //Add appropriate subtypes
}
}
CodePudding user response:
@camickr - thank you for the answer. I overrode the prepareEditor method as suggested which updates the ListModel in the combo box as required.
JTable typeTable = new JTable() {
@Override
public Component prepareEditor(TableCellEditor editor, int row, int column) {
if (column == SUBTYPE_COLUMN) {
// User has entered a subtype cell
// Get value of the adjacent type cell
updateCBOSubtypeDataModel(myTableModel.getValueAt(row, TYPE_COLUMN);
}
return super.prepareEditor(editor, row, column);
}
};
and
private void updateCBOSubtypeDataModel(TypeObject newType) {
myComboBoxListModel.removeAllElements();
myComboBoxListModel.addAll(getListOfSubtypesForThisType(newType));
}
Working nicely. Thanks.