I am using AbstractTableModel.
I have used the code provided from this website(http://www.javalobby.org/articles/jtable/?source=archives) to auto generate new rows for the JTable when JTable reaches on last row.
Its working fine but I am not able to update the column names anymore when user clicks a button. So I have to remove auto generate row feature to update column names.
As far as I understand the code from the website, Its using a hidden column as a trigger to create new row. InteractiveTableModelListener is disabling something which I don't understand how to change/edit it?
How could I solve this?
Thanks for your time!!
This is a simple example of how it might be possible to provide "auto add at end" functionality
Basically, what this does is maps custom key bindings to provide customised key board navigation, in this example, this includes Tab, Shift+Tab, Enter, Left, Right, Up, Down which allows the ability to determine what should happen when these keys are pressed.
Mostly, Tab, Enter, Left, Up, Down are most relevant, I just added the others in as an example and to maintain consistency.
A feature that might be slightly more useful would be to provide a "row factory" which would be capable of producing the required data for a new blank row, instead of just inserting a bunch of nulls, or at least I would find it useful.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableModel;
public class TestTable {
public static void main(String[] args) {
new TestTable();
}
public TestTable() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
MutableDefaultTableModel model = new MutableDefaultTableModel(4, 4);
JTable table = new JTable(model);
KeyStroke tabKey = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
KeyStroke shiftTabKey = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK);
KeyStroke enterKey = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
KeyStroke arrowLeftKey = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
KeyStroke arrowRightKey = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0);
KeyStroke arrowDownKey = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0);
KeyStroke arrowUpKey = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0);
InputMap im = table.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.put(tabKey, "nextCell");
im.put(enterKey, "nextCell");
im.put(arrowRightKey, "nextCell");
im.put(shiftTabKey, "previousCell");
im.put(arrowLeftKey, "previousCell");
im.put(arrowDownKey, "nextRow");
im.put(arrowUpKey, "previousRow");
ActionMap am = table.getActionMap();
am.put("nextCell", new NextCellAction(table, model));
am.put("previousCell", new PreviousCellAction(table, model));
am.put("nextRow", new NextRowAction(table, model));
am.put("previousRow", new PreviousRowAction(table, model));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MutableDefaultTableModel extends DefaultTableModel implements MutableTableModel {
public MutableDefaultTableModel(int rows, int cols) {
super(rows, cols);
}
#Override
public void insertNewRow(int row) {
Object[] rowData = new Object[getColumnCount()];
if (row < getRowCount()) {
insertRow(row, rowData);
} else {
addRow(rowData);
}
}
}
public interface MutableTableModel extends TableModel {
public void insertNewRow(int row);
}
public abstract class AbstractTableAction extends AbstractAction {
private JTable table;
private MutableTableModel model;
private boolean forceStopEditing;
public AbstractTableAction(JTable table, MutableTableModel model) {
this.table = table;
this.model = model;
}
public MutableTableModel getModel() {
return model;
}
public JTable getTable() {
return table;
}
public boolean isForceStopEditing() {
return forceStopEditing;
}
public void setForceStopEditing(boolean forceStopEditing) {
this.forceStopEditing = forceStopEditing;
}
public void stopCellEditing() {
TableCellEditor editor = getTable().getCellEditor();
if (editor != null) {
if (!editor.stopCellEditing() && isForceStopEditing()) {
editor.cancelCellEditing();
}
}
}
#Override
public void actionPerformed(ActionEvent e) {
JTable table = getTable();
int row = table.getSelectedRow();
int col = table.getSelectedColumn();
int rowCount = table.getRowCount();
int colCount = table.getColumnCount();
stopCellEditing();
int cell[] = updateCellCoordinates(row, col);
row = cell[0];
col = cell[1];
if (col < 0) {
col = colCount - 1;
row--;
} else if (col >= colCount) {
col = 0;
row++;
}
if (row < 0) {
row = 0;
} else if (row >= rowCount) {
getModel().insertNewRow(row);
}
table.setRowSelectionInterval(row, row);
table.setColumnSelectionInterval(col, col);
}
protected abstract int[] updateCellCoordinates(int row, int col);
}
public class NextCellAction extends AbstractTableAction {
public NextCellAction(JTable table, MutableTableModel model) {
super(table, model);
}
#Override
protected int[] updateCellCoordinates(int row, int col) {
return new int[]{row, ++col};
}
}
public class PreviousCellAction extends AbstractTableAction {
public PreviousCellAction(JTable table, MutableTableModel model) {
super(table, model);
}
#Override
protected int[] updateCellCoordinates(int row, int col) {
return new int[]{row, --col};
}
}
public class NextRowAction extends AbstractTableAction {
public NextRowAction(JTable table, MutableTableModel model) {
super(table, model);
}
#Override
protected int[] updateCellCoordinates(int row, int col) {
return new int[]{++row, col};
}
}
public class PreviousRowAction extends AbstractTableAction {
public PreviousRowAction(JTable table, MutableTableModel model) {
super(table, model);
}
#Override
protected int[] updateCellCoordinates(int row, int col) {
return new int[]{--row, col};
}
}
}
Related
I want to change the action of the button to delete. I have this code:
package buttonexample;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
public class ButtonExample {
public JTable table;
public static void main(String[] args) {
final ButtonExample example = new ButtonExample();
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
example.createAndShowGUI();
}
});
}
private void createAndShowGUI() {
JFrame frame = new JFrame("Button Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
table = new JTable(new JTableModel());
JScrollPane scrollPane = new JScrollPane(table);
table.setFillsViewportHeight(true);
TableCellRenderer buttonRenderer = new JTableButtonRenderer();
//table.getColumn("Button1").setCellRenderer(buttonRenderer);
table.getColumn("Button2").setCellRenderer(buttonRenderer);
table.addMouseListener(new JTableButtonMouseListener(table));
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
frame.getContentPane().setPreferredSize(new Dimension(500, 200));
frame.pack();
frame.setVisible(true);
}
public static class JTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private static final String[] COLUMN_NAMES = new String[] {"Id", "Stuff", "Asdfsdf", "Button2"};
private static final Class<?>[] COLUMN_TYPES = new Class<?>[] {Integer.class, String.class, String.class, JButton.class};
#Override public int getColumnCount() {
return COLUMN_NAMES.length;
}
#Override public int getRowCount() {
return 4;
}
#Override public String getColumnName(int columnIndex) {
return COLUMN_NAMES[columnIndex];
}
#Override public Class<?> getColumnClass(int columnIndex) {
return COLUMN_TYPES[columnIndex];
}
#Override public Object getValueAt(final int rowIndex, final int columnIndex) {
switch (columnIndex) {
case 0: return rowIndex;
case 1: return "Text for "+rowIndex;
case 2: return "Column for "+rowIndex;
case 3: final JButton button = new JButton(COLUMN_NAMES[columnIndex]);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// When this is clicked the whole row will be deleted.
JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(button),
"Button clicked for row "+rowIndex);
}
});
return button;
default: return "Error";
}
}
}
private static class JTableButtonRenderer implements TableCellRenderer {
#Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JButton button = (JButton)value;
if (isSelected) {
button.setForeground(table.getSelectionForeground());
button.setBackground(table.getSelectionBackground());
} else {
button.setForeground(table.getForeground());
button.setBackground(UIManager.getColor("Button.background"));
}
return button;
}
}
private static class JTableButtonMouseListener extends MouseAdapter {
private final JTable table;
public JTableButtonMouseListener(JTable table) {
this.table = table;
}
public void mouseClicked(MouseEvent e) {
int column = table.getColumnModel().getColumnIndexAtX(e.getX());
int row = e.getY()/table.getRowHeight();
if (row < table.getRowCount() && row >= 0 && column < table.getColumnCount() &&
column >= 0) {
Object value = table.getValueAt(row, column);
if (value instanceof JButton) {
((JButton)value).doClick();
}
}
}
}
}
But the (DefaultTableModel)someTable.getModel(); model.removeRow(row); I can't make it work because the table model does not have any name and I don't know what to name it. I have tried initializing a table model and I get error saying non-static variable model cannot be reference to a static context. Is there a way to delete the row in a static context?
The example by camickr is probably a more reusable solution, but for the sake of education...
Rendering of cells is the domain of the view
Model's should never contain components, especially if you're thinking about rendering them to the screen, this is simply the wrong approach to take...
You need to set up a TableCellEditor which will act as the means by which you can retrieve notification of the edit actions (mouse click or keypress)
This is a basic example, for simplicity, I've used a DefaultTableModel as it has a nice removeRow method, but conceivably, you could use any TableModel, so long as you provided the means to remove a row and modified by the editor to support it...
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.event.CellEditorListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
public class ButtonExample {
public JTable table;
public static void main(String[] args) {
final ButtonExample example = new ButtonExample();
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
example.createAndShowGUI();
}
});
}
private void createAndShowGUI() {
JFrame frame = new JFrame("Button Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
table = new JTable(new ExampleTableModel());
JScrollPane scrollPane = new JScrollPane(table);
table.setFillsViewportHeight(true);
table.getColumn("action").setCellRenderer(new ButtonCellRenderer());
table.getColumn("action").setCellEditor(new ButtonCellEditor());
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
public static class ExampleTableModel extends DefaultTableModel {
public ExampleTableModel() {
super(new Object[]{"id", "stuff", "blah", "action"}, 0);
for (int index = 0; index < 10; index++) {
addRow(new Object[]{index, "Text for " + index, "Na na", index});
}
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex == 3;
}
}
public static class ButtonCellRenderer extends JButton implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value != null) {
setText("Delete row " + value.toString());
} else {
setText("Delete Me");
}
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(UIManager.getColor("Button.background"));
}
return this;
}
}
public static class ButtonCellEditor extends AbstractCellEditor implements TableCellEditor {
private JButton editor;
private Object value;
private int row;
private JTable table;
public ButtonCellEditor() {
editor = new JButton();
editor.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (table != null) {
fireEditingStopped();
TableModel model = table.getModel();
if (model instanceof DefaultTableModel) {
((DefaultTableModel) model).removeRow(row);
}
}
}
});
}
#Override
public boolean isCellEditable(EventObject e) {
return true;
}
#Override
public Object getCellEditorValue() {
return value;
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
this.table = table;
this.row = row;
this.value = value;
if (value != null) {
editor.setText("Delete row " + value.toString());
} else {
editor.setText("Delete Me");
}
if (isSelected) {
editor.setForeground(table.getSelectionForeground());
editor.setBackground(table.getSelectionBackground());
} else {
editor.setForeground(table.getForeground());
editor.setBackground(UIManager.getColor("Button.background"));
}
return editor;
}
}
}
Take a closer look at How to Use Tables for more details
This is just a personal preference, but I prefer to use a toolbar or menu item and key bindings to provide this support. Buttons in a table just seem so...dated - IMHO
Imagine I'm building an IRC client with Java and I'd like rich text in the chat view to show IRC colors and colored nicks. I'd like to build this with a JTable. I can do that, but the text is then not selectable. Making the table editable doesn't make sense.
I've also investigated:
TextArea - no rich text formatting
JEditPane - can't append, only replace which is bad performance wise
JList - can't select text
So I got a table working I just need the text to be selectable without making it editable. I'd also would only like the text contents, and none of the HTML to be copied into the clipboard upon copying the text selection.
I have tried various iterations of setRowSelectionAllowed(), setColumnSelectionEnabled() and setCellSelectionEnabled() and setSelectionMode the table model returns false for isCellEditable(). Nothing has made the text selectable.
EDIT: as per answer 1 I was wrong about text editor panes so I'm trying those solutions.
I don't know why you don't want to use a JTextPane or JEditorPane. You insert text by its document. Examples here --> How to use Editor Panes and Text Panes.
But for your purpose you can for example do something like this. I override changeSelection to selectAll text when is clicking, the cells are editable but its cellEditors are not editable.
public class JTableTest {
private final DefaultCellEditor cellEditor;
private final JTextField textfield;
private JPanel panel;
private MyTableModel tableModel = new MyTableModel();
private JTable table = new JTable() {
#Override
public TableCellEditor getCellEditor(int row, int column) {
return JTableTest.this.cellEditor;
}
#Override
public void changeSelection(
final int row, final int column, final boolean toggle, final boolean extend) {
super.changeSelection(row, column, toggle, extend);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if ((getCellEditor(row, column) != null && !editCellAt(row, column))) {
JTextField textfield=(JTextField)JTableTest.this.cellEditor.getComponent();
textfield.selectAll();
}
}
});
}
};
public JTableTest() {
JScrollPane scroll = new JScrollPane(table);
table.setModel(tableModel);
panel = new JPanel(new BorderLayout());
panel.add(scroll, BorderLayout.CENTER);
textfield = new JTextField();
textfield.setEditable(Boolean.FALSE);
textfield.setBorder(null);
cellEditor = new DefaultCellEditor(textfield);
tableModel.insertValue(new ItemRow("nonEditable", "Editable"));
}
private class ItemRow {
private String column1;
private String column2;
public ItemRow(String column1, String column2) {
this.column1 = column1;
this.column2 = column2;
}
public String getColumn1() {
return column1;
}
public void setColumn1(String column1) {
this.column1 = column1;
}
public String getColumn2() {
return column2;
}
public void setColumn2(String column2) {
this.column2 = column2;
}
}
private class MyTableModel extends AbstractTableModel {
public static final int COLUMN1_INDEX = 0;
public static final int COLUMN2_INDEX = 1;
private final List<ItemRow> data = new ArrayList<>();
private final String[] columnsNames = {
"Column1",
"Column2",};
private final Class<?>[] columnsTypes = {
String.class,
String.class
};
public MyTableModel() {
super();
}
#Override
public Object getValueAt(int inRow, int inCol) {
ItemRow row = data.get(inRow);
Object outReturn = null;
switch (inCol) {
case COLUMN1_INDEX:
outReturn = row.getColumn1();
break;
case COLUMN2_INDEX:
outReturn = row.getColumn2();
break;
default:
throw new RuntimeException("invalid column");
}
return outReturn;
}
#Override
public void setValueAt(Object inValue, int inRow, int inCol) {
System.out.println("Gets called ");
if (inRow < 0 || inCol < 0 || inRow >= data.size()) {
return;
}
ItemRow row = data.get(inRow);
switch (inCol) {
case COLUMN1_INDEX:
row.setColumn1(inValue.toString());
break;
case COLUMN2_INDEX:
row.setColumn2(inValue.toString());
break;
}
fireTableCellUpdated(inRow, inCol);
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public int getColumnCount() {
return columnsTypes.length;
}
#Override
public String getColumnName(int inCol) {
return this.columnsNames[inCol];
}
#Override
public Class<?> getColumnClass(int columnIndex) {
return this.columnsTypes[columnIndex];
}
/**
*
* #param row
*/
public void insertValue(ItemRow row) {
data.add(row);
fireTableRowsInserted(data.size() - 1, data.size() - 1);
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
}
private static void createAndShowGUI(final Container container, final String title) {
//Create and set up the window.
JFrame frame = new JFrame(title);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(Boolean.TRUE);
frame.add(container);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI(new JTableTest().panel, "Test");
}
});
}
}
I accomplished this by enabling the editing and then making the component responsible for the edition ignore any changes. For this I created a TableCellEditor and intercepted the key types to the JTextField, the component used for editing.
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
public class TableCellSelectionTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
new TableCellSelectionTest().initUI();
}
});
}
public void initUI()
{
JFrame frame = new JFrame();
int N = 5;
int M = 3;
Object[][] data = new Object[N][M];
for (int i = 0; i < N; ++i)
{
for (int j = 0; j < M; ++j)
{
data[i][j] = "This is the cell (" + i + ", " + j +")";
}
}
String[] columnNames = { "Column 1", "Column 2", "Column 3" };
DefaultTableModel model = new DefaultTableModel(data, columnNames);
final MyTableCellEditor editor = new MyTableCellEditor();
JTable table = new JTable(model) {
#Override
public TableCellEditor getCellEditor(int row, int column)
{
return editor;
}
};
frame.add(new JScrollPane(table), BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
class MyTableCellEditor extends AbstractCellEditor implements
TableCellEditor
{
Object _value;
#Override
public Object getCellEditorValue()
{
return _value;
}
#Override
public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column)
{
_value = value;
JTextField textField = new JTextField(_value.toString());
textField.addKeyListener(new KeyAdapter()
{
public void keyTyped(KeyEvent e) {
e.consume(); //ignores the key
}
#Override
public void keyPressed(KeyEvent e)
{
e.consume();
}});
textField.setEditable(false); //this is functionally irrelevent, makes slight visual changes
return textField;
}
}
}
I tried both the answers here... but one problem at least is that you can tell when you've entered the "editing" mode.
This might be of interest... uses a combination of Editor magic and cheeky rendering to make it look like no editing is going on: editor's click-count-to-start is set to 1, and the component (JTextPane) delivered by the editor's method does setEditable( false ).
If this tickles your fancy, you might be interested at looking at my implementation of a JTable which adjusts (perfectly, harnessing the JTextPane's powerful wrapping power) the row height to the text, for individual rows, including when you change the columns: How to wrap lines in a jtable cell?
public class SelectableNonEditableTableTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame main_frame = new JFrame();
main_frame.setPreferredSize(new Dimension(1200, 300));
main_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ArrayList<String> nonsense = new ArrayList<String>(
Arrays.asList(
"Lorem ipsum dolor sit amet, sed dolore vivendum ut",
"pri an soleat causae doctus.",
"Alienum abhorreant mea ea",
"cum malorum diceret ei. Pri oratio invidunt consequat ne.",
"Ius tritani detraxit scribentur et",
"has detraxit legendos intellegat at",
"quo oporteat constituam ex"));
JTable example_table = new JTable(10, 4);
example_table.setRowHeight( example_table.getRowHeight() * 2 );
DefaultCellEditor cell_editor = new SelectableNonEditableCellEditor(
new JTextField());
cell_editor.setClickCountToStart(1);
example_table.setDefaultEditor(Object.class, cell_editor);
TableCellRenderer renderer = new SelectableNonEditableTableRenderer();
example_table.setDefaultRenderer(Object.class, renderer);
for (int i = 0; i < 10; i++) {
example_table.setValueAt(nonsense.get(i % nonsense.size()),
i, i % 4);
}
main_frame.getContentPane().add(new JScrollPane(example_table));
main_frame.pack();
main_frame.setVisible(true);
}
});
}
}
class SelectableNonEditableCellEditor extends DefaultCellEditor {
public SelectableNonEditableCellEditor(JTextField textField) {
super(textField);
}
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int col) {
Component comp = super.getTableCellEditorComponent(table, value,
isSelected, row, col);
if (value instanceof java.lang.String) {
DefaultStyledDocument sty_doc = new DefaultStyledDocument();
try {
sty_doc.insertString(0, (String) value, null);
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JTextPane jtp_comp = new JTextPane(sty_doc);
jtp_comp.setEditable(false);
return jtp_comp;
}
return comp;
}
}
class SelectableNonEditableTableRenderer extends JTextPane implements
TableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof DefaultStyledDocument) {
setDocument((DefaultStyledDocument) value);
} else {
setText((String) value);
}
return this;
}
}
Maybe you can implement your own TableCellRenderer that extends JTextField in your table.
I have two JTables, and they are set up so that you can drag and drop the rows within each JTable. The problem is that it lets me drag a row from one JTable to the other JTable, and I am trying to figure out how to stop that. I only want the user to be able to drag and drop a row within that same JTable.
In other words, when I drag a row outside the current table and hover the mouse over the empty panel space, the mouse cursor displays a circle with a diagonal line through it, which is what I want. However, when I drag the mouse over the other table, it displays the small rectangular "drop" icon, which is what I am trying to block. When the user tries to drag this row on top of other table, I would like the small circle with diagonal line to appear.
This is a working example that demonstrates the problem:
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DragSource;
import java.util.ArrayList;
import java.util.List;
import javax.activation.ActivationDataFlavor;
import javax.activation.DataHandler;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.TransferHandler;
import javax.swing.table.AbstractTableModel;
public class JTableDnD extends JFrame
{
private JTable tableA;
private JTable tableB;
public JTableDnD()
{
// *** Create First Table ***
List<Object[]> dataA = new ArrayList<Object[]>();
dataA.add(new Object[] {"A1", "A1"});
dataA.add(new Object[] {"A2", "A2"});
dataA.add(new Object[] {"A3", "A3"});
List<String> columnsA = new ArrayList<String>();
columnsA.add("Column 1");
columnsA.add("Column 2");
tableA = new JTable(new TableModel(columnsA, dataA));
tableA.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
tableA.setDragEnabled(true);
tableA.setDropMode(DropMode.INSERT_ROWS);
tableA.setFillsViewportHeight(true);
tableA.setTransferHandler(new TableTransferHandler(tableA));
JScrollPane scrollPaneA = new JScrollPane(tableA);
// *** Create Second Table ***
List<Object[]> dataB = new ArrayList<Object[]>();
dataB.add(new Object[] {"B1", "B1"});
dataB.add(new Object[] {"B2", "B2"});
dataB.add(new Object[] {"B3", "B3"});
List<String> columnsB = new ArrayList<String>();
columnsB.add("Column 1");
columnsB.add("Column 2");
tableB = new JTable(new TableModel(columnsB, dataB));
tableB.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
tableB.setDragEnabled(true);
tableB.setDropMode(DropMode.INSERT_ROWS);
tableB.setFillsViewportHeight(true);
tableB.setTransferHandler(new TableTransferHandler(tableB));
JScrollPane scrollPaneB = new JScrollPane(tableB);
// *** Add ScrollPanes to Panel ***
this.getContentPane().setLayout(new FlowLayout());
add(scrollPaneA);
JPanel emptyPanel = new JPanel();
emptyPanel.setPreferredSize(new Dimension(100, 200));
add(emptyPanel);
add(scrollPaneB);
} // end JTableDnD constructor
private static void createAndShowGUI()
{
JFrame frame = new JTableDnD();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
interface Reorderable
{
public void reorder(int from, int to);
}
class TableModel extends AbstractTableModel implements Reorderable
{
private List<String> columnNames;
private List<Object[]> data;
public TableModel(List<String> columnNames, List<Object[]> data)
{
super();
this.columnNames = columnNames;
this.data = data;
}
#Override
public void reorder(int from, int to)
{
if (from < to)
{
to--;
}
Object[] row = data.remove(from);
data.add(to, row);
fireTableDataChanged();
}
#Override
public int getRowCount()
{
return data.size();
}
#Override
public int getColumnCount()
{
return columnNames.size();
}
#Override
public String getColumnName(int column)
{
return columnNames.get(column);
}
#Override
public Object getValueAt(int rowIndex, int columnIndex)
{
return data.get(rowIndex)[columnIndex];
}
} // end TableModel
class TableTransferHandler extends TransferHandler
{
private final DataFlavor localObjectFlavor = new ActivationDataFlavor(Integer.class, DataFlavor.javaJVMLocalObjectMimeType, "Integer Row Index");
private JTable table = null;
public TableTransferHandler(JTable table)
{
this.table = table;
}
#Override
protected Transferable createTransferable(JComponent component)
{
return new DataHandler(new Integer(table.getSelectedRow()), localObjectFlavor.getMimeType());
}
#Override
public boolean canImport(TransferHandler.TransferSupport support)
{
boolean b = support.getComponent() == table &&
support.isDrop() &&
support.isDataFlavorSupported(localObjectFlavor);
table.setCursor(b ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop);
return b;
}
#Override
public int getSourceActions(JComponent component)
{
return TransferHandler.COPY_OR_MOVE;
}
#Override
public boolean importData(TransferHandler.TransferSupport support)
{
JTable target = (JTable) support.getComponent();
JTable.DropLocation dropLocation = (JTable.DropLocation) support.getDropLocation();
int index = dropLocation.getRow();
int max = table.getModel().getRowCount();
if (index < 0 || index > max)
{
index = max;
}
target.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
try
{
Integer rowFrom = (Integer) support.getTransferable().getTransferData(localObjectFlavor);
if (rowFrom != -1 && rowFrom != index)
{
((Reorderable) table.getModel()).reorder(rowFrom, index);
if (index > rowFrom)
{
index--;
}
target.getSelectionModel().addSelectionInterval(index, index);
return true;
}
}
catch (Exception e)
{
e.printStackTrace();
}
return false;
}
#Override
protected void exportDone(JComponent component, Transferable transferable, int action)
{
if (action == TransferHandler.MOVE)
{
table.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
} // end TableTransferHandler
I believe I need to add some extra logic to the canImport() method to make sure that the row that is being dropped is from that same table, but I can't seem to figure it out. I've tried inspecting the data inside the TransferSupport object that gets passed into canImport(), but it does not seem to have any function that returns the exact JTable object source.
In the simplified Swing DnD, there is no support for getting at the source of the transferable. The component returned by TransferSupport` is the target of the drop, that is the component that somehow should handle the associated transferable. So if you have a per-component TransferHandler configured with a particular table, the support's component will always be that same table instance and your check will be true trivially.
If you want to enable/disable dropping based on the sender, you'll have to provide it on a per-drag basis: a drag starts in exportAsDrag and ends in exportDone, so you can set/null the sender in those.
#Override
protected void exportDone(JComponent component,
Transferable transferable, int action) {
table = null;
}
#Override
public void exportAsDrag(JComponent comp, InputEvent e, int action) {
table = (JTable) comp;
super.exportAsDrag(comp, e, action);
}
Now you can re-use the same instance of the handler across several instances of table:
TableTransferHandler handler = new TableTransferHandler();
tableA.setTransferHandler(handler);
tableB.setTransferHandler(handler);
As an aside: I wouldn't fiddle with the cursors, they are supporsed to be under complete control of the dnd subsystem.
I have following code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import javax.swing.table.DefaultTableModel;
public class NewClass1 extends JFrame {
private JTable table;
private JScrollPane scrollPane;
private DefaultTableModel defaultTableModel;
public NewClass1() {
setLocationByPlatform(true);
setLayout(new BorderLayout());
setPreferredSize(new Dimension(600, 400));
setTitle("Table Issues");
setDefaultCloseOperation(EXIT_ON_CLOSE);
createTableModel();
table = new JTable(defaultTableModel);
scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane, BorderLayout.CENTER);
pack();
}
private void createTableModel() {
Vector cols = new Vector();
cols.add("A");
Vector rows = new Vector();
for (int i = 0; i < 50; i++) {
Vector row = new Vector();
row.add((i + 1) + "");
rows.add(row);
}
defaultTableModel = new DefaultTableModel(rows, cols) {
Class[] types = new Class[]{
String.class
};
#Override
public Class getColumnClass(int columnIndex) {
return types[columnIndex];
}
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(new NimbusLookAndFeel());
} catch (Exception e) {
}
final NewClass1 nc = new NewClass1();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
nc.setVisible(true);
}
});
while (true) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
int row = (int) (Math.random() * 50);
int move = (int) (Math.random() * 50);
nc.defaultTableModel.moveRow(row, row, move);
}
});
try{
Thread.sleep(1000);
}catch(Exception e){
}
}
}
}
Please run the above code and select row.
My problem is with row movement, row selection is not moving. It is staying at fixed position. Suppose I selected row with column value 25, selected row must be of column value 25 after row movements.
Please help me on this.
My real problem is, user will select row and clicks menu to perform action, meanwhile other threads may have moved rows, and performed action will be on other row than actual one.
The easiest way is to remember the selected row somewhere outside of the ListSelectionModel and adjust the selection whenever the TableModel changes. For example you could do this:
public class NewClass1 extends JFrame {
private JTable table;
private DefaultTableModel defaultTableModel;
private JScrollPane scrollPane;
private class SelectionHelper implements ListSelectionListener, TableModelListener {
private Object selectedRow;
#Override
public void valueChanged(ListSelectionEvent event) {
if (!event.getValueIsAdjusting()) return;
int selectedIndex = table.getSelectedRow();
if (selectedIndex >= 0) {
selectedRow = defaultTableModel.getDataVector().get(selectedIndex);
} else {
selectedRow = null;
}
}
#Override
public void tableChanged(TableModelEvent event) {
if (selectedRow == null) return;
int selectedIndex = defaultTableModel.getDataVector().indexOf(selectedRow);
table.getSelectionModel().setSelectionInterval(selectedIndex, selectedIndex);
}
}
public NewClass1() {
// ...
createTableModel();
table = new JTable(defaultTableModel);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
SelectionHelper helper = new SelectionHelper();
table.getModel().addTableModelListener(helper);
table.getSelectionModel().addListSelectionListener(helper);
// ...
}
// ...
}
Note however, that you should adjust this code for production use, for example in regards to thread safety or portability (using the table and defaultTableModel attributes in the inner class is bad style).
I have JTable and it has a JCheckBox and a JComoboBox in two different columns. When I select a JCheckBox that corresponds to that row, the JComboBox should be disable. Kindly help me.
Simply disable editing of the cell based on your model. In your TableModel, override/implement the isCellEditable() method to return the "value" of the checkbox.
Although the following example is not based on JComboBox, it illustrates how to disable edition of a cell based on the value of a checkbox at the beginning of the row:
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
public class TestTable {
public JFrame f;
private JTable table;
public class TestTableModel extends DefaultTableModel {
public TestTableModel() {
super(new String[] { "Editable", "DATA" }, 3);
for (int i = 0; i < 3; i++) {
setValueAt(Boolean.TRUE, i, 0);
setValueAt(Double.valueOf(i), i, 1);
}
}
#Override
public boolean isCellEditable(int row, int column) {
if (column == 1) {
return (Boolean) getValueAt(row, 0);
}
return super.isCellEditable(row, column);
}
#Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 0) {
return Boolean.class;
} else if (columnIndex == 1) {
return Double.class;
}
return super.getColumnClass(columnIndex);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestTable().initUI();
}
});
}
protected void initUI() {
table = new JTable(new TestTableModel());
f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.add(new JScrollPane(table));
f.setVisible(true);
}
}