0

Normal JCheckbox react directly on user input and sets or unsets the tick. After this the MouseListener is called. What I want to achieve is that the state of the JCheckbox can only be changed by the controller. What are decent way to achieve this?

I tried to add a mouse listener which immediatly add/removes the tick again but this results in flickering.

The only way I found was to overwrite the processMouseEvent method but this is really bad...

EDIT (my current version): This does now work now. Missed to adjust the model before.

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.EventListener;
import javax.swing.JCheckBox;

public class MyJCheckBox extends JCheckBox {
    public MyJCheckBox() {
        MouseListener[] ml = (MouseListener[]) this.getListeners(MouseListener.class);

        for (int i = 0; i < ml.length; i++) {
            this.removeMouseListener(ml[i]);
            this.addMouseListener(new MouseListenerWrapper(ml[i]));
        }

    }

    public void addClickListener(ClickListener listener) {
        listenerList.add(ClickListener.class, listener);
    }

    private class MouseListenerWrapper implements MouseListener {
        MouseListener listener;

        public MouseListenerWrapper(MouseListener listener) {
            this.listener = listener;
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            listener.mouseClicked(e);
        }

        @Override
        public void mousePressed(MouseEvent e) {
            listener.mousePressed(e);
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            for(ClickListener listener : listenerList.getListeners(ClickListener.class)) {
                listener.onClick();

                            //Adjust model
                MyJCheckBox.this.getModel().setArmed(false);
                MyJCheckBox.this.getModel().setPressed(false);
            }

        }

        @Override
        public void mouseEntered(MouseEvent e) {
            listener.mouseEntered(e);
        }

        @Override
        public void mouseExited(MouseEvent e) {
            listener.mouseExited(e);
        }
    }
}

interface ClickListener extends EventListener {
    public void onClick();
}
Pascal Zaugg
  • 183
  • 2
  • 9

2 Answers2

1

I don't like it when people play with the UI. This will confuse the user and they will think the application is broken if they can't click on the check box.

Anyway, remove the MouseListener from the check box.

MouseListener[] ml = (MouseListener[])checkBox.getListeners(MouseListener.class);

for (int i = 0; i < ml.length; i++)
    checkBox.removeMouseListener( ml[i] );
camickr
  • 321,443
  • 19
  • 166
  • 288
  • AFAIK those listener are shared, without effect model could be a way – mKorbel Dec 12 '13 at 20:17
  • @mKorbel , don't know if they are shared or not, but if you only remove them from one check box, then that check box won't respond to the mouse click. – camickr Dec 12 '13 at 20:19
  • maybe you are right, but I'm remember issue with, I'd have to check – mKorbel Dec 12 '13 at 20:22
  • I agree with you. You should disable the CheckBox when it is not clickable. But when you have a button you have the same issue. There is no state change when you click on it. You have to implement this first. I just want the JCheckbox to be passive so a state change only happens by the controller. The only thing I want to remove is unsetting/setting the tick but keeping the rest like getting darker if clicked and get bigger border if entered. Getting the mouseListener looks like a way to go. – Pascal Zaugg Dec 12 '13 at 20:33
  • there is very simple way, use Java7 and paint, simulate everything by using JLayer – mKorbel Dec 12 '13 at 23:03
0

There is another way to implement the behaviour I would like to have if you overwrite the paint method and change the model according to your needs before you paint it.

import java.awt.Graphics;
import javax.swing.AbstractButton;
import javax.swing.ButtonModel;

public class MyJCheckBox extends JCheckBox {
    private boolean change = false;
    private boolean previousState = false;

    @Override
    public void setSelected(boolean selected) {
        if(this.isSelected() != selected) {
            change = true;
    }
        super.setSelected(selected);
    }

    @Override
    public void paint(Graphics g) {
        AbstractButton b = this;
        ButtonModel model = b.getModel();

        boolean changeRequest = false;

        //adjust model state to our needs. A state change
        //is only possible if it was requested by
        //setSelected()

        if(previousState != model.isSelected()) {
            //Revert change if it was not requested by
            //by setSelected()
            if(! change) {
                 changeRequest = true;
                 model.setSelected(previousState);
            }
            //Reset change to false so it can only be changed once
            //To change it again you have to call setSelected() again.
            change = false;
        }

        //Set current state as previous state
        previousState = model.isSelected();

        //paint with adjusted model
        super.paint(g);

        //Inform listener that a change was requested
        if(changeRequest) {
            for(ClickListener listener : listenerList.getListeners(ClickListener.class)) {
            listener.onClick();
            }
        }
    }
}

interface ClickListener extends EventListener {
    public void onClick();
}
Pascal Zaugg
  • 183
  • 2
  • 9