001    package org.gwtbootstrap3.client.ui;
002    
003    /*
004     * #%L
005     * GwtBootstrap3
006     * %%
007     * Copyright (C) 2013 GwtBootstrap3
008     * %%
009     * Licensed under the Apache License, Version 2.0 (the "License");
010     * you may not use this file except in compliance with the License.
011     * You may obtain a copy of the License at
012     * 
013     *      http://www.apache.org/licenses/LICENSE-2.0
014     * 
015     * Unless required by applicable law or agreed to in writing, software
016     * distributed under the License is distributed on an "AS IS" BASIS,
017     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018     * See the License for the specific language governing permissions and
019     * limitations under the License.
020     * #L%
021     */
022    
023    import org.gwtbootstrap3.client.ui.base.HasActive;
024    import org.gwtbootstrap3.client.ui.base.HasIcon;
025    import org.gwtbootstrap3.client.ui.base.HasIconPosition;
026    import org.gwtbootstrap3.client.ui.base.HasSize;
027    import org.gwtbootstrap3.client.ui.base.HasType;
028    import org.gwtbootstrap3.client.ui.base.helper.StyleHelper;
029    import org.gwtbootstrap3.client.ui.base.mixin.ActiveMixin;
030    import org.gwtbootstrap3.client.ui.constants.ButtonSize;
031    import org.gwtbootstrap3.client.ui.constants.ButtonType;
032    import org.gwtbootstrap3.client.ui.constants.IconFlip;
033    import org.gwtbootstrap3.client.ui.constants.IconPosition;
034    import org.gwtbootstrap3.client.ui.constants.IconRotate;
035    import org.gwtbootstrap3.client.ui.constants.IconSize;
036    import org.gwtbootstrap3.client.ui.constants.IconType;
037    import org.gwtbootstrap3.client.ui.constants.Styles;
038    
039    import com.google.gwt.dom.client.Document;
040    import com.google.gwt.dom.client.InputElement;
041    import com.google.gwt.event.dom.client.ClickEvent;
042    import com.google.gwt.event.dom.client.ClickHandler;
043    import com.google.gwt.event.logical.shared.ValueChangeEvent;
044    import com.google.gwt.i18n.client.HasDirection.Direction;
045    import com.google.gwt.i18n.shared.DirectionEstimator;
046    import com.google.gwt.safehtml.shared.SafeHtml;
047    import com.google.gwt.uibinder.client.UiConstructor;
048    import com.google.gwt.user.client.DOM;
049    import com.google.gwt.user.client.Event;
050    
051    /**
052     * Button representing a radio button used within a {@link ButtonGroup} that has
053     * toggle set to {@code Toogle.BUTTONS}.
054     * <p/>
055     * If you are looking for a classic radio button see {@link RadioButton}.
056     *
057     * @author Sven Jacobs
058     */
059    public class RadioButton extends Radio implements HasActive,
060            HasType<ButtonType>, HasSize<ButtonSize>, HasIcon, HasIconPosition {
061    
062        private final ActiveMixin<RadioButton> activeMixin = new ActiveMixin<RadioButton>(this);
063    
064        private IconPosition iconPosition = IconPosition.LEFT;
065        private Icon icon;
066    
067    
068        /**
069         * Creates a new radio associated with a particular group, and initialized
070         * with the given HTML label. All radio buttons associated with the same
071         * group name belong to a mutually-exclusive set.
072         * 
073         * Radio buttons are grouped by their name attribute, so changing their name
074         * using the setName() method will also change their associated group.
075         * 
076         * @param name
077         *            the group name with which to associate the radio button
078         * @param label
079         *            this radio button's html label
080         */
081        public RadioButton(String name, SafeHtml label) {
082            this(name, label.asString(), true);
083        }
084    
085        /**
086         * @see #RadioButtonToggle(String, SafeHtml)
087         * 
088         * @param name
089         *            the group name with which to associate the radio button
090         * @param label
091         *            this radio button's html label
092         * @param dir
093         *            the text's direction. Note that {@code DEFAULT} means
094         *            direction should be inherited from the widget's parent
095         *            element.
096         */
097        public RadioButton(String name, SafeHtml label, Direction dir) {
098            this(name);
099            setHTML(label, dir);
100        }
101    
102        /**
103         * @see #RadioButtonToggle(String, SafeHtml)
104         * 
105         * @param name
106         *            the group name with which to associate the radio button
107         * @param label
108         *            this radio button's html label
109         * @param directionEstimator
110         *            A DirectionEstimator object used for automatic direction
111         *            adjustment. For convenience,
112         *            {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
113         */
114        public RadioButton(String name, SafeHtml label, DirectionEstimator directionEstimator) {
115            this(name);
116            setDirectionEstimator(directionEstimator);
117            setHTML(label.asString());
118        }
119    
120        /**
121         * Creates a new radio associated with a particular group, and initialized
122         * with the given HTML label. All radio buttons associated with the same
123         * group name belong to a mutually-exclusive set.
124         * 
125         * Radio buttons are grouped by their name attribute, so changing their name
126         * using the setName() method will also change their associated group.
127         * 
128         * @param name
129         *            the group name with which to associate the radio button
130         * @param label
131         *            this radio button's label
132         */
133        public RadioButton(String name, String label) {
134            this(name);
135            setText(label);
136        }
137    
138        /**
139         * @see #RadioButtonToggle(String, SafeHtml)
140         * 
141         * @param name
142         *            the group name with which to associate the radio button
143         * @param label
144         *            this radio button's label
145         * @param dir
146         *            the text's direction. Note that {@code DEFAULT} means
147         *            direction should be inherited from the widget's parent
148         *            element.
149         */
150        public RadioButton(String name, String label, Direction dir) {
151            this(name);
152            setText(label, dir);
153        }
154    
155        /**
156         * @see #RadioButtonToggle(String, SafeHtml)
157         * 
158         * @param name
159         *            the group name with which to associate the radio button
160         * @param label
161         *            this radio button's label
162         * @param directionEstimator
163         *            A DirectionEstimator object used for automatic direction
164         *            adjustment. For convenience,
165         *            {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
166         */
167        public RadioButton(String name, String label, DirectionEstimator directionEstimator) {
168            this(name);
169            setDirectionEstimator(directionEstimator);
170            setText(label);
171        }
172    
173        /**
174         * Creates a new radio button associated with a particular group, and
175         * initialized with the given label (optionally treated as HTML). All radio
176         * buttons associated with the same group name belong to a
177         * mutually-exclusive set.
178         * 
179         * Radio buttons are grouped by their name attribute, so changing their name
180         * using the setName() method will also change their associated group.
181         * 
182         * @param name
183         *            name the group with which to associate the radio button
184         * @param label
185         *            this radio button's label
186         * @param asHTML
187         *            <code>true</code> to treat the specified label as HTML
188         */
189        public RadioButton(String name, String label, boolean asHTML) {
190            this(name);
191            if (asHTML) {
192                setHTML(label);
193            } else {
194                setText(label);
195            }
196        }
197    
198        @UiConstructor
199        public RadioButton(String name) {
200            this(Document.get().createRadioInputElement(name));
201        }
202    
203        protected RadioButton(InputElement element) {
204            super(DOM.createLabel(), element);
205    
206            setStyleName(Styles.BTN);
207            setType(ButtonType.DEFAULT);
208    
209            getElement().appendChild(inputElem);
210            getElement().appendChild(Document.get().createTextNode(" "));
211            getElement().appendChild(labelElem);
212            getElement().appendChild(Document.get().createTextNode(" "));
213        }
214        
215        @Override
216        protected void ensureDomEventHandlers() {
217            // Use a ClickHandler since Bootstrap's jQuery does not trigger native
218            // change events:
219            // http://learn.jquery.com/events/triggering-event-handlers/
220            addClickHandler(new ClickHandler() {
221    
222                @Override
223                public void onClick(ClickEvent event) {
224                    ValueChangeEvent.fire(RadioButton.this, getValue());
225                }
226    
227            });
228        }
229    
230        @Override
231        public void sinkEvents(int eventBitsToAdd) {
232            // Sink on the actual element because that's what gets clicked
233            if (isOrWasAttached()) {
234                Event.sinkEvents(getElement(),
235                        eventBitsToAdd | Event.getEventsSunk(getElement()));
236            } else {
237                super.sinkEvents(eventBitsToAdd);
238            }
239        }
240    
241        @Override
242        public void setSize(ButtonSize size) {
243            StyleHelper.addUniqueEnumStyleName(this, ButtonSize.class, size);
244        }
245    
246        @Override
247        public ButtonSize getSize() {
248            return ButtonSize.fromStyleName(getStyleName());
249        }
250    
251        @Override
252        public void setType(ButtonType type) {
253            StyleHelper.addUniqueEnumStyleName(this, ButtonType.class, type);
254        }
255    
256        @Override
257        public ButtonType getType() {
258            return ButtonType.fromStyleName(getStyleName());
259        }
260    
261        @Override
262        public void setActive(boolean active) {
263            setValue(active);
264            activeMixin.setActive(active);
265        }
266    
267        @Override
268        public boolean isActive() {
269            return activeMixin.isActive();
270        }
271    
272        @Override
273        public void setIconPosition(IconPosition iconPosition) {
274            this.iconPosition = iconPosition;
275            render();
276        }
277    
278        @Override
279        public IconPosition getIconPosition() {
280            return iconPosition;
281        }
282    
283        @Override
284        public void setIcon(IconType iconType) {
285            getActualIcon().setType(iconType);
286        }
287    
288        @Override
289        public IconType getIcon() {
290            return getActualIcon().getType();
291        }
292    
293        @Override
294        public void setIconSize(IconSize iconSize) {
295            getActualIcon().setSize(iconSize);
296        }
297    
298        @Override
299        public IconSize getIconSize() {
300            return getActualIcon().getSize();
301        }
302    
303        @Override
304        public void setIconFlip(IconFlip iconFlip) {
305            getActualIcon().setFlip(iconFlip);
306        }
307    
308        @Override
309        public IconFlip getIconFlip() {
310            return getActualIcon().getFlip();
311        }
312    
313        @Override
314        public void setIconRotate(IconRotate iconRotate) {
315            getActualIcon().setRotate(iconRotate);
316        }
317    
318        @Override
319        public IconRotate getIconRotate() {
320            return getActualIcon().getRotate();
321        }
322    
323        @Override
324        public void setIconBordered(boolean iconBordered) {
325            getActualIcon().setBorder(iconBordered);
326        }
327    
328        @Override
329        public boolean isIconBordered() {
330            return getActualIcon().isBorder();
331        }
332    
333        @Override
334        public void setIconMuted(boolean iconMuted) {
335            getActualIcon().setMuted(iconMuted);
336        }
337    
338        @Override
339        public boolean isIconMuted() {
340            return getActualIcon().isMuted();
341        }
342    
343        @Override
344        public void setIconLight(boolean iconLight) {
345            getActualIcon().setLight(iconLight);
346        }
347    
348        @Override
349        public boolean isIconLight() {
350            return getActualIcon().isLight();
351        }
352    
353        @Override
354        public void setIconSpin(boolean iconSpin) {
355            getActualIcon().setSpin(iconSpin);
356        }
357    
358        @Override
359        public boolean isIconSpin() {
360            return getActualIcon().isSpin();
361        }
362    
363        @Override
364        public void setIconFixedWidth(boolean iconFixedWidth) {
365            getActualIcon().setFixedWidth(iconFixedWidth);
366        }
367    
368        @Override
369        public boolean isIconFixedWidth() {
370            return getActualIcon().isFixedWidth();
371        }
372    
373        private Icon getActualIcon() {
374            if (icon == null) {
375                icon = new Icon();
376                render();
377            }
378            return icon;
379        }
380    
381        private void render() {
382            if (iconPosition == IconPosition.LEFT) {
383                getElement().insertAfter(icon.getElement(), inputElem);
384            } else {
385                getElement().insertAfter(icon.getElement(), null);
386            }
387        }
388    
389    }