1/*
2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27package sun.lwawt;
28
29import java.awt.Checkbox;
30import java.awt.CheckboxGroup;
31import java.awt.Component;
32import java.awt.Dimension;
33import java.awt.event.ItemEvent;
34import java.awt.event.ItemListener;
35import java.awt.peer.CheckboxPeer;
36import java.beans.Transient;
37
38import javax.swing.JCheckBox;
39import javax.swing.JComponent;
40import javax.swing.JRadioButton;
41import javax.swing.JToggleButton;
42import javax.swing.SwingUtilities;
43
44/**
45 * Lightweight implementation of {@link CheckboxPeer}. Delegates most of the
46 * work to the {@link JCheckBox} and {@link JRadioButton}, which are placed
47 * inside an empty {@link JComponent}.
48 */
49final class LWCheckboxPeer
50        extends LWComponentPeer<Checkbox, LWCheckboxPeer.CheckboxDelegate>
51        implements CheckboxPeer, ItemListener {
52
53    LWCheckboxPeer(final Checkbox target,
54                   final PlatformComponent platformComponent) {
55        super(target, platformComponent);
56    }
57
58    @Override
59    CheckboxDelegate createDelegate() {
60        return new CheckboxDelegate();
61    }
62
63    @Override
64    Component getDelegateFocusOwner() {
65        return getDelegate().getCurrentButton();
66    }
67
68    @Override
69    void initializeImpl() {
70        super.initializeImpl();
71        setLabel(getTarget().getLabel());
72        setState(getTarget().getState());
73        setCheckboxGroup(getTarget().getCheckboxGroup());
74    }
75
76    @Override
77    public void itemStateChanged(final ItemEvent e) {
78        // group.setSelectedCheckbox() will repaint the component
79        // to let LWCheckboxPeer correctly handle it we should call it
80        // after the current event is processed
81        SwingUtilities.invokeLater(new Runnable() {
82            @Override
83            public void run() {
84                boolean postEvent = true;
85                final CheckboxGroup group = getTarget().getCheckboxGroup();
86                if (group != null) {
87                    if (e.getStateChange() == ItemEvent.SELECTED) {
88                        if (group.getSelectedCheckbox() != getTarget()) {
89                            group.setSelectedCheckbox(getTarget());
90                        } else {
91                            postEvent = false;
92                        }
93                    } else {
94                        postEvent = false;
95                        if (group.getSelectedCheckbox() == getTarget()) {
96                            // Don't want to leave the group with no selected
97                            // checkbox.
98                            getTarget().setState(true);
99                        }
100                    }
101                } else {
102                    getTarget().setState(e.getStateChange()
103                                         == ItemEvent.SELECTED);
104                }
105                if (postEvent) {
106                    postEvent(new ItemEvent(getTarget(),
107                                            ItemEvent.ITEM_STATE_CHANGED,
108                                            getTarget().getLabel(),
109                                            e.getStateChange()));
110                }
111            }
112        });
113    }
114
115    @Override
116    public void setCheckboxGroup(final CheckboxGroup g) {
117        synchronized (getDelegateLock()) {
118            getDelegate().getCurrentButton().removeItemListener(this);
119            getDelegate().setRadioButton(g != null);
120            getDelegate().getCurrentButton().addItemListener(this);
121        }
122        repaintPeer();
123    }
124
125    @Override
126    public void setLabel(final String label) {
127        synchronized (getDelegateLock()) {
128            getDelegate().setText(label);
129        }
130    }
131
132    @Override
133    public void setState(final boolean state) {
134        synchronized (getDelegateLock()) {
135            getDelegate().getCurrentButton().removeItemListener(this);
136            getDelegate().setSelected(state);
137            getDelegate().getCurrentButton().addItemListener(this);
138        }
139        repaintPeer();
140    }
141
142    @Override
143    public boolean isFocusable() {
144        return true;
145    }
146
147    @SuppressWarnings("serial")// Safe: outer class is non-serializable.
148    final class CheckboxDelegate extends JComponent {
149
150        private final JCheckBox cb;
151        private final JRadioButton rb;
152
153        CheckboxDelegate() {
154            super();
155            cb = new JCheckBox() {
156                @Override
157                public boolean hasFocus() {
158                    return getTarget().hasFocus();
159                }
160            };
161            rb = new JRadioButton() {
162                @Override
163                public boolean hasFocus() {
164                    return getTarget().hasFocus();
165                }
166            };
167            setLayout(null);
168            setRadioButton(false);
169            add(rb);
170            add(cb);
171        }
172
173        @Override
174        public void setEnabled(final boolean enabled) {
175            super.setEnabled(enabled);
176            rb.setEnabled(enabled);
177            cb.setEnabled(enabled);
178        }
179
180        @Override
181        public void setOpaque(final boolean isOpaque) {
182            super.setOpaque(isOpaque);
183            rb.setOpaque(isOpaque);
184            cb.setOpaque(isOpaque);
185        }
186
187        @Override
188        @Deprecated
189        public void reshape(final int x, final int y, final int w,
190                            final int h) {
191            super.reshape(x, y, w, h);
192            cb.setBounds(0, 0, w, h);
193            rb.setBounds(0, 0, w, h);
194        }
195
196        @Override
197        public Dimension getPreferredSize() {
198            return getCurrentButton().getPreferredSize();
199        }
200
201        @Override
202        @Transient
203        public Dimension getMinimumSize() {
204            return getCurrentButton().getMinimumSize();
205        }
206
207        void setRadioButton(final boolean showRadioButton) {
208            rb.setVisible(showRadioButton);
209            cb.setVisible(!showRadioButton);
210        }
211
212        @Transient
213        JToggleButton getCurrentButton() {
214            return cb.isVisible() ? cb : rb;
215        }
216
217        void setText(final String label) {
218            cb.setText(label);
219            rb.setText(label);
220        }
221
222        void setSelected(final boolean state) {
223            cb.setSelected(state);
224            rb.setSelected(state);
225        }
226    }
227}
228