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     */
016    package com.github.gwtbootstrap.client.ui;
017    
018    import com.google.gwt.core.client.GWT;
019    import com.google.gwt.dom.client.Element;
020    import com.google.gwt.dom.client.EventTarget;
021    import com.google.gwt.event.logical.shared.ValueChangeEvent;
022    import com.google.gwt.i18n.client.HasDirection.Direction;
023    import com.google.gwt.i18n.shared.DirectionEstimator;
024    import com.google.gwt.safehtml.shared.SafeHtml;
025    import com.google.gwt.uibinder.client.UiConstructor;
026    import com.google.gwt.user.client.DOM;
027    import com.google.gwt.user.client.Event;
028    import 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     */
039    public 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    }