Запретить переключение с определенной кнопки-переключателя в группе кнопок, если не выполняется условие

Я хотел бы создать группу из двух кнопок (A и B), где переключение с B на A всегда разрешено, но переключение с A на B зависит от условия. Условие может быть проверено только в последнем случае (от A до B), и проверка может быть выполнена только один раз за попытку переключения. Если переключение предотвращено, не должно быть ItemEvent.SELECTED события, генерируемые для любой из кнопок.

Кажется довольно простым, поэтому я озадачен тем, почему я не смог сделать это простым и кратким способом. Я думал, что расширение ButtonGroup было способом сделать это, но теперь я не уверен больше.

import java.awt.FlowLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;

public class ToggleGroup extends JFrame {

    private ButtonGroup group = new MyButtonGroup();
    private JToggleButton buttonA = new JToggleButton("A");
    private JToggleButton buttonB = new JToggleButton("B");

    public ToggleGroup() {
        setLayout(new FlowLayout());
        add(buttonA);
        add(buttonB);
        group.add(buttonA);
        group.add(buttonB);
        group.setSelected(buttonA.getModel(), true);
        pack();
        setLocationRelativeTo(null);

        ItemListener itemListener = new ItemListener() {
            public void itemStateChanged(ItemEvent e) {                
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    System.out.println("-> " + (e.getSource() == buttonA ? "A" : "B") + " selected");
                }
            }
        };
        buttonA.addItemListener(itemListener);
        buttonB.addItemListener(itemListener);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                ToggleGroup test = new ToggleGroup();
                test.setVisible(true);
            }
        });
    }

    private class MyButtonGroup extends ButtonGroup {

        private boolean check() {
            int result = JOptionPane.showConfirmDialog(
                    ToggleGroup.this, "May I switch to B?",
                    "Important question", JOptionPane.YES_NO_OPTION,
                    JOptionPane.WARNING_MESSAGE);
            return result == JOptionPane.YES_OPTION;
        }

        @Override
        public void setSelected(ButtonModel m, boolean b) {      
            if (!b) {
                return;
            }
            if (m == buttonA.getModel() || m == buttonB.getModel() && check()) {
                super.setSelected(m, b);
            }
        }

    }
}

Проблема с моим кодом очевидна. Условие проверяется несколько раз, поэтому диалоговое окно также отображается несколько раз.

Итак, как я могу "потреблять" попытку переключения, когда условие не выполняется?

РЕДАКТИРОВАТЬ:

Контекст, в котором я использую эти кнопки, является реализацией переключения между различными режимами приложения. Один из режимов позволяет изменять данные, а затем фиксировать их. Если существуют незафиксированные изменения, переключение режимов может означать потерю данных. Я хотел бы убедиться, что переключатель был преднамеренным. Отключение любой из кнопок, пока условие не выполнено, не является вариантом.

2 ответа

Решение

Интересно... копание немного оказывается, что взаимодействие выбранного / вооруженного / нажатого несколько смущено прерванной проверкой (слабо помню некоторую ошибку, но не могу найти ее прямо сейчас). Основная проблема заключается в том, чтобы не допустить повторного входа в метод setSelected. Грязный (читай: не копать дальше) выход - переключать флаг, что-то вроде

private boolean isChecking;
@Override
public void setSelected(final ButtonModel m, boolean b) {   
    if (isChecking) return;
    isChecking = false;
    if (!b) {
        return;
    }
    if (m == buttonB.getModel()) {
        isChecking = true;
        final boolean select = check();
        if (select) {
            superSetSelected(m, select);
        }
        isChecking = false;
        return;
    } else {
        superSetSelected(m, b);
    }
}

protected void superSetSelected(ButtonModel m, boolean b) {
    super.setSelected(m, b);
}

После консультации по внедрению ButtonGroup Я понял, что зовет ButtonGroup.setSelected(ButtonModel, boolean) может вызвать новые вызовы того же метода, что приведет к тому, что мое состояние будет проверено до трех раз. Поэтому я теперь остерегаюсь от последующих звонков. Кажется, работает. Хотя немного сомнительно.

private class MyButtonGroup extends ButtonGroup {

    private Stack<ButtonModel> locked = new Stack<ButtonModel>();

    @Override
    public void setSelected(ButtonModel m, boolean b) {      
        if (!b || !locked.isEmpty() && locked.peek() == m) {
            return;
        }
        locked.push(m);
        if (m == buttonA.getModel() || m == buttonB.getModel() && check()) {
            super.setSelected(m, b);
        }
        locked.pop();
    }

}
Другие вопросы по тегам