Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4533820
Votes 163
Synopsis Extend InputVerifiers to keep events from firing on target components.
Category java:classes_swing
Reported Against 1.3 , 1.4.1 , merlin , 1.3.1_01 , merlin-beta3
Release Fixed
State 6-Fix Understood, request for enhancement
Priority: 4-Low
Related Bugs 4342333 , 4549203 , 4368790
Submit Date 30-NOV-2001
Description
If the currently focused component has an input verifier that returns false,
does not want to yield focus, and a button is clicked the action is still fired.
Developers expect the input verifier to keep the events from firing on
components if focus isn't yielded.
Work Around
N/A
Evaluation
InputVerifiers are intended to control focus and not events.  It is questionable
whether this should be supported by the developer or by the InputVerifier. 
As mentioned in the comments section there definately is code out there
already depending on the events being fired.
  xxxxx@xxxxx   2003-01-02
Comments
  
  Include a link with my name & email   

Submitted On 15-APR-2002
krunkle
For JButton and JToggleButton:
These are the only 2 button types I used but this should work for other buttons too...For all JButtons, I did 
a setUI(MyButtonUI) and for all JToggleButtons I did a setUI(MyToggleButtonUI).  The code for those is:
public class MyToggleButtonUI extends MetalToggleButtonUI {
             protected BasicButtonListener createButtonListener(AbstractButton b) {
                     return new MyButtonListener(b);
             }
    }
    
    public class MyButtonUI extends MetalButtonUI {
             protected BasicButtonListener createButtonListener(AbstractButton b) {
                     return new MyButtonListener(b);
             }
    }

The code for MyButtonListener is listed below. Again, I only had to change mousePressed to check for focus 
before arming the Button
:
public class MyButtonListener extends BasicButtonListener {

             public MyButtonListener(AbstractButton b) {
                     super(b);
             }
             
             public void mousePressed(MouseEvent e) {
                   if (SwingUtilities.isLeftMouseButton(e)) {
                       AbstractButton b = (AbstractButton) e.getSource();
                       if (b.contains(e.getX(), e.getY())) {
                           ButtonModel model = b.getModel();
                           if (!model.isEnabled()) {
                               // Disabled buttons ignore all input...
                               return;
                           }
                           if (!b.hasFocus()) {
                               b.requestFocus();
                           }
                           //System.out.println("has focus" + b.hasFocus());
                           if (!b.hasFocus()) return;
                           
                           if (!model.isArmed()) {
                               // button not armed, should be
                               model.setArmed(true);
                           }
                           model.setPressed(true);
                       }
                   }
             }
}


Submitted On 15-APR-2002
krunkle
If you are still looking for a workaround in the meantime....see the comments of bug 4302322 or...

I had to work around this bug for a GUI I developed and here's what I did.
I had to subclass BasicMenuUI, MetalComboBoxUI and BasicButtonListenerUI (along with MetalButtonUI and 
MetalToggleButtonUI)

For JMenu:
I had to setUI(MyMenuUI) for each JMenu I was using so that the menu would not popup if my input verifier 
returned false.  The only change I had to make to BasicMenuUI was to check for focus in the mousePressed 
method before allowing the menu to popup. Here's the code I used for MyMenuUI...

public class MyMenuUI extends BasicMenuUI {
    
    protected  MouseInputListener createMouseInputListener(JComponent c) {
                return new MouseInputHandler();
    }
   

    private class MouseInputHandler implements MouseInputListener {
	public void mouseClicked(MouseEvent e) {}
    public void mousePressed(MouseEvent e) {
	    JMenu menu = (JMenu)menuItem;
	    if (!menu.isEnabled())
		return;
        
        if (!menu.hasFocus()) {
            menu.requestFocus();
        }
        if (!menu.hasFocus()) return;
        
	    MenuSelectionManager manager = 
		MenuSelectionManager.defaultManager();
            if(menu.isTopLevelMenu()) {
		if(menu.isSelected()) {
		    manager.clearSelectedPath();
		} else {
		    Container cnt = menu.getParent();
		    if(cnt != null && cnt instanceof JMenuBar) {
			MenuElement me[] = new MenuElement[2];
			me[0]=(MenuElement)cnt;
			me[1]=menu;
			manager.setSelectedPath(me);
		    }
		}
	    }

            MenuElement selectedPath[] = manager.getSelectedPath();
            if(!(selectedPath.length > 0 && 
		 selectedPath[selectedPath.length-1] == 
		 menu.getPopupMenu())) {
		if(menu.isTopLevelMenu() || 
		   menu.getDelay() == 0) {
		    MenuElement newPath[] = new MenuElement[selectedPath.length+1];
		    System.arraycopy(selectedPath,0,newPath,0,selectedPath.length);
		    newPath[selectedPath.length] = menu.getPopupMenu();
		    manager.setSelectedPath(newPath);
		} else {
		    setupPostTimer(menu);
		}
            }
        }

	public void mouseReleased(MouseEvent e) {
	    JMenu menu = (JMenu)menuItem;
	    if (!menu.isEnabled())
		return;
	    MenuSelectionManager manager = 
		MenuSelectionManager.defaultManager();
	    manager.processMouseEvent(e);
	    if (!e.isConsumed())
		manager.clearSelectedPath();		
	}
	public void mouseEntered(MouseEvent e) {
	    JMenu menu = (JMenu)menuItem;
	    if (!menu.isEnabled())
		return;

	    MenuSelectionManager manager = 
		MenuSelectionManager.defaultManager();
	    MenuElement selectedPath[] = manager.getSelectedPath();	    
	    if (!menu.isTopLevelMenu()) {
		if(!(selectedPath.length > 0 && 
		     selectedPath[selectedPath.length-1] == 
		     menu.getPopupMenu())) {
		    if(menu.getDelay() == 0) {
			MenuElement newPath[] = new MenuElement[selectedPath.length+2];
			System.arraycopy(selectedPath,0,newPath,0,selectedPath.length);
			newPath[selectedPath.length] = menuItem;
			newPath[selectedPath.length+1] = menu.getPopupMenu();
			manager.setSelectedPath(newPath);
		    } else {
			manager.setSelectedPath(getPath());
			setupPostTimer(menu);
		    }
		}
	    } else {
		if(selectedPath.length > 0 &&
		   selectedPath[0] == menu.getParent()) {
		    MenuElement newPath[] = new MenuElement[3];
		    // A top level menu's parent is by definition 
		    // a JMenuBar
		    newPath[0] = (MenuElement)menu.getParent();
		    newPath[1] = menu;
		    newPath[2] = menu.getPopupMenu();
		    manager.setSelectedPath(newPath);
		}
	    }
	}
	public void mouseExited(MouseEvent e) {
	}
	public void mouseDragged(MouseEvent e) {
	    JMenu menu = (JMenu)menuItem;
	    if (!menu.isEnabled())
		return;
	    MenuSelectionManager.defaultManager().processMouseEvent(e);
	}
	public void mouseMoved(MouseEvent e) {
	}
    }
}


Submitted On 15-APR-2002
krunkle
For JComboBox:
For all JComboBoxes I did a setUI(MyComboBoxUI).  You also need MyButtonListener for this one too (see 
createArrowButton) and I also changed InvocationMouseHandler's mousePressed method.  See code below:

public class MyComboBoxUI extends MetalComboBoxUI {
            protected ComboPopup createPopup() {
                return new MetalComboPopup( comboBox );
            }
            
            public JComboBox getcomboBox() {
                return comboBox;
            }
            
            protected JButton createArrowButton() {
                JButton button = new MetalComboBoxButton( comboBox,
                                                  new MetalComboBoxIcon(),
                                                  comboBox.isEditable() ? true : false,
                                                  currentValuePane,
                                                  listBox ) ;

                button.setMargin( new Insets( 0, 1, 1, 3 ) );
                button.setDefaultCapable(false);
                button.setUI(new MetalButtonUI() {
                        protected BasicButtonListener createButtonListener(AbstractButton b) {
                            return new MyButtonListener(b);
                        }
                });
                return button;
            }
   
            public class MetalComboPopup extends BasicComboPopup {
                public MetalComboPopup( JComboBox cBox ) {
                    super( cBox );
                }
                
            
                protected MouseListener createMouseListener() {
                    return new InvocationMouseHandler();
                }
    
               
                protected class InvocationMouseHandler extends MouseAdapter {
                    public void mousePressed( MouseEvent e ) {
                        Rectangle r;

                        if ( !SwingUtilities.isLeftMouseButton(e) )
                        return;

                        if ( !comboBox.isEnabled() )
                        return;
                        
            
                        if (!arrowButton.hasFocus()) return;

                        togglePopup();
                    }

                    public void mouseReleased( MouseEvent e ) {
            
                        Component source = (Component)e.getSource();
                        Dimension size = source.getSize();
                        Rectangle bounds = new Rectangle( 0, 0, size.width - 1, size.height - 1 );
                        if ( !bounds.contains( e.getPoint() ) ) {
                            MouseEvent newEvent = convertMouseEvent( e );
                            Point location = newEvent.getPoint();
                            Rectangle r = new Rectangle();
                            list.computeVisibleRect( r );
                            if ( r.contains( location ) ) {
                                updateListBoxSelectionForEvent( newEvent, false );
                                comboBox.setSelectedIndex( list.getSelectedIndex() );
                            }
                            hide();
                        }
                        hasEntered = false;
                        stopAutoScrolling();
                    }
                }
            }
}

Hope these help for now......They worked for me.


Submitted On 08-SEP-2002
vesely99
Since work is in progress for enhancing it, might one ask for
- no InputVerifier class, since an interface is enough,
- one more parameter, to learn which component it was?


Submitted On 17-OCT-2002
markusmenner
Sun, please, please solve this problem, it's urgent ...


Submitted On 30-OCT-2002
DReese
How is this considered to be an enhancement, and not a bug?
 The Swing documents describes the InputVerifier as follows:

The purpose of this class is to help clients support smooth
focus navigation through GUIs with text fields. Such GUIs
often need to ensure that the text entered by the user is
valid (for example, that it's in the proper format) before
allowing the user to navigate out of the text field. To do
this, clients create a subclass of InputVerifier and, using
JComponent's setInputVerifier method, attach an instance of
their subclass to the JComponent whose input they want to
validate. Before focus is transfered to another Swing
component that requests it, the input verifier's
shouldYieldFocus method is called. Focus is transfered only
if that method returns true. 

This is not how the class behave.  It's a bug.


Submitted On 21-NOV-2002
wille1234
I consider the current behaviour a bug, since it renders 
InputVerifier useless.

Stefan


Submitted On 02-JAN-2003
ragnor
InputVerifier would be infinitely more useful if you could 
determine the opposite component like you get in a 
FocusEvent. For example, on a entry form, you might want to 
allow a uset to hit a CANCEL button without validating the 
input in the current field. 
Using focus listeners to do this are messy, as your only 
choice to bring focus back is requestFocus(), which brings up 
problem cases like the validation failing on the component 
that briefly recieves focus. 

The event that caused the focus change would be extremely 
useful as well. For example, mouse event, tab, shift tab, etc. 
In our application, we would like to allow the user to back up 
(shift tab) on a failed validation to correct a field you are 
dependant upon, but not go forward. 

As it stands, the only option to have a good, working 
validation system is to write your own focus manager to do it. 
Perhaps in the next JDK release this could be corrected. 


Submitted On 08-MAR-2003
unipax
Any kind of input must be validated, not only that from a
text field,  radio button and check box, combo box and list
field, and any other input from the user may be valid or not
for the particular  application context.
To control all the user GUI interaction is a must.
If InputVerifiers is not the solution find one, but once for
all.


Submitted On 26-AUG-2003
sebastiankirsch
If I'm right, there' s really simple work-around for this bug.

For example, if you wan't to prevent executing ActionEvents,
just do the following:

    public void actionPerformed(ActionEvent actionEvent) {
        JComponent c = (JComponent) actionEvent.getSource();
        if (c.getVerifyInputWhenFocusTarget())
            if (!c.requestFocusInWindow())
                return;
// your handling code
    }

This should just work fine. If you use awt components (which
should occur seldom, as inputverifiers are for JComponents
only), just check if the source is an instance of
JComponent, too.

This is not the best solution, as the actions are fired
nethertheless. But yo don't need to replace the JDK-classes.

Sebastian Kirsch


Submitted On 26-AUG-2003
sebastiankirsch
What's more, with the c.getVerifyInputWhenFocusTarget() you
can have cancel buttons that are ignore the InputVerifiers.
- It seems like some didn't knew of this property.

Sebastian


Submitted On 26-AUG-2003
sebastiankirsch
It seems like >c.requestFocusInWindow()< returns false even
if the component already has the focus...
So it is better to use this code:

    public void actionPerformed(ActionEvent actionEvent) {
        JComponent c = (JComponent) actionEvent.getSource();
        if (c.getVerifyInputWhenFocusTarget()) {
            c.requestFocusInWindow();
            if (!c.hasFocus())
                return;
        }
        // handling code
    }


Submitted On 09-SEP-2003
boldue
Of course this is a bug.  The whole reason for using an 
InputVerifier is to (bear with me here) verify input!  This 
means the user cannot leave the field until the input verify is 
satisfied or they choose a field that does not cause the input 
verifier to be fired.  As it stands, clicking on any field that has 
a popup window is a simple way for users to by-pass the 
input verifier thus rendering it absolutely useless for its 
intended purpose.  This can be fixed with custom coding, but 
what good is a feature if custom coding is needed to fix it.  
Either fix this bug or get rid of input verifiers.


Submitted On 16-SEP-2003
hampton13
This is definately a bug.  Both the focus and the selection 
should be stalled when an InputVerifier fails  The focus is 
nothing more than the visualization of the selection.


Submitted On 30-AUG-2004
pcopeland
This is a serious high priority bug that should have been fixed a long time ago. Its very disapointing to see that this has now been open for 3 years without fixing. Data validation has always been a painful and time consuming exercise - without serious & fundemental problems in the base classes.


Submitted On 21-JAN-2005
rcclough
The InputVerifier class is a pile.  It should be fixed NOW or taken out of the JDK.  It has never worked, and it doesn't work yet, years later.   To call this litany of problem reports a "request for enhancement" is absurd.  It is as buggy as anything I've seen.  Come on SUN.  Stop jerking us around.

- Ray Clough


Submitted On 21-JAN-2005
kozchris
Just wasted a couple of errors to figure out this was a bug. I kept thinking that I was doing something wrong because the documentation clearly states how this is supposed to work. I jumped up to java 5 only to find out this still isn


Submitted On 21-JAN-2005
kozchris
How come this doesn't show up on the top 25 bugs list?


Submitted On 27-JAN-2005
carnoult
This definitly should be considered as a high priority bug.

Here is the "literature" i could collect on it on the internet. As far as combobox are concerned, remember to use a verifier on the editor, instead of the combo itself.
The "rootpane solution" (http://weblogs.java.net/blog/castelaz/archive/2004/08/the_best_laid_p.html) was promosing but then it's the menus that i couldn't handle.


http://java.sun.com/j2se/1.5.0/fixedbugs/fixedbugs.html
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4911422
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4533820
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4342333 
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4302322
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4368790
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4490197
 
Sun's forum threads:
http://forum.java.sun.com/thread.jspa?threadID=177664&start=15&tstart=0
 
Misc:
http://weblogs.java.net/blog/castelaz/archive/2004/08/the_best_laid_p.html
http://www.mycgiserver.com/~Kleopatra/swing/button/buttonverify.html
http://weblogs.java.net/blog/joshy/archive/2003/10/swing_hack_4_th.html
http://java.sun.com/docs/books/tutorial/uiswing/components/rootpane.html#glasspane
http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html#popup
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/FocusSpec.html#FocusAndVetoableChangeListener


Submitted On 25-OCT-2005
benarnold
I think this should be changed to a "bug", not because of the interaction with JButtons in general, but because of JComboBoxes.

Normal buttons can have logic attached to them that checks that the currently focused field is valid. It might make sense for some buttons (like "Cancel") to stay active when the user has entered invalid data.

JComboBoxes are different. All the event-handling for a JComboBox's pop-up button is done in the look and feel, which means that there is no way to intercept it and disable it without writing custom versions for each look and feel your customers are likely to use... or resorting to something drastic like a glass pane.

Most Swing forms have a combination of text fields and combo boxes. What sense is there in a verifier that prevents the user from changing text fields but lets them change combo boxes, which are just text fields with a pick-list. As others have said, it renders the whole InputValidator concept almost useless.


Submitted On 29-DEC-2005
jesse_barnum
Sun - what is the best way to solve this problem? I've read that the behavior is 'correct', but the problem remains. There is currently no good solution that I can see to ensure that a field is valid before the user can do other actions.


Submitted On 14-MAR-2007
We are now up to JDK 1.6 and this bug still hasn't been fixed.

I don't understand why Sun can't fix this bug in over 6 years and several JVM releases.

Swing won't go anywhere if bugs like these remain over a span of many years.


Submitted On 02-APR-2008
Please do something with this bug. It makes InputVerifier completly unusable. I can even create JTabbedPane and  switch focus to different JTextField on different tab.

Marek Mosiewicz
http://www.jotel.com.pl


Submitted On 12-AUG-2008
Any status update on this bug? I am using a JComboBox in one of the projects I am doing and the InputVerifier is proving to be a pain. The JComboBox is embedded in a small JDialog which is hidden when the user clicks 'OK' or 'Cancel'. When the JDialog becomes active again, there is code to clear out the JComboBox and initialize it with different data. The InputVerifier also seems to be called when the removeAllItems() method of the box is called, even though I have attached the InputVerifier to the underlying textfield...this is really a painful process, maybe another JVM will do the trick.



PLEASE NOTE: JDK6 is formerly known as Project Mustang