Q:

Java Swing API - How Can Two JPanels Communicate With Each Other?

This is my second post related to Java Swing, so pardon me if my question is too straightforward. I'm trying to get multiple JPanels to communicate with each other. I'm building a simple 2D grid that I can add walls/blocked cells to, and then run a simple Floodfill/A* Search algorithm on (given start and goal locations). 
To isolate my issue, I decided that working with an example would be easier. So I created a simple application, that allows the user to write to a text box, provided that he/she has clicked on a "Start" button. Once writing to the text box is done, the user can click on the "Start" button to flip it to the "Stop" state. In the "Stop" state, the user cannot add any text to the text box (i.e. the application should not register any keystrokes at all). This is a simple problem that really brings out my question here. Here's how the UI looks like right now: 

My question: I should be able to write when the button shows "Stop" (since it is in edit mode) and I shouldn't be able to write in the text area when the button shows "Start" (since it is not it edit mode). However, from the above images, you can see that I'm able to write in the text area in any case. How do I then make the editing of the text area dependent on the button state?
Here's my code which attempts at setting up that connection between the button panel and the text panel, but it somehow isn't working as expected.
I looked at StackOverflow posts here and here, but frankly, the answers didn't seem clear to me.
SimpleTextPanel:
public class SimpleTextPanel extends JPanel implements PropertyChangeListener, ChangeListener {

    private boolean canWrite;
    public SimpleTextPanel() {

        // set the border properties
        canWrite = true;
        TitledBorder title = BorderFactory.createTitledBorder("Simple Text Panel");
        title.setTitleColor(Color.BLACK);
        title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED,
                Color.DARK_GRAY, Color.GRAY));
        this.setBorder(title);

        JTextArea editSpace = new JTextArea(10, 20);
        editSpace.setEditable(true);
        editSpace.addPropertyChangeListener(this);
        this.add(editSpace);
    }

    @Override
    public void stateChanged(ChangeEvent changeEvent) {
        JButton button = (JButton)changeEvent.getSource();
        canWrite = button.getText().equals("Start");
    }

    @Override
    public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
        JTextArea area = (JTextArea)propertyChangeEvent.getSource();
        if(!canWrite) area.setText((String)propertyChangeEvent.getOldValue());
    }
}

SimpleButtonPanel:
public class SimpleButtonPanel extends JPanel implements ActionListener {

    JButton switchButton;
    private boolean canWrite = true;

    public SimpleButtonPanel(SimpleTextPanel txt) {

        // set the border properties
        TitledBorder title = BorderFactory.createTitledBorder("Simple Button Panel");
        title.setTitleColor(Color.BLACK);
        title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED,
                Color.DARK_GRAY, Color.GRAY));
        this.setBorder(title);

        switchButton = new JButton("Start");
        switchButton.addActionListener(this);
        switchButton.addChangeListener(txt);
        this.add(switchButton);
    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        if(switchButton.getText().equals("Start")) {
            switchButton.setText("Stop");
            canWrite = false;
        } else if(switchButton.getText().equals("Stop")) {
            switchButton.setText("Start");
            canWrite = true;
        }
    }
}

SimpleExampleTest:
public class SimpleExampleTest extends JFrame {

    public SimpleExampleTest() {

        setLayout(new BorderLayout());
        setTitle("Simple Example");
        setSize(300, 300);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        SimpleTextPanel text = new SimpleTextPanel();
        SimpleButtonPanel button = new SimpleButtonPanel(text);

        add(text, BorderLayout.NORTH);
        add(button, BorderLayout.SOUTH);
        //button.addPropertyChangeListener(text); // so that text editor knows whether to allow writing or not
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                SimpleExampleTest ex = new SimpleExampleTest();
                ex.setVisible(true);
            }
        });
    }
}

Any/all help is appreciated. Thank you! 

A:

One approach would be to devise a model that could shared by the panels.
The text panel only wants to know what the current state is and when that state changes.  The button panel wants to know when the state changes and also wants to be able to change the state.
This decouples the two panels from each other, as they don't really care how the state is changed or who by, only that they can respond to the changes accordingly...
Also, the model doesn't care about either of them, it just carries the state and provides notification when it changes.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.BevelBorder;
import javax.swing.border.TitledBorder;

public class TalkToEachOther {

    public static void main(String[] args) {
        new TalkToEachOther();
    }

    public TalkToEachOther() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Simple Example");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                MutableSimpleModel model = new DefaultSimpleModel();

                SimpleTextPanel text = new SimpleTextPanel(model);
                SimpleButtonPanel button = new SimpleButtonPanel(model);

                frame.add(text, BorderLayout.NORTH);
                frame.add(button, BorderLayout.SOUTH);
                //button.addPropertyChangeListener(text); // so that text editor knows whether to allow writing or not
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface SimpleModel {

        public boolean isEditable();

        public void addPropertyChangeListener(PropertyChangeListener listener);
        public void removePropertyChangeListener(PropertyChangeListener listener);

    }

    public interface MutableSimpleModel extends SimpleModel {

        public void setEditable(boolean editable);

    }

    public class DefaultSimpleModel implements MutableSimpleModel {

        private Set<PropertyChangeListener> listeners;
        private boolean editable;

        public DefaultSimpleModel() {
            listeners = new HashSet<>(25);
        }

        @Override
        public void setEditable(boolean value) {
            if (value != editable) {
                editable = value;
                firePropertyChange("editable", !editable, editable);
            }
        }

        @Override
        public boolean isEditable() {
            return editable;
        }

        @Override
        public void addPropertyChangeListener(PropertyChangeListener listener) {
            listeners.add(listener);
        }

        @Override
        public void removePropertyChangeListener(PropertyChangeListener listener) {
            listeners.remove(listener);
        }

        protected void firePropertyChange(String editable, boolean oldValue, boolean newValue) {
            PropertyChangeEvent evt = new PropertyChangeEvent(this, editable, oldValue, newValue);
            for (PropertyChangeListener listener : listeners) {
                listener.propertyChange(evt);
            }
        }

    }

    public class SimpleTextPanel extends JPanel {

        private SimpleModel model;
        private JTextArea editSpace = new JTextArea(10, 20);

        public SimpleTextPanel(SimpleModel model) {

            this.model = model;
            model.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    updateEditableState();
                }
            });
            setLayout(new BorderLayout());

            // set the border properties
            TitledBorder title = BorderFactory.createTitledBorder("Simple Text Panel");
            title.setTitleColor(Color.BLACK);
            title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED,
                            Color.DARK_GRAY, Color.GRAY));
            this.setBorder(title);

            editSpace = new JTextArea(10, 20);
            this.add(new JScrollPane(editSpace));

            updateEditableState();
        }

        protected void updateEditableState() {
            editSpace.setEditable(model.isEditable());
        }
    }

    public class SimpleButtonPanel extends JPanel implements ActionListener {

        private MutableSimpleModel model;
        private boolean canWrite = true;

        private JButton switchButton;

        public SimpleButtonPanel(MutableSimpleModel model) {

            this.model = model;
            model.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    updateEditableState();
                }
            });

            // set the border properties
            TitledBorder title = BorderFactory.createTitledBorder("Simple Button Panel");
            title.setTitleColor(Color.BLACK);
            title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED,
                            Color.DARK_GRAY, Color.GRAY));
            this.setBorder(title);

            switchButton = new JButton("Start");
            switchButton.addActionListener(this);
            this.add(switchButton);

            updateEditableState();
        }

        @Override
        public void actionPerformed(ActionEvent actionEvent) {
            model.setEditable(!model.isEditable());
        }

        protected void updateEditableState() {
            if (model.isEditable()) {
                switchButton.setText("Stop");
            } else {
                switchButton.setText("Start");
            }
        }
    }
}

