001/*
002 *  Copyright 2012 GWT-Bootstrap
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.github.gwtbootstrap.client.ui;
017
018import com.google.gwt.core.client.GWT;
019import com.google.gwt.dom.client.Element;
020import com.google.gwt.dom.client.EventTarget;
021import com.google.gwt.event.logical.shared.ValueChangeEvent;
022import com.google.gwt.i18n.client.HasDirection.Direction;
023import com.google.gwt.i18n.shared.DirectionEstimator;
024import com.google.gwt.safehtml.shared.SafeHtml;
025import com.google.gwt.uibinder.client.UiConstructor;
026import com.google.gwt.user.client.DOM;
027import com.google.gwt.user.client.Event;
028import com.google.gwt.user.client.ui.DirectionalTextHelper;
029
030/**
031 * RadioButton widgets.
032 * <p>
033 * Re-design for Bootstrap.
034 * </p>
035 * 
036 * @since 2.0.4.0
037 * @author ohashi keisuke
038 */
039public class RadioButton extends CheckBox {
040
041        public static final DirectionEstimator DEFAULT_DIRECTION_ESTIMATOR = DirectionalTextHelper.DEFAULT_DIRECTION_ESTIMATOR;
042
043        private Boolean oldValue;
044
045        /**
046         * Creates a new radio associated with a particular group name. All radio
047         * buttons associated with the same group name belong to a
048         * mutually-exclusive set.
049         * 
050         * Radio buttons are grouped by their name attribute, so changing their name
051         * using the setName() method will also change their associated group.
052         * 
053         * @param name
054         *            the group name with which to associate the radio button
055         */
056        @UiConstructor
057        public RadioButton(String name) {
058                super(DOM.createInputRadio(name));
059
060                sinkEvents(Event.ONCLICK);
061                sinkEvents(Event.ONMOUSEUP);
062                sinkEvents(Event.ONBLUR);
063                sinkEvents(Event.ONKEYDOWN);
064        }
065
066        /**
067         * Creates a new radio associated with a particular group, and initialized
068         * with the given HTML label. All radio buttons associated with the same
069         * group name belong to a mutually-exclusive set.
070         * 
071         * Radio buttons are grouped by their name attribute, so changing their name
072         * using the setName() method will also change their associated group.
073         * 
074         * @param name
075         *            the group name with which to associate the radio button
076         * @param label
077         *            this radio button's html label
078         */
079        public RadioButton(String name,
080                SafeHtml label) {
081                this(name, label.asString(), true);
082        }
083
084        /**
085         * @see #RadioButton(String, SafeHtml)
086         * 
087         * @param name
088         *            the group name with which to associate the radio button
089         * @param label
090         *            this radio button's html label
091         * @param dir
092         *            the text's direction. Note that {@code DEFAULT} means
093         *            direction should be inherited from the widget's parent
094         *            element.
095         */
096        public RadioButton(String name,
097                SafeHtml label,
098                Direction dir) {
099                this(name);
100                setHTML(label, dir);
101        }
102
103        /**
104         * @see #RadioButton(String, SafeHtml)
105         * 
106         * @param name
107         *            the group name with which to associate the radio button
108         * @param label
109         *            this radio button's html label
110         * @param directionEstimator
111         *            A DirectionEstimator object used for automatic direction
112         *            adjustment. For convenience,
113         *            {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
114         */
115        public RadioButton(String name,
116                SafeHtml label,
117                DirectionEstimator directionEstimator) {
118                this(name);
119                setDirectionEstimator(directionEstimator);
120                setHTML(label.asString());
121        }
122
123        /**
124         * Creates a new radio associated with a particular group, and initialized
125         * with the given HTML label. All radio buttons associated with the same
126         * group name belong to a mutually-exclusive set.
127         * 
128         * Radio buttons are grouped by their name attribute, so changing their name
129         * using the setName() method will also change their associated group.
130         * 
131         * @param name
132         *            the group name with which to associate the radio button
133         * @param label
134         *            this radio button's label
135         */
136        public RadioButton(String name,
137                String label) {
138                this(name);
139                setText(label);
140        }
141
142        /**
143         * @see #RadioButton(String, SafeHtml)
144         * 
145         * @param name
146         *            the group name with which to associate the radio button
147         * @param label
148         *            this radio button's label
149         * @param dir
150         *            the text's direction. Note that {@code DEFAULT} means
151         *            direction should be inherited from the widget's parent
152         *            element.
153         */
154        public RadioButton(String name,
155                String label,
156                Direction dir) {
157                this(name);
158                setText(label, dir);
159        }
160
161        /**
162         * @see #RadioButton(String, SafeHtml)
163         * 
164         * @param name
165         *            the group name with which to associate the radio button
166         * @param label
167         *            this radio button's label
168         * @param directionEstimator
169         *            A DirectionEstimator object used for automatic direction
170         *            adjustment. For convenience,
171         *            {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
172         */
173        public RadioButton(String name,
174                String label,
175                DirectionEstimator directionEstimator) {
176                this(name);
177                setDirectionEstimator(directionEstimator);
178                setText(label);
179        }
180
181        /**
182         * Creates a new radio button associated with a particular group, and
183         * initialized with the given label (optionally treated as HTML). All radio
184         * buttons associated with the same group name belong to a
185         * mutually-exclusive set.
186         * 
187         * Radio buttons are grouped by their name attribute, so changing their name
188         * using the setName() method will also change their associated group.
189         * 
190         * @param name
191         *            name the group with which to associate the radio button
192         * @param label
193         *            this radio button's label
194         * @param asHTML
195         *            <code>true</code> to treat the specified label as HTML
196         */
197        public RadioButton(String name,
198                String label,
199                boolean asHTML) {
200                this(name);
201                if (asHTML) {
202                        setHTML(label);
203                } else {
204                        setText(label);
205                }
206        }
207
208        /**
209         * Overridden to send ValueChangeEvents only when appropriate.
210         */
211        @Override
212        public void onBrowserEvent(Event event) {
213
214                switch (DOM.eventGetType(event)) {
215                case Event.ONMOUSEUP:
216                case Event.ONBLUR:
217                case Event.ONKEYDOWN:
218                        // Note the old value for onValueChange purposes (in ONCLICK case)
219                        oldValue = getValue();
220                        break;
221
222                case Event.ONCLICK:
223                        EventTarget target = event.getEventTarget();
224                        
225                        if (Element.is(target) 
226                                && !Element.as(target).getTagName().toUpperCase().equals("INPUT")
227                                && asLabel().isOrHasChild(Element.as(target))) {
228                                GWT.log("test");
229                                // They clicked the label. Note our pre-click value, and
230                                // short circuit event routing so that other click handlers
231                                // don't hear about it
232                                oldValue = getValue();
233                                return;
234                        }
235
236                        // It's not the label. Let our handlers hear about the
237                        // click...
238                        super.onBrowserEvent(event);
239                        // ...and now maybe tell them about the change
240                        ValueChangeEvent.fireIfNotEqual(RadioButton.this, oldValue, getValue());
241                        return;
242                }
243                super.onBrowserEvent(event);
244        }
245
246        /**
247         * Change the group name of this radio button.
248         * 
249         * Radio buttons are grouped by their name attribute, so changing their name
250         * using the setName() method will also change their associated group.
251         * 
252         * If changing this group name results in a new radio group with multiple
253         * radio buttons selected, this radio button will remain selected and the
254         * other radio buttons will be unselected.
255         * 
256         * @param name
257         *            name the group with which to associate the radio button
258         */
259        @Override
260        public void setName(String name) {
261                // Just changing the radio button name tends to break groupiness,
262                // so we have to replace it. Note that replaceInputElement is careful
263                // not to propagate name when it propagates everything else
264                replaceInputElement(DOM.createInputRadio(name));
265        }
266
267        @Override
268        public void sinkEvents(int eventBitsToAdd) {
269                super.sinkEvents(eventBitsToAdd);
270        }
271
272        /**
273         * No-op. CheckBox's click handler is no good for radio button, so don't use
274         * it. Our event handling is all done in {@link #onBrowserEvent}
275         */
276        @Override
277        protected void ensureDomEventHandlers() {
278        }
279}