How to force stop editing a cell in a JTable when the user clicks on any other component than the table itself?
Tried this but it didn't work...
myTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
... or this, which also does not work as expected
Component co = myTable.getEditorComponent();
if (co != null && !(co instanceof JComboBox)) {
co.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusLost(java.awt.event.FocusEvent evt) {
TableCellEditor tce = myTable.getCellEditor();
if (tce != null) {
tce.stopCellEditing(); // should accept partial edit
}
}
});
}
Having a button with an action listener attached to it makes it possible to force stop editing of any cell in the table, however that's not really the solution I am looking for.
It should work by clicking on any component.
SSCCE
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
public class TableExample {
public static void main(String args[]) {
final Object rowData[][] = { { "1", "one", "not empty" }, { "2", "two", "" }, { "3", "three", "" } };
final String columnNames[] = { "#", "Some Column", "Some Other Column" };
final JTable table = new JTable(rowData, columnNames);
JScrollPane scrollPane = new JScrollPane(table);
table.getModel().addTableModelListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
System.out.println("column: "+e.getColumn());
if(1==e.getColumn()){
System.out.println(table.getModel().getValueAt(e.getLastRow(), e.getColumn()));
String value = table.getModel().getValueAt(e.getLastRow(), e.getColumn()).toString();
int rowIndex = e.getLastRow();
if(table.getModel().getValueAt(e.getLastRow(), 2).toString().isEmpty())
table.getModel().setValueAt(value,e.getLastRow(), 2);
}
}
});
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
table.setValueAt("",0,0);
JFrame frame = new JFrame("Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane, BorderLayout.CENTER);
frame.setSize(700, 150);
frame.setVisible(true);
}
}
The table does not take up all the space in the viewport of the scrollpane so you are actually clicking on the viewport. By default a viewport doesn't gain focus when you click on it so there is no focusLost method generated on the table.
Maybe you can just use the following so the table fills the viewport:
table.setFillsViewportHeight(true);
Otherwise, you would need to add a MouseListener to the viewport to handle the mouse click event. You would need to do this for all components on the frame that cannot receive focus, like a JPanel.
Or, maybe another approach is to use an AWTEventListener to listen for all mouse clicks. The draw back to this approach is that you can't distinguish between a click on a focusable component and a non-focusable component so you would be attempting to stop the cell editing every time a click is made. See Global Event Listeners for more information.
Related
I am working with some strange legacy code. They have a custom object which implements a JPanel. This JPanel object is a secondary popup screen within the main application. The issue I'm having is to detect when the secondary popup screen is closed.
I tried to implement a WindowListener for the class, but when I try to add it, there is no JFrame associated with this object. I am assuming this is because they are using a custom object and it is an embedded popup screen.
I tried to retrieve a JFrame using:
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
which fails on a NullPointerException. I have no idea why it's so difficult to detect the right hand corner "x" close button on this page! I should mention that they were able to add Mouse and Key Listeners to the table which is embedded within the JPanel. But the outside listener for the entire window is causing me troubles.
(Please bear with me, this is my first stackoverflow post and I am new to Swing.)
Thanks so very much!!
Try to call getParent() for that strange panel. It should return the parent GUI component. If this is still not your frame but some intermediate panel instead, call getParent() on it as well. The top level component returns null.
Component p = strangePanel;
while ( p != null && ! (p instanceof Window))
p = p.getParent();
((Window) p ).addWindowListener(..);
Cannot understand why you are getting "NullPointerException" at:
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
In two cases this can happen:
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(null);
In your case, this is not possible as you have used this as a parameter.
Second, are you doing some other operations in above code line, like:
JFrame parentFrame = ((JFrame) SwingUtilities.getWindowAncestor(this)).someOperation();
In this case, if your this object represent the top window then you are supposed to get "NullPointerException" because ancestor of top parent is returned as "null". In other cases, I suspect you will get this exception.
Can you post a block of code where you are getting exception.
For this answer I'm making a minor assumption that the Nullpointer is not being thrown at the line that you mentioned, but rather when you attempt to add the WindowListener to the parentFrame. This is most likely because you're calling
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
before the JPanel has been added to the JFrame hierarchy.
Here's a rought code sample on how you could work around this. The thought it to wait for the panel to be notified that it has been attached to the JFrame somewhere in its hierarchy.
package test;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class HierarchyTest extends JPanel {
protected static void loadApp() {
HierarchyTest test = new HierarchyTest();
JFrame frame = new JFrame();
frame.add(test);
frame.setSize(200, 200);
frame.setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
loadApp();
}
});
}
public HierarchyTest() {
this.addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
// This can be optimized by checking the right flags, but I leave that up to you to look into
boolean connected = setupListenersWhenConnected();
if (connected) {
HierarchyTest.this.removeHierarchyListener(this);
}
}
});
}
protected boolean setupListenersWhenConnected() {
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
if (parentFrame == null) {
return false;
}
parentFrame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
// Implementation here
System.out.println("This window is closing!");
}
});
return true;
}
}
I have a JScrollPane containing a panel with a BoxLayout (PAGE AXIS).
My problem is that the JScrollPane does not react to mouse wheel events. To make it scroll using the mouse wheel i need to be on the JScrollBar.
I found this thread and i have no MouseMotionListener or MouseWheelListener, only a MouseListener. I think my problem come from the fact that my JScrollPane act on a JPanel that contains other panels itself. So when the mouse is on a panel within the JScrollPane it seems that the event is consumed by this panel i never seen by the scroll pane.
Is there a correct way to make the events caught by the children of the scroll pane visible to this scroll pane?
SSCCE:
Here a simple test case trying to show when i try to do in my Swing application.
The frame:
public class NewJFrame extends javax.swing.JFrame {
public NewJFrame() {
initComponents();
for (int i = 0; i < 50; i++) {
jPanel1.add(new TestPanel());
}
}
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
jPanel1 = new javax.swing.JPanel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jPanel1.setLayout(new javax.swing.BoxLayout(jPanel1, javax.swing.BoxLayout.PAGE_AXIS));
jScrollPane1.setViewportView(jPanel1);
getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER);
pack();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new NewJFrame().setVisible(true);
}
});
}
}
And the TestPanel definition:
public class TestPanel extends javax.swing.JPanel {
public TestPanel() {
initComponents();
}
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
jLabel2 = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
jTextArea1 = new javax.swing.JTextArea();
jLabel1.setText("jLabel1");
setBackground(new java.awt.Color(255, 51, 51));
setLayout(new java.awt.BorderLayout());
jLabel2.setText("TEST LABEL");
jLabel2.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
add(jLabel2, java.awt.BorderLayout.PAGE_START);
jTextArea1.setEditable(false);
jTextArea1.setColumns(20);
jTextArea1.setRows(5);
jTextArea1.setFocusable(false);
jScrollPane1.setViewportView(jTextArea1);
add(jScrollPane1, java.awt.BorderLayout.CENTER);
}
}
The JTextArea seems to consume the event since when the mouse cursor is inside it, the scrolling using wheel does not work. I have to put the mouse cursor outside the text area to make it works again.
Walter beat me to analysing the issue :-)
Adding a bit of detail:
It's correct that a JScrollPane supports mouseWheelHandling. According to the rules of mouseEvent dispatching, the top-most (in z-order) component gets the event, and that's the scrollPane around the textArea. So if wheeling the textarea is not required, a simple solution might be to disable the wheel-support in its scrollPane. And JScrollPane even has api for doing it:
scrollPane.setWheelScrollingEnabled(false);
Unfortunately, that doesn't work. Reason it's not working is that this property has no effect in the event dispatch chain which ultimately calls into eventTypeEnabled:
case MouseEvent.MOUSE_WHEEL:
if ((eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0 ||
mouseWheelListener != null) {
return true;
}
This returns true if a mouseWheelListener is installed - which is done unconditionally by BasicScrollPaneUI, and not removed when the wheelEnabled property is changed (the ui doesn't even listen to that property ...) Plus the listener simply does nothing if the property is false. At least one of those facts is a bug, the ui should
either remove/add the listener depending on wheelEnabled
or: implement the listener such that it dispatches the event up the chain (as Walter does in his example)
The first option can be handled by application code:
scrollPane = new JScrollPane();
scrollPane.removeMouseWheelListener(scrollPane.getMouseWheelListeners()[0]);
it's a bit of a hack (as bug-workarounds always are :-), production code would have to listen to the wheelEnable to re-install if needed plus listen to LAF changes to update/re-remove the listeners installed by the ui.
Implementing the second option in slight modification (as to Walter's dispatching) by subclassing the JScrollPane and dispatch the event to parent if the wheelEnabled is false:
scrollPane = new JScrollPane() {
#Override
protected void processMouseWheelEvent(MouseWheelEvent e) {
if (!isWheelScrollingEnabled()) {
if (getParent() != null)
getParent().dispatchEvent(
SwingUtilities.convertMouseEvent(this, e, getParent()));
return;
}
super.processMouseWheelEvent(e);
}
};
scrollPane.setWheelScrollingEnabled(false);
The mouse wheel event gets consumed by the scroll pane around the text area. You can try to manually pass the event to the parent scroll pane like this:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TestScrollPane2 {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
// might want to use a http://tips4java.wordpress.com/2009/12/20/scrollable-panel/
JPanel panel = new JPanel(new GridLayout(0, 1));
for (int i = 0; i < 10; i++) {
panel.add(new JScrollPane(new JTextArea(3, 40)) {
#Override
protected void processMouseWheelEvent(MouseWheelEvent e) {
Point oldPosition = getViewport().getViewPosition();
super.processMouseWheelEvent(e);
if(getViewport().getViewPosition().y == oldPosition.y) {
delegateToParent(e);
}
}
private void delegateToParent(MouseWheelEvent e) {
// even with scroll bar set to never the event doesn't reach the parent scroll frame
JScrollPane ancestor = (JScrollPane) SwingUtilities.getAncestorOfClass(
JScrollPane.class, this);
if (ancestor != null) {
MouseWheelEvent converted = null;
for (MouseWheelListener listener : ancestor
.getMouseWheelListeners()) {
listener.mouseWheelMoved(converted != null ? converted
: (converted = (MouseWheelEvent) SwingUtilities
.convertMouseEvent(this, e, ancestor)));
}
}
}
});
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(panel));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class scjp extends TransferHandler
{
JTextField txtField;
JRadioButton lbl=new JRadioButton("Hello");
public static void main(String[] args)
{
scjp sdd = new scjp();
transfer th=new transfer();
}
public scjp()
{
MouseListener ml = new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
JComponent jc = (JComponent)e.getSource();
TransferHandler th = jc.getTransferHandler();
th.exportAsDrag(jc, e, TransferHandler.COPY);
}
};
MouseMotionListener m2=new MouseAdapter()
{
public void mouseDragged(MouseEvent e)
{
}
};
JFrame frame = new JFrame("SCJP");
txtField = new JTextField(20);
lbl.setTransferHandler(new TransferHandler("text"));
lbl.addMouseListener(ml);
lbl.addMouseMotionListener(m2);
JPanel panel = new JPanel();
panel.add(txtField);
frame.add(lbl, BorderLayout.CENTER);
frame.add(panel, BorderLayout.NORTH);
frame.setSize(400, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EX…
frame.setResizable(false);
}
}
here i m dragging a radio and dropping that into a textBox, when i m dragging the radio button, my mouse pointer looks like (a rectangle and plus sign below the arrow).
What i need to do:
when i pick/drag the radio that time instead of that rectangle and plus sign, i want that string which is of radio??
i mean to say the radio button string/text i want as with my mouse cursor upto i drop that component/radio in text box..?
please help me to figure out this problem as soon as possible, please.
First look here - this explains how to change the cursor while performing drag n drop.
Once you understand the concept, you will have to code a method that creates an in-memory image of text from the radio button in question. That image can be used in populating the cursor.
Pseudo code:
img = //in-memory image created by writing text of radio button to graphics.
Cursor curCircle = Toolkit.getDefaultToolkit().createCustomCursor(img,new Point(5,5),"some text");
Set the new cursor in dragEnter - and then take care of dragExit to restore original cursor.
I have a JList with an array of strings. Basically it displays a restaurant menu.
right next to the JList i have another JList which is empty. Whenever a user double clicks on a string in the first JList (where the menu is displayed) I want it to show up on the next JList which is right next to it.
how do i do that?
You can try
final JList list = new JList(dataModel);
MouseListener mouseListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
String selectedItem = (String) list.getSelectedValue();
// add selectedItem to your second list.
DefaultListModel model = (DefaultListModel) list2.getModel();
if(model == null)
{
model = new DefaultListModel();
list2.setModel(model);
}
model.addElement(selectedItem);
}
}
};
list.addMouseListener(mouseListener);
You may also want to do it with the Enter key pressed by adding a KeyListener:
jlist.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ENTER){
//do what you want to do
}
}
});
I know that this is not for a double click but some people want to do it with the Enter button instead as I wanted to do.
I have done it already in your code in the other question?
[link] I want to add an action listener from one JList to another JList and how can a JList appear with out any text inside?
The only think you must do there is to put it into the #Bala R's if statement doing the check of number of clicks:
if (e.getClickCount() == 2) {
//your code
}
Actually you would be better to use addElement(selectedItem); method, as in the #Bala R's code instead of
add(orderList.getModel().getSize(), selectedItem); in my code. Both add the item to the end but addElement looks nicer and you do not need to retrieve the model's size.
Oi, Boro.
public void addActionListener(final ActionListener al) {
jList.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
al.actionPerformed(new ActionEvent(e.getSource(), e.getID(), "ENTER"));
}
}
});
jList().addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
al.actionPerformed(new ActionEvent(e.getSource(), e.getID(), "ENTER"));
}
}
});
}
I'm having a JTable containing JComboBox editors initialized somewhat like
JComboBox comboBox = ...;
TableColumn tc = table.getColumnModel().getColumn(i);
tc.setCellEditor(new DefaultCellEditor(comboBox));
This is working otherwise fine but I'd like to be able to navigate in the table and update the values with keyboard only. Now this is possible with the combo boxes but if I want to update the value "1" I must first press a key to activate the combo box and then press "1" to select the item.
So, what I want is that I could press "1" and the item would be selected with only one key press.
For the text cells I've managed to do this with prepareEditor like the following...
#Override
public Component prepareEditor(TableCellEditor editor, int row, int column) {
Component c = super.prepareEditor(editor, row, column);
if (c instanceof JTextComponent) {
((JTextComponent) c).selectAll();
}
return c;
}
... but I haven't managed to figure out what to do with the combo box.
One possibility could be own TableCellEditor but if there's a more simple solution that would be nice =)
br,
Touko
In case anyone is still interested, I do a simple modification to Touko's code and this works for me:
public class CustomTable extends JTable {
private static final long serialVersionUID = -8855616864660280561L;
public CustomTable(TableModel tableModel) {
super(tableModel);
}
#Override
public Component prepareEditor(TableCellEditor editor, int row, int column) {
final Component comp = super.prepareEditor(editor, row, column);
// Text component should select all text when initiated for editing.
if (comp instanceof JTextComponent)
((JTextComponent) comp).selectAll();
// Try to obtain focus for the editor component.
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() { comp.requestFocusInWindow(); }
});
return comp;
}
}
So basically, I'm just requesting focus for the editor component at some later time using SwingUtilities.invokeLater. The reason for this approach is because focus request will fail if the editor component is not yet visible.
Hope this can help anyone.
You must add a KeyListener to your code.
The best solution is add it to the JTable component where you are laying the JComboBox and implement the method keyPressed(KeyEvent e) or the keyReleased(KeyEvent e) one in order to know which is the key and do the necessary action.
Here I give you an example:
JTable table = new JTable();
// Your necessary code (create combo box, cell editor...etc)
table.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
switch(keyCode) {
case KeyEvent.VK_1:
// manage key 1
break;
case KeyEvent.VK_A:
// manage key A
break;
case KeyEvent.VK_F1:
// manage key F1
break;
case KeyEvent.VK_TAB:
// manage key TAB
break;
default:
// manage other keys
}
}
});
You also can combine this solution with a dictionary which relates the keyCode with an action interface.
This second solution needs the following code:
A global attribute (the dictionary):
Map<Integer,MyAction> keyActions = new Hashmap<Integer,MyAction>();
A own action interface:
public interface MyAction {
public void doAction();
}
And the KeyListener.keyPressed() function would be the following:
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
MyAction ma = keyActions.get(keyCode);
if (ma != null) {
ma.doAction();
}
else {
// do default action for other keys
}
}
I hope this helps you.
Regards!