Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forbid tab change in a JTabbedPane

I'm trying to prevent the user from changing a tab when the current tab is not valid. So when he clicks on a tab, I want to check if the current one is "valid", and if not, stay on the current tab. I tried to use a VetoableChangeListener which didn't work, the code never goes inside the vetoableChange method:

jTabbedPane.addVetoableChangeListener(new VetoableChangeListener() {

  @Override
  public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
    if (!isCurrentTabValid()) {
      throw new PropertyVetoException("test", evt);
    }
  }
});

How can I do this properly?

Thanks!

like image 899
Nanocom Avatar asked Sep 12 '12 13:09

Nanocom


2 Answers

Sounds like you want to disable the tab first. Then when the current page is valid enable the tab. Also sounds like you may want to consider a CardLayout instead of tabs. Then use a "Next" or "Continue" button when the current page is valid.

like image 43
km1 Avatar answered Sep 18 '22 01:09

km1


A VetoableChangeListener is useful only if the class it is registered with fires a vetoable propertyChange. Most (all? never came across one) properties on JComponents and subclasses are not vetoable. Plus the selection is handled by a SingleSelectionModel, not the component itself.

That model is the hook to support vetoable changes

  • implement a custom model that fires a vetoablePropertyChange on selection change
  • if none of its listeners object, go ahead with the change, otherwise do nothing
  • set the custom model to the tabbedPane
  • implement a VetoablePropertyChangeListener which contains the validation logic
  • register the vetoableListener to the model

in code, something like

public static class VetoableSingleSelectionModel extends
        DefaultSingleSelectionModel {

    private VetoableChangeSupport vetoableChangeSupport;

    @Override
    public void setSelectedIndex(int index) {
        if (getSelectedIndex() == index)
            return;
        try {
            fireVetoableChange(getSelectedIndex(), index);
        } catch (PropertyVetoException e) {
            return;
        }
        super.setSelectedIndex(index);
    }

    private void fireVetoableChange(int oldSelectionIndex,
            int newSelectionIndex) throws PropertyVetoException {
        if (!isVetoable())
            return;
        vetoableChangeSupport.fireVetoableChange("selectedIndex",
                oldSelectionIndex, newSelectionIndex);

    }

    private boolean isVetoable() {
        if (vetoableChangeSupport == null)
            return false;
        return vetoableChangeSupport.hasListeners(null);
    }

    public void addVetoableChangeListener(VetoableChangeListener l) {
        if (vetoableChangeSupport == null) {
            vetoableChangeSupport = new VetoableChangeSupport(this);
        }
        vetoableChangeSupport.addVetoableChangeListener(l);
    }

    public void removeVetoableChangeListener(VetoableChangeListener l) {
        if (vetoableChangeSupport == null)
            return;
        vetoableChangeSupport.removeVetoableChangeListener(l);
    }

}

// usage
JTabbedPane pane = new JTabbedPane();
VetoableSingleSelectionModel model = new VetoableSingleSelectionModel();
VetoableChangeListener validator = new VetoableChangeListener() {

    @Override
    public void vetoableChange(PropertyChangeEvent evt)
            throws PropertyVetoException {
        int oldSelection = (int) evt.getOldValue();
        if ((oldSelection == -1) || isValidTab(oldSelection)) return;

        throw new PropertyVetoException("change not valid", evt);

    }

    private boolean isValidTab(int oldSelection) {
        // implement your validation logic here
        return false;
    }
};
model.addVetoableChangeListener(validator);
pane.setModel(model);
pane.addTab("one", new JLabel("here we are and stay"));
pane.addTab("other", new JLabel("poor me, never shown"));
like image 76
kleopatra Avatar answered Sep 21 '22 01:09

kleopatra