Jörg
Jörg

Reputation: 291

Prevent a JComboBox popup from appearing. Passing InputVerifier

Given the following scenario:

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();
        });
      }
    }
  }

}

Upvotes: 2

Views: 64

Answers (1)

J&#246;rg
J&#246;rg

Reputation: 291

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")) {
...

Upvotes: 0

Related Questions