How to make Jtable forbid duplicates - java

I have a Jtable allow editing and inserting.
When editing a column, lets say "ID", I want to have a check, if the inserted record or the record after editing have duplicat "ID" column value with the other records. it is not allowed.
actually, to be specific, if someone is editing the ID column, when he hit the "Enter" or move the focus to another cell, perform a check, if the ID is duplicate, then disallow the edition.
how can I do this?

if someone is editing the ID column, when he hit the "Enter" or move the focus to another cell, perform a check, if the ID is duplicate, then disallow the edition.
Create a custom editor to validate the id before it is saved to the model. Here is a simple example to get your started:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
public class TableEdit extends JFrame
{
TableEdit()
{
JTable table = new JTable(5,5);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollpane = new JScrollPane(table);
getContentPane().add(scrollpane);
// Use a custom editor
TableCellEditor fce = new FiveCharacterEditor();
table.setDefaultEditor(Object.class, fce);
}
class FiveCharacterEditor extends DefaultCellEditor
{
FiveCharacterEditor()
{
super( new JTextField() );
}
public boolean stopCellEditing()
{
try
{
String editingValue = (String)getCellEditorValue();
if(editingValue.length() != 5)
{
JTextField textField = (JTextField)getComponent();
textField.setBorder(new LineBorder(Color.red));
textField.selectAll();
textField.requestFocusInWindow();
JOptionPane.showMessageDialog(
null,
"Please enter string with 5 letters.",
"Alert!",JOptionPane.ERROR_MESSAGE);
return false;
}
}
catch(ClassCastException exception)
{
return false;
}
return super.stopCellEditing();
}
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
Component c = super.getTableCellEditorComponent(
table, value, isSelected, row, column);
((JComponent)c).setBorder(new LineBorder(Color.black));
return c;
}
}
public static void main(String [] args)
{
JFrame frame = new TableEdit();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}

Extend DefaultTableModel and override the add and update methods checking for duplication and use this class for your JTable.

Extend AbstractTableModel and arrange for it to contain a Set, which precludes duplicate elements. As suggested by #camickr, you'll want a custom cell editor to communicate the results failed additions.

Related

how to detect enter pressed while jtable cell is being edited?

I have a keylistener on jtable so that when someone presses enter some calculations happen. However, this only happens if the person is not editing. I would like to apply this action when a person finishes editing a cell and is pressing enter to finish and close the editing.
I cannot figure this out, anyone ever did this or know how to?
Basically, now for the action to be done, people must press enter twice, one to end the editing and another for the action that I want to happen, I would like to make it needed only once, while editing.
Thank you
You could customize your own editor. Using DefaultCellEditor Instead using KeyListener you should use KeyBindings.
See this example.
JTable table = new JTable(myModel);
JTextField cell = new JTextField();
final TableCellEditor cellEditor = new DefaultCellEditor(cell);
table.getColumnModel().getColumn(column).setCellEditor(cellEditor);
InputMap iMap = cell.getInputMap(JComponent.WHEN_FOCUSED);
iMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), KeyEvent.getKeyText(KeyEvent.VK_ENTER));
ActionMap aMap = cell.getActionMap();
aMap.put(KeyEvent.getKeyText(KeyEvent.VK_ENTER), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
if(callSomeOperationsIsOk()){
cellEditor.stopCellEditing();
}else{
cellEditor.cancelCellEditing();
}
}
});
}
Read more in tutorials How to use Tables, and perhaps you have the same trouble that i have see my previous question
I have a keylistener on jtable so that when someone presses enter some
calculations happen. However, this only happens if the person is not
editing. I would like to apply this action when a person finishes
editing a cell and is pressing enter to finish and close the editing.
TableCellEditor hasn't something with KeyListener added to JTable
Basically, now for the action to be done, people must press enter
twice, one to end the editing and another for the action that I want
to happen, I would like to make it needed only once, while editing.
JComponents (used as TableCellEditor) by default to react to ENTER key pressed
don't to put JComponent to the TableModel, there should be stored only value painted by TableCellRenderer and initial value for TableCellEditor
TableCellEditor is temporarily JComponent, you have to add KeyBindings to invoke stopCellEditing in the case that JComponents used as TableCellEditor doesn't to react to ENTER key pressed
this issue isn't about standards, for better help sooner post an SSCCE, short, runnable, compilable, with hardcoded valur for JTable/XxxTableModel
before anything to read Oracle tutorial How to use Tables, especially parts
Creating a Table Model
Concepts: Editors and Renderers
Using Custom Renderers
You can override JTable.editingStopped, which is invoked when editing is finished and apply your actions in that method.
EDIT:
JTable.editingStopped was not designed for application extension. To avoid complications, in particular platform dependent ones, a better approach is to override model's setValueAt or register a TableModelListener. Here is an example:
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
public class DemoTable3 {
private static void createAndShowUI() {
JFrame frame = new JFrame("DemoTable");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Object[][] rows = { { "Column 1", "Column 2" },
{ "Column 1", "Column 2" } };
Object[] columns = { "Column 1", "Column 2" };
DefaultTableModel model = new DefaultTableModel(rows, columns);
model.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
System.out.println("apply additional action");
}
});
JTable table = new JTable(model);
frame.add(new JScrollPane(table));
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Another alternative is to add CellEditorListener to catch editingStopped events. For example:
import javax.swing.*;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.DefaultTableModel;
public class DemoTable2 {
private static void createAndShowUI() {
JFrame frame = new JFrame("DemoTable");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Object[][] rows = { { "Column 1", "Column 2" },
{ "Column 1", "Column 2" } };
Object[] columns = { "Column 1", "Column 2" };
final JTable table = new JTable(new DefaultTableModel(rows, columns));
table.getDefaultEditor(String.class).addCellEditorListener(
new CellEditorListener() {
public void editingCanceled(ChangeEvent e) {
System.out.println("editingCanceled");
}
public void editingStopped(ChangeEvent e) {
System.out.println("editingStopped: apply additional action");
}
});
frame.add(new JScrollPane(table));
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Also look at a Table Cell Listener by #camickr which offers custom processing of the edits.

JTable all column aligned right

Is there a way to align all the column in jtable at the same time?
using this:
DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer();
rightRenderer.setHorizontalAlignment( JLabel.RIGHT );
JTAB_TABLE.getColumnModel().getColumn(0).setCellRenderer( rightRenderer );
will let me align only one column but i need to align all.
Normally, a table contains different kinds of data, (Date, Number, Boolean, String) and it doesn't make sense to force all types of data to be right aligned.
If however you have a table with all the same type of data and you want to force the renderering of all columns to be the same, then you should probably use the same renderer. Assuming you are using the default renderer you can use:
DefaultTableCellRenderer renderer = (DefaultTableCellRenderer)table.getDefaultRenderer(Object.class);
renderer.setHorizontalAlignment( JLabel.RIGHT );
You can do so by overriding prepareRenderer(...) in JTable. This assumes that any custom renderers are JLabels (they're JLabels by default). You'd have to guard against it otherwise.
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableDemo implements Runnable
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new TableDemo());
}
public void run()
{
JTable table = new JTable(5, 5)
{
#Override
public Component prepareRenderer(TableCellRenderer renderer,
int row, int col)
{
Component comp = super.prepareRenderer(renderer, row, col);
((JLabel) comp).setHorizontalAlignment(JLabel.RIGHT);
return comp;
}
};
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
JFrame frame = new JFrame();
frame.getContentPane().add(scrollPane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}

How to clear table selection when clicking on right of table

I have a JTable which is held in a scrollpane, which in turn sits in a panel, which is embedded in a JFrame. If the JFrame is expanded, then there is empty space, both below, as well as to the right of the JTable.
I want to clear the selection on the table if the user clicks outside the table, either below the table, or to the right of the table.
In order to clear the selection when the user clicks BELOW the table, I configured the table to fill the height of the scrollpane viewport, and added a MouseListener to the table, so that when the user clicks below the table, "rowAtPoint" returns -1, and then I clear the selection. However, this doesn't work for the RHS of the table. The table doesn't even receive these Mouse Events. How should I detect a click on the right of the JTable and clear the selection on the table? See code below. Please note that I haven't bothered to make the code pretty and do things the "right way". My focus was just on creating a SSCCE that illustrated the issue. :)
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class TableTest {
public static void main(String args[]) {
JFrame f = new JFrame();
JPanel p = new JPanel(new BorderLayout());
JScrollPane sp = new JScrollPane();
final JTable t = new JTable(5,5);
t.setFillsViewportHeight(true);
t.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for(int i = 0; i < 5; i++) {
t.getColumnModel().getColumn(i).setWidth(50);
t.getColumnModel().getColumn(i).setMaxWidth(75);
}
t.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
int row = t.rowAtPoint(e.getPoint());
if(row == -1) {
t.clearSelection();
}
}
});
sp.getViewport().add(t);
p.add(sp, BorderLayout.CENTER);
f.add(p);
f.pack();
f.setVisible(true);
}
}
EDIT : I understand that I could probably just add a mouselistener to the scrollpane or panel as well, so that the table selection is cleared when I click on them. I'm just wondering if there's a better / cleaner solution out there, to make sure that the JTable selection is cleared when I click outside its bounds.
Investigate using focus listener (table.addFocusListener( ... );) to find out when the table/cells no longer have the focus, and when that is the case call: table.getSelectionModel().clearSelection();
You could try adding a global mouse listener like below:
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener(){
#Override
public void eventDispatched(AWTEvent event) {
if(event.getID() == MouseEvent.MOUSE_CLICKED) {
MouseEvent mevent = (MouseEvent) event;
int row = t.rowAtPoint(mevent.getPoint());
if(row == -1) {
t.clearSelection();
}
}
}
}, AWTEvent.MOUSE_EVENT_MASK);
You can probably use the FocusListener.
When the table loses Focus, you can clear the selection.
http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/event/FocusListener.html

Problem getting focus when use JPanel as JTable cell editor

I have a cell editor that contains a little button that can be double clicked on to bring up an edit dialog, and then a textfield that can be used to edit the value inline (the popup is required to allow editing of additional values, only the first is shown in the JTable).
When user clicks on field everything is okay, but if they tab into the field they textfield doesn't receive focus and they cannot edit the field unless they click on it with the mouse.
I tried fiddling with the various focus methods of jpanel but it made no difference, anybody know what Im doing wrong ?
package com.jthink.jaikoz.celleditor;
import com.jthink.jaikoz.celldata.Cell;
import com.jthink.jaikoz.guielement.Focus;
import com.jthink.jaikoz.table.CellLocation;
import com.jthink.jaikoz.table.DatasheetToggleButton;
import com.jthink.jaikoz.table.datasheet.Datasheet;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
public class SimpleMultiRowCellEditor
extends DefaultCellEditor implements ActionListener
{
final JPanel panel;
private final DatasheetToggleButton rowCount;
Cell value;
public SimpleMultiRowCellEditor(final JTextField text)
{
super(text);
this.setClickCountToStart(1);
rowCount = new DatasheetToggleButton();
rowCount.setVisible(true);
rowCount.addActionListener(this);
panel = new JPanel();
panel.setOpaque(false);
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.add(rowCount);
panel.add(editorComponent);
/*panel.setFocusable(true);
panel.setFocusCycleRoot(true);
ArrayList focusOrder = new ArrayList();
focusOrder.add(editorComponent);
focusOrder.add(rowCount);
focusOrder.add(panel);
panel.setFocusTraversalPolicy(new Focus(focusOrder));
*/
}
public Component getTableCellEditorComponent(
final JTable table, final Object val, final boolean isSelected,
final int row, final int column)
{
value = (Cell) ((Cell) val).clone();
rowCount.setText(String.valueOf(value.getValues().size()));
delegate.setValue(value.getValue());
return panel;
}
public Object getCellEditorValue()
{
final String s = (String) delegate.getCellEditorValue();
value.setValue(s);
return value;
}
public void actionPerformed(final ActionEvent e)
{
this.stopCellEditing();
final CellLocation cl = Datasheet.getActiveEditSheet()
.getTable().getSelectedCellLocations().get(0);
UpdateMultiRowCellDialog.getInstanceOf().display(value,cl);
}
}
Tried adding focuslistener to panel, didnt seem to make any difference
class PanelFocusListener implements FocusListener
{
public void focusGained(FocusEvent e)
{
System.out.println("Gained Focus");
editorComponent.requestFocusInWindow();
}
public void focusLost(FocusEvent e)
{
System.out.println("Lost Focus");
}
}
So after tabbing into field, I type a key and it sorts of look likes focus is gained but you cannot enter anything into the field whereas if I type RETURN then I can start editing the field, what does pressing RETURN do that allows it to work ?
what does pressing RETURN do that allows it to work?
As shown in the handy Key Bindings application, the default ENTER key binding in most L&Fs is notify-field-accept. It's not clear why your ActionListener begins with stopCellEditing(). I would have expected it to invoke fireEditingStopped() after updating the data model, as suggested in this example.
Sadly, I'm unfamiliar with Jaikoz. You might look at Concepts: Editors and Renderers and the subsequent sections for additional guidance.
Addendum: As noted in your comment, a JTextField in a DefaultCellEditor allows typing in the selected field by default. It's not clear from your example how that default is being nullified. Absent an sscce that demonstrates the problem, you might compare your code with this related example that exhibits the default behavior using a subclass of JTextField.

Adding Buttons inside cell of JTable along with data?

Is it possible to add buttons inside the JTable cell along with data?
What I am trying to do is to create a table with columns which display data(number) from the database, and two buttons to increase/decrease the number inside the same cell.
|ID | Quantity|
|06| 2 [+][-] |
it would be something like above with [+][-] being buttons. So when I press [+], the number will change to 3 and 1 if pressing [-].
Yes, it is possible, although It won't be easy.
You have to write your own custom cell renderer and your own cell editor.
This is a sample I made in 5 minutes:
It is far from perfect, but shows the concept.
Here's the source code:
import java.awt.Component;
import java.awt.Font;
import javax.swing.*;
import javax.swing.table.*;
import java.awt.Dimension;
public class CustomCell {
public static void main( String [] args ) {
Object [] columnNames = new Object[]{ "Id", "Quantity" };
Object [][] data = new Object[][]{ {"06", 1}, {"08", 2} };
JTable table = new JTable( data, columnNames ) {
public TableCellRenderer getCellRenderer( int row, int column ) {
return new PlusMinusCellRenderer();
}
};
table.setRowHeight( 32 );
showFrame( table );
}
private static void showFrame( JTable table ) {
JFrame f = new JFrame("Custom Cell Renderer sample" );
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.add( new JScrollPane( table ) );
f.pack();
f.setVisible( true );
}
}
class PlusMinusCellRenderer extends JPanel implements TableCellRenderer {
public Component getTableCellRendererComponent(
final JTable table, Object value,
boolean isSelected, boolean hasFocus,
int row, int column) {
this.add( new JTextField( value.toString() ) );
this.add( new JButton("+"));
this.add( new JButton("-"));
return this;
}
}
Here's a thread that may be interesting and here.
As discussed in the tutorial you'll need both a renderer to display your value and an editor to detect events from the cell being edited. In this example, the Component is a JCheckBox. Note that this requires a custom DataModel that extends AbstractTableModel to supply the correct Class for a given column. Joonas' suggestion to use JSpinner is a good one that works well.
I think you need to create a custom cell renderer if you want to show anything else than text (or numbers) in the cell. The cell renderer's job is to paint whatever you need to show in the cell.
See Table Renderer documentation.
So in this case you could create a small JPane which contains the text field and the tiny + and - buttons - or a just a JSpinner component, if does what you need. A bit tricky, for sure, but should be possible.

Categories