Given the following scenario:
one has a JComboBox and a JTextField
the latter has an InputVerifier providing its own error message.
the textField is the current focus owner and its input does not satisfy the inputVerifier (Here: Less than 3 characters).
Now you click on the combo, which will fire the InputVerifier.
The InputVerifier in turn will show its error message.
You acknowledge the error message and, lo and behold, the combo's popup appears, ready to accept a selection.
To me this is an undesired behaviour, and I would prefer to have no other
component change its input until the inputVerifier is satisfied. Paticularly
in the described scenario the JComboBox could have its own ActionListener
changing the whole situation in case of a new selection.
The following code solves this problem by applying a PopupMenuListener, which
checks a boolean in the verifier telling its state, and then decides whether to
show the popup or not.
What I would like to do now is to write an extended JComboBox class implementing this functionality and accepting all kinds of (extended) InputVerifiers. But I
am already stuck in my test code here, for I don't see a way how to give the
PopupMenuListener access to the verifers' boolean 'satisfied' in a general way.
Passing just an InputVerifier as parameter would need casting later, which
unmakes the generality. - Or is there any "variable" casting?
Any idea is welcome, even if it is a completely different approach to the initial problem.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.metal.*;
public class DisableComboPopupByVerifier extends JFrame {
public static final long serialVersionUID = 100L;
JComboBox<String> cmb;
public DisableComboPopupByVerifier() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(450, 240);
setLocationRelativeTo(null);
setLayout(new FlowLayout(FlowLayout.LEFT, 2, 2));
cmb= new JComboBox<>(new String[]{"AA", "BB", "CC"});
cmb.setPreferredSize(new Dimension(43, 20));
MyMinLenVerifier verifier= new MyMinLenVerifier(this, 3);
MyPopupListener popupListener= new MyPopupListener(verifier);
cmb.addPopupMenuListener(popupListener);
add(cmb);
JTextField tf1= new JTextField(5);
tf1.setInputVerifier(verifier);
add(tf1);
JTextField tf2= new JTextField(5);
tf2.setInputVerifier(verifier);
add(tf2);
SwingUtilities.invokeLater(() -> tf1.requestFocusInWindow());
setVisible(true);
}
static public void main(String args[]) {
EventQueue.invokeLater(DisableComboPopupByVerifier::new);
}
class MyMinLenVerifier extends InputVerifier {
boolean satisfied;
int minlen;
Component parent;
public MyMinLenVerifier(Component parent, int minlen) {
this.minlen= minlen;
this.parent= parent;
}
public boolean verify(JComponent input) {
String s= ((JTextField)input).getText();
if (s.length()>=minlen) {
satisfied= true;
}
else {
satisfied= false;
JOptionPane.showMessageDialog(parent,
"Enter at least "+minlen+" characters.",
"Error", JOptionPane.ERROR_MESSAGE);
}
return satisfied;
}
}
class MyPopupListener implements PopupMenuListener {
//** To accept all kinds of verifiers the specific one here needs to be replaced
by the super class InputVerifier, which, however, makes casting necessary to access the boolean 'satisfied' below.
**//
MyMinLenVerifier verifier;
public MyPopupListener(MyMinLenVerifier verifier) {
this.verifier= verifier;
}
public void popupMenuCanceled(PopupMenuEvent e) {
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
//** boolean 'satisfied' is a requirement in all passed verifiers. **//
if (!verifier.satisfied) {
BasicComboPopup popup= (BasicComboPopup)cmb.getUI()
.getAccessibleChild(cmb, 0);
SwingUtilities.invokeLater(() -> {
popup.setVisible(false);
// This restauration of the button background is not reliable!
MetalComboBoxButton btn= (MetalComboBoxButton)cmb.getComponent(0);
btn.getModel().setPressed(false);
btn.repaint();
((JComboBox)e.getSource()).repaint();
});
}
}
}
}
For lack of a better idea I overrode toString() in MyMinLenVerifier
#Override
public String toString() {
return "satisfied:"+satisfied;
}
And the MyPopupListener class looks now like this:
class MyPopupListener implements PopupMenuListener {
InputVerifier verifier;
public MyPopupListener(InputVerifier verifier) {
this.verifier= verifier;
}
.
.
.
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
if (verifier.toString().endsWith("false")) {
...
Related
In my online Java class, I need to write a program that counts the number of mouse clicks on a button within a frame. Here is my code:
import java.awt.*;
import java.awt.event.*;
public class option1 extends Frame {
option1() {
setTitle("Final Project Option 1");
setSize(300,300);
show();
}
public static void main(String[] args) {
option1 test = new option1();
int a = 0;
String s1 = "" + a;
Frame objFrame;
Button objButton1;
Label objLabel1;
objFrame = new option1();
objButton1 = new Button("Button");
objLabel1 = new Label();
objLabel1.setBounds(150,220,50,30);
objButton1.setBounds(40,35,50,50);
objLabel1.setText(s1);
objButton1.addMouseListener(new MyMouseListener()); //line 29
objFrame.add(objLabel1);
objFrame.add(objButton1);
}
public class MyMouseListener extends MouseAdapter {
public void mouseClicked(MouseEvent me) {
a++; //line 36
}
}
}
When compiling, I get two errors. One error is on line 29, which is "non-static variable this cannot be referenced from a static context", and the other is on line 36, which is "cannot find symbol".
So, what exactly am I doing wrong? I would appreciate responders telling exactly what I need to do to fix the problem, and avoiding using technical terms since I'm rather new to programming.
I see two issues, namely your inner class should be static (to use it without an instance of option1 which should probably be Option1 to fit with Java naming conventions) and you need to define and initialize a. Something like
public static class MyMouseListener extends MouseAdapter {
int a = 0; //<-- add this.
public void mouseClicked(MouseEvent me) {
a++;
}
}
Also, I suggest you consider using the more modern JFrame instead of the older Frame.
Edit
You'll need to save a reference to your MouseListener like
MyMouseListener mml = new MyMouseListener();
objButton1.addMouseListener(mml);
Then you can get it the a like
System.out.println(mml.a);
Finally, your original approach of "" + a would be "0".
Generally, as soon as you possibly can, get out of the main method into a non-static context...
public class option1 extends Frame {
private int a = 0;
private Label objLabel1;
option1() {
setTitle("Final Project Option 1");
setSize(300,300);
Button objButton1;
objButton1 = new Button("Button");
objLabel1 = new Label();
objLabel1.setBounds(150,220,50,30);
objButton1.setBounds(40,35,50,50);
objLabel1.setText(Integer.toString(a));
objButton1.addMouseListener(new MyMouseListener()); //line 29
add(objLabel1);
add(objButton1);
show();
}
public static void main(String[] args) {
option1 test = new option1();
}
public class MyMouseListener extends MouseAdapter {
public void mouseClicked(MouseEvent me) {
a++; //line 36
objLabel1.setText(Integer.toString(a));
}
}
}
Generally speaking, AWT is out-of-date (by some 15 years) and you really should be trying to use Swing or JavaFX instead.
Buttons should use ActionListener, as a mouse is not the only way a button might be triggered
You might like to have a read through Code Conventions for the Java TM Programming Language, it will make it easier for people to read your code and for you to read others
I just tried to make your code working. But there is some issues regarding the standard Java coding. But you should consider previous answers concerning the coding style.
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class Main {
public static void main(String[] args) {
final Frame mainFrame = new OptionOne();
Button button = new Button("Button");
final Label label = new Label();
label.setBounds(150, 220, 50, 30);
label.setText("0");
button.setBounds(40, 35, 50, 50);
label.addPropertyChangeListener(label.getText(), new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
mainFrame.addNotify();
}
});
button.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
int value = Integer.parseInt(label.getText());
label.setText(String.valueOf(value + 1));
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
mainFrame.add(label);
mainFrame.add(button);
}
}
class OptionOne extends Frame {
OptionOne() {
setTitle("Final Project Option 1");
setSize(300, 300);
show();
}
}
I am new to Swing and I have a situation. I am designing an application that renders the GUI components dynamically based on an xml file input(meta-data) . Now most of my JTextFields have InputVerifier set to them, for validation purpose. The input verifier pops up JOptionPane whenever there is an invalid input.
Now, if a user enter an invalid data and moves ahead and clicks a button on the Panel, then a dialog pops up and the user have to respond to it. but after that also the button does not paint to release state. It still looked like it is pressed but actually it is not. As the whole code is pretty messy, I am putting the problem scenario in the code below:-
What should I do so that the JButton looks unpressed? I would appreciate if the logic is also explained.
Thanks in advance.
package test;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class VerifierTest extends JFrame {
private static final long serialVersionUID = 1L;
public VerifierTest() {
JTextField tf;
tf = new JTextField("TextField1");
getContentPane().add(tf, BorderLayout.NORTH);
tf.setInputVerifier(new PassVerifier());
final JButton b = new JButton("Button");
b.setVerifyInputWhenFocusTarget(true);
getContentPane().add(b, BorderLayout.EAST);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (b.hasFocus())
System.out.println("Button clicked");
}
});
addWindowListener(new MyWAdapter());
}
public static void main(String[] args) {
Frame frame = new VerifierTest();
frame.setSize(400, 200);
frame.setVisible(true);
//frame.pack();
}
class MyWAdapter extends WindowAdapter {
public void windowClosing(WindowEvent event) {
System.exit(0);
}
}
class PassVerifier extends InputVerifier {
public boolean verify(JComponent input) {
JTextField tf = (JTextField) input;
String pass = tf.getText();
if (pass.equals("Manish"))
return true;
else {
String message = "illegal value: " + tf.getText();
JOptionPane.showMessageDialog(tf.getParent(), message,
"Illegal Value", JOptionPane.ERROR_MESSAGE);
return false;
}
}
}
}
The method verify is actually not a good place to open a JOptionPane.
There are several approaches you could consider to solve your problem:
You want this JOptionPane to appear everytime the textfield looses the focus and the input is incorrect: use a FocusListener on the JTextField and act upon appropriate events
You want this JOptionPane to appear everytime the buttons is pressed: use your ActionListener to do it if the input is incorrect.
Here is a small snippet of the latter option:
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class VerifierTest extends JFrame {
private static final long serialVersionUID = 1L;
public VerifierTest() {
final JTextField tf = new JTextField("TextField1");
getContentPane().add(tf, BorderLayout.NORTH);
tf.setInputVerifier(new PassVerifier());
final JButton b = new JButton("Button");
b.setVerifyInputWhenFocusTarget(true);
getContentPane().add(b, BorderLayout.EAST);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!tf.getInputVerifier().verify(tf)) {
JOptionPane.showMessageDialog(tf.getParent(), "illegal value: " + tf.getText(), "Illegal Value",
JOptionPane.ERROR_MESSAGE);
}
if (b.hasFocus()) {
System.out.println("Button clicked");
}
}
});
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
Frame frame = new VerifierTest();
frame.setSize(400, 200);
frame.setVisible(true);
}
class PassVerifier extends InputVerifier {
#Override
public boolean verify(JComponent input) {
final JTextField tf = (JTextField) input;
String pass = tf.getText();
return pass.equals("Manish");
}
}
}
Also consider setting the default close operation of the JFrame instead of adding a window listener (but it is a good approach to use a WindowListener if you want to pop up a dialog asking the user if he is sure he wants to exit your application).
I added a call to SwingUtilities to ensure that the GUI is on the event thread, and I removed your reference to Frame.
The GUI works for me on Windows XP.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class VerifierTest implements Runnable {
private static final long serialVersionUID = 1L;
public VerifierTest() {
}
#Override
public void run() {
JFrame frame = new JFrame();
frame.setSize(400, 200);
JTextField tf;
tf = new JTextField("TextField1");
tf.setInputVerifier(new PassVerifier());
frame.getContentPane().add(tf, BorderLayout.NORTH);
final JButton b = new JButton("Button");
b.setVerifyInputWhenFocusTarget(true);
frame.getContentPane().add(b, BorderLayout.EAST);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (b.hasFocus())
System.out.println("Button clicked");
}
});
frame.addWindowListener(new MyWAdapter());
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new VerifierTest());
}
class MyWAdapter extends WindowAdapter {
#Override
public void windowClosing(WindowEvent event) {
System.exit(0);
}
}
class PassVerifier extends InputVerifier {
#Override
public boolean verify(JComponent input) {
JTextField tf = (JTextField) input;
String pass = tf.getText();
if (pass.equals("Manish"))
return true;
else {
String message = "illegal value: " + tf.getText();
JOptionPane.showMessageDialog(tf.getParent(), message,
"Illegal Value", JOptionPane.ERROR_MESSAGE);
return false;
}
}
}
}
I have added a new mouse listener to the button as below and its seems to be working fine for me now, but I am not sure if it is a good way of rectifying the buttons selection state.
package test;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.plaf.basic.BasicButtonListener;
public class VerifierTest extends JFrame {
private static final long serialVersionUID = 1L;
public VerifierTest() {
JTextField tf;
tf = new JTextField("TextField1");
getContentPane().add(tf, BorderLayout.NORTH);
tf.setInputVerifier(new PassVerifier());
final JButton b = new JButton("Button");
b.setVerifyInputWhenFocusTarget(true);
getContentPane().add(b, BorderLayout.EAST);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (b.hasFocus())
System.out.println("Button clicked");
}
});
b.addMouseListener(new BasicButtonListener(b) {
#Override
public void mouseExited(MouseEvent e) {
((JButton)e.getSource()).getModel().setArmed(false);
((JButton)e.getSource()).getModel().setPressed(false);
}
});
addWindowListener(new MyWAdapter());
}
public static void main(String[] args) {
Frame frame = new VerifierTest();
frame.setSize(400, 200);
frame.setVisible(true);
// frame.pack();
}
class MyWAdapter extends WindowAdapter {
public void windowClosing(WindowEvent event) {
System.exit(0);
}
}
class PassVerifier extends InputVerifier {
public boolean verify(JComponent input) {
JTextField tf = (JTextField) input;
String pass = tf.getText();
if (pass.equals("Manish"))
return true;
else {
final String message = "illegal value: " + tf.getText();
JOptionPane.showMessageDialog(null, message,
"Illegal Value", JOptionPane.ERROR_MESSAGE);
return false;
}
}
}
}
First: all implementations of InputVerifier which open the dialog in verify() are invalid. They violated their contract, API doc:
This method should have no side effects.
with the "should" really meaning "must not". The correct place for side-effects is shouldYieldFocus.
Second: moving the side-effect (showing the message dialog) correctly into the shouldYieldFocus doesn't work as well ... due to a bug (THEY call it feature request ;-), that's older than a decade and in the top 10 RFEs
Being a hack-around a bug, #dareurdrem's mouseListener is as good as any workable hack can get :-)
Update
After playing a bit with different options to hack around the bug, here's another hack - it's as brittle as all hacks are (and doesn't survive a LAF toggle, has to be re-installed if dynamic toggling is required)
For hacking the mouse behaviour the basic approach is to hook into the listener installed by the ui:
find the original
implement a custom listener which delegates most events directly to the original
for pressed events request focus first: if yielded delegate to original, if not do nothing
The last bullet is slightly more involved because focus events can be asynchronous, so we have to invoke the check for being focused. Invoking, in turn, requires to send a release in case nobody objected.
Another quirk is the rootPane's pressed action (for its defaultButton): it's done without respecting any inputVerifiers by unconditionally calling doClick. That can be hacked by hooking into the action, following the same pattern as hooking into the mouseListener:
find the rootPane's pressed action
implement a custom action which checks for a potentially vetoing inputVerifier: delegate to the original if not, do nothing otherwise
The example modified along those lines:
public class VerifierTest implements Runnable {
private static final long serialVersionUID = 1L;
#Override
public void run() {
InteractiveTestCase.setLAF("Win");
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
JTextField tf = new JTextField("TextField1");
tf.setInputVerifier(new PassVerifier());
frame.add(tf, BorderLayout.NORTH);
final JButton b = new JButton("Button");
frame.add(b);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked");
}
});
// hook into the mouse listener
replaceBasicButtonListener(b);
frame.add(new JTextField("not validating, something else to focus"),
BorderLayout.SOUTH);
frame.getRootPane().setDefaultButton(b);
// hook into the default button action
Action pressDefault = frame.getRootPane().getActionMap().get("press");
frame.getRootPane().getActionMap().put("press", new DefaultButtonAction(pressDefault));
frame.setVisible(true);
}
protected void replaceBasicButtonListener(AbstractButton b) {
final BasicButtonListener original = getButtonListener(b);
if (original == null) return;
Hacker l = new Hacker(original);
b.removeMouseListener(original);
b.addMouseListener(l);
}
public static class Hacker implements MouseListener {
private BasicButtonListener original;
/**
* #param original the listener to delegate to.
*/
public Hacker(BasicButtonListener original) {
this.original = original;
}
/**
* Hook into the mousePressed: first request focus and
* check its success before handling it.
*/
#Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
if(e.getComponent().contains(e.getX(), e.getY())) {
// check if we can get the focus
e.getComponent().requestFocus();
invokeHandleEvent(e);
return;
}
}
original.mousePressed(e);
}
/**
* Handle the pressed only if we are focusOwner.
*/
protected void handlePressed(final MouseEvent e) {
if (!e.getComponent().hasFocus()) {
// something vetoed the focus transfer
// do nothing
return;
} else {
original.mousePressed(e);
// need a fake released now: the one from the
// original cycle might never has reached us
MouseEvent released = new MouseEvent(e.getComponent(), MouseEvent.MOUSE_RELEASED,
e.getWhen(), e.getModifiers(),
e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()
);
original.mouseReleased(released);
}
}
/**
* focus requests might be handled
* asynchronously. So wrap the check
* wrap the block into an invokeLater.
*/
protected void invokeHandleEvent(final MouseEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
handlePressed(e);
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
original.mouseClicked(e);
}
#Override
public void mouseReleased(MouseEvent e) {
original.mouseReleased(e);
}
#Override
public void mouseEntered(MouseEvent e) {
original.mouseEntered(e);
}
#Override
public void mouseExited(MouseEvent e) {
original.mouseExited(e);
}
}
public static class DefaultButtonAction extends AbstractAction {
private Action original;
/**
* #param original
*/
public DefaultButtonAction(Action original) {
this.original = original;
}
#Override
public void actionPerformed(ActionEvent e) {
JRootPane root = (JRootPane) e.getSource();
JButton owner = root.getDefaultButton();
if (owner != null && owner.getVerifyInputWhenFocusTarget()) {
Component c = KeyboardFocusManager
.getCurrentKeyboardFocusManager()
.getFocusOwner();
if (c instanceof JComponent && ((JComponent) c).getInputVerifier() != null) {
if (!((JComponent) c).getInputVerifier().shouldYieldFocus((JComponent) c)) return;
}
}
original.actionPerformed(e);
}
}
/**
* Returns the ButtonListener for the passed in Button, or null if one
* could not be found.
*/
private BasicButtonListener getButtonListener(AbstractButton b) {
MouseMotionListener[] listeners = b.getMouseMotionListeners();
if (listeners != null) {
for (MouseMotionListener listener : listeners) {
if (listener instanceof BasicButtonListener) {
return (BasicButtonListener) listener;
}
}
}
return null;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new VerifierTest());
}
public static class PassVerifier extends InputVerifier {
/**
* Decide whether or not the input is valid without
* side-effects.
*/
#Override
public boolean verify(JComponent input) {
final JTextField tf = (JTextField) input;
String pass = tf.getText();
if (pass.equals("Manish"))
return true;
return false;
}
/**
* Implemented to ask the user what to do if the input isn't valid.
* Note: not necessarily the best usability, it's mainly to
* demonstrate the different effects on not/agreeing with
* yielding focus transfer.
*/
#Override
public boolean shouldYieldFocus(final JComponent input) {
boolean valid = super.shouldYieldFocus(input);
if (!valid) {
String message = "illegal value: " + ((JTextField) input).getText();
int goAnyWay = JOptionPane.showConfirmDialog(input, "invalid value: " +
message + " - go ahead anyway?");
valid = goAnyWay == JOptionPane.OK_OPTION;
}
return valid;
}
}
}
Actually the real problem is in how the focus system and awt listeners interact. There are a few bugs declared in Java that the developers are going back and forth on who is responsible.
The mouse listener does : processMouseEvent and within that logic, the current FocusOwner is asked to yield Focus. it fails. But because half the event is processed already, the button becomes armed and the focus remains with the field.
I finally saw one developer comment: Don't let the listener proceed if the field is not allowed to lose focus.
For example:
Define a JTextfield with edits to only allow values < 100.
A message pops up when you lose focus.
I overrode my base JButton classes' processMouseEvent(MouseEvent e)
with code:
protected void processMouseEvent(MouseEvent e) {
if ( e.getComponent() != null && e.getComponent().isEnabled() ) { //should not be processing mouse events if it's disabled.
if (e.getID() == MouseEvent.MOUSE_RELEASED && e.getClickCount() == 1) {
// The mouse button is being released as per normal, and it's the first click. Process it as per normal.
super.processMouseEvent(e);
// If the release occured within the bounds of this component, we want to simulate a click as well
if (this.contains(e.getX(), e.getY())) {
super.processMouseEvent(new MouseEvent(e.getComponent(),
MouseEvent.MOUSE_CLICKED,
e.getWhen(),
e.getModifiers(),
e.getX(),
e.getY(),
e.getClickCount(),
e.isPopupTrigger(),
e.getButton()));
}
}
else if (e.getID() == MouseEvent.MOUSE_CLICKED && e.getClickCount() == 1) {
// Normal clicks are ignored to prevent duplicate events from normal, non-moved events
}
else if (e.getID() == MouseEvent.MOUSE_PRESSED && e.getComponent() != null && (e.getComponent().isFocusOwner() || e.getComponent().requestFocusInWindow())) {// if already focus owner process mouse event
super.processMouseEvent(e);
}
else {
// Otherwise, just process as per normal.
if (e.getID() != MouseEvent.MOUSE_PRESSED) {
super.processMouseEvent(e);
}
}
}
}
in the guts of this logic is the simple questions.
Button: Are you already focus owner.
if not: can you(Button) possibly GAIN focus ( remember - shouldYieldFocus() is called on the current focus holder inside the requestFocusInWindow() call and will return false ALWAYS if not valid )
This Also has the side affect of popping up your error dialog!
This logic Stops the Java libraries processMouseEvent logic from processing half an event while the Focus System stops it from completing.
Obviously you'll need this type of logic on all your different JComponents that perform an action on a click.
I have been searching this site and google for a solution to my problem, and I can't find anything. I think it's supposed to just work; however, it doesn't. The arrow icon for my JComboBox doesn't show up, and I can't find anywhere to set its visibility to true.
Here's my code:
public class Driver implements ActionListener {
private JTextField userIDField;
private JTextField[] documentIDField;
private JComboBox repository, environment;
private JButton close, clear, submit;
private JFrame window;
public Driver()
{
window = makeWindow();
makeContents(window);
window.repaint();
}
private JFrame makeWindow()
{
JFrame window = new JFrame("");
window.setSize(500,300);
window.setLocation(50,50);
window.getContentPane().setLayout(null);
window.setResizable(false);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
return window;
}
private void makeContents(JFrame w)
{
makeDropDowns(w);
w.repaint();
}
private void makeDropDowns(JFrame w)
{
String[] repositoryArray = {"Click to select", "NSA", "Finance", "Test"};
repository = new JComboBox(repositoryArray);
repository.setSelectedIndex(0);
repository.addActionListener(this);
repository.setSize(150,20);
repository.setLocation(175,165);
repository.setEditable(false);
w.add(repository);
String[] environmentArray = {"Click to select", "Dev", "Test", "Qual"};
environment = new JComboBox(environmentArray);
environment.setSelectedIndex(0);
environment.addActionListener(this);
environment.setSize(150,20);
environment.setLocation(175,195);
//environment.setEditable(false);
w.add(environment,0);
}
public void actionPerformed(ActionEvent e)
{
String repositoryID = "null", environmentID = "null";
if (e.getSource() == repository)
{
repositoryID = (String)repository.getSelectedItem();
}
if(e.getSource() == environment)
{
environmentID = (String)environment.getSelectedItem();
}
}
}
Here's a link to a picture of the problem:
If anyone could help that would be awesome.
It doesn't appear to be the issue you were suffering from, but I found this post due to the same resulting issue of the arrow disappearing.
In my case it was due to me mistakenly using .removeAll() on the JComboBox rather than .removeAllItems() when I was attempting to empty and then reuse the JComboBox after a refresh of the data I was using. Just thought I'd include it as an answer in case someone else comes across this thread for similar reasons.
The code you show works, but it looks like you're fighting the enclosing container's default layout. Here, ComboTest is a JPanel which defaults to FlowLayout.
Addendum: In general, do not use absolute positioning, as shown in your update. I've changed the example to use GridLayout; comment out the setLayout() call to see the default, FlowLayout.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/10824504/230513
*/
public class ComboTest extends JPanel {
private JComboBox repository = createCombo(new String[]{
"Click to select", "NSA", "Finance", "Test"});
private JComboBox environment = createCombo(new String[]{
"Click to select", "Dev", "Test", "Qual"});
public ComboTest() {
this.setLayout(new GridLayout(0, 1));
this.add(repository);
this.add(environment);
}
private JComboBox createCombo(String[] data) {
final JComboBox combo = new JComboBox(data);
combo.setSelectedIndex(1);
combo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(e.getActionCommand()
+ ": " + combo.getSelectedItem().toString());
}
});
return combo;
}
private void display() {
JFrame f = new JFrame("ComboTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ComboTest().display();
}
});
}
}
I had the same issue. I fixed it by revalidating and repainting the panel with the following code :
myPanel.revalidate();
myPanel.repaint();
Maybe a little late, but for those who are still looking for an easy and fail-safe way to use the JComboBox can use this:
public class FixedJComboBox<E>
extends JComboBox<E> {
// Copied constructors
public FixedJComboBox() {
super();
}
public FixedJComboBox(ComboBoxModel<E> aModel) {
super(aModel);
}
public FixedJComboBox(E[] items) {
super(items);
}
public FixedJComboBox(Vector<E> items) {
super(items);
}
#Override
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
// The arrow is the first (and only) component
// that is added by default
Component[] comps = getComponents();
if (comps != null && comps.length >= 1) {
Component arrow = comps[0];
// 20 is the default width of the arrow (for me at least)
arrow.setSize(20, height);
arrow.setLocation(width - arrow.getWidth(), 0);
}
}
}
As described here, the bug is caused by incorrectly setting both the location and the size of the arrow to (0,0), followed by some repainting issues. By simply overriding the setBounds() function, the arrow is always corrected after the UI/layout manager has wrongly updated the arrow.
Also, since new components are added after the old ones (i.e. higher index), the arrow will always be at the first element in the array (assuming you don't remove and re-add the arrow).
The disadvantage is of this class is that the width of the arrow is now determined by a constant instead of the UI/layout manager.
I want my GUI to make some checks when a JOptionPane appears.
Because I can't find any other way, I though I can do those each time the application window loses focus(its just checking a string). For that reason I added the following code on my JFrame:
appFrame.addWindowListener(new WindowAdapter() {
#Override
public void windowLostFocus(WindowEvent e) {
System.out.println("Focus Lost");
}
#Override
public void windowClosing(WindowEvent e) {
//some other stuff here that work
}
});
The window closing listener works fine. Although when the JFrame isn't focused nothing happens. Shouldn't "Focus Lost" be printed each time I switch from JFrame to some other window? Also, will this method be triggered when a JOptionPane is shown?
The key to me is that you want a change in the GUI triggered by a change of a String variable. The best way I see to solve this is to make the String variable a bound property by using PropertyChangeListenerSupport. This way you can have the GUI attach a PropertyChangeListener to the class that holds the String variable and then be notified when it changes allowing you to update the GUI appropriately.
If you go this route, consider giving the observed class a SwingPropertyChangeSupport field so that the listeners will be notified on the Swing event thread and hopefully avoid any Swing concurrency issues.
Here's a brief example:
import java.awt.Dimension;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
public class ShowPropertyChangeSupport {
#SuppressWarnings("serial")
private static void createAndShowGui() {
final MainGUI mainGui = new MainGUI("Title");
final ObservedClass observedClass = new ObservedClass();
observedClass.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getPropertyName().equals(ObservedClass.BOUND_PROPERTY)) {
mainGui.setTitle(pcEvt.getNewValue().toString());
}
}
});
mainGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainGui.pack();
mainGui.setLocationRelativeTo(null);
mainGui.setVisible(true);
int timerDelay = 6000; // every 6 seconds
new Timer(timerDelay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
String result = JOptionPane.showInputDialog(mainGui,
"Please enter a String", "Set GUI title", JOptionPane.PLAIN_MESSAGE);
if (result != null) {
observedClass.setBoundProperty(result);
}
}
}){{setInitialDelay(1000);}}.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
// ** note that I don't like extending JFrame,
// but will do this for sake of example simplicity
class MainGUI extends JFrame {
public MainGUI(String title) {
super(title);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
class ObservedClass {
public static final String BOUND_PROPERTY = "bound property";
private String boundProperty = "";
private SwingPropertyChangeSupport spcSupport = new SwingPropertyChangeSupport(
this);
public SwingPropertyChangeSupport getSpcSupport() {
return spcSupport;
}
public void setSpcSupport(SwingPropertyChangeSupport spcSupport) {
this.spcSupport = spcSupport;
}
public String getBoundProperty() {
return boundProperty;
}
public void setBoundProperty(String boundProperty) {
String oldValue = this.boundProperty;
String newValue = boundProperty;
this.boundProperty = newValue;
spcSupport.firePropertyChange(BOUND_PROPERTY, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
spcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
spcSupport.removePropertyChangeListener(listener);
}
}
The key to all this in my mind is to use the listener so that the class with the bound property -- the String being listened to -- has no knowledge of the GUI, the listener, and the GUI, likewise has no knowledge of the class with the bound property. They are fully decoupled.
I'm not going to go into why you are doing what you are doing, but it is not working as you expect for the following reason:
WindowAdapter is a convenience class so you can create one listener and register it for multiple types of events. You have only registered it for one set of events, you need to also register it for focus events via: Window.addWindowFocusListener()
WindowAdapter adapter = new WindowAdapter() {
#Override
public void windowLostFocus(WindowEvent e) {
System.out.println("Focus Lost");
}
#Override
public void windowClosing(WindowEvent e) {
//some other stuff here that work
}
};
appFrame.addWindowListener(adapter);
appFrame.addWindowFocusListener(adapter);
1) JOptionPane / modal JDialog have got modality issue, but modality could be advantage if all containers have got own owner, for real workaround you need to know (I'll talking about how can I do test that)
numbers of Window[], and if isDisplayable(), then you can use follows
you can get SwingUtilities#getAccessibleIndexInXxx can returns AccessibleState
KeyboardFocusManager (very interesting methods for multi-touch) returns getXxxFocusXxx methods
Focus, FocusSubsystem is pretty asynchronous,
2) Please, with due respect, I don't know why you needed that, for why reasons I need to know about that, there is about business rules, you always need to know ...., and if is done on EDT
Focus, FocusSubsystem is pretty asynchronous,
I have made a multiple input dialog by building a JPanel with the fields I want and adding it to a JOption pane
JMainPanel mainPanel = new JMainPanel(mensaje, parametros, mgr);
int i = JOptionPane.showOptionDialog(null, mainPanel, "Sirena",
JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null,
new String[] {"Aceptar", "Cancelar"}, "Aceptar");
However I'm having trouble with the buttons, because some of the fields are required. How can I make the "Ok" button to be enabled once every required field is up, or making the click on the button to make the validations and do not close the pane until every required field is filled?
From the Java API, I found this:
options - an array of objects indicating the possible choices the user
can make; if the objects are components, they are rendered properly;
non-String objects are rendered using their toString methods; if this
parameter is null, the options are determined by the Look and Feel
So, can't I pass custom buttons as parameter?
Looks like I will have to make my own JDialog? for which case, I don't know how to make it return an int just like JOptionPane does, any recommended tutorial?
In the example options is {"Aceptar", "Cancelar"} which are the displayed buttons,
PS. I have full controll over the fields I added to the JPanel.
This is a screenshot of the JOptionPane:
I don't think that you can de-activate a JOptionPane's selections buttons, but one way to still use the JOptionPane is to simply re-display it if the required fields have not been set. You could display an error message JOptionPane first describing the error, and then display a new JOptionPane that holds the same JPanel as its second parameter -- so that the data already entered has not been lost. Otherwise, you may want to create your own JDialog which by the way isn't that hard to do.
Edit
I'm wrong. You can enable and disable the dialog buttons if you use a little recursion.
For example:
import java.awt.Component;
import java.awt.Container;
import java.awt.event.*;
import java.util.HashSet;
import java.util.Set;
import javax.swing.*;
public class Foo extends JPanel {
private static final String[] DIALOG_BUTTON_TITLES = new String[] { "Aceptar", "Cancelar" };
private JCheckBox checkBox = new JCheckBox("Buttons Enabled", true);
private Set<AbstractButton> exemptButtons = new HashSet<AbstractButton>();
public Foo() {
JButton exemptBtn = new JButton("Exempt Button");
JButton nonExemptBtn = new JButton("Non-Exempt Button");
add(checkBox);
add(exemptBtn);
add(nonExemptBtn);
exemptButtons.add(checkBox);
exemptButtons.add(exemptBtn);
checkBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
allBtnsSetEnabled(checkBox.isSelected());
}
});
}
private void allBtnsSetEnabled(boolean enabled) {
JRootPane rootPane = SwingUtilities.getRootPane(checkBox);
if (rootPane != null) {
Container container = rootPane.getContentPane();
recursiveBtnEnable(enabled, container);
}
}
private void recursiveBtnEnable(boolean enabled, Container container) {
Component[] components = container.getComponents();
for (Component component : components) {
if (component instanceof AbstractButton && !exemptButtons.contains(component)) {
((AbstractButton) component).setEnabled(enabled);
} else if (component instanceof Container) {
recursiveBtnEnable(enabled, (Container) component);
}
}
}
public int showDialog() {
return JOptionPane.showOptionDialog(null, this, "Sirena",
JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null,
DIALOG_BUTTON_TITLES, "Aceptar");
}
private static void createAndShowGui() {
Foo foo = new Foo();
int result = foo.showDialog();
System.out.println(DIALOG_BUTTON_TITLES[result]);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
This code uses listeners to check the state of a JCheckBox, but you can have listeners (DocumentListeners) listening to text field documents if you desire to know if they have data or not. The code then gets the JRootPane that holds the JCheckBox, then the root pane's contentPane, and all components of the dialog are held by this. It then recurses through all the components held by the dialog. If a component is a Container, it recurses through that container. If the component is an AbstractButton (such any JButton or checkbox), it enables or disables -- except for buttons held in the exempt buttons set.
A better example with document listeners
import java.awt.*;
import java.awt.event.*;
import java.util.HashSet;
import java.util.Set;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class Foo2 extends JPanel {
private static final String[] DIALOG_BUTTON_TITLES = new String[] {
"Aceptar", "Cancelar" };
private static final int FIELD_COUNT = 10;
private Set<AbstractButton> exemptButtons = new HashSet<AbstractButton>();
private JTextField[] fields = new JTextField[FIELD_COUNT];
public Foo2() {
setLayout(new GridLayout(0, 5, 5, 5));
DocumentListener myDocListener = new MyDocumentListener();
for (int i = 0; i < fields.length; i++) {
fields[i] = new JTextField(10);
add(fields[i]);
fields[i].getDocument().addDocumentListener(myDocListener);
}
// cheating here
int timerDelay = 200;
Timer timer = new Timer(timerDelay , new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
checkDocsForText();
}
});
timer.setRepeats(false);
timer.setInitialDelay(timerDelay);
timer.start();
}
private void checkDocsForText() {
for (JTextField field : fields) {
if (field.getText().trim().isEmpty()) {
allBtnsSetEnabled(false);
return;
}
}
allBtnsSetEnabled(true);
}
private void allBtnsSetEnabled(boolean enabled) {
JRootPane rootPane = SwingUtilities.getRootPane(this);
if (rootPane != null) {
Container container = rootPane.getContentPane();
recursiveBtnEnable(enabled, container);
}
}
private void recursiveBtnEnable(boolean enabled, Container container) {
Component[] components = container.getComponents();
for (Component component : components) {
if (component instanceof AbstractButton && !exemptButtons.contains(component)) {
((AbstractButton) component).setEnabled(enabled);
} else if (component instanceof Container) {
recursiveBtnEnable(enabled, (Container) component);
}
}
}
public int showDialog() {
return JOptionPane.showOptionDialog(null, this, "Sirena",
JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null,
DIALOG_BUTTON_TITLES, "Aceptar");
}
private class MyDocumentListener implements DocumentListener {
public void removeUpdate(DocumentEvent arg0) {
checkDocsForText();
}
public void insertUpdate(DocumentEvent arg0) {
checkDocsForText();
}
public void changedUpdate(DocumentEvent arg0) {
checkDocsForText();
}
}
private static void createAndShowGui() {
Foo2 foo = new Foo2();
int result = foo.showDialog();
if (result >= 0) {
System.out.println(DIALOG_BUTTON_TITLES[result]);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I suggest you to define some properties into your JPanel extended class, and use PropertyChangeListener to listen the occured changes and enable/disable relative buttons.
Here's an article.
Another issue maybe finding the ok/cancel buttons in the hierarchy of components, since the JDialog is created through JOptionPane and you haven't a reference to the buttons. Here's a useful thread .
You can add a property to a JComponent using putClientProperty method.
When changes occurs to a given property a PropertyChanged event is raised.
So in your example you can define a boolean property indicating that required that are inserted into the JDialog. Then add a PropertyChangeListener that when is notified enable/disable the ok button.