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.
Related
I want to call a function when the user pastes text in my JTextArea. Is there any event generated when the text is pasted to the JTextArea and which listener can I use to trigger my function on this event?
One possible solution (and I hope some one has a better one) would be to replace the key binding Action responsible for actually performing the paste operation.
Now, before you do this, the default paste operation is not trivial, instead, I would replace the default paste Action with a proxy, which could call the original, but would allow you to intercept the operation, but not have to re-implement the functionality yourself, for example...
public class ProxyAction extends AbstractAction {
private Action action;
public ProxyAction(Action action) {
this.action = action;
}
#Override
public void actionPerformed(ActionEvent e) {
action.actionPerformed(e);
System.out.println("Paste Occured...");
}
}
Then you would simply need to look up the default Action and replace it...
JTextArea ta = new JTextArea(10, 10);
Action action = ta.getActionMap().get("paste-from-clipboard");
ta.getActionMap().put("paste-from-clipboard", new ProxyAction(action));
The problem here is, this won't tell you if the operation failed or succeeded or what was actually pasted. For that, you could use a DocumentListener, registered before you call the default Action which could record the changes to the document. Obviously, you'd want to deregister this after the default action ;)...
Now, equally, you could just override the paste method of the JTextArea, which equates to about the same thing, but, the first option would be more portable...
As an idea...
Take a look at How to Use Actions and How to Use Key Bindings for more details
you can have something like below, whenever you paste something in the textarea, then 'Pasted!' is printed out on your console. It prints only on paste !
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
public class TextAreaDemo extends JFrame {
JTextArea _resultArea = new JTextArea(6, 20);
public TextAreaDemo() {
_resultArea.setText("");
JScrollPane scrollingArea = new JScrollPane(_resultArea);
JPanel content = new JPanel();
content.setLayout(new BorderLayout());
content.add(scrollingArea, BorderLayout.CENTER);
this.setContentPane(content);
this.setTitle("TextAreaDemo B");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
_resultArea.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if ((e.getKeyCode() == KeyEvent.VK_V) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
System.out.println("Pasted!");
}
}
#Override
public void keyReleased(KeyEvent e) {
}
});
}
public static void main(String[] args) {
JFrame win = new TextAreaDemo();
win.setVisible(true);
}
}
You can also check out Wrapping Actions which is basically the same suggestion as MadProgrammer except that the WrapperAction will delegate all the methods of the Action to the original Action. This will allow you to pick up the text and Icons associated with the original Action in case you ever want to add your custom Action to a JMenuItem or JButton.
I'd like to add to a database, and my editable comboBoxModel when I enter a new name into the comboBox. I have the method for adding to the database down fine, I'm just trying to get it to somehow listen to an entry being added to the ComboBox.
What's the Best way to do this?
I've read the Java tutorial on Editable ComboBoxes, and noted where it said:
An editable combo box fires an action event when the user chooses an item from the menu and when the user types Enter. Note that the menu remains unchanged when the user enters a value into the combo box. If you want, you can easily write an action listener that adds a new item to the combo box's menu each time the user types in a unique value.
So I thought to myself, ok lets try this, and looked up some examples. Here is my attempt, essentially copy pasted out of the example I found, with my variable names:
playerNameComboBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("comboBoxEdited")) {
System.out.println("Adding new player!");
IController.Util.getInstance().addNewPlayer();
playerNameComboBox.insertItemAt(playerNameComboBox.getSelectedItem(), 0);
}
}
});
When I type in a new name, and press enter, it does nothing. No new database entry and no addtional option on the ComboBox. I haven't attached an action command to the ComboBox as I thought the example above assumed it would have that as default, and so did I.
But how do I get it to shout out that action command when I press enter, with the focus on the comboBox? I would have thought that comboBoxes would have had some sort of default behaviour to shout that out? Do I need to use an if(playerNameComboBox.hasFocus()) statement? Should I implement some kind of keylistener when my comboBox hasFocus()?
I'm very new at Java, so I'm unsure as to how this sort of thing should be done; any help is very much appreciated.
As requested, here is my short example in which names may be added to a JComboBox.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Test extends JFrame {
private JComboBox box;
public static void main(String[] args) {
new Test();
}
public Test()
{
super();
setSize(200, 100);
setDefaultCloseOperation(EXIT_ON_CLOSE);
box = new JComboBox();
box.setEditable(true);
getContentPane().add(box);
box.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("comboBoxEdited")) {
System.out.println("Adding new player!");
box.insertItemAt(box.getSelectedItem(), 0);
}
}
});
setVisible(true);
}
}
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
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.
Made a custom ListCellRenderer:
import java.awt.Component;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
/**
*
* #author Spencer
*/
public class TaskRenderer implements ListCellRenderer {
private Task task;
private JPanel panel = new JPanel();
private JCheckBox checkbox = new JCheckBox();
private JLabel label = new JLabel();
public TaskRenderer() {
panel.add(checkbox);
panel.add(label);
}
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
task = (Task) value;
label.setText(task.getName());
return panel;
}
}
Have a JList with each cell in it rendered using the above class, but the checkboxes in the panels for each cell cannot be clicked. Thought it had to do with it not getting focus. Any ideas?
Thanks,
Spencer
Your custom renderer is simply governing the appearance of the JList contents, not adding any functionality such as the ability to modify the components (check box) - Imagine it simply as a rubber stamp used to display each list cell in turn.
I'd recommend solving the problem by:
Use a single-column JTable instead of a JList.
Define a bespoke TableModel implementation by sub-classing AbstractTableModel and override getColumnClass(int) to return Boolean.class for column 0. Note that the default renderer will now render this as a JCheckBox. However, it will not be a labelled JCheckBox as you require.
Add a bespoke TableCellRenderer for Booleans; e.g. myTable.setCellRenderer(Boolean.class, new MyLabelledCheckBoxRenderer());
Add an editor for Booleans, using something similar to: myTable.setCellEditor(Boolean.class, new DefaultEditor(new JCheckBox("Is Enabled)));
JIDE Common Layer has a GPL'ed CheckBoxList. Basically it uses a JPanel as cell renderer with a JCheckBox in front of another renderer (which you can set yourself), and handles mouse/key events.
If you really want to stick to your JCheckBox renderer, you can listen to mouse/key events and process them appropriately. Keep in mind that, as Adamski pointed out, cell renderer is a rubber stamp (Swing 101) so you have to always set the check box selected state in getListCellRendererComponent(), otherwise all your checkboxes will have the save state.